You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@deltacloud.apache.org by lu...@redhat.com on 2011/03/30 01:35:49 UTC

Make drivers a toplevel collection (rev 2)

This repost addresses the issues Michal found; specifically (1) the view
templates for drivers are now included and (2) unit tests actually work

Original post:

We've had an XML representation of drivers supported by the server as a
one-off page for a while now. In keeping with HATEAOS, the list of drivers
should be reachable through the main API entry point.

To make that possible, I turned drivers into their own toplevel
collection. I also changed the XML format for a driver to emphasize the
list of possible provider values more. For the EC2 driver the XML now is:

  <driver href="http://localhost:3001/api/drivers/ec2" id="ec2">
    <name>EC2</name>

    <provider id="us-west-1">
      <entrypoint kind="s3">s3-us-west-1.amazonaws.com</entrypoint>
      <entrypoint kind="elb">elasticloadbalancing.us-west-1.amazonaws.com</entrypoint>
      <entrypoint kind="ec2">ec2.us-west-1.amazonaws.com</entrypoint>
    </provider>

    .. similar provider tags for the other regions ..
  </driver>

David

[PATCH 3/3] Driver docs: explain how to switch drivers dynamically

Posted by lu...@redhat.com.
From: David Lutterkort <lu...@redhat.com>

---
 site/content/_drivers.mdown |   30 +++++++++++++-
 site/output/drivers.html    |   95 +++++++++++++++++++++++++++++-------------
 2 files changed, 93 insertions(+), 32 deletions(-)

diff --git a/site/content/_drivers.mdown b/site/content/_drivers.mdown
index f952875..ed87d23 100644
--- a/site/content/_drivers.mdown
+++ b/site/content/_drivers.mdown
@@ -5,8 +5,6 @@ can handle a set of standard operations, some of them also support a number of
 optional operations to expose the features of specific clouds more closely. The
 drivers and their capabilities are:
 
-<table providers></table>
-
 ## Setting up the code
 
 To set up a Deltacloud core and the drivers, install the
@@ -29,6 +27,34 @@ This will start a webserver running the mock driver on
 `http://localhost:3001/api`; you can simply browse to that URL to get a
 pretty view of the objects the driver deals with.
 
+## Dynamic driver switching
+
+The driver specified with the `-i` switch when `deltacloudd` is launched is
+the default driver. Clients can switch drivers for any request. The list of
+drivers supported by the server can be obtained from the `drivers`
+collection.
+
+Some drivers also support the notion of a *provider*. Changing the provider
+makes it possible to use the same driver against different instances of a
+cloud, for example different regions in EC2 or different installations of
+RHEV-M. The possible range of values for the provider is driver-specific.
+
+The driver and provider can be selected in one of two ways:
+
+1. Through the request headers `X-Deltacloud-Driver` and
+   `X-Deltacloud-Provider`. For example, including the headers
+   `X-Deltacloud-Driver: ec2` and `X-Deltacloud-Provider: eu-west-1`
+   ensures that a request will be serviced by the EC2 driver, and that the
+   driver will use the eu-west-1 region in EC2.
+2. Through the matrix request parameters `driver` and `provider` in the
+   `api` component of the server's URL. For example, requesting
+   `http://localhost:3001/api;driver=ec2;provider=eu-west-1` has the same
+   effect as using the two request headers mentioned above.
+
+## Notes on specific drivers
+
+<table providers></table>
+
 ### EC2 Driver
 
 For the Amazon EC2 you need to install the `amazon-ec2` Ruby gem:
diff --git a/site/output/drivers.html b/site/output/drivers.html
index 3494813..5dcc2cc 100644
--- a/site/output/drivers.html
+++ b/site/output/drivers.html
@@ -82,21 +82,27 @@
         </li>
         <li>
         <a href="#h2">Launch the server</a>
