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 2012/04/17 15:39:54 UTC

[PATCH core 15/32] Core: Added Deltacloud::API class and helpers to serve the default entrypoint

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


Signed-off-by: Michal fojtik <mf...@redhat.com>
---
 server/lib/deltacloud/helpers.rb |   86 ++-
 server/lib/deltacloud/server.rb  | 1278 +-------------------------------------
 2 files changed, 107 insertions(+), 1257 deletions(-)

diff --git a/server/lib/deltacloud/helpers.rb b/server/lib/deltacloud/helpers.rb
index cf8531a..7be8faf 100644
--- a/server/lib/deltacloud/helpers.rb
+++ b/server/lib/deltacloud/helpers.rb
@@ -1,4 +1,3 @@
-#
 # 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
@@ -14,10 +13,83 @@
 # License for the specific language governing permissions and limitations
 # under the License.
 
-require 'deltacloud/helpers/application_helper'
-require 'deltacloud/helpers/json_helper'
-require 'deltacloud/helpers/conversion_helper'
-require 'deltacloud/helpers/hardware_profiles_helper'
-require 'deltacloud/helpers/blob_stream'
+require_relative 'helpers/driver_helper'
+require_relative 'helpers/auth_helper'
+require_relative 'helpers/url_helper'
+require_relative 'helpers/assets_helper'
+require_relative 'helpers/deltacloud_helper'
+require_relative 'helpers/rabbit_helper'
+require_relative 'core_ext/string'
+require_relative 'core_ext/array'
+require_relative 'core_ext/hash'
+require_relative 'core_ext/integer'
+require_relative 'core_ext/proc'
+
+module Deltacloud::Collections
+  class Base < Sinatra::Base
+
+    extend Deltacloud::Helpers::Drivers
+    include Sinatra::Rabbit::Features
+
+    helpers Deltacloud::Helpers::Drivers
+    helpers Sinatra::AuthHelper
+    helpers Sinatra::UrlForHelper
+    helpers Sinatra::StaticAssets::Helpers
+    helpers Rack::RespondTo::Helpers
+    helpers Deltacloud::Helpers::Application
+
+    register Rack::RespondTo
+
+    enable :xhtml
+    enable :dump_errors
+    enable :show_errors
+    disable :show_exceptions
+
+    set :root_url, API_ROOT_URL
+    set :version, API_VERSION
+    set :root, File.join(File.dirname(__FILE__), '..', '..')
+    set :views, root + '/views'
+    set :public_folder, root + '/public'
+
+    error do
+      report_error
+    end
+
+    error Deltacloud::ExceptionHandler::ValidationFailure do
+      report_error
+    end
+
+    before do
+      # Respond with 400, If we don't get a http Host header,
+      halt 400, "Unable to find HTTP Host header" if @env['HTTP_HOST'] == nil
+    end
+
+    after do
+      headers 'Server' => 'Apache-Deltacloud/' + settings.version
+    end
+
+    def self.new_route_for(route, &block)
+      get route_for('/' + route.to_s + '/new') do
+        instance_eval(&block) if block_given?
+        respond_to do |format|
+          format.html do
+            haml :"#{route}/new"
+          end
+        end
+      end
+    end
+
+    def self.check_capability(opts={})
+      Sinatra::Rabbit.set :check_capability, opts[:for]
+    end
+
+    def self.check_features(opts={})
+      Sinatra::Rabbit.set :check_features, opts[:for]
+    end
+
+    def self.route_for(url)
+      "#{settings.root_url}#{url}"
+    end
 
-helpers ApplicationHelper, ConversionHelper, HardwareProfilesHelper, JSONHelper
+  end
+end
diff --git a/server/lib/deltacloud/server.rb b/server/lib/deltacloud/server.rb
index 7b499d9..ebb7cb7 100644
--- a/server/lib/deltacloud/server.rb
+++ b/server/lib/deltacloud/server.rb
@@ -1,1266 +1,44 @@
-# 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 'sinatra'
-require 'deltacloud'
+require 'rubygems'
+require 'crack'
 require 'json'
-require 'sinatra/rack_accept'
-require 'sinatra/static_assets'
-require 'sinatra/rabbit'
-require 'sinatra/lazy_auth'
-require 'erb'
+require 'yaml'
 require 'haml'
