You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@deltacloud.apache.org by mf...@apache.org on 2012/05/25 09:34:06 UTC

[3/4] git commit: EC2: Added initial support for EC2 frontend

EC2: Added initial support for EC2 frontend


Project: http://git-wip-us.apache.org/repos/asf/deltacloud/repo
Commit: http://git-wip-us.apache.org/repos/asf/deltacloud/commit/3baf29ca
Tree: http://git-wip-us.apache.org/repos/asf/deltacloud/tree/3baf29ca
Diff: http://git-wip-us.apache.org/repos/asf/deltacloud/diff/3baf29ca

Branch: refs/heads/master
Commit: 3baf29ca80817c1279ae71d1e4db9f56c6b072ca
Parents: 3c05173
Author: Michal Fojtik <mf...@redhat.com>
Authored: Thu May 10 17:19:04 2012 +0200
Committer: Michal fojtik <mf...@redhat.com>
Committed: Fri May 25 09:33:50 2012 +0200

----------------------------------------------------------------------
 server/bin/deltacloudd              |    4 +-
 server/config.ru                    |    8 ++
 server/lib/ec2/helpers.rb           |   22 +++++
 server/lib/ec2/helpers/converter.rb |  140 ++++++++++++++++++++++++++++++
 server/lib/ec2/helpers/errors.rb    |   46 ++++++++++
 server/lib/ec2/query_parser.rb      |  129 +++++++++++++++++++++++++++
 server/lib/ec2/server.rb            |   60 +++++++++++++
 7 files changed, 407 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/deltacloud/blob/3baf29ca/server/bin/deltacloudd
----------------------------------------------------------------------
diff --git a/server/bin/deltacloudd b/server/bin/deltacloudd
index 57b6c49..8eb448d 100755
--- a/server/bin/deltacloudd
+++ b/server/bin/deltacloudd
@@ -61,8 +61,8 @@ BANNER
   opts.on( '-P', '--provider PROVIDER', 'Use PROVIDER (default is set in the driver)') do |provider|
     ENV['API_PROVIDER'] = provider
   end
-  opts.on('--cimi', 'USe the DMTF CIMI frontend, not the Deltacloud frontend') do
-    ENV['API_FRONTEND'] = 'cimi'
+  opts.on('-f', '--frontend FRONTEND', 'Use the different frontend, not the Deltacloud (cimi or ec2)') do |frontend|
+    ENV['API_FRONTEND'] = frontend
   end
   opts.on( '-c', '--config [FILE]', 'Read provider and other config from FILE (default: ~/.deltacloud/config)') do |config|
     options[:config] = File::expand_path(config || DEFAULT_CONFIG)

http://git-wip-us.apache.org/repos/asf/deltacloud/blob/3baf29ca/server/config.ru
----------------------------------------------------------------------
diff --git a/server/config.ru b/server/config.ru
index 87d9506..d3c6ce6 100644
--- a/server/config.ru
+++ b/server/config.ru
@@ -35,6 +35,14 @@ if ENV['API_FRONTEND'] == 'cimi'
   end
 end
 
+if ENV['API_FRONTEND'] == 'ec2'
+  Deltacloud::configure do |server|
+    server.root_url '/'
+    server.version '2012-04-01'
+    server.klass 'Deltacloud::EC2::API'
+  end
+end
+
 Deltacloud.require_frontend!
 
 class IndexEntrypoint < Sinatra::Base

http://git-wip-us.apache.org/repos/asf/deltacloud/blob/3baf29ca/server/lib/ec2/helpers.rb
----------------------------------------------------------------------
diff --git a/server/lib/ec2/helpers.rb b/server/lib/ec2/helpers.rb
new file mode 100644
index 0000000..7517f36
--- /dev/null
+++ b/server/lib/ec2/helpers.rb
@@ -0,0 +1,22 @@
+# 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.
+
+module Deltacloud
+  module EC2; end
+end
+
+require_relative './helpers/errors'
+require_relative '../deltacloud/helpers/driver_helper'
+require_relative '../deltacloud/helpers/auth_helper'

