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 2011/08/10 15:38:27 UTC

Added possibility to define 'new' operation for collection in Rabbit

Hi,

This patch will add possibility to add a 'new' operation for any collection.
This operation should provide the HTML form in HTML response type and XML/JSON
representation of form for other media types.
Typicall use-case for this would be to check what parameters are required in
order to sucessfuly create a new resource (like instance).

You are of course not limited just to 'new' action but you can also create
operation for any other 'form' actions (like 'new_rule' in firewalls or
'new_blob') and provide usefull informations to clients about required
parameters.

This patch adds only HTML forms, but we should be able to extend it with XML
forms/JSON forms in future.

  -- Michal 


Re: Added possibility to define 'new' operation for collection in Rabbit

Posted by Michal Fojtik <mf...@redhat.com>.
On Aug 12, 2011, at 12:00 AM, David Lutterkort wrote:

> On Wed, 2011-08-10 at 15:38 +0200, mfojtik@redhat.com wrote:
>> This patch will add possibility to add a 'new' operation for any collection.
>> This operation should provide the HTML form in HTML response type and XML/JSON
>> representation of form for other media types.
> 
> Nice. ACK. Pushed

Thanks!

> 
>> You are of course not limited just to 'new' action but you can also create
>> operation for any other 'form' actions (like 'new_rule' in firewalls or
>> 'new_blob') and provide usefull informations to clients about required
>> parameters.
> 
> If we want to go there, we should teach rabbit about nested collections

Yes, we should add this into backlog/release4 planning maybe.

The benefits of using Rabbit are auto generated documentation and parameter
validation. Also I personally don't like mixing Sinatra routes and the Rabbit
routes. Maybe we should separate them into two different files.   

  -- Michal

------------------------------------------------------
Michal Fojtik, mfojtik@redhat.com
Deltacloud API: http://deltacloud.org


Re: Added possibility to define 'new' operation for collection in Rabbit

Posted by Francesco Vollero <ra...@gmail.com>.
On Fri, Aug 12, 2011 at 12:00 AM, David Lutterkort <lu...@redhat.com> wrote:
> On Wed, 2011-08-10 at 15:38 +0200, mfojtik@redhat.com wrote:
>> This patch will add possibility to add a 'new' operation for any collection.
>> This operation should provide the HTML form in HTML response type and XML/JSON
>> representation of form for other media types.
>
> Nice. ACK. Pushed
>
>> You are of course not limited just to 'new' action but you can also create
>> operation for any other 'form' actions (like 'new_rule' in firewalls or
>> 'new_blob') and provide usefull informations to clients about required
>> parameters.
>
> If we want to go there, we should teach rabbit about nested collections
>

Yeah, can be really useful, even a bit scary :)

Francesco

Re: Added possibility to define 'new' operation for collection in Rabbit

Posted by David Lutterkort <lu...@redhat.com>.
On Wed, 2011-08-10 at 15:38 +0200, mfojtik@redhat.com wrote:
> This patch will add possibility to add a 'new' operation for any collection.
> This operation should provide the HTML form in HTML response type and XML/JSON
> representation of form for other media types.

Nice. ACK. Pushed

> You are of course not limited just to 'new' action but you can also create
> operation for any other 'form' actions (like 'new_rule' in firewalls or
> 'new_blob') and provide usefull informations to clients about required
> parameters.

If we want to go there, we should teach rabbit about nested collections

David



[PATCH core 2/2] Rabbit: Fixed exception on duplicate operation

Posted by mf...@redhat.com.
From: Michal Fojtik <mf...@redhat.com>

Signed-off-by: Michal fojtik <mf...@redhat.com>
---
 server/lib/sinatra/rabbit.rb |    4 +++-
 1 files changed, 3 insertions(+), 1 deletions(-)

