You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@deltacloud.apache.org by mf...@redhat.com on 2010/12/20 17:41:22 UTC

Initial import of new RHEV-M driver (rev 2)

Hi,

I managed some RHEV-M setup on my desk and actually fixed all
bugs from RHEV Mock API.
So for now, this driver *should* work with latest RHEV API.

Things supported:

* Create VM and managing VMs
  * POST /api/instances
  * POST /api/instances/:id/stop
  * POST /api/instances/:id/start
  * POST /api/instances/:id/reboot
  * DELETE /api/instances/:id

* Realms are now reported correctly

  -- Michal


[PATCH core 2/2] Fixed a lot bugs inside RHEV driver Added support for creating VMs

Posted by mf...@redhat.com.
From: Michal Fojtik <mf...@redhat.com>

---
 .../lib/deltacloud/drivers/rhevm2/rhevm2_driver.rb |   75 ++++++++++++++------
 .../lib/deltacloud/drivers/rhevm2/rhevm_client.rb  |   66 +++++++++++-------
 2 files changed, 94 insertions(+), 47 deletions(-)

diff --git a/server/lib/deltacloud/drivers/rhevm2/rhevm2_driver.rb b/server/lib/deltacloud/drivers/rhevm2/rhevm2_driver.rb
index f1655d1..25b3345 100644
--- a/server/lib/deltacloud/drivers/rhevm2/rhevm2_driver.rb
+++ b/server/lib/deltacloud/drivers/rhevm2/rhevm2_driver.rb
@@ -36,13 +36,13 @@ class RHEVM2Driver < Deltacloud::BaseDriver
 
   define_hardware_profile 'SERVER' do
     cpu         1
-    memory      [ 1*1024 .. 32*1024 ]
+    memory      ( 512 .. 32*1024 )
     architecture 'x86_64'
   end
 
   define_hardware_profile 'DESKTOP' do
     cpu         1
-    memory      [ 1*1024 .. 32*1024 ]
+    memory      ( 512 .. 32*1024 )
     architecture 'x86_64'
   end
 
@@ -55,7 +55,7 @@ class RHEVM2Driver < Deltacloud::BaseDriver
 
   def provider_uri
     # 'http://localhost:8099/rhevm-api-mock/' || @RHEVM_URI
-    'http://localhost:8080'
+    'https://10.34.2.122:8443/rhevm-api-powershell'
   end
 
   # FIX: This is state table from EC2, fix it pls to 
@@ -64,9 +64,10 @@ class RHEVM2Driver < Deltacloud::BaseDriver
   define_instance_states do
     start.to( :pending )          .automatically
     pending.to( :running )        .automatically
-    pending.to( :stopping )       .on( :stop )
     pending.to( :stopped )        .automatically
+    pending.to( :finish )         .on(:destroy)
     stopped.to( :running )        .on( :start )
+    stopped.to( :finish )         .on( :destroy )
     running.to( :running )        .on( :reboot )
     running.to( :stopping )       .on( :stop )
     shutting_down.to( :stopped )  .automatically
@@ -98,7 +99,7 @@ class RHEVM2Driver < Deltacloud::BaseDriver
         templates = templates.select{|t| opts[:id] == t.id}
       end
       templates.each do |t|
-        img_arr << convert_image(t)
+        img_arr << convert_image(client, t)
       end
     end
     img_arr = filter_on( img_arr, :architecture, opts )
@@ -125,6 +126,13 @@ class RHEVM2Driver < Deltacloud::BaseDriver
     end
   end
 
+  def start_instance(credentials, id)
+    client = new_client(credentials)
+    safely do
+      client.vm_action(:start, id)
+    end
+  end
+
   def stop_instance(credentials, id)
     client = new_client(credentials)
     safely do
@@ -132,17 +140,30 @@ class RHEVM2Driver < Deltacloud::BaseDriver
     end
   end
 
-  def start_instance(credentials, id)
+  def destroy_instance(credentials, id)
     client = new_client(credentials)
     safely do
-      client.vm_action(:start, id)
+      client.delete_vm(id)
     end
   end
 
-  def shutdown_instance(credentials, id)
+  def create_instance(credentials, image_id, opts={})
     client = new_client(credentials)
     safely do
