You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@deltacloud.apache.org by mf...@apache.org on 2013/03/26 18:57:49 UTC

[03/30] git commit: Client: Complete rewrite of deltacloud-client

Client: Complete rewrite of deltacloud-client

- Now use Faraday HTTP lib
- Superb error reporting (based on Faraday middleware)
- Documentation
- Easy to read/fix bugs/add features
- Compatible with jRuby/MRI 2.0.0


Project: http://git-wip-us.apache.org/repos/asf/deltacloud/repo
Commit: http://git-wip-us.apache.org/repos/asf/deltacloud/commit/b6d3365c
Tree: http://git-wip-us.apache.org/repos/asf/deltacloud/tree/b6d3365c
Diff: http://git-wip-us.apache.org/repos/asf/deltacloud/diff/b6d3365c

Branch: refs/heads/master
Commit: b6d3365c1e594a5a898d40d262db80882ceb963d
Parents: 95e8662
Author: Michal Fojtik <mf...@redhat.com>
Authored: Thu Mar 7 13:51:26 2013 +0100
Committer: Michal fojtik <mf...@redhat.com>
Committed: Tue Mar 26 15:21:34 2013 +0100

----------------------------------------------------------------------
 client/.gitignore                                  |   21 +-
 client/Gemfile                                     |   12 +
 client/README                                      |  127 ----
 client/README.md                                   |   73 +++
 client/Rakefile                                    |   92 +++-
 client/deltacloud-client.gemspec                   |   13 +-
 client/lib/base_object.rb                          |  386 ------------
 client/lib/client_bucket_methods.rb                |   69 --
 client/lib/deltacloud.rb                           |  486 ---------------
 client/lib/deltacloud/client.rb                    |   78 +++
 client/lib/deltacloud/client/base_error.rb         |   84 +++
 client/lib/deltacloud/client/connection.rb         |  135 ++++
 .../lib/deltacloud/client/helpers/model_helper.rb  |   69 ++
 .../deltacloud/client/helpers/property_helper.rb   |  103 +++
 client/lib/deltacloud/client/helpers/xml_helper.rb |   33 +
 client/lib/deltacloud/client/methods.rb            |   29 +
 client/lib/deltacloud/client/methods/address.rb    |   68 ++
 client/lib/deltacloud/client/methods/api.rb        |   96 +++
 .../client/methods/backward_compatiblity.rb        |   72 +++
 client/lib/deltacloud/client/methods/blob.rb       |   91 +++
 client/lib/deltacloud/client/methods/bucket.rb     |   56 ++
 client/lib/deltacloud/client/methods/common.rb     |   46 ++
 client/lib/deltacloud/client/methods/driver.rb     |   54 ++
 client/lib/deltacloud/client/methods/firewall.rb   |   66 ++
 .../deltacloud/client/methods/hardware_profile.rb  |   42 ++
 client/lib/deltacloud/client/methods/image.rb      |   62 ++
 client/lib/deltacloud/client/methods/instance.rb   |  140 +++++
 .../deltacloud/client/methods/instance_state.rb    |   41 ++
 client/lib/deltacloud/client/methods/key.rb        |   59 ++
 client/lib/deltacloud/client/methods/realm.rb      |   43 ++
 .../deltacloud/client/methods/storage_snapshot.rb  |   62 ++
 .../deltacloud/client/methods/storage_volume.rb    |   95 +++
 client/lib/deltacloud/client/models.rb             |   30 +
 client/lib/deltacloud/client/models/address.rb     |   57 ++
 client/lib/deltacloud/client/models/base.rb        |  151 +++++
 client/lib/deltacloud/client/models/blob.rb        |   56 ++
 client/lib/deltacloud/client/models/bucket.rb      |   65 ++
 client/lib/deltacloud/client/models/driver.rb      |   87 +++
 client/lib/deltacloud/client/models/firewall.rb    |   70 ++
 .../deltacloud/client/models/hardware_profile.rb   |   68 ++
 client/lib/deltacloud/client/models/image.rb       |   60 ++
 client/lib/deltacloud/client/models/instance.rb    |  122 ++++
 .../deltacloud/client/models/instance_address.rb   |   35 +
 .../lib/deltacloud/client/models/instance_state.rb |   52 ++
 client/lib/deltacloud/client/models/key.rb         |   52 ++
 client/lib/deltacloud/client/models/realm.rb       |   29 +
 .../deltacloud/client/models/storage_snapshot.rb   |   54 ++
 .../lib/deltacloud/client/models/storage_volume.rb |   96 +++
 client/lib/deltacloud/core_ext.rb                  |   19 +
 client/lib/deltacloud/core_ext/element.rb          |   32 +
 client/lib/deltacloud/core_ext/fixnum.rb           |   30 +
 client/lib/deltacloud/core_ext/nil.rb              |   22 +
 client/lib/deltacloud/core_ext/string.rb           |   49 ++
 client/lib/deltacloud/error_response.rb            |   92 +++
 client/lib/documentation.rb                        |   59 --
 client/lib/errors.rb                               |  140 -----
 client/lib/hwp_properties.rb                       |   61 --
 client/lib/instance_state.rb                       |   44 --
 client/lib/string.rb                               |   59 --
 client/support/method_test_template.erb            |   53 ++
 client/support/methods_template.erb                |   54 ++
 client/support/model_template.erb                  |   45 ++
 62 files changed, 3304 insertions(+), 1442 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/deltacloud/blob/b6d3365c/client/.gitignore
----------------------------------------------------------------------
diff --git a/client/.gitignore b/client/.gitignore
index b4b07a3..3a0a93e 100644
--- a/client/.gitignore
+++ b/client/.gitignore
@@ -1,4 +1,19 @@
-spec_report.html
-*.swp
-tmp/
 *.gem
+*.rbc
+.bundle
+.config
+coverage
+InstalledFiles
+lib/bundler/man
+pkg
+rdoc
+spec/reports
+test/tmp
+test/version_tmp
+tmp
+*.lock
+
+# YARD artifacts
+.yardoc
+_yardoc
+doc/

http://git-wip-us.apache.org/repos/asf/deltacloud/blob/b6d3365c/client/Gemfile
----------------------------------------------------------------------
diff --git a/client/Gemfile b/client/Gemfile
new file mode 100644
index 0000000..4c6d274
--- /dev/null
+++ b/client/Gemfile
@@ -0,0 +1,12 @@
+source 'https://rubygems.org'
+
+gem 'faraday'
+gem 'nokogiri'
+
+group :development do
+  gem 'rake'
+  gem 'minitest'
+  gem 'vcr'
+  gem 'pry'
+  gem 'simplecov', :require => false
+end

http://git-wip-us.apache.org/repos/asf/deltacloud/blob/b6d3365c/client/README
----------------------------------------------------------------------
diff --git a/client/README b/client/README
deleted file mode 100644
index 5324062..0000000
--- a/client/README
+++ /dev/null
@@ -1,127 +0,0 @@
-# Deltacloud Client (Ruby)
-
-The Deltacloud project includes a Ruby client.  Other language-bindings
-are possible and will be supported soon.  The client aims to insulate
-users from having to deal with HTTP and REST directly.
-
-Each resource type has an associated model to ease usage.  Where
-resource reference other resources, natural navigation across the
-object model is possible.
-
-For example
-
-    puts instance.image.name
-    puts instance.hardware_profile.architecture
-
-## Basics
-
-To use the client, you must require `deltacloud`.
-
-    require 'deltacloud'
-
-## Connecting to a Deltacloud provider
-
-    require 'deltacloud'
-
-    api_url      = 'http://localhost:3001/api'
-    api_name     = 'mockuser'
-    api_password = 'mockpassword'
-
-    client = DeltaCloud.new( api_name, api_password, api_url )
-
-    # work with client here
-
-In addition to creating a client, operations may occur within a block
-included on the initialization
-
-    DeltaCloud.new( api_name, api_password, api_url ) do |client|
-      # work with client here
-    end
-
-In the event of a failure, any underlying HTTP transport exceptions
-will be thrown all the way out to the caller.
-
-## Listing realms
-
-You may retrieve a complete list of realms available to you
-
-    realms = client.realms
-
-You may retrieve a specific realm by its identifier
-
-    realm = client.realm( 'us' )
-
-## Listing hardware profiles
-
-You may retrieve a complete list of hardware profiles available for launching
-machines
-
-    hwp = client.hardware_profiles
-
-You may filter hardware profiles by architecture
-
-    flavors = client.hardware_profiles( :architecture=>'x86_64' )
-
-You may retrieve a specific hardware profile by its identifier
-
-    flavor = client.hardware_profile( 'm1-small' )
-
-## Listing images
-
-You may retrieve a complete list of images
-
-    images = client.images
-
-You may retrieve a list of images owned by the currently authenticated
-user
-
-    images = client.images( :owner_id=>:self )
-
-You may retrieve a list of images visible to you but owned by a specific
-user
-
-    images = client.images( :owner_id=>'daryll' )
-
-You may retrieve a specific image by its identifier
-
-    image = client.image( 'ami-8675309' )
-
-## Listing instances
-
-You may retrieve a list of all instances visible to you
-
-    instances = client.instances
-
-You may retrieve a specific instance by its identifier
-
-    instance = client.instance( 'i-90125' )
-
-## Launching instances
-
-An instance may be launched using just an image identifier
-
-    image = client.image( 'ami-8675309' )
-    instance = client.create_instance( image.id )
-
-Optionally, a flavor or realm may be specified
-
-    instance = client.create_instance( image.id, :flavor=>'m1-small', :realm=>'us' )
-
-## Manipulating instances
-
-Given an instance, depending on its state, various actions _may_ be available.
-
-To determine what's available, the `instance#actions` method may be used.
-
-    instance.actions # [ 'reboot', 'stop' ]
-
-For a valid action, the method matching the action with an exclamation point may be called.
-
-    instance.reboot!
-
-Upon invoking an action, the instance will refresh its contents, in case the state has changed.
-To determine later if the state has changed again, the instance must be refetched using
-the `client.instance(...)` method.
-
-
-