diff --git a/server/lib/sinatra/rabbit.rb b/server/lib/sinatra/rabbit.rb
index fe925af..70691d5 100644
--- a/server/lib/sinatra/rabbit.rb
+++ b/server/lib/sinatra/rabbit.rb
@@ -265,7 +265,9 @@ module Sinatra
       # This also defines a helper method like show_instance_url that returns
       # the URL to this operation (in request context)
       def operation(name, opts = {}, &block)
-        raise DuplicateOperationException if @operations[name]
+        if @operations.keys.include?(name)
+          raise DuplicateOperationException::new(500, "DuplicateOperation", "Operation #{name} is already defined", [])
+        end
         @operations[name] = Operation.new(self, name, opts, &block)
       end
 
-- 
1.7.4.1


[PATCH core 1/2] Rabbit: Added :new operation to collection

Posted by mf...@redhat.com.
From: Michal Fojtik <mf...@redhat.com>

Signed-off-by: Michal fojtik <mf...@redhat.com>
---
 server/lib/sinatra/rabbit.rb |   18 +++-
 server/server.rb             |  203 ++++++++++++++++++++++++------------------
 2 files changed, 130 insertions(+), 91 deletions(-)

diff --git a/server/lib/sinatra/rabbit.rb b/server/lib/sinatra/rabbit.rb
index 5c1d757..fe925af 100644
--- a/server/lib/sinatra/rabbit.rb
+++ b/server/lib/sinatra/rabbit.rb
@@ -38,12 +38,14 @@ module Sinatra
     end
 
     class Operation
-      attr_reader :name, :method, :collection
+      attr_reader :name, :method, :collection, :member
 
       include ::Deltacloud::BackendCapability
       include ::Deltacloud::Validation