-require 'open3'
-require 'sinatra/sinatra_verbose'
-require 'sinatra/rack_driver_select'
-require 'sinatra/rack_runtime'
-require 'sinatra/rack_etag'
-require 'sinatra/rack_date'
-require 'sinatra/rack_matrix_params'
-require 'sinatra/rack_syslog'
-
-set :version, '0.5.0'
-
-include Deltacloud::Drivers
-set :drivers, Proc.new { driver_config }
-
-Sinatra::Application.register Rack::RespondTo
-
-use Rack::ETag
-use Rack::Runtime
-use Rack::MatrixParams
-use Rack::DriverSelect
-use Rack::MediaType
-use Rack::Date
-use Rack::CommonLogger
-
-configure do
-  set :root_url, "/api"
-  set :views, File::join($top_srcdir, 'views')
-  # NOTE: Change :public to :public_folder once we update sinatra to 1.3
-  # set :public_folder, File::join($top_srcdir, 'public')
-  if settings.respond_to? :public_folder
-    set :public_folder, File::join($top_srcdir, 'public')
-  else
-    set :public, File::join($top_srcdir, 'public')
-  end
-  # Try to load the driver on startup to fail early if there are issues
-  driver
-end
-
-configure :production do
-  use Rack::SyslogLogger
-  set :logger, SyslogFile.new
-  disable :logging
-  enable :show_errors
-  enable :dump_errors
-end
-
-configure :development do
-  # So we can just use puts for logging
-  set :raise_errors => false
-  set :show_exceptions, false
-  $stdout.sync = true
-  $stderr.sync = true
-end
-
-# You could use $API_HOST environment variable to change your hostname to
-# whatever you want (eg. if you running API behind NAT)
-HOSTNAME=ENV['API_HOST'] ? ENV['API_HOST'] : nil
-
-error do
-  report_error
-end
-
-error Deltacloud::ExceptionHandler::ValidationFailure do
-  report_error
-end
-
-before do
-  # Respond with 400, If we don't get a http Host header,
-  halt 400, "Unable to find HTTP Host header" if @env['HTTP_HOST'] == nil
-end
-
-after do
-  headers 'Server' => 'Apache-Deltacloud/' + settings.version
-end
-
-# Redirect to /api
-get '/' do redirect settings.root_url, 301; end
-
-
-# Generate a root route for API docs
-get "#{settings.root_url}/docs\/?" do
-  respond_to do |format|
-    format.html { haml :'docs/index' }
-    format.xml { haml :'docs/index' }
-  end
-end
-
-get "#{settings.root_url}\/?" do
-  if params[:force_auth]
-    return [401, 'Authentication failed'] unless driver.valid_credentials?(credentials)
-  end
-  @collections = [:drivers] + driver.supported_collections
-  @driver_name = driver.name unless driver.name.to_sym == DRIVER
-  @providers = driver.configured_providers
-  respond_to do |format|
-    format.xml { haml :"api/show" }
-    format.json do
-      { :api => {
-          :version => settings.version,
-          :driver => driver_symbol,
-          :links => entry_points.collect do |l|
-            { :rel => l[0], :href => l[1] }.merge(json_features_for_entrypoint(l))
-          end
-        }
-      }.to_json
-    end
-    format.html { haml :"api/show" }
-  end
-end
-
-post "#{settings.root_url}\/?"  do
-  p = {}
-  ["provider", "driver"].each { |k| p[k] = params[k] if params[k] }
-  p.delete("provider") if p["provider"] == "default"
-  q = p.map { |k,v| "#{k}=#{v}" }.join(";")
-  q = ";" + q unless q.empty?
-  redirect "#{settings.root_url}#{q}", 301
-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
-      if driver_symbol == @name
-        @providers = driver.providers(credentials)  if driver.respond_to? :providers
-      end
-      @driver = settings.drivers[@name]
-      halt 404 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.
-  The exact definition of a realm is left to the cloud provider.
-  In some cases, a realm may represent different datacenters, different continents,
-  or different pools of resources within a single datacenter.
-  A cloud provider may insist that resources must all exist within a single realm in
-  order to cooperate. For instance, storage volumes may only be allowed to be mounted to
-  instances within the same realm.
-END
-
-  operation :index do
-    description <<END
-    Operation will list all available realms. Realms can be filtered using
-    the "architecture" parameter.
-END
-    with_capability :realms
-    param :id,            :string
-    param :architecture,  :string,  :optional,  [ 'i386', 'x86_64' ]
-    control { filter_all(:realms) }
-  end
-
-  #FIXME: It always shows whole list
-  operation :show do
-    description 'Show an realm identified by "id" parameter.'
-    with_capability :realm
-    param :id,           :string, :required
-    control { show(:realm) }
-  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
-    available to the current use. Images can be filtered using the
-    "owner_id" and "architecture" parameters.
-END
-    with_capability :images
-    param :id,            :string
-    param :architecture,  :string,  :optional
-    control { filter_all(:images) }
-  end
-
-  operation :show do
-    description 'Show an image identified by "id" parameter.'
-    with_capability :image
-    param :id,           :string, :required
-    control { show(:image) }
-  end
-
-  operation :create do
-    description 'Create image from instance'
-    with_capability :create_image
-    param :instance_id, :string, :required
-    control do
-      @image = driver.create_image(credentials, {
-        :id => params[:instance_id],
-        :name => params[:name],
-        :description => params[:description]
-      })
-      status 201  # Created
-      response['Location'] = image_url(@image.id)
-      respond_to do |format|
-        format.xml  { haml :"images/show" }
-        format.json { convert_to_json(:image, @image) }
-        format.html { haml :"images/show" }
-      end
-    end
-  end
-
-  operation :destroy do
-    description "Remove specified image from collection"
-    with_capability :destroy_image
-    param :id,    :string,    :required
-    control do
-      driver.destroy_image(credentials, params[:id])
-      status 204
-      respond_to do |format|
-        format.xml
-        format.json
-        format.html { redirect(images_url) }
-      end
-    end
-  end
-
-end
-
-collection :instance_states do
-  description "The possible states of an instance, and how to traverse between them "
-
-  operation :index do
-    control do
-      @machine = driver.instance_state_machine
-      respond_to do |format|
-        format.xml { haml :'instance_states/show', :layout => false }
-        format.json do
-          out = []
-          @machine.states.each do |state|
-            transitions = state.transitions.collect do |t|
-              t.automatically? ? {:to => t.destination, :auto => 'true'} : {:to => t.destination, :action => t.action}
-            end
-            out << { :name => state, :transitions => transitions }
-          end
-          out.to_json
-        end
-        format.html { haml :'instance_states/show'}
-        format.gv { erb :"instance_states/show" }
-        format.png do
-          # Trick respond_to into looking up the right template for the
-          # graphviz file
-          gv = erb(:"instance_states/show")
-          png =  ''
-          cmd = 'dot -Kdot -Gpad="0.2,0.2" -Gsize="5.0,8.0" -Gdpi="180" -Tpng'
-          Open3.popen3( cmd ) do |stdin, stdout, stderr|
-            stdin.write( gv )
-            stdin.close()
-            png = stdout.read
-          end
-          content_type 'image/png'
-          png
-        end
-      end
-    end
-  end
-end
-
-get "#{settings.root_url}/instances/:id/run" do
-  @instance = driver.instance(credentials, :id => params[:id])
-  respond_to do |format|
-    format.html { haml :"instances/run_command" }
-  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
-      filter_all :load_balancers
-    end
-  end
-
-  operation :show do
-    description "Show details about given load balancer"
-    param :id,  :string,  :required
-    control { show :load_balancer }
-  end
-
-  operation :create do
-    description "Create a new load balancer"
-    param :name,  :string,  :required
-    param :realm_id,  :string,  :required
-    param :listener_protocol,  :string,  :required, ['HTTP', 'TCP']
-    param :listener_balancer_port,  :string,  :required
-    param :listener_instance_port,  :string,  :required
-    control do
-      @load_balancer = driver.create_load_balancer(credentials, params)
-      status 201  # Created
-      response['Location'] = load_balancer_url(@instance.id)
-      respond_to do |format|
-        format.xml  { haml :"load_balancers/show" }
-        format.json { convert_to_json(:load_balancer, @load_balancer) }
-        format.html { haml :"load_balancers/show" }
-      end
-    end
-  end
-
-  operation :register, :method => :post, :member => true do
-    description "Add instance to loadbalancer"
-    param :id,  :string,  :required
-    param :instance_id, :string,  :required
-    control do
-      driver.lb_register_instance(credentials, params)
-      status 204
-      respond_to do |format|
-        format.xml
-        format.json
-        format.html { redirect(load_balancer_url(params[:id])) }
-      end
-    end
-  end
-
-  operation :unregister, :method => :post, :member => true do
-    description "Remove instance from loadbalancer"
-    param :id,  :string,  :required
-    param :instance_id, :string,  :required
-    control do
-      driver.lb_unregister_instance(credentials, params)
-      status 204
-      respond_to do |format|
-        format.xml
-        format.json
-        format.html { redirect(load_balancer_url(params[:id])) }
-      end
-    end
-  end
-
-  operation :destroy do
-    description "Destroy given load balancer"
-    param :id,  :string,  :required
-    control do
-      driver.destroy_load_balancer(credentials, params[:id])
-      status 204
-      respond_to do |format|
-        format.xml
-        format.json
-        format.html { redirect(load_balancers_url) }
-      end
-    end
-  end
-
-end
-
-
-collection :instances do
-  description <<END
-  An instance is a concrete machine realized from an image.
-  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 ||= driver.realms(credentials)
-      @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
-    param :id,            :string,  :optional
-    param :state,         :string,  :optional
-    control { filter_all(:instances) }
-  end
-
-  operation :show do
-    description 'Show an instance identified by "id" parameter.'
-    with_capability :instance
-    param :id,           :string, :required
-    control { show(:instance) }
-  end
-
-  operation :create do
-    description "Create a new instance."
-    with_capability :create_instance
-    param :image_id,     :string, :required
-    param :realm_id,     :string, :optional
-    param :hwp_id,       :string, :optional
-    control do
-      @instance = driver.create_instance(credentials, params[:image_id], params)
-      if @instance.kind_of? Array
-        @elements = @instance
-        action_handler = "index"
-      else
-        response['Location'] = instance_url(@instance.id)
-        action_handler = "show"
-      end
-      status 201  # Created
-      respond_to do |format|
-        format.xml  { haml :"instances/#{action_handler}" }
-        format.json do
-          if @elements
-            convert_to_json(:instances, @elements)
-          else
-            convert_to_json(:instance, @instance)
-          end
-        end
-        format.html do
-          if @elements
-            haml :"instances/index"
-          elsif @instance and @instance.id
-            response['Location'] = instance_url(@instance.id)
-            haml :"instances/show"
-          else
-            redirect instances_url
-          end
-        end
-      end
-    end
-  end
-
-  operation :reboot, :method => :post, :member => true do
-    description "Reboot a running instance."
-    with_capability :reboot_instance
-    param :id,           :string, :required
-    control { instance_action(:reboot) }
-  end
-
-  operation :start, :method => :post, :member => true do
-    description "Start an instance."
-    with_capability :start_instance
-    param :id,           :string, :required
-    control { instance_action(:start) }
-  end
-
-  operation :stop, :method => :post, :member => true do
-    description "Stop a running instance."
-    with_capability :stop_instance
-    param :id,           :string, :required
-    control { instance_action(:stop) }
-  end
-
-  operation :destroy do
-    description "Destroy an instance."
-    with_capability :destroy_instance
-    param :id,           :string, :required
-    control { instance_action(:destroy) }
-  end
-
-  operation :run, :method => :post, :member => true do
-    description <<END
-  Run command on instance. Either password or private key must be send
-  in order to execute command. Authetication method should be advertised
-  in instance.
-END
-    with_capability :run_on_instance
-    param :id,          :string,  :required
-    param :cmd,         :string,  :required, [], "Shell command to run on instance"
-    param :private_key, :string,  :optional, [], "Private key in PEM format for authentication"
-    param :password,    :string,  :optional, [], "Password used for authentication"
-    control do
-      @output = driver.run_on_instance(credentials, params)
-      respond_to do |format|
-        format.xml { haml :"instances/run" }
-        format.html { haml :"instances/run" }
-      end
-    end
-  end
-end
-
-collection :hardware_profiles do
-  description <<END
- A hardware profile represents a configuration of resources upon which a
- machine may be deployed. It defines aspects such as local disk storage,
- available RAM, and architecture. Each provider is free to define as many
- (or as few) hardware profiles as desired.
-END
-
-  operation :index do
-    description "List of available hardware profiles."
-    with_capability :hardware_profiles
-    param :id,          :string
-    param :architecture,  :string,  :optional,  [ 'i386', 'x86_64' ]
-    control do
-        @profiles = driver.hardware_profiles(credentials, params)
-        respond_to do |format|
-          format.xml  { haml :'hardware_profiles/index' }
-          format.html  { haml :'hardware_profiles/index' }
-          format.json { convert_to_json(:hardware_profile, @profiles) }
-        end
-    end
-  end
-
-  operation :show do
-    description "Show specific hardware profile."
-    with_capability :hardware_profile
-    param :id,          :string,    :required
-    control do
-      @profile =  driver.hardware_profile(credentials, params[:id])
-      if @profile
-        respond_to do |format|
-          format.xml { haml :'hardware_profiles/show', :layout => false }
-          format.html { haml :'hardware_profiles/show' }
-          format.json { convert_to_json(:hardware_profile, @profile) }
-        end
-      else
-        report_error(404)
-      end
-    end
-  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
-    param :id,            :string
-    control { filter_all(:storage_snapshots) }
-  end
-
-  operation :show do
-    description "Show storage snapshot."
-    with_capability :storage_snapshot
-    param :id,          :string,    :required
-    control { show(:storage_snapshot) }
-  end
-
-  operation :create do
-    description "Create a new snapshot from volume"
-    with_capability :create_storage_snapshot
-    param :volume_id, :string,  :required
-    control do
-      @storage_snapshot = driver.create_storage_snapshot(credentials, params)
-      status 201  # Created
-      response['Location'] = storage_snapshot_url(@storage_snapshot.id)
-      show(:storage_snapshot)
-    end
-  end
-
-  operation :destroy do
-    description "Delete storage snapshot"
-    with_capability :destroy_storage_snapshot
-    param :id,  :string,  :required
-    control do
-      driver.destroy_storage_snapshot(credentials, params)
-      status 204
-      respond_to do |format|
-        format.xml
-        format.json
-        format.html { redirect(storage_snapshots_url) }
-      end
-    end
-  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
-    param :id,            :string
-    control { filter_all(:storage_volumes) }
-  end
-
-  operation :show do
-    description "Show storage volume."
-    with_capability :storage_volume
-    param :id,          :string,    :required
-    control { show(:storage_volume) }
-  end
-
-  operation :create do
-    description "Create a new storage volume"
-    with_capability :create_storage_volume
-    param :snapshot_id, :string,  :optional
-    param :capacity,    :string,  :optional
-    param :realm_id,    :string,  :optional
-    control do
-      @storage_volume = driver.create_storage_volume(credentials, params)
-      status 201
-      response['Location'] = storage_volume_url(@storage_volume.id)
-      respond_to do |format|
-        format.xml  { haml :"storage_volumes/show" }
-        format.html { haml :"storage_volumes/show" }
-        format.json { convert_to_json(:storage_volume, @storage_volume) }
-      end
-    end
-  end
-
-  operation :attach_instance, :method=>:get, :member=>true  do
-    description "A form to attach a storage volume to an instance"
-    control do
-      @instances = driver.instances(credentials)
-      respond_to do |format|
-        format.html{ haml :"storage_volumes/attach"}
-      end
-    end
-  end
-
-  operation :attach, :method => :post, :member => true do
-    description "Attach storage volume to instance"
-    with_capability :attach_storage_volume
-    param :id,         :string,  :required
-    param :instance_id,:string,  :required
-    param :device,     :string,  :required
-    control do
-      @storage_volume = driver.attach_storage_volume(credentials, params)
-      status 202
-      respond_to do |format|
-        format.html { redirect(storage_volume_url(params[:id]))}
-        format.xml  { haml :"storage_volumes/show" }
-        format.json { convert_to_json(:storage_volume, @storage_volume) }
-      end
-    end
-  end
-
-  operation :detach, :method => :post, :member => true do
-    description "Detach storage volume to instance"
-    with_capability :detach_storage_volume
-    param :id,         :string,  :required
-    control do
-      volume = driver.storage_volume(credentials, :id => params[:id])
-      @storage_volume =  driver.detach_storage_volume(credentials, :id => volume.id, :instance_id => volume.instance_id, :device => volume.device)
-      status 202
-      respond_to do |format|
-        format.html { redirect(storage_volume_url(params[:id]))}
-        format.xml  { haml :"storage_volumes/show" }
-        format.json { convert_to_json(:storage_volume, @storage_volume) }
-      end
-    end
-  end
-
-  operation :destroy do
-    description "Destroy storage volume"
-    with_capability :destroy_storage_volume
-    param :id,          :string,  :optional
-    control do
-      driver.destroy_storage_volume(credentials, params)
-      status 204
-      respond_to do |format|
-        format.xml
-        format.json
-        format.html { redirect(storage_volumes_url) }
-      end
-    end
-  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
-    control do
-      filter_all :keys
-    end
-  end
-
-  operation :show do
-    description "Show details about given instance credential."
-    with_capability :key
-    param :id,  :string,  :required
-    control { show :key }
-  end
-
-  operation :create do
-    description "Create a new instance credential if backend supports this."
-    with_capability :create_key
-    param :name,  :string,  :required
-    control do
-      @key = driver.create_key(credentials, { :key_name => params[:name] })
-      status 201
-      response['Location'] = key_url(@key.id)
-      respond_to do |format|
-        format.xml  { haml :"keys/show", :ugly => true }
-        format.html { haml :"keys/show" }
-        format.json { convert_to_json(:key, @key)}
-      end
-    end
-  end
-
-  operation :destroy do
-    description "Destroy given instance credential if backend supports this."
-    with_capability :destroy_key
-    param :id,  :string,  :required
-    control do
-      driver.destroy_key(credentials, { :id => params[:id]})
-      status 204
-      respond_to do |format|
-        format.xml
-        format.json
-        format.html { redirect(keys_url) }
-      end
-    end
-  end
-
-end
-
-#get html form for creating a new blob
-
-# The URL for getting the new blob form for the HTML UI looks like the URL
-# for getting the details of an existing blob. To make collisions less
-# likely, we use a name for the form that will rarely be the name of an
-# existing blob
-NEW_BLOB_FORM_ID = "new_blob_form_d15cfd90"
-
-get "#{settings.root_url}/buckets/:bucket/#{NEW_BLOB_FORM_ID}" do
-  @bucket_id = params[:bucket]
-  respond_to do |format|
-    format.html {haml :"blobs/new"}
-  end
-end
-
-collection :buckets do
-  description "Cloud Storage buckets - aka buckets|directories|folders"
-
-  collection :blobs do
-    description "Blobs associated with given bucket"
-
-    operation :show do
-      description "Display blob"
-      control do
-        @blob = driver.blob(credentials, { :id => params[:blob], 'bucket' => params[:bucket]})
-        if @blob
-          respond_to do |format|
-            format.xml { haml :"blobs/show" }
-            format.html { haml :"blobs/show" }
-            format.json { convert_to_json(:blob, @blob) }
-          end
-        else
-          report_error(404)
-        end
-      end
-
-    end
-
-    operation :create do
-      description "Create new blob"
-      param :blob_id,  :string,  :required
-      param :blob_data, :hash, :required
-      control do
-        bucket_id = params[:bucket]
-        blob_id = params['blob_id']
-        blob_data = params['blob_data']
-        user_meta = {}
-        #metadata from params (i.e., passed by http form post, e.g. browser)
-        max = params[:meta_params]
-        if(max)
-          (1..max.to_i).each do |i|
-            key = params[:"meta_name#{i}"]
-            key = "HTTP_X_Deltacloud_Blobmeta_#{key}"
-            value = params[:"meta_value#{i}"]
-            user_meta[key] = value
-          end
-        end
-        @blob = driver.create_blob(credentials, bucket_id, blob_id, blob_data, user_meta)
-        respond_to do |format|
-          format.xml { haml :"blobs/show" }
-          format.html { haml :"blobs/show"}
-          format.json {convert_to_json(:blob, @blob)}
-        end
-      end
-    end
-
-    operation :destroy do
-      description "Destroy given blob"
-      control do
-        bucket_id = params[:bucket]
-        blob_id = params[:blob]
-        driver.delete_blob(credentials, bucket_id, blob_id)
-        status 204
-        respond_to do |format|
-          format.xml
-          format.json
-          format.html { redirect(bucket_url(bucket_id)) }
-        end
-      end
-    end
-
-    operation :stream, :member => true, :standard => true, :method => :put do
-      description "Stream new blob data into the blob"
-      control do
-        if(env["BLOB_SUCCESS"]) #ie got a 200ok after putting blob
-          content_type = env["CONTENT_TYPE"]
-          content_type ||=  ""
-          @blob = driver.blob(credentials, {:id => params[:blob],
-                                            'bucket' => params[:bucket]})
-          respond_to do |format|
-            format.xml { haml :"blobs/show" }
-            format.html { haml :"blobs/show" }
-            format.json { convert_to_json(:blob, @blob) }
-          end
-        elsif(env["BLOB_FAIL"])
-          report_error(500) #OK?
-        else # small blobs - < 112kb dont hit the streaming monkey patch - use 'normal' create_blob
-          # also, if running under webrick don't hit the streaming patch (Thin specific)
-          bucket_id = params[:bucket]
-          blob_id = params[:blob]
-          temp_file = Tempfile.new("temp_blob_file")
-          temp_file.write(env['rack.input'].read)
-          temp_file.flush
-          content_type = env['CONTENT_TYPE'] || ""
-          blob_data = {:tempfile => temp_file, :type => content_type}
-          user_meta = BlobHelper::extract_blob_metadata_hash(request.env)
-          @blob = driver.create_blob(credentials, bucket_id, blob_id, blob_data, user_meta)
-          temp_file.delete
-          respond_to do |format|
-            format.xml { haml :"blobs/show" }
-            format.html { haml :"blobs/show" }
-            format.json { convert_to_json(:blob, @blob) }
-          end
-        end
-      end
-    end
-
-    operation :metadata, :member => true, :standard => true, :method => :head do
-      description "Get blob metadata"
-      control do
-        @blob_id = params[:blob]
-        @blob_metadata = driver.blob_metadata(credentials, {:id => params[:blob], 'bucket' => params[:bucket]})
-        if @blob_metadata
-          @blob_metadata.each do |k,v|
-            headers["X-Deltacloud-Blobmeta-#{k}"] = v
-          end
-          status 204
-          respond_to do |format|
-            format.xml
-            format.json
-          end
-        else
-          report_error(404)
-        end
-      end
-    end
-
-    operation :update, :member => true, :method => :post do
-      description "Update blob metadata"
-      control do
-        meta_hash = BlobHelper::extract_blob_metadata_hash(request.env)
-        success = driver.update_blob_metadata(credentials, {'bucket'=>params[:bucket], :id =>params[:blob], 'meta_hash' => meta_hash})
-        if(success)
-          meta_hash.each do |k,v|
-            headers["X-Deltacloud-Blobmeta-#{k}"] = v
-          end
-          status 204
-          respond_to do |format|
-            format.xml
-            format.json
-          end
-        else
-          report_error(404) #FIXME is this the right error code?
-        end
-      end
-    end
+require 'sinatra/base'
+require 'sinatra/rabbit'
 
