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:50 UTC

[PATCH core 11/32] Core: Moved base_driver and exception handling API to drivers directory

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


Signed-off-by: Michal fojtik <mf...@redhat.com>
---
 server/lib/deltacloud/base_driver/base_driver.rb |  250 --------------------
 server/lib/deltacloud/base_driver/exceptions.rb  |  191 ---------------
 server/lib/deltacloud/base_driver/features.rb    |  276 ----------------------
 server/lib/deltacloud/drivers/base_driver.rb     |  265 +++++++++++++++++++++
 server/lib/deltacloud/drivers/exceptions.rb      |  191 +++++++++++++++
 5 files changed, 456 insertions(+), 717 deletions(-)
 delete mode 100644 server/lib/deltacloud/base_driver/base_driver.rb
 delete mode 100644 server/lib/deltacloud/base_driver/exceptions.rb
 delete mode 100644 server/lib/deltacloud/base_driver/features.rb
 create mode 100644 server/lib/deltacloud/drivers/base_driver.rb
 create mode 100644 server/lib/deltacloud/drivers/exceptions.rb

diff --git a/server/lib/deltacloud/base_driver/base_driver.rb b/server/lib/deltacloud/base_driver/base_driver.rb
deleted file mode 100644
index 01dd5e7..0000000
--- a/server/lib/deltacloud/base_driver/base_driver.rb
+++ /dev/null
@@ -1,250 +0,0 @@
-#
-# 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 'deltacloud/base_driver/exceptions'
-
-module Deltacloud
-
-  class BaseDriver
-
-    include ExceptionHandler
-
-    STATE_MACHINE_OPTS = {
-      :all_states => [:start, :pending, :running, :stopping, :stopped, :finish],
-      :all_actions => [:create, :reboot, :stop, :start, :destroy]
-    }
-
-    def name
-      self.class.name.split('::').last.gsub('Driver', '').downcase
-    end
-
-    def self.exceptions(&block)
-      ExceptionHandler::exceptions(&block)
-    end
-
-    def self.define_hardware_profile(name,&block)
-      @hardware_profiles ||= []
-      hw_profile = @hardware_profiles.find{|e| e.name == name}
-      return if hw_profile
-      hw_profile = ::Deltacloud::HardwareProfile.new( name, &block )
-      @hardware_profiles << hw_profile
-      hw_params = hw_profile.params
-      unless hw_params.empty?
-        feature :instances, :hardware_profiles do
-          decl.operation(:create) { add_params(hw_params) }
-        end
-      end
-    end
-
-    def self.hardware_profiles
-      @hardware_profiles ||= []
-      @hardware_profiles
-    end
-
-    def hardware_profiles(credentials, opts = nil)
-      results = self.class.hardware_profiles
-      filter_hardware_profiles(results, opts)
-    end
-
-    def hardware_profile(credentials, name)
-      hardware_profiles(credentials, :id => name).first
-    end
-
-    def filter_hardware_profiles(profiles, opts)
-      if opts
-        if v = opts[:architecture]
-          profiles = profiles.select { |hwp| hwp.include?(:architecture, v) }
-        end
-        # As a request param, we call 'name' 'id'
-        if v = opts[:id]
-          profiles = profiles.select { |hwp| hwp.name == v }
-        end
-      end
-      profiles
-    end
-
-    def find_hardware_profile(credentials, name, image_id)
-      hwp = nil
-      if name
-        unless hwp = hardware_profiles(credentials, :id => name).first
-          raise BackendError.new(400, "bad-hardware-profile-name",
-            "Hardware profile '#{name}' does not exist", nil)
-        end
-      else
-        unless image = image(credentials, :id=>image_id)
-          raise BackendError.new(400, "bad-image-id",
-              "Image with ID '#{image_id}' does not exist", nil)
-        end
-        hwp = hardware_profiles(credentials,
-                                :architecture=>image.architecture).first
-      end
-      return hwp
-    end
-
-    def self.define_instance_states(&block)
-      machine = ::Deltacloud::StateMachine.new(STATE_MACHINE_OPTS, &block)
-      @instance_state_machine = machine
-    end
-
-    def self.instance_state_machine
-      @instance_state_machine
-    end
-
-    def instance_state_machine
-      self.class.instance_state_machine
-    end
-
-    def instance_actions_for(state)
-      actions = []
-      state_key = state.downcase.to_sym
-      states = instance_state_machine.states()
-      current_state = states.find{|e| e.name == state.underscore.to_sym }
-      if ( current_state )
-        actions = current_state.transitions.collect{|e|e.action}
-        actions.reject!{|e| e.nil?}
-      end
-      actions
-    end
-
-    ## Capabilities
-    # The rabbit dsl supports declaring a capability that is required
-    # in the backend driver for the call to succeed. A driver can
-    # provide a capability by implementing the method with the same
-    # name as the capability. Below is a list of the capabilities as
-    # the expected method signatures.
-    #
-    # Following the capability list are the resource member show
-    # methods. They each require that the corresponding collection
-    # method be defined
-    #
-    # TODO: standardize all of these to the same signature (credentials, opts)
-    #
-    # def realms(credentials, opts=nil)
-    #
-    # def images(credentials, ops)
-    #
-    # def instances(credentials, ops)
-    # def create_instance(credentials, image_id, opts)
-    # def start_instance(credentials, id)
-    # def stop_instance(credentials, id)
-    # def reboot_instance(credentials, id)
-    #
-    # def storage_volumes(credentials, ops)
-    #
-    # def storage_snapshots(credentials, ops)
-    #
-    # def buckets(credentials, opts = nil)
-    # def create_bucket(credentials, name, opts=nil)
-    # def delete_bucket(credentials, name, opts=nil)
-    #
-    # def blobs(credentials, opts = nil)
-    # def blob_data(credentials, bucket_id, blob_id, opts)
-    # def create_blob(credentials, bucket_id, blob_id, blob_data, opts=nil)
-    # def delete_blob(credentials, bucket_id, blob_id, opts=nil)
-    #
-    # def keys(credentials, opts)
-    # def create_key(credentials, opts)
-    # def destroy_key(credentials, opts)
-    #
-    # def firewalls(credentials, opts)
-    # def create_firewall(credentials, opts)
-    # def delete_firewall(credentials, opts)
-    # def create_firewall_rule(credentials, opts)
-    # def delete_firewall_rule(credentials, opts)
-    # def providers(credentials)
-    def realm(credentials, opts)
-      realms = realms(credentials, opts).first if has_capability?(:realms)
-    end
-
-    def image(credentials, opts)
-      images(credentials, opts).first if has_capability?(:images)
-    end
-
-    def instance(credentials, opts)
-      instances(credentials, opts).first if has_capability?(:instances)
-    end
-
-    def storage_volume(credentials, opts)
-      storage_volumes(credentials, opts).first if has_capability?(:storage_volumes)
-    end
-
-    def storage_snapshot(credentials, opts)
-      storage_snapshots(credentials, opts).first if has_capability?(:storage_snapshots)
-    end
-
-    def bucket(credentials, opts = {})
-      #list of objects within bucket
-      buckets(credentials, opts).first if has_capability?(:buckets)
-    end
-
-    def blob(credentials, opts = {})
-      blobs(credentials, opts).first if has_capability?(:blobs)
-    end
-
-    def key(credentials, opts=nil)
-      keys(credentials, opts).first if has_capability?(:keys)
-    end
-
-    def firewall(credentials, opts={})
-      firewalls(credentials, opts).first if has_capability?(:firewalls)
-    end
-
-    MEMBER_SHOW_METHODS =
-      [ :realm, :image, :instance, :storage_volume, :bucket, :blob, :key, :firewall ]
-
-    def has_capability?(capability)
-      if MEMBER_SHOW_METHODS.include?(capability.to_sym)
-        has_capability?(capability.to_s.pluralize)
-      else
-        respond_to?(capability)
-      end
-    end
-
-    def filter_on(collection, attribute, opts)
-      return collection if opts.nil?
-      return collection if opts[attribute].nil?
-      filter = opts[attribute]
-      if ( filter.is_a?( Array ) )
-        return collection.select{|e| filter.include?( e.send(attribute) ) }
-      else
-        return collection.select{|e| filter == e.send(attribute) }
-      end
-    end
-
-    def supported_collections
-      DEFAULT_COLLECTIONS
-    end
-
-    def has_collection?(collection)
-      supported_collections.include?(collection)
-    end
-
-    def catched_exceptions_list
-      { :error => [], :auth => [], :glob => [] }
-    end
-
-    def api_provider
-      Thread.current[:provider] || ENV['API_PROVIDER']
-    end
-
-    # Return an array of the providers statically configured
-    # in the driver's YAML file
-    def configured_providers
-      []
-    end
-  end
-
-end
diff --git a/server/lib/deltacloud/base_driver/exceptions.rb b/server/lib/deltacloud/base_driver/exceptions.rb
deleted file mode 100644
index a89b05f..0000000
--- a/server/lib/deltacloud/base_driver/exceptions.rb
+++ /dev/null
@@ -1,191 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.  The
-# ASF licenses this file to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance with the
-# License.  You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
-# License for the specific language governing permissions and limitations
-# under the License.
-#
-
-module Deltacloud
-  module ExceptionHandler
-
-    class DeltacloudException < StandardError
-
-      attr_accessor :code, :name, :message, :backtrace, :request
-
-      def initialize(code, name, message, backtrace, request=nil)
-        @code, @name, @message = code, name, message
-        @backtrace = backtrace
-        @request = request
-        self
-      end
-
-    end
-
-    class AuthenticationFailure < DeltacloudException
-      def initialize(e, message=nil)
-        message ||= e.message
-        super(401, e.class.name, message, e.backtrace)
-      end
-    end
-
-    class UnknownMediaTypeError < DeltacloudException
-      def initialize(e, message=nil)
-        message ||= e.message
-        super(406, e.class.name, message, e.backtrace)
-      end
-    end
-
-    class MethodNotAllowed < DeltacloudException
-      def initialize(e, message=nil)
-        message ||= e.message
-        super(405, e.class.name, message, e.backtrace)
-      end
-    end
-
-    class ValidationFailure < DeltacloudException
-      def initialize(e, message=nil)
-        message ||= e.message
-        super(400, e.class.name, message, e.backtrace)
-      end
-    end
-
-    class BackendError < DeltacloudException
-      def initialize(e, message=nil)
-        message ||= e.message
-        super(500, e.class.name, message, e.backtrace, message)
-      end
-    end
-
-    class ProviderError < DeltacloudException
-      def initialize(e, message)
-        message ||= e.message
-        super(502, e.class.name, message, e.backtrace)
-      end
-    end
-
-    class ProviderTimeout < DeltacloudException
-      def initialize(e, message)
-        message ||= e.message
-        super(504, e.class.name, message, e.backtrace)
-      end
-    end
-
-    class NotImplemented < DeltacloudException
-      def initialize(e, message)
-        message ||= e.message
-        super(501, e.class.name, message, e.backtrace)
-      end
-    end
-
-    class ObjectNotFound < DeltacloudException
-      def initialize(e, message)
-        message ||= e.message
-        super(404, e.class.name, message, e.backtrace)
-      end
-    end
-
-    class NotSupported < DeltacloudException
-      def initialize(message)
-        super(501, self.class.name, message, self.backtrace)
-      end
-    end
-
-    class ExceptionDef
-      attr_accessor :status
-      attr_accessor :message
-      attr_reader   :conditions
-      attr_reader   :handler
-
-      def initialize(conditions, &block)
-        @conditions = conditions
-        instance_eval(&block) if block_given?
-      end
-
-      def status(code)
-        self.status = code
-      end
-
-      def message(message)
-        self.message = message
-      end
-
-      def exception(handler)
-        self.handler = handler
-      end
-
-      # Condition can be class or regexp
-      #
-      def match?(e)
-        @conditions.each do |c|
-          return true if c.class == Class && e.class == c
-          return true if c.class == Regexp && (e.class.name =~ c or e.message =~ c)
-        end
-        return false
-      end
-
-      def handler(e)
-        return @handler if @handler
-        case @status
-          when 401 then Deltacloud::ExceptionHandler::AuthenticationFailure.new(e, @message)
-          when 404 then Deltacloud::ExceptionHandler::ObjectNotFound.new(e, @message)
-          when 406 then Deltacloud::ExceptionHandler::UnknownMediaTypeError.new(e, @message)
-          when 405 then Deltacloud::ExceptionHandler::MethodNotAllowed.new(e, @message)
-          when 400 then Deltacloud::ExceptionHandler::ValidationFailure.new(e, @message)
-          when 500 then Deltacloud::ExceptionHandler::BackendError.new(e, @message)
-          when 501 then Deltacloud::ExceptionHandler::NotImplemented.new(e, @message)
-          when 502 then Deltacloud::ExceptionHandler::ProviderError.new(e, @message)
-          when 504 then Deltacloud::ExceptionHandler::ProviderTimeout.new(e, @message)
-        end
-      end
-
-    end
-
-    class Exceptions
-      attr_reader :exception_definitions
-
-      def initialize(&block)
-        @exception_definitions = []
-        instance_eval(&block) if block_given?
-        self
-      end
-
-      def on(*conditions, &block)
-        @exception_definitions << ExceptionDef::new(conditions, &block) if block_given?
-      end
-    end
-
-    def self.exceptions(&block)
-      @definitions = Exceptions.new(&block).exception_definitions if block_given?
-      @definitions
-    end
-
-    def safely(&block)
-      begin
-        block.call
-      rescue
-        report_method = $stderr.respond_to?(:err) ? :err : :puts
-        Deltacloud::ExceptionHandler::exceptions.each do |exdef|
-          if exdef.match?($!)
-            new_exception = exdef.handler($!)
-            m = new_exception.message.nil? ? $!.message : new_exception.message
-            $stderr.send(report_method, "#{[$!.class.to_s, m].join(':')}\n#{$!.backtrace[0..10].join("\n")}")
-            raise exdef.handler($!) unless new_exception.nil?
-          end
-        end
-        $stderr.send(report_method, "[NO HANDLED] #{[$!.class.to_s, $!.message].join(': ')}\n#{$!.backtrace.join("\n")}")
-        raise Deltacloud::ExceptionHandler::BackendError.new($!, "Unhandled exception or status code (#{$!.message})")
-      end
-    end
-
-  end
-
-end
diff --git a/server/lib/deltacloud/base_driver/features.rb b/server/lib/deltacloud/base_driver/features.rb
deleted file mode 100644
index 37e5ef0..0000000
--- a/server/lib/deltacloud/base_driver/features.rb
+++ /dev/null
@@ -1,276 +0,0 @@
-#
-# 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 'deltacloud/validation'
-
-# Add advertising of optional features to the base driver
-module Deltacloud
-
-  class FeatureError < StandardError; end
-  class DuplicateFeatureDeclError < FeatureError; end
-  class UndeclaredFeatureError < FeatureError; end
-
-  class BaseDriver
-
-    # An operation on a collection like cretae or show. Features
-    # can add parameters to operations
-    class Operation
-      attr_reader :name
-
-      include Deltacloud::Validation
-
-      def initialize(name, &block)
-        @name = name
-        @params = {}
-        instance_eval &block
-      end
-    end
-
-    # The declaration of a feature, defines what operations
-    # are modified by it
-    class FeatureDecl
-      attr_reader :name, :operations
-
-      def initialize(name, &block)
-        @name = name
-        @operations = []
-        instance_eval &block
-      end
-
-      def description(text=nil)
-        @description = text if text
-        @description
-      end
-
-      # Add/modify an operation or look up an existing one. If +block+ is
-      # provided, create a new operation if none exists with name
-      # +name+. Evaluate the +block+ against this instance. If no +block+
-      # is provided, look up the operation with name +name+
-      def operation(name, &block)
-        op = @operations.find { |op| op.name == name }
-        if block_given?
-          if op.nil?
-            op = Operation.new(name, &block)
-            @operations << op
-          else
-            op.instance_eval(&block)
-          end
-        end
-        op
-      end
-    end
-
-    # A specific feature enabled by a driver (see +feature+)
-    class Feature
-      attr_reader :decl, :constraints
-
-      def initialize(decl, &block)
-        @decl = decl
-        @constraints = {}
-        instance_eval &block if block_given?
-      end
-
-      def name
-        decl.name
-      end
-
-      def operations
-        decl.operations
-      end
-
-      def description
-        decl.description
-      end
-
-      def constraint(name, value)
-        @constraints[name] = value
-      end
-    end
-
-    def self.feature_decls
-      @@feature_decls ||= {}
-    end
-
-    def self.feature_decl_for(collection, name)
-      decls = feature_decls[collection]
-      if decls
-        decls.find { |dcl| dcl.name == name }
-      else
-        nil
-      end
-    end
-
-    # Declare a new feature
-    def self.declare_feature(collection, name, &block)
-      feature_decls[collection] ||= []
-      raise DuplicateFeatureDeclError if feature_decl_for(collection, name)
-      feature_decls[collection] << FeatureDecl.new(name, &block)
-    end
-
-    def self.features
-      @features ||= {}
-    end
-
-    # Declare in a driver that it supports a specific feature
-    #
-    # The same feature can be declared multiple times in a driver, so that
-    # it can be changed successively by passing in different blocks.
-    def self.feature(collection, name, &block)
-      features[collection] ||= []
-      if f = features[collection].find { |f| f.name == name }
-        f.instance_eval(&block) if block_given?
-        return f
-      end
-      unless decl = feature_decl_for(collection, name)
-        raise UndeclaredFeatureError, "No feature #{name} for #{collection}"
-      end
-      features[collection] << Feature.new(decl, &block)
-    end
-
-    def features(collection)
-      self.class.features[collection] || []
-    end
-
-    def features_for_operation(collection, operation)
-      features(collection).select do |f|
-        f.operations.detect { |o| o.name == operation }
-      end
-    end
-
-    #
-    # Declaration of optional features
-    #
-    declare_feature :images,  :owner_id do
-      description "Filter images using owner id"
-      operation :index do
-        param :owner_id,  :string,  :optional,  [],  "Owner ID"
-      end
-    end
-
-    declare_feature :images,  :user_name do
-      description "Allow specifying user name for created image"
-      operation :create do
-        param :name,  :string,  :optional,  [],  "Image name"
-      end
-    end
-
-    declare_feature :images,  :user_description do
-      description "Allow specifying user description for created image"
-      operation :create do
-        param :description, :string,  :optional,  [],  "Image description"
-      end
-    end
-
-    declare_feature :instances, :user_name do
-      description "Accept a user-defined name on instance creation"
-      operation :create do
-        param :name, :string, :optional, [], "The user-defined name"
-      end
-    end
-
-    declare_feature :instances, :user_data do
-      description "Make user-defined data available on a special webserver"
-      operation :create do
-        param :user_data, :string, :optional, [],
-        "Base64 encoded user data will be published to internal webserver"
-      end
-    end
-
-    declare_feature :instances, :user_iso do
-      description "Make user-defined ISO available inside instance"
-      operation :create do
-        param :user_iso, :string, :optional, [],
-        "Base64 encoded gzipped ISO file will be accessible as CD-ROM drive in instance"
-      end
-    end
-
-    declare_feature :instances, :user_files do
-      description "Accept up to 5 files to be placed into the instance before launch."
-      operation :create do
-        1.upto(5) do |i|
-          param :"path#{i}", :string, :optional, [],
-          "Path where to place the #{i.ordinalize} file, up to 255 characters"
-          param :"content#{i}", :string, :optional, nil,
-          "Contents for the #{i.ordinalize} file, up to 10 kB, Base64 encoded"
-        end
-      end
-    end
-
-    declare_feature :instances, :firewalls do
-      description "Put instance in one or more firewalls (security groups) on launch"
-      operation :create do
-        param :firewalls, :array, :optional, nil, "Array of firewall ID strings"
-        "Array of firewall (security group) id"
-      end
-    end
-
-    declare_feature :instances, :authentication_key do
-      operation :create do
-        param :keyname, :string,  :optional, [], "Key authentification method"
-      end
-      operation :show do
-      end
-    end
-
-    declare_feature :instances, :authentication_password do
-      operation :create do
-        param :password, :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
-    end
-
-    declare_feature :buckets, :bucket_location do
-      description "Take extra location parameter for Bucket creation (e.g. S3, 'eu' or 'us-west-1')"
-      operation :create do
-        param :location, :string, :optional
-      end
-    end
-
-    declare_feature :instances, :register_to_load_balancer do
-      description "Register instance to load balancer"
-      operation :create do
-        param :load_balancer_id, :string, :optional
-      end
-    end
-
-    declare_feature :instances, :instance_count do
-      description "Number of instances to be launch with at once"
-      operation :create do
-        param :instance_count,  :string,  :optional
-      end
-    end
-
-    declare_feature :instances, :attach_snapshot do
-      description "Attach an snapshot to instance on create"
-      operation :create do
-        param :snapshot_id,  :string,  :optional
-        param :device_name,  :string,  :optional
-      end
-    end
-
-    declare_feature :instances, :sandboxing do
-      description "Allow lanuching sandbox images"
-      operation :create do
-        param :sandbox, :string,  :optional
-      end
-    end
-
-  end
-end
diff --git a/server/lib/deltacloud/drivers/base_driver.rb b/server/lib/deltacloud/drivers/base_driver.rb
new file mode 100644
index 0000000..5fb1a79
--- /dev/null
+++ b/server/lib/deltacloud/drivers/base_driver.rb
@@ -0,0 +1,265 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.  The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+module Deltacloud
+
+  class BaseDriver
+
+    include ExceptionHandler
+
+    def self.driver_name
+      name.split('::').last.gsub('Driver', '').downcase
+    end
+
+    def self.features
+      @features ||= []
+    end
+
+    def self.features_for(entity)
+      features.inject([]) do |result, item|
+        result << item[entity] if item.has_key? entity
+        result
+      end
+    end
+
+    def self.feature(collection, feature_name)
+      return if has_feature?(collection, feature_name)
+      features << { collection => feature_name }
+    end
+
+    def self.has_feature?(collection, feature_name)
+      features.any? { |f| (f.values.first == feature_name) && (f.keys.first == collection) }
+    end
+
+    def name
+      self.class.name.split('::').last.gsub('Driver', '').downcase
+    end
+
+    def self.exceptions(&block)
+      ExceptionHandler::exceptions(&block)
+    end
+
+    def self.define_hardware_profile(name,&block)
+      @hardware_profiles ||= []
+      hw_profile = @hardware_profiles.find{|e| e.name == name}
+      return if hw_profile
+      hw_profile = ::Deltacloud::HardwareProfile.new( name, &block )
+      @hardware_profiles << hw_profile
+      hw_params = hw_profile.params
+      # FIXME: Features
+      #unless hw_params.empty?
+      #  feature :instances, :hardware_profiles do
+      #    decl.operation(:create) { add_params(hw_params) }
+      #  end
+      #end
+    end
+
+    def self.hardware_profiles
+      @hardware_profiles ||= []
+      @hardware_profiles
+    end
+
+    def hardware_profiles(credentials, opts = nil)
+      results = self.class.hardware_profiles
+      filter_hardware_profiles(results, opts)
+    end
+
+    def hardware_profile(credentials, name)
+      name = name[:id] if name.kind_of? Hash
+      hardware_profiles(credentials, :id => name).first
+    end
+
+    def filter_hardware_profiles(profiles, opts)
+      if opts
+        if v = opts[:architecture]
+          profiles = profiles.select { |hwp| hwp.include?(:architecture, v) }
+        end
+        # As a request param, we call 'name' 'id'
+        if v = opts[:id]
+          profiles = profiles.select { |hwp| hwp.name == v }
+        end
+      end
+      profiles
+    end
+
+    def find_hardware_profile(credentials, name, image_id)
+      hwp = nil
+      if name
+        unless hwp = hardware_profiles(credentials, :id => name).first
+          raise BackendError.new(400, "bad-hardware-profile-name",
+            "Hardware profile '#{name}' does not exist", nil)
+        end
+      else
+        unless image = image(credentials, :id=>image_id)
+          raise BackendError.new(400, "bad-image-id",
+              "Image with ID '#{image_id}' does not exist", nil)
+        end
+        hwp = hardware_profiles(credentials,
+                                :architecture=>image.architecture).first
+      end
+      return hwp
+    end
+
+    def self.define_instance_states(&block)
+      machine = ::Deltacloud::StateMachine.new(&block)
+      @instance_state_machine = machine
+    end
+
+    def self.instance_state_machine
+      @instance_state_machine
+    end
+
+    def instance_state_machine
+      self.class.instance_state_machine
+    end
+
+    def instance_actions_for(state)
+      actions = []
+      state_key = state.downcase.to_sym
+      states = instance_state_machine.states()
+      current_state = states.find{|e| e.name == state.underscore.to_sym }
+      if ( current_state )
+        actions = current_state.transitions.collect{|e|e.action}
+        actions.reject!{|e| e.nil?}
+      end
+      actions
+    end
+
+    def has_capability?(method)
+      (self.class.instance_methods - self.class.superclass.methods).include? method
+    end
+
+    ## Capabilities
+    # The rabbit dsl supports declaring a capability that is required
+    # in the backend driver for the call to succeed. A driver can
+    # provide a capability by implementing the method with the same
+    # name as the capability. Below is a list of the capabilities as
+    # the expected method signatures.
+    #
+    # Following the capability list are the resource member show
+    # methods. They each require that the corresponding collection
+    # method be defined
+    #
+    # TODO: standardize all of these to the same signature (credentials, opts)
+    #
+    # def realms(credentials, opts=nil)
+    #
+    # def images(credentials, ops)
+    #
+    # def instances(credentials, ops)
+    # def create_instance(credentials, image_id, opts)
+    # def start_instance(credentials, id)
+    # def stop_instance(credentials, id)
+    # def reboot_instance(credentials, id)
+    #
+    # def storage_volumes(credentials, ops)
+    #
+    # def storage_snapshots(credentials, ops)
+    #
+    # def buckets(credentials, opts = nil)
+    # def create_bucket(credentials, name, opts=nil)
+    # def delete_bucket(credentials, name, opts=nil)
+    #
+    # def blobs(credentials, opts = nil)
+    # def blob_data(credentials, bucket_id, blob_id, opts)
+    # def create_blob(credentials, bucket_id, blob_id, blob_data, opts=nil)
+    # def delete_blob(credentials, bucket_id, blob_id, opts=nil)
+    #
+    # def keys(credentials, opts)
+    # def create_key(credentials, opts)
+    # def destroy_key(credentials, opts)
+    #
+    # def firewalls(credentials, opts)
+    # def create_firewall(credentials, opts)
+    # def delete_firewall(credentials, opts)
+    # def create_firewall_rule(credentials, opts)
+    # def delete_firewall_rule(credentials, opts)
+    # def providers(credentials)
+    def realm(credentials, opts)
+      realms = realms(credentials, opts).first if has_capability?(:realms)
+    end
+
+    def image(credentials, opts)
+      images(credentials, opts).first if has_capability?(:images)
+    end
+
+    def instance(credentials, opts)
+      instances(credentials, opts).first if has_capability?(:instances)
+    end
+
+    def storage_volume(credentials, opts)
+      storage_volumes(credentials, opts).first if has_capability?(:storage_volumes)
+    end
+
+    def storage_snapshot(credentials, opts)
+      storage_snapshots(credentials, opts).first if has_capability?(:storage_snapshots)
+    end
+
+    def bucket(credentials, opts = {})
+      #list of objects within bucket
+      buckets(credentials, opts).first if has_capability?(:buckets)
+    end
+
+    def blob(credentials, opts = {})
+      blobs(credentials, opts).first if has_capability?(:blobs)
+    end
+
+    def key(credentials, opts=nil)
+      keys(credentials, opts).first if has_capability?(:keys)
+    end
+
+    def firewall(credentials, opts={})
+      firewalls(credentials, opts).first if has_capability?(:firewalls)
+    end
+
+    MEMBER_SHOW_METHODS =
+      [ :realm, :image, :instance, :storage_volume, :bucket, :blob, :key, :firewall ]
+
+    def filter_on(collection, attribute, opts)
+      return collection if opts.nil?
+      return collection if opts[attribute].nil?
+      filter = opts[attribute]
+      if ( filter.is_a?( Array ) )
+        return collection.select{|e| filter.include?( e.send(attribute) ) }
+      else
+        return collection.select{|e| filter == e.send(attribute) }
+      end
+    end
+
+    def supported_collections
+      DEFAULT_COLLECTIONS
+    end
+
+    def has_collection?(collection)
+      supported_collections.include?(collection)
+    end
+
+    def catched_exceptions_list
+      { :error => [], :auth => [], :glob => [] }
+    end
+
+    def api_provider
+      Thread.current[:provider] || ENV['API_PROVIDER']
+    end
+
+    # Return an array of the providers statically configured
+    # in the driver's YAML file
+    def configured_providers
+      []
+    end
+  end
+
+end
diff --git a/server/lib/deltacloud/drivers/exceptions.rb b/server/lib/deltacloud/drivers/exceptions.rb
new file mode 100644
index 0000000..a89b05f
--- /dev/null
+++ b/server/lib/deltacloud/drivers/exceptions.rb
@@ -0,0 +1,191 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.  The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+
+module Deltacloud
+  module ExceptionHandler
+
+    class DeltacloudException < StandardError
+
+      attr_accessor :code, :name, :message, :backtrace, :request
+
+      def initialize(code, name, message, backtrace, request=nil)
+        @code, @name, @message = code, name, message
+        @backtrace = backtrace
+        @request = request
+        self
+      end
+
+    end
+
+    class AuthenticationFailure < DeltacloudException
+      def initialize(e, message=nil)
+        message ||= e.message
+        super(401, e.class.name, message, e.backtrace)
+      end
+    end
+
+    class UnknownMediaTypeError < DeltacloudException
+      def initialize(e, message=nil)
+        message ||= e.message
+        super(406, e.class.name, message, e.backtrace)
+      end
+    end
+
+    class MethodNotAllowed < DeltacloudException
+      def initialize(e, message=nil)
+        message ||= e.message
+        super(405, e.class.name, message, e.backtrace)
+      end
+    end
+
+    class ValidationFailure < DeltacloudException
+      def initialize(e, message=nil)
+        message ||= e.message
+        super(400, e.class.name, message, e.backtrace)
+      end
+    end
+
+    class BackendError < DeltacloudException
+      def initialize(e, message=nil)
+        message ||= e.message
+        super(500, e.class.name, message, e.backtrace, message)
+      end
+    end
+
+    class ProviderError < DeltacloudException
+      def initialize(e, message)
+        message ||= e.message
+        super(502, e.class.name, message, e.backtrace)
+      end
+    end
+
+    class ProviderTimeout < DeltacloudException
+      def initialize(e, message)
+        message ||= e.message
+        super(504, e.class.name, message, e.backtrace)
+      end
+    end
+
+    class NotImplemented < DeltacloudException
+      def initialize(e, message)
+        message ||= e.message
+        super(501, e.class.name, message, e.backtrace)
+      end
+    end
+
+    class ObjectNotFound < DeltacloudException
+      def initialize(e, message)
+        message ||= e.message
+        super(404, e.class.name, message, e.backtrace)
+      end
+    end
+
+    class NotSupported < DeltacloudException
+      def initialize(message)
+        super(501, self.class.name, message, self.backtrace)
+      end
+    end
+
+    class ExceptionDef
+      attr_accessor :status
+      attr_accessor :message
+      attr_reader   :conditions
+      attr_reader   :handler
+
+      def initialize(conditions, &block)
+        @conditions = conditions
+        instance_eval(&block) if block_given?
+      end
+
+      def status(code)
+        self.status = code
+      end
+
+      def message(message)
+        self.message = message
+      end
+
+      def exception(handler)
+        self.handler = handler
+      end
+
+      # Condition can be class or regexp
+      #
+      def match?(e)
+        @conditions.each do |c|
+          return true if c.class == Class && e.class == c
+          return true if c.class == Regexp && (e.class.name =~ c or e.message =~ c)
+        end
+        return false
+      end
+
+      def handler(e)
+        return @handler if @handler
+        case @status
+          when 401 then Deltacloud::ExceptionHandler::AuthenticationFailure.new(e, @message)
+          when 404 then Deltacloud::ExceptionHandler::ObjectNotFound.new(e, @message)
+          when 406 then Deltacloud::ExceptionHandler::UnknownMediaTypeError.new(e, @message)
+          when 405 then Deltacloud::ExceptionHandler::MethodNotAllowed.new(e, @message)
+          when 400 then Deltacloud::ExceptionHandler::ValidationFailure.new(e, @message)
+          when 500 then Deltacloud::ExceptionHandler::BackendError.new(e, @message)
+          when 501 then Deltacloud::ExceptionHandler::NotImplemented.new(e, @message)
+          when 502 then Deltacloud::ExceptionHandler::ProviderError.new(e, @message)
+          when 504 then Deltacloud::ExceptionHandler::ProviderTimeout.new(e, @message)
+        end
+      end
+
+    end
+
+    class Exceptions
+      attr_reader :exception_definitions
+
+      def initialize(&block)
+        @exception_definitions = []
+        instance_eval(&block) if block_given?
+        self
+      end
+
+      def on(*conditions, &block)
+        @exception_definitions << ExceptionDef::new(conditions, &block) if block_given?
+      end
+    end
+
+    def self.exceptions(&block)
+      @definitions = Exceptions.new(&block).exception_definitions if block_given?
+      @definitions
+    end
+
+    def safely(&block)
+      begin
+        block.call
+      rescue
+        report_method = $stderr.respond_to?(:err) ? :err : :puts
+        Deltacloud::ExceptionHandler::exceptions.each do |exdef|
+          if exdef.match?($!)
+            new_exception = exdef.handler($!)
+            m = new_exception.message.nil? ? $!.message : new_exception.message
+            $stderr.send(report_method, "#{[$!.class.to_s, m].join(':')}\n#{$!.backtrace[0..10].join("\n")}")
+            raise exdef.handler($!) unless new_exception.nil?
+          end
+        end
+        $stderr.send(report_method, "[NO HANDLED] #{[$!.class.to_s, $!.message].join(': ')}\n#{$!.backtrace.join("\n")}")
+        raise Deltacloud::ExceptionHandler::BackendError.new($!, "Unhandled exception or status code (#{$!.message})")
+      end
+    end
+
+  end
+
+end
-- 
1.7.10