http://git-wip-us.apache.org/repos/asf/deltacloud/blob/b6d3365c/client/README.md
----------------------------------------------------------------------
diff --git a/client/README.md b/client/README.md
new file mode 100644
index 0000000..fcd7595
--- /dev/null
+++ b/client/README.md
@@ -0,0 +1,73 @@
+# deltacloud-client
+
+The Deltacloud project includes a Ruby client.  Other language-bindings
+are possible and will be supported soon.  The client aims to insulate
+users from having to deal with HTTP and REST directly.
+
+Each resource type has an associated model to ease usage.  Where
+resource reference other resources, natural navigation across the
+object model is possible.
+
+This is a Ruby client library for the [Deltacloud API](http://deltacloud.apache.org).
+
+## Usage
+
+```ruby
+require 'deltacloud/client'
+
+API_URL = "http://localhost:3001/api" # Deltacloud API endpoint
+
+# Simple use-cases
+client = Deltacloud::Client(API_URL, 'mockuser', 'mockpassword')
+
+pp client.instances           # List all instances
+pp client.instance('i-12345') # Get one instance
+
+inst = client.create_instance 'ami-1234', :hwp_id => 'm1.small' # Create instance
+
+inst.reboot!  # Reboot instance
+
+# Advanced usage
+
+# Deltacloud API supports changing driver per-request:
+
+client.use(:ec2, 'API_KEY', 'API_SECRET').instances # List EC2 instances
+client.use(:openstack, 'admin@tenant', 'password', KEYSTONE_URL).instances # List Openstack instances
+
+```
+# Want help?
+
+## Adding new Deltacloud collection to client
+
+```
+$ rake generate[YOUR_COLLECTION] # eg. 'storage_snapshot'
+# Hit Enter 2x
+```
+
+- Edit `lib/deltacloud/client/methods/YOUR_COLLECTION.rb` and add all
+  methods for manipulating your collection. The list/show methods
+  should already be generated for you, but double-check them.
+
+- Edit `lib/deltacloud/client/model/YOUR_COLLECTION.rb` and add model
+  methods. Model methods should really be just a syntax sugar and exercise
+  the *Deltacloud::Client::Methods* methods.
+  The purpose of *model* class life is to deserialize XML body received
+  from Deltacloud API to a Ruby class.
+
+## Debugging a nasty bug?
+
+- You can easily debug deltacloud-client using powerful **pry**.
+
+  - `gem install deltacloud-core`
+  - optional: `rbenv rehash` ;-)
+  - `deltacloudd -i mock -p 3002`
+  - `rake console`
+
+Console require **pry** gem installed. If you are not using this awesome
+gem, you can fix it by `gem install pry`.
+
+# License
+
+Apache License
+Version 2.0, January 2004
+http://www.apache.org/licenses/

http://git-wip-us.apache.org/repos/asf/deltacloud/blob/b6d3365c/client/Rakefile
----------------------------------------------------------------------
diff --git a/client/Rakefile b/client/Rakefile
index 4248ca5..17d0cdd 100644
--- a/client/Rakefile
+++ b/client/Rakefile
@@ -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,12 @@
 # License for the specific language governing permissions and limitations
 # under the License.
 
+require 'rubygems'
 require 'rubygems/package_task'
+require 'rake'
 require 'rake/testtask'
 
-load 'deltacloud-client.gemspec'
+require 'pry' rescue LoadError
 
 spec = Gem::Specification.load('deltacloud-client.gemspec')
 
@@ -25,15 +26,98 @@ Gem::PackageTask.new(spec) do |pkg|
   pkg.need_tar = true
 end
 
-desc "Re-install the deltacloud-client gem"
+desc "Re-install the deltacloud-client gem (used for development)"
 task :reinstall do
   puts %x{gem uninstall deltacloud-client --all -I -x}
   puts %x{gem build deltacloud-client.gemspec}
   puts %x{gem install deltacloud-client-*.gem --local}
 end
 
+desc 'Generate model/methods files for collection.'
+task :generate, :name do |t, args|
+  require 'erb'
+  require_relative './lib/deltacloud/core_ext'
+  model_tpl = ERB.new(File.read('support/model_template.erb'))
+  methods_tpl = ERB.new(File.read('support/methods_template.erb'))
+  name = args[:name]
+  model_file = "lib/deltacloud/client/models/#{name}.rb"
+  methods_file = "lib/deltacloud/client/methods/#{name}.rb"
+  puts model_body = model_tpl.result(binding)
+  print "Save model to '#{model_file}'? [Y/n]"
+  answer = $stdin.gets.chomp
+  if answer.empty? or answer == 'Y'
+    File.open(model_file, 'w') { |f|
+      f.write(model_body)
+    }
+    File.open('lib/deltacloud/client/models.rb', 'a') { |f|
+      f.puts "require_relative './models/#{name}'"
+    }
+  end
+  puts methods_body = methods_tpl.result(binding)
+  print "Save methods to '#{methods_file}'? [Y/n]"
+  answer = $stdin.gets.chomp
+  if answer.empty? or answer == 'Y'
+    File.open(methods_file, 'w') { |f|
+      f.write(methods_body)
+    }
+    File.open('lib/deltacloud/client/methods.rb', 'a') { |f|
+      f.puts "require_relative './methods/#{name}'"
+    }
+  end
+  puts
+  puts "Don't forget to add this line to 'lib/deltacloud/client/connection.rb':"
+  puts
+  puts "include Deltacloud::Client::Methods::#{name.to_s.camelize}"
+  puts
+end
+
+desc 'Generate method test file'
+task :test_generate, :name do |t, args|
+  require 'erb'
+  require_relative './lib/deltacloud/core_ext'
+  method_tpl = ERB.new(File.read('support/method_test_template.erb'))
+  name = args[:name]
+  methods_file = "tests/methods/#{name}_test.rb"
+  puts method_body = method_tpl.result(binding)
+  print "Save method test to '#{methods_file}'? [Y/n]"
+  answer = $stdin.gets.chomp
+  if answer.empty? or answer == 'Y'
+    File.open(methods_file, 'w') { |f|
+      f.write(method_body)
+    }
+  end
+end
+
+
+desc "Open console with client connected to #{ENV['API_URL'] || 'localhost:3002/api'}"
+task :console do
+  unless binding.respond_to? :pry
+    puts 'To open a console, you need to have "pry" installed (gem install pry)'
+    exit(1)
+  end
+  require_relative './lib/deltacloud/client'
+  client = Deltacloud::Client(
+    ENV['API_URL'] || 'http://localhost:3002/api',
+    ENV['API_USER'] || 'mockuser',
+    ENV['API_PASSWORD'] || 'mockpassword'
+  )
+  binding.pry
+end
+
 Rake::TestTask.new(:test) do |t|
   t.test_files = FileList[
-    'tests/*test.rb',                     # EC2 frontend internal API tests
+    'tests/*/*_test.rb'
   ]
 end
+
+desc "Execute test against live Deltacloud API"
+task :test_live do
+  ENV['NO_VCR'] = 'true'
+  Rake::Task[:test].invoke
+end
+
+desc "Generate test coverage report"
+task :coverage do
+  ENV['COVERAGE'] = 'true'
+  Rake::Task[:test].invoke
+end

http://git-wip-us.apache.org/repos/asf/deltacloud/blob/b6d3365c/client/deltacloud-client.gemspec
----------------------------------------------------------------------
diff --git a/client/deltacloud-client.gemspec b/client/deltacloud-client.gemspec
index 1b14f43..874e2f0 100644
--- a/client/deltacloud-client.gemspec
+++ b/client/deltacloud-client.gemspec
@@ -20,14 +20,19 @@ Gem::Specification.new do |s|
   s.homepage = "http://www.deltacloud.org"
   s.email = 'dev@deltacloud.apache.org'
   s.name = 'deltacloud-client'
-  s.description = %q{Deltacloud REST Client for API}
+  s.description = %q{A REST client for the Deltacloud API}
   s.version = Deltacloud::API_VERSION
   s.summary = %q{Deltacloud REST Client}
   s.files = Dir['Rakefile', 'lib/**/*.rb']
   s.test_files= Dir.glob("tests/**/**")
-  s.extra_rdoc_files = Dir["LICENSE", "NOTICE", "DISCLAIMER"]
+  s.extra_rdoc_files = Dir["LICENSE", "NOTICE", "README.md"]
 
-  s.add_dependency('rest-client', '>= 1.6.1')
+  s.add_dependency('faraday', '>=0.8.6')
   s.add_dependency('nokogiri', '>= 1.4.3')
-  s.add_development_dependency('rspec', '>= 2.0.0')
+
+  s.add_development_dependency('minitest')
+  s.add_development_dependency('simplecov')
+  s.add_development_dependency('vcr')
+  s.add_development_dependency('rake')
+  s.add_development_dependency('pry')
 end

