You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@deltacloud.apache.org by ma...@redhat.com on 2012/02/14 14:25:30 UTC
OpenNebula driver for OpenNebula 3.x API
Patch 1/3 is the updated driver, contributed by Daniel Molina - https://issues.apache.org/jira/browse/DTACLOUD-140
Patch 2/3 is a minor edit to allow use of the 'API_PROVIDER' variable to maintain consistency across drivers (though if OCCI_URL is specified this will take precedence)
Patch 3/3 minor edit to hardware_profile html view to show cpu info.
marios
Re: OpenNebula driver for OpenNebula 3.x API
Posted by "marios@redhat.com" <ma...@redhat.com>.
On 16/02/12 16:57, Daniel Molina wrote:
> On 15 February 2012 10:16, marios@redhat.com <ma...@redhat.com> wrote:
>> On 15/02/12 10:58, Michal Fojtik wrote:
>>>
>>> On Feb 14, 2012, at 2:25 PM, marios@redhat.com wrote:
>>>
>>>>
>>>> Patch 1/3 is the updated driver, contributed by Daniel Molina - https://issues.apache.org/jira/browse/DTACLOUD-140
>>>> Patch 2/3 is a minor edit to allow use of the 'API_PROVIDER' variable to maintain consistency across drivers (though if OCCI_URL is specified this will take precedence)
>>>> Patch 3/3 minor edit to hardware_profile html view to show cpu info.
>>>
>>> ACK whole series. Good job Daniel and Marios!
>>
>> thanks - but I just reviewed it, Daniel did all of the work here
>>
>> pushing to git repo.
>
> Thanks.
>
> BTW, In this patch the reboot action was included, so I don't know if
> I have to send a patch to modify the supported providers table [1] or
> you can change it.
>
> [1] http://deltacloud.apache.org/index.html#providers
>
good catch - thanks - we'll sort it out,
marios
> Cheers
>
>>
>>>
>>> -- Michal
>>>
>>>>
>>>> marios
>>>
>>> ------------------------------------------------------
>>> Michal Fojtik, mfojtik@redhat.com
>>> Deltacloud API: http://deltacloud.org
>>>
>>
>
>
>
Re: OpenNebula driver for OpenNebula 3.x API
Posted by Daniel Molina <dm...@opennebula.org>.
On 15 February 2012 10:16, marios@redhat.com <ma...@redhat.com> wrote:
> On 15/02/12 10:58, Michal Fojtik wrote:
>>
>> On Feb 14, 2012, at 2:25 PM, marios@redhat.com wrote:
>>
>>>
>>> Patch 1/3 is the updated driver, contributed by Daniel Molina - https://issues.apache.org/jira/browse/DTACLOUD-140
>>> Patch 2/3 is a minor edit to allow use of the 'API_PROVIDER' variable to maintain consistency across drivers (though if OCCI_URL is specified this will take precedence)
>>> Patch 3/3 minor edit to hardware_profile html view to show cpu info.
>>
>> ACK whole series. Good job Daniel and Marios!
>
> thanks - but I just reviewed it, Daniel did all of the work here
>
> pushing to git repo.
Thanks.
BTW, In this patch the reboot action was included, so I don't know if
I have to send a patch to modify the supported providers table [1] or
you can change it.
[1] http://deltacloud.apache.org/index.html#providers
Cheers
>
>>
>> -- Michal
>>
>>>
>>> marios
>>
>> ------------------------------------------------------
>> Michal Fojtik, mfojtik@redhat.com
>> Deltacloud API: http://deltacloud.org
>>
>
--
Daniel Molina
Project Engineer
OpenNebula - The Open Source Solution for Data Center Virtualization
www.OpenNebula.org | dmolina@opennebula.org | @OpenNebula
Re: OpenNebula driver for OpenNebula 3.x API
Posted by "marios@redhat.com" <ma...@redhat.com>.
On 15/02/12 10:58, Michal Fojtik wrote:
>
> On Feb 14, 2012, at 2:25 PM, marios@redhat.com wrote:
>
>>
>> Patch 1/3 is the updated driver, contributed by Daniel Molina - https://issues.apache.org/jira/browse/DTACLOUD-140
>> Patch 2/3 is a minor edit to allow use of the 'API_PROVIDER' variable to maintain consistency across drivers (though if OCCI_URL is specified this will take precedence)
>> Patch 3/3 minor edit to hardware_profile html view to show cpu info.
>
> ACK whole series. Good job Daniel and Marios!
thanks - but I just reviewed it, Daniel did all of the work here
pushing to git repo.
>
> -- Michal
>
>>
>> marios
>
> ------------------------------------------------------
> Michal Fojtik, mfojtik@redhat.com
> Deltacloud API: http://deltacloud.org
>
Re: OpenNebula driver for OpenNebula 3.x API
Posted by Michal Fojtik <mf...@redhat.com>.
On Feb 14, 2012, at 2:25 PM, marios@redhat.com wrote:
>
> Patch 1/3 is the updated driver, contributed by Daniel Molina - https://issues.apache.org/jira/browse/DTACLOUD-140
> Patch 2/3 is a minor edit to allow use of the 'API_PROVIDER' variable to maintain consistency across drivers (though if OCCI_URL is specified this will take precedence)
> Patch 3/3 minor edit to hardware_profile html view to show cpu info.
ACK whole series. Good job Daniel and Marios!
-- Michal
>
> marios
------------------------------------------------------
Michal Fojtik, mfojtik@redhat.com
Deltacloud API: http://deltacloud.org
[PATCH 2/3] Minor edit to OpenNebula driver - allows use of API_PROVIDER environment variable.
Posted by ma...@redhat.com.
From: marios <ma...@redhat.com>
Can use either API_PROVIDER (or -P option to deltacloudd) or OCCI_URL environment variables
to specify location of the OpenNebula server. If both are set, OCCI_URL will take precendence.
Signed-off-by: marios <ma...@redhat.com>
---
.../deltacloud/drivers/opennebula/occi_client.rb | 9 ++-------
.../drivers/opennebula/opennebula_driver.rb | 2 +-
2 files changed, 3 insertions(+), 8 deletions(-)
diff --git a/server/lib/deltacloud/drivers/opennebula/occi_client.rb b/server/lib/deltacloud/drivers/opennebula/occi_client.rb
index 3eab5d7..574cb88 100644
--- a/server/lib/deltacloud/drivers/opennebula/occi_client.rb
+++ b/server/lib/deltacloud/drivers/opennebula/occi_client.rb
@@ -40,13 +40,8 @@ module OCCIClient
@timeout = timeout
# Server location
- if endpoint_str
- @endpoint = endpoint_str
- elsif ENV["OCCI_URL"]
- @endpoint = ENV["OCCI_URL"]
- else
- @endpoint = "http://localhost:4567"
- end
+ @endpoint = ENV["OCCI_URL"] || endpoint_str || Proc.new(raise "No OpenNebula Provider location configured! Client needs to set \'X-Deltacloud-Provider\' HTTP request header, OR, Deltacloud server administrator must set either the OCCI_URL or API_PROVIDER environment variables")
+#"http://localhost:4567"
# Autentication
if user && pass
diff --git a/server/lib/deltacloud/drivers/opennebula/opennebula_driver.rb b/server/lib/deltacloud/drivers/opennebula/opennebula_driver.rb
index 81a1290..3b00eca 100644
--- a/server/lib/deltacloud/drivers/opennebula/opennebula_driver.rb
+++ b/server/lib/deltacloud/drivers/opennebula/opennebula_driver.rb
@@ -219,7 +219,7 @@ class OpennebulaDriver < Deltacloud::BaseDriver
private
def new_client(credentials)
- OCCIClient::Client.new(nil, credentials.user, credentials.password, false)
+ OCCIClient::Client.new(api_provider, credentials.user, credentials.password, false)
end
--
1.7.6.5
[PATCH 1/3] Updated OpenNebula driver+client (and occi_client) for OpenNebula API v.3.x Contributed by Daniel Molina dmolina@opennebula.org
Posted by ma...@redhat.com.
From: marios <ma...@redhat.com>
Signed-off-by: marios <ma...@redhat.com>
---
.../deltacloud/drivers/opennebula/cloud_client.rb | 180 +++++----
.../deltacloud/drivers/opennebula/occi_client.rb | 433 +++++++++++++-------
.../drivers/opennebula/opennebula_driver.rb | 315 +++++++++------
3 files changed, 585 insertions(+), 343 deletions(-)
diff --git a/server/lib/deltacloud/drivers/opennebula/cloud_client.rb b/server/lib/deltacloud/drivers/opennebula/cloud_client.rb
index 00be869..283efd9 100644
--- a/server/lib/deltacloud/drivers/opennebula/cloud_client.rb
+++ b/server/lib/deltacloud/drivers/opennebula/cloud_client.rb
@@ -18,11 +18,30 @@
require 'rubygems'
require 'uri'
+require 'digest/sha1'
require 'net/https'
+require "rexml/document"
+
+begin
+ require 'rexml/formatters/pretty'
+ REXML_FORMATTERS=true
+rescue LoadError
+ REXML_FORMATTERS=false
+end
+
+begin
+ require 'curb'
+ CURL_LOADED=true
+rescue LoadError
+ CURL_LOADED=false
+end
+
begin
- require 'net/http/post/multipart'
+ require 'net/http/post/multipart'
+ MULTIPART_LOADED=true
rescue LoadError
+ MULTIPART_LOADED=false
end
###############################################################################
@@ -30,85 +49,100 @@ end
# Cloud Client
###############################################################################
module CloudClient
- # #########################################################################
- # Default location for the authentication file
- # #########################################################################
- DEFAULT_AUTH_FILE = ENV["HOME"]+"/.one/one_auth"
-
- # #########################################################################
- # Gets authorization credentials from ONE_AUTH or default
- # auth file.
- #
- # Raises an error if authorization is not found
- # #########################################################################
- def self.get_one_auth
- if ENV["ONE_AUTH"] and !ENV["ONE_AUTH"].empty? and
- File.file?(ENV["ONE_AUTH"])
- one_auth=File.read(ENV["ONE_AUTH"]).strip.split(':')
- elsif File.file?(DEFAULT_AUTH_FILE)
- one_auth=File.read(DEFAULT_AUTH_FILE).strip.split(':')
- else
- raise "No authorization data present"
- end
-
- raise "Authorization data malformed" if one_auth.length < 2
-
- one_auth
+ # #########################################################################
+ # Default location for the authentication file
+ # #########################################################################
+ DEFAULT_AUTH_FILE = ENV["HOME"]+"/.one/one_auth"
+
+ # #########################################################################
+ # Gets authorization credentials from ONE_AUTH or default
+ # auth file.
+ #
+ # Raises an error if authorization is not found
+ # #########################################################################
+ def self.get_one_auth
+ if ENV["ONE_AUTH"] and !ENV["ONE_AUTH"].empty? and
+ File.file?(ENV["ONE_AUTH"])
+ one_auth=File.read(ENV["ONE_AUTH"]).strip.split(':')
+ elsif File.file?(DEFAULT_AUTH_FILE)
+ one_auth=File.read(DEFAULT_AUTH_FILE).strip.split(':')
+ else
+ raise "No authorization data present"
end
- # #########################################################################
- # Starts an http connection and calls the block provided. SSL flag
- # is set if needed.
- # #########################################################################
- def self.http_start(url, &block)
- http = Net::HTTP.new(url.host, url.port)
- if url.scheme=='https'
- http.use_ssl = true
- http.verify_mode=OpenSSL::SSL::VERIFY_NONE
- end
-
- begin
- http.start do |connection|
- block.call(connection)
- end
- rescue Errno::ECONNREFUSED => e
- str = "Error connecting to server (#{e.to_s})."
- str << "Server: #{url.host}:#{url.port}"
-
- return CloudClient::Error.new(str)
- end
+ raise "Authorization data malformed" if one_auth.length < 2
+
+ one_auth
+ end
+
+ # #########################################################################
+ # Starts an http connection and calls the block provided. SSL flag
+ # is set if needed.
+ # #########################################################################
+ def self.http_start(url, timeout, &block)
+ http = Net::HTTP.new(url.host, url.port)
+
+ if timeout
+ http.read_timeout = timeout.to_i
end
- # #########################################################################
- # The Error Class represents a generic error in the Cloud Client
- # library. It contains a readable representation of the error.
- # #########################################################################
- class Error
- attr_reader :message
-
- # +message+ a description of the error
- def initialize(message=nil)
- @message=message
- end
-
- def to_s()
- @message
- end
+ if url.scheme=='https'
+ http.use_ssl = true
+ http.verify_mode=OpenSSL::SSL::VERIFY_NONE
end
- # #########################################################################
- # Returns true if the object returned by a method of the OpenNebula
- # library is an Error
- # #########################################################################
- def self.is_error?(value)
- value.class==CloudClient::Error
+ begin
+ res = http.start do |connection|
+ block.call(connection)
+ end
+ rescue Errno::ECONNREFUSED => e
+ str = "Error connecting to server (#{e.to_s}).\n"
+ str << "Server: #{url.host}:#{url.port}"
+
+ return CloudClient::Error.new(str,"503")
+ rescue Errno::ETIMEDOUT => e
+ str = "Error timeout connecting to server (#{e.to_s}).\n"
+ str << "Server: #{url.host}:#{url.port}"
+
+ return CloudClient::Error.new(str,"504")
+ rescue Timeout::Error => e
+ str = "Error timeout while connected to server (#{e.to_s}).\n"
+ str << "Server: #{url.host}:#{url.port}"
+
+ return CloudClient::Error.new(str,"504")
end
-end
-# Command line help functions
-module CloudCLI
- # Returns the command name
- def cmd_name
- File.basename($0)
+ if res.is_a?(Net::HTTPSuccess)
+ res
+ else
+ CloudClient::Error.new(res.body, res.code)
end
+ end
+
+ # #########################################################################
+ # The Error Class represents a generic error in the Cloud Client
+ # library. It contains a readable representation of the error.
+ # #########################################################################
+ class Error
+ attr_reader :message
+ attr_reader :code
+
+ # +message+ a description of the error
+ def initialize(message=nil, code="500")
+ @message=message
+ @code=code
+ end
+
+ def to_s()
+ @message
+ end
+ end
+
+ # #########################################################################
+ # Returns true if the object returned by a method of the OpenNebula
+ # library is an Error
+ # #########################################################################
+ def self.is_error?(value)
+ value.class==CloudClient::Error
+ end
end
diff --git a/server/lib/deltacloud/drivers/opennebula/occi_client.rb b/server/lib/deltacloud/drivers/opennebula/occi_client.rb
index 533eb94..3eab5d7 100644
--- a/server/lib/deltacloud/drivers/opennebula/occi_client.rb
+++ b/server/lib/deltacloud/drivers/opennebula/occi_client.rb
@@ -16,187 +16,330 @@
#
require 'rubygems'
-require 'uri'
require 'rexml/document'
+require 'uri'
require 'deltacloud/drivers/opennebula/cloud_client'
module OCCIClient
- #####################################################################
- # Client Library to interface with the OpenNebula OCCI Service
- #####################################################################
- class Client
-
- ######################################################################
- # Initialize client library
- ######################################################################
- def initialize(endpoint_str=nil, user=nil, pass=nil, debug_flag=true)
- @debug = debug_flag
-
- # Server location
- if endpoint_str
- @endpoint = endpoint_str
- elsif ENV["OCCI_URL"]
- @endpoint = ENV["OCCI_URL"]
- else
- @endpoint = "http://localhost:4567"
- end
-
- # Autentication
- if user && pass
- @occiauth = [user, pass]
- else
- @occiauth = CloudClient::get_one_auth
- end
-
- if !@occiauth
- raise "No authorization data present"
- end
-
- @occiauth[1] = Digest::SHA1.hexdigest(@occiauth[1])
- end
+ #####################################################################
+ # Client Library to interface with the OpenNebula OCCI Service
+ #####################################################################
+ class Client
+
+ attr_accessor :endpoint
+
+ ######################################################################
+ # Initialize client library
+ ######################################################################
+ def initialize(endpoint_str=nil, user=nil, pass=nil,
+ timeout=nil, debug_flag=true)
+ @debug = debug_flag
+ @timeout = timeout
+
+ # Server location
+ if endpoint_str
+ @endpoint = endpoint_str
+ elsif ENV["OCCI_URL"]
+ @endpoint = ENV["OCCI_URL"]
+ else
+ @endpoint = "http://localhost:4567"
+ end
+
+ # Autentication
+ if user && pass
+ @occiauth = [user, pass]
+ else
+ @occiauth = CloudClient::get_one_auth
+ end
+
+ if !@occiauth
+ raise "No authorization data present"
+ end
+
+ @occiauth[1] = Digest::SHA1.hexdigest(@occiauth[1])
+ end
- #################################
- # Pool Resource Request Methods #
- #################################
+ #################################
+ # Pool Resource Request Methods #
+ #################################
- ######################################################################
- # Post a new VM to the VM Pool
- # :instance_type
- # :xmlfile
- ######################################################################
- def post_vms(xmlfile)
- xml=File.read(xmlfile)
+ def get_root
+ get('/')
+ end
+
+ ######################################################################
+ # Retieves the available Instance types
+ ######################################################################
+ def get_instance_types
+ get('/instance_type')
+ end
- url = URI.parse(@endpoint+"/compute")
+ ######################################################################
+ # Post a new VM to the VM Pool
+ # :xmlfile
+ ######################################################################
+ def post_vms(xmlfile)
+ post('/compute', xmlfile)
+ end
- req = Net::HTTP::Post.new(url.path)
- req.body=xml
+ ######################################################################
+ # Retieves the pool of Virtual Machines
+ ######################################################################
+ def get_vms
+ get('/compute')
+ end
- req.basic_auth @occiauth[0], @occiauth[1]
+ ######################################################################
+ # Post a new Network to the VN Pool
+ # :xmlfile xml description of the Virtual Network
+ ######################################################################
+ def post_network(xmlfile)
+ post('/network', xmlfile)
+ end
- res = CloudClient::http_start(url) do |http|
- http.request(req)
- end
+ ######################################################################
+ # Retieves the pool of Virtual Networks
+ ######################################################################
+ def get_networks
+ get('/network')
+ end
- if CloudClient::is_error?(res)
- return res
- else
- return res.body
- end
- end
+ ######################################################################
+ # Post a new Image to the Image Pool
+ # :xmlfile
+ ######################################################################
+ def post_image(xmlfile, curb=true)
+ xml = File.read(xmlfile)
- ######################################################################
- # Retieves the pool of Virtual Machines
- ######################################################################
- def get_vms
- url = URI.parse(@endpoint+"/compute")
- req = Net::HTTP::Get.new(url.path)
+ begin
+ image_info = REXML::Document.new(xml).root
+ rescue Exception => e
+ error = CloudClient::Error.new(e.message)
+ return error
+ end
- req.basic_auth @occiauth[0], @occiauth[1]
+ if image_info.elements['URL']
+ file_path = image_info.elements['URL'].text
- res = CloudClient::http_start(url) {|http|
- http.request(req)
- }
+ m = file_path.match(/^\w+:\/\/(.*)$/)
- if CloudClient::is_error?(res)
- return res
- else
- return res.body
- end
+ if m
+ file_path="/"+m[1]
end
+ elsif !image_info.elements['TYPE'] == "DATABLOCK"
+ return CloudClient::Error.new("Can not find URL")
+ end
+
+ if curb
+ if !CURL_LOADED
+ error_msg = "curb gem not loaded"
+ error = CloudClient::Error.new(error_msg)
+ return error
+ end
+
+ curl=Curl::Easy.new(@endpoint+"/storage")
- ######################################################################
- # Retieves the pool of Images owned by the user
- ######################################################################
- def get_images
- url = URI.parse(@endpoint+"/storage")
- req = Net::HTTP::Get.new(url.path)
+ curl.http_auth_types = Curl::CURLAUTH_BASIC
+ curl.userpwd = "#{@occiauth[0]}:#{@occiauth[1]}"
+ curl.verbose = true if @debug
+ curl.multipart_form_post = true
- req.basic_auth @occiauth[0], @occiauth[1]
+ begin
+ postfields = Array.new
+ postfields << Curl::PostField.content('occixml', xml)
- res = CloudClient::http_start(url) {|http|
- http.request(req)
- }
+ if file_path
+ postfields << Curl::PostField.file('file', file_path)
+ end
- if CloudClient::is_error?(res)
- return res
- else
- return res.body
- end
+ curl.http_post(*postfields)
+ rescue Exception => e
+ return CloudClient::Error.new(e.message)
end
- ####################################
- # Entity Resource Request Methods #
- ####################################
+ return curl.body_str
+ else
+ if !MULTIPART_LOADED
+ error_msg = "multipart-post gem not loaded"
+ error = CloudClient::Error.new(error_msg)
+ return error
+ end
- ######################################################################
- # :id VM identifier
- ######################################################################
- def get_vm(id)
- url = URI.parse(@endpoint+"/compute/" + id.to_s)
- req = Net::HTTP::Get.new(url.path)
+ params=Hash.new
- req.basic_auth @occiauth[0], @occiauth[1]
+ if file_path
+ file=File.open(file_path)
+ params["file"]=UploadIO.new(file,
+ 'application/octet-stream', file_path)
+ end
- res = CloudClient::http_start(url) {|http|
- http.request(req)
- }
+ params['occixml'] = xml
- if CloudClient::is_error?(res)
- return res
- else
- return res.body
- end
+ url = URI.parse(@endpoint+"/storage")
+
+ req = Net::HTTP::Post::Multipart.new(url.path, params)
+
+ req.basic_auth @occiauth[0], @occiauth[1]
+
+ res = CloudClient::http_start(url, @timeout) do |http|
+ http.request(req)
end
- ######################################################################
- # Puts a new Compute representation in order to change its state
- # :xmlfile Compute OCCI xml representation
- ######################################################################
- def put_vm(xmlfile)
- xml=File.read(xmlfile)
- vm_info=REXML::Document.new(xml).root.elements
+ file.close if file_path
- url = URI.parse(@endpoint+'/compute/' + vm_info['ID'].text)
+ if CloudClient::is_error?(res)
+ return res
+ else
+ return res.body
+ end
+ end
+ end
- req = Net::HTTP::Put.new(url.path)
- req.body = xml
+ ######################################################################
+ # Retieves the pool of Images owned by the user
+ ######################################################################
+ def get_images
+ get('/storage')
+ end
- req.basic_auth @occiauth[0], @occiauth[1]
- res = CloudClient::http_start(url) do |http|
- http.request(req)
- end
+ ####################################
+ # Entity Resource Request Methods #
+ ####################################
- if CloudClient::is_error?(res)
- return res
- else
- return res.body
- end
- end
+ ######################################################################
+ # :id VM identifier
+ ######################################################################
+ def get_vm(id)
+ get('/compute/'+id.to_s)
+ end
- #######################################################################
- # Retieves an Image
- # :image_uuid Image identifier
- ######################################################################
- def get_image(image_uuid)
- url = URI.parse(@endpoint+"/storage/"+image_uuid)
- req = Net::HTTP::Get.new(url.path)
-
- req.basic_auth @occiauth[0], @occiauth[1]
-
- res = CloudClient::http_start(url) {|http|
- http.request(req)
- }
-
- if CloudClient::is_error?(res)
- return res
- else
- return res.body
- end
- end
+ ######################################################################
+ # Puts a new Compute representation in order to change its state
+ # :xmlfile Compute OCCI xml representation
+ ######################################################################
+ def put_vm(xmlfile)
+ put('/compute/', xmlfile)
+ end
+
+ ####################################################################
+ # :id Compute identifier
+ ####################################################################
+ def delete_vm(id)
+ delete('/compute/'+id.to_s)
+ end
+
+ ######################################################################
+ # Retrieves a Virtual Network
+ # :id Virtual Network identifier
+ ######################################################################
+ def get_network(id)
+ get('/network/'+id.to_s)
+ end
+
+ ######################################################################
+ # Puts a new Network representation in order to change its state
+ # :xmlfile Network OCCI xml representation
+ ######################################################################
+ def put_network(xmlfile)
+ put('/network/', xmlfile)
+ end
+
+ ######################################################################
+ # :id VM identifier
+ ######################################################################
+ def delete_network(id)
+ delete('/network/'+id.to_s)
+ end
+
+ #######################################################################
+ # Retieves an Image
+ # :image_uuid Image identifier
+ ######################################################################
+ def get_image(id)
+ get('/storage/'+id.to_s)
+ end
+
+ ######################################################################
+ # Puts a new Storage representation in order to change its state
+ # :xmlfile Storage OCCI xml representation
+ ######################################################################
+ def put_image(xmlfile)
+ put('/storage/', xmlfile)
+ end
+
+ ######################################################################
+ # :id VM identifier
+ ######################################################################
+ def delete_image(id)
+ delete('/storage/'+id.to_s)
+ end
+
+ private
+
+ def get(path)
+ url = URI.parse(@endpoint+path)
+ req = Net::HTTP::Get.new(url.path)
+
+ do_request(url, req)
+ end
+
+ def post(path, xmlfile)
+ url = URI.parse(@endpoint+path)
+ req = Net::HTTP::Post.new(url.path)
+
+ req.body=File.read(xmlfile)
+
+ do_request(url, req)
+ end
+
+ def delete(path, xmlfile)
+ url = URI.parse(@endpoint+path)
+ req = Net::HTTP::Delete.new(url.path)
+
+ do_request(url, req)
+ end
+
+ def put(path, xmlfile)
+ xml = File.read(xmlfile)
+
+ # Get ID from XML
+ begin
+ info = REXML::Document.new(xml).root
+ rescue Exception => e
+ error = CloudClient::Error.new(e.message)
+ return error
+ end
+
+ if info.elements['ID'] == nil
+ return CloudClient::Error.new("Can not find RESOURCE ID")
+ end
+
+ resource_id = info.elements['ID'].text
+
+ url = URI.parse(@endpoint+path + resource_id)
+ req = Net::HTTP::Put.new(url.path)
+
+ req.body = xml
+
+ do_request(url, req)
+ end
+
+ def do_request(url, req)
+ req.basic_auth @occiauth[0], @occiauth[1]
+
+ res = CloudClient::http_start(url, @timeout) do |http|
+ http.request(req)
+ end
+
+ if CloudClient::is_error?(res)
+ return res
+ else
+ return res.body
+ end
end
+ end
end
diff --git a/server/lib/deltacloud/drivers/opennebula/opennebula_driver.rb b/server/lib/deltacloud/drivers/opennebula/opennebula_driver.rb
index e4f8f4d..81a1290 100644
--- a/server/lib/deltacloud/drivers/opennebula/opennebula_driver.rb
+++ b/server/lib/deltacloud/drivers/opennebula/opennebula_driver.rb
@@ -16,229 +16,294 @@
#
require 'deltacloud/base_driver'
+require 'deltacloud/drivers/opennebula/occi_client'
require 'erb'
+# TBD Nokogiri support
require 'rexml/document'
-path = File.dirname(__FILE__)
-$: << path
-
-require 'occi_client'
-
module Deltacloud
module Drivers
module Opennebula
class OpennebulaDriver < Deltacloud::BaseDriver
- feature :instances, :user_name
+ def supported_collections
+ DEFAULT_COLLECTIONS - [:storage_volumes, :storage_snapshots]
+ end
######################################################################
# Hardware profiles
- ######################################################################
-
- define_hardware_profile 'small'
-
- define_hardware_profile 'medium'
+ #####################################################################
+ def hardware_profiles(credentials, opts=nil)
+ occi_client = new_client(credentials)
+ xml = occi_client.get_instance_types
+ if CloudClient.is_error?(xml)
+ # OpenNebula 3.0 support
+ @hardware_profiles = ['small','medium','large'].map {|name|
+ ::Deltacloud::HardwareProfile.new(name)
+ }
+ else
+ # OpenNebula 3.2 support
+ @hardware_profiles = REXML::Document.new(xml).root.elements.map {|d|
+ elem = d.elements
+ ::Deltacloud::HardwareProfile.new(elem['NAME'].text) {
+ cpu elem['CPU']
+ memory elem['MEMORY']
+ storage elem['STORAGE']
+ architecture elem['ARCHITECTURE']
+ }
+ }
+ end
- define_hardware_profile 'large'
+ filter_hardware_profiles(@hardware_profiles, opts)
+ end
######################################################################
# Realms
######################################################################
(REALMS = [
- Realm.new( {
- :id=>'Any id',
- :name=>'Any name',
- :limit=>:unlimited,
- :state=>'Any state',
- } ),
+ Realm.new( {
+ :id=>'ONE',
+ :name=>'Opennebula',
+ :limit=>:unlimited,
+ :state=>'AVAILABLE',
+ } ),
] ) unless defined?( REALMS )
def realms(credentials, opts=nil)
- return REALMS if ( opts.nil? )
- results = REALMS
- results = filter_on( results, :id, opts )
- results
+ return REALMS if ( opts.nil? )
+ results = REALMS
+ results = filter_on( results, :id, opts )
+ results
end
######################################################################
# Images
######################################################################
-
def images(credentials, opts=nil)
- occi_client = new_client(credentials)
+ occi_client = new_client(credentials)
- images = []
- imagesxml = occi_client.get_images
+ xml = treat_response(occi_client.get_images)
- storage = REXML::Document.new(imagesxml)
- storage.root.elements.each do |d|
- id = d.attributes['href'].split("/").last
-
- diskxml = occi_client.get_image(id)
+ # TBD Add extended info in the pool
+ images = REXML::Document.new(xml).root.elements.map do |d|
+ im_id = d.attributes['href'].split("/").last
+ storage = treat_response(occi_client.get_image(im_id))
+ convert_image(storage, credentials)
+ end
+ end
- images << convert_image(diskxml.to_s(), credentials)
- end
- images
+ def image(credentials, opts=nil)
+ occi_client = new_client(credentials)
+ xml = treat_response(occi_client.get_image(opts[:id]))
+ convert_image(xml, credentials)
end
+ def destroy_image(credentials, id)
+ occi_client = new_client(credentials)
+ treat_response(occi_client.delete_image(opts[:id]))
+ end
######################################################################
# Instances
######################################################################
- define_instance_states do
- start.to( :pending ) .on( :create )
-
- pending.to( :running ) .automatically
-
- running.to( :stopped ) .on( :stop )
+ feature :instances, :user_name
+ # TBD Add Context to the VMs
+
+ OCCI_VM = %q{
+ <COMPUTE>
+ <% if opts[:name] %>
+ <NAME><%=opts[:name]%></NAME>
+ <% end %>
+ <INSTANCE_TYPE><%= opts[:hwp_id] || 'small' %></INSTANCE_TYPE>
+ <DISK>
+ <STORAGE href="<%= storage_href %>" />
+ </DISK>
+ </COMPUTE>
+ }
+
+ OCCI_ACTION = %q{
+ <COMPUTE>
+ <ID><%= id %></ID>
+ <STATE><%= strstate %></STATE>
+ </COMPUTE>
+ }
+
+ VM_STATES = {
+ "INIT" => "START",
+ "PENDING" => "PENDING",
+ "HOLD" => "STOPPED",
+ "ACTIVE" => "RUNNING",
+ "STOPPED" => "STOPPED",
+ "SUSPENDED" => "STOPPED",
+ "DONE" => "FINISHED",
+ "FAILED" => "FINISHED"
+ }
- stopped.to( :running ) .on( :start )
- stopped.to( :finish ) .on( :destroy )
+ define_instance_states do
+ start.to(:pending) .on( :create )
+ pending.to(:running) .automatically
+ stopped.to(:running) .on( :start )
+ running.to(:running) .on( :reboot )
+ running.to(:stopping) .on( :stop )
+ stopping.to(:stopped) .automatically
+ running.to(:shutting_down) .on( :destroy )
+ shutting_down.to(:finish) .automatically
end
-
def instances(credentials, opts=nil)
- occi_client = new_client(credentials)
-
- instances = []
- instancesxml = occi_client.get_vms
+ occi_client = new_client(credentials)
- computes = REXML::Document.new(instancesxml)
- computes.root.elements.each do |d|
- vm_id = d.attributes['href'].split("/").last
+ xml = treat_response(occi_client.get_vms)
- computexml = occi_client.get_vm(vm_id)
+ # TBD Add extended info in the pool
+ instances = REXML::Document.new(xml).root.elements.map do |d|
+ vm_id = d.attributes['href'].split("/").last
+ computexml = treat_response(occi_client.get_vm(vm_id))
+ convert_instance(computexml, credentials)
+ end
- instances << convert_instance(computexml.to_s(), credentials)
- end
- instances = filter_on( instances, :id, opts )
- instances = filter_on( instances, :state, opts )
- instances
+ instances = filter_on( instances, :state, opts )
end
+ def instance(credentials, opts=nil)
+ occi_client = new_client(credentials)
+ xml = treat_response(occi_client.get_vm(opts[:id]))
+ convert_instance(xml, credentials)
+ end
def create_instance(credentials, image_id, opts=nil)
- occi_client = new_client(credentials)
+ occi_client = new_client(credentials)
- hwp_id = opts[:hwp_id] || 'small'
+ storage_href = "#{occi_client.endpoint}/storage/#{image_id}"
- if not opts[:name]
- opts[:name] = "#{Time.now.to_i.to_s}#{rand(9)}"
- end
+ instancexml = ERB.new(OCCI_VM).result(binding)
+ instancefile = "|echo '#{instancexml}'"
- instancexml = ERB.new(OCCI_VM).result(binding)
- instancefile = "|echo '#{instancexml}'"
+ # TBD Specify VNET in the template.
- xmlvm = occi_client.post_vms(instancefile)
+ xmlvm = treat_response(occi_client.post_vms(instancefile))
- convert_instance(xmlvm.to_s(), credentials)
+ convert_instance(xmlvm, credentials)
end
-
def start_instance(credentials, id)
- occi_action(credentials, id, 'RESUME')
+ occi_action(credentials, id, 'RESUME')
end
def stop_instance(credentials, id)
- occi_action(credentials, id, 'STOPPED')
+ occi_action(credentials, id, 'STOPPED')
end
def destroy_instance(credentials, id)
- occi_action(credentials, id, 'DONE')
+ occi_action(credentials, id, 'DONE')
end
-
+ def reboot_instance(credentials, id)
+ begin
+ occi_action(credentials, id, 'REBOOT')
+ rescue Exception => e
+ # TBD Check exception
+ # OpenNebula 3.0 support
+ raise "Reboot action not supported"
+ end
+ end
private
def new_client(credentials)
- OCCIClient::Client.new(nil, credentials.user, credentials.password, false)
+ OCCIClient::Client.new(nil, credentials.user, credentials.password, false)
end
def convert_image(diskxml, credentials)
- disk = REXML::Document.new(diskxml)
- diskhash = disk.root.elements
-
- Image.new( {
- :id=>diskhash['ID'].text,
- :name=>diskhash['NAME'].text,
- :description=>diskhash['NAME'].text,
- :owner_id=>credentials.user,
- :architecture=>'Any architecture',
- } )
+ storage = REXML::Document.new(diskxml).root.elements
+
+ # TBD Add Image STATE, OWNER
+ Image.new( {
+ :id=>storage['ID'].text,
+ :name=>storage['NAME'].text,
+ :description=>storage['TYPE'].text,
+ :owner_id=>credentials.user,
+ :state=>"AVAILABLE",
+ :architecture=>storage['ARCH'],
+ } )
end
def convert_instance(computexml, credentials)
- compute = REXML::Document.new(computexml)
- computehash = compute.root.elements
-
- imageid = computehash['STORAGE/DISK[@type="disk"]'].attributes['href'].split("/").last
+ compute = REXML::Document.new(computexml)
+ computehash = compute.root.elements
- state = (computehash['STATE'].text == "ACTIVE") ? "RUNNING" : "STOPPED"
+ network = []
+ computehash.each('NIC/IP') {|ip| network<<InstanceAddress.new(ip)}
- hwp_name = computehash['INSTANCE_TYPE'] || 'small'
-
- networks = []
- (computehash['NETWORK'].each do |n|
- networks << InstanceAddress.new(n.attributes['ip'])
- end) unless computehash['NETWORK'].nil?
+ image_id = nil
+ if computehash['DISK/STORAGE']
+ image_id = computehash['DISK/STORAGE'].attributes['href'].split("/").last
+ end
- Instance.new( {
- :id=>computehash['ID'].text,
- :owner_id=>credentials.user,
- :name=>computehash['NAME'].text,
- :image_id=>imageid,
- :instance_profile=>InstanceProfile.new(hwp_name),
- :realm_id=>'Any realm',
- :state=>state,
- :public_addreses=>networks,
- :private_addreses=>networks,
- :actions=> instance_actions_for( state )
- } )
+ Instance.new( {
+ :id=>computehash['ID'].text,
+ :owner_id=>credentials.user,
+ :name=>computehash['NAME'].text,
+ :image_id=>image_id,
+ :instance_profile=>InstanceProfile.new(computehash['INSTANCE_TYPE']||'small'),
+ :realm_id=>'ONE',
+ :state=>VM_STATES[computehash['STATE'].text],
+ :public_addreses=>network,
+ :private_addreses=>[],
+ :actions=> instance_actions_for( VM_STATES[computehash['STATE'].text] )
+ } )
end
def occi_action(credentials, id, strstate)
- occi_client = new_client(credentials)
+ occi_client = new_client(credentials)
- actionxml = ERB.new(OCCI_ACTION).result(binding)
- actionfile = "|echo '#{actionxml}'"
+ actionxml = ERB.new(OCCI_ACTION).result(binding)
+ actionfile = "|echo '#{actionxml}'"
- xmlvm = occi_client.put_vm(actionfile)
+ xmlvm = treat_response(occi_client.put_vm(actionfile))
- convert_instance(xmlvm.to_s(), credentials)
+ convert_instance(xmlvm, credentials)
end
- (OCCI_VM = %q{
- <COMPUTE>
- <NAME><%=opts[:name]%></NAME>
- <INSTANCE_TYPE><%= hwp_id %></INSTANCE_TYPE>
- <STORAGE>
- <DISK image="<%= image_id %>" dev="sda1" />
- </STORAGE>
- </COMPUTE>
- }.gsub(/^ /, '') ) unless defined?( OCCI_VM )
+ def treat_response(res)
+ safely do
+ if CloudClient.is_error?(res)
+ raise case res.code
+ when "401" then "AuthenticationFailure"
+ when "404" then "ObjectNotFound"
+ else res.message
+ end
+ end
+ end
+ res
+ end
+ exceptions do
+ on /AuthenticationFailure/ do
+ status 401
+ end
- (OCCI_ACTION = %q{
- <COMPUTE>
- <ID><%= id %></ID>
- <STATE><%= strstate %></STATE>
- </COMPUTE>
- }.gsub(/^ /, '') ) unless defined?( OCCI_ACTION )
+ on /ObjectNotFound/ do
+ status 404
+ end
- end
+ on // do
+ status 502
+ end
+ end
+end
end
end
--
1.7.6.5
[PATCH 3/3] Minor edit to include hardware_profile CPU information in html view
Posted by ma...@redhat.com.
From: marios <ma...@redhat.com>
Signed-off-by: marios <ma...@redhat.com>
---
server/views/hardware_profiles/index.html.haml | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/server/views/hardware_profiles/index.html.haml b/server/views/hardware_profiles/index.html.haml
index 06b4d16..64e3455 100644
--- a/server/views/hardware_profiles/index.html.haml
+++ b/server/views/hardware_profiles/index.html.haml
@@ -15,3 +15,5 @@
= format_hardware_property profile.memory
%strong Storage:
= format_hardware_property profile.storage
+ %strong CPU:
+ = format_hardware_property profile.cpu
--
1.7.6.5