+        </li>
+        <li>
+        <a href="#h3">Dynamic driver switching</a>
+        </li>
+        <li>
+        <a href="#h4">Notes on specific drivers</a>
         <ul>
         <li>
-        <a href="#h2_1">EC2 Driver</a>
+        <a href="#h4_1">EC2 Driver</a>
         </li>
         <li>
-        <a href="#h2_2">RHEV-M Driver</a>
+        <a href="#h4_2">RHEV-M Driver</a>
         </li>
         <li>
-        <a href="#h2_3">Rackspace Driver</a>
+        <a href="#h4_3">Rackspace Driver</a>
         </li>
         <li>
-        <a href="#h2_4">RimuHosting</a>
+        <a href="#h4_4">RimuHosting</a>
         </li>
         <li>
-        <a href="#h2_5">OpenNebula</a>
+        <a href="#h4_5">OpenNebula</a>
         </li>
         </ul></li></ul>
         <!-- = rest -->
@@ -106,6 +112,55 @@
         can handle a set of standard operations, some of them also support a number of
         optional operations to expose the features of specific clouds more closely. The
         drivers and their capabilities are:</p>
+        
+        <h2 id="h1">Setting up the code</h2>
+        
+        <p>To set up a Deltacloud core and the drivers, install the
+        <a href="http://rubygems.org/gems/deltacloud-core">deltacloud-core</a> Ruby gem:</p>
+        
+        <pre><code># gem install deltacloud-core&#x000A;</code></pre>
+        
+        <p>RPM package will be available soon.</p>
+        
+        <h2 id="h2">Launch the server</h2>
+        
+        <p>The server is launched with the <code>deltacloudd</code> command and pass it the name
+        of the driver you want to use:</p>
+        
+        <pre><code>$ deltacloudd -i mock&#x000A;</code></pre>
+        
+        <p>This will start a webserver running the mock driver on
+        <code>http://localhost:3001/api</code>; you can simply browse to that URL to get a
+        pretty view of the objects the driver deals with.</p>
+        
+        <h2 id="h3">Dynamic driver switching</h2>
+        
+        <p>The driver specified with the <code>-i</code> switch when <code>deltacloudd</code> is launched is
+        the default driver. Clients can switch drivers for any request. The list of
+        drivers supported by the server can be obtained from the <code>drivers</code>
+        collection.</p>
+        
+        <p>Some drivers also support the notion of a <em>provider</em>. Changing the provider
+        makes it possible to use the same driver against different instances of a
+        cloud, for example different regions in EC2 or different installations of
+        RHEV-M. The possible range of values for the provider is driver-specific.</p>
+        
+        <p>The driver and provider can be selected in one of two ways:</p>
+        
+        <ol>
+        <li>Through the request headers <code>X-Deltacloud-Driver</code> and
+        <code>X-Deltacloud-Provider</code>. For example, including the headers
+        <code>X-Deltacloud-Driver: ec2</code> and <code>X-Deltacloud-Provider: eu-west-1</code>
+        ensures that a request will be serviced by the EC2 driver, and that the
+        driver will use the eu-west-1 region in EC2.</li>
+        <li>Through the matrix request parameters <code>driver</code> and <code>provider</code> in the
+        <code>api</code> component of the server's URL. For example, requesting
+        <code>http://localhost:3001/api;driver=ec2;provider=eu-west-1</code> has the same
+        effect as using the two request headers mentioned above.</li>
+        </ol>
+        
+        
+        <h2 id="h4">Notes on specific drivers</h2>
         <h3>Compute Drivers</h3>
         <table id='providers'>
           <tr>
@@ -340,27 +395,7 @@
         
         
         