http://git-wip-us.apache.org/repos/asf/deltacloud/blob/b6d3365c/client/lib/base_object.rb
----------------------------------------------------------------------
diff --git a/client/lib/base_object.rb b/client/lib/base_object.rb
deleted file mode 100644
index 6a28d5a..0000000
--- a/client/lib/base_object.rb
+++ /dev/null
@@ -1,386 +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_relative './string.rb'
-
-module DeltaCloud
-
-    class BaseObjectParamError < Exception; end
-    class NoHandlerForMethod < Exception; end
-
-    # BaseObject model basically provide the basic operation around
-    # REST model, like defining a links between different objects,
-    # element with text values, or collection of these elements
-    class BaseObject
-      attr_reader :id, :url, :client, :base_name
-      attr_reader :objects
-
-      alias :uri :url
-
-      # For initializing new object you require to set
-      # id, url, client and name attribute.
-      def initialize(opts={}, &block)
-        @id, @url, @client, @base_name = opts[:id], opts[:url], opts[:client], opts[:name]
-        @objects = []
-        raise BaseObjectParamError if @id.nil? or @url.nil? or @client.nil? or @base_name.nil?
-        yield self if block_given?
-      end
-
-      # This method add link to another object in REST model
-      # XML syntax: <link rel="destroy" href="http://localhost/api/resource" method="post"/>
-      def add_link!(object_name, id)
-        @objects << {
-          :type => :link,
-          :method_name => object_name.sanitize,
-          :id => id
-        }
-        @objects << {
-          :type => :text,
-          :method_name => "#{object_name.sanitize}_id",
-          :value => id
-        }
-      end
-
-      # Method add property for hardware profile
-      def add_hwp_property!(name, property, type)
-        hwp_property=case type
-          when :float then DeltaCloud::HWP::FloatProperty.new(property, name)
-          when :integer then DeltaCloud::HWP::Property.new(property, name)
-        end
-        @objects << {
-          :type => :property,
-          :method_name => name.sanitize,
-          :property => hwp_property
-        }
-      end
-
-      # This method define text object in REST model
-      # XML syntax: <name>Instance 1</name>
-      def add_text!(object_name, value)
-        @objects << {
-          :type => :text,
-          :method_name => object_name.sanitize,
-          :value => value
-        }
-      end
-
-      def add_authentication!(auth_type, values=[])
-        value = { :key => (values/'login/keyname').text.strip } if auth_type == 'key'
-        if auth_type == 'password'
-          value = {
-            :username => (values/'login/username').text.strip,
-            :username => (values/'login/password').text.strip
-          }
-        end
-        @objects << {
-          :type => :collection,
-          :method_name => 'authentication',
-          :values => value
-        }
-      end
-
-      def add_provider!(provider_id, entrypoints)
-        @providers ||= []
-        @providers << {
-          provider_id.intern => entrypoints.map { |e| { :kind => e[:kind], :url => e.text } }
-        }
-        @objects << {
-          :type => :collection,
-          :method_name => 'providers',
-          :values => @providers
-        }
-      end
-
-
-      # This method define collection of text elements inside REST model
-      # XML syntax: <addresses>
-      #               <address>127.0.0.1</address>
-      #               <address>127.0.0.2</address>
-      #             </addresses>
-      def add_addresses!(collection_name, values=[])
-        @objects << {
-          :type => :collection,
-          :method_name => collection_name.sanitize,
-          :values => values.collect { |v| { :address => v.text.strip, :type => v[:type] }}
-        }
-      end
-
-      # This method define collection of text elements inside REST model
-      # XML syntax: <addresses>
-      #               <address>127.0.0.1</address>
-      #               <address>127.0.0.2</address>
-      #             </addresses>
-      def add_collection!(collection_name, values=[])
-        @objects << {
-          :type => :collection,
-          :method_name => collection_name.sanitize,
-          :values => values
-        }
-      end
-
-      # Basic method hander. This define a way how value from property
-      # will be returned
-      def method_handler(m, args=[])
-        case m[:type]
-          when :link then return @client.send(m[:method_name].singularize, m[:id])
-          when :text then return m[:value]
-          when :property then return m[:property]
-          when :collection then return m[:values]
-          when :list then return m[:value].join(", ")
-          else raise NoHandlerForMethod
-        end
-      end
-
-      def method_missing(method_name, *args)
-        # First of all search throught array for method name
-        m = search_for_method(method_name)
-        if m.nil?
-          if method_name == :"valid_provider?"
-            return providers.any? { |p| p.keys.include? args.first.to_sym }
-          end
-          if method_name == :"valid_provider_url?"
-            return providers.any? { |p| !p.find { |k, v| v.find { |u| u[:url] == args.first } }.nil? }
-          end
-          super
-        else
-          # Call appropriate handler for method
-          method_handler(m, args)
-        end
-      end
-
-      # This method adds blobs to the blob_list property
-      # of a bucket
-      def add_blob!(blob_name)
-        if @blob_list.nil?
-          @blob_list = [blob_name]
-          @objects << {
-            :type => :list,
-            :method_name => "blob_list",
-            :value => @blob_list
-          }
-        else
-          @blob_list << blob_name
-          current = search_for_method('blob_list')
-          current[:value] = @blob_list
-        end
-      end
-
-      private
-
-      def search_for_method(name)
-        @objects.detect { |o| o[:method_name] == "#{name}" }
-      end
-
-    end
-
-    class ActionObject < BaseObject
-
-      def initialize(opts={}, &block)
-        super(opts)
-        @action_urls = opts[:action_urls] || []
-        @actions = []
-      end
-
-      # This trigger is called right after action.
-      # This method does nothing inside ActionObject
-      # but it can be redifined and used in meta-programming
-      def action_trigger(action)
-      end
-
-      def add_action_link!(id, link)
-        m = {
-          :type => :action_link,
-          :method_name => "#{link['rel'].sanitize}!",
-          :id => id,
-          :href => link['href'],
-          :rel => link['rel'].sanitize,
-          :method => link['method'].sanitize
-        }
-        @objects << m
-        @actions << [m[:rel], m[:href]]
-        @action_urls << m[:href]
-      end
-
-      def actions
-        @objects.inject([]) do |result, item|
-          result << [item[:rel], item[:href]] if item[:type].eql?(:action_link)
-          result
-        end
-      end
-
-      def action_urls
-        actions.collect { |a| a.last }
-      end
-
-      alias :base_method_handler :method_handler
-
-      # First call BaseObject method handler,
-      # then, if not method found try ActionObject handler
-      def method_handler(m, args=[])
-        begin
-          base_method_handler(m, args)
-        rescue NoHandlerForMethod
-          case m[:type]
-            when :action_link then do_action(m, args)
-            else raise NoHandlerForMethod
-          end
-        end
-      end
-
-      alias :original_method_missing :method_missing
-
-      def method_missing(name, *args)
-        if name.to_s =~ /^has_(\w+)\?$/
-          return actions.any? { |a| a[0] == $1 }
-        end
-        original_method_missing(name, args)
-      end
-
-      private
-
-      def do_action(m, args)
-        args = args.first || {}
-        method = m[:method].to_sym
-        @client.request(method,
-                        m[:href],
-                        method == :get ? args : {},
-                        method == :get ? {} : args)
-        action_trigger(m[:rel])
-      end
-
-    end
-
-    class StatefulObject < ActionObject
-      attr_reader :state
-
-      def initialize(opts={}, &block)
-        super(opts)
-        @state = opts[:initial_state] || ''
-        add_default_states!
-      end
-
-      def add_default_states!
-        @objects << {
-          :method_name => 'stopped?',
-          :type => :state,
-          :state => 'STOPPED'
-        }
-        @objects << {
-          :method_name => 'running?',
-          :type => :state,
-          :state => 'RUNNING'
-        }
-        @objects << {
-          :method_name => 'pending?',
-          :type => :state,
-          :state => 'PENDING'
-        }
-        @objects << {
-          :method_name => 'shutting_down?',
-          :type => :state,
-          :state => 'SHUTTING_DOWN'
-        }
-      end
-
-      def action_trigger(action)
-        # Refresh object state after action unless the object was destroyed
-        return if action.to_s == "destroy"
-        @new_state_object = @client.send(self.base_name, self.id)
-        @state = @new_state_object.state
-        self.update_actions!
-      end
-
-      def add_run_action!(id, link)
-        @objects << {
-          :method_name => 'run',
-          :type => :run,
-          :url => link,
-        }
-      end
-
-      alias :action_method_handler :method_handler
-
-      def method_handler(m, args=[])
-        begin
-          action_method_handler(m, args)
-        rescue NoHandlerForMethod
-          case m[:type]
-            when :state then evaluate_state(m[:state], @state)
-            when :run then run_command(m[:url][:href], args)
-            else raise NoHandlerForMethod
-          end
-        end
-      end
-
-#      private
-
-      def run_command(instance_url, args)
-        credentials = args[1]
-        params = {
-          :cmd => args[0],
-          :private_key => credentials[:pem] ? File.read(credentials[:pem]) : nil,
-        }
-        params.merge!({
-          :username => credentials[:username],
-          :password => credentials[:password]
-        }) if credentials[:username] and credentials[:password]
-        @client.request(:post, instance_url, {}, params) do |response|
-          output = Nokogiri::XML(response)
-          (output/'/instance/output').first.text
-        end
-      end
-
-      def evaluate_state(method_state, current_state)
-        method_state.eql?(current_state)
-      end
-
-      def action_objects
-        @objects.select { |o| o[:type] == :action_link }
-      end
-
-      def update_actions!
-        new_actions = @new_state_object.action_objects
-        @objects.reject! { |o| o[:type] == :action_link }
-        @objects = (@objects + new_actions)
-      end
-
-    end
-
-    def self.add_class(name, parent=:base)
-      parent = parent.to_s
-      parent_class = "#{parent.classify}Object"
-      @defined_classes ||= []
-      class_name = "#{parent.classify}::#{name.classify}"
-      unless @defined_classes.include?(class_name)
-        DeltaCloud::API.class_eval("class #{class_name} < DeltaCloud::#{parent_class}; end")
-        @defined_classes << class_name
-      end
-
-      DeltaCloud::API.const_get(parent.classify).const_get(name.classify)
-    end
-
-    def self.guess_model_type(response)
-      response = Nokogiri::XML(response.to_s)
-      return :action if ((response/'//actions').length >= 1) and ((response/'//state').length == 0)
-      return :stateful if ((response/'//actions').length >= 1) and ((response/'//state').length >= 1)
-      return :base
-    end
-
-    class API
-      class Action; end
-      class Base; end
-      class Stateful; end
-    end
-end