-      client.vm_action(:shutdown, id)
+      cluster = get_cluster_from_realm(client, opts[:realm_id] || 'Default')
+      vm_name = opts[:name] ? "<name>#{opts[:name]}</name>" : ""
+      vm_template = "<template id='#{image_id}'/>"
+      vm_cluster = opts[:realm_id] ? "<cluster id='#{cluster.id}'/>" : "<cluster id='Default'/>"
+      # This cause Internal Server Error on RHEV-API side
+      # vm_memory = opts[:hwp_memory] ? "<memory>#{opts[:hwp_memory].to_i*1024}</memory>"  : ''
+      convert_instance(client, RHEVM::Vm::new(client, client.create_vm(
+        "<vm>"+
+        vm_name +
+        vm_template +
+        vm_cluster +
+        # vm_memory +
+        "</vm>"
+      ).xpath('vm')))
     end
   end
 
@@ -153,10 +174,15 @@ class RHEVM2Driver < Deltacloud::BaseDriver
     RHEVM::Client.new(credentials.user, credentials.password, provider_uri, opts)
   end
 
+  def get_cluster_from_realm(client, datacenter_id)
+    clusters = client.clusters
+    clusters.select { |c| c.datacenter_id }.first
+  end
+
   # I can't test this method, since 'cluster' is not a part of Mock driver
   def get_realm_from_cluster(client, cluster_id)
     cluster = client.clusters(:id => cluster_id)
-    datacenter = client.datacenter(:id => cluster.datacenter_id)
+    datacenter = client.datacenters(:id => cluster.datacenter_id)
     convert_realm(datacenter)
   end
 
@@ -164,18 +190,16 @@ class RHEVM2Driver < Deltacloud::BaseDriver
     # FIXME: Remove 'DOWN', it's used just because RHEV Mock driver
     #        has no 'state' property in XML.
     #
-    # FIXME2: This call will now throw 500 because I'm unable to fetch realm
-    # for instance. 
-    #
     state = convert_state(inst.status=='' ? 'DOWN' : inst.status)
