Return-Path: Delivered-To: apmail-incubator-deltacloud-dev-archive@minotaur.apache.org Received: (qmail 5685 invoked from network); 29 Nov 2010 13:17:33 -0000 Received: from unknown (HELO mail.apache.org) (140.211.11.3) by 140.211.11.9 with SMTP; 29 Nov 2010 13:17:33 -0000 Received: (qmail 78549 invoked by uid 500); 29 Nov 2010 13:17:33 -0000 Delivered-To: apmail-incubator-deltacloud-dev-archive@incubator.apache.org Received: (qmail 78533 invoked by uid 500); 29 Nov 2010 13:17:33 -0000 Mailing-List: contact deltacloud-dev-help@incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: deltacloud-dev@incubator.apache.org Delivered-To: mailing list deltacloud-dev@incubator.apache.org Received: (qmail 78524 invoked by uid 99); 29 Nov 2010 13:17:32 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 29 Nov 2010 13:17:32 +0000 X-ASF-Spam-Status: No, hits=-5.0 required=10.0 tests=RCVD_IN_DNSWL_HI,SPF_HELO_PASS,SPF_PASS X-Spam-Check-By: apache.org Received-SPF: pass (nike.apache.org: domain of mfojtik@redhat.com designates 209.132.183.28 as permitted sender) Received: from [209.132.183.28] (HELO mx1.redhat.com) (209.132.183.28) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 29 Nov 2010 13:17:25 +0000 Received: from int-mx12.intmail.prod.int.phx2.redhat.com (int-mx12.intmail.prod.int.phx2.redhat.com [10.5.11.25]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id oATDH3lA007652 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Mon, 29 Nov 2010 08:17:03 -0500 Received: from patashnik.brq.redhat.com (dhcp-2-138.brq.redhat.com [10.34.2.138]) by int-mx12.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id oATDH0w2001104 for ; Mon, 29 Nov 2010 08:17:02 -0500 From: mfojtik@redhat.com To: deltacloud-dev@incubator.apache.org Subject: [PATCH core 1/6] Revamped EC2 driver using 'aws' gem Date: Mon, 29 Nov 2010 14:16:53 +0100 Message-Id: <1291036618-21348-2-git-send-email-mfojtik@redhat.com> In-Reply-To: <1291036618-21348-1-git-send-email-mfojtik@redhat.com> References: <1291036618-21348-1-git-send-email-mfojtik@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.25 X-Virus-Checked: Checked by ClamAV on apache.org From: Michal Fojtik --- server/lib/deltacloud/drivers/ec2/ec2_driver.rb | 1027 ++++++++++------------- server/lib/deltacloud/models/storage_volume.rb | 2 + server/server.rb | 90 ++ server/views/keys/index.html.haml | 2 +- 4 files changed, 531 insertions(+), 590 deletions(-) diff --git a/server/lib/deltacloud/drivers/ec2/ec2_driver.rb b/server/lib/deltacloud/drivers/ec2/ec2_driver.rb index d38f8f7..29ea78e 100644 --- a/server/lib/deltacloud/drivers/ec2/ec2_driver.rb +++ b/server/lib/deltacloud/drivers/ec2/ec2_driver.rb @@ -1,5 +1,5 @@ # -# Copyright (C) 2009 Red Hat, Inc. +# Copyright (C) 2010 Red Hat, Inc. # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with @@ -15,654 +15,503 @@ # 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 'active_support' -require 'AWS' -require 'right_aws' - -module Deltacloud - module Drivers - module EC2 -class EC2Driver < Deltacloud::BaseDriver - - def supported_collections - DEFAULT_COLLECTIONS + [ :keys, :buckets, :load_balancers ] - end - - feature :instances, :user_data - feature :instances, :authentication_key - feature :instances, :security_group - feature :images, :owner_id - feature :buckets, :bucket_location - feature :instances, :register_to_load_balancer - - define_hardware_profile('m1.small') do - cpu 1 - memory 1.7 * 1024 - storage 160 - architecture 'i386' - end +require 'aws' - define_hardware_profile('m1.large') do - cpu 4 - memory 7.5 * 1024 - storage 850 - architecture 'x86_64' - end - - define_hardware_profile('m1.xlarge') do - cpu 8 - memory 15 * 1024 - storage 1690 - architecture 'x86_64' - end - - define_hardware_profile('c1.medium') do - cpu 5 - memory 1.7 * 1024 - storage 350 - architecture 'i386' - end - - define_hardware_profile('c1.xlarge') do - cpu 20 - memory 7 * 1024 - storage 1690 - architecture 'x86_64' - end - - define_hardware_profile('m2.xlarge') do - cpu 6.5 - memory 17.1 * 1024 - storage 420 - architecture 'x86_64' - end +class Instance + attr_accessor :keyname + attr_accessor :authn_error - define_hardware_profile('m2.2xlarge') do - cpu 13 - memory 34.2 * 1024 - storage 850 - architecture 'x86_64' + def authn_feature_failed? + return true unless authn_error.nil? end - define_hardware_profile('m2.4xlarge') do - cpu 26 - memory 68.4 * 1024 - storage 1690 - architecture 'x86_64' - end +end - 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 +module Deltacloud + module Drivers + module EC2 + class EC2Driver < Deltacloud::BaseDriver - # - # Images - # - - def images(credentials, opts={} ) - ec2 = new_client(credentials) - img_arr = [] - # if we know the image_id, we don't want to limit by owner_id, since this - # will exclude public images - if (opts and opts[:id]) - config = { :image_id => opts[:id] } - else - config = { :owner_id => "amazon" } - config.merge!({ :owner_id => opts[:owner_id] }) if opts and opts[:owner_id] - end - safely do - image_set = ec2.describe_images(config).imagesSet - unless image_set.nil? - image_set.item.each do |image| - img_arr << convert_image(image) + def supported_collections + DEFAULT_COLLECTIONS + [ :keys, :buckets ] end - end - end - img_arr = filter_on( img_arr, :architecture, opts ) - img_arr.sort_by{|e| [e.owner_id, e.name]} - end - # - # Realms - # - - def realms(credentials, opts=nil) - ec2 = new_client(credentials) - realms = [] - safely do - ec2.describe_availability_zones.availabilityZoneInfo.item.each do |ec2_realm| - realms << convert_realm( ec2_realm ) - end - end - realms - end - - # - # Instances - # - def instances(credentials, opts=nil) - ec2 = new_client(credentials) - instances = [] - safely do - param = opts.nil? ? nil : opts[:id] - ec2_instances = ec2.describe_instances.reservationSet - return [] unless ec2_instances - ec2_instances.item.each do |item| - item.instancesSet.item.each do |ec2_instance| - instances << convert_instance( ec2_instance, item.ownerId ) + feature :instances, :user_data + feature :instances, :authentication_key + feature :instances, :security_group + feature :images, :owner_id + feature :buckets, :bucket_location + + define_hardware_profile('t1.micro') do + cpu 1 + memory 0.63 * 1024 + storage 160 + architecture 'i386' end - end - end - instances = filter_on( instances, :id, opts ) - instances = filter_on( instances, :state, opts ) - instances - end - - def create_instance(credentials, image_id, opts) - ec2 = new_client( credentials ) - realm_id = opts[:realm_id] - safely do - image = image(credentials, :id => image_id ) - hwp = find_hardware_profile(credentials, opts[:hwp_id], image.id) - ec2_instances = ec2.run_instances( - :image_id => image.id, - :user_data => opts[:user_data], - :key_name => opts[:keyname], - :availability_zone => realm_id, - :monitoring_enabled => true, - :instance_type => hwp.name, - :disable_api_termination => false, - :instance_initiated_shutdown_behavior => 'terminate', - :security_group => opts[:security_group] - ) - new_instance = convert_instance( ec2_instances.instancesSet.item.first, 'pending' ) - if opts[:load_balancer_id] and opts[:load_balancer_id]!="" - elb = new_client(credentials, :elb) - elb.register_instances_with_load_balancer({ - :instances => [new_instance.id], - :load_balancer_name => opts[:load_balancer_id] - }) - end - return new_instance - end - end + define_hardware_profile('m1.small') do + cpu 1 + memory 1.7 * 1024 + storage 160 + architecture 'i386' + end - def generate_instance(ec2, id, backup) - begin - this_instance = ec2.describe_instances( :instance_id => id ).reservationSet.item.first.instancesSet.item.first - convert_instance(this_instance, this_instance.ownerId) - rescue Exception => e - puts "WARNING: ignored error during instance refresh: #{e.message}" - # at this point, the action has succeeded but our follow-up - # "describe_instances" failed for some reason. Create a simple Instance - # object with only the ID and new state in place - state = convert_state(backup.instancesSet.item.first.currentState.name) - Instance.new( { - :id => id, - :state => state, - :actions => instance_actions_for( state ), - } ) - end - end + define_hardware_profile('m1.large') do + cpu 4 + memory 7.5 * 1024 + storage 850 + architecture 'x86_64' + end - def reboot_instance(credentials, id) - ec2 = new_client(credentials) - backup = ec2.reboot_instances( :instance_id => id ) + define_hardware_profile('m1.xlarge') do + cpu 8 + memory 15 * 1024 + storage 1690 + architecture 'x86_64' + end - generate_instance(ec2, id, backup) - end + define_hardware_profile('c1.medium') do + cpu 5 + memory 1.7 * 1024 + storage 350 + architecture 'i386' + end - def stop_instance(credentials, id) - ec2 = new_client(credentials) - backup = ec2.terminate_instances( :instance_id => id ) + define_hardware_profile('c1.xlarge') do + cpu 20 + memory 7 * 1024 + storage 1690 + architecture 'x86_64' + end - generate_instance(ec2, id, backup) - end + define_hardware_profile('m2.xlarge') do + cpu 6.5 + memory 17.1 * 1024 + storage 420 + architecture 'x86_64' + end - def destroy_instance(credentials, id) - ec2 = new_client(credentials) - backup = ec2.terminate_instances( :instance_id => id ) + define_hardware_profile('m2.2xlarge') do + cpu 13 + memory 34.2 * 1024 + storage 850 + architecture 'x86_64' + end - generate_instance(ec2, id, backup) - end + define_hardware_profile('m2.4xlarge') do + cpu 26 + memory 68.4 * 1024 + storage 1690 + architecture 'x86_64' + end - # - # Storage Volumes - # + 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 - def storage_volumes(credentials, opts=nil) - ec2 = new_client( credentials ) - volumes = [] - safely do - if (opts) - ec2.describe_volumes(:volume_id => opts[:id]).volumeSet.item.each do |ec2_volume| - volumes << convert_volume( ec2_volume ) + def images(credentials, opts={}) + ec2 = new_client(credentials) + img_arr = [] + opts ||= {} + if opts[:id] + safely do + img_arr = ec2.describe_images(opts[:id]).collect do |image| + convert_image(image) + end + end + else + owner_id = opts[:owner_id] || "amazon" + safely do + img_arr = ec2.describe_images_by_owner(owner_id, "machine").collect do |image| + convert_image(image) + end + end + end + img_arr = filter_on( img_arr, :architecture, opts ) + img_arr.sort_by { |e| [e.owner_id, e.name] } end - else - ec2_volumes = ec2.describe_volumes.volumeSet - return [] unless ec2_volumes - ec2_volumes.item.each do |ec2_volume| - volumes << convert_volume( ec2_volume ) + + def realms(credentials, opts={}) + ec2 = new_client(credentials) + zone_id = opts ? opts[:id] : nil + safely do + return ec2.describe_availability_zones(zone_id).collect do |realm| + convert_realm(realm) + end + end end - end - end - volumes - end - # - # Storage Snapshots - # + def instances(credentials, opts={}) + ec2 = new_client(credentials) + inst_arr = [] + safely do + inst_arr = ec2.describe_instances.collect do |instance| + convert_instance(instance) if instance + end.flatten + end + inst_arr = filter_on( inst_arr, :id, opts ) + filter_on( inst_arr, :state, opts ) + end - def storage_snapshots(credentials, opts=nil) - ec2 = new_client( credentials ) - snapshots = [] - safely do - if (opts) - ec2.describe_snapshots(:owner => 'self', :snapshot_id => opts[:id]).snapshotSet.item.each do |ec2_snapshot| - snapshots << convert_snapshot( ec2_snapshot ) + def create_instance(credentials, image_id, opts={}) + ec2 = new_client(credentials) + instance_options = {} + instance_options.merge!(:user_data => opts[:user_data]) if opts[:user_data] + instance_options.merge!(:key_name => opts[:key_name]) if opts[:key_name] + instance_options.merge!(:availability_zone => opts[:availability_zone]) if opts[:availability_zone] + instance_options.merge!(:instance_type => opts[:hwp_id]) if opts[:hwp_id] + instance_options.merge!(:group_ids => opts[:security_group]) if opts[:security_group] + safely do + new_instance = convert_instance(ec2.launch_instances(image_id, instance_options).first) + if opts[:public_ip] + ec2.associate_address(new_instance.id, opts[:public_ip]) + end + new_instance + end end - else - ec2_snapshots = ec2.describe_snapshots(:owner => 'self').snapshotSet - return [] unless ec2_snapshots - ec2_snapshots.item.each do |ec2_snapshot| - snapshots << convert_snapshot( ec2_snapshot ) + + def reboot_instance(credentials, instance_id) + ec2 = new_client(credentials) + if ec2.reboot_instances([instance_id]) + instance(credentials, instance_id) + else + raise Deltacloud::BackendError.new(500, "Instance", "Instance reboot failed", "") + end end - end - end - snapshots - end - - def key(credentials, opts=nil) - keys(credentials, opts).first - end - def keys(credentials, opts=nil) - ec2 = new_client( credentials ) - opts[:key_name] = opts[:id] if opts and opts[:id] - keypairs = ec2.describe_keypairs(opts || {}) - result = [] - safely do - keypairs.keySet.item.each do |keypair| - result << convert_key(keypair) - end - end - result - end + def destroy_instance(credentials, instance_id) + ec2 = new_client(credentials) + puts "Terminating instance #{instance_id}" + if ec2.terminate_instances([instance_id]) + instance(credentials, instance_id) + else + raise Deltacloud::BackendError.new(500, "Instance", "Instance cannot be terminated", "") + end + end - def create_key(credentials, opts={}) - key = Key.new - ec2 = new_client( credentials ) - safely do - key = convert_key(ec2.create_keypair(opts)) - end - return key - end + alias :stop_instance :destroy_instance - def destroy_key(credentials, opts={}) - safely do - ec2 = new_client( credentials ) - ec2.delete_keypair(opts) - end - end + def keys(credentials, opts={}) + ec2 = new_client(credentials) + opts ||= {} + safely do + ec2.describe_key_pairs(opts[:id] || nil).collect do |key| + convert_key(key) + end + end + end - def valid_credentials?(credentials) - client = new_client(credentials) - # FIXME: We need to do this call to determine if - # EC2 is working with given credentials. There is no - # other way to check, if given credentials are valid or not. - realms = client.describe_availability_zones rescue false - return realms ? true : false - end + def key(credentials, opts={}) + keys(credentials, :id => opts[:id]).first + end -#-- -# Buckets -#-- get a list of your buckets from the s3 service - def buckets(credentials, opts) - buckets = [] - safely do - s3_client = s3_client(credentials) - bucket_list = s3_client.buckets - bucket_list.each do |current| - buckets << convert_bucket(current) - end - end - buckets = filter_on(buckets, :id, opts) - buckets - end + def create_key(credentials, opts={}) + ec2 = new_client(credentials) + safely do + convert_key(ec2.create_key_pair(opts[:key_name])) + end + end -#-- -# Create bucket -#-- -#valid values for bucket location: 'EU'|'us-west1'|'ap-southeast-1' - if you -#don't specify a location then by default buckets are created in 'us-east' -#[but if you *do* specify 'us-east' things blow up] - def create_bucket(credentials, name, opts={}) - bucket = nil - safely do - begin - s3_client = s3_client(credentials) - bucket_location = opts['location'] - if bucket_location - bucket = RightAws::S3::Bucket.create(s3_client, name, true, nil, :location => bucket_location) - else - bucket = RightAws::S3::Bucket.create(s3_client, name, true) - end #if - rescue RightAws::AwsError => e - raise e unless e.message =~ /BucketAlreadyExists/ - raise Deltacloud::BackendError.new(409, e.class.to_s, e.message, e.backtrace) - end #begin - end #do - convert_bucket(bucket) - end + def destroy_key(credentials, opts={}) + ec2 = new_client(credentials) + original_key = key(credentials, opts) + safely do + ec2.delete_key_pair(original_key.id) + original_key= original_key.state = "DELETED" + end + original_key + end -#-- -# Delete_bucket -#-- - def delete_bucket(credentials, name, opts={}) - s3_client = s3_client(credentials) - safely do - s3_client.interface.delete_bucket(name) - end - end + def buckets(credentials, opts) + buckets = [] + safely do + s3_client = new_client(credentials, :s3) + bucket_list = s3_client.buckets + bucket_list.each do |current| + buckets << convert_bucket(current) + end + end + filter_on(buckets, :id, opts) + end -#-- -# Blobs -#-- - def blobs(credentials, opts = nil) - s3_client = s3_client(credentials) - blobs = [] - safely do - s3_bucket = s3_client.bucket(opts['bucket']) - s3_bucket.keys({}, true).each do |s3_object| - blobs << convert_object(s3_object) - end - end - blobs = filter_on(blobs, :id, opts) - blobs - end + def create_bucket(credentials, name, opts={}) + bucket = nil + safely do + s3_client = new_client(credentials, :s3) + bucket_location = opts['location'] + if bucket_location + bucket = Aws::S3::Bucket.create(s3_client, name, true, nil, :location => bucket_location) + else + bucket = Aws::S3::Bucket.create(s3_client, name, true) + end + end + convert_bucket(bucket) + end -#-- -# Blob data -#-- - def blob_data(credentials, bucket_id, blob_id, opts) - s3_client = s3_client(credentials) - s3_client.interface.get(bucket_id, blob_id) do |chunk| - yield chunk - end - end + def delete_bucket(credentials, name, opts={}) + s3_client = new_client(credentials, :s3) + safely do + s3_client.interface.delete_bucket(name) + end + end -#-- -# Create Blob -#-- - def create_blob(credentials, bucket_id, blob_id, data = nil, opts = nil) - s3_client = s3_client(credentials) - #data is a construct with the temporary file created by server @.tempfile - #also file[:type] will give us the content-type - res = s3_client.interface.put(bucket_id, blob_id, data.tempfile, {"Content-Type" => data[:type]}) - #create a new Blob object and return that - Blob.new( { :id => blob_id, - :bucket => bucket_id, - :content_length => data.tempfile.length, - :content_type => data[:type], - :last_modified => '' - } - ) - end + def blobs(credentials, opts = nil) + s3_client = new_client(credentials, :s3) + blobs = [] + safely do + s3_bucket = s3_client.bucket(opts['bucket']) + s3_bucket.keys({}, true).each do |s3_object| + blobs << convert_object(s3_object) + end + end + blobs = filter_on(blobs, :id, opts) + blobs + end -#-- -# Delete Blob -#-- - def delete_blob(credentials, bucket_id, blob_id, opts=nil) - s3_client = s3_client(credentials) - s3_client.interface.delete(bucket_id, blob_id) - end + #-- + # Create Blob + #-- + def create_blob(credentials, bucket_id, blob_id, data = nil, opts = nil) + s3_client = new_client(credentials, :s3) + #data is a construct with the temporary file created by server @.tempfile + #also file[:type] will give us the content-type + res = nil + # File stream needs to be reopened in binary mode for whatever reason + file = File::open(data[:tempfile].path, 'rb') + safely do + res = s3_client.interface.put(bucket_id, + blob_id, + file, + {"Content-Type" => data[:type]}) + end + #create a new Blob object and return that + Blob.new( { :id => blob_id, + :bucket => bucket_id, + :content_length => data[:tempfile].length, + :content_type => data[:type], + :last_modified => '' + } + ) + end - def load_balancer(credentials, opts={}) - load_balancers(credentials, { - :load_balancer_names => [opts[:id]] - }).first - end + #-- + # Delete Blob + #-- + def delete_blob(credentials, bucket_id, blob_id, opts=nil) + s3_client = new_client(credentials, :s3) + s3_client.interface.delete(bucket_id, blob_id) + end - def load_balancers(credentials, opts=nil) - ec2 = new_client( credentials, :elb ) - result = [] - safely do - loadbalancers = ec2.describe_load_balancers(opts || {}) - return [] unless loadbalancers.DescribeLoadBalancersResult.LoadBalancerDescriptions - loadbalancers.DescribeLoadBalancersResult.LoadBalancerDescriptions.member.each do |loadbalancer| - result << convert_load_balancer(credentials, loadbalancer) - end - end - return result - end - def create_load_balancer(credentials, opts={}) - ec2 = new_client( credentials, :elb ) - safely do - ec2.create_load_balancer({ - :load_balancer_name => opts['name'], - # TODO: Add possibility to push more listeners/realms in one request - # Something like 'Hash' in 'Array' parameter - :availability_zones => [opts['realm_id']], - :listeners => [{ - :protocol => opts['listener_protocol'], - :load_balancer_port => opts['listener_balancer_port'], - :instance_port => opts['listener_instance_port'] - }] - }) - return load_balancer(credentials, opts['name']) - end - end + def blob_data(credentials, bucket_id, blob_id, opts) + s3_client = new_client(credentials, :s3) + s3_client.interface.get(bucket_id, blob_id) do |chunk| + yield chunk + end + end - def destroy_load_balancer(credentials, id) - ec2 = new_client( credentials, :elb ) - safely do - ec2.delete_load_balancer({ - :load_balancer_name => id - }) - end - end + def storage_volumes(credentials, opts={}) + ec2 = new_client( credentials ) + volume_list = (opts and opts[:id]) ? opts[:id] : nil + safely do + ec2.describe_volumes(volume_list).collect do |volume| + convert_volume(volume) + end + end + end - def lb_register_instance(credentials, opts={}) - ec2 = new_client( credentials, :elb) - safely do - ec2.register_instances_with_load_balancer(:instances => [opts[:instance_id]], - :load_balancer_name => opts[:id]) - load_balancer(credentials, :id => opts[:id]) - end - end + def create_storage_volume(credentials, opts=nil) + ec2 = new_client(credentials) + opts ||= {} + opts[:snapshot_id] ||= "" + opts[:capacity] ||= "1" + opts[:realm_id] ||= realms(credentials).first.id + safely do + convert_volume(ec2.create_volume(opts[:snapshot_id], opts[:capacity], opts[:realm_id])) + end + end - def lb_unregister_instance(credentials, opts={}) - ec2 = new_client( credentials, :elb) - safely do - ec2.deregister_instances_from_load_balancer(:instances => [opts[:instance_id]], - :load_balancer_name => opts[:id]) - load_balancer(credentials, :id => opts[:id]) - end - end + def destroy_storage_volume(credentials, opts={}) + ec2 = new_client(credentials) + safely do + unless ec2.delete_volume(opts[:id]) + raise Deltacloud::BackendError.new(500, "StorageVolume", "Cannot delete storage volume") + end + storage_volume(credentials, opts[:id]) + end + end - private - - def new_client(credentials, provider_type = :ec2) - opts = { - :access_key_id => credentials.user, - :secret_access_key => credentials.password - } - opts[:server] = ENV['DCLOUD_EC2_URL'] if ENV['DCLOUD_EC2_URL'] - safely do - case provider_type - when :ec2 - AWS::EC2::Base.new(opts) - when :elb - AWS::ELB::Base.new(opts) - end - end - end + def attach_storage_volume(credentials, opts={}) + ec2 = new_client(credentials) + safely do + convert_volume(ec2.attach_volume(opts[:id], opts[:instance_id], opts[:device])) + end + end - def convert_load_balancer(credentials, loadbalancer) - balancer_realms = loadbalancer.AvailabilityZones.member.collect do |m| - realm(credentials, m) - end - balancer = LoadBalancer.new({ - :id => loadbalancer['LoadBalancerName'], - :created_at => loadbalancer['CreatedTime'], - :public_addresses => [loadbalancer['DNSName']], - :realms => balancer_realms - }) - balancer.listeners = [] - balancer.instances = [] - loadbalancer.Listeners.member.each do |listener| - balancer.add_listener({ - :protocol => listener['Protocol'], - :load_balancer_port => listener['LoadBalancerPort'], - :instance_port => listener['InstancePort'] - }) - end - loadbalancer.Instances.member.each do |instance| - balancer.instances << instances(credentials, :id => instance['InstanceId']).first - end if loadbalancer.Instances - balancer - end + def detach_storage_volume(credentials, opts={}) + ec2 = new_client(credentials) + safely do + convert_volume(ec2.detach_volume(opts[:id], opts[:instance_id], opts[:device], true)) + end + end + def storage_snapshots(credentials, opts={}) + ec2 = new_client(credentials) + snapshot_list = (opts and opts[:id]) ? opts[:id] : [] + safely do + ec2.describe_snapshots(snapshot_list).collect do |snapshot| + convert_snapshot(snapshot) + end + end + end - def convert_key(key) - Key.new({ - :id => key['keyName'], - :fingerprint => key['keyFingerprint'], - :credential_type => :key, - :pem_rsa_key => key['keyMaterial'] - }) - end + def create_storage_snapshot(credentials, opts={}) + ec2 = new_client(credentials) + safely do + convert_snapshot(ec2.try_create_snapshot(opts[:volume_id])) + end + end - def convert_image(ec2_image) - Image.new( { - :id=>ec2_image['imageId'], - :name=>ec2_image['name'] || ec2_image['imageId'], - :description=>ec2_image['description'] || ec2_image['imageLocation'] || '', - :owner_id=>ec2_image['imageOwnerId'], - :architecture=>ec2_image['architecture'], - } ) - end + def destroy_storage_snapshot(credentials, opts={}) + ec2 = new_client(credentials) + safely do + unless convert_snapshot(opts[:id]) + raise Deltacloud::BackendError.new(500, "StorageSnapshot", "Cannot destroy this snapshot") + end + end + end - def convert_realm(ec2_realm) - Realm.new( { - :id=>ec2_realm['zoneName'], - :name=>ec2_realm['zoneName'], - :limit=>ec2_realm['zoneState'].eql?('available') ? :unlimited : 0, - :state=>ec2_realm['zoneState'].upcase, - } ) - end + private - def convert_state(ec2_state) - case ec2_state - when "terminated" - "STOPPED" - when "stopped" - "STOPPED" - when "running" - "RUNNING" - when "pending" - "PENDING" - when "shutting-down" - "STOPPED" - end - end + def new_client(credentials, type = :ec2) + case type + when :ec2 then Aws::Ec2.new(credentials.user, credentials.password) + when :s3 then Aws::S3.new(credentials.user, credentials.password) + end + end + + def convert_bucket(s3_bucket) + #get blob list: + blob_list = [] + s3_bucket.keys.each do |s3_object| + blob_list << s3_object.name + end + #can use AWS::S3::Owner.current.display_name or current.id + Bucket.new( + :id => s3_bucket.name, + :name => s3_bucket.name, + :size => s3_bucket.keys.length, + :blob_list => blob_list + ) + end - def convert_instance(ec2_instance, owner_id) - state = convert_state(ec2_instance['instanceState']['name']) - realm_id = ec2_instance['placement']['availabilityZone'] - (realm_id = nil ) if ( realm_id == '' ) - hwp_name = ec2_instance['instanceType'] - instance = Instance.new( { - :id=>ec2_instance['instanceId'], - :name => ec2_instance['imageId'], - :state=>state, - :image_id=>ec2_instance['imageId'], - :owner_id=>owner_id, - :realm_id=>realm_id, - :public_addresses=>( ec2_instance['dnsName'] == '' ? [] : [ec2_instance['dnsName']] ), - :private_addresses=>( ec2_instance['privateDnsName'] == '' ? [] : [ec2_instance['privateDnsName']] ), - :instance_profile =>InstanceProfile.new(hwp_name), - :actions=>instance_actions_for( state ), - :keyname => ec2_instance['keyName'], - :launch_time => ec2_instance['launchTime'] - } ) - instance.authn_error = "Key not set for instance" unless ec2_instance['keyName'] - return instance - end + def convert_realm(realm) + Realm.new( + :id => realm[:zone_name], + :name => realm[:zone_name], + :state => realm[:zone_state], + :limit => realm[:zone_state].eql?('available') ? :unlimited : 0 + ) + end - def convert_volume(ec2_volume) - StorageVolume.new( { - :id=>ec2_volume['volumeId'], - :created=>ec2_volume['createTime'], - :state=>ec2_volume['status'].upcase, - :capacity=>ec2_volume['size'], - :instance_id=>ec2_volume['snapshotId'], - :device=>ec2_volume['attachmentSet'], - } ) - end + def convert_image(image) + # There is not support for 'name' for now + Image.new( + :id => image[:aws_id], + :name => image[:aws_name] || image[:aws_id], + :description => image[:aws_description] || image[:aws_location], + :owner_id => image[:aws_owner], + :architecture => image[:aws_architecture], + :state => image[:state] + ) + end - def convert_snapshot(ec2_snapshot) - StorageSnapshot.new( { - :id=>ec2_snapshot['snapshotId'], - :state=>ec2_snapshot['status'].upcase, - :storage_volume_id=>ec2_snapshot['volumeId'], - :created=>ec2_snapshot['startTime'], - } ) - end + def convert_instance(instance) + Instance.new( + :id => instance[:aws_instance_id], + :name => instance[:aws_image_id], + :state => convert_state(instance[:aws_state]), + :image_id => instance[:aws_image_id], + :owner_id => instance[:aws_owner], + :actions => instance_actions_for(convert_state(instance[:aws_state])), + :key_name => instance[:ssh_key_name], + :launch_time => instance[:aws_launch_time], + :instance_profile => InstanceProfile.new(instance[:aws_instance_type]), + :realm_id => instance[:aws_availability_zone], + :private_addresses => instance[:private_dns_name], + :public_addresses => instance[:public_addresses] + ) + end - def s3_client(credentials) - safely do - s3_client = RightAws::S3.new(credentials.user, credentials.password) - end - end + def convert_key(key) + Key.new( + :id => key[:aws_key_name], + :fingerprint => key[:aws_fingerprint], + :credential_type => :key, + :pem_rsa_key => key[:aws_material], + :state => "AVAILABLE" + ) + end - def convert_bucket(s3_bucket) - #get blob list: - blob_list = [] - s3_bucket.keys.each do |s3_object| - blob_list << s3_object.name - end - #can use AWS::S3::Owner.current.display_name or current.id - Bucket.new( { :id => s3_bucket.name, - :name => s3_bucket.name, - :size => s3_bucket.keys.length, - :blob_list => blob_list - } - ) - end + def convert_volume(volume) + StorageVolume.new( + :id => volume[:aws_id], + :created => volume[:aws_created_at], + :state => volume[:aws_status] ? volume[:aws_status].upcase : 'unknown', + :capacity => volume[:aws_size], + :instance_id => volume[:aws_instance_id], + :realm_id => volume[:zone], + :device => volume[:aws_device], + # TODO: the available actions should be tied to the current + # volume state + :actions => [:attach, :detach, :destroy] + ) + end - def convert_object(s3_object) - Blob.new({ :id => s3_object.name, - :bucket => s3_object.bucket.name.to_s, - :content_length => s3_object.size, - :content_type => s3_object.content_type, - :last_modified => s3_object.last_modified - }) - end + def convert_snapshot(snapshot) + StorageSnapshot.new( + :id => snapshot[:aws_id], + :state => snapshot[:aws_status], + :storage_volume_id => snapshot[:aws_volume_id], + :created => snapshot[:aws_started_at] + ) + end - def catched_exceptions_list - { - :auth => [ AWS::AuthFailure ], - :error => [], - :glob => [ /AWS::(\w+)/ ] - } - end + def convert_state(ec2_state) + case ec2_state + when "terminated" + "STOPPED" + when "stopped" + "STOPPED" + when "running" + "RUNNING" + when "pending" + "PENDING" + when "shutting-down" + "STOPPED" + end + end -end + def catched_exceptions_list + { + :auth => [], # [ ::Aws::AuthFailure ], + :error => [ ::Aws::AwsError ], + :glob => [ /AWS::(\w+)/ ] + } + end + end end end end diff --git a/server/lib/deltacloud/models/storage_volume.rb b/server/lib/deltacloud/models/storage_volume.rb index 5e9e948..da96c6b 100644 --- a/server/lib/deltacloud/models/storage_volume.rb +++ b/server/lib/deltacloud/models/storage_volume.rb @@ -24,5 +24,7 @@ class StorageVolume < BaseModel attr_accessor :capacity attr_accessor :instance_id attr_accessor :device + attr_accessor :realm_id + attr_accessor :actions end diff --git a/server/server.rb b/server/server.rb index d3f7d8a..52892cd 100644 --- a/server/server.rb +++ b/server/server.rb @@ -373,6 +373,12 @@ END end +get '/api/storage_snapshots/new' do + respond_to do |format| + format.html { haml :"storage_snapshots/new" } + end +end + collection :storage_snapshots do description "Storage snapshots description here" @@ -389,6 +395,40 @@ collection :storage_snapshots do param :id, :string, :required control { show(:storage_snapshot) } end + + operation :create do + description "Create a new snapshot from volume" + with_capability :create_storage_snapshot + param :volume_id, :string, :required + control do + @storage_snapshot = driver.create_storage_snapshot(credentials, params) + show(:storage_snapshot) + end + end + + operation :destroy do + description "Delete storage snapshot" + with_capability :destroy_storage_snapshot + param :id, :string, :required + control do + driver.create_storage_snapshot(credentials, params) + redirect(storage_snapshot_url(params[:id])) + end + end + +end + +get '/api/storage_volumes/new' do + respond_to do |format| + format.html { haml :"storage_volumes/new" } + end +end + +get '/api/storage_volumes/attach' do + respond_to do |format| + @instances = driver.instances(credentials) + format.html { haml :"storage_volumes/attach" } + end end collection :storage_volumes do @@ -407,6 +447,56 @@ collection :storage_volumes do param :id, :string, :required control { show(:storage_volume) } end + + operation :create do + description "Create a new storage volume" + with_capability :create_storage_volume + param :snapshot_id, :string, :optional + param :capacity, :string, :optional + param :realm_id, :string, :optional + control do + @storage_volume = driver.create_storage_volume(credentials, params) + respond_to do |format| + format.html { haml :"storage_volumes/show" } + format.xml { haml :"storage_volumes/show" } + end + end + end + + operation :attach, :method => :post, :member => true do + description "Attach storage volume to instance" + with_capability :attach_storage_volume + param :id, :string, :required + param :instance_id,:string, :required + param :device, :string, :required + control do + driver.attach_storage_volume(credentials, params) + redirect(storage_volume_url(params[:id])) + end + end + + operation :detach, :method => :post, :member => true do + description "Detach storage volume to instance" + with_capability :detach_storage_volume + param :id, :string, :required + control do + volume = driver.storage_volume(credentials, :id => params[:id]) + driver.detach_storage_volume(credentials, :id => volume.id, :instance_id => volume.instance_id, + :device => volume.device) + redirect(storage_volume_url(params[:id])) + end + end + + operation :destroy do + description "Destroy storage volume" + with_capability :destroy_storage_volume + param :id, :string, :optional + control do + driver.destroy_storage_volume(credentials, params) + redirect(storage_volumes_url) + end + end + end get '/api/keys/new' do diff --git a/server/views/keys/index.html.haml b/server/views/keys/index.html.haml index 6f246a4..02f6b0c 100644 --- a/server/views/keys/index.html.haml +++ b/server/views/keys/index.html.haml @@ -18,7 +18,7 @@ = "#{key.username} - #{key.password}" %td - if driver.respond_to?(:destroy_key) - =link_to_action 'Destroy', destroy_key_url(key.id), :delete + =link_to 'Destroy', destroy_key_url(key.id), :class => 'delete' %tfoot - if driver.respond_to?(:create_key) %tr -- 1.7.3.2