http://git-wip-us.apache.org/repos/asf/deltacloud/blob/3baf29ca/server/lib/ec2/helpers/converter.rb
----------------------------------------------------------------------
diff --git a/server/lib/ec2/helpers/converter.rb b/server/lib/ec2/helpers/converter.rb
new file mode 100644
index 0000000..8a70fe1
--- /dev/null
+++ b/server/lib/ec2/helpers/converter.rb
@@ -0,0 +1,140 @@
+# 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.
+
+module Deltacloud::EC2
+
+  class Converter
+
+    def self.convert(builder, action, result)
+      klass_name = ActionHandler::MAPPINGS[action][:method].to_s.camelize
+      klass = Converter.const_get(klass_name)
+      klass.new(builder, result).to_xml
+    end
+
+    class Base
+
+      attr_reader :xml
+      attr_reader :obj
+
+      def initialize(builder, object)
+        @xml = builder
+        @obj = object
+      end
+
+    end
+
+    class Realms < Base
+
+      def to_xml
+        xml.availabilityZoneInfo {
+          obj.each do |item|
+            xml.item {
+              xml.zoneName item.id
+              xml.zoneState item.state
+              xml.regionName item.name
+            }
+          end
+        }
+      end
+
+    end
+
+    class Images < Base
+
+      def to_xml
+        xml.imagesSet {
+          obj.each do |item|
+            xml.item {
+              xml.imageId item.id
+              xml.imageState item.state.downcase
+              xml.imageOwnerId item.owner_id
+              xml.architecture item.architecture
+              xml.imageType 'machine'
+              xml.name item.name
+              xml.description item.description
+            }
+          end
+        }
+      end
+
+    end
+
+    class CreateInstance < Base
+
+      def to_xml
+        xml.reservationId 'r-11111111'
+        xml.ownerId @obj.owner_id
+        xml.groupSet {
+          xml.item {
+            xml.groupId 'sg-11111111'
+            xml.groupName 'default'
+          }
+        }
+        Instances.new(@xml, [@obj]).instance_set
+      end
+
+    end
+
+    class Instances < Base
+
+      def instance_set
+        xml.instancesSet {
+          obj.each do |item|
+            xml.item {
+              xml.instanceId item.id
+              xml.imageId item.image_id
+              xml.instanceType item.instance_profile.name
+              xml.launchTime item.launch_time
+              xml.ipAddress item.public_addresses.first.address
+              xml.privateIpAddress item.public_addresses.first.address
+              xml.dnsName item.public_addresses.first.address
+              xml.privateDnsName item.private_addresses.first.address
+              xml.architecture item.instance_profile.architecture
+              xml.keyName item.keyname
+              xml.instanceState {
+                xml.code '16'
+                xml.name item.state.downcase
+              }
+              xml.placement {
+                xml.availabilityZone item.realm_id
+                xml.groupName
+                xml.tenancy 'default'
+              }
+            }
+          end
+        }
+      end
+
+      def to_xml
+        xml.reservationSet {
+          xml.item {
+            xml.reservationId 'r-11111111'
+            xml.ownerId 'deltacloud'
+            xml.groupSet {
+              xml.item {
+                xml.groupId 'sg-11111111'
+                xml.groupName 'default'
+              }
+            }
+            self.instance_set
+          }
+        }
+      end
+
+    end
+
+  end
+
+end

http://git-wip-us.apache.org/repos/asf/deltacloud/blob/3baf29ca/server/lib/ec2/helpers/errors.rb
----------------------------------------------------------------------
diff --git a/server/lib/ec2/helpers/errors.rb b/server/lib/ec2/helpers/errors.rb
new file mode 100644
index 0000000..7f8d610
--- /dev/null
+++ b/server/lib/ec2/helpers/errors.rb
@@ -0,0 +1,46 @@
+# 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.
+
+module Deltacloud::EC2
+  module Errors
+
+    def report_error(code)
+      error = (request.env['sinatra.error'] || @exception)
+      code = 500 if not code and not error.class.method_defined? :code
+      Nokogiri::XML::Builder.new do |xml|
+        xml.send(:Response) {
+          xml.send(:Errors) {
+            xml.send(:Code, error_for_code(code))
+            xml.send(:Message, error.respond_to?(:message) ? error.message : '')
+          }
+          xml.send(:RequestID, request_id)
+        }
+      end.to_xml
+    end
+
+    def request_id
+      Digest::MD5.hexdigest("#{request.env['REMOTE_ADDR']}#{request.env['HTTP_USER_AGENT']}#{Time.now.to_i}#{rand(250)}")
+    end
+
+    def error_for_code(code)
+      case code
+        when 401 then 'AuthFailure'
+        when 500 then 'InternalError'
+        else "Unavailable (#{code})"
+      end
+    end
+
+  end
+end

