You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@deltacloud.apache.org by mf...@redhat.com on 2010/08/27 14:55:28 UTC

IP managment support (rev2)

Hi,

I squeezed previous patchset to 2 patches to keep this thread clean.
New patches adds new features likes assigning instance to IP for EC2
and small improvement to /api/instances/new (now when you don't set
'image_id' parameter instead of choosing first image (tomcat) you
can choose image you want to launch from select box)

But the most interesting feature is a new Rabbit method called
'disable_if_not' ;-) (gimme better name...) and it's suppose is
to disable entire collection operation if given method is
not present in driver.

Well it will not be 'literally' disabled. Operation still remains
in collection. Inovation is, that when you request for HEAD on
collection (HEAD /api/ip_addresses) all HTTP methods from 
operations which are disabled will be not present in 'Allow' header.
This could be easely picked up by client.

Mispelled 'destroy_ip_addresss' and 'create_ip_addresss' are just
for demostration suppose. To try this patches, you need to install
a 'Poster' extension to Firefox (or use Curl) and request for:

HEAD "/api/ip_addresses"

Response header should include "Allow: HEAD, GET" instead of
"Allow: HEAD, POST, DELETE, GET".

So as you can see, DELETE and POST methods are not here and therefore
collection operations assigned to them are not supported.

Please let me know if this approach is correct.
If so, I'll update client library to support HEAD request.

 -- Michal



[PATCH core 5/5] Added 'disable_if_not' method into Rabbit DSL.

Posted by mf...@redhat.com.
---
 server/lib/sinatra/rabbit.rb |   34 ++++++++++++++++++++++++++++++----
 server/server.rb             |   10 +++++++++-
 2 files changed, 39 insertions(+), 5 deletions(-)

diff --git a/server/lib/sinatra/rabbit.rb b/server/lib/sinatra/rabbit.rb
index 91d48da..4d52f5d 100644
--- a/server/lib/sinatra/rabbit.rb
+++ b/server/lib/sinatra/rabbit.rb
@@ -25,13 +25,14 @@ module Sinatra
 
       def initialize(coll, name, opts, &block)
         @name = name.to_sym
+        @disabled = false
         opts = STANDARD[@name].merge(opts) if standard?
         @collection = coll
         raise "No method for operation #{name}" unless opts[:method]
         @method = opts[:method].to_sym
         @member = opts[:member]
         @description = ""
-        instance_eval(&block) if block_given?
+        instance_eval(&block) if block_given? and not disabled?
         generate_documentation
       end
 
@@ -39,6 +40,10 @@ module Sinatra
         STANDARD.keys.include?(name)
       end
 
+      def disabled?
+        @disabled
+      end
+
       def description(text="")
         return @description if text.blank?
         @description = text
@@ -63,6 +68,17 @@ module Sinatra
         end
       end
 
+      # Exclude this operation HTTP method from HEAD if current driver
+      # is not supporting given method
+      #
+      # Examples: 
+      # disable_if_not :create_instance
+      # HEAD /api/instances => Allow: GET, DELETE
+      
+      def disable_if_not(method)
+        @disabled = true unless driver.respond_to?(method.to_s)
+      end
+
       def prefix
         # FIXME: Make the /api prefix configurable
         "/api"
@@ -85,6 +101,7 @@ module Sinatra
         ::Sinatra::Application.send(@method, path, {}, &@control)
         # Set up some Rails-like URL helpers
         if name == :index
+
           gen_route "#{@collection.name}_url"
         elsif name == :show
           gen_route "#{@collection.name.to_s.singularize}_url"
@@ -117,6 +134,7 @@ module Sinatra
         @name = name
         @description = ""
         @operations = {}
+        @methods = []
         instance_eval(&block) if block_given?
         generate_documentation
       end
@@ -157,17 +175,25 @@ module Sinatra
       # the URL to this operation (in request context)
       def operation(name, opts = {}, &block)
         raise DuplicateOperationException if @operations[name]
-        @operations[name] = Operation.new(self, name, opts, &block)
+        o = Operation.new(self, name, opts, &block)
+        # Add operation HTTP method to collection
+        @methods << o.method.to_s.upcase unless o.disabled?
+        @operations[name] = o
       end
 
       def generate