http://git-wip-us.apache.org/repos/asf/deltacloud/blob/b6d3365c/client/lib/client_bucket_methods.rb
----------------------------------------------------------------------
diff --git a/client/lib/client_bucket_methods.rb b/client/lib/client_bucket_methods.rb
deleted file mode 100644
index a3dfda0..0000000
--- a/client/lib/client_bucket_methods.rb
+++ /dev/null
@@ -1,69 +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 ClientBucketMethods
-
-  def create_bucket(params)
-    obj = nil
-    request(:post, "#{api_uri.to_s}/buckets", {:name => params['id'],:location=>params['bucket_location'] }) do |response|
-      handle_backend_error(response) if response.code!=201
-      obj = base_object(:bucket, response)
-    end
-  end
-
-  def destroy_bucket(params)
-    #actually response here is 204 - no content - so nothing returned to client?
-    request(:delete, "#{api_uri.to_s}/buckets/#{params['id']}") do |response|
-      handle_backend_error(response) if response.code!=204
-      nil if response.code == 204
-    end
-  end
-
-  def create_blob(params)
-    blob = nil
-    resource = RestClient::Resource.new("#{api_uri.to_s}/buckets/#{params['bucket']}", :open_timeout => 10, :timeout => 45)
-    headers = default_headers.merge(extended_headers)
-    unless params['metadata'].nil?
-      metadata_headers = {}
-      params['metadata'].each   do |k,v|
-        metadata_headers["X-Deltacloud-Blobmeta-#{k}"] = v
-      end
-      headers = headers.merge(metadata_headers)
-    end
-    resource.send(:post, {:blob_data => File.new(params['file_path'], 'rb'), :blob_id => params[:id]}, headers) do |response, request, block|
-      handle_backend_error(response) if response.code.eql?(500)
-      blob = base_object(:blob, response)
-      yield blob if block_given?
-    end
-    return blob
-  end
-
-  def destroy_blob(params)
-    request(:delete, "#{api_uri.to_s}/buckets/#{params['bucket']}/#{params[:id]}") do |response|
-      handle_backend_error(response) if response.code!=204
-      nil if response.code == 204
-    end
-  end
-
-  #RestClient doesn't do streaming 'get' yet - we already opened a pull request on this see
-  #https://github.com/archiloque/rest-client/issues/closed#issue/62 - apparently its going to
-  #be in the next version - unknown when. For now get full response. FIXME
-  def blob_data(params)
-    request(:get, "#{api_uri.to_s}/buckets/#{params['bucket']}/#{params[:id]}/content") do |response|
-      response
-    end
-  end
-
-end

