You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@deltacloud.apache.org by ma...@redhat.com on 2012/11/19 15:54:04 UTC

Initial StorageVolume support for the Openstack driver

Hi, these patches (2 and 3) add storage volumes support for the
Openstack driver. This relies on getting the latest version of
the Openstack rubygem - 1.0.7 which is currently available.

More info/write-up about how/why when adding this functionality 
in both the gem and deltacloud is at
http://mariosandreou.com/deltacloud/cloud_API/2012/11/19/deltacloud-openstack-driver-volumes.html

marios

[PATCH 1/3] Fix bug in images show.xml ... hwp.name vs hwp.id

Posted by ma...@redhat.com.
From: marios <ma...@redhat.com>


Signed-off-by: marios <ma...@redhat.com>
---
 server/views/images/show.xml.haml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/server/views/images/show.xml.haml b/server/views/images/show.xml.haml
index 66b4f9a..6350706 100644
--- a/server/views/images/show.xml.haml
+++ b/server/views/images/show.xml.haml
@@ -12,7 +12,7 @@
   - if @image.hardware_profiles
     %hardware_profiles
       - @image.hardware_profiles.each do |profile|
-        %hardware_profile{ :href => hardware_profile_url(profile.name), :id => profile.name, :rel => :hardware_profile }
+        %hardware_profile{ :href => hardware_profile_url(profile.id), :id => profile.id, :rel => :hardware_profile }
   %actions
     %link{ :rel => 'create_instance', :method => :post, :href => "#{instances_url};image_id=#{@image.id}"}
     %link{ :rel => 'destroy_image', :method => :delete, :href => "#{destroy_image_url(@image.id)}" }
-- 
1.7.11.7


[PATCH 2/3] Add features and attributes for Openstack block storage volumes

Posted by ma...@redhat.com.
From: marios <ma...@redhat.com>


Signed-off-by: marios <ma...@redhat.com>
---
 server/lib/deltacloud/collections/storage_volumes.rb |  4 ++++
 server/lib/deltacloud/drivers/features.rb            | 19 +++++++++++++++++++
 server/lib/deltacloud/models/storage_volume.rb       |  1 +
 3 files changed, 24 insertions(+)

diff --git a/server/lib/deltacloud/collections/storage_volumes.rb b/server/lib/deltacloud/collections/storage_volumes.rb
index 0b52045..bc505cb 100644
--- a/server/lib/deltacloud/collections/storage_volumes.rb
+++ b/server/lib/deltacloud/collections/storage_volumes.rb
@@ -16,6 +16,8 @@
 module Deltacloud::Collections
   class StorageVolumes < Base
 
+    include Deltacloud::Features
+
     set :capability, lambda { |m| driver.respond_to? m }
     check_features :for => lambda { |c, f| driver.class.has_feature?(c, f) }
 
@@ -37,6 +39,8 @@ module Deltacloud::Collections
         param :snapshot_id, :string,  :optional
         param :capacity,    :string,  :optional
         param :realm_id,    :string,  :optional
+        param :name,        :string,  :optional
+        param :description, :string,  :optional
         control do
           @storage_volume = driver.create_storage_volume(credentials, params)
           status 201
diff --git a/server/lib/deltacloud/drivers/features.rb b/server/lib/deltacloud/drivers/features.rb
index 3e1b704..ca4e19a 100644
--- a/server/lib/deltacloud/drivers/features.rb
+++ b/server/lib/deltacloud/drivers/features.rb
@@ -163,6 +163,25 @@ module Deltacloud
         end
       end
 
+      feature :volume_name, :for => :storage_volumes do
+        description "Specify name when creating storage_volume"
+        operation :create do
+          param :name, :string, :optional
+        end
+      end
+
+      feature :volume_description, :for => :storage_volumes do
+        description "Specify description when creating storage_volume"
+        operation :create do
+          param :description, :string, :optional
+        end
+      end
+
+
+
+
+
+
     end
 
   end
diff --git a/server/lib/deltacloud/models/storage_volume.rb b/server/lib/deltacloud/models/storage_volume.rb
index 0722395..7aa00a9 100644
--- a/server/lib/deltacloud/models/storage_volume.rb
+++ b/server/lib/deltacloud/models/storage_volume.rb
@@ -26,5 +26,6 @@ class StorageVolume < BaseModel
   attr_accessor :actions
   attr_accessor :name
   attr_accessor :kind
