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/28 17:10:52 UTC
[PATCH 4/7] Updated openstack driver for openstack compute v2.0 API
From: marios <ma...@redhat.com>
Signed-off-by: marios <ma...@redhat.com>
---
.../drivers/openstack/openstack_driver.rb | 312 +++++++++++++++++++-
1 files changed, 306 insertions(+), 6 deletions(-)
diff --git a/server/lib/deltacloud/drivers/openstack/openstack_driver.rb b/server/lib/deltacloud/drivers/openstack/openstack_driver.rb
index d09edf2..5e49392 100644
--- a/server/lib/deltacloud/drivers/openstack/openstack_driver.rb
+++ b/server/lib/deltacloud/drivers/openstack/openstack_driver.rb
@@ -14,16 +14,22 @@
# under the License.
#
-require 'deltacloud/drivers/rackspace/rackspace_driver.rb'
+require 'deltacloud/base_driver'
+require 'openstack/compute'
+require 'tempfile'
module Deltacloud
module Drivers
module Openstack
- class OpenstackDriver < Rackspace::RackspaceDriver
+ class OpenstackDriver < Deltacloud::BaseDriver
feature :instances, :user_name
feature :instances, :authentication_password
feature :instances, :user_files
+ def supported_collections
+ DEFAULT_COLLECTIONS - [ :storage_snapshots, :storage_volumes ] #+ [ :buckets ]
+ end
+
define_instance_states do
start.to( :pending ) .on( :create )
pending.to( :running ) .automatically
@@ -33,15 +39,309 @@ module Deltacloud
stopped.to( :finish ) .automatically
end
- def new_client(credentials)
+ def hardware_profiles(credentials, opts = {})
+ os = new_client(credentials)
+ results = []
+ safely do
+ if opts[:id]
+ flavor = os.flavor(opts[:id])
+ results << convert_from_flavor(flavor)
+ else
+ results = os.flavors.collect do |f|
+ convert_from_flavor(f)
+ end
+ end
+ filter_hardware_profiles(results, opts)
+ end
+ end
+
+ def images(credentials, opts=nil)
+ os = new_client(credentials)
+ results = []
+ profiles = hardware_profiles(credentials)
+ safely do
+ if(opts[:id])
+ img = os.get_image(opts[:id])
+ results << convert_from_image(img, os.authuser)
+ else
+ results = os.list_images.collect do |img|
+ convert_from_image(img, os.authuser)
+ end
+ end
+ end
+ results.each do |img|
+ img.hardware_profiles = profiles
+ end
+ filter_on(results, :owner_id, opts)
+ end
+
+ def create_image(credentials, opts)
+ os = new_client(credentials)
+ safely do
+ server = os.get_server(opts[:id])
+ image_name = opts[:name] || "#{server.name}_#{Time.now}"
+ img = server.create_image(:name=>image_name)
+ convert_from_image(img, os.authuser)
+ end
+ end
+
+ def destroy_image(credentials, image_id)
+ os = new_client(credentials)
+ safely do
+ image = os.get_image(image_id)
+ unless image.delete!
+ raise "ERROR: Cannot delete image with ID:#{image_id}"
+ end
+ end
+ end
+
+ def realms(credentials, opts=nil)
+ os = new_client(credentials)
+ limits = ""
+ safely do
+ lim = os.limits
+ limits << "ABSOLUTE >> Max. Instances: #{lim[:absolute][:maxTotalInstances]} Max. RAM: #{lim[:absolute][:maxTotalRAMSize]} || "
+ lim[:rate].each do |rate|
+ if rate[:regex] =~ /servers/
+ limits << "SERVERS >> Total: #{rate[:limit].first[:value]} Remaining: #{rate[:limit].first[:remaining]} Time Unit: per #{rate[:limit].first[:unit]}"
+ end
+ end
+ end
+ [ Realm.new( { :id=>'default',
+ :name=>'default',
+ :limit => limits,
+ :state=>'AVAILABLE' })]
+ end
+
+ def instances(credentials, opts={})
+ os = new_client(credentials)
+ insts = []
+ safely do
+ if opts[:id]
+ server = os.get_server(opts[:id].to_i)
+ insts << convert_from_server(server, os.authuser)
+ else
+ insts = os.list_servers_detail.collect do |server|
+ convert_from_server(server, os.authuser)
+ end
+ end
+ end
+ insts = filter_on( insts, :state, opts )
+ insts
+ end
+
+ def create_instance(credentials, image_id, opts)
+ os = new_client( credentials )
+ result = nil
+#opts[:personality]: path1='server_path1'. content1='contents1', path2='server_path2', content2='contents2' etc
+ params = extract_personality(opts)
+# ref_prefix = get_prefix(os)
+ params[:name] = (opts[:name] && opts[:name].length>0)? opts[:name] : Time.now.to_s
+ params[:imageRef] = image_id
+ params[:flavorRef] = (opts[:hwp_id] && opts[:hwp_id].length>0) ?
+ opts[:hwp_id] : hardware_profiles(credentials).first
+ if opts[:password] && opts[:password].length > 0
+ params[:adminPass]=opts[:password]
+ end
safely do
- CloudServers::Connection.new(:username => credentials.user, :api_key => credentials.password, :auth_url => api_provider)
+ server = os.create_server(params)
+ result = convert_from_server(server, os.authuser)
end
+ result
+ end
+
+ def reboot_instance(credentials, instance_id)
+ os = new_client(credentials)
+ safely do
+ server = os.get_server(instance_id.to_i)
+ server.reboot! # sends a hard reboot (power cycle) - could instead server.reboot("SOFT")
+ convert_from_server(server, os.authuser)
+ end
+ end
+
+ def destroy_instance(credentials, instance_id)
+ os = new_client(credentials)
+ safely do
+ server = os.get_server(instance_id.to_i)
+ server.delete!
+ convert_from_server(server, os.authuser)
+ end
+ end
+
+ alias_method :stop_instance, :destroy_instance
+
+ def valid_credentials?(credentials)
+ begin
+ new_client(credentials)
+ rescue
+ return false
+ end
+ true
+ end
+
+ def buckets(credentials, opts={})
+
+ end
+
+ def create_bucket(credentials, name, opts={})
+
+ end
+
+ def delete_bucket(credentials, name, opts={})
+
+ end
+
+ def blobs(credentials, opts={})
+
+ end
+
+ def blob_data(credentials, bucket, blob, opts={})
+
end
- private :new_client
+ def create_blob(credentials, bucket, blob, data, opts={})
+
+ end
+
+ def delete_blob(credentials, bucket, blob, opts={})
+
+ end
+
+ def blob_metadata(credentials, opts={})
+
+ end
+
+ def update_blob_metadata(credentials, opts={})
+
+ end
+
+ def blob_stream_connection(params)
+
+ end
+
+private
+
+ #for v2 authentication credentials.name == "username+tenant_name"
+ def new_client(credentials, buckets=false)
+ tokens = credentials.user.split("+")
+ if (tokens.size != 2 && api_v2)
+ raise ValidationFailure.new(Exception.new("Error: expected \"username+tenantname\" as username, you provided: #{credentials.user}"))
+ else
+ user_name, tenant_name = tokens.first, tokens.last
+ end
+ safely do
+ OpenStack::Compute::Connection.new(:username => user_name, :api_key => credentials.password, :authtenant => tenant_name, :auth_url => api_provider)
+ end
+ end
+
+ def cloudfiles_client(credentials)
+ safely do
+ CloudFiles::Connection.new(:username => credentials.user, :api_key => credentials.password)
+ end
+ end
+
+#NOTE: for the convert_from_foo methods below... openstack-compute
+#gives Hash for 'flavors' but OpenStack::Compute::Flavor for 'flavor'
+#hence the use of 'send' to deal with both cases and save duplication
+
+ def convert_from_flavor(flavor)
+ op = (flavor.class == Hash)? :fetch : :send
+ HardwareProfile.new(flavor.send(op, :id).to_s) do
+ architecture 'x86_64'
+ memory flavor.send(op, :ram).to_i
+ storage flavor.send(op, :disk).to_i
+ cpu flavor.send(op, :vcpus).to_i
+ end
+ end
+
+ def convert_from_image(image, owner)
+ op = (image.class == Hash)? :fetch : :send
+ Image.new({
+ :id => image.send(op, :id),
+ :name => image.send(op, :name),
+ :description => image.send(op, :name),
+ :owner_id => owner,
+ :state => image.send(op, :status),
+ :architecture => 'x86_64'
+ })
+ end
+
+ def convert_from_server(server, owner)
+ op = (server.class == Hash)? :fetch : :send
+ image = server.send(op, :image)
+ flavor = server.send(op, :flavor)
+ begin
+ password = server.send(op, :adminPass) || ""
+ rescue IndexError
+ password = ""
+ end
+ inst = Instance.new(
+ :id => server.send(op, :id).to_s,
+ :realm_id => 'default',
+ :owner_id => owner,
+ :description => server.send(op, :name),
+ :name => server.send(op, :name),
+ :state => (server.send(op, :status) == 'ACTIVE') ? 'RUNNING' : 'PENDING',
+ :architecture => 'x86_64',
+ :image_id => image[:id] || image["id"],
+ :instance_profile => InstanceProfile::new(flavor[:id] || flavor["id"]),
+ :public_addresses => convert_server_addresses(server, :public),
+ :private_addresses => convert_server_addresses(server, :private),
+ :username => 'root',
+ :password => password
+ )
+ inst.actions = instance_actions_for(inst.state)
+ inst.create_image = 'RUNNING'.eql?(inst.state)
+ inst
+ end
+
+ def convert_server_addresses(server, type)
+ op, address_label = (server.class == Hash)? [:fetch, :addr] : [:send, :address]
+ addresses = (server.send(op, :addresses)[type] || []).collect do |addr|
+ type = (addr.send(op, :version) == 4)? :ipv4 : :ipv6
+ InstanceAddress.new(addr.send(op, address_label), {:type=>type} )
+ end
+ end
+
+ #IN: path1='server_path1'. content1='contents1', path2='server_path2', content2='contents2' etc
+ #OUT:{local_path=>server_path, local_path1=>server_path2 etc}
+ def extract_personality(opts)
+ personality_hash = opts.inject({}) do |result, (opt_k,opt_v)|
+ if opt_k.to_s =~ /^path([1-5]+)/
+ tempfile = Tempfile.new("os_personality_local_#{$1}")
+ tempfile.write(opts[:"content#{$1}"])
+ result[tempfile.path]=opts[:"path#{$1}"]
+ end
+ result
+ end
+ end
+
+ def api_v2
+ if api_provider =~ /.*v2.0/
+ true
+ else
+ false
+ end
+ end
+
+ exceptions do
+
+ on /Exception::BadRequest/ do
+ status 400
+ end
+
+ on /Exception::Authentication/ do
+ status 401
+ end
+
+ on /Exception::ItemNotFound/ do
+ status 404
+ end
+
+ end
+
+
end
end
end
end
-
--
1.7.6.5