-    operation :content, :member => true, :method => :get do
-      description "Download blob content"
-      control do
-        @blob = driver.blob(credentials, { :id => params[:blob], 'bucket' => params[:bucket]})
-        if @blob
-          params['content_length'] = @blob.content_length
-          params['content_type'] = @blob.content_type
-          params['content_disposition'] = "attachment; filename=#{@blob.id}"
-          BlobStream.call(env, credentials, params)
-        else
-          report_error(404)
-        end
-      end
-    end
+require_relative '../sinatra'
+require_relative './models'
+require_relative './drivers'
+require_relative './helpers'
+require_relative './collections'
 
-  end
 
-  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
+module Deltacloud
+  class API < Collections::Base
 
-  operation :index do
-    description "List buckets associated with this account"
-    with_capability :buckets
-    param :id,        :string
-    param :name,      :string
-    param :size,      :string
-    control { filter_all(:buckets) }
-  end
+    # Enable logging
+    use Rack::CommonLogger
+    use Rack::Date
+    use Rack::ETag
+    use Rack::MatrixParams
+    use Rack::DriverSelect
+    use Rack::Accept
+    use Rack::MediaType
 
-  operation :show do
-    description "Show bucket"
-    with_capability :bucket
-    param :id,        :string
-    control { show(:bucket) }
-  end
+    include Deltacloud::Helpers
+    include Deltacloud::Collections
 