+        collname = name # Work around Ruby's weird scoping/capture
+        methods = @methods
+        # Generate HEAD route and supported HTTP methods for current
+        # collection
+        ::Sinatra::Application.send(:head, "/api/#{collname}", {}) do
+          headers 'Allow' => 'HEAD,'+methods.uniq.join(',')
+        end
         operations.values.each { |op| op.generate }
         app = ::Sinatra::Application
-        collname = name # Work around Ruby's weird scoping/capture
         app.send(:define_method, "#{name.to_s.singularize}_url") do |id|
             url_for "/api/#{collname}/#{id}", :full
         end
-
         if index_op = operations[:index]
           app.send(:define_method, "#{name}_url") do
             url_for index_op.path.gsub(/\/\?$/,''), :full
diff --git a/server/server.rb b/server/server.rb
index 130ecb1..6007959 100644
--- a/server/server.rb
+++ b/server/server.rb
@@ -2,8 +2,8 @@ require 'sinatra'
 require 'deltacloud'
 require 'drivers'
 require 'json'
-require 'sinatra/respond_to'
 require 'sinatra/static_assets'
+require 'sinatra/respond_to'
 require 'sinatra/rabbit'
 require 'sinatra/lazy_auth'
 require 'erb'
@@ -353,6 +353,12 @@ collection :keys do
 
 end
 
+get '/api/ip_addresses/new' do
+  respond_to do |format|
+    format.html { haml :"ip_addresses/new" }
+  end
+end
+
 collection :ip_addresses do
   description "Manage IP addresses"
 
@@ -374,6 +380,7 @@ collection :ip_addresses do
   operation :create do
     description "Create a new IP address"
     param  :ip,  :string,  :optional
+    disable_if_not :create_ip_addresss
     control do
       @ip_address = driver.create_ip_address(credentials, params)
       show :ip_address
@@ -383,6 +390,7 @@ collection :ip_addresses do
   operation :destroy do
     description "Destroy IP address"
     param  :id,  :string,  :required
+    disable_if_not :destroy_ip_addresss
     control do
       @ip_address = driver.destroy_ip_address(credentials, params)
       redirect ip_addresses_url
-- 
1.7.2.2


[PATCH core 4/5] Fixed helper to support checking features of other collections than instances

Posted by mf...@redhat.com.
---
 .../lib/deltacloud/helpers/application_helper.rb   |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/server/lib/deltacloud/helpers/application_helper.rb b/server/lib/deltacloud/helpers/application_helper.rb
index 8b525ff..26c0a7b 100644
--- a/server/lib/deltacloud/helpers/application_helper.rb
+++ b/server/lib/deltacloud/helpers/application_helper.rb
@@ -41,8 +41,8 @@ module ApplicationHelper
     collections[:instances].operations[action.to_sym].method
   end
 
-  def driver_has_feature?(feature_name)
-    not driver.features(:instances).select{ |f| f.name.eql?(feature_name) }.empty?
+  def driver_has_feature?(feature_name, collection=:instances)
+    not driver.features(collection).select{ |f| f.name.eql?(feature_name) }.empty?
   end
 
   def driver_has_auth_features?
-- 
1.7.2.2


[PATCH core 3/5] Updated views to support assigning instances for EC2

Posted by mf...@redhat.com.
---
 server/public/javascripts/application.js  |   20 +++++++++++++++++++-
 server/views/instances/new.html.haml      |   14 ++++++++++----
 server/views/ip_addresses/index.html.haml |    5 ++++-
 server/views/ip_addresses/new.html.haml   |    9 +++++++++
 4 files changed, 42 insertions(+), 6 deletions(-)
 create mode 100644 server/views/ip_addresses/new.html.haml

diff --git a/server/public/javascripts/application.js b/server/public/javascripts/application.js
index 19ee379..cff7452 100644
--- a/server/public/javascripts/application.js
+++ b/server/public/javascripts/application.js
@@ -31,11 +31,29 @@ $(document).ready(function() {
 
   if ($("select[name=ip_address_id]").length) {
     $.getJSON('/api/ip_addresses', { format : 'json', state : 'unassigned', type : 'public'}, function(data) {
-      $("select[name=ip_address_id]").empty()
+      $("select[name=ip_address_id]").html('<option value="" selected="selected"></option>')
       $.each(data.ip_addresses, function(i, ip_address){
         $("select[name=ip_address_id]").append('<option value="'+ip_address.id+'">'+ip_address.address+'</option>')
       }) 
     }) 
   }
 
+  if ($("select[name=instance_id]").length) {
+    $.getJSON('/api/instances', { format : 'json', state : 'RUNNING'}, function(data) {
+      $("select[name=instance_id]").html('<option value="" selected="selected">No Instance</option>')
+      $.each(data.instances, function(i, instance){
+        $("select[name=instance_id]").append('<option value="'+instance.id+'">'+instance.id+'</option>')
+      }) 
+    }) 
+  }
+
+  if ($("select[name=image_id]").length) {
+    $.getJSON('/api/images', { format : 'json'}, function(data) {
+     $("select[name=image_id]").empty()
+      $.each(data.images, function(i, image){
+        $("select[name=image_id]").append('<option value="'+image.id+'">'+image.description+'</option>')
+      }) 
+    }) 
+  }
+
 })