+    instance_realm = get_realm_from_cluster(client, inst.cluster)
     Instance.new(
       :id => inst.id,
       :name => inst.name,
       :state => state,
       :image_id => inst.template,
       # Realm aka DataCenter could be retrieved from Cluster
-      :realm_id => get_realm_from_cluster(client, inst.cluster),
-      :owner_id => 'self',
+      :realm_id => instance_realm ? instance_realm.id : nil,
+      :owner_id => client.username,
       :launch_time => inst.creation_time,
       :instance_profile => InstanceProfile::new(inst.profile),
       :hardware_profile_id => inst.profile,
@@ -192,37 +216,44 @@ class RHEVM2Driver < Deltacloud::BaseDriver
   # FIXME: This will need to be fixed regarding to state-machine
   def convert_state(state)
     case state
-    when 'POWERING_UP', 'WAIT_FOR_LAUNCH', 'REBOOT_IN_PROGRESS', 'SAVING_STATE',
+    when 'WAIT_FOR_LAUNCH', 'REBOOT_IN_PROGRESS', 'SAVING_STATE',
       'RESTORING_STATE', 'POWERING_DOWN' then
       'PENDING'
     when 'UNASSIGNED', 'DOWN', 'POWERING_DOWN', 'PAUSED', 'NOT_RESPONDING', 'SAVING_STATE', 
       'SUSPENDED', 'IMAGE_ILLEGAL', 'IMAGE_LOCKED', 'UNKNOWN' then
       'STOPPED'
-    when 'UP', 'MIGRATING_TO', 'MIGRATING_FROM'
+    when 'POWERING_UP', 'UP', 'MIGRATING_TO', 'MIGRATING_FROM'
       'RUNNING'
     end
   end
 
-  def convert_image(img)
+  def convert_image(client, img)
     Image.new(
       :id => img.id,
       :name => img.name,
       :description => img.description,
-      :owner_id => 'self',
+      :owner_id => client.username,
       :architecture => 'x86_64', # All RHEV-M VMs are x86_64
       :status => img.status
     )
   end
 
-  def convert_realm(realm)
+  def convert_realm(dc)
     Realm.new(
-      :id => realm.id,
-      :name => realm.name,
+      :id => dc.id,
+      :name => dc.name,
       :state => 'AVAILABLE',
       :limit => :unlimited
     )
   end
 
+#  def catched_exceptions_list
+#    {
+#      :auth => RestClient::Unauthorized,
+#      :error => RestClient::InternalServerError,
+#      :glob => [ /RestClient::(\w+)/ ]
+#    }
+#  end
 
 end
 
diff --git a/server/lib/deltacloud/drivers/rhevm2/rhevm_client.rb b/server/lib/deltacloud/drivers/rhevm2/rhevm_client.rb
index 856844c..25d886a 100644
--- a/server/lib/deltacloud/drivers/rhevm2/rhevm_client.rb
+++ b/server/lib/deltacloud/drivers/rhevm2/rhevm_client.rb
@@ -1,9 +1,4 @@
-require 'rubygems'
-
-#gem 'rest-client'
-
 require 'base64'
-require 'logger'
 require 'restclient'
 require 'nokogiri'
 
@@ -11,17 +6,18 @@ module RHEVM
 
   class Client
     attr_reader :base_uri
+    attr_reader :host
     attr_reader :entry_points
-    attr_reader :logger
+    attr_reader :username
 
     # Define a list of supported collections which will be handled automatically
     # by method_missing
     @@COLLECTIONS = [ :templates, :clusters, :storagedomains, :vms, :datacenters ]
 
     def initialize(username, password, base_uri, opts={})
-      @logger = opts[:verbose] ? Logger.new(STDERR) : []
       @username, @password = username, password
-      URI.parse(base_uri)
+      uri = URI.parse(base_uri)
+      @host = "#{uri.scheme}://#{uri.host}:#{uri.port}"
       @base_uri = base_uri
       @entry_points = {}
       discover_entry_points()
@@ -30,14 +26,14 @@ module RHEVM
     def method_missing(method_name, *args)
       opts = args[0] if args[0].class.eql?(Hash)
       opts ||= {}
-      puts opts.inspect
       if @@COLLECTIONS.include?(method_name.to_sym)
         if opts[:id]
-          object = Nokogiri::XML(get("%s/%s" % @entry_points[method_name.to_s], opts[:id]))
+          puts "[GET] #{@entry_points[method_name.to_s]}#{opts[:id]}"
+          object = Nokogiri::XML(get("#{@entry_points[method_name.to_s]}#{opts[:id]}"))
           element = method_name.to_s
           element = 'data_centers' if method_name.eql?(:datacenters)
+          puts ">>>> #{element}"
           inst = RHEVM.const_get(element.classify)
-          puts inst.inspect
           return inst::new(self, object)
         else
           objects = Nokogiri::XML(get(@entry_points[method_name.to_s]))
@@ -57,34 +53,55 @@ module RHEVM
     end
 
     def vm_action(action, vm)
-      Nokogiri::XML(post("/vms/%s/" % action.to_s))
+      response = post("#{@base_uri}/vms/#{vm}/%s" % action)
+      Nokogiri::XML(response)
+    end
+
+    def create_vm(opts="")
+      Nokogiri::XML(post("#{@base_uri}/vms", opts))
+    end
+
+    def delete_vm(id)
+      delete("#{@base_uri}/vms/#{id}")
     end
 
     protected
 
+    def delete(uri)
+      headers = {
+        :authorization => "Basic " + Base64.encode64("#{@username}:#{@password}"),
+        :accept => 'application/xml',
+      }
+      RestClient.delete(uri, headers).to_s
+    end
+
     def get(uri)
       headers = {
         :authorization => "Basic " + Base64.encode64("#{@username}:#{@password}"),
-        :accept => "application/xml"
+        :accept => 'application/xml',
       }
-      @logger << "GET #{uri}\n"
-      RestClient.get(uri, headers).to_s()
+      response = RestClient.get(uri, headers).to_s
+      puts "=====\n#{response}\n====="
+      response
     end
 
-    def post(uri, params={})
+    def post(uri, params="")
       headers = {
         :authorization => "Basic " + Base64.encode64("#{@username}:#{@password}"),
-        :accept => "application/xml"
+        :accept => 'application/xml',
+        :content_type => 'application/xml'
       }
-      @logger << "POST #{uri}\n"
-      RestClient.post(uri, params, headers).to_s
+      params = "<action/>" if params.size==0
+      response = RestClient.post(uri, params, headers).to_s
+      puts "=====\n#{response}\n====="
+      response
     end
 
     def discover_entry_points()
       return if @discovered
       doc = Nokogiri.XML(get(@base_uri))
       doc.xpath('api/link').each() do |link|
-        @entry_points[link['rel']] = @base_uri + link['href']
+        @entry_points[link['rel']] = @host + link['href']
       end
       @discovered = true
     end
@@ -101,10 +118,9 @@ module RHEVM
     attr_accessor(:id, :href, :name)
 
     def initialize(client, xml)
-      client.logger << "#{xml}\n"
       @client = client
       @id = xml[:id]
-      @href = @client.base_uri + xml[:href]
+      @href = "#{@client.base_uri}#{xml[:href]}"
       @name = xml.xpath('name').text
     end
   end
@@ -132,8 +148,8 @@ module RHEVM
       @status = xml.xpath('status').text
       @memory = xml.xpath('memory').text
       @profile = xml.xpath('type').text
-      @sockets = xml.xpath('cpu/topology').first[:sockets]
-      @cores = xml.xpath('cpu/topology').first[:cores]
+      @sockets = xml.xpath('cpu/topology').first[:sockets] rescue ''
+      @cores = xml.xpath('cpu/topology').first[:cores] rescue ''
       @bootdevs = []
       xml.xpath('os/boot').each do |boot|
         @bootdevs << boot[:dev]
@@ -141,7 +157,7 @@ module RHEVM
       @host = xml.xpath('host')[:id]
       @cluster = xml.xpath('cluster').first[:id]
       @template = xml.xpath('template').first[:id]
-      @vmpool = xml.xpath('vmpool').first[:id]
+      @vmpool = xml.xpath('vmpool').first[:id] if xml.xpath('vmpool').size >0
       @creation_time = xml.xpath('creation_time').text
     end
   end
-- 
1.7.3.3


[PATCH core 1/2] Initial import of RHEV REST API driver

Posted by mf...@redhat.com.
From: Michal Fojtik <mf...@redhat.com>

---
 .../lib/deltacloud/drivers/rhevm2/rhevm2_driver.rb |  231 ++++++++++++++++++++
 .../lib/deltacloud/drivers/rhevm2/rhevm_client.rb  |  217 ++++++++++++++++++
 server/lib/drivers.rb                              |    1 +
 3 files changed, 449 insertions(+), 0 deletions(-)
 create mode 100644 server/lib/deltacloud/drivers/rhevm2/rhevm2_driver.rb
 create mode 100644 server/lib/deltacloud/drivers/rhevm2/rhevm_client.rb

diff --git a/server/lib/deltacloud/drivers/rhevm2/rhevm2_driver.rb b/server/lib/deltacloud/drivers/rhevm2/rhevm2_driver.rb
new file mode 100644
index 0000000..f1655d1
--- /dev/null
+++ b/server/lib/deltacloud/drivers/rhevm2/rhevm2_driver.rb
@@ -0,0 +1,231 @@
+#
+# Copyright (C) 2009  Red Hat, Inc.
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.  The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+require 'deltacloud/base_driver'
+require 'deltacloud/drivers/rhevm2/rhevm_client'
+
+module Deltacloud
+  module Drivers
+    module RHEVM2
+
+class RHEVM2Driver < Deltacloud::BaseDriver
+
+  feature :instances, :user_name
+
+  # FIXME: These values are just for ilustration
+  # Also I choosed 'SERVER' and 'DESKTOP' names
+  # because they are referred by VM (API type)
+  #
+  # Values like RAM or STORAGE are reported by VM
+  # so they are not static.
+
+  define_hardware_profile 'SERVER' do
+    cpu         1
+    memory      [ 1*1024 .. 32*1024 ]
+    architecture 'x86_64'
+  end
+
+  define_hardware_profile 'DESKTOP' do
+    cpu         1
+    memory      [ 1*1024 .. 32*1024 ]
+    architecture 'x86_64'
+  end
+
+  # Instead of setting a URL for RHEV provider
+  # do it here in driver, so it can be altered by HTTP headers
+  #
+  def provider_uri=(uri)
+    @RHEVM_URI = uri
+  end
+
+  def provider_uri
+    # 'http://localhost:8099/rhevm-api-mock/' || @RHEVM_URI
+    'http://localhost:8080'
+  end
+
+  # FIX: This is state table from EC2, fix it pls to 
+  # match RHEV-M model
+  #
+  define_instance_states do
+    start.to( :pending )          .automatically
+    pending.to( :running )        .automatically
+    pending.to( :stopping )       .on( :stop )
+    pending.to( :stopped )        .automatically
+    stopped.to( :running )        .on( :start )
+    running.to( :running )        .on( :reboot )
+    running.to( :stopping )       .on( :stop )
+    shutting_down.to( :stopped )  .automatically
+    stopped.to( :finish )         .automatically
+  end
+
+  #
+  # Realms
+  #
+
+  def realms(credentials, opts=nil)
+    client = new_client(credentials)
+    realm_arr = []
+    safely do
+      datacenters = client.datacenters
+      datacenters.each do |d|
+        realm_arr << convert_realm(d)
+      end
+    end
+    realm_arr
+  end
+
+  def images(credentials, opts={})
+    client = new_client(credentials)
+    img_arr = []
+    safely do
+      templates = client.templates
+      if (!opts.nil? && opts[:id])
+        templates = templates.select{|t| opts[:id] == t.id}
+      end
+      templates.each do |t|
+        img_arr << convert_image(t)
+      end
+    end
+    img_arr = filter_on( img_arr, :architecture, opts )
+    img_arr.sort_by{|e| [e.owner_id, e.name]}
+  end
+
+  def instances(credentials, opts={})
+    client = new_client(credentials)
+    inst_arr = []
+    safely do
+      vms = client.vms
+      vms.each do |vm|
+        inst_arr << convert_instance(client, vm)
+      end
+    end
+    inst_arr = filter_on( inst_arr, :id, opts )
+    filter_on( inst_arr, :state, opts )
+  end
+
+  def reboot_instance(credentials, id)
+    client = new_client(credentials)
+    safely do
+      client.vm_action(:reboot, id)
+    end
+  end
+
+  def stop_instance(credentials, id)
+    client = new_client(credentials)
+    safely do
+      client.vm_action(:suspend, id)
+    end
+  end
+
+  def start_instance(credentials, id)
+    client = new_client(credentials)
+    safely do
+      client.vm_action(:start, id)
+    end
+  end
+
+  def shutdown_instance(credentials, id)
+    client = new_client(credentials)
+    safely do
+      client.vm_action(:shutdown, id)
+    end
+  end
+
+  private
+
+  def new_client(credentials)
+    opts = {:verbose => true}
+    RHEVM::Client.new(credentials.user, credentials.password, provider_uri, opts)
+  end
+
+  # I can't test this method, since 'cluster' is not a part of Mock driver
+  def get_realm_from_cluster(client, cluster_id)
+    cluster = client.clusters(:id => cluster_id)
+    datacenter = client.datacenter(:id => cluster.datacenter_id)
+    convert_realm(datacenter)
+  end
+
+  def convert_instance(client, inst)
+    # FIXME: Remove 'DOWN', it's used just because RHEV Mock driver
+    #        has no 'state' property in XML.
+    #
+    # FIXME2: This call will now throw 500 because I'm unable to fetch realm
+    # for instance. 
+    #
+    state = convert_state(inst.status=='' ? 'DOWN' : inst.status)
+    Instance.new(
+      :id => inst.id,
+      :name => inst.name,
+      :state => state,
+      :image_id => inst.template,
+      # Realm aka DataCenter could be retrieved from Cluster
+      :realm_id => get_realm_from_cluster(client, inst.cluster),
+      :owner_id => 'self',
+      :launch_time => inst.creation_time,
+      :instance_profile => InstanceProfile::new(inst.profile),
+      :hardware_profile_id => inst.profile,
+      :actions=>instance_actions_for( state )
+    )
+  end
+
+  # STATES: 
+  #
+  # UNASSIGNED, DOWN, UP, POWERING_UP, POWERED_DOWN, PAUSED, MIGRATING_FROM, MIGRATING_TO, 
+  # UNKNOWN, NOT_RESPONDING, WAIT_FOR_LAUNCH, REBOOT_IN_PROGRESS, SAVING_STATE, RESTORING_STATE, 
+  # SUSPENDED, IMAGE_ILLEGAL, IMAGE_LOCKED or POWERING_DOWN 
+  #
+  # FIXME: This will need to be fixed regarding to state-machine
+  def convert_state(state)
+    case state
+    when 'POWERING_UP', 'WAIT_FOR_LAUNCH', 'REBOOT_IN_PROGRESS', 'SAVING_STATE',
+      'RESTORING_STATE', 'POWERING_DOWN' then
+      'PENDING'
+    when 'UNASSIGNED', 'DOWN', 'POWERING_DOWN', 'PAUSED', 'NOT_RESPONDING', 'SAVING_STATE', 
+      'SUSPENDED', 'IMAGE_ILLEGAL', 'IMAGE_LOCKED', 'UNKNOWN' then
+      'STOPPED'
+    when 'UP', 'MIGRATING_TO', 'MIGRATING_FROM'
+      'RUNNING'
+    end
+  end
+
+  def convert_image(img)
+    Image.new(
+      :id => img.id,
+      :name => img.name,
+      :description => img.description,
+      :owner_id => 'self',
+      :architecture => 'x86_64', # All RHEV-M VMs are x86_64
+      :status => img.status
+    )
+  end
+
+  def convert_realm(realm)
+    Realm.new(
+      :id => realm.id,
+      :name => realm.name,
+      :state => 'AVAILABLE',
+      :limit => :unlimited
+    )
+  end
+
+
+end
+
+    end
+  end
+end
diff --git a/server/lib/deltacloud/drivers/rhevm2/rhevm_client.rb b/server/lib/deltacloud/drivers/rhevm2/rhevm_client.rb
new file mode 100644
index 0000000..856844c
--- /dev/null
+++ b/server/lib/deltacloud/drivers/rhevm2/rhevm_client.rb
@@ -0,0 +1,217 @@
+require 'rubygems'
+
+#gem 'rest-client'
+
+require 'base64'
+require 'logger'
+require 'restclient'
+require 'nokogiri'
+
+module RHEVM
+
+  class Client
+    attr_reader :base_uri
+    attr_reader :entry_points
+    attr_reader :logger
+
+    # Define a list of supported collections which will be handled automatically
+    # by method_missing
+    @@COLLECTIONS = [ :templates, :clusters, :storagedomains, :vms, :datacenters ]
+
+    def initialize(username, password, base_uri, opts={})
+      @logger = opts[:verbose] ? Logger.new(STDERR) : []
+      @username, @password = username, password
+      URI.parse(base_uri)
+      @base_uri = base_uri
+      @entry_points = {}
+      discover_entry_points()
+    end
+
+    def method_missing(method_name, *args)
+      opts = args[0] if args[0].class.eql?(Hash)
+      opts ||= {}
+      puts opts.inspect
+      if @@COLLECTIONS.include?(method_name.to_sym)
+        if opts[:id]
+          object = Nokogiri::XML(get("%s/%s" % @entry_points[method_name.to_s], opts[:id]))
+          element = method_name.to_s
+          element = 'data_centers' if method_name.eql?(:datacenters)
+          inst = RHEVM.const_get(element.classify)
+          puts inst.inspect
+          return inst::new(self, object)
+        else
+          objects = Nokogiri::XML(get(@entry_points[method_name.to_s]))
+          objects_arr = []
+          element = method_name.to_s
+          # FIXME:
+          # This is an exception/or bug in RHEV-M API:
+          # (uri is /datacenters but root element it 'data_centers')
+          element = 'data_centers' if method_name.eql?(:datacenters)
+          (objects/"#{element}/#{element.singularize}").each do |item|
+            inst = RHEVM.const_get(element.classify)
+            objects_arr << inst.new(self, item)
+          end
+          return objects_arr
+        end
+      end
+    end
+
+    def vm_action(action, vm)
+      Nokogiri::XML(post("/vms/%s/" % action.to_s))
+    end
+
+    protected
+
+    def get(uri)
+      headers = {
+        :authorization => "Basic " + Base64.encode64("#{@username}:#{@password}"),
+        :accept => "application/xml"
+      }
+      @logger << "GET #{uri}\n"
+      RestClient.get(uri, headers).to_s()
+    end
+
+    def post(uri, params={})
+      headers = {
+        :authorization => "Basic " + Base64.encode64("#{@username}:#{@password}"),
+        :accept => "application/xml"
+      }
+      @logger << "POST #{uri}\n"
+      RestClient.post(uri, params, headers).to_s
+    end
+
+    def discover_entry_points()
+      return if @discovered
+      doc = Nokogiri.XML(get(@base_uri))
+      doc.xpath('api/link').each() do |link|
+        @entry_points[link['rel']] = @base_uri + link['href']
+      end
+      @discovered = true
+    end
+
+    private
+    
+    def singularize(str)
+      str.gsub(/s$/, '')
+    end
+
+  end
+
+  class BaseModel
+    attr_accessor(:id, :href, :name)
+
+    def initialize(client, xml)
+      client.logger << "#{xml}\n"
+      @client = client
+      @id = xml[:id]
+      @href = @client.base_uri + xml[:href]
+      @name = xml.xpath('name').text
+    end
+  end
+
+  class StorageDomain < BaseModel
+    attr_accessor(:type, :status, :master, :storage_type, :address, :path, :host)
+
+    def initialize(client, xml)
+      super(client, xml)
+      @type = xml.xpath('type').text
+      @status = xml.xpath('master').text
+      @storage_type = xml.xpath('storage/type')
+      @address = xml.xpath('storage/address').text
+      @path = xml.xpath('storage/path').text
+      @host = xml.xpath('host')[:id]
+    end
+  end
+
+  class Vm < BaseModel
+    attr_accessor(:status, :memory, :sockets, :cores, :bootdevs, :host, :cluster, :template, :vmpool, :profile)
+    attr_accessor(:creation_time)
+
+    def initialize(client, xml)
+      super(client, xml)
+      @status = xml.xpath('status').text
+      @memory = xml.xpath('memory').text
+      @profile = xml.xpath('type').text
+      @sockets = xml.xpath('cpu/topology').first[:sockets]
+      @cores = xml.xpath('cpu/topology').first[:cores]
+      @bootdevs = []
+      xml.xpath('os/boot').each do |boot|
+        @bootdevs << boot[:dev]
+      end
+      @host = xml.xpath('host')[:id]
+      @cluster = xml.xpath('cluster').first[:id]
+      @template = xml.xpath('template').first[:id]
+      @vmpool = xml.xpath('vmpool').first[:id]
+      @creation_time = xml.xpath('creation_time').text
+    end
+  end
+
+  class Template < BaseModel
+    attr_accessor(:status, :memory, :name, :description)
+    
+    def initialize(client, xml)
+      super(client, xml)
+      @status = (xml/'status').text
+      @memory = (xml/'memory').text
+      @description = (xml/'description').text
+    end
+  end
+
+  class DataCenter < BaseModel
+    attr_accessor :name, :storage_type, :description
+
+    def initialize(client, xml)
+      super(client, xml)
+      @name, @storage_type, @description = (xml/'name').text, (xml/'storage_type').text, (xml/'description').text
+    end
+  end
+
+  class Cluster < BaseModel
+    attr_accessor :name, :datacenter_id, :cpu
+
+    def initialize(client, xml)
+      super(client, xml)
+      @name = (xml/'name').text
+      @datacenter_id = (xml/'data_center').first['id']
+      @cpu = (xml/'cpu').first['id']
+      @name = (xml/'name').text
+    end
+  end
+
+end
+
+class String
+
+  unless method_defined?(:classify)
+    # Create a class name from string
+    def classify
+      self.singularize.camelize
+    end
+  end
+
+  unless method_defined?(:camelize)
+    # Camelize converts strings to UpperCamelCase
+    def camelize
+      self.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
+    end
+  end
+
+  unless method_defined?(:singularize)
+    # Strip 's' character from end of string
+    def singularize
+      self.gsub(/s$/, '')
+    end
+  end
+
+  # Convert string to float if string value seems like Float
+  def convert
+    return self.to_f if self.strip =~ /^([\d\.]+$)/
+    self
+  end
+
+  # Simply converts whitespaces and - symbols to '_' which is safe for Ruby
+  def sanitize
+    self.strip.gsub(/(\W+)/, '_')
+  end
+
+end
diff --git a/server/lib/drivers.rb b/server/lib/drivers.rb
index 420621c..a164f28 100644
--- a/server/lib/drivers.rb
+++ b/server/lib/drivers.rb
@@ -21,6 +21,7 @@ module Deltacloud
     :rackspace => { :name => "Rackspace" },
     :gogrid => { :name => "Gogrid" },
     :rhevm => { :name => "RHEVM" },
+    :rhevm2 => { :name => "RHEVM2" },
     :rimuhosting => { :name => "RimuHosting"},
     :opennebula => { :name => "Opennebula", :class => "OpennebulaDriver" },
     :terremark => { :name => "Terremark"},
-- 
1.7.3.3