You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@deltacloud.apache.org by lu...@apache.org on 2010/07/09 01:33:41 UTC
svn commit: r962174 - in /incubator/deltacloud/trunk/server/libexec: config/
lib/deltacloud/drivers/opennebula/
Author: lutter
Date: Thu Jul 8 23:33:40 2010
New Revision: 962174
URL: http://svn.apache.org/viewvc?rev=962174&view=rev
Log:
Add a driver for OpenNebula
Added:
incubator/deltacloud/trunk/server/libexec/lib/deltacloud/drivers/opennebula/
incubator/deltacloud/trunk/server/libexec/lib/deltacloud/drivers/opennebula/cloud_client.rb
incubator/deltacloud/trunk/server/libexec/lib/deltacloud/drivers/opennebula/occi_client.rb
incubator/deltacloud/trunk/server/libexec/lib/deltacloud/drivers/opennebula/opennebula_driver.rb
Modified:
incubator/deltacloud/trunk/server/libexec/config/environment.rb
Modified: incubator/deltacloud/trunk/server/libexec/config/environment.rb
URL: http://svn.apache.org/viewvc/incubator/deltacloud/trunk/server/libexec/config/environment.rb?rev=962174&r1=962173&r2=962174&view=diff
==============================================================================
--- incubator/deltacloud/trunk/server/libexec/config/environment.rb (original)
+++ incubator/deltacloud/trunk/server/libexec/config/environment.rb Thu Jul 8 23:33:40 2010
@@ -71,6 +71,8 @@ case DRIVER
DRIVER_CLASS_NAME = "Deltacloud::Drivers::RHEVM::RHEVMDriver"
when :rimu
DRIVER_CLASS_NAME = "Deltacloud::Drivers::Rimu::RimuHostingDriver"
+ when :opennebula
+ DRIVER_CLASS_NAME = "Deltacloud::Drivers::Opennebula::OpennebulaDriver"
else
raise Exception.new( "Unknown driver #{DRIVER}" )
end
Added: incubator/deltacloud/trunk/server/libexec/lib/deltacloud/drivers/opennebula/cloud_client.rb
URL: http://svn.apache.org/viewvc/incubator/deltacloud/trunk/server/libexec/lib/deltacloud/drivers/opennebula/cloud_client.rb?rev=962174&view=auto
==============================================================================
--- incubator/deltacloud/trunk/server/libexec/lib/deltacloud/drivers/opennebula/cloud_client.rb (added)
+++ incubator/deltacloud/trunk/server/libexec/lib/deltacloud/drivers/opennebula/cloud_client.rb Thu Jul 8 23:33:40 2010
@@ -0,0 +1,123 @@
+#--------------------------------------------------------------------------- #
+# Copyright 2002-2009, Distributed Systems Architecture Group, Universidad
+# Complutense de Madrid (dsa-research.org)
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#--------------------------------------------------------------------------- #
+
+require 'rubygems'
+require 'uri'
+
+require 'net/https'
+
+begin
+ require 'curb'
+ CURL_LOADED=true
+rescue LoadError
+ CURL_LOADED=false
+end
+
+begin
+ require 'net/http/post/multipart'
+rescue LoadError
+end
+
+###############################################################################
+# The CloudClient module contains general functionality to implement a
+# 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
+ 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
+ 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
+ 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
+
+# Command line help functions
+module CloudCLI
+ # Returns the command name
+ def cmd_name
+ File.basename($0)
+ end
+end
Added: incubator/deltacloud/trunk/server/libexec/lib/deltacloud/drivers/opennebula/occi_client.rb
URL: http://svn.apache.org/viewvc/incubator/deltacloud/trunk/server/libexec/lib/deltacloud/drivers/opennebula/occi_client.rb?rev=962174&view=auto
==============================================================================
--- incubator/deltacloud/trunk/server/libexec/lib/deltacloud/drivers/opennebula/occi_client.rb (added)
+++ incubator/deltacloud/trunk/server/libexec/lib/deltacloud/drivers/opennebula/occi_client.rb Thu Jul 8 23:33:40 2010
@@ -0,0 +1,373 @@
+#--------------------------------------------------------------------------- #
+# Copyright 2002-2009, Distributed Systems Architecture Group, Universidad
+# Complutense de Madrid (dsa-research.org)
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#--------------------------------------------------------------------------- #
+
+require 'rubygems'
+require 'crack'
+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
+
+ #################################
+ # Pool Resource Request Methods #
+ #################################
+
+ ######################################################################
+ # Post a new VM to the VM Pool
+ # :instance_type
+ # :xmlfile
+ ######################################################################
+ def post_vms(xmlfile)
+ xml=File.read(xmlfile)
+
+ url = URI.parse(@endpoint+"/compute")
+
+ req = Net::HTTP::Post.new(url.path)
+ req.body=xml
+
+ req.basic_auth @occiauth[0], @occiauth[1]
+
+ res = CloudClient::http_start(url) do |http|
+ http.request(req)
+ end
+
+ if CloudClient::is_error?(res)
+ return res
+ else
+ return res.body
+ end
+ end
+
+ ######################################################################
+ # Retieves the pool of Virtual Machines
+ ######################################################################
+ def get_vms
+ url = URI.parse(@endpoint+"/compute")
+ 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
+
+ ######################################################################
+ # Post a new Network to the VN Pool
+ # :xmlfile xml description of the Virtual Network
+ ######################################################################
+ def post_network(xmlfile)
+ xml=File.read(xmlfile)
+
+ url = URI.parse(@endpoint+"/network")
+
+ req = Net::HTTP::Post.new(url.path)
+ req.body=xml
+
+ req.basic_auth @occiauth[0], @occiauth[1]
+
+ res = CloudClient::http_start(url) do |http|
+ http.request(req)
+ end
+
+ if CloudClient::is_error?(res)
+ return res
+ else
+ return res.body
+ end
+ end
+
+ ######################################################################
+ # Retieves the pool of Virtual Networks
+ ######################################################################
+ def get_networks
+ url = URI.parse(@endpoint+"/network")
+ 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
+
+ ######################################################################
+ # Post a new Image to the Image Pool
+ # :xmlfile
+ ######################################################################
+ def post_image(xmlfile, curb=true)
+ xml=File.read(xmlfile)
+ image_info=Crack::XML.parse(xml)
+
+ file_path = image_info['DISK']['URL']
+
+ m=file_path.match(/^\w+:\/\/(.*)$/)
+
+ if m
+ file_path="/"+m[1]
+ end
+
+ if curb and CURL_LOADED
+ curl=Curl::Easy.new(@endpoint+"/storage")
+
+ curl.http_auth_types = Curl::CURLAUTH_BASIC
+ curl.userpwd = "#{@occiauth[0]}:#{@occiauth[1]}"
+ curl.verbose = true if @debug
+ curl.multipart_form_post = true
+
+ begin
+ curl.http_post(
+ Curl::PostField.content('occixml', xml),
+ Curl::PostField.file('file', file_path)
+ )
+ rescue Exception => e
+ return CloudClient::Error.new(e.message)
+ end
+
+ return curl.body_str
+ else
+ file=File.open(file_path)
+
+ params=Hash.new
+ params["file"]=UploadIO.new(file,
+ 'application/octet-stream', file_path)
+
+ params['occixml'] = xml
+
+ 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) do |http|
+ http.request(req)
+ end
+
+ file.close
+
+ if CloudClient::is_error?(res)
+ return res
+ else
+ return res.body
+ end
+ end
+ end
+
+ ######################################################################
+ # Retieves the pool of Images owned by the user
+ ######################################################################
+ def get_images
+ url = URI.parse(@endpoint+"/storage")
+ 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
+
+ ####################################
+ # Entity Resource Request Methods #
+ ####################################
+
+ ######################################################################
+ # :id VM identifier
+ ######################################################################
+ def get_vm(id)
+ url = URI.parse(@endpoint+"/compute/" + id.to_s)
+ 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)
+ xml=File.read(xmlfile)
+ vm_info=Crack::XML.parse(xml)
+
+ url = URI.parse(@endpoint+'/compute/' + vm_info['COMPUTE']['ID'])
+
+ req = Net::HTTP::Put.new(url.path)
+ req.body = xml
+
+ req.basic_auth @occiauth[0], @occiauth[1]
+
+ res = CloudClient::http_start(url) do |http|
+ http.request(req)
+ end
+
+ if CloudClient::is_error?(res)
+ return res
+ else
+ return res.body
+ end
+ end
+
+ ####################################################################
+ # :id Compute identifier
+ ####################################################################
+ def delete_vm(id)
+ url = URI.parse(@endpoint+"/compute/" + id.to_s)
+ req = Net::HTTP::Delete.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
+
+ ######################################################################
+ # Retrieves a Virtual Network
+ # :id Virtual Network identifier
+ ######################################################################
+ def get_network(id)
+ url = URI.parse(@endpoint+"/network/" + id.to_s)
+ 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
+
+ ######################################################################
+ # :id VM identifier
+ ######################################################################
+ def delete_network(id)
+ url = URI.parse(@endpoint+"/network/" + id.to_s)
+ req = Net::HTTP::Delete.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
+
+ #######################################################################
+ # 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
+ end
+end
Added: incubator/deltacloud/trunk/server/libexec/lib/deltacloud/drivers/opennebula/opennebula_driver.rb
URL: http://svn.apache.org/viewvc/incubator/deltacloud/trunk/server/libexec/lib/deltacloud/drivers/opennebula/opennebula_driver.rb?rev=962174&view=auto
==============================================================================
--- incubator/deltacloud/trunk/server/libexec/lib/deltacloud/drivers/opennebula/opennebula_driver.rb (added)
+++ incubator/deltacloud/trunk/server/libexec/lib/deltacloud/drivers/opennebula/opennebula_driver.rb Thu Jul 8 23:33:40 2010
@@ -0,0 +1,266 @@
+#--------------------------------------------------------------------------- #
+# Copyright 2002-2009, Distributed Systems Architecture Group, Universidad
+# Complutense de Madrid (dsa-research.org)
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#--------------------------------------------------------------------------- #
+
+require 'deltacloud/base_driver'
+
+require 'erb'
+require 'rexml/document'
+
+path = File.dirname(__FILE__)
+$: << path
+
+require 'occi_client'
+
+module Deltacloud
+ module Drivers
+ module Opennebula
+
+class OpennebulaDriver < Deltacloud::BaseDriver
+
+ ######################################################################
+ # Flavors
+ ######################################################################
+
+ (FLAVORS = [
+ Flavor.new( {
+ :id=>'small',
+ :memory=>'small-memory',
+ :storage=>'small-storage',
+ :architecture=>'Any architecture',
+ } ),
+ Flavor.new( {
+ :id=>'medium',
+ :memory=>'medium-memory',
+ :storage=>'medium-storage',
+ :architecture=>'Any architecture',
+ } ),
+ Flavor.new( {
+ :id=>'large',
+ :memory=>'large-memory',
+ :storage=>'large-storage',
+ :architecture=>'Any architecture',
+ } ),
+ ] ) unless defined?( FLAVORS )
+
+
+ def flavors(credentials, opts=nil)
+ return FLAVORS if ( opts.nil? )
+ results = FLAVORS
+ results = filter_on( results, :id, opts )
+ results
+ end
+
+
+ ######################################################################
+ # Realms
+ ######################################################################
+
+ (REALMS = [
+ Realm.new( {
+ :id=>'Any id',
+ :name=>'Any name',
+ :limit=>:unlimited,
+ :state=>'Any state',
+ } ),
+ ] ) unless defined?( REALMS )
+
+
+ def realms(credentials, opts=nil)
+ 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)
+
+ images = []
+ imagesxml = 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)
+
+ images << convert_image(diskxml.to_s(), credentials)
+ end
+ images
+ end
+
+
+ ######################################################################
+ # Instances
+ ######################################################################
+
+ define_instance_states do
+ start.to( :pending ) .on( :create )
+
+ pending.to( :running ) .automatically
+
+ running.to( :stopped ) .on( :stop )
+
+ stopped.to( :running ) .on( :start )
+ stopped.to( :finish ) .on( :destroy )
+ end
+
+
+ def instances(credentials, opts=nil)
+ occi_client = new_client(credentials)
+
+ instances = []
+ instancesxml = occi_client.get_vms
+
+ computes = REXML::Document.new(instancesxml)
+ computes.root.elements.each do |d|
+ vm_id = d.attributes['href'].split("/").last
+
+ computexml = occi_client.get_vm(vm_id)
+
+ instances << convert_instance(computexml.to_s(), credentials)
+ end
+ instances
+ end
+
+
+ def create_instance(credentials, image_id, opts=nil)
+ occi_client = new_client(credentials)
+
+ flavor_id = opts[:flavor_id].nil? ? 'small' : opts[:flavor_id]
+
+ instancexml = ERB.new(OCCI_VM).result(binding)
+ instancefile = "|echo '#{instancexml}'"
+
+ xmlvm = occi_client.post_vms(instancefile)
+
+ convert_instance(xmlvm.to_s(), credentials)
+ end
+
+
+ def start_instance(credentials, id)
+ occi_action(credentials, id, 'RESUME')
+ end
+
+
+ def stop_instance(credentials, id)
+ occi_action(credentials, id, 'STOPPED')
+ end
+
+
+ def destroy_instance(credentials, id)
+ occi_action(credentials, id, 'DONE')
+ end
+
+
+
+ private
+
+ def new_client(credentials)
+ if ( credentials[:name].nil? || credentials[:password].nil? || credentials[:name] == '' || credentials[:password] == '' )
+ raise Deltacloud::AuthException.new
+ end
+ OCCIClient::Client.new(nil, credentials[:name], 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[:name],
+ :architecture=>'Any architecture',
+ } )
+ 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
+
+ state = (computehash['STATE'].text == 'ACTIVE') ? 'RUNNING' : computehash['STATE'].text
+
+ flavor = computehash['INSTANCE_TYPE'].nil? ? 'small' : computehash['INSTANCE_TYPE'].text
+
+ networks = []
+ (computehash['NETWORK'].each do |n|
+ networks << n.attributes['ip']
+ end) unless computehash['NETWORK'].nil?
+
+ Instance.new( {
+ :id=>computehash['ID'].text,
+ :owner_id=>credentials[:name],
+ :name=>computehash['NAME'].text,
+ :image_id=>imageid,
+ :flavor_id=>flavor,
+ :realm_id=>'Any realm',
+ :state=>state,
+ :public_addreses=>networks,
+ :private_addreses=>networks,
+ :actions=> instance_actions_for( state )
+ } )
+ end
+
+
+ def occi_action(credentials, id, strstate)
+ occi_client = new_client(credentials)
+
+ actionxml = ERB.new(OCCI_ACTION).result(binding)
+ actionfile = "|echo '#{actionxml}'"
+
+ xmlvm = occi_client.put_vm(actionfile)
+
+ convert_instance(xmlvm.to_s(), credentials)
+ end
+
+
+ (OCCI_VM = %q{
+ <COMPUTE>
+ <NAME><%=opts[:name]%></NAME>
+ <INSTANCE_TYPE><%= flavor_id %></INSTANCE_TYPE>
+ <STORAGE>
+ <DISK image="<%= image_id %>" dev="sda1" />
+ </STORAGE>
+ </COMPUTE>
+ }.gsub(/^ /, '') ) unless defined?( DELTA_VM )
+
+
+ (OCCI_ACTION = %q{
+ <COMPUTE>
+ <ID><%= id %></ID>
+ <STATE><%= strstate %></STATE>
+ </COMPUTE>
+ }.gsub(/^ /, '') ) unless defined?( DELTA_ACTION )
+
+ end
+
+ end
+ end
+end