diff --git a/server/views/instances/new.html.haml b/server/views/instances/new.html.haml
index cf79146..c1a1b8b 100644
--- a/server/views/instances/new.html.haml
+++ b/server/views/instances/new.html.haml
@@ -1,10 +1,16 @@
 %h1 New Instance
 
-%h2= @image.id
-%h3= @image.description
-
 %form{ :action => instances_url, :method => :post }
-  %input{ :name => :image_id, :type => :hidden, :value => @instance.image_id }/
+  - if params[:image_id]
+    %h2= @image.id
+    %h3= @image.description
+    %input{ :name => :image_id, :type => :hidden, :value => @instance.image_id }/
+  - else
+    %p
+      %label
+        Image:
+      %select{ :name => :image_id}
+        %option Loading images...
   %p
     %label
       Instance Name:
diff --git a/server/views/ip_addresses/index.html.haml b/server/views/ip_addresses/index.html.haml
index b0a430b..f66113c 100644
--- a/server/views/ip_addresses/index.html.haml
+++ b/server/views/ip_addresses/index.html.haml
@@ -24,4 +24,7 @@
   %tfoot
     %tr
       %td{:colspan => 5, :style => "text-align:right;"}
-        =link_to 'Create &raquo;', create_ip_address_url, :class => 'post'
+        -if driver_has_feature?(:assign_instance, :ip_addresses)
+          =link_to 'Create &raquo;', url_for('/api/ip_addresses/new')
+        -else
+          =link_to 'Create &raquo;', create_ip_address_url, :class => 'post'
diff --git a/server/views/ip_addresses/new.html.haml b/server/views/ip_addresses/new.html.haml
new file mode 100644
index 0000000..7348d7e
--- /dev/null
+++ b/server/views/ip_addresses/new.html.haml
@@ -0,0 +1,9 @@
+%h1 New IP Address
+
+%form{ :action => '/api/ip_addresses', :method => :post }
+  %p
+    %label
+      Instance to associate:
+    %select{ :name => 'instance_id'}
+      %option Loading...
+    %input{ :type => :submit, :name => "commit", :value => "create" }/
-- 
1.7.2.2


[PATCH core 1/5] Fixed gitignore filename IP managment for GoGrid driver

Posted by mf...@redhat.com.
---
 server/.gitiginore                                 |    4 --
 server/.gitignore                                  |    4 ++
 server/deltacloud.rb                               |    1 +
 server/lib/deltacloud/base_driver/base_driver.rb   |    4 ++
 server/lib/deltacloud/base_driver/features.rb      |    6 +++
 server/lib/deltacloud/drivers/ec2/ec2_driver.rb    |   47 +++++++++++++++++++-
 .../lib/deltacloud/drivers/gogrid/gogrid_client.rb |    7 +--
 .../lib/deltacloud/drivers/gogrid/gogrid_driver.rb |   47 ++++++++++++++++++-
 .../lib/deltacloud/helpers/application_helper.rb   |    1 +
 server/lib/deltacloud/helpers/conversion_helper.rb |    2 +-
 server/lib/deltacloud/models/ip_address.rb         |   26 +++++++++++
 server/lib/sinatra/rabbit.rb                       |    2 +
 server/public/javascripts/application.js           |    9 ++++
 server/server.rb                                   |   38 ++++++++++++++++
 server/views/instances/new.html.haml               |    6 +++
 server/views/ip_addresses/index.html.haml          |   27 +++++++++++
 server/views/ip_addresses/index.xml.haml           |    4 ++
 server/views/ip_addresses/show.html.haml           |   23 ++++++++++
 server/views/ip_addresses/show.xml.haml            |   10 ++++
 server/views/realms/index.html.haml                |    4 +-
 20 files changed, 256 insertions(+), 16 deletions(-)
 delete mode 100644 server/.gitiginore
 create mode 100644 server/.gitignore
 create mode 100644 server/lib/deltacloud/models/ip_address.rb
 create mode 100644 server/views/ip_addresses/index.html.haml
 create mode 100644 server/views/ip_addresses/index.xml.haml
 create mode 100644 server/views/ip_addresses/show.html.haml
 create mode 100644 server/views/ip_addresses/show.xml.haml

