incubator-deltacloud-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Sang-Min Park <sang-min.p...@eucalyptus.com>
Subject RE: [PATCH] initial eucalyptus support
Date Fri, 25 Feb 2011 05:59:59 GMT
The pull request to aws gem was accepted and released as aws-2.4.3.
So the functions listed in the previous email will all work fine.

Sang-min

-----Original Message-----
From: sang-min.park@eucalyptus.com [mailto:sang-min.park@eucalyptus.com]
Sent: Wednesday, February 23, 2011 10:56 PM
To: deltacloud-dev@incubator.apache.org
Cc: sang-min.park@eucalyptus.com; Sang-Min Park
Subject: [PATCH] initial eucalyptus support

From: Sang-Min Park <spark@eucalyptus.com>

---
 server/config/drivers.yaml                         |    4 +
 .../drivers/eucalyptus/eucalyptus_driver.rb        |  640
++++++++++++++++++++
 server/server.rb                                   |    4 +-
 3 files changed, 646 insertions(+), 2 deletions(-)  create mode 100644
server/lib/deltacloud/drivers/eucalyptus/eucalyptus_driver.rb

diff --git a/server/config/drivers.yaml b/server/config/drivers.yaml index
f28234c..e21ef47 100644
--- a/server/config/drivers.yaml
+++ b/server/config/drivers.yaml
@@ -28,6 +28,10 @@
   :name: Rackspace
 :azure:
   :name: Azure
+:eucalyptus:
+  :name: Eucalyptus
+  :username: Access Key ID
+  :password: Secret Access Key
 :ec2:
   :entrypoints:
     s3:
diff --git a/server/lib/deltacloud/drivers/eucalyptus/eucalyptus_driver.rb
b/server/lib/deltacloud/drivers/eucalyptus/eucalyptus_driver.rb
new file mode 100644
index 0000000..1b88bca
--- /dev/null
+++ b/server/lib/deltacloud/drivers/eucalyptus/eucalyptus_driver.rb
@@ -0,0 +1,640 @@
+# Copyright (C) 2009-2011 Eucalyptus Systems, Inc.
+#
+# Copyright (C) 2009, 2010  Red Hat, Inc.
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more #
+contributor license agreements.  See the NOTICE file distributed with #
+this work for additional information regarding copyright ownership.
+The # ASF licenses this file to you under the Apache License, Version
+2.0 (the # "License"); you may not use this file except in compliance
+with the # License.  You may obtain a copy of the License at #
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software #
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT # 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 'aws'
+
+class Instance
+  attr_accessor :keyname
+  attr_accessor :authn_error
+
+  def authn_feature_failed?
+    return true unless authn_error.nil?
+  end
+
+end
+
+module Deltacloud
+  module Drivers
+    module Eucalyptus
+      class EucalyptusDriver < Deltacloud::BaseDriver
+
+        def supported_collections
+          DEFAULT_COLLECTIONS + [ :keys, :buckets ]
+        end
+
+        feature :instances, :user_data
+        feature :instances, :authentication_key
+        feature :instances, :security_group
+        feature :instances, :instance_count
+        feature :images, :owner_id
+        feature :buckets, :bucket_location	
+        #feature :instances, :register_to_load_balancer  # not
+ supported by Eucalyptus 2.0.3
+
+        DEFAULT_REGION = 'us-east-1'
+
+        define_hardware_profile('m1.small') do
+          cpu                1
+          memory             128
+          storage            2
+          architecture       'x86_64'
+        end
+
+        define_hardware_profile('c1.medium') do
+          cpu                1
+          memory             256
+          storage            5
+          architecture       'x86_64'
+        end
+
+        define_hardware_profile('m1.large') do
+          cpu                2
+          memory             512
+          storage            10
+          architecture       'x86_64'
+        end
+
+        define_hardware_profile('m1.xlarge') do
+          cpu                2
+          memory             1024
+          storage            20
+          architecture       'x86_64'
+        end
+
+        define_hardware_profile('c1.xlarge') do
+          cpu                4
+          memory             2048
+          storage            20
+          architecture       'x86_64'
+        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
+
+        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
+            return img_arr
+          end
+          owner_id = opts[:owner_id] || "self"
+          safely do
+            img_arr = ec2.describe_images_by_owner(owner_id).collect do
|image|
+              convert_image(image)
+            end
+          end
+          #img_arr = filter_on( img_arr, :architecture, opts )
+          img_arr.sort_by { |e| [e.owner_id, e.name] }
+        end
+
+        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
+
+        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
+          filter_on( inst_arr, :state, opts )
+        end
+
+        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[:keyname]) if
opts[:keyname]
+          instance_options.merge!(:availability_zone => opts[:realm_id])
if opts[:realm_id]
+          instance_options.merge!(:instance_type => opts[:hwp_id]) if
opts[:hwp_id] && opts[:hwp_id].length > 0
+          instance_options.merge!(:group_ids => opts[:security_group]) if
opts[:security_group]
+          instance_options.merge!(
+            :min_count => opts[:instance_count],
+            :max_count => opts[:instance_count]
+          ) if opts[:instance_count] and opts[:instance_count].length!=0
+          safely do
+            new_instance =
convert_instance(ec2.launch_instances(image_id, instance_options).first)
+            # TODO: Rework this to use client_id for name instead of tag
+            #       Tags should be keept as an optional feature
+            #if opts[:name]
+            #  tag_instance(credentials, new_instance, opts[:name])
+            #end
+            # Register Instance to Load Balancer if load_balancer_id is
set.
+            # This parameter is a feature parameter
+            #if opts[:load_balancer_id] and
opts[:load_balancer_id].length>0
+            #  elb = new_client(credentials, :elb)
+            #
elb.register_instances_with_load_balancer(opts[:load_balancer_id],
[new_instance.id])
+            #end
+            new_instance
+          end
+        end
+
+        def run_on_instance(credentials, opts={})
+          target = instance(credentials, :id => opts[:id])
+          param = {}
+          param[:credentials] = {
+            :username => 'root', # Default for EC2 Linux instances
+          }
+          param[:port] = opts[:port] || '22'
+          param[:ip] = target.public_addresses
+          param[:private_key] = (opts[:private_key].length > 1) ?
opts[:private_key] : nil
+          safely do
+            Deltacloud::Runner.execute(opts[:cmd], param)
+          end
+        end
+
+        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
+
+        def destroy_instance(credentials, instance_id)
+          ec2 = new_client(credentials)
+          instance_id = instance_id
+          if ec2.terminate_instances([instance_id])
+            #untag_instance(credentials, instance_id)
+            instance(credentials, instance_id)
+          else
+            raise Deltacloud::BackendError.new(500, "Instance", "Instance
cannot be terminated", "")
+          end
+        end
+
+        alias :stop_instance :destroy_instance
+
+        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 create_key(credentials, opts={})
+          ec2 = new_client(credentials)
+          safely do
+            convert_key(ec2.create_key_pair(opts[:key_name]))
+          end
+        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
+
+        def load_balancer(credentials, opts={})
+	    raise Deltacloud::BackendError.new(500, "Loadbalancer",
"Loadbalancer not supported yet", "")
+        end
+
+        def load_balancers(credentials, opts=nil)
+ 	   raise Deltacloud::BackendError.new(500, "Loadbalancer",
"Loadbalancer not supported yet", "")
+        end
+
+        def create_load_balancer(credentials, opts={})
+	    raise Deltacloud::BackendError.new(500, "Loadbalancer",
"Loadbalancer not supported yet", "")
+        end
+
+        def destroy_load_balancer(credentials, id)
+	    raise Deltacloud::BackendError.new(500, "Loadbalancer",
"Loadbalancer not supported yet", "")
+        end
+
+        def lb_register_instance(credentials, opts={})
+	    raise Deltacloud::BackendError.new(500, "Loadbalancer",
"Loadbalancer not supported yet", "")
+        end
+
+        def lb_unregister_instance(credentials, opts={})
+	    raise Deltacloud::BackendError.new(500, "Loadbalancer",
"Loadbalancer not supported yet", "")
+        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
+	  buckets
+        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
+
+        def delete_bucket(credentials, name, opts={})
+          s3_client = new_client(credentials, :s3)
+          safely do
+            s3_client.interface.delete_bucket(name)
+          end
+        end
+
+        def blobs(credentials, opts = {})
+          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
+
+        #--
+        # Create Blob
+        #--
+        def create_blob(credentials, bucket_id, blob_id, data = nil, opts
= {})
+          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')
+          #insert ec2-specific header for user metadata ...
x-amz-meta-KEY = VALUE
+          opts.gsub_keys('HTTP_X_Deltacloud_Blobmeta_', 'x-amz-meta-')
+          opts["Content-Type"] = data[:type]
+          safely do
+            res = s3_client.interface.put(bucket_id,
+                                        blob_id,
+                                        file,
+                                        opts)
+          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 => '',
+                      :user_metadata => opts.select{|k,v|
k.match(/^x-amz-meta-/i)}
+                    }
+                  )
+        end
+
+        #--
+        # Delete Blob
+        #--
+        def delete_blob(credentials, bucket_id, blob_id, opts={})
+          s3_client = new_client(credentials, :s3)
+          safely do
+            s3_client.interface.delete(bucket_id, blob_id)
+          end
+        end
+
+
+        def blob_data(credentials, bucket_id, blob_id, opts={})
+          s3_client = new_client(credentials, :s3)
+          safely do
+            s3_client.interface.get(bucket_id, blob_id) do |chunk|
+              yield chunk
+            end
+          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 create_storage_volume(credentials, opts=nil)
+          ec2 = new_client(credentials)
+          opts ||= {}
+          opts[:snapshot_id] ||= ""# -> this causes signature validation
error with Euca backend
+          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 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
+
+        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 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 create_storage_snapshot(credentials, opts={})
+          ec2 = new_client(credentials)
+          safely do
+            convert_snapshot(ec2.try_create_snapshot(opts[:volume_id]))
+          end
+        end
+
+        def destroy_storage_snapshot(credentials, opts={})
+          ec2 = new_client(credentials)
+          safely do
+            unless ec2.delete_snapshot(opts[:id])
+              raise Deltacloud::BackendError.new(500, "StorageSnapshot",
"Cannot destroy this snapshot")
+            end
+          end
+        end
+
+        def valid_credentials?(credentials)
+          retval = true
+          begin
+            realms(credentials)
+          rescue Deltacloud::BackendError
+            retval = false
+          end
+          retval
+        end
+
+        private
+
+        def new_client(credentials, type = :ec2)
+          klass = case type
+                    when :elb then Aws::Elb
+                    when :ec2 then Aws::Ec2
+                    when :s3 then Aws::S3
+                  end
+          klass.new(credentials.user, credentials.password,
+eucalyptus_endpoint) #:server => endpoint_for_service(type))
+        end
+
+        def eucalyptus_endpoint
+          endpoint = (Thread.current[:provider] || ENV['API_PROVIDER'])
+	  #parse endpoint string into server, port, service, and protocol
+	  if endpoint
+	  	{:server => URI.parse(endpoint).host, :port =>
URI.parse(endpoint).port, :service => URI.parse(endpoint).path, :protocol
=> URI.parse(endpoint).scheme}
+	  else
+		{ } #EC2_URL env variable will be used by AWS
+	  end
+        end
+
+	# shouldn't be called now; eucalyptus doens't support tagging yet
+        def tag_instance(credentials, instance, name)
+          ec2 = new_client(credentials)
+          safely do
+            ec2.create_tag(instance.id, 'name', name)
+          end
+        end
+
+        # shouldn't be called now; eucalyptus doens't support tagging yet
+        def untag_instance(credentials, instance_id)
+          ec2 = new_client(credentials)
+          safely do
+            ec2.delete_tag(instance_id, 'name')
+          end
+        end
+
+        # shouldn't be called now; eucalyptus doens't support tagging yet
+        def delete_unused_tags(credentials, inst_ids)
+          ec2 = new_client(credentials)
+          tags = []
+          safely do
+            tags = ec2.describe_tags('Filter.1.Name' => 'resource-type',
'Filter.1.Value' => 'instance')
+            tags.collect! { |t| t[:aws_resource_id] }
+            inst_ids.each do |inst_id|
+              unless tags.include?(inst_id)
+                ec2.delete_tag(inst_id, 'name')
+              end
+            end
+          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_object(s3_object)
+          Blob.new(
+            :id => s3_object.name,
+            :bucket => s3_object.bucket.name.to_s,
+            :content_length => s3_object.headers['content-length'],
+            :content_type => s3_object.headers['content-type'],
+            :last_modified => s3_object.last_modified,
+            :user_metadata => s3_object.meta_headers
+          )
+        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_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_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[:dns_name]
+          )
+        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_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_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 convert_load_balancer(credentials, loadbalancer)
+          puts loadbalancer.inspect
+          realms = []
+          balancer_realms = loadbalancer[:availability_zones].each do
|zone|
+            realms << realm(credentials, zone)
+          end
+          balancer = LoadBalancer.new({
+            :id => loadbalancer[:name],
+            :created_at => loadbalancer[:created_time],
+            :public_addresses => [loadbalancer[:dns_name]],
+            :realms => realms
+          })
+          balancer.listeners = []
+          balancer.instances = []
+          loadbalancer[:listeners].each do |listener|
+            balancer.add_listener(listener)
+          end
+          loadbalancer[:instances].each do |instance|
+            balancer.instances << instance(credentials, :id =>
instance[:id])
+          end
+          balancer
+        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
+
+        def catched_exceptions_list
+          {
+            :auth => [], # [ ::Aws::AuthFailure ],
+            :error => [ ::Aws::AwsError ],
+            :glob => [ /AWS::(\w+)/, /Deltacloud::Runner::(\w+)/ ]
+          }
+        end
+
+      end
+    end
+  end
+end
diff --git a/server/server.rb b/server/server.rb index fe723c5..ff7c988
100644
--- a/server/server.rb
+++ b/server/server.rb
@@ -478,8 +478,8 @@ collection :storage_snapshots do
     with_capability :destroy_storage_snapshot
     param :id,  :string,  :required
     control do
-      driver.create_storage_snapshot(credentials, params)
-      redirect(storage_snapshot_url(params[:id]))
+      driver.destroy_storage_snapshot(credentials, params)
+      redirect(storage_snapshots_url)
     end
   end

--
1.7.1

Mime
View raw message