http://git-wip-us.apache.org/repos/asf/deltacloud/blob/3baf29ca/server/lib/ec2/query_parser.rb
----------------------------------------------------------------------
diff --git a/server/lib/ec2/query_parser.rb b/server/lib/ec2/query_parser.rb
new file mode 100644
index 0000000..7eebc9a
--- /dev/null
+++ b/server/lib/ec2/query_parser.rb
@@ -0,0 +1,129 @@
+# 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.
+
+module Deltacloud::EC2
+
+  class ActionHandler
+
+    MAPPINGS = {
+      :describe_availability_zones => { :method => :realms, :params => { 'ZoneName.1' => :id } },
+      :describe_images => { :method => :images, :params => { 'ImageId.1' => :id }},
+      :describe_instances => { :method => :instances, :params => {} },
+      :run_instances => { :method => :create_instance, :params => { 'ImageId' => :image_id, 'InstanceType' => :hwp_id, 'Placement.AvailabilityZone' => :realm_id }}
+    }
+
+    attr_reader :action
+
+    def initialize(action)
+      @action = action
+    end
+
+    def deltacloud_method
+      MAPPINGS[action.action][:method]
+    end
+
+    def deltacloud_method_params
+      MAPPINGS[action.action][:params].inject({}) do |result, p|
+        result[p.last] = action.parameters.delete(p.first)
+        result.delete_if { |k,v| v.nil? }
+      end
+    end
+
+    def perform!(credentials, driver)
+      @result = case deltacloud_method
+        when :create_instance then driver.send(deltacloud_method, credentials, deltacloud_method_params.delete(:image_id), deltacloud_method_params)
+        else driver.send(deltacloud_method, credentials, deltacloud_method_params)
+      end
+    end
+
+    def to_xml
+      ResultParser.parse(action, @result).to_xml
+    end
+
+  end
+
+  class ResultParser
+
+    def self.parse(parser, result)
+      Nokogiri::XML::Builder.new do |xml|
+        xml.send(:"#{parser.action.to_s.camelize}Response", :xmlns => 'http://ec2.amazonaws.com/doc/2012-04-01/') {
+          xml.requestId parser.request_id
+          new(xml, parser, result).build_xml
+        }
+      end
+    end
+
+    def initialize(xml, parser, result)
+      @builder = xml
+      @parser = parser
+      @result = result
+    end
+
+    def build_xml
+      Converter.convert(@builder, @parser.action, @result)
+    end
+
+  end
+
+  class QueryParser
+
+    def self.parse(params, request_id)
+      parser = new(request_id, params)
+      unless parser.valid_action?
+        raise 'Invalid action (%s)' % parser.action
+      else
+        ActionHandler.new(parser)
+      end
+    end
+
+    attr_reader :action
+    attr_reader :parameters
+    attr_reader :version
+    attr_reader :expiration
+    attr_reader :authentication
+    attr_reader :request_id
+
+    def initialize(request_id, params={})
+      @request_id = request_id
+      @action = (params.delete('Action') || 'Unknown').underscore.intern
+      @version = params.delete('Version')
+      @authentication = {
+        :security_token => params.delete('SecurityToken'),
+        :access_key_id => params.delete('AWSAccessKeyId'),
+        :signature => {
+          :version => params.delete('SignatureVersion'),
+          :value => params.delete('Signature'),
+          :method => params.delete('SignatureMethod'),
+          :timestamp => params.delete('Timestamp')
+        }
+      }
+      @expiration = params.delete('Expires')
+      @parameters = params
+    end
+
+    def valid_actions
+      ActionHandler::MAPPINGS.keys
+    end
+
+    def valid_action?
+      return false if @action == :unknown
+      return false unless valid_actions.include?(@action)
+      true
+    end
+
+
+  end
+
+end

http://git-wip-us.apache.org/repos/asf/deltacloud/blob/3baf29ca/server/lib/ec2/server.rb
----------------------------------------------------------------------
diff --git a/server/lib/ec2/server.rb b/server/lib/ec2/server.rb
new file mode 100644
index 0000000..29fc545
--- /dev/null
+++ b/server/lib/ec2/server.rb
@@ -0,0 +1,60 @@
+# 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 'rubygems'
+require 'nokogiri'
+require 'sinatra/base'
+
+require_relative '../sinatra'
+require_relative './helpers'
+require_relative './query_parser'
+require_relative '../deltacloud/models'
+require_relative '../deltacloud/drivers'
+
+module Deltacloud::EC2
+  class API < Sinatra::Base
+
+    extend Deltacloud::Helpers::Drivers
+
+    use Rack::ETag
+    use Rack::CommonLogger
+
+    helpers Sinatra::AuthHelper
+    helpers Deltacloud::Helpers::Drivers
+    helpers Deltacloud::EC2::Errors
+
+    enable :xhtml
+    enable :dump_errors
+    enable :show_errors
+    enable :method_override
+    disable :show_exceptions
+
+    set :version, Deltacloud[:version]
+    set :root_url, Deltacloud[:root_url]
+    set :root, File.join(File.dirname(__FILE__), '..', '..')
+
+    after do
+      headers 'Server' => 'Apache-Deltacloud-EC2/' + settings.version
+    end
+
+    get '/' do
+      content_type :xml
+      ec2_action = QueryParser.parse(params, request_id)
+      ec2_action.perform!(credentials, driver)
+      ec2_action.to_xml
+    end
+
+  end
+end