+      include ::ApplicationHelper
 
       STANDARD = {
+        :new => { :method => :get, :member => false, :form => true },
         :index => { :method => :get, :member => false },
         :show =>  { :method => :get, :member => true },
         :create => { :method => :post, :member => false },
@@ -72,6 +74,10 @@ module Sinatra
         STANDARD.keys.include?(name)
       end
 
+      def form?
+        STANDARD[name] and STANDARD[name][:form]
+      end
+
       def description(text="")
         return @description if text.blank?
         @description = text
@@ -122,7 +128,11 @@ module Sinatra
             "#{@collection.name}/:id/#{name}"
           end
         else
-          "#{@collection.name}"
+          if form?
+            "#{@collection.name}/#{name}"
+          else
+            "#{@collection.name}"
+          end
         end
       end
 
@@ -260,13 +270,13 @@ module Sinatra
       end
 
       def generate
-        operations.values.each { |op| op.generate }
+        operations.values.reject { |op| op.member }.each { |o| o.generate }
+        operations.values.select { |op| op.member }.each { |o| o.generate }
         app = ::Sinatra::Application
         collname = name # Work around Ruby's weird scoping/capture
         app.send(:define_method, "#{name.to_s.singularize}_url") do |id|
             api_url_for "#{collname}/#{id}", :full
         end
-
         if index_op = operations[:index]
           app.send(:define_method, "#{name}_url") do
             api_url_for index_op.path.gsub(/\/\?$/,''), :full
diff --git a/server/server.rb b/server/server.rb
index 60c1d36..08d72b0 100644
--- a/server/server.rb
+++ b/server/server.rb
@@ -161,19 +161,23 @@ END
 
 end
 
-get "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/images/new" do
-  @instance = Instance.new( :id => params[:instance_id] )
-  respond_to do |format|
-    format.html { haml :"images/new" }
-  end
-end
-
 collection :images do
   description <<END
   An image is a platonic form of a machine. Images are not directly executable,
   but are a template for creating actual instances of machines.
 END
 
+  operation :new do
+    description "Form to create a new image resource"
+    param :instance_id, :string,  "An instance from which the new image will be created from"
+    control do
+      @instance = Instance.new( :id => params[:instance_id] )
+      respond_to do |format|
+        format.html { haml :"images/new" }
+      end
+    end
+  end
+
   operation :index do
     description <<END
     The images collection will return a set of all images
@@ -271,21 +275,6 @@ collection :instance_states do
   end
 end
 
-get "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/instances/new" do
-  @instance = Instance.new( { :id=>params[:id], :image_id=>params[:image_id] } )
-  @image   = driver.image( credentials, :id => params[:image_id] )
-  @hardware_profiles = driver.hardware_profiles(credentials, :architecture => @image.architecture )
-  @realms = driver.realms(credentials)
-  @keys = driver.keys(credentials) if driver_has_feature?(:authentication_key)
-  @firewalls = driver.firewalls(credentials) if driver_has_feature?(:firewalls)
-  if driver_has_feature?(:register_to_load_balancer)
-    @load_balancers = driver.load_balancers(credentials)
-  end
-  respond_to do |format|
-    format.html { haml :"instances/new" }
-  end
-end
-
 get "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/instances/:id/run" do
   @instance = driver.instance(credentials, :id => params[:id])
   respond_to do |format|
@@ -293,18 +282,20 @@ get "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/instances/:id/run" do
   end
 end
 
-get "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/load_balancers/new" do
-  @realms = driver.realms(credentials)
-  @instances = driver.instances(credentials) if driver_has_feature?(:register_instance, :load_balancers)
-  respond_to do |format|
-    format.html { haml :"load_balancers/new" }
-  end
-end
-
-
 collection :load_balancers do
   description "Load balancers"
 
+  operation :new do
+    description "Form to create a new load balancer"
+    control do
+      @realms = driver.realms(credentials)
+      @instances = driver.instances(credentials) if driver_has_feature?(:register_instance, :load_balancers)
+      respond_to do |format|
+        format.html { haml :"load_balancers/new" }
+      end
+    end
+  end
+
   operation :index do
     description "List of all active load balancers"
     control do
@@ -390,6 +381,32 @@ collection :instances do
   The images collection may be obtained by following the link from the primary entry-point.
 END
 
+  operation :new do
+    description "Form for creating a new instance resource"
+    param :image_id,  :string,  "Image from which will be the new instance created from"
+    param :realm_id,  :string, :optional
+    if driver_has_feature? :authentication_key
+      param :authentication_key, :string, :optional
+    end
+    if driver_has_feature? :firewalls
+      param :firewalls, :string, :optional
+    end
+    control do
+      @instance = Instance.new( { :id=>params[:id], :image_id=>params[:image_id] } )
+      @image   = Image.new( :id => params[:image_id] )
+      @hardware_profiles = driver.hardware_profiles(credentials, :architecture => @image.architecture )
+      @realms = Realm.new(:id => params[:realm_id]) if params[:realm_id]
+      @realms ||= []
+      @keys = driver.keys(credentials) if driver_has_feature?(:authentication_key)
+      @firewalls = driver.firewalls(credentials) if driver_has_feature?(:firewalls)
+      respond_to do |format|
+        format.html do
+          haml :'instances/new'
+        end
+      end
+    end
+  end
+
   operation :index do
     description "List all instances."
     with_capability :instances
@@ -518,15 +535,18 @@ END
 
 end
 
-get "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/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"
 
+  operation :new do
+    description "A form to create a new storage snapshot"
+    control do
+      respond_to do |format|
+        format.html { haml :"storage_snapshots/new" }
+      end
+    end
+  end
+
   operation :index do
     description "List of storage snapshots."
     with_capability :storage_snapshots
@@ -569,22 +589,18 @@ collection :storage_snapshots do
   end
 end
 
-get "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/storage_volumes/new" do
-  respond_to do |format|
-    format.html { haml :"storage_volumes/new" }
-  end
-end
-
-get "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/storage_volumes/attach" do
-  respond_to do |format|
-    @instances = driver.instances(credentials)
-    format.html { haml :"storage_volumes/attach" }
-  end
-end
-
 collection :storage_volumes do
   description "Storage volumes description here"
 
+  operation :new do
+    description "A form to create a new storage volume"
+    control do
+      respond_to do |format|
+        format.html { haml :"storage_volumes/new" }
+      end
+    end
+  end
+
   operation :index do
     description "List of storage volumes."
     with_capability :storage_volumes
@@ -667,15 +683,18 @@ collection :storage_volumes do
 
 end
 
-get "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/keys/new" do
-  respond_to do |format|
-    format.html { haml :"keys/new" }
-  end
-end
-
 collection :keys do
   description "Instance authentication credentials."
 
+  operation :new do
+    description "A form to create a new key resource"
+    control do
+      respond_to do |format|
+        format.html { haml :"keys/new" }
+      end
+    end
+  end
+
   operation :index do
     description "List all available credentials which could be used for instance authentication."
     with_capability :keys
@@ -724,14 +743,6 @@ collection :keys do
 
 end
 
-#get html form for creating a new blob
-get "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/buckets/:bucket/new_blob" do
-  @bucket_id = params[:bucket]
-  respond_to do |format|
-    format.html {haml :"blobs/new"}
-  end
-end
-
 #create a new blob using PUT - streams through deltacloud
 put "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/buckets/:bucket/:blob" do
   if(env["BLOB_SUCCESS"]) #ie got a 200ok after putting blob
@@ -864,16 +875,28 @@ get "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/buckets/:bucket/:blob/content"
   end
 end
 
-#Get html form for creating a new bucket
-get "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/buckets/new" do
-  respond_to do |format|
-    format.html { haml :"buckets/new" }
-  end
-end
-
 collection :buckets do
   description "Cloud Storage buckets - aka buckets|directories|folders"
 
+  operation :new do
+    description "A form to create a new bucket resource"
+    control do
+      respond_to do |format|
+        format.html { haml :"buckets/new" }
+      end
+    end
+  end
+
+  operation :new_bucket, :form => true, :method => :get, :member => true do
+    param :bucket,  :string
+    control do
+      @bucket_id = params[:bucket]
+      respond_to do |format|
+        format.html {haml :"blobs/new"}
+      end
+    end
+  end
+
   operation :index do
     description "List buckets associated with this account"
     with_capability :buckets
@@ -1015,21 +1038,6 @@ collection :addresses do
 
 end
 
-#html for creating a new firewall
-get "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/firewalls/new" do
-  respond_to do |format|
-    format.html { haml :"firewalls/new" }
-  end
-end
-
-#html for creating a new firewall rule
-get "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/firewalls/:firewall/new_rule" do
-  @firewall_name = params[:firewall]
-  respond_to do |format|
-    format.html {haml :"firewalls/new_rule" }
-  end
-end
-
 #delete a firewall rule
 delete '/api/firewalls/:firewall/:rule' do
   opts = {}
@@ -1047,6 +1055,27 @@ end
 #FIREWALLS
 collection :firewalls do
   description "Allow user to define firewall rules for an instance (ec2 security groups) eg expose ssh access [port 22, tcp]."
+
+  operation :new do
+    description "A form to create a new firewall resource"
+    control do
+      respond_to do |format|
+        format.html { haml :"firewalls/new" }
+      end
+    end
+  end
+
+  operation :new_rule, :form => true, :member => true, :method => :get do
+    description "A form to create a new firewall rule"
+    param :firewall,  :string,  :optional
+    control do
+      @firewall_name = params[:firewall]
+      respond_to do |format|
+        format.html {haml :"firewalls/new_rule" }
+      end
+    end
+  end
+
   operation :index do
     description 'List all firewalls'
     with_capability :firewalls
@@ -1092,7 +1121,7 @@ collection :firewalls do
     end
   end
 
-#create a new firewall rule - POST /api/firewalls/:firewall/rules
+  #create a new firewall rule - POST /api/firewalls/:firewall/rules
   operation :rules, :method => :post, :member => true do
     description 'Create a new firewall rule for the specified firewall'
     param :id,  :required, :string, [],  "Name of firewall in which to apply this rule"
-- 
1.7.4.1