-  operation :create do
-    description "Create a new bucket (POST /api/buckets)"
-    with_capability :create_bucket
-    param :name,      :string,    :required
-    control do
-      @bucket = driver.create_bucket(credentials, params[:name], params)
-      status 201
-      response['Location'] = bucket_url(@bucket.id)
-      respond_to do |format|
-        format.xml  { haml :"buckets/show" }
-        format.json { convert_to_json(:bucket, @bucket) }
-        format.html do
-          redirect bucket_url(@bucket.id) if @bucket and @bucket.id
-          redirect buckets_url
-        end
+    get API_ROOT_URL do
+      if params[:force_auth]
+        return [401, 'Authentication failed'] unless driver.valid_credentials?(credentials)
       end
-    end
-  end
-
-  operation :destroy do
-    description "Delete a bucket by name - bucket must be empty"
-    with_capability :delete_bucket
-    param :id,    :string,    :required
-    control do
-      driver.delete_bucket(credentials, params[:id], params)
-      status 204
       respond_to do |format|
-        format.xml
-        format.json
-        format.html {  redirect(buckets_url) }
+        format.xml { haml :"api/show" }
+        format.json { xml_to_json :"api/show" }
+        format.html { haml :"api/show" }
       end
     end
-  end
-
-end
 
-get "#{settings.root_url}/addresses/:id/associate" do
-  @instances = driver.instances(credentials)
-  @address = Address::new(:id => params[:id])
-  respond_to do |format|
-    format.html { haml :"addresses/associate" }
   end
 end
 