-        <h2 id="h1">Setting up the code</h2>
-        
-        <p>To set up a Deltacloud core and the drivers, install the
-        <a href="http://rubygems.org/gems/deltacloud-core">deltacloud-core</a> Ruby gem:</p>
-        
-        <pre><code># gem install deltacloud-core&#x000A;</code></pre>
-        
-        <p>RPM package will be available soon.</p>
-        
-        <h2 id="h2">Launch the server</h2>
-        
-        <p>The server is launched with the <code>deltacloudd</code> command and pass it the name
-        of the driver you want to use:</p>
-        
-        <pre><code>$ deltacloudd -i mock&#x000A;</code></pre>
-        
-        <p>This will start a webserver running the mock driver on
-        <code>http://localhost:3001/api</code>; you can simply browse to that URL to get a
-        pretty view of the objects the driver deals with.</p>
-        
-        <h3 id="h2_1">EC2 Driver</h3>
+        <h3 id="h4_1">EC2 Driver</h3>
         
         <p>For the Amazon EC2 you need to install the <code>amazon-ec2</code> Ruby gem:</p>
         
@@ -373,7 +408,7 @@
         <p>These credentials may be found on the <a href="http://aws-portal.amazon.com/gp/aws/developer/account/index.html?action=access-key">Access Identifiers</a>
         page at Amazon AWS.</p>
         
-        <h3 id="h2_2">RHEV-M Driver</h3>
+        <h3 id="h4_2">RHEV-M Driver</h3>
         
         <p>The RHEV-M driver needs to be installed on a Windows machine which has the
         RHEV-M Powershell API installed and configured. Assuming the directory
@@ -386,18 +421,18 @@
         the RHEVM.dll.config file which is referenced from the profile.ps1
         file located in My Documents/WindowsPowershell directory</p>
         
-        <h3 id="h2_3">Rackspace Driver</h3>
+        <h3 id="h4_3">Rackspace Driver</h3>
         
         <p>When using the Rackspace-cloud driver (Rackspace cloud used to be called
         "Mosso") - the password in a HTTP 401 challenge should be your API key, NOT
         your rackspace account password.  (you can get the API-key, or generate a
         new one, from the rackspace console).</p>
         
-        <h3 id="h2_4">RimuHosting</h3>
+        <h3 id="h4_4">RimuHosting</h3>
         
         <p>Further details coming soon.</p>
         
-        <h3 id="h2_5">OpenNebula</h3>
+        <h3 id="h4_5">OpenNebula</h3>
         
         <p>When using the <a href="http://www.opennebula.org/">OpenNebula</a> driver, the
         credentials passed in response to the HTTP 401 authentication challenge
-- 
1.7.4


[PATCH 2/3] drivers: new collection

Posted by lu...@redhat.com.
From: David Lutterkort <lu...@redhat.com>

---
 .../lib/deltacloud/helpers/application_helper.rb   |   14 ++++++
 server/server.rb                                   |   43 ++++++++++++++++---
 server/tests/drivers/mock/api_test.rb              |   16 +++++---
 server/views/api/drivers.html.haml                 |   14 ------
 server/views/api/drivers.xml.haml                  |   11 -----
 server/views/api/show.html.haml                    |    2 +-
 server/views/drivers/index.html.haml               |   15 +++++++
 server/views/drivers/index.xml.haml                |    7 +++
 server/views/drivers/show.html.haml                |   20 +++++++++
 server/views/drivers/show.xml.haml                 |    7 +++
 10 files changed, 110 insertions(+), 39 deletions(-)
 delete mode 100644 server/views/api/drivers.html.haml
 delete mode 100644 server/views/api/drivers.xml.haml
 create mode 100644 server/views/drivers/index.html.haml
 create mode 100644 server/views/drivers/index.xml.haml
 create mode 100644 server/views/drivers/show.html.haml
 create mode 100644 server/views/drivers/show.xml.haml

diff --git a/server/lib/deltacloud/helpers/application_helper.rb b/server/lib/deltacloud/helpers/application_helper.rb
index 4ee0480..345aaff 100644
--- a/server/lib/deltacloud/helpers/application_helper.rb
+++ b/server/lib/deltacloud/helpers/application_helper.rb
@@ -204,4 +204,18 @@ module ApplicationHelper
     "#{text[0..(length/2)]}#{end_string}"
   end
 