+  attr_accessor :description # openstack volumes have a display_description attr
 
 end
-- 
1.7.11.7


[PATCH 3/3] Adds storage volume support for deltacloud driver

Posted by ma...@redhat.com.
From: marios <ma...@redhat.com>

needs openstack gem 1.0.7

Signed-off-by: marios <ma...@redhat.com>
---
 server/deltacloud-core.gemspec                     |   2 +-
 .../drivers/openstack/openstack_driver.rb          | 153 +++++++++++++++++----
 2 files changed, 125 insertions(+), 30 deletions(-)

diff --git a/server/deltacloud-core.gemspec b/server/deltacloud-core.gemspec
index cb9a8c8..3117a0e 100644
--- a/server/deltacloud-core.gemspec
+++ b/server/deltacloud-core.gemspec
@@ -104,7 +104,7 @@ Gem::Specification.new do |s|
   s.add_dependency('uuidtools', '>= 2.1.1')
 
   # Openstack Compute and Object-Storage
-  s.add_dependency('openstack', '>= 1.0.6')
+  s.add_dependency('openstack', '>= 1.0.7')
 
   # Aruba Cloud
   s.add_dependency('savon', '>= 1.0.0')
diff --git a/server/lib/deltacloud/drivers/openstack/openstack_driver.rb b/server/lib/deltacloud/drivers/openstack/openstack_driver.rb
index 6425d1b..2bf45ab 100644
--- a/server/lib/deltacloud/drivers/openstack/openstack_driver.rb
+++ b/server/lib/deltacloud/drivers/openstack/openstack_driver.rb
@@ -30,6 +30,8 @@ module Deltacloud
         feature :instances, :user_data
         feature :images, :user_name
         feature :keys, :import_key
+        feature :storage_volumes, :volume_name
+        feature :storage_volumes, :volume_description
 
         define_instance_states do
           start.to( :pending )          .on( :create )
@@ -46,9 +48,14 @@ module Deltacloud
           #get the collections as defined by 'capability' and 'respond_to?' blocks
           super_collections = super
           begin
-             new_client(credentials, :buckets)
+             new_client(credentials, "object-store")
           rescue Deltacloud::Exceptions::NotImplemented #OpenStack::Exception::NotImplemented...
-             return super_collections - [Sinatra::Rabbit::BucketsCollection]
+             super_collections = super_collections - [Sinatra::Rabbit::BucketsCollection]
+          end
+          begin
+              new_client(credentials, "volume")
+          rescue Deltacloud::Exceptions::NotImplemented
+              super_collections = super_collections - [Sinatra::Rabbit::StorageVolumesCollection]
           end
           super_collections
         end
@@ -138,19 +145,19 @@ module Deltacloud
 
         def instances(credentials, opts={})
           os = new_client(credentials)
-          insts = []
+          insts = attachments = []
           safely do
             if opts[:id]
               begin
                 server = os.get_server(opts[:id])
-                insts << convert_from_server(server, os.connection.authuser)
+                insts << convert_from_server(server, os.connection.authuser, get_attachments(opts[:id], os))
               rescue => e
                 raise e unless e.message =~ /The resource could not be found/
                 insts = []
               end
             else
               insts = os.list_servers_detail.collect do |s|
-                convert_from_server(s, os.connection.authuser)
+                convert_from_server(s, os.connection.authuser,get_attachments(s[:id], os))
               end
             end
           end
@@ -179,7 +186,7 @@ module Deltacloud
           end
           safely do
             server = os.create_server(params)
-            result = convert_from_server(server, os.connection.authuser)
+            result = convert_from_server(server, os.connection.authuser, get_attachments(server.id, os))
           end
           result
         end
@@ -189,7 +196,7 @@ module Deltacloud
           safely do
             server = os.get_server(instance_id)
             server.reboot! # sends a hard reboot (power cycle) - could instead server.reboot("SOFT")
-            convert_from_server(server, os.connection.authuser)
+            convert_from_server(server, os.connection.authuser, get_attachments(instance_id, os))
           end
         end
 