-collection :addresses do
-  description "Manage IP addresses"
-
-  operation :index do
-    description "List IP addresses assigned to your account."
-    with_capability :addresses
-    control do
-      filter_all :addresses
-    end
-  end
-
-  operation :show do
-    description "Show details about IP addresses specified by given ID"
-    with_capability :address
-    param :id,  :string,  :required
-    control { show :address }
-  end
-
-  operation :create do
-    description "Acquire a new IP address for use with your account."
-    with_capability :create_address
-    control do
-      @address = driver.create_address(credentials, {})
-      status 201    # Created
-      response['Location'] = address_url(@address.id)
-      respond_to do |format|
-        format.xml  { haml :"addresses/show", :ugly => true }
-        format.html { haml :"addresses/_address", :layout => false }
-        format.json { convert_to_json(:address, @address) }
-      end
-    end
-  end
-
-  operation :destroy do
-    description "Release an IP address associated with your account"
-    with_capability :destroy_address
-    param :id,  :string,  :required
-    control do
-      driver.destroy_address(credentials, { :id => params[:id]})
-      status 204
-      respond_to do |format|
-        format.xml
-        format.json
-        format.html { redirect(addresses_url) }
-      end
-    end
-  end
-
-  operation :associate, :method => :post, :member => true do
-    description "Associate an IP address to an instance"
-    with_capability :associate_address
-    param :id, :string, :required
-    param :instance_id, :string, :required
-    control do
-      driver.associate_address(credentials, { :id => params[:id], :instance_id => params[:instance_id]})
-      status 202   # Accepted
-      respond_to do |format|
-        format.xml
-        format.json
-        format.html { redirect(address_url(params[:id])) }
-      end
-    end
-  end
-
-  operation :disassociate, :method => :post, :member => true do
-    description "Disassociate an IP address from an instance"
-    with_capability :associate_address
-    param :id, :string, :required
-    control do
-      driver.disassociate_address(credentials, { :id => params[:id] })
-      status 202   # Accepted
-      respond_to do |format|
-        format.xml
-        format.json
-        format.html { redirect(address_url(params[:id])) }
-      end
-    end
-  end
-
-end
-
-#delete a firewall rule
-delete '/api/firewalls/:firewall/:rule' do
-  opts = {}
-  opts[:firewall] = params[:firewall]
-  opts[:rule_id] = params[:rule]
-  driver.delete_firewall_rule(credentials, opts)
-  status 204
-  respond_to do |format|
-    format.xml
-    format.json
-    format.html {redirect firewall_url(params[:firewall])}
-  end
-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 :id,  :string,  :required
-    control do
-      @firewall_name = params[:id]
-      respond_to do |format|
-        format.html {haml :"firewalls/new_rule" }
-      end
-    end
-  end
-
-  operation :index do
-    description 'List all firewalls'
-    with_capability :firewalls
-    control { filter_all(:firewalls) }
-  end
-
-  operation :show do
-    description 'Show details for a specific firewall - list all rules'
-    with_capability :firewall
-    param :id,            :string,    :required
-    control { show(:firewall) }
-  end
-
-  operation :create do
-    description 'Create a new firewall'
-    with_capability :create_firewall
-    param :name,          :string,    :required
-    param :description,   :string,    :required
-    control do
-      @firewall = driver.create_firewall(credentials, params )
-      status 201  # Created
-      response['Location'] = firewall_url(@firewall.id)
-      respond_to do |format|
-        format.xml  { haml :"firewalls/show" }
-        format.html { haml :"firewalls/show" }
-        format.json { convert_to_json(:firewall, @firewall) }
-      end
-    end
-  end
-
-  operation :destroy do
-    description 'Delete a specified firewall - error if firewall has rules'
-    with_capability :delete_firewall
-    param :id,            :string,    :required
-    control do
-      driver.delete_firewall(credentials, params)
-      status 204
-      respond_to do |format|
-        format.xml
-        format.json
-        format.html {  redirect(firewalls_url) }
-      end
-    end
-  end
-
-  #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"
-    param :protocol,  :required, :string, ['tcp','udp','icmp'], "Transport layer protocol for the rule"
-    param :port_from, :required, :string, [], "Start of port range for the rule"
-    param :port_to,   :required, :string, [], "End of port range for the rule"
-    with_capability :create_firewall_rule
-    control do
-      #source IPs from params
-      addresses =  params.inject([]){|result,current| result << current.last unless current.grep(/^ip[-_]address/i).empty?; result}
-      #source groups from params
-      groups = {}
-      max_groups  = params.select{|k,v| k=~/^group/}.size/2
-      for i in (1..max_groups) do
-        groups.merge!({params["group#{i}"]=>params["group#{i}owner"]})
-      end
-      params['addresses'] = addresses
-      params['groups'] = groups
-      if addresses.empty? && groups.empty?
-        raise Deltacloud::ExceptionHandler::ValidationFailure.new(
-          StandardError.new("No sources. Specify at least one source ip_address or group")
-        )
-      end
-      driver.create_firewall_rule(credentials, params)
-      @firewall = driver.firewall(credentials, {:id => params[:id]})
-      status 201
-      respond_to do |format|
-        format.xml  { haml :"firewalls/show" }
-        format.html { haml :"firewalls/show" }
-        format.json { convert_to_json(:firewall, @firewall) }
-      end
-    end
-  end
-
-end
-- 
1.7.10