+  # Reverse the entrypoints hash for a driver from drivers.yaml; note that
+  # +d+ is a hash, not an actual driver object
+  def driver_provider(d)
+    result = {}
+    if d[:entrypoints]
+      d[:entrypoints].each do |kind, details|
+        details.each do |prov, url|
+          result[prov] ||= {}
+          result[prov][kind] = url
+        end
+      end
+    end
+    result
+  end
 end
diff --git a/server/server.rb b/server/server.rb
index 3c4273c..d287031 100644
--- a/server/server.rb
+++ b/server/server.rb
@@ -83,17 +83,11 @@ Sinatra::Application.register Sinatra::RespondTo
 # Redirect to /api
 get '/' do redirect url_for('/api'), 301; end
 
-get '/api/drivers\/?' do
-  respond_to do |format|
-    format.xml { haml :"api/drivers" }
-    format.html { haml :"api/drivers" }
-  end
-end
-
 get '/api\/?' do
   if params[:force_auth]
     return [401, 'Authentication failed'] unless driver.valid_credentials?(credentials)
   end
+  @collections = [:drivers] + driver.supported_collections
   respond_to do |format|
     format.xml { haml :"api/show" }
     format.json do
@@ -110,6 +104,41 @@ end
 
 # Rabbit DSL
 
+collection :drivers do
+  global!
+
+  description <<EOS
+List all the drivers supported by this server.
+EOS
+
+  operation :index do
+    description "List all drivers"
+    control do
+      @drivers = settings.drivers
+      respond_to do |format|
+        format.xml { haml :"drivers/index" }
+        format.json { @drivers.to_json }
+        format.html { haml :"drivers/index" }
+      end
+    end
+  end
+
+  operation :show do
+    description "Show details for a driver"
+    param :id,      :string
+    control do
+      @name = params[:id].to_sym
+      @driver = settings.drivers[@name]
+      return [404, "Driver #{@name} not found"] unless @driver
+      respond_to do |format|
+        format.xml { haml :"drivers/show" }
+        format.json { @driver.to_json }
+        format.html { haml :"drivers/show" }
+      end
+    end
+  end
+end
+
 collection :realms do
   description <<END
   Within a cloud provider a realm represents a boundary containing resources.
diff --git a/server/tests/drivers/mock/api_test.rb b/server/tests/drivers/mock/api_test.rb
index 1b0fd55..be4c7ee 100644
--- a/server/tests/drivers/mock/api_test.rb
+++ b/server/tests/drivers/mock/api_test.rb
@@ -76,17 +76,21 @@ module DeltacloudUnitTest
     def test_it_expose_available_drivers
       do_xml_request '/api/drivers'
       last_response.status.should == 200
-      (last_xml_response/"api/drivers").length.should > 0
-      (last_xml_response/'api/drivers/driver').length.should > 0
+      (last_xml_response/"drivers").length.should > 0
+      (last_xml_response/'drivers/driver').length.should > 0
+      (last_xml_response/"drivers/driver[@id = 'mock']").length.should == 1
     end
 
     def test_it_expose_ec2_driver_entrypoints
       do_xml_request '/api/drivers'
       last_response.status.should == 200
-      (last_xml_response/"api/drivers").length.should > 0
-      (last_xml_response/'api/drivers/driver[@id=ec2]/entrypoints').length.should > 0
-      (last_xml_response/'api/drivers/driver[@id=ec2]/entrypoints/entrypoint').first[:id].should_not == nil
-      (last_xml_response/'api/drivers/driver[@id=ec2]/entrypoints/entrypoint').first.text.should_not == ""
+      ec2 = (last_xml_response/'drivers/driver[@id=ec2]').first
+      (ec2/"provider").length.should > 0
+      (ec2/"provider[@id = 'eu-west-1']").length.should == 1
+      do_xml_request ec2[:href]
+      eu_west = (last_xml_response/"provider[@id = 'eu-west-1']").first
+      (eu_west/"entrypoint").length.should > 0
+      (eu_west/"entrypoint[@kind = 'ec2']").length.should == 1
     end
 
     def test_it_supports_matrix_params