diff --git a/server/.gitiginore b/server/.gitiginore
deleted file mode 100644
index 48e68d2..0000000
--- a/server/.gitiginore
+++ /dev/null
@@ -1,4 +0,0 @@
-log/*.log
-*.swp
-*.kpf
-tmp/*
diff --git a/server/.gitignore b/server/.gitignore
new file mode 100644
index 0000000..48e68d2
--- /dev/null
+++ b/server/.gitignore
@@ -0,0 +1,4 @@
+log/*.log
+*.swp
+*.kpf
+tmp/*
diff --git a/server/deltacloud.rb b/server/deltacloud.rb
index 6799b2f..49050fa 100644
--- a/server/deltacloud.rb
+++ b/server/deltacloud.rb
@@ -10,6 +10,7 @@ require 'deltacloud/models/realm'
 require 'deltacloud/models/image'
 require 'deltacloud/models/instance'
 require 'deltacloud/models/key'
+require 'deltacloud/models/ip_address'
 require 'deltacloud/models/instance_profile'
 require 'deltacloud/models/storage_snapshot'
 require 'deltacloud/models/storage_volume'
diff --git a/server/lib/deltacloud/base_driver/base_driver.rb b/server/lib/deltacloud/base_driver/base_driver.rb
index 88563a5..82209bb 100644
--- a/server/lib/deltacloud/base_driver/base_driver.rb
+++ b/server/lib/deltacloud/base_driver/base_driver.rb
@@ -136,6 +136,10 @@ module Deltacloud
       []
     end
 
+    def ip_addresses(credentials, opts=nil)
+      []
+    end
+
     def image(credentials, opts)
       images = images(credentials, opts)
       return images.first unless images.empty?
diff --git a/server/lib/deltacloud/base_driver/features.rb b/server/lib/deltacloud/base_driver/features.rb
index 3ed4085..abc97b5 100644
--- a/server/lib/deltacloud/base_driver/features.rb
+++ b/server/lib/deltacloud/base_driver/features.rb
@@ -158,6 +158,12 @@ module Deltacloud
       end
     end
 
+    declare_feature :instances, :assign_ip_address do
+      operation :create do
+        param :ip_address_id, :string, :optional
+      end
+    end
+
     declare_feature :instances, :hardware_profiles do
       description "Size instances according to changes to a hardware profile"
       # The parameters are filled in from the hardware profiles
diff --git a/server/lib/deltacloud/drivers/ec2/ec2_driver.rb b/server/lib/deltacloud/drivers/ec2/ec2_driver.rb
index 909eca3..eea0c41 100644
--- a/server/lib/deltacloud/drivers/ec2/ec2_driver.rb
+++ b/server/lib/deltacloud/drivers/ec2/ec2_driver.rb
@@ -36,7 +36,7 @@ module Deltacloud
 class EC2Driver < Deltacloud::BaseDriver
 
   def supported_collections
-    DEFAULT_COLLECTIONS + [ :keys ]
+    DEFAULT_COLLECTIONS + [ :keys, :ip_addresses ]
   end
 
   feature :instances, :user_data
@@ -310,6 +310,41 @@ class EC2Driver < Deltacloud::BaseDriver
     end
   end
 
+  def ip_addresses(credentials, opts=nil)
+    ec2 = new_client( credentials )
+    ips = []
+    safely do
+      address_set = ec2.describe_addresses.addressesSet
+      address_set.item.each do |ip|
+        ips << convert_ip_address(ip)
+      end if address_set
+    end
+    ips = filter_on( ips, :type, opts )
+    ips = filter_on( ips, :state, opts )
+    return ips
+  end
+
+  def ip_address(credentials, opts={})
+    ip_addresses(credentials).select { |ip| ip.id.eql?(opts[:id])}.first
+  end
+
+  def create_ip_address(credentials, opts)
+    ec2 = new_client( credentials )
+    safely do
+      ip = ec2.allocate_address().publicIp
+      return ip_addresses(credentials, :id => ip)
+    end
+  end
+
+  def destroy_ip_address(credentials, opts)
+    ec2 = new_client( credentials )
+    response = false
+    safely do
+      response = ec2.release_address(:public_ip => opts[:id]).return
+    end
+    throw BackendError.new(500, 'Unable to release this IP address', '', '') unless response.to_s.eql?("true")
+  end
+
   private
 
   def new_client(credentials)
@@ -323,6 +358,16 @@ class EC2Driver < Deltacloud::BaseDriver
     end
   end
 
+  def convert_ip_address(ip)
+    IpAddress.new({
+      :id => ip['publicIp'],
+      :address => ip['publicIp'],
+      :type => 'public',
+      :state => ip['instanceId'] ? 'assigned' : 'unassigned',
+      :realm_id => 'us-east-1a' # TODO: Add realm Realm here
+    })
+  end
+
   def convert_key(key)
     Key.new({
       :id => key['keyName'],
diff --git a/server/lib/deltacloud/drivers/gogrid/gogrid_client.rb b/server/lib/deltacloud/drivers/gogrid/gogrid_client.rb
index c37f061..4ab73b1 100644
--- a/server/lib/deltacloud/drivers/gogrid/gogrid_client.rb
+++ b/server/lib/deltacloud/drivers/gogrid/gogrid_client.rb
@@ -36,11 +36,8 @@ class GoGridClient
     else
       @default_params['v'] = '1.5'
     end
-    begin
-      JSON::parse(sendAPIRequest(method, params))
-    rescue Exception => e
-      STDERR.puts("ERROR: #{e.message}")
-    end
+    result = sendAPIRequest(method, params)
+    JSON::parse(result)
   end
   
   def encode_params(params)
diff --git a/server/lib/deltacloud/drivers/gogrid/gogrid_driver.rb b/server/lib/deltacloud/drivers/gogrid/gogrid_driver.rb
index e6c99c5..4034b5d 100644
--- a/server/lib/deltacloud/drivers/gogrid/gogrid_driver.rb
+++ b/server/lib/deltacloud/drivers/gogrid/gogrid_driver.rb
@@ -36,6 +36,7 @@ module Deltacloud
 class GogridDriver < Deltacloud::BaseDriver
 
   feature :instances, :authentication_password
+  feature :instances, :assign_ip_address
 
   define_hardware_profile 'server' do
     cpu            2
@@ -45,7 +46,7 @@ class GogridDriver < Deltacloud::BaseDriver
 
   def supported_collections
     DEFAULT_COLLECTIONS.reject! { |c| [ :storage_volumes, :storage_snapshots ].include?(c) }
-    DEFAULT_COLLECTIONS + [ :keys ]
+    DEFAULT_COLLECTIONS + [ :keys, :ip_addresses ]
   end
 
   def images(credentials, opts=nil)
@@ -82,13 +83,19 @@ class GogridDriver < Deltacloud::BaseDriver
       server_ram = "512MB"
     end
     client = new_client(credentials)
+    if opts and opts[:ip_address_id]
+      ip_address = ip_address(credentials, :id => opts[:ip_address_id])
+      ip_address = ip_address.address
+    else
+      ip_address = get_free_ip_from_realm(credentials, opts[:realm_id] || '1')
+    end
     name = (opts[:name] && opts[:name]!='') ? opts[:name] : get_random_instance_name
     safely do
       instance = client.request('grid/server/add', {
         'name' => name,
         'image' => image_id,
         'server.ram' => server_ram,
-        'ip' => get_free_ip_from_realm(credentials, opts[:realm_id] || '1')
+        'ip' => ip_address
       })['list'].first
       if instance
         login_data = get_login_data(client, instance[:id])
@@ -120,7 +127,6 @@ class GogridDriver < Deltacloud::BaseDriver
   end
 
   def instances(credentials, opts=nil)
-    require 'ap'
     instances = []
     if opts and opts[:id]
       begin
@@ -192,6 +198,32 @@ class GogridDriver < Deltacloud::BaseDriver
     return creds
   end
 
+  def ip_addresses(credentials, opts=nil)
+    gogrid = new_client( credentials )
+    ips = []
+    safely do
+      gogrid.request('grid/ip/list')['list'].each do |ip|
+        ips << convert_ip_address(ip)
+      end
+    end
+    puts opts.inspect
+    ips = filter_on( ips, :type, opts )
+    ips = filter_on( ips, :state, opts )
+    return ips
+  end
+
+  def ip_address(credentials, opts={})
+    ip_addresses(credentials).select { |ip| ip.id.eql?(opts[:id].to_i)}.first
+  end
+
+  def create_ip_address(credentials, opts)
+    throw BackendFeatureUnsupported.new(501, "GoGrid not supports creating new IP addresses")
+  end
+
+  def destroy_ip_address(credentials, opts)
+    throw BackendFeatureUnsupported.new(501, "GoGrid not supports destroying new IP addresses")
+  end
+
   define_instance_states do
     start.to( :pending )         .automatically
     pending.to( :running )       .automatically
@@ -205,7 +237,16 @@ class GogridDriver < Deltacloud::BaseDriver
 
   def new_client(credentials)
     GoGridClient.new('https://api.gogrid.com/api', credentials.user, credentials.password)
+  end
 
+  def convert_ip_address(ip)
+    IpAddress.new({
+      :id => ip['id'],
+      :address => ip['ip'],
+      :type => ip['public'] ? 'public' : 'private',
+      :state => ip['state']['name'].downcase,
+      :realm_id => ip['datacenter']['id']
+    })
   end
 
   def get_login_data(client, instance_id)
diff --git a/server/lib/deltacloud/helpers/application_helper.rb b/server/lib/deltacloud/helpers/application_helper.rb
index 9a9dfdc..8b525ff 100644
--- a/server/lib/deltacloud/helpers/application_helper.rb
+++ b/server/lib/deltacloud/helpers/application_helper.rb
@@ -60,6 +60,7 @@ module ApplicationHelper
       filter.merge!(:architecture => params[:architecture]) if params[:architecture]
       filter.merge!(:owner_id => params[:owner_id]) if params[:owner_id]
       filter.merge!(:state => params[:state]) if params[:state]
+      filter.merge!(:type => params[:type]) if params[:type]
       filter = nil if filter.keys.size.eql?(0)
       singular = model.to_s.singularize.to_sym
       @elements = driver.send(model.to_sym, credentials, filter)
diff --git a/server/lib/deltacloud/helpers/conversion_helper.rb b/server/lib/deltacloud/helpers/conversion_helper.rb
index e96f9b7..5641343 100644
--- a/server/lib/deltacloud/helpers/conversion_helper.rb
+++ b/server/lib/deltacloud/helpers/conversion_helper.rb
@@ -22,7 +22,7 @@ require 'deltacloud/base_driver'
 module ConversionHelper
 
   def convert_to_json(type, obj)
-    if ( [ :flavor, :account, :image, :realm, :instance, :storage_volume, :storage_snapshot, :hardware_profile ].include?( type ) )
+    if ( [ :flavor, :account, :image, :realm, :instance, :storage_volume, :storage_snapshot, :hardware_profile, :ip_address ].include?( type ) )
       if Array.eql?(obj.class)
         data = obj.collect do |o|
           o.to_hash.merge({ :href => self.send(:"#{type}_url", type.eql?(:hardware_profile) ? o.name : o.id ) })
diff --git a/server/lib/deltacloud/models/ip_address.rb b/server/lib/deltacloud/models/ip_address.rb
new file mode 100644
index 0000000..db81271
--- /dev/null
+++ b/server/lib/deltacloud/models/ip_address.rb
@@ -0,0 +1,26 @@
+#
+# Copyright (C) 2009  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.
+
+class IpAddress < BaseModel
+
+  attr_accessor :address
+  attr_accessor :type
+  attr_accessor :state
+  attr_accessor :realm_id
+
+end
diff --git a/server/lib/sinatra/rabbit.rb b/server/lib/sinatra/rabbit.rb
index f411268..91d48da 100644
--- a/server/lib/sinatra/rabbit.rb
+++ b/server/lib/sinatra/rabbit.rb
@@ -256,10 +256,12 @@ class String
   end
 
   def pluralize
+    return self+'es' if self =~ /ss$/
     self + "s"
   end
 
   def singularize
+    return self.gsub(/es$/, '') if self =~ /ses$/
     self.gsub(/s$/, '')
   end
 
diff --git a/server/public/javascripts/application.js b/server/public/javascripts/application.js
index 80e1d1c..19ee379 100644
--- a/server/public/javascripts/application.js
+++ b/server/public/javascripts/application.js
@@ -29,4 +29,13 @@ $(document).ready(function() {
     return false;
   })
 
+  if ($("select[name=ip_address_id]").length) {
+    $.getJSON('/api/ip_addresses', { format : 'json', state : 'unassigned', type : 'public'}, function(data) {
+      $("select[name=ip_address_id]").empty()
+      $.each(data.ip_addresses, function(i, ip_address){
+        $("select[name=ip_address_id]").append('<option value="'+ip_address.id+'">'+ip_address.address+'</option>')
+      }) 
+    }) 
+  }
+
 })
diff --git a/server/server.rb b/server/server.rb
index 1373fa3..130ecb1 100644
--- a/server/server.rb
+++ b/server/server.rb
@@ -352,3 +352,41 @@ collection :keys do
   end
 
 end
+
+collection :ip_addresses do
+  description "Manage IP addresses"
+
+  operation :index do
+    description "List all IP addresses associated with your account"
+    param :type,  :string,  :optional
+    param :state,  :string,  :optional
+    control do
+      filter_all :ip_addresses
+    end
+  end
+
+  operation :show do
+    description "Show details about given IP address"
+    param :id,  :string,  :required
+    control { show :ip_address }
+  end
+
+  operation :create do
+    description "Create a new IP address"
+    param  :ip,  :string,  :optional
+    control do
+      @ip_address = driver.create_ip_address(credentials, params)
+      show :ip_address
+    end
+  end
+
+  operation :destroy do
+    description "Destroy IP address"
+    param  :id,  :string,  :required
+    control do
+      @ip_address = driver.destroy_ip_address(credentials, params)
+      redirect ip_addresses_url
+    end
+  end
+
+end
diff --git a/server/views/instances/new.html.haml b/server/views/instances/new.html.haml
index 6d67c2d..cf79146 100644
--- a/server/views/instances/new.html.haml
+++ b/server/views/instances/new.html.haml
@@ -14,6 +14,12 @@
       %label
         Instance Keyname:
         %input{:name => 'keyname', :size => 30 }
+  -if driver_has_feature?(:assign_ip_address)
+    %p
+      %label
+        Instance IP address:
+        %select{:name => 'ip_address_id'}
+          %option Loading...
   - if !@hardware_profiles.empty?
     %h3 What size machine?
     - for hwp in @hardware_profiles
diff --git a/server/views/ip_addresses/index.html.haml b/server/views/ip_addresses/index.html.haml
new file mode 100644
index 0000000..b0a430b
--- /dev/null
+++ b/server/views/ip_addresses/index.html.haml
@@ -0,0 +1,27 @@
+%h1 IP Addresses
+
+%table.display
+  %thead
+    %tr
+      %th ID
+      %th IP Address
+      %th Type
+      %th State
+      %th Realm
+  %tbody
+    - @elements.each do |ip|
+      %tr
+        %td
+          =link_to ip.id, ip_address_url( ip.id )
+        %td
+          =ip.address
+        %td
+          =ip.type
+        %td
+          =ip.state
+        %td
+          =link_to ip.realm_id, realm_url(ip.realm_id)
+  %tfoot
+    %tr
+      %td{:colspan => 5, :style => "text-align:right;"}
+        =link_to 'Create &raquo;', create_ip_address_url, :class => 'post'
diff --git a/server/views/ip_addresses/index.xml.haml b/server/views/ip_addresses/index.xml.haml
new file mode 100644
index 0000000..2f4752c
--- /dev/null
+++ b/server/views/ip_addresses/index.xml.haml
@@ -0,0 +1,4 @@
+!!!XML
+%ip_addresses
+  - @elements.each do |c|
+    = haml :'ip_addresses/show', :locals => { :@ip_address => c, :partial => true }
diff --git a/server/views/ip_addresses/show.html.haml b/server/views/ip_addresses/show.html.haml
new file mode 100644
index 0000000..76d3722
--- /dev/null
+++ b/server/views/ip_addresses/show.html.haml
@@ -0,0 +1,23 @@
+%h1
+  = @ip_address.id
+
+%dl
+  %di
+    %dt ID
+    %dd
+      = @ip_address.id
+    %dt Address
+    %dd
+      = @ip_address.address
+    %dt State
+    %dd
+      = @ip_address.state
+    %dt IP Type
+    %dd
+      = @ip_address.type
+    %dt Realm
+    %dd
+      = link_to driver.realm(credentials, @ip_address.realm_id).name, realm_url(@ip_address.realm_id)
+    %dt
+    %dd
+      = link_to 'Destroy', destroy_ip_address_url(@ip_address.id), :class => 'delete'
diff --git a/server/views/ip_addresses/show.xml.haml b/server/views/ip_addresses/show.xml.haml
new file mode 100644
index 0000000..e49493f
--- /dev/null
+++ b/server/views/ip_addresses/show.xml.haml
@@ -0,0 +1,10 @@
+- unless defined?(partial)
+  !!! XML
+%ip_address{ :href => ip_address_url(@ip_address.id), :id => @ip_address.id, :type => "#{@ip_address.type}" }
+  %address<
+    =@ip_address.address
+  %state<
+    =@ip_address.state
+  %realm{:href => realm_url(@ip_address.realm_id), :id => @ip_address.realm_id}
+  %actions
+    %link{ :rel => "destroy", :method => "delete", :href => destroy_ip_address_url(@ip_address.id)}
diff --git a/server/views/realms/index.html.haml b/server/views/realms/index.html.haml
index cfee320..ea4cf85 100644
--- a/server/views/realms/index.html.haml
+++ b/server/views/realms/index.html.haml
@@ -5,9 +5,9 @@
   %thead
     %tr
       %th
-        Name
+        ID
       %th
-        Architecture
+        Name
       %th
         Memory
       %th
-- 
1.7.2.2


[PATCH core 2/5] Added 'assign_instance' feature for EC2

Posted by mf...@redhat.com.
---
 server/lib/deltacloud/base_driver/features.rb   |    6 ++++++
 server/lib/deltacloud/drivers/ec2/ec2_driver.rb |   12 ++++++++++--
 2 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/server/lib/deltacloud/base_driver/features.rb b/server/lib/deltacloud/base_driver/features.rb
index abc97b5..e4808c4 100644
--- a/server/lib/deltacloud/base_driver/features.rb
+++ b/server/lib/deltacloud/base_driver/features.rb
@@ -164,6 +164,12 @@ module Deltacloud
       end
     end
 
+    declare_feature :ip_addresses, :assign_instance do
+      operation :create do
+        param :instance_id, :string, :optional
+      end
+    end
+
     declare_feature :instances, :hardware_profiles do
       description "Size instances according to changes to a hardware profile"
       # The parameters are filled in from the hardware profiles
diff --git a/server/lib/deltacloud/drivers/ec2/ec2_driver.rb b/server/lib/deltacloud/drivers/ec2/ec2_driver.rb
index eea0c41..db48d40 100644
--- a/server/lib/deltacloud/drivers/ec2/ec2_driver.rb
+++ b/server/lib/deltacloud/drivers/ec2/ec2_driver.rb
@@ -40,6 +40,7 @@ class EC2Driver < Deltacloud::BaseDriver
   end
 
   feature :instances, :user_data
+  feature :ip_addresses, :assign_instance
   feature :instances, :authentication_key
   feature :images, :owner_id
 
@@ -175,6 +176,7 @@ class EC2Driver < Deltacloud::BaseDriver
   def create_instance(credentials, image_id, opts)
     ec2 = new_client( credentials )
     realm_id = opts[:realm_id]
+    ec2_instance = nil
     safely do
       image = image(credentials, :id => image_id )
       hwp = find_hardware_profile(credentials, opts[:hwp_id], image.id)
@@ -188,8 +190,9 @@ class EC2Driver < Deltacloud::BaseDriver
         :disable_api_termination => false,
         :instance_initiated_shutdown_behavior => 'terminate'
       )
-      return convert_instance( ec2_instances.instancesSet.item.first, 'pending' )
+      ec2_instance = convert_instance( ec2_instances.instancesSet.item.first, 'pending' )
     end
+    return ec2_instance
   end
 
   def generate_instance(ec2, id, backup)
@@ -330,10 +333,15 @@ class EC2Driver < Deltacloud::BaseDriver
 
   def create_ip_address(credentials, opts)
     ec2 = new_client( credentials )
+    ip = nil
     safely do
       ip = ec2.allocate_address().publicIp
-      return ip_addresses(credentials, :id => ip)
+      if opts and opts[:instance_id]
+        ec2.associate_address(:public_ip => ip,
+          :instance_id => opts[:instance_id])
+      end
     end
+    return ip_addresses(credentials, :id => ip)
   end
 
   def destroy_ip_address(credentials, opts)
-- 
1.7.2.2