@@ -222,7 +229,7 @@ module Deltacloud
         end
 
         def buckets(credentials, opts={})
-          os = new_client(credentials, :buckets)
+          os = new_client(credentials, "object-store")
           buckets = []
           safely do
             if opts[:id]
@@ -235,7 +242,7 @@ module Deltacloud
         end
 
         def create_bucket(credentials, name, opts={})
-          os = new_client(credentials, :buckets)
+          os = new_client(credentials, "object-store")
           bucket = nil
           safely do
             bucket = os.create_container(name)
@@ -244,14 +251,14 @@ module Deltacloud
         end
 
         def delete_bucket(credentials, name, opts={})
-          os = new_client(credentials, :buckets)
+          os = new_client(credentials, "object-store")
           safely do
             os.delete_container(name)
           end
         end
 
         def blobs(credentials, opts={})
-          os = new_client(credentials, :buckets)
+          os = new_client(credentials, "object-store")
           blobs = []
           safely do
             bucket = os.container(opts['bucket'])
@@ -265,7 +272,7 @@ module Deltacloud
         end
 
         def blob_data(credentials, bucket, blob, opts={})
-          os = new_client(credentials, :buckets)
+          os = new_client(credentials, "object-store")
           safely do
             os.container(bucket).object(blob).data_stream do |chunk|
               yield chunk
@@ -274,7 +281,7 @@ module Deltacloud
         end
 
         def create_blob(credentials, bucket, blob, data, opts={})
-          os = new_client(credentials, :buckets)
+          os = new_client(credentials, "object-store")
           safely do
             BlobHelper.rename_metadata_headers(opts, "X-Object-Meta-")
             os_blob = os.container(bucket).create_object(blob, {:content_type=> data[:type], :metadata=>opts}, data[:tempfile])
@@ -283,21 +290,21 @@ module Deltacloud
         end
 
         def delete_blob(credentials, bucket, blob, opts={})
-          os = new_client(credentials, :buckets)
+          os = new_client(credentials, "object-store")
           safely do
             os.container(bucket).delete_object(blob)
           end
         end
 
         def blob_metadata(credentials, opts={})
-          os = new_client(credentials, :buckets)
+          os = new_client(credentials, "object-store")
           safely do
             os.container(opts['bucket']).object(opts[:id]).metadata
           end
         end
 
         def update_blob_metadata(credentials, opts={})
-          os = new_client(credentials, :buckets)
+          os = new_client(credentials, "object-store")
           safely do
             BlobHelper.rename_metadata_headers(opts["meta_hash"], "")
             blob = os.container(opts['bucket']).object(opts[:id])
@@ -352,10 +359,82 @@ module Deltacloud
           end
         end
 
+        def storage_volumes(credentials, opts={})
+          vs = new_client(credentials, "volume")
+          volumes = []
+          safely do
+            if opts[:id]
+              volumes <<  convert_volume(vs.get_volume(opts[:id]))
+            else
+              vs.volumes.each do |vol|
+                volumes << convert_volume(vol)
+              end
+            end
+          end
+          volumes
+        end
+
+        def create_storage_volume(credentials, opts=nil)
+          vs = new_client(credentials, "volume")
+          params = {}
+          safely do
+            params[:size] = opts.delete("capacity") || 1
+            params[:display_name] = opts.delete("name") || "Volume#{Time.now}"
+            params[:display_description] = opts.delete("description") || params[:display_name]
+            params[:availability_zone] = opts.delete("realm_id") unless (opts["realm_id"].nil? || opts["realm_id"].empty?)
+            opts.delete("commit")
+            opts.delete("snapshot_id") #FIXME AFTER ADDING SNAPSHOTS TO OPENSTACK GEM
+            volume = convert_volume(vs.create_volume(opts.merge(params)))
+          end
+        end
+
+        def destroy_storage_volume(credentials, opts={})
+          vs = new_client(credentials, "volume")
+          safely do
+            vs.delete_volume(opts[:id])
+          end
+        end
+
+        def attach_storage_volume(credentials, opts={})
+          vs = new_client(credentials, "volume")
+          cs = new_client(credentials, "compute")
+          safely do
+            cs.attach_volume(opts[:instance_id], opts[:id], opts[:device])
+            volume = convert_volume(vs.get_volume(opts[:id]))
+          end
+        end
+
+        def detach_storage_volume(credentials, opts={})
+          vs = new_client(credentials, "volume")
+          cs = new_client(credentials, "compute")
+          safely do
+            cs.detach_volume(opts[:instance_id], opts[:id])
+            volume = convert_volume(vs.get_volume(opts[:id]))
+          end
+        end
+
+        def storage_snapshots(credentials, opts={})
+          vs = new_client(credentials, "volume")
+          safely do
+          end
+        end
+
+        def create_storage_snapshot(credentials, opts={})
+          vs = new_client(credentials, "volume")
+          safely do
+          end
+        end
+
+        def destroy_storage_snapshot(credentials, opts={})
+          vs = new_client(credentials, "volume")
+          safely do
+          end
+        end
+
 private
 
         #for v2 authentication credentials.name == "username+tenant_name"
-        def new_client(credentials, type = :compute)
+        def new_client(credentials, type = "compute")
           tokens = credentials.user.split("+")
           if credentials.user.empty?
             raise AuthenticationFailure.new(Exception.new("Error: you must supply the username"))
@@ -366,16 +445,10 @@ private
             user_name, tenant_name = tokens.first, tokens.last
           end
           safely do
-              case type
-                when :compute
-                  OpenStack::Connection.create(:username => user_name, :api_key => credentials.password, :authtenant => tenant_name, :auth_url => api_provider)
-                when :buckets
-                  OpenStack::Connection.create(:username => user_name, :api_key => credentials.password, :authtenant => tenant_name, :auth_url => api_provider, :service_type => "object-store")
-                else
-                  raise ValidationFailure.new(Exception.new("Error: tried to initialise Openstack connection using" +
-                    " an unknown service_type: #{type}"))
-              end
-          end
+            raise ValidationFailure.new(Exception.new("Error: tried to initialise Openstack connection using" +
+                    " an unknown service_type: #{type}")) unless ["volume", "compute", "object-store"].include? type
+            OpenStack::Connection.create(:username => user_name, :api_key => credentials.password, :authtenant => tenant_name, :auth_url => api_provider, :service_type => type)
+           end
         end
 
 #NOTE: for the convert_from_foo methods below... openstack-compute
@@ -407,7 +480,7 @@ private
                     })
         end
 
-        def convert_from_server(server, owner)
+        def convert_from_server(server, owner, attachments=[])
           op = (server.class == Hash)? :fetch : :send
           image = server.send(op, :image)
           flavor = server.send(op, :flavor)
@@ -430,7 +503,8 @@ private
             :private_addresses => convert_server_addresses(server, :private),
             :username => 'root',
             :password => password,
-            :keyname => server.send(op, :key_name)
+            :keyname => server.send(op, :key_name),
+            :storage_volumes => attachments.inject([]){|res, cur| res << {cur[:volumeId] => cur[:device]} ;res}
           )
           inst.actions = instance_actions_for(inst.state)
           inst.create_image = 'RUNNING'.eql?(inst.state)
@@ -489,6 +563,27 @@ private
           )
         end
 
+        def get_attachments(server_id, client)
+          if client.api_extensions[:"os-volumes"]
+            attachments = client.list_attachments(server_id)
+            attachments[:volumeAttachments] || []
+          else
+            []
+          end
+        end
+
+        def convert_volume(vol)
+          StorageVolume.new({ :id => vol.id,
+                              :name => vol.display_name,
+                              :created => vol.created_at,
+                              :state => (vol.attachments.inject([]){|res, cur| res << cur if cur.size > 0 ; res}.empty?) ? "AVAILABLE" : "IN-USE",
+                              :capacity => vol.size,
+                              :instance_id => vol.attachments.first["serverId"],
+                              :device =>vol.attachments.first["device"],
+                              :realm_id => vol.availability_zone,
+                              :description => vol.display_description # openstack volumes have a display_description attr
+          })
+        end
 
         #IN: path1='server_path1'. content1='contents1', path2='server_path2', content2='contents2' etc
         #OUT:{local_path=>server_path, local_path1=>server_path2 etc}
-- 
1.7.11.7