diff --git a/server/views/api/drivers.html.haml b/server/views/api/drivers.html.haml
deleted file mode 100644
index 9eef8cf..0000000
--- a/server/views/api/drivers.html.haml
+++ /dev/null
@@ -1,14 +0,0 @@
-%h1
-  Available Drivers
-
-%table.display
-  %thead
-    %tr
-      %th ID
-      %th Name
-  %tbody
-    - settings.drivers.each do |id, details|
-      %tr
-        %td{ :width => '20%' }
-          %tt= id
-        %td= details[:name]
diff --git a/server/views/api/drivers.xml.haml b/server/views/api/drivers.xml.haml
deleted file mode 100644
index 93975f0..0000000
--- a/server/views/api/drivers.xml.haml
+++ /dev/null
@@ -1,11 +0,0 @@
-%api{ :version => settings.version }
-  %drivers
-    - settings.drivers.each do |id, details|
-      %driver{ :id => id }
-        %name<
-          =details[:name]
-        - if details[:entrypoints]
-          - details[:entrypoints].each do |list_id, entrypoints|
-            %entrypoints{:id => list_id}
-              - entrypoints.each do |entrypoint, url|
-                %entrypoint{ :id => entrypoint }<=cdata(url)
diff --git a/server/views/api/show.html.haml b/server/views/api/show.html.haml
index 199c9fd..287b989 100644
--- a/server/views/api/show.html.haml
+++ b/server/views/api/show.html.haml
@@ -2,7 +2,7 @@
   Deltacloud API #{settings.version}
 
 %ul
-  - driver.supported_collections.sort_by { |k| k.to_s }.each do |key|
+  - @collections.sort_by { |k| k.to_s }.each do |key|
     %li
       = link_to key.to_s.gsub('_', ' ').titlecase, url_for("/api/#{key}")
       %dl
diff --git a/server/views/drivers/index.html.haml b/server/views/drivers/index.html.haml
new file mode 100644
index 0000000..fc35115
--- /dev/null
+++ b/server/views/drivers/index.html.haml
@@ -0,0 +1,15 @@
+%h1
+  Available Drivers
+
+%table.display
+  %thead
+    %tr
+      %th ID
+      %th Name
+  %tbody
+    - @drivers.each do |id, details|
+      %tr
+        %td{ :width => '20%' }
+          %tt
+            %a{ :href => driver_url(id) }= id
+        %td= details[:name]
diff --git a/server/views/drivers/index.xml.haml b/server/views/drivers/index.xml.haml
new file mode 100644
index 0000000..63e809f
--- /dev/null
+++ b/server/views/drivers/index.xml.haml
@@ -0,0 +1,7 @@
+%drivers
+  - @drivers.each do |id, details|
+    %driver{ :href => driver_url(id), :id => id }
+      %name<
+        =details[:name]
+      - driver_provider(details).keys.each do |prov|
+        %provider{ :id => prov }
diff --git a/server/views/drivers/show.html.haml b/server/views/drivers/show.html.haml
new file mode 100644
index 0000000..00f5d36
--- /dev/null
+++ b/server/views/drivers/show.html.haml
@@ -0,0 +1,20 @@
+%h1
+  Details for driver #{@name}
+
+%dl
+  %di
+    %dt ID
+    %dd= @name
+  %di
+    %dt Name
+    %dd= @driver[:name]
+  %di
+    %dt Provider
+    %dd
+      - providers = driver_provider(@driver).keys.collect { |k| k.to_s }.sort
+      - if providers.empty?
+        None
+      - else
+        %ol
+          - providers.each do |k|
+            %li= k
diff --git a/server/views/drivers/show.xml.haml b/server/views/drivers/show.xml.haml
new file mode 100644
index 0000000..ea5f508
--- /dev/null
+++ b/server/views/drivers/show.xml.haml
@@ -0,0 +1,7 @@
+%driver{ :href => driver_url(@name), :id => @name }
+  %name<
+    = @driver[:name]
+  - driver_provider(@driver).each do |prov, details|
+    %provider{ :id => prov }
+      - details.each do |kind, url|
+        %entrypoint{ :kind => kind }<=cdata(url)
-- 
1.7.4