http://git-wip-us.apache.org/repos/asf/deltacloud/blob/b6d3365c/client/lib/deltacloud.rb
----------------------------------------------------------------------
diff --git a/client/lib/deltacloud.rb b/client/lib/deltacloud.rb
deleted file mode 100644
index a58c280..0000000
--- a/client/lib/deltacloud.rb
+++ /dev/null
@@ -1,486 +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 'nokogiri'
-require 'rest_client'
-require 'base64'
-require 'logger'
-require 'require_relative' if RUBY_VERSION =~ /^1\.8/
-require_relative './hwp_properties.rb'
-require_relative './instance_state.rb'
-require_relative './documentation.rb'
-require_relative './base_object.rb'
-require_relative './errors.rb'
-require_relative './client_bucket_methods.rb'
-
-module DeltaCloud
-
-  # Get a new API client instance
-  #
-  # @param [String, user_name] API user name
-  # @param [String, password] API password
-  # @param [String, url] API URL (eg. http://localhost:3001/api)
-  # @return [DeltaCloud::API]
-  #def self.new(user_name, password, api_url, opts={}, &block)
-  #  opts ||= {}
-  #  API.new(user_name, password, api_url, opts, &block)
-  #end
-
-  def self.new(user_name, password, api_url, &block)
-    API.new(user_name, password, api_url, &block)
-  end
-
-  # Check given credentials if their are valid against
-  # backend cloud provider
-  #
-  # @param [String, user_name] API user name
-  # @param [String, password] API password
-  # @param [String, user_name] API URL (eg. http://localhost:3001/api)
-  # @return [true|false]
-  def self.valid_credentials?(user_name, password, api_url, opts={})
-    api=API.new(user_name, password, api_url, opts)
-    result = false
-    api.request(:get, '', :force_auth => '1') do |response|
-      result = true if response.code.eql?(200)
-    end
-    return result
-  end
-
-  # Return a API driver for specified URL
-  #
-  # @param [String, url] API URL (eg. http://localhost:3001/api)
-  def self.driver_name(url)
-    API.new(nil, nil, url).driver_name
-  end
-
-  class API
-    attr_reader :api_uri, :driver_name, :api_version, :features, :entry_points
-    attr_reader :api_driver, :api_provider
-
-    def initialize(user_name, password, api_url, opts={}, &block)
-      opts[:version] = true
-      @api_driver, @api_provider = opts[:driver], opts[:provider]
-      @username, @password = opts[:username] || user_name, opts[:password] || password
-      @api_uri = URI.parse(api_url)
-      @features, @entry_points = {}, {}
-      @verbose = opts[:verbose] || false
-      discover_entry_points
-      if entry_points.include?(:buckets)
-        extend(ClientBucketMethods)
-      end
-      yield self if block_given?
-    end
-
-    # This method can be used to switch back-end cloud
-    # for API instance using HTTP headers.
-    # Options must include:
-    # {
-    #   :driver => 'rhevm|ec2|gogrid|...',
-    #   :username => 'API key for backend',
-    #   :password => 'API secret key for backend',
-    # }
-    # Optionally you can pass also :provider option to change
-    # provider entry-point
-    #
-    # Example usage:
-    # client = Deltacloud::new('url', 'username', 'password')
-    # ...
-    # client.with_config(:driver => 'ec2', :username => '', :password => '') do |ec2|
-    #   ec2.realms
-    # end
-    #
-    # Note: After this block finish client instance will be set back to default
-    # state
-    #
-    # @param [Hash, opts] New provider configuration
-    def with_config(opts, &block)
-      api_instance = self.dup
-      api_instance.use_driver(opts[:driver],
-                             :username => opts[:username],
-                             :password => opts[:password],
-                             :provider => opts[:provider])
-      yield api_instance if block_given?
-      api_instance
-    end
-
-    def connect(&block)
-      yield self
-    end
-
-    # Return API hostname
-    def api_host; @api_uri.host ; end
-
-    # Return API port
-    def api_port; @api_uri.port ; end
-
-    # Return API path
-    def api_path; @api_uri.path ; end
-
-    # Define methods based on 'rel' attribute in entry point
-    # Two methods are declared: 'images' and 'image'
-    def declare_entry_points_methods(entry_points)
-      API.instance_eval do
-        entry_points.keys.select {|k| [:instance_states].include?(k)==false }.each do |model|
-
-          define_method model do |*args|
-            request(:get, entry_points[model], args.first) do |response|
-              base_object_collection(model, response)
-            end
-          end
-
-          define_method :"#{model.to_s.singularize}" do |*args|
-            request(:get, "#{entry_points[model]}/#{args[0]}") do |response|
-              base_object(model, response)
-            end
-          end
-
-          define_method :"fetch_#{model.to_s.singularize}" do |url|
-            url =~ /\/#{model}\/(.*)$/
-            self.send(model.to_s.singularize.to_sym, $1)
-          end
-
-      end
-
-      #define methods for blobs:
-      if(entry_points.include?(:buckets))
-        define_method :"blob" do |*args|
-            bucket = args[0]["bucket"]
-            blob = args[0][:id]
-            request(:get, "#{entry_points[:buckets]}/#{bucket}/#{blob}") do |response|
-              base_object("blob", response)
-            end
-        end
-      end
-
-      end
-    end
-
-    def base_object_collection(model, response)
-      Nokogiri::XML(response).xpath("#{model}/#{model.to_s.singularize}").collect do |item|
-        base_object(model, item.to_s)
-      end
-    end
-
-    # Add default attributes [id and href] to class
-    def base_object(model, response)
-      c = DeltaCloud.add_class("#{model}", DeltaCloud::guess_model_type(response))
-      xml_to_class(c, Nokogiri::XML(response).xpath("#{model.to_s.singularize}").first)
-    end
-
-    # Convert XML response to defined Ruby Class
-    def xml_to_class(base_object, item)
-
-      return nil unless item
-
-      params = {
-          :id => item['id'],
-          :url => item['href'],
-          :name => item.name,
-          :client => self
-      }
-      params.merge!({ :initial_state => (item/'state').text.sanitize }) if (item/'state').length > 0
-
-      obj = base_object.new(params)
-      # Traverse across XML document and deal with elements
-      item.xpath('./*').each do |attribute|
-        # Do a link for elements which are links to other REST models
-        if self.entry_points.keys.include?(:"#{attribute.name}s")
-          obj.add_link!(attribute.name, attribute['id']) && next unless (attribute.name == 'bucket' && item.name == 'blob')
-        end
-
-        # Do a HWP property for hardware profile properties
-        if attribute.name == 'property'
-          if attribute['value'] =~ /^(\d+)\.(\d+)$/
-            obj.add_hwp_property!(attribute['name'], attribute, :float) && next
-          else
-            obj.add_hwp_property!(attribute['name'], attribute, :integer) && next
-          end
-        end
-
-        # If there are actions, add they to ActionObject/StateFullObject
-        if attribute.name == 'actions'
-          (attribute/'link').each do |link|
-            (obj.add_run_action!(item['id'], link) && next) if link[:rel] == 'run'
-            obj.add_action_link!(item['id'], link)
-          end && next
-        end
-
-        if attribute.name == 'mount'
-          obj.add_link!("instance", (attribute/"./instance/@id").first.value)
-          obj.add_text!("device", (attribute/"./device/@name").first.value)
-          next
-        end
-
-        #deal with blob metadata
-        if (attribute.name == 'user_metadata')
-          meta = {}
-          attribute.children.select {|x| x.name=="entry" }.each  do |element|
-            value = element.content.gsub!(/(\n) +/,'')
-            meta[element['key']] = value
-          end
-          obj.add_collection!(attribute.name, meta.inspect) && next
-        end
-
-        if (['public_addresses', 'private_addresses'].include? attribute.name)
-          obj.add_addresses!(attribute.name, (attribute/'*')) && next
-        end
-
-        if ('authentication'.include? attribute.name)
-          obj.add_authentication!(attribute[:type], (attribute/'*')) && next
-        end
-
-        #deal with providers
-        if(attribute.name == 'provider')
-          obj.add_provider!(attribute.attributes['id'].value, (attribute/'entrypoint')) && next
-        end
-
-        # Deal with collections like public-addresses, private-addresses
-        if (attribute/'./*').length > 0
-          obj.add_collection!(attribute.name, (attribute/'*').collect { |value| value.text }) && next
-        end
-
-        #deal with blobs for buckets
-        if(attribute.name == 'blob')
-          obj.add_blob!(attribute.attributes['id'].value) && next
-        end
-
-        # Anything else is treaten as text object
-        obj.add_text!(attribute.name, attribute.text.convert)
-      end
-      return obj
-    end
-
-    # Get /api and parse entry points
-    def discover_entry_points
-      return if discovered?
-      request(:get, @api_uri.to_s) do |response|
-        if response.code == 301
-          @api_uri = response.headers[:location]
-          return discover_entry_points
-        end
-        api_xml = Nokogiri::XML(response)
-        @driver_name = api_xml.xpath('/api').first[:driver]
-        @api_version = api_xml.xpath('/api').first[:version]
-
-        api_xml.css("api > link").each do |entry_point|
-          rel, href = entry_point['rel'].to_sym, entry_point['href']
-          @entry_points.store(rel, href)
-
-          entry_point.css("feature").each do |feature|
-            @features[rel] ||= []
-            @features[rel] << feature['name'].to_sym
-
-          end
-        end
-      end
-      declare_entry_points_methods(@entry_points)
-    end
-
-    # Generate create_* methods dynamically
-    #
-    def method_missing(name, *args)
-      if name.to_s =~ /^([\w_]+)_ids$/
-        return self.send(:"#{$1.pluralize}").map { |o| o.id }
-      end
-      if name.to_s =~ /^create_(\w+)/
-        params = args[0] if args[0] and args[0].class.eql?(Hash)
-        params ||= args[1] if args[1] and args[1].class.eql?(Hash)
-        params ||= {}
-
-        # FIXME: This fixes are related to Instance model and should be
-        # replaced by 'native' parameter names
-
-        params[:realm_id] ||= params[:realm] if params[:realm]
-        params[:keyname] ||= params[:key_name] if params[:key_name]
-        params[:user_data] = Base64::encode64(params[:user_data]) if params[:user_data]
-
-        if params[:hardware_profile] and params[:hardware_profile].class.eql?(Hash)
-          params[:hardware_profile].each do |k,v|
-            params[:"hwp_#{k}"] ||= v
-          end
-        else
-          params[:hwp_id] ||= params[:hardware_profile]
-        end
-
-        params[:image_id] ||= params[:image_id] || args[0] if args[0].class!=Hash
-
-        obj = nil
-
-        request(:post, entry_points[:"#{$1}s"], {}, params) do |response|
-          obj = base_object(:"#{$1}", response)
-          response_error(response) unless response_successful?(response.code)
-          yield obj if block_given?
-        end
-        return obj
-      end
-      raise NoMethodError
-    end
-
-    def use_driver(driver, opts={})
-      if driver
-        @api_driver = driver
-        @driver_name = driver
-        @api_provider = opts[:provider] if opts[:provider]
-        @features, @entry_points = {}, {}
-        discover_entry_points
-      end
-      @username = opts[:username] if opts[:username]
-      @password = opts[:password] if opts[:password]
-      @api_provider = opts[:provider] if opts[:provider]
-      return self
-    end
-
-    def use_config!(opts={})
-      @api_uri = URI.parse(opts[:url]) if opts[:url]
-      use_driver(opts[:driver], opts)
-    end
-
-    def extended_headers
-      headers = {}
-      headers["X-Deltacloud-Driver"] = @api_driver.to_s if @api_driver
-      headers["X-Deltacloud-Provider"] = @api_provider.to_s if @api_provider
-      headers
-    end
-
-    def response_successful?(code)
-      return true if code.to_s =~ /^2(\d{2})$/
-      return true if code.to_s =~ /^3(\d{2})$/
-      return false
-    end
-
-    def response_error(response)
-      xml = Nokogiri::XML(response.to_s)
-      if (xml/'message').empty? and response.code.to_s =~ /4(\d{2})/
-        DeltaCloud::HTTPError.client_error(response.code)
-      else
-        opts = {
-          :params => (xml/'request/param').inject({}) { |r,p| r[:"#{p[:name]}"] = p.text; r }
-        }
-        if backend_node = xml.at_xpath('/error/backend')
-          opts[:driver]   = backend_node[:driver]
-          opts[:provider] = backend_node[:provider]
-        end
-        backtrace = (xml/'backtrace').empty? ? nil : (xml/'backtrace').first.text.split("\n")[1..10].map { |l| l.strip }
-        DeltaCloud::HTTPError.server_error(xml.root[:status] || response.code,
-                                           (xml/'message').first.text, opts, backtrace)
-      end
-    end
-
-    # Basic request method
-    #
-    def request(*args, &block)
-      conf = {
-        :method => (args[0] || 'get').to_sym,
-        :path => (args[1]=~/^http/) ? args[1] : "#{api_uri.to_s}#{args[1]}",
-        :query_args => args[2] || {},
-        :form_data => args[3] || {},
-        :timeout => args[4] || 45,
-        :open_timeout => args[5] || 10
-      }
-      if conf[:query_args] != {}
-        conf[:path] += '?' + URI.escape(conf[:query_args].collect{ |key, value| "#{key}=#{value}" }.join('&')).to_s
-      end
-
-      if conf[:method].eql?(:post)
-        resource = RestClient::Resource.new(conf[:path], :open_timeout => conf[:open_timeout], :timeout => conf[:timeout])
-        resource.send(:post, conf[:form_data], default_headers.merge(extended_headers)) do |response, request, block|
-          response_error(response) unless response_successful? response.code
-          yield response.to_s if block_given?
-        end
-      else
-        resource = RestClient::Resource.new(conf[:path], :open_timeout => conf[:open_timeout], :timeout => conf[:timeout])
-        resource.send(conf[:method], default_headers.merge(extended_headers)) do |response, request, block|
-          response_error(response) unless response_successful? response.code
-          yield response.to_s if block_given?
-        end
-      end
-    end
-
-
-    # Check if specified collection have wanted feature
-    def feature?(collection, name)
-      @features.has_key?(collection) && @features[collection].include?(name)
-    end
-
-    # List available instance states and transitions between them
-    def instance_states
-      states = []
-      request(:get, entry_points[:instance_states]) do |response|
-        Nokogiri::XML(response).xpath('states/state').each do |state_el|
-          state = DeltaCloud::InstanceState::State.new(state_el['name'])
-          state_el.xpath('transition').each do |transition_el|
-            state.transitions << DeltaCloud::InstanceState::Transition.new(
-              transition_el['to'],
-              transition_el['action']
-            )
-          end
-          states << state
-        end
-      end
-      states
-    end
-
-    # Select instance state specified by name
-    def instance_state(name)
-      instance_states.select { |s| s.name.to_s.eql?(name.to_s) }.first
-    end
-
-    # Skip parsing /api when we already got entry points
-    def discovered?
-      true if @entry_points!={}
-    end
-
-    # This method will retrieve API documentation for given collection
-    def documentation(collection, operation=nil)
-      data = {}
-      request(:get, "/docs/#{collection}") do |body|
-        document = Nokogiri::XML(body)
-        if operation
-          data[:operation] = operation
-          data[:description] = document.xpath('/docs/collection/operations/operation[@name = "'+operation+'"]/description').first.text.strip
-          return false unless data[:description]
-          data[:params] = []
-          (document/"/docs/collection/operations/operation[@name='#{operation}']/parameter").each do |param|
-            data[:params] << {
-              :name => param['name'],
-              :required => param['type'] == 'optional',
-              :type => (param/'class').text
-            }
-          end
-        else
-          data[:description] = (document/'/docs/collection/description').text
-          data[:collection] = collection
-          data[:operations] = (document/"/docs/collection/operations/operation").collect{ |o| o['name'] }
-        end
-      end
-      return Documentation.new(self, data)
-    end
-
-    private
-
-    def default_headers
-      # The linebreaks inserted every 60 characters in the Base64
-      # encoded header cause problems under JRuby
-      auth_header = "Basic "+Base64.encode64("#{@username}:#{@password}")
-      auth_header.gsub!("\n", "")
-      {
-        :authorization => auth_header,
-        :accept => "application/xml"
-      }
-    end
-
-  end
-
-end

http://git-wip-us.apache.org/repos/asf/deltacloud/blob/b6d3365c/client/lib/deltacloud/client.rb
----------------------------------------------------------------------
diff --git a/client/lib/deltacloud/client.rb b/client/lib/deltacloud/client.rb
new file mode 100644
index 0000000..36200ad
--- /dev/null
+++ b/client/lib/deltacloud/client.rb
@@ -0,0 +1,78 @@
+# 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 Client
+    require 'require_relative' if RUBY_VERSION < '1.9'
+    require 'ostruct'
+    require 'nokogiri'
+    require 'faraday'
+
+    # Core extensions
+    require_relative './core_ext'
+
+    # Errors && Helpers
+    require_relative './client/helpers/model_helper'
+    require_relative './client/helpers/xml_helper'
+    require_relative './client/helpers/property_helper'
+
+    # Exceptions goes here
+    require_relative './client/base_error'
+
+    # Faraday Middleware for Deltacloud errors
+    require_relative './error_response'
+
+    # Deltacloud API methods
+    require_relative './client/methods/api'
+    require_relative './client/methods/backward_compatiblity'
+
+    # Extend Client module with methods that existed in old client
+    # and we want to keep them.
+    # Deprecation warnings should be provided to users if they use something
+    # from these modules.
+    #
+    extend Deltacloud::Client::Methods::BackwardCompatibility::ClassMethods
+
+    # Deltacloud methods
+    require_relative './client/methods'
+
+    # Deltacloud models
+    require_relative './client/models'
+
+    require_relative './client/connection'
+
+    VERSION = '1.1.2'
+
+    # Check if the connection to Deltacloud API is valid
+    def self.valid_connection?(api_url)
+      begin
+        Deltacloud::Client(api_url, '', '') && true
+      rescue Faraday::Error::ConnectionFailed
+        false
+      rescue Deltacloud::Client::AuthenticationError
+        false
+      end
+    end
+
+  end
+
+  def self.Client(url, api_user, api_password, opts={})
+    Client::Connection.new({
+      :url => url,
+      :api_user => api_user,
+      :api_password => api_password
+    }.merge(opts))
+  end
+end

http://git-wip-us.apache.org/repos/asf/deltacloud/blob/b6d3365c/client/lib/deltacloud/client/base_error.rb
----------------------------------------------------------------------
diff --git a/client/lib/deltacloud/client/base_error.rb b/client/lib/deltacloud/client/base_error.rb
new file mode 100644
index 0000000..544aa90
--- /dev/null
+++ b/client/lib/deltacloud/client/base_error.rb
@@ -0,0 +1,84 @@
+# 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::Client
+
+  # Reporting internal client errors
+  #
+  class Error < StandardError; end
+
+  class BaseError < Error
+    attr_reader :server_backtrace
+    attr_reader :driver
+    attr_reader :provider
+    attr_reader :status
+
+    def initialize(opts={})
+      if opts.is_a? Hash
+        @server_backtrace = opts[:server_backtrace]
+        @driver = opts[:driver]
+        @provider = opts[:provider]
+        @status = opts[:status]
+        @original_error = opts[:original_error]
+        super(opts[:message])
+      else
+        super(opts)
+      end
+    end
+
+    # Return the original XML error message received from Deltacloud API
+    def original_error
+      @original_error
+    end
+
+    # If the Deltacloud API server error response contain backtrace from
+    # server,then make this backtrace available as part of this exception
+    # backtrace
+    #
+    def set_backtrace(backtrace)
+      return super(backtrace) if @server_backtrace.nil?
+      super([
+        backtrace[0..3],
+        "-------Deltacloud API backtrace-------",
+        @server_backtrace.split[0..10],
+      ].flatten)
+    end
+
+  end
+
+  # Report 401 errors
+  class AuthenticationError < BaseError; end
+
+  # Report 502 errors (back-end cloud provider encounter error)
+  class BackendError < BaseError; end
+
+  # Report 5xx errors (error on Deltacloud API server)
+  class ServerError < BaseError; end
+
+  # Report 501 errors (collection or operation is not supported)
+  class NotSupported < ServerError; end
+
+  # Report 4xx failures (client failures)
+  class ClientFailure < BaseError; end
+
+  # Report 404 error (object not found)
+  class NotFound < BaseError; end
+
+  # Report 405 failures (resource state does not permit the requested operation)
+  class InvalidState < ClientFailure; end
+
+  # Report this when client do Image#launch using incompatible HWP
+  class IncompatibleHardwareProfile < ClientFailure; end
+end

http://git-wip-us.apache.org/repos/asf/deltacloud/blob/b6d3365c/client/lib/deltacloud/client/connection.rb
----------------------------------------------------------------------
diff --git a/client/lib/deltacloud/client/connection.rb b/client/lib/deltacloud/client/connection.rb
new file mode 100644
index 0000000..ad91cb0
--- /dev/null
+++ b/client/lib/deltacloud/client/connection.rb
@@ -0,0 +1,135 @@
+# 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::Client
+  class Connection
+
+    attr_accessor :connection
+    attr_reader :request_driver
+    attr_reader :request_provider
+    attr_reader :entrypoint
+
+    include Deltacloud::Client::Helpers::Model
+
+    include Deltacloud::Client::Methods::Common
+    include Deltacloud::Client::Methods::Api
+    include Deltacloud::Client::Methods::BackwardCompatibility
+    include Deltacloud::Client::Methods::Driver
+    include Deltacloud::Client::Methods::Realm
+    include Deltacloud::Client::Methods::HardwareProfile
+    include Deltacloud::Client::Methods::Image
+    include Deltacloud::Client::Methods::Instance
+    include Deltacloud::Client::Methods::InstanceState
+    include Deltacloud::Client::Methods::Key
+    include Deltacloud::Client::Methods::StorageVolume
+    include Deltacloud::Client::Methods::StorageSnapshot
+    include Deltacloud::Client::Methods::Address
+    include Deltacloud::Client::Methods::Bucket
+    include Deltacloud::Client::Methods::Blob
+    include Deltacloud::Client::Methods::Firewall
+
+    def initialize(opts={})
+      @request_driver = opts[:driver]
+      @request_provider = opts[:provider]
+      @connection = Faraday.new(:url => opts[:url]) do |f|
+        # NOTE: The order of this is somehow important for VCR
+        #       recording.
+        f.request :url_encoded
+        f.headers = deltacloud_request_headers
+        f.basic_auth opts[:api_user], opts[:api_password]
+        f.use Deltacloud::ErrorResponse
+        f.adapter :net_http
+      end
+      cache_entrypoint!
+      @request_driver ||= current_driver
+      @request_provider ||= current_provider
+    end
+
+    # Change the current driver and return copy of the client
+    # This allows chained calls like: client.driver(:ec2).instances
+    #
+    # - driver_id -> The new driver id (:mock, :ec2, :rhevm, ...)
+    # - api_user -> API user name
+    # - api_password -> API password
+    # - api_provider -> API provider (aka API_PROVIDER string)
+    #
+    def use(driver_id, api_user, api_password, api_provider=nil, &block)
+      new_client = self.class.new(
+        :url => @connection.url_prefix.to_s,
+        :api_user => api_user,
+        :api_password => api_password,
+        :provider => api_provider,
+        :driver => driver_id
+      )
+      new_client.cache_entrypoint!
+      yield new_client if block_given?
+      new_client
+    end
+
+    # Change the API provider but keep the current client credentials.
+    # This allows to change the EC2 region and list instances in that
+    # region without need to supply credentials.
+    #
+    # client.use_provider('eu-west-1') { |p| p.instances }
+    #
+    # - provider_id -> API provider (aka API_PROVIDER)
+    #
+    def use_provider(provider_id, &block)
+      new_client = self.clone
+      new_connection = @connection.clone
+      new_connection.headers['X-Deltacloud-Provider'] = provider_id
+      new_client.connection = new_connection
+      new_client.cache_entrypoint!(true)
+      yield new_client if block_given?
+      new_client
+    end
+
+    # Cache the API entrypoint (/api) for the current connection,
+    # so we don't need to query /api everytime we ask if certain
+    # collection/operation is supported
+    #
+    # - force -> If 'true' force to refresh stored cached entrypoint
+    #
+    def cache_entrypoint!(force=false)
+      @entrypoint = nil if force
+      @entrypoint ||= connection.get(path).body
+    end
+
+    # Check if the credentials used are valid for the current @connection
+    #
+    def valid_credentials?
+      begin
+        r = connection.get(path, { :force_auth => 'true' })
+        r.status == 200
+      rescue error(:authentication_error)
+        false
+      end
+    end
+
+    private
+
+    # Default Deltacloud HTTP headers. Common for *all* requests
+    # to Deltacloud API
+    #
+    def deltacloud_request_headers
+      headers = {}
+      headers['Accept'] = 'application/xml'
+      headers['X-Deltacloud-Driver'] = @request_driver.to_s if @request_driver
+      headers['X-Deltacloud-Provider'] = @request_provider.to_s if @request_provider
+      headers
+    end
+
+  end
+end

http://git-wip-us.apache.org/repos/asf/deltacloud/blob/b6d3365c/client/lib/deltacloud/client/helpers/model_helper.rb
----------------------------------------------------------------------
diff --git a/client/lib/deltacloud/client/helpers/model_helper.rb b/client/lib/deltacloud/client/helpers/model_helper.rb
new file mode 100644
index 0000000..2224d0d
--- /dev/null
+++ b/client/lib/deltacloud/client/helpers/model_helper.rb
@@ -0,0 +1,69 @@
+# 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::Client
+  module Helpers
+    module Model
+
+      # Retrieve the class straight from
+      # Deltacloud::Client model.
+      #
+      # -name -> A class name in underscore form (:storage_volume)
+      #
+      def model(name)
+        if name.nil? or (!name.nil? and name.empty?)
+          raise error.new("The model name can't be blank")
+        end
+        Deltacloud::Client.const_get(name.to_s.camelize)
+      end
+
+      # Syntax sugar method for retrieving various Client
+      # exception classes.
+      #
+      # - name -> Exception class name in underscore
+      #
+      # NOTE: If name is 'nil' the default Error exception
+      #       will be returned
+      #
+      def error(name=nil)
+        model(name || :error)
+      end
+
+      # Checks if current @connection support +model_name+
+      # and then convert HTTP response to a Ruby model
+      #
+      # - model_name -> A class name in underscore form
+      # - collection_body -> HTTP body of collection
+      #
+      def from_collection(model_name, collection_body)
+        must_support!(model_name)
+        model(model_name.to_s.singularize).from_collection(
+          self,
+          collection_body
+        )
+      end
+
+      # Check if the collection for given model is supported
+      # in current @connection and then parse/convert
+      # resource XML to a Ruby class
+      #
+      def from_resource(model_name, resource_body)
+        must_support!(model_name.to_s.pluralize)
+        model(model_name).convert(self, resource_body)
+      end
+
+    end
+  end
+end

http://git-wip-us.apache.org/repos/asf/deltacloud/blob/b6d3365c/client/lib/deltacloud/client/helpers/property_helper.rb
----------------------------------------------------------------------
diff --git a/client/lib/deltacloud/client/helpers/property_helper.rb b/client/lib/deltacloud/client/helpers/property_helper.rb
new file mode 100644
index 0000000..a30ab9a
--- /dev/null
+++ b/client/lib/deltacloud/client/helpers/property_helper.rb
@@ -0,0 +1,103 @@
+# 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::Client
+  module Helpers
+    module Property
+
+      class Property
+        attr_reader :name, :unit, :default
+
+        def initialize(name, unit, default=nil)
+          @name = name
+          @unit = unit
+          @default = default
+        end
+
+        def value
+          @default || 'opaque'
+        end
+
+        def self.parse(body)
+          Property.new(body['name'], body['unit'], body['value'])
+        end
+
+        def kind
+          self.class.name.split('::').last.downcase.to_sym
+        end
+
+      end
+
+      class Range < Property
+
+        attr_reader :first, :last
+
+        def initialize(name, unit, first, last, default=nil)
+          @first, @last = first, last
+          super(name, unit, default)
+        end
+
+        def value
+          ::Range.new(@first.to_i, @last.to_i)
+        end
+
+        def self.parse(body)
+          base = super
+          new(base.name, base.unit, body.at('range')['first'], body.at('range')['last'], base.default)
+        end
+
+      end
+
+      class Enum < Property
+        include Enumerable
+        attr_reader :values
+
+        def initialize(name, unit, values, default=nil)
+          @values = values
+          super(name, unit, default)
+        end
+
+        def value
+          @values
+        end
+
+        def each
+          value.each
+        end
+
+        def self.parse(body)
+          base = super
+          new(base.name, base.unit, body.xpath('enum/entry').map { |e| e['value'] }, base.default)
+        end
+      end
+
+      class Fixed < Property
+        attr_reader :value
+
+        def initialize(name, unit, value)
+          @value = value
+          super(name, unit, @value)
+        end
+
+        def self.parse(body)
+          base = super
+          new(base.name, base.unit, body['value'])
+        end
+
+      end
+
+    end
+  end
+end

http://git-wip-us.apache.org/repos/asf/deltacloud/blob/b6d3365c/client/lib/deltacloud/client/helpers/xml_helper.rb
----------------------------------------------------------------------
diff --git a/client/lib/deltacloud/client/helpers/xml_helper.rb b/client/lib/deltacloud/client/helpers/xml_helper.rb
new file mode 100644
index 0000000..5874c00
--- /dev/null
+++ b/client/lib/deltacloud/client/helpers/xml_helper.rb
@@ -0,0 +1,33 @@
+# 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::Client
+  module Helpers
+    module XmlHelper
+
+      # Extract XML string from the various objects
+      #
+      def extract_xml_body(obj)
+        case obj
+        when Faraday::Response then obj.body
+        when Nokogiri::XML::Element then obj.to_s
+        when Nokogiri::XML::Document then obj.to_s
+        else obj
+        end
+      end
+
+    end
+  end
+end

http://git-wip-us.apache.org/repos/asf/deltacloud/blob/b6d3365c/client/lib/deltacloud/client/methods.rb
----------------------------------------------------------------------
diff --git a/client/lib/deltacloud/client/methods.rb b/client/lib/deltacloud/client/methods.rb
new file mode 100644
index 0000000..0897866
--- /dev/null
+++ b/client/lib/deltacloud/client/methods.rb
@@ -0,0 +1,29 @@
+# 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_relative './methods/common'
+require_relative './methods/driver'
+require_relative './methods/realm'
+require_relative './methods/hardware_profile'
+require_relative './methods/image'
+require_relative './methods/instance'
+require_relative './methods/instance_state'
+require_relative './methods/storage_volume'
+require_relative './methods/storage_snapshot'
+require_relative './methods/key'
+require_relative './methods/address'
+require_relative './methods/bucket'
+require_relative './methods/blob'
+require_relative './methods/firewall'

http://git-wip-us.apache.org/repos/asf/deltacloud/blob/b6d3365c/client/lib/deltacloud/client/methods/address.rb
----------------------------------------------------------------------
diff --git a/client/lib/deltacloud/client/methods/address.rb b/client/lib/deltacloud/client/methods/address.rb
new file mode 100644
index 0000000..21da5f6
--- /dev/null
+++ b/client/lib/deltacloud/client/methods/address.rb
@@ -0,0 +1,68 @@
+# 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::Client
+  module Methods
+    module Address
+
+      # Retrieve list of all address entities
+      #
+      # Filter options:
+      #
+      # - :id -> Filter entities using 'id' attribute
+      #
+      def addresses(filter_opts={})
+        from_collection :addresses,
+        connection.get(api_uri('addresses'), filter_opts)
+      end
+
+      # Retrieve the single address entity
+      #
+      # - address_id -> Address entity to retrieve
+      #
+      def address(address_id)
+        from_resource :address,
+          connection.get(api_uri("addresses/#{address_id}"))
+      end
+
+      # Create a new address
+      #
+      def create_address
+        create_resource :address, {}
+      end
+
+      def destroy_address(address_id)
+        destroy_resource :address, address_id
+      end
+
+      def associate_address(address_id, instance_id)
+        result = connection.post(
+          api_uri("/addresses/#{address_id}/associate")
+        ) do |request|
+          request.params = { :instance_id => instance_id }
+        end
+        result.status == 202
+      end
+
+      def disassociate_address(address_id)
+        result = connection.post(
+          api_uri("/addresses/#{address_id}/disassociate")
+        )
+        result.status == 202
+      end
+
+    end
+  end
+end

http://git-wip-us.apache.org/repos/asf/deltacloud/blob/b6d3365c/client/lib/deltacloud/client/methods/api.rb
----------------------------------------------------------------------
diff --git a/client/lib/deltacloud/client/methods/api.rb b/client/lib/deltacloud/client/methods/api.rb
new file mode 100644
index 0000000..32bd048
--- /dev/null
+++ b/client/lib/deltacloud/client/methods/api.rb
@@ -0,0 +1,96 @@
+# 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::Client
+  module Methods
+    module Api
+
+      def path
+        connection.url_prefix.path
+      end
+
+      def api_uri(uri)
+        URI.parse([path, uri.gsub(/^(\/+)/,'')].join('/'))
+      end
+
+      # The current version of Deltacloud API
+      #
+      def version
+        entrypoint.to_xml.root['version']
+      end
+
+      alias_method :api_version, :version
+
+      # The current driver the @connection is using
+      #
+      def current_driver
+        entrypoint.to_xml.root['driver']
+      end
+
+      alias_method :api_driver,   :current_driver
+      alias_method :driver_name,  :current_driver
+
+      # The current provider the @connection is using
+      #
+      def current_provider
+        entrypoint.to_xml.root['provider']
+      end
+
+      # List of the currently supported collections by @connection
+      #
+      def supported_collections
+        entrypoint.to_xml.root.xpath('link').map { |l| l['rel'] }
+      end
+
+      alias_method :entrypoints, :supported_collections
+
+      # Syntax sugar for +supported_collections+
+      # Return 'true' if the collection is supported by current API entrypoint
+      #
+      def support?(collection)
+        supported_collections.include? collection.to_s
+      end
+
+      # Syntax sugar for Method modules, where you can 'require' the support
+      # for the given collection before you execute API call
+      #
+      # Raise +NotSupported+ exception if the given +collection+ is not
+      # supported
+      #
+      def must_support!(collection)
+        unless support?(collection)
+          raise error(:not_supported).new("Collection '#{collection}' not supported by current API endpoint.")
+        end
+      end
+
+      # +Hash+ of all features supported by current connection
+      #
+      def features
+        entrypoint.to_xml.root.xpath('link/feature').inject(Hash.new(Array.new)) do |result, feature|
+          result[feature.parent['rel']] += [feature['name']]
+          result
+        end
+      end
+
+      # Check if the current collection support given feature for given
+      # collection
+      #
+      def feature?(collection_name, feature_name)
+        features[collection_name.to_s].include?(feature_name.to_s)
+      end
+
+    end
+  end
+end

http://git-wip-us.apache.org/repos/asf/deltacloud/blob/b6d3365c/client/lib/deltacloud/client/methods/backward_compatiblity.rb
----------------------------------------------------------------------
diff --git a/client/lib/deltacloud/client/methods/backward_compatiblity.rb b/client/lib/deltacloud/client/methods/backward_compatiblity.rb
new file mode 100644
index 0000000..8718836
--- /dev/null
+++ b/client/lib/deltacloud/client/methods/backward_compatiblity.rb
@@ -0,0 +1,72 @@
+# 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::Client
+  module Methods
+    module BackwardCompatibility
+
+      # Backward compatibility methods provides fallback for the
+      # old deltacloud-client gem.
+      #
+      #
+      def api_host
+        connection.url_prefix.host
+      end
+
+      def api_port
+        connection.url_prefix.port
+      end
+
+      def connect(&block)
+        yield self.clone
+      end
+
+      def with_config(opts, &block)
+        yield inst = use(
+          opts[:driver],
+          opts[:username],
+          opts[:password],
+          opts[:provider]
+        ) if block_given?
+        inst
+      end
+
+      def use_driver(new_driver, opts={})
+        with_config(opts.merge(:driver => new_driver))
+      end
+
+      alias_method :"use_config!", :use_driver
+
+      def discovered?
+        true unless entrypoint.nil?
+      end
+
+      module ClassMethods
+
+        def valid_credentials?(api_user, api_password, api_url, opts={})
+          args = {
+            :api_user => api_user,
+            :api_password => api_password,
+            :url => api_url
+          }
+          args.merge!(:providers => opts[:provider]) if opts[:provider]
+          Deltacloud::Client::Connection.new(args).valid_credentials?
+        end
+
+      end
+
+    end
+  end
+end

http://git-wip-us.apache.org/repos/asf/deltacloud/blob/b6d3365c/client/lib/deltacloud/client/methods/blob.rb
----------------------------------------------------------------------
diff --git a/client/lib/deltacloud/client/methods/blob.rb b/client/lib/deltacloud/client/methods/blob.rb
new file mode 100644
index 0000000..80a3945
--- /dev/null
+++ b/client/lib/deltacloud/client/methods/blob.rb
@@ -0,0 +1,91 @@
+# 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::Client
+  module Methods
+    module Blob
+
+      # Retrieve list of all blob entities from given bucket
+      #
+      def blobs(bucket_id=nil)
+        raise error.new("The :bucket_id cannot be nil.") if bucket_id.nil?
+        bucket(bucket_id).blob_ids.map { |blob_id| blob(bucket_id, blob_id) }
+      end
+
+      # Retrieve the single blob entity
+      #
+      # - blob_id -> Blob entity to retrieve
+      #
+      def blob(bucket_id, blob_id)
+        model(:blob).convert(
+          self,
+          connection.get(api_uri("buckets/#{bucket_id}/#{blob_id}"))
+        )
+      end
+
+      # Create a new blob
+      #
+      # - bucket_id -> A bucket ID that new blob should belong to
+      # - blob_name -> A name for new blob
+      # - blob_data -> Data stored in this blob
+      # - create_opts
+      #   - :user_metadata -> A Ruby +Hash+ with key => value metadata
+      #
+      def create_blob(bucket_id, blob_name, blob_data, create_opts={})
+        must_support! :buckets
+        create_opts.merge!(convert_meta_params(create_opts.delete(:user_metadata)))
+        response = connection.post(api_uri("buckets/#{bucket_id}")) do |request|
+          request.params = create_opts.merge(
+            :blob_id => blob_name,
+            :blob_data => blob_data
+        )
+        end
+        model(:blob).convert(self, response.body)
+      end
+
+      # Destroy given bucket blob
+      #
+      def destroy_blob(bucket_id, blob_id)
+        must_support! :buckets
+        r = connection.delete(api_uri("buckets/#{bucket_id}/#{blob_id}"))
+        r.status == 204
+      end
+
+      private
+
+      # Convert the user_metadata into POST params compatible with
+      # blob creation
+      #
+      # - params -> Simple Ruby +Hash+
+      #
+      # @return { :meta_params => COUNTER, :meta_name1 => '', :meta_value1 => ''}
+      #
+      def convert_meta_params(params)
+        meta_params = {}
+        counter = 0
+        (params || {}).each do |key, value|
+          counter += 1
+          meta_params["meta_name#{counter}"] = key
+          meta_params["meta_value#{counter}"] = value
+        end
+        if counter >= 1
+          meta_params.merge!(:meta_params => counter.to_s)
+        end
+        meta_params
+      end
+
+    end
+  end
+end

http://git-wip-us.apache.org/repos/asf/deltacloud/blob/b6d3365c/client/lib/deltacloud/client/methods/bucket.rb
----------------------------------------------------------------------
diff --git a/client/lib/deltacloud/client/methods/bucket.rb b/client/lib/deltacloud/client/methods/bucket.rb
new file mode 100644
index 0000000..b68cb05
--- /dev/null
+++ b/client/lib/deltacloud/client/methods/bucket.rb
@@ -0,0 +1,56 @@
+# 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::Client
+  module Methods
+    module Bucket
+
+      # Retrieve list of all bucket entities
+      #
+      # Filter options:
+      #
+      # - :id -> Filter entities using 'id' attribute
+      #
+      def buckets(filter_opts={})
+        from_collection :buckets,
+          connection.get(api_uri('buckets'), filter_opts)
+      end
+
+      # Retrieve the single bucket entity
+      #
+      # - bucket_id -> Bucket entity to retrieve
+      #
+      def bucket(bucket_id)
+        from_resource :bucket,
+          connection.get(api_uri("buckets/#{bucket_id}"))
+      end
+
+      # Create a new bucket
+      #
+      # - create_opts
+      #
+      def create_bucket(name)
+        create_resource :bucket, :name => name
+      end
+
+      # Destroy given bucket
+      #
+      def destroy_bucket(bucket_id)
+        destroy_resource :bucket, bucket_id
+      end
+
+    end
+  end
+end

http://git-wip-us.apache.org/repos/asf/deltacloud/blob/b6d3365c/client/lib/deltacloud/client/methods/common.rb
----------------------------------------------------------------------
diff --git a/client/lib/deltacloud/client/methods/common.rb b/client/lib/deltacloud/client/methods/common.rb
new file mode 100644
index 0000000..250e3a4
--- /dev/null
+++ b/client/lib/deltacloud/client/methods/common.rb
@@ -0,0 +1,46 @@
+# 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::Client
+  module Methods
+    module Common
+
+      # A generic method for creating a new resources
+      #
+      # - resource_name -> A resource name to create (eg. :image)
+      # - create_opts -> HTTP options to pass into the create operation
+      #
+      def create_resource(resource_name, create_opts={})
+        no_convert_model = create_opts.delete(:no_convert_model)
+        must_support! resource_name.to_s.pluralize
+        response = connection.post(api_uri(resource_name.to_s.pluralize)) do |request|
+          request.params = create_opts
+        end
+        no_convert_model ? response : model(resource_name).convert(self, response.body)
+      end
+
+      # A generic method for destroying resources
+      #
+      def destroy_resource(resource_name, resource_id)
+        must_support! resource_name.to_s.pluralize
+        result = connection.delete(
+          api_uri([resource_name.to_s.pluralize, resource_id].join('/'))
+        )
+        result.status.is_no_content?
+      end
+
+    end
+  end
+end