From: marios <[email protected]>
Signed-off-by: marios <[email protected]> --- .../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