[PATCH 1/3] Rabbit: add notion of a 'global' collection

Posted by lu...@redhat.com.
From: David Lutterkort <lu...@redhat.com>

Global collections are supported by every driver
---
 server/lib/sinatra/rabbit.rb |   17 +++++++++++++++--
 1 files changed, 15 insertions(+), 2 deletions(-)

diff --git a/server/lib/sinatra/rabbit.rb b/server/lib/sinatra/rabbit.rb
index a465ed6..8be2b8b 100644
--- a/server/lib/sinatra/rabbit.rb
+++ b/server/lib/sinatra/rabbit.rb
@@ -179,6 +179,7 @@ module Sinatra
         @name = name
         @description = ""
         @operations = {}
+        @global = false
         instance_eval(&block) if block_given?
         generate_documentation
         generate_head
@@ -193,6 +194,18 @@ module Sinatra
         @description = text
       end
 
+      # Mark this collection as global, i.e. independent of any specific
+      # driver
+      def global!
+        @global = true
+      end
+
+      # Return +true+ if this collection is global, i.e. independent of any
+      # specific driver
+      def global?
+        @global
+      end
+
       def generate_head
         current_collection = self
         ::Sinatra::Application.head("/api/#{name}") do
@@ -261,7 +274,7 @@ module Sinatra
       end
 
       def check_supported(driver)
-        unless driver.has_collection?(@name)
+        unless global? || driver.has_collection?(@name)
           raise UnsupportedCollectionException,
             "Collection #{@name} not supported by this driver"
         end
@@ -301,7 +314,7 @@ module Sinatra
 
     def entry_points
       collections.values.select { |coll|
-        driver.has_collection?(coll.name)
+        coll.global? || driver.has_collection?(coll.name)
       }.inject([]) do |m, coll|
         url = url_for coll.operations[:index].path, :full
         m << [ coll.name, url ]
-- 
1.7.4


Re: Make drivers a toplevel collection (rev 2)

Posted by Michal Fojtik <mf...@redhat.com>.
On Mar 30, 2011, at 1:35 AM, lutter@redhat.com wrote:

ACK to whole patchset.

  -- Michal

> This repost addresses the issues Michal found; specifically (1) the view
> templates for drivers are now included and (2) unit tests actually work
> 
> Original post:
> 
> We've had an XML representation of drivers supported by the server as a
> one-off page for a while now. In keeping with HATEAOS, the list of drivers
> should be reachable through the main API entry point.
> 
> To make that possible, I turned drivers into their own toplevel
> collection. I also changed the XML format for a driver to emphasize the
> list of possible provider values more. For the EC2 driver the XML now is:
> 
>  <driver href="http://localhost:3001/api/drivers/ec2" id="ec2">
>    <name>EC2</name>
> 
>    <provider id="us-west-1">
>      <entrypoint kind="s3">s3-us-west-1.amazonaws.com</entrypoint>
>      <entrypoint kind="elb">elasticloadbalancing.us-west-1.amazonaws.com</entrypoint>
>      <entrypoint kind="ec2">ec2.us-west-1.amazonaws.com</entrypoint>
>    </provider>
> 
>    .. similar provider tags for the other regions ..
>  </driver>
> 
> David

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