diff --git a/fog.gemspec b/fog.gemspec index 72bc0d45c..96b0793b7 100644 --- a/fog.gemspec +++ b/fog.gemspec @@ -10,7 +10,6 @@ Gem::Specification.new do |s| s.date = '2013-01-19' s.rubyforge_project = 'fog' - ## Make sure your summary is short. The description may be as long ## as you like. s.summary = "brings clouds to you" diff --git a/lib/fog/bin/hp.rb b/lib/fog/bin/hp.rb index 025946c51..2b8dee8b5 100644 --- a/lib/fog/bin/hp.rb +++ b/lib/fog/bin/hp.rb @@ -3,6 +3,8 @@ class HP < Fog::Bin def class_for(key) case key + when :block_storage + Fog::HP::BlockStorage when :cdn Fog::CDN::HP when :compute @@ -17,11 +19,16 @@ class HP < Fog::Bin def [](service) @@connections ||= Hash.new do |hash, key| hash[key] = case key + when :block_storage + Fog::HP::BlockStorage.new when :cdn + Fog::Logger.warning("HP[:cdn] is deprecated, use CDN[:hp] instead") Fog::CDN.new(:provider => 'HP') when :compute + Fog::Logger.warning("HP[:compute] is deprecated, use Compute[:hp] instead") Fog::Compute.new(:provider => 'HP') when :storage + Fog::Logger.warning("HP[:storage] is deprecated, use Storage[:hp] instead") Fog::Storage.new(:provider => 'HP') else raise ArgumentError, "Unrecognized service: #{key.inspect}" diff --git a/lib/fog/core/errors.rb b/lib/fog/core/errors.rb index aaea7e6f8..29ea82e78 100644 --- a/lib/fog/core/errors.rb +++ b/lib/fog/core/errors.rb @@ -46,9 +46,10 @@ An alternate file may be used by placing its path in the FOG_RC environment vari :go_grid_shared_secret: :google_storage_access_key_id: :google_storage_secret_access_key: - :hp_account_id: + :hp_access_key: :hp_secret_key: :hp_tenant_id: + :hp_avl_zone: :linode_api_key: :local_root: :bare_metal_cloud_password: diff --git a/lib/fog/hp.rb b/lib/fog/hp.rb index 1ea08c2fb..887dfe5ca 100644 --- a/lib/fog/hp.rb +++ b/lib/fog/hp.rb @@ -2,6 +2,12 @@ require 'fog/core' module Fog module HP + + # define a specific version for the HP Provider + unless const_defined?(:VERSION) + VERSION = '0.0.19' + end + extend Fog::Provider module Errors @@ -13,10 +19,14 @@ module Fog data = nil message = nil else - data = Fog::JSON.decode(error.response.body) - message = data['message'] - if message.nil? and !data.values.first.nil? - message = data.values.first['message'] + begin + data = Fog::JSON.decode(error.response.body) + message = data['message'] + if message.nil? and !data.values.first.nil? + message = data.values.first['message'] + end + rescue MultiJson::DecodeError + message = error.response.body #### body is not in JSON format, so just return as is end end @@ -29,6 +39,7 @@ module Fog class InternalServerError < ServiceError; end class Conflict < ServiceError; end class NotFound < ServiceError; end + class Forbidden < ServiceError; end class ServiceUnavailable < ServiceError; end class BadRequest < ServiceError @@ -47,6 +58,7 @@ module Fog service(:cdn, 'hp/cdn', 'CDN') service(:compute, 'hp/compute', 'Compute') service(:storage, 'hp/storage', 'Storage') + service(:block_storage, 'hp/block_storage', 'BlockStorage') # legacy swauth 1.0/1.1 style authentication def self.authenticate_v1(options, connection_options = {}) @@ -61,14 +73,17 @@ module Fog @auth_path = "auth/v1.0" end service_url = "#{@scheme}://#{@host}:#{@port}" + # Set the User-Agent + @user_agent = options[:user_agent] + set_user_agent_header(connection_options, "hpfog v1/#{Fog::HP::VERSION}", @user_agent) connection = Fog::Connection.new(service_url, false, connection_options) - @hp_account_id = options[:hp_account_id] + @hp_access_key = options[:hp_access_key] @hp_secret_key = options[:hp_secret_key] response = connection.request({ :expects => [200, 204], :headers => { 'X-Auth-Key' => @hp_secret_key, - 'X-Auth-User' => @hp_account_id + 'X-Auth-User' => @hp_access_key }, :host => @host, :port => @port, @@ -101,16 +116,19 @@ module Fog @auth_path = "v2.0/tokens" end service_url = "#{@scheme}://#{@host}:#{@port}" + # Set the User-Agent. If the caller sets a user_agent, use it. + @user_agent = options[:user_agent] + set_user_agent_header(connection_options, "hpfog/#{Fog::HP::VERSION}", @user_agent) connection = Fog::Connection.new(service_url, false, connection_options) ### Implement HP Control Services Authentication services ### # Get the style of auth credentials passed, defaults to access/secret key style @hp_use_upass_auth_style = options[:hp_use_upass_auth_style] || false - @hp_account_id = options[:hp_account_id] + @hp_access_key = options[:hp_access_key] @hp_secret_key = options[:hp_secret_key] @hp_tenant_id = options[:hp_tenant_id] @hp_service_type = options[:hp_service_type] - @hp_avl_zone = options[:hp_avl_zone] || :az1 + @hp_avl_zone = options[:hp_avl_zone] ### Decide which auth style to use unless (@hp_use_upass_auth_style) @@ -118,7 +136,7 @@ module Fog request_body = { 'auth' => { 'apiAccessKeyCredentials' => { - 'accessKey' => "#{@hp_account_id}", + 'accessKey' => "#{@hp_access_key}", 'secretKey' => "#{@hp_secret_key}" } } @@ -128,17 +146,17 @@ module Fog request_body = { 'auth' => { 'passwordCredentials' => { - 'username' => "#{@hp_account_id}", + 'username' => "#{@hp_access_key}", 'password' => "#{@hp_secret_key}" } } } end # add tenant_id if specified - request_body['auth']['tenantId'] = "#{@hp_tenant_id}" if @hp_tenant_id + request_body['auth']['tenantId'] = @hp_tenant_id if @hp_tenant_id ### Make the call to CS to get auth token and service catalog - response = service.request( + response = connection.request( { :expects => 200, :headers => { @@ -157,9 +175,9 @@ module Fog ### fish out auth_token and endpoint for the service auth_token = body['access']['token']['id'] endpoint_url = get_endpoint_from_catalog(body['access']['serviceCatalog'], @hp_service_type, @hp_avl_zone) - # If service is Storage, then get the CDN endpoint as well - if @hp_service_type == "object-store" - cdn_endpoint_url = get_endpoint_from_catalog(body['access']['serviceCatalog'], "hpext:cdn", @hp_avl_zone) + # If service is Storage, then get the CDN endpoint as well. 'Name' is unique instead of 'Type' + if @hp_service_type == "Object Storage" + cdn_endpoint_url = get_endpoint_from_catalog(body['access']['serviceCatalog'], "CDN", @hp_avl_zone) end return { @@ -180,19 +198,31 @@ module Fog private def self.get_endpoint_from_catalog(service_catalog, service_type, avl_zone) - if service_catalog - service_item = service_catalog.select {|s| s["type"] == service_type}.first - if service_item and service_item['endpoints'] and - if avl_zone == :az1 - endpoint_url = service_item['endpoints'][0]['publicURL'] if service_item['endpoints'][0] - elsif avl_zone == :az2 - endpoint_url = service_item['endpoints'][1]['publicURL'] if service_item['endpoints'][1] - end - raise "Unable to retrieve endpoint service url from service catalog." if endpoint_url.nil? - return endpoint_url + raise "Unable to parse service catalog." unless service_catalog + service_item = service_catalog.detect do |s| + # 'Name' is unique instead of 'Type' + s["name"] == service_type + end + if service_item and service_item['endpoints'] + endpoint = service_item['endpoints'].detect do |ep| + ep['region'] == avl_zone end + endpoint_url = endpoint['publicURL'] if endpoint + raise "Unable to retrieve endpoint service url for availability zone '#{avl_zone}' from service catalog. " if endpoint_url.nil? + return endpoint_url + end + end + + def self.set_user_agent_header(conn_opts, base_str, client_str) + if client_str + user_agent = {'User-Agent' => base_str + " (#{client_str})"} else - raise "Unable to parse service catalog." + user_agent = {'User-Agent' => base_str} + end + if conn_opts[:headers] + conn_opts[:headers] = user_agent.merge!(conn_opts[:headers]) + else + conn_opts[:headers] = user_agent end end diff --git a/lib/fog/hp/CHANGELOG.hp b/lib/fog/hp/CHANGELOG.hp new file mode 100644 index 000000000..6f188b6b8 --- /dev/null +++ b/lib/fog/hp/CHANGELOG.hp @@ -0,0 +1,113 @@ +0.0.19 18/01/2013 +================= +- update Block Storage namespace to be Fog::HP::BlockStorage +- merge with upstream fog v1.8.0 +- deprecate hp_account_id to use hp_access_key instead + +0.0.18 12/04/2012 +================= +- add support for controlled access via cross-tenant ACLs +- add support for creating bootable volumes +- add support for creating persistent server instances using bootable volumes + +0.0.17 10/16/2012 +================= +- add support for generating temporary urls +- add support for retrieving encrypted password for Windows instances +- add support for creating Windows instances + +0.0.16 08/15/2012 +================= +- add support for User-Agent string in header +- add support for Snapshots API to BlockStorage provider +- add support for Volumes API to BlockStorage provider +- add BlockStorage service to HP provider +- upgrade to excon 0.14.0 to take advantage of instrumentation +- add a head method for the directories collection +- update image model +- update put_object and get_object to accept blocks + +0.0.15 06/08/2012 +================= +- add metadata attribute in server and image models to manage metadata +- add metadata model and request layer functionality with mocking support +- add tests for metadata +- upgrade to excon 0.13.0 to take advantage of ssl passthru functionality +- add security_groups attribute to the server model +- add support for future availability zones for all HP services +- change hp_avl_zone to be a required parameter for all HP services +- add support for console output in compute service +- add helper method for cdn public ssl url +- add tests for storage, compute and cdn services +- improve quality of mocks to match real implementation + +0.0.14 02/23/2012 +================= + - minor fix for public ip address + - minor fix for exceptions raised by CDN service + - add keypair get/set methods to server model + +0.0.13 02/09/2012 +================= + - provide access to availability zones for compute + - minor fix for CS authentication uri + - update inline documentation + - update README_HP with copyright message + +0.0.12 02/02/2012 +================= + - integrate CS authentication + - refactor HP provider to work with legacy v1 authentication as well + - integrate CDN to work with Storage provider + - add additional attributes for CDN provider + - remove bits and add cores as alias to vcpus in flavor model + - update inline documentation + - update README_HP with copyright message + +0.0.11 01/09/2012 +================= + - add ability to customize various connection timeouts options + - add CDN service capabilities + - integrate the CDN service with the Storage service + - add a new CDN Uri option to Storage service + - incorporate Diablo 4 API changes + - update image, server and flavor models + - update create_image and create_server calls + - add rebuild_server and change_password_server calls + - new extensions APIs + - add keypair model and request layer with mocking support + - add security group model and request layer with mocking support + - add security group rule model and request layer with mocking support + - add address model and request layer with mocking support + - update server model with address management methods + - update server creation to use keypairs, security groups and other options + - update inline documentation + +0.0.10 10/18/2011 +================= + - merge with upstream fog v1.0.0 + +0.0.9 09/21/2011 +================ + - minor bug fixes + - add support for special char. including '?' in container and object names + - fix public_url for special char. in container and object names + +0.0.8 08/29/2011 +================ + - add README_HP.rdoc + - add CHANGELOG.hp + - remove resize_server, confirm_resized_server and revert_resized_server from HP Cloud Compute + +0.0.7 08/10/2011 +================ + - minor fixes + - added acl support to directory + - added public? method to directory model + - added mock support for HP Cloud Object Storage + +0.0.6 07/26/2011 +================ + - initial release with name change to 'hpfog' + - HP provider for HP Cloud Object Storage + - HP provider for HP Cloud Compute diff --git a/lib/fog/hp/README_HP.rdoc b/lib/fog/hp/README_HP.rdoc index 190965400..4084e876b 100644 --- a/lib/fog/hp/README_HP.rdoc +++ b/lib/fog/hp/README_HP.rdoc @@ -29,11 +29,14 @@ If you should ever need to remove the library: The Ruby Fog library is well documented on the community web site at {fog.io}[http://fog.io], but for specific examples using HP's extensions, see: -* {HP Cloud Fog library - Object Storage Examples}[https://build.hpcloud.com/bindings/fog/object-storage] -* {HP Cloud Fog library - Compute Examples}[https://build.hpcloud.com/bindings/fog/compute] -* {HP Cloud Fog library - CDN Examples}[https://build.hpcloud.com/bindings/fog/cdn] +* {HP Cloud Fog library - Object Storage Examples}[https://docs.hpcloud.com/bindings/fog/object-storage] +* {HP Cloud Fog library - Compute Examples}[https://docs.hpcloud.com/bindings/fog/compute] +* {HP Cloud Fog library - CDN Examples}[https://docs.hpcloud.com/bindings/fog/cdn] +* {HP Cloud Fog library - Block Storage Examples}[https://docs.hpcloud.com/bindings/fog/block-storage] + +Having trouble? Get help over at the {Forums}[https://community.hpcloud.com/forum] or +at the {Knowledge Base}[https://community.hpcloud.com/knowledge-base] areas. -Having trouble? {Get help over at the Forums}[https://connect.hpcloud.com] == Notice @@ -56,5 +59,3 @@ PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - diff --git a/lib/fog/hp/block_storage.rb b/lib/fog/hp/block_storage.rb new file mode 100644 index 000000000..530251772 --- /dev/null +++ b/lib/fog/hp/block_storage.rb @@ -0,0 +1,169 @@ +require 'fog/hp' + +module Fog + module HP + class BlockStorage < Fog::Service + + requires :hp_secret_key, :hp_tenant_id, :hp_avl_zone + recognizes :hp_auth_uri + recognizes :persistent, :connection_options + recognizes :hp_use_upass_auth_style, :hp_auth_version, :user_agent + recognizes :hp_access_key, :hp_account_id # :hp_account_id is deprecated use hp_access_key instead + + secrets :hp_secret_key + + model_path 'fog/hp/models/block_storage' + model :volume + collection :volumes + collection :bootable_volumes + + model :snapshot + collection :snapshots + + request_path 'fog/hp/requests/block_storage' + request :create_volume + request :delete_volume + request :get_bootable_volume_details + request :get_volume_details + request :list_bootable_volumes + request :list_volumes + + request :create_snapshot + request :delete_snapshot + request :get_snapshot_details + request :list_snapshots + + module Utils + + def compute + @compute ||= Fog::Compute.new( + :provider => 'HP', + :hp_access_key => @hp_access_key, + :hp_secret_key => @hp_secret_key, + :hp_auth_uri => @hp_auth_uri, + :hp_tenant_id => @hp_tenant_id, + :hp_avl_zone => @hp_avl_zone, + :connection_options => @connection_options + ) + end + + end + + class Mock + include Utils + + def self.data + @data ||= Hash.new do |hash, key| + hash[key] = { + :volumes => {}, + :snapshots => {} + } + end + end + + def self.reset + @data = nil + end + + def initialize(options={}) + # deprecate hp_account_id + if options[:hp_account_id] + Fog::Logger.deprecation(":hp_account_id is deprecated, please use :hp_access_key instead.") + @hp_access_key = options.delete(:hp_account_id) + end + @hp_access_key = options[:hp_access_key] + unless @hp_access_key + raise ArgumentError.new("Missing required arguments: hp_access_key. :hp_account_id is deprecated, please use :hp_access_key instead.") + end + end + + def data + self.class.data[@hp_access_key] + end + + def reset_data + self.class.data.delete(@hp_access_key) + end + + end + + class Real + include Utils + + def initialize(options={}) + # deprecate hp_account_id + if options[:hp_account_id] + Fog::Logger.deprecation(":hp_account_id is deprecated, please use :hp_access_key instead.") + options[:hp_access_key] = options.delete(:hp_account_id) + end + @hp_access_key = options[:hp_access_key] + unless @hp_access_key + raise ArgumentError.new("Missing required arguments: hp_access_key. :hp_account_id is deprecated, please use :hp_access_key instead.") + end + @hp_secret_key = options[:hp_secret_key] + @hp_auth_uri = options[:hp_auth_uri] + @connection_options = options[:connection_options] || {} + ### Set an option to use the style of authentication desired; :v1 or :v2 (default) + auth_version = options[:hp_auth_version] || :v2 + ### Pass the service name for object storage to the authentication call + options[:hp_service_type] = "Block Storage" + @hp_tenant_id = options[:hp_tenant_id] + @hp_avl_zone = options[:hp_avl_zone] + + ### Make the authentication call + if (auth_version == :v2) + # Call the control services authentication + credentials = Fog::HP.authenticate_v2(options, @connection_options) + # the CS service catalog returns the block storage endpoint + @hp_block_uri = credentials[:endpoint_url] + else + # Call the legacy v1.0/v1.1 authentication + credentials = Fog::HP.authenticate_v1(options, @connection_options) + # the user sends in the block storage endpoint + @hp_block_uri = options[:hp_auth_uri] + end + + @auth_token = credentials[:auth_token] + @persistent = options[:persistent] || false + + uri = URI.parse(@hp_block_uri) + @host = uri.host + @path = uri.path + @port = uri.port + @scheme = uri.scheme + + @connection = Fog::Connection.new("#{@scheme}://#{@host}:#{@port}", @persistent, @connection_options) + end + + def reload + @connection.reset + end + + def request(params, parse_json = true, &block) + begin + response = @connection.request(params.merge!({ + :headers => { + 'Content-Type' => 'application/json', + 'X-Auth-Token' => @auth_token + }.merge!(params[:headers] || {}), + :host => @host, + :path => "#{@path}/#{params[:path]}", + }), &block) + rescue Excon::Errors::HTTPStatusError => error + raise case error + when Excon::Errors::NotFound + Fog::HP::BlockStorage::NotFound.slurp(error) + else + error + end + end + if !response.body.empty? && parse_json && response.headers['Content-Type'] =~ %r{application/json} + response.body = Fog::JSON.decode(response.body) + end + response + end + + end + end + end +end diff --git a/lib/fog/hp/cdn.rb b/lib/fog/hp/cdn.rb index 85ca0cb39..16473610c 100644 --- a/lib/fog/hp/cdn.rb +++ b/lib/fog/hp/cdn.rb @@ -5,8 +5,11 @@ module Fog module CDN class HP < Fog::Service - requires :hp_secret_key, :hp_account_id, :hp_tenant_id - recognizes :hp_auth_uri, :hp_cdn_uri, :persistent, :connection_options, :hp_use_upass_auth_style, :hp_auth_version + requires :hp_secret_key, :hp_tenant_id, :hp_avl_zone + recognizes :hp_auth_uri, :hp_cdn_uri + recognizes :hp_use_upass_auth_style, :hp_auth_version, :user_agent + recognizes :persistent, :connection_options + recognizes :hp_access_key, :hp_account_id # :hp_account_id is deprecated use hp_access_key instead secrets :hp_secret_key @@ -39,15 +42,23 @@ module Fog end def initialize(options={}) - @hp_account_id = options[:hp_account_id] + # deprecate hp_account_id + if options[:hp_account_id] + Fog::Logger.deprecation(":hp_account_id is deprecated, please use :hp_access_key instead.") + @hp_access_key = options.delete(:hp_account_id) + end + @hp_access_key = options[:hp_access_key] + unless @hp_access_key + raise ArgumentError.new("Missing required arguments: hp_access_key. :hp_account_id is deprecated, please use :hp_access_key instead.") + end end def data - self.class.data[@hp_account_id] + self.class.data[@hp_access_key] end def reset_data - self.class.data.delete(@hp_account_id) + self.class.data.delete(@hp_access_key) end end @@ -56,11 +67,22 @@ module Fog include Utils def initialize(options={}) + # deprecate hp_account_id + if options[:hp_account_id] + Fog::Logger.deprecation(":hp_account_id is deprecated, please use :hp_access_key instead.") + options[:hp_access_key] = options.delete(:hp_account_id) + end + @hp_access_key = options[:hp_access_key] + unless @hp_access_key + raise ArgumentError.new("Missing required arguments: hp_access_key. :hp_account_id is deprecated, please use :hp_access_key instead.") + end + @hp_secret_key = options[:hp_secret_key] @connection_options = options[:connection_options] || {} ### Set an option to use the style of authentication desired; :v1 or :v2 (default) auth_version = options[:hp_auth_version] || :v2 - ### Pass the service type for object storage to the authentication call - options[:hp_service_type] = "hpext:cdn" + ### Pass the service name for object storage to the authentication call + options[:hp_service_type] = "CDN" + @hp_tenant_id = options[:hp_tenant_id] ### Make the authentication call if (auth_version == :v2) diff --git a/lib/fog/hp/compute.rb b/lib/fog/hp/compute.rb index f0526be83..312c1e1af 100644 --- a/lib/fog/hp/compute.rb +++ b/lib/fog/hp/compute.rb @@ -5,8 +5,11 @@ module Fog module Compute class HP < Fog::Service - requires :hp_secret_key, :hp_account_id, :hp_tenant_id - recognizes :hp_auth_uri, :hp_servicenet, :persistent, :connection_options, :hp_use_upass_auth_style, :hp_auth_version, :hp_avl_zone + requires :hp_secret_key, :hp_tenant_id, :hp_avl_zone + recognizes :hp_auth_uri + recognizes :hp_use_upass_auth_style, :hp_auth_version, :user_agent + recognizes :persistent, :connection_options + recognizes :hp_access_key, :hp_account_id # :hp_account_id is deprecated use hp_access_key instead secrets :hp_secret_key @@ -19,6 +22,8 @@ module Fog collection :images model :key_pair collection :key_pairs + model :meta + collection :metadata model :security_group collection :security_groups model :server @@ -27,6 +32,7 @@ module Fog request_path 'fog/hp/requests/compute' request :allocate_address request :associate_address + request :attach_volume request :change_password_server #request :confirm_resized_server request :create_image @@ -34,15 +40,21 @@ module Fog request :create_security_group request :create_security_group_rule request :create_server + request :create_persistent_server request :delete_image request :delete_key_pair + request :delete_meta request :delete_security_group request :delete_security_group_rule request :delete_server + request :detach_volume request :disassociate_address request :get_address + request :get_console_output request :get_flavor_details request :get_image_details + request :get_meta + request :get_windows_password request :get_security_group request :get_server_details request :list_addresses @@ -51,10 +63,12 @@ module Fog request :list_images request :list_images_detail request :list_key_pairs + request :list_metadata request :list_security_groups request :list_server_addresses request :list_server_private_addresses request :list_server_public_addresses + request :list_server_volumes request :list_servers request :list_servers_detail request :reboot_server @@ -63,9 +77,55 @@ module Fog #request :resize_server #request :revert_resized_server request :server_action + request :set_metadata + request :update_meta + request :update_metadata request :update_server + module Utils + + # extract windows password from log + def extract_password_from_log(log_text) + encrypted_text = "" + section = [] + return if log_text.nil? + log_text.each_line do |line| + case line + when /^-----BEGIN (\w+)/ + section.push $1 + next + when /^-----END (\w+)/ + section.pop + next + end + + case section + when ["BASE64"] + encrypted_text << line + end + end + # return the encrypted portion only + encrypted_text + end + + def encrypt_using_public_key(text, public_key_data) + return if (text.nil? || public_key_data.nil?) + public_key = OpenSSL::PKey::RSA.new(public_key_data) + encrypted_text = public_key.public_encrypt(text).strip + Base64.encode64(encrypted_text) + end + + def decrypt_using_private_key(encrypted_text, private_key_data) + return if (encrypted_text.nil? || private_key_data.nil?) + private_key = OpenSSL::PKey::RSA.new(private_key_data) + from_base64 = Base64.decode64(encrypted_text) + private_key.private_decrypt(from_base64).strip + end + + end + class Mock + include Utils def self.data @data ||= Hash.new do |hash, key| @@ -91,30 +151,46 @@ module Fog end def initialize(options={}) - @hp_account_id = options[:hp_account_id] + # deprecate hp_account_id + if options[:hp_account_id] + Fog::Logger.deprecation(":hp_account_id is deprecated, please use :hp_access_key instead.") + @hp_access_key = options.delete(:hp_account_id) + end + @hp_access_key = options[:hp_access_key] + unless @hp_access_key + raise ArgumentError.new("Missing required arguments: hp_access_key. :hp_account_id is deprecated, please use :hp_access_key instead.") + end end def data - self.class.data[@hp_account_id] + self.class.data[@hp_access_key] end def reset_data - self.class.data.delete(@hp_account_id) + self.class.data.delete(@hp_access_key) end end class Real + include Utils def initialize(options={}) + # deprecate hp_account_id + if options[:hp_account_id] + Fog::Logger.deprecation(":hp_account_id is deprecated, please use :hp_access_key instead.") + options[:hp_access_key] = options.delete(:hp_account_id) + end + @hp_access_key = options[:hp_access_key] + unless @hp_access_key + raise ArgumentError.new("Missing required arguments: hp_access_key. :hp_account_id is deprecated, please use :hp_access_key instead.") + end @hp_secret_key = options[:hp_secret_key] - @hp_account_id = options[:hp_account_id] - @hp_servicenet = options[:hp_servicenet] @connection_options = options[:connection_options] || {} ### Set an option to use the style of authentication desired; :v1 or :v2 (default) auth_version = options[:hp_auth_version] || :v2 - ### Pass the service type for compute via the options hash - options[:hp_service_type] = "compute" + ### Pass the service name for compute via the options hash + options[:hp_service_type] = "Compute" @hp_tenant_id = options[:hp_tenant_id] ### Make the authentication call @@ -133,12 +209,11 @@ module Fog @auth_token = credentials[:auth_token] uri = URI.parse(@hp_compute_uri) - @host = @hp_servicenet == true ? "snet-#{uri.host}" : uri.host + @host = uri.host @path = uri.path @persistent = options[:persistent] || false @port = uri.port @scheme = uri.scheme - Excon.ssl_verify_peer = false if options[:hp_servicenet] == true @connection = Fog::Connection.new("#{@scheme}://#{@host}:#{@port}", @persistent, @connection_options) end @@ -166,12 +241,8 @@ module Fog error end end - unless response.body.empty? - begin - response.body = Fog::JSON.decode(response.body) - rescue MultiJson::DecodeError => error - response.body #### the body is not in JSON format so just return it as it is - end + if !response.body.empty? && parse_json && response.headers['Content-Type'] =~ %r{application/json} + response.body = Fog::JSON.decode(response.body) end response end diff --git a/lib/fog/hp/models/block_storage/bootable_volumes.rb b/lib/fog/hp/models/block_storage/bootable_volumes.rb new file mode 100644 index 000000000..f40eb170b --- /dev/null +++ b/lib/fog/hp/models/block_storage/bootable_volumes.rb @@ -0,0 +1,28 @@ +require 'fog/core/collection' +require 'fog/hp/models/block_storage/volume' + +module Fog + module HP + class BlockStorage + + class BootableVolumes < Fog::Collection + + model Fog::HP::BlockStorage::Volume + + def all + data = service.list_bootable_volumes.body['volumes'] + load(data) + end + + def get(volume_id) + volume = service.get_bootable_volume_details(volume_id).body['volume'] + new(volume) + rescue Fog::HP::BlockStorage::NotFound + nil + end + + end + + end + end +end diff --git a/lib/fog/hp/models/block_storage/snapshot.rb b/lib/fog/hp/models/block_storage/snapshot.rb new file mode 100644 index 000000000..39c4dcfdc --- /dev/null +++ b/lib/fog/hp/models/block_storage/snapshot.rb @@ -0,0 +1,56 @@ +require 'fog/core/model' + +module Fog + module HP + class BlockStorage + + class Snapshot < Fog::Model + + identity :id + + attribute :name, :aliases => 'displayName' + attribute :description, :aliases => 'displayDescription' + attribute :size + attribute :status + attribute :created_at, :aliases => 'createdAt' + attribute :volume_id, :aliases => 'volumeId' + #attribute :metadata + + def initialize(attributes = {}) + # assign these attributes first to prevent race condition with persisted? + self.force = attributes.delete(:force) # force snapshotting of attached volumes + super + end + + def destroy + requires :id + service.delete_snapshot(id) + true + end + + def force=(new_force) + @force = new_force + end + + def ready? + self.status == 'available' + end + + def save + raise Fog::Errors::Error.new('Resaving an existing object may create a duplicate') if persisted? + requires :name, :volume_id + options = { + #'metadata' => metadata, # TODO: Add metadata when snapshots support it + 'force' => @force + } + options = options.reject {|key, value| value.nil?} + data = service.create_snapshot(name, description, volume_id, options) + merge_attributes(data.body['snapshot']) + true + end + + end + + end + end +end diff --git a/lib/fog/hp/models/block_storage/snapshots.rb b/lib/fog/hp/models/block_storage/snapshots.rb new file mode 100644 index 000000000..c0a7135d5 --- /dev/null +++ b/lib/fog/hp/models/block_storage/snapshots.rb @@ -0,0 +1,29 @@ +require 'fog/core/collection' +require 'fog/hp/models/block_storage/snapshot' + +module Fog + module HP + class BlockStorage + + class Snapshots < Fog::Collection + + model Fog::HP::BlockStorage::Snapshot + + def all + data = service.list_snapshots.body['snapshots'] + load(data) + end + + def get(snapshot_id) + if snapshot = service.get_snapshot_details(snapshot_id).body['snapshot'] + new(snapshot) + end + rescue Fog::HP::BlockStorage::NotFound + nil + end + + end + + end + end +end diff --git a/lib/fog/hp/models/block_storage/volume.rb b/lib/fog/hp/models/block_storage/volume.rb new file mode 100644 index 000000000..88a342704 --- /dev/null +++ b/lib/fog/hp/models/block_storage/volume.rb @@ -0,0 +1,102 @@ +require 'fog/core/model' + +module Fog + module HP + class BlockStorage + + class Volume < Fog::Model + + identity :id + + attribute :name, :aliases => 'displayName' + attribute :description, :aliases => 'displayDescription' + attribute :size + attribute :status + attribute :type, :aliases => 'volumeType' + attribute :created_at, :aliases => 'createdAt' + attribute :availability_zone, :aliases => 'availabilityZone' + attribute :snapshot_id, :aliases => 'snapshotId' + attribute :source_image_id, :aliases => 'sourceImageRef' # only for bootable volumes + attribute :attachments + attribute :metadata + + attr_reader :server_id + attr_reader :device + + def initialize(attributes = {}) + # assign these attributes first to prevent race condition with new_record? + self.image_id = attributes.delete(:image_id) + super + end + + def device + attachments[0]['device'] if has_attachments? + end + + def server_id + attachments[0]['serverId'] if has_attachments? + end + + # used for creating bootable volumes + def image_id=(new_image_id) + @image_id = new_image_id + end + + # a volume can be attached to only one server at a time + def has_attachments? + !(attachments.nil? || attachments.empty? || attachments[0].empty?) + end + + def in_use? + self.status == 'in-use' + end + + def ready? + self.status == 'available' + end + + # volume can be attached to only one server at a time + def attach(new_server_id, device) + requires :id + unless in_use? + data = service.compute.attach_volume(new_server_id, id, device) + merge_attributes(:attachments => attachments << data.body['volumeAttachment']) + true + else + false + end + end + + def detach + requires :id + if has_attachments? + service.compute.detach_volume(server_id, id) + end + true + end + + def destroy + requires :id + service.delete_volume(id) + true + end + + def save + raise Fog::Errors::Error.new('Resaving an existing object may create a duplicate') if persisted? + requires :name, :size + options = { + 'metadata' => metadata, + 'snapshot_id' => snapshot_id, + 'imageRef' => @image_id + } + options = options.reject {|key, value| value.nil?} + data = service.create_volume(name, description, size, options) + merge_attributes(data.body['volume']) + true + end + + end + + end + end +end diff --git a/lib/fog/hp/models/block_storage/volumes.rb b/lib/fog/hp/models/block_storage/volumes.rb new file mode 100644 index 000000000..a9db39783 --- /dev/null +++ b/lib/fog/hp/models/block_storage/volumes.rb @@ -0,0 +1,28 @@ +require 'fog/core/collection' +require 'fog/hp/models/block_storage/volume' + +module Fog + module HP + class BlockStorage + + class Volumes < Fog::Collection + + model Fog::HP::BlockStorage::Volume + + def all + data = service.list_volumes.body['volumes'] + load(data) + end + + def get(volume_id) + volume = service.get_volume_details(volume_id).body['volume'] + new(volume) + rescue Fog::HP::BlockStorage::NotFound + nil + end + + end + + end + end +end diff --git a/lib/fog/hp/models/compute/address.rb b/lib/fog/hp/models/compute/address.rb index ec843e6df..d8d9156cf 100644 --- a/lib/fog/hp/models/compute/address.rb +++ b/lib/fog/hp/models/compute/address.rb @@ -20,7 +20,6 @@ module Fog def destroy requires :id - service.release_address(id) true end diff --git a/lib/fog/hp/models/compute/image.rb b/lib/fog/hp/models/compute/image.rb index 31923f5f1..af556da10 100644 --- a/lib/fog/hp/models/compute/image.rb +++ b/lib/fog/hp/models/compute/image.rb @@ -1,4 +1,5 @@ require 'fog/core/model' +require 'fog/openstack/models/compute/metadata' module Fog module Compute @@ -13,15 +14,53 @@ module Fog attribute :updated_at, :aliases => 'updated' attribute :progress attribute :status - attribute :minDisk, :aliases => 'min_disk' - attribute :minRam, :aliases => 'min_ram' - attribute :server, :aliases => 'server' + attribute :server attribute :metadata attribute :links + # these values are extracted from metadata + attr_reader :min_disk + attr_reader :min_ram + attr_reader :image_type + attr_reader :architecture + + def metadata + @metadata ||= begin + Fog::Compute::HP::Metadata.new({ + :service => service, + :parent => self + }) + end + end + + def metadata=(new_metadata={}) + metas = [] + new_metadata.each_pair {|k,v| metas << {"key" => k, "value" => v} } + metadata.load(metas) + end + + def min_disk + m = metadata.get("min_disk") + m.value unless m.nil? + end + + def min_ram + m = metadata.get("min_ram") + m.value unless m.nil? + end + + def image_type + m = metadata.get("image_type") + m.value unless m.nil? + end + + def architecture + m = metadata.get("architecture") + m.value unless m.nil? + end + def destroy requires :id - service.delete_image(id) true end diff --git a/lib/fog/hp/models/compute/images.rb b/lib/fog/hp/models/compute/images.rb index 66e21ab6f..9ce22f14d 100644 --- a/lib/fog/hp/models/compute/images.rb +++ b/lib/fog/hp/models/compute/images.rb @@ -9,14 +9,9 @@ module Fog model Fog::Compute::HP::Image - attribute :server - def all data = service.list_images_detail.body['images'] load(data) - if server - self.replace(self.select {|image| image.server.id == server.id}) - end self end diff --git a/lib/fog/hp/models/compute/key_pair.rb b/lib/fog/hp/models/compute/key_pair.rb index eb7e994d5..95204cfd9 100644 --- a/lib/fog/hp/models/compute/key_pair.rb +++ b/lib/fog/hp/models/compute/key_pair.rb @@ -13,18 +13,14 @@ module Fog attribute :private_key attribute :user_id - attr_accessor :public_key - def destroy requires :name - service.delete_key_pair(name) true end def save requires :name - data = if public_key service.create_key_pair(name, public_key).body['keypair'] else @@ -36,7 +32,6 @@ module Fog end def write(path="#{ENV['HOME']}/.ssh/hp_#{Fog.credential.to_s}_#{name}.pem") - if writable? split_private_key = private_key.split(/\n/) File.open(path, "w") do |f| diff --git a/lib/fog/hp/models/compute/meta.rb b/lib/fog/hp/models/compute/meta.rb new file mode 100644 index 000000000..df8f7f445 --- /dev/null +++ b/lib/fog/hp/models/compute/meta.rb @@ -0,0 +1,29 @@ +require 'fog/core/model' +require 'fog/hp/models/meta_parent' + +module Fog + module Compute + class HP + class Meta < Fog::Model + + include Fog::Compute::HP::MetaParent + + identity :key + attribute :value + + def destroy + requires :identity + service.delete_meta(collection_name, @parent.id, key) + true + end + + def save + requires :identity, :value + service.update_meta(collection_name, @parent.id, key, value) + true + end + + end + end + end +end diff --git a/lib/fog/hp/models/compute/metadata.rb b/lib/fog/hp/models/compute/metadata.rb new file mode 100644 index 000000000..f7c911b57 --- /dev/null +++ b/lib/fog/hp/models/compute/metadata.rb @@ -0,0 +1,79 @@ +require 'fog/core/collection' +require 'fog/hp/models/meta_parent' +require 'fog/hp/models/compute/meta' +require 'fog/hp/models/compute/image' +require 'fog/hp/models/compute/server' + +module Fog + module Compute + class HP + + class Metadata < Fog::Collection + + model Fog::Compute::HP::Meta + + include Fog::Compute::HP::MetaParent + + def all + requires :parent + if @parent.id + metadata = service.list_metadata(collection_name, @parent.id).body['metadata'] + metas = [] + metadata.each_pair {|k,v| metas << {"key" => k, "value" => v} } + load(metas) + end + end + + def destroy(key) + requires :parent + service.delete_meta(collection_name, @parent.id, key) + rescue Fog::Compute::HP::NotFound + nil + end + + def get(key) + requires :parent + data = service.get_meta(collection_name, @parent.id, key).body["meta"] + metas = [] + data.each_pair {|k,v| metas << {"key" => k, "value" => v} } + new(metas[0]) + rescue Fog::Compute::HP::NotFound + nil + end + + def new(attributes = {}) + requires :parent + super({ :parent => @parent }.merge!(attributes)) + end + + def set(data=nil) + requires :parent + service.set_metadata(collection_name, @parent.id, meta_hash(data)) + end + + def update(data=nil) + requires :parent + service.update_metadata(collection_name, @parent.id, meta_hash(data)) + end + + + private + def meta_hash(data=nil) + if data.nil? + data={} + self.each do |meta| + if meta.is_a?(Fog::Compute::HP::Meta) then + data.store(meta.key, meta.value) + else + data.store(meta["key"], meta["value"]) + end + end + end + data + end + + end + + end + end +end diff --git a/lib/fog/hp/models/compute/server.rb b/lib/fog/hp/models/compute/server.rb index 0771d1a96..bce6828b4 100644 --- a/lib/fog/hp/models/compute/server.rb +++ b/lib/fog/hp/models/compute/server.rb @@ -1,4 +1,5 @@ require 'fog/compute/models/server' +require 'fog/hp/models/compute/metadata' module Fog module Compute @@ -24,6 +25,8 @@ module Fog attribute :tenant_id attribute :user_id attribute :key_name + attribute :security_groups + attribute :config_drive # these are implemented as methods attribute :image_id attribute :flavor_id @@ -31,27 +34,44 @@ module Fog attribute :public_ip_address attr_reader :password - attr_writer :image_id, :flavor_id + attr_writer :private_key, :private_key_path + attr_writer :public_key, :public_key_path + attr_writer :username, :image_id, :flavor_id, :network_name def initialize(attributes = {}) - # assign these attributes first to prevent race condition with persisted? - self.security_groups = attributes.delete(:security_groups) + # assign these attributes first to prevent race condition with new_record? self.min_count = attributes.delete(:min_count) self.max_count = attributes.delete(:max_count) + self.block_device_mapping = attributes.delete(:block_device_mapping) super end + def console_output(num_lines) + requires :id + service.get_console_output(id, num_lines) + end + + def metadata + @metadata ||= begin + Fog::Compute::HP::Metadata.new({ + :service => service, + :parent => self + }) + end + end + + def metadata=(new_metadata={}) + metas = [] + new_metadata.each_pair {|k,v| metas << {"key" => k, "value" => v} } + metadata.load(metas) + end + def destroy requires :id service.delete_server(id) true end - def images - requires :id - service.images(:server => self) - end - def key_pair requires :key_name @@ -62,16 +82,29 @@ module Fog self.key_name = new_keypair && new_keypair.name end + def network_name + @network_name ||= "private" + end + def private_ip_address - addr = addresses.nil? ? nil : addresses.fetch('private', []).first + addr = addresses.nil? ? nil : addresses.fetch(network_name, []).first addr["addr"] if addr end + def private_key_path + @private_key_path ||= Fog.credentials[:private_key_path] + @private_key_path &&= File.expand_path(@private_key_path) + end + + def private_key + @private_key ||= private_key_path && File.read(private_key_path) + end + def public_ip_address # FIX: Both the private and public ips are bundled under "private" network name # So hack to get to the public ip address if !addresses.nil? - addr = addresses.fetch('private', []) + addr = addresses.fetch(network_name, []) # if we have more than 1 address, then the return the second address which is public if addr.count > 1 addr[1]["addr"] @@ -83,6 +116,15 @@ module Fog end end + def public_key_path + @public_key_path ||= Fog.credentials[:public_key_path] + @public_key_path &&= File.expand_path(@public_key_path) + end + + def public_key + @public_key ||= public_key_path && File.read(public_key_path) + end + def image_id @image_id ||= (image.nil? ? nil : image["id"]) end @@ -107,12 +149,8 @@ module Fog @max_count = new_max_count end - def security_groups=(new_security_groups) - @security_groups = new_security_groups - end - - def security_groups - @security_groups + def block_device_mapping=(new_block_device_mapping) + @block_device_mapping = new_block_device_mapping end def ready? @@ -125,6 +163,11 @@ module Fog true end + def windows_password() + requires :id + service.get_windows_password(id) + end + def reboot(type = 'SOFT') requires :id service.reboot_server(id, type) @@ -160,21 +203,38 @@ module Fog service.create_image(id, name, metadata) end + def volume_attachments + requires :id + if vols = service.list_server_volumes(id).body + vols["volumeAttachments"] + end + end + def save raise Fog::Errors::Error.new('Resaving an existing object may create a duplicate') if persisted? - requires :flavor_id, :image_id, :name + requires :flavor_id, :name + meta_hash = {} + metadata.each { |meta| meta_hash.store(meta.key, meta.value) } options = { - 'metadata' => metadata, + 'metadata' => meta_hash, 'personality' => personality, 'accessIPv4' => accessIPv4, 'accessIPv6' => accessIPv6, 'min_count' => @min_count, 'max_count' => @max_count, 'key_name' => key_name, - 'security_groups' => @security_groups + 'security_groups' => security_groups, + 'config_drive' => config_drive } options = options.reject {|key, value| value.nil?} - data = service.create_server(name, flavor_id, image_id, options) + # either create a regular server or a persistent server based on input + if image_id + # create a regular server using the image + data = service.create_server(name, flavor_id, image_id, options) + elsif image_id.nil? && !@block_device_mapping.nil? && !@block_device_mapping.empty? + # create a persistent server using the bootable volume in the block_device_mapping + data = service.create_persistent_server(name, flavor_id, @block_device_mapping, options) + end merge_attributes(data.body['server']) true end @@ -193,6 +253,10 @@ module Fog retry end + def username + @username ||= 'root' + end + private def adminPass=(new_admin_pass) diff --git a/lib/fog/hp/models/meta_parent.rb b/lib/fog/hp/models/meta_parent.rb new file mode 100644 index 000000000..108e59508 --- /dev/null +++ b/lib/fog/hp/models/meta_parent.rb @@ -0,0 +1,33 @@ +module Fog + module Compute + class HP + module MetaParent + + def parent + @parent + end + + def parent=(new_parent) + @parent = new_parent + end + + def collection_name + if @parent.class == Fog::Compute::HP::Image + return "images" + elsif @parent.class == Fog::Compute::HP::Server + return "servers" + else + raise "Metadata is not supported for this model type." + end + end + + def metas_to_hash(metas) + hash = {} + metas.each { |meta| hash.store(meta.key, meta.value) } + hash + end + + end + end + end +end diff --git a/lib/fog/hp/models/storage/directories.rb b/lib/fog/hp/models/storage/directories.rb index 6fa3bcad7..dd24961c3 100644 --- a/lib/fog/hp/models/storage/directories.rb +++ b/lib/fog/hp/models/storage/directories.rb @@ -14,10 +14,39 @@ module Fog load(data) end + def head(key, options = {}) + data = service.head_container(key) + directory = create_directory(key, data) + # set the cdn state for the directory + directory.cdn_enabled? + + directory + rescue Fog::Storage::HP::NotFound + nil + end + def get(key, options = {}) + data = service.get_container(key, options) + directory = create_directory(key, data) + # set the files for the directory + directory.files.merge_attributes(options) + directory.files.instance_variable_set(:@loaded, true) + data.body.each do |file| + directory.files << directory.files.new(file) + end + # set the cdn state for the directory + directory.cdn_enabled? + + directory + rescue Fog::Storage::HP::NotFound + nil + end + + private + + def create_directory(key, data) read_header = nil write_header = nil - data = service.get_container(key, options) directory = new(:key => key) for key, value in data.headers if ['X-Container-Bytes-Used', 'X-Container-Object-Count'].include?(key) @@ -31,16 +60,12 @@ module Fog end # set the acl on the directory based on the headers if !(read_header.nil? && write_header.nil?) - directory.acl = service.header_to_acl(read_header, write_header) - end - directory.files.merge_attributes(options) - directory.files.instance_variable_set(:@loaded, true) - data.body.each do |file| - directory.files << directory.files.new(file) + read_acl, write_acl = service.header_to_perm_acl(read_header, write_header) + # do not want to expose the read_acl and write_acl as writable attributes + directory.instance_variable_set(:@read_acl, read_acl) + directory.instance_variable_set(:@write_acl, write_acl) end directory - rescue Fog::Storage::HP::NotFound - nil end end diff --git a/lib/fog/hp/models/storage/directory.rb b/lib/fog/hp/models/storage/directory.rb index 313da36dd..b03977c77 100644 --- a/lib/fog/hp/models/storage/directory.rb +++ b/lib/fog/hp/models/storage/directory.rb @@ -12,15 +12,86 @@ module Fog attribute :bytes, :aliases => 'X-Container-Bytes-Used' attribute :count, :aliases => 'X-Container-Object-Count' - def acl=(new_acl) - if new_acl.nil? - new_acl = "private" + def initialize(attributes = {}) + @read_acl = [] + @write_acl = [] + super + end + + def read_acl + @read_acl || [] + end + + def write_acl + @write_acl || [] + end + + def can_read?(user) + return false if @read_acl.nil? + list_users_with_read.include?(user) + end + + def can_write?(user) + return false if @write_acl.nil? + list_users_with_write.include?(user) + end + + def can_read_write?(user) + can_read?(user) && can_write?(user) + end + + def list_users_with_read + users = [] + users = @read_acl.map {|acl| acl.split(':')[1]} unless @read_acl.nil? + return users + end + + def list_users_with_write + users = [] + users = @write_acl.map {|acl| acl.split(':')[1]} unless @write_acl.nil? + return users + end + + def grant(perm, users=nil) + # support passing in a list of users in a comma-separated list or as an Array + if users.is_a?(String) + user_list = users.split(',') + else + user_list = users end - valid_acls = ['private', 'public-read', 'public-write', 'public-read-write'] - unless valid_acls.include?(new_acl) - raise ArgumentError.new("acl must be one of [#{valid_acls.join(', ')}]") + r_acl, w_acl = service.perm_to_acl(perm, user_list) + unless r_acl.nil? || r_acl.empty? + @read_acl = [] if @read_acl.nil? + @read_acl = @read_acl + r_acl + @read_acl.uniq! end - @acl = new_acl + unless w_acl.nil? || w_acl.empty? + @write_acl = [] if @write_acl.nil? + @write_acl = @write_acl + w_acl + @write_acl.uniq! + end + true + end + + def revoke(perm, users=nil) + # support passing in a list of users in a comma-separated list or as an Array + if users.is_a?(String) + user_list = users.split(',') + else + user_list = users + end + r_acl, w_acl = service.perm_to_acl(perm, user_list) + unless r_acl.nil? || r_acl.empty? + @read_acl = [] if @read_acl.nil? + @read_acl = @read_acl - r_acl + @read_acl.uniq! + end + unless w_acl.nil? || w_acl.empty? + @write_acl = [] if @write_acl.nil? + @write_acl = @write_acl - w_acl + @write_acl.uniq! + end + true end def destroy @@ -50,18 +121,19 @@ module Fog def public=(new_public) if new_public - @acl = 'public-read' + self.grant("pr") else - @acl = 'private' + self.revoke("pr") end @public = new_public end def public? - if @acl.nil? - false + @read_acl = [] if @read_acl.nil? + if @read_acl.include?(".r:*") + true else - @acl == 'public-read' + false end end @@ -70,7 +142,7 @@ module Fog @public_url ||= begin begin response = service.head_container(key) # escape the key to cover for special char. in container names - url = "#{service.url}/#{Fog::HP.escape(key)}" + url = service.public_url(key) rescue Fog::Storage::HP::NotFound => err nil end @@ -96,7 +168,8 @@ module Fog else @cdn_enable = false end - rescue Fog::CDN::HP::NotFound => err + # If CDN endpoint is unreachable, a SocketError is raised + rescue Fog::CDN::HP::NotFound, Excon::Errors::SocketError @cdn_enable = false end else @@ -110,24 +183,32 @@ module Fog # return the CDN public url from the appropriate uri from the header begin response = service.cdn.head_container(key) if response.headers['X-Cdn-Enabled'] == 'True' - if service.hp_cdn_ssl == true - response.headers.fetch('X-Cdn-Ssl-Uri', nil) - else - response.headers.fetch('X-Cdn-Uri', nil) - end + response.headers.fetch('X-Cdn-Uri', nil) end - rescue Fog::CDN::HP::NotFound => err + rescue Fog::CDN::HP::NotFound nil end end end - def save + def cdn_public_ssl_url requires :key - options = {} - if @acl - options.merge!(service.acl_to_header(@acl)) + @cdn_public_ssl_url ||= begin + # return the CDN public ssl url from the appropriate uri from the header + begin response = service.cdn.head_container(key) + if response.headers['X-Cdn-Enabled'] == 'True' + response.headers.fetch('X-Cdn-Ssl-Uri', nil) + end + rescue Fog::CDN::HP::NotFound + nil + end end + end + + def save(options = {}) + requires :key + # write out the acls into the headers before save + options.merge!(service.perm_acl_to_header(@read_acl, @write_acl)) service.put_container(key, options) # Added an extra check to see if CDN is enabled for the container if (!service.cdn.nil? && service.cdn.enabled?) @@ -137,16 +218,20 @@ module Fog begin response = service.cdn.head_container(key) ### Deleting a container from CDN is much more expensive than flipping the bit to disable it service.cdn.post_container(key, {'X-CDN-Enabled' => 'True'}) - rescue Fog::CDN::HP::NotFound => err + rescue Fog::CDN::HP::NotFound service.cdn.put_container(key) + rescue Excon::Errors::SocketError + # means that the CDN endpoint is unreachable end else # check to make sure that the container exists. If yes, cdn disable it. begin response = service.cdn.head_container(key) ### Deleting a container from CDN is much more expensive than flipping the bit to disable it service.cdn.post_container(key, {'X-CDN-Enabled' => 'False'}) - rescue Fog::CDN::HP::NotFound => err + rescue Fog::CDN::HP::NotFound # just continue, as container is not cdn-enabled. + rescue Excon::Errors::SocketError + # means that the CDN endpoint is unreachable end end end diff --git a/lib/fog/hp/models/storage/file.rb b/lib/fog/hp/models/storage/file.rb index 2988ea7f0..4d1076d17 100644 --- a/lib/fog/hp/models/storage/file.rb +++ b/lib/fog/hp/models/storage/file.rb @@ -65,6 +65,16 @@ module Fog self.collection.get_cdn_url(self.key) end + def cdn_public_ssl_url + requires :key + self.collection.get_cdn_ssl_url(self.key) + end + + def temp_signed_url(expires_secs, method) + requires :directory, :key + service.get_object_temp_url(directory.key, key, expires_secs, method) + end + def save(options = {}) requires :body, :directory, :key options['Content-Type'] = content_type if content_type diff --git a/lib/fog/hp/models/storage/files.rb b/lib/fog/hp/models/storage/files.rb index 23f490512..46718124e 100644 --- a/lib/fog/hp/models/storage/files.rb +++ b/lib/fog/hp/models/storage/files.rb @@ -80,6 +80,14 @@ module Fog end end + def get_cdn_ssl_url(key) + requires :directory + if self.directory.cdn_public_ssl_url + # escape the key to cover for special char. in object names + "#{self.directory.cdn_public_ssl_url}/#{Fog::HP.escape(key)}" + end + end + def head(key, options = {}) requires :directory data = service.head_object(directory.key, key) diff --git a/lib/fog/hp/models/storage/shared_directories.rb b/lib/fog/hp/models/storage/shared_directories.rb new file mode 100644 index 000000000..c105b6807 --- /dev/null +++ b/lib/fog/hp/models/storage/shared_directories.rb @@ -0,0 +1,55 @@ +require 'fog/core/collection' +require 'fog/hp/models/storage/shared_directory' + +module Fog + module Storage + class HP + + class SharedDirectories < Fog::Collection + + model Fog::Storage::HP::SharedDirectory + + def all + nil + end + + def head(url) + data = service.head_shared_container(url) + shared_directory = new(:url => url) + for key, value in data.headers + if ['X-Container-Bytes-Used', 'X-Container-Object-Count'].include?(key) + shared_directory.merge_attributes(key => value) + end + end + + shared_directory + # throws exception Fog::HP::Errors::Forbidden if insufficient access + rescue Fog::Storage::HP::NotFound + nil + end + + def get(url) + data = service.get_shared_container(url) + shared_directory = new(:url => url) + for key, value in data.headers + if ['X-Container-Bytes-Used', 'X-Container-Object-Count'].include?(key) + shared_directory.merge_attributes(key => value) + end + end + # set the files for the directory + shared_directory.files.instance_variable_set(:@loaded, true) + data.body.each do |file| + shared_directory.files << shared_directory.files.new(file) + end + + shared_directory + # throws exception Fog::HP::Errors::Forbidden if insufficient access + rescue Fog::Storage::HP::NotFound + nil + end + + end + + end + end +end diff --git a/lib/fog/hp/models/storage/shared_directory.rb b/lib/fog/hp/models/storage/shared_directory.rb new file mode 100644 index 000000000..fed94c13e --- /dev/null +++ b/lib/fog/hp/models/storage/shared_directory.rb @@ -0,0 +1,39 @@ +require 'fog/core/model' +require 'fog/hp/models/storage/shared_files' + +module Fog + module Storage + class HP + + class SharedDirectory < Fog::Model + + identity :url + + attribute :bytes, :aliases => 'X-Container-Bytes-Used' + attribute :count, :aliases => 'X-Container-Object-Count' + + def files + @files ||= begin + Fog::Storage::HP::SharedFiles.new( + :shared_directory => self, + :service => service + ) + end + end + + def destroy + requires :url + # delete is not allowed + false + end + + def save(options = {}) + requires :url + # put is not allowed + false + end + end + + end + end +end diff --git a/lib/fog/hp/models/storage/shared_file.rb b/lib/fog/hp/models/storage/shared_file.rb new file mode 100644 index 000000000..a1e1beeeb --- /dev/null +++ b/lib/fog/hp/models/storage/shared_file.rb @@ -0,0 +1,67 @@ +require 'fog/core/model' + +module Fog + module Storage + class HP + + class SharedFile < Fog::Model + + identity :key, :aliases => 'name' + attribute :url + + attribute :content_length, :aliases => ['bytes', 'Content-Length'], :type => :integer + attribute :content_type, :aliases => ['content_type', 'Content-Type'] + attribute :etag, :aliases => ['hash', 'Etag'] + attribute :last_modified, :aliases => ['last_modified', 'Last-Modified'], :type => :time + + def url + "#{self.collection.shared_directory.url}/#{key}" + end + + def body + attributes[:body] ||= if last_modified + collection.get(identity).body + else + '' + end + end + + def body=(new_body) + attributes[:body] = new_body + end + + def destroy + requires :shared_directory, :key + service.delete_shared_object(self.url) + true + # throws exception Fog::HP::Errors::Forbidden if insufficient access + rescue Fog::Storage::HP::NotFound + false + end + + def shared_directory + @shared_directory + end + + def save(options = {}) + requires :shared_directory, :key + options['Content-Type'] = content_type if content_type + data = service.put_shared_object(shared_directory.url, key, body, options) + merge_attributes(data.headers) + self.content_length = Fog::Storage.get_body_size(body) + true + # throws exception Fog::HP::Errors::Forbidden if insufficient access + rescue Fog::Storage::HP::NotFound + false + end + + private + + def shared_directory=(new_shared_directory) + @shared_directory = new_shared_directory + end + end + + end + end +end diff --git a/lib/fog/hp/models/storage/shared_files.rb b/lib/fog/hp/models/storage/shared_files.rb new file mode 100644 index 000000000..da65392fe --- /dev/null +++ b/lib/fog/hp/models/storage/shared_files.rb @@ -0,0 +1,64 @@ +require 'fog/core/collection' +require 'fog/hp/models/storage/shared_file' + +module Fog + module Storage + class HP + + class SharedFiles < Fog::Collection + + attribute :shared_directory + + model Fog::Storage::HP::SharedFile + + def all + requires :shared_directory + parent = shared_directory.collection.get(shared_directory.url) + if parent + load(parent.files.map {|file| file.attributes}) + else + nil + end + # throws exception Fog::HP::Errors::Forbidden if insufficient access + rescue Fog::Storage::HP::NotFound + nil + end + + def get(key, &block) + requires :shared_directory + shared_object_url = "#{shared_directory.url}/#{key}" + data = service.get_shared_object(shared_object_url, &block) + file_data = data.headers.merge({ + :body => data.body, + :key => key + }) + new(file_data) + # throws exception Fog::HP::Errors::Forbidden if insufficient access + rescue Fog::Storage::HP::NotFound + nil + end + + def head(key) + requires :shared_directory + shared_object_url = "#{shared_directory.url}/#{key}" + data = service.head_shared_object(shared_object_url) + file_data = data.headers.merge({ + :body => '', + :key => key + }) + new(file_data) + # throws exception Fog::HP::Errors::Forbidden if insufficient access + rescue Fog::Storage::HP::NotFound + nil + end + + def new(attributes = {}) + requires :shared_directory + super({ :shared_directory => shared_directory }.merge!(attributes)) + end + + end + + end + end +end diff --git a/lib/fog/hp/requests/block_storage/create_snapshot.rb b/lib/fog/hp/requests/block_storage/create_snapshot.rb new file mode 100644 index 000000000..0049a315c --- /dev/null +++ b/lib/fog/hp/requests/block_storage/create_snapshot.rb @@ -0,0 +1,76 @@ +module Fog + module HP + class BlockStorage + class Real + + # Create a new block storage snapshot + # + # ==== Parameters + # * name<~String> - Name of the snapshot + # * description<~String> - Description of the snapshot + # * volume_id<~Integer> - Id of the volume to create snapshot of + # * options<~Hash>: + # * 'force'<~Boolean> - Not implemented yet. True or False, to allow online snapshots (i.e. when volume is attached) + # + # ==== Returns + # * response<~Excon::Response>: + # * body<~Hash>: + # * snapshot<~Hash>: + # * 'id'<~Integer>: - Id for the snapshot + # * 'displayName'<~String>: - Name of the snapshot + # * 'displayDescription'<~String>: - Description of the snapshot + # * 'size'<~Integer>: - Size in GB for the snapshot + # * 'status'<~String>: - Status of the snapshot i.e. "creating" + # * 'volumeId'<~Integer>: - Id of the volume from which the snapshot was created + # * 'createdAt'<~String>: - Timestamp in UTC when snapshot was created + def create_snapshot(name, description, volume_id, options={}) + data = { + 'snapshot' => { + 'display_name' => name, + 'display_description' => description, + 'volume_id' => volume_id + } + } + + l_options = ['force'] + l_options.select{|o| options[o]}.each do |key| + data['snapshot'][key] = options[key] + end + + request( + :body => Fog::JSON.encode(data), + :expects => 200, + :method => 'POST', + :path => "os-snapshots" + ) + end + + end + + class Mock # :nodoc:all + + def create_snapshot(name, description, volume_id, options={}) + response = Excon::Response.new + if self.data[:volumes][volume_id] + response.status = 200 + data = { + 'id' => Fog::Mock.random_numbers(3).to_i, + 'displayName' => name, + 'displayDescription' => description, + 'size' => self.data[:volumes][volume_id]['size'], + 'status' => 'available', + 'volumeId' => volume_id, + 'createdAt' => Time.now.to_s + } + self.data[:snapshots][data['id']] = data + response.body = { 'snapshot' => data } + else + raise Fog::HP::BlockStorage::NotFound + end + response + end + end + + end + end +end \ No newline at end of file diff --git a/lib/fog/hp/requests/block_storage/create_volume.rb b/lib/fog/hp/requests/block_storage/create_volume.rb new file mode 100644 index 000000000..e16705739 --- /dev/null +++ b/lib/fog/hp/requests/block_storage/create_volume.rb @@ -0,0 +1,87 @@ +module Fog + module HP + class BlockStorage + class Real + + # Create a new block storage volume + # + # ==== Parameters + # * name<~String> - Name of the volume + # * description<~String> - Description of the volume + # * size<~Integer> - Size of the volume (in GBs) + # * options<~Hash>: + # * 'snapshot_id'<~String> - Id of the volume snapshot to create the volume from. The request is invalid if both the snapshot_id and the imageRef parameters are specified and are not null. + # * 'imageRef'<~String> - Id of the image to create the volume from. This creates a bootable volume. The request is invalid if both the snapshot_id and the imageRef parameters are specified and are not null. + # + # ==== Returns + # * response<~Excon::Response>: + # * body<~Hash>: + # * volume<~Hash>: + # * 'id'<~Integer> - Id for the volume + # * 'displayName'<~String> - Name of the volume + # * 'displayDescription'<~String> - Description of the volume + # * 'size'<~Integer> - Size in GB for the volume + # * 'status'<~String> - Status of the volume i.e. "creating" + # * 'volumeType'<~String> - Type of the volume + # * 'snapshotId'<~String> - Id of the snapshot, the volume was created from. + # * 'imageRef'<~String> - Id of the image, the volume was created from. A not null value means it is a bootable volume. + # * 'createdAt'<~String> - Timestamp in UTC when volume was created + # * 'availabilityZone'<~String> - Availability zone i.e. "nova" + # * attachments<~Array>: Array of hashes of attachments + # * metadata<~Hash>: Hash of metadata for the volume + def create_volume(name, description, size, options={}) + data = { + 'volume' => { + 'display_name' => name, + 'display_description' => description, + 'size' => size + } + } + + l_options = ['snapshot_id', 'imageRef', 'metadata'] + l_options.select{|o| options[o]}.each do |key| + data['volume'][key] = options[key] + end + + request( + :body => Fog::JSON.encode(data), + :expects => 200, + :method => 'POST', + :path => "os-volumes" + ) + end + + end + + class Mock # :nodoc:all + + def create_volume(name, description, size, options={}) + if options['snapshotId'] && options['imageRef'] + raise Fog::Errors::BadRequest.new("Snapshot and image cannot be specified together.") + else + response = Excon::Response.new + response.status = 200 + data = { + 'id' => Fog::Mock.random_numbers(3).to_i, + 'displayName' => name, + 'displayDescription' => description, + 'size' => size, + 'status' => 'available', + 'snapshotId' => options['snapshot_id'] || "", + #'imageRef' => options['imageRef'] || "", # TODO: not implemented to preserve backward compatibility + 'volumeType' => nil, + 'availabilityZone' => 'nova', + 'createdAt' => Time.now.to_s, + 'metadata' => options['metadata'] || {}, + 'attachments' => [{}] + } + self.data[:volumes][data['id']] = data + response.body = { 'volume' => data } + response + end + end + end + + end + end +end \ No newline at end of file diff --git a/lib/fog/hp/requests/block_storage/delete_snapshot.rb b/lib/fog/hp/requests/block_storage/delete_snapshot.rb new file mode 100644 index 000000000..3ff2c3b84 --- /dev/null +++ b/lib/fog/hp/requests/block_storage/delete_snapshot.rb @@ -0,0 +1,38 @@ +module Fog + module HP + class BlockStorage + class Real + + # Delete an existing block storage snapshot + # + # ==== Parameters + # * snapshot_id<~Integer> - Id of the snapshot to delete + # + def delete_snapshot(snapshot_id) + response = request( + :expects => 202, + :method => 'DELETE', + :path => "os-snapshots/#{snapshot_id}" + ) + response + end + + end + + class Mock # :nodoc:all + + def delete_snapshot(snapshot_id) + response = Excon::Response.new + if self.data[:snapshots][snapshot_id] + self.data[:snapshots].delete(snapshot_id) + response.status = 202 + else + raise Fog::HP::BlockStorage::NotFound + end + response + end + end + + end + end +end \ No newline at end of file diff --git a/lib/fog/hp/requests/block_storage/delete_volume.rb b/lib/fog/hp/requests/block_storage/delete_volume.rb new file mode 100644 index 000000000..cfc6d1476 --- /dev/null +++ b/lib/fog/hp/requests/block_storage/delete_volume.rb @@ -0,0 +1,38 @@ +module Fog + module HP + class BlockStorage + class Real + + # Delete an existing block storage volume + # + # ==== Parameters + # * volume_id<~Integer> - Id of the volume to delete + # + def delete_volume(volume_id) + response = request( + :expects => 202, + :method => 'DELETE', + :path => "os-volumes/#{volume_id}" + ) + response + end + + end + + class Mock # :nodoc:all + + def delete_volume(volume_id) + response = Excon::Response.new + if self.data[:volumes][volume_id] + self.data[:volumes].delete(volume_id) + response.status = 202 + else + raise Fog::HP::BlockStorage::NotFound + end + response + end + end + + end + end +end \ No newline at end of file diff --git a/lib/fog/hp/requests/block_storage/get_bootable_volume_details.rb b/lib/fog/hp/requests/block_storage/get_bootable_volume_details.rb new file mode 100644 index 000000000..42271c0f9 --- /dev/null +++ b/lib/fog/hp/requests/block_storage/get_bootable_volume_details.rb @@ -0,0 +1,59 @@ +module Fog + module HP + class BlockStorage + class Real + + # Get details for existing block storage bootable volume + # + # ==== Parameters + # * volume_id<~Integer> - Id of the volume to get + # + # ==== Returns + # * response<~Excon::Response>: + # * body<~Hash>: + # * volume<~Hash>: + # * 'id'<~Integer> - Id for the volume + # * 'displayName'<~String> - Name of the volume + # * 'displayDescription'<~String> - Description of the volume + # * 'size'<~Integer> - Size in GB for the volume + # * 'status'<~String> - Status of the volume i.e. "available" + # * 'volumeType'<~String> - Type of the volume + # * 'snapshotId'<~String> - Id of the volume snapshot + # * 'sourceImageRef'<~String> - Id of the volume snapshot + # * 'createdAt'<~String> - Timestamp in UTC when volume was created + # * 'availabilityZone'<~String> - Availability zone i.e. "nova" + # * attachments<~Array> Array of hashes of attachments + # * metadata<~Hash> Hash of metadata for the volume + + def get_bootable_volume_details(volume_id) + response = request( + :expects => 200, + :method => 'GET', + :path => "hp-bootable-volumes/#{volume_id}" + ) + response + end + + end + + class Mock # :nodoc:all + + def get_bootable_volume_details(volume_id) + unless volume_id + raise ArgumentError.new('volume_id is required') + end + response = Excon::Response.new + if volume = self.data[:volumes][volume_id] + response.status = 200 + response.body = { 'volume' => volume } + response + else + raise Fog::HP::BlockStorage::NotFound + end + + end + end + + end + end +end \ No newline at end of file diff --git a/lib/fog/hp/requests/block_storage/get_snapshot_details.rb b/lib/fog/hp/requests/block_storage/get_snapshot_details.rb new file mode 100644 index 000000000..3bd236792 --- /dev/null +++ b/lib/fog/hp/requests/block_storage/get_snapshot_details.rb @@ -0,0 +1,54 @@ +module Fog + module HP + class BlockStorage + class Real + + # Get details for existing block storage snapshot + # + # ==== Parameters + # * snapshot_id<~Integer> - Id of the snapshot to get + # + # ==== Returns + # * response<~Excon::Response>: + # * body<~Hash>: + # * snapshot<~Hash>: + # * 'id'<~Integer>: - Id for the snapshot + # * 'displayName'<~String>: - Name of the snapshot + # * 'displayDescription'<~String>: - Description of the snapshot + # * 'size'<~Integer>: - Size in GB for the snapshot + # * 'status'<~String>: - Status of the snapshot i.e. "available" + # * 'volumeId'<~Integer>: - Id of the volume from which the snapshot was created + # * 'createdAt'<~String>: - Timestamp in UTC when volume was created + + def get_snapshot_details(snapshot_id) + response = request( + :expects => 200, + :method => 'GET', + :path => "os-snapshots/#{snapshot_id}" + ) + response + end + + end + + class Mock # :nodoc:all + + def get_snapshot_details(snapshot_id) + unless snapshot_id + raise ArgumentError.new('snapshot_id is required') + end + response = Excon::Response.new + if snapshot = self.data[:snapshots][snapshot_id] + response.status = 200 + response.body = { 'snapshot' => snapshot } + response + else + raise Fog::HP::BlockStorage::NotFound + end + + end + end + + end + end +end \ No newline at end of file diff --git a/lib/fog/hp/requests/block_storage/get_volume_details.rb b/lib/fog/hp/requests/block_storage/get_volume_details.rb new file mode 100644 index 000000000..9049642e1 --- /dev/null +++ b/lib/fog/hp/requests/block_storage/get_volume_details.rb @@ -0,0 +1,58 @@ +module Fog + module HP + class BlockStorage + class Real + + # Get details for existing block storage volume + # + # ==== Parameters + # * volume_id<~Integer> - Id of the volume to get + # + # ==== Returns + # * response<~Excon::Response>: + # * body<~Hash>: + # * volume<~Hash>: + # * 'id'<~Integer>: - Id for the volume + # * 'displayName'<~String>: - Name of the volume + # * 'displayDescription'<~String>: - Description of the volume + # * 'size'<~Integer>: - Size in GB for the volume + # * 'status'<~String>: - Status of the volume i.e. "available" + # * 'volumeType'<~String>: - Type of the volume + # * 'snapshotId'<~String>: - Id of the volume snapshot + # * 'createdAt'<~String>: - Timestamp in UTC when volume was created + # * 'availabilityZone'<~String>: - Availability zone i.e. "nova" + # * attachments<~Array>: Array of hashes of attachments + # * metadata<~Hash>: Hash of metadata for the volume + + def get_volume_details(volume_id) + response = request( + :expects => 200, + :method => 'GET', + :path => "os-volumes/#{volume_id}" + ) + response + end + + end + + class Mock # :nodoc:all + + def get_volume_details(volume_id) + unless volume_id + raise ArgumentError.new('volume_id is required') + end + response = Excon::Response.new + if volume = self.data[:volumes][volume_id] + response.status = 200 + response.body = { 'volume' => volume } + response + else + raise Fog::HP::BlockStorage::NotFound + end + + end + end + + end + end +end \ No newline at end of file diff --git a/lib/fog/hp/requests/block_storage/list_bootable_volumes.rb b/lib/fog/hp/requests/block_storage/list_bootable_volumes.rb new file mode 100644 index 000000000..699209880 --- /dev/null +++ b/lib/fog/hp/requests/block_storage/list_bootable_volumes.rb @@ -0,0 +1,53 @@ +module Fog + module HP + class BlockStorage + class Real + + # List existing block storage bootbale volumes + # + # ==== Parameters + # None + # + # ==== Returns + # * response<~Excon::Response>: + # * body<~Array>: + # * volumes<~Hash>: + # * 'id'<~Integer>: - Id for the volume + # * 'displayName'<~String>: - Name of the volume + # * 'displayDescription'<~String>: - Description of the volume + # * 'size'<~Integer>: - Size in GB for the volume + # * 'status'<~String>: - Status of the volume i.e. "available" + # * 'volumeType'<~String>: - Type of the volume + # * 'snapshotId'<~String>: - Id of the source snapshot used to create volume + # * 'sourceImageRef'<~String>: - Id of the source image used to create volume + # * 'createdAt'<~String>: - Timestamp in UTC when volume was created + # * 'availabilityZone'<~String>: - Availability zone i.e. "nova" + # * attachments<~Array>: Array of hashes of attachments + # * metadata<~Hash>: Hash of metadata for the volume + def list_bootable_volumes + response = request( + :expects => 200, + :method => 'GET', + :path => "hp-bootable-volumes" + ) + response + end + + end + + class Mock # :nodoc:all + + def list_bootable_volumes + response = Excon::Response.new + volumes = [] + volumes = self.data[:volumes].values unless self.data[:volumes].nil? + + response.status = 200 + response.body = { 'volumes' => volumes } + response + end + end + + end + end +end \ No newline at end of file diff --git a/lib/fog/hp/requests/block_storage/list_snapshots.rb b/lib/fog/hp/requests/block_storage/list_snapshots.rb new file mode 100644 index 000000000..743d9cc83 --- /dev/null +++ b/lib/fog/hp/requests/block_storage/list_snapshots.rb @@ -0,0 +1,48 @@ +module Fog + module HP + class BlockStorage + class Real + + # List existing block storage snapshots + # + # ==== Parameters + # None + # + # ==== Returns + # * response<~Excon::Response>: + # * body<~Array>: + # * snapshots<~Hash>: + # * 'id'<~Integer>: - Id for the snapshot + # * 'displayName'<~String>: - Name of the snapshot + # * 'displayDescription'<~String>: - Description of the snapshot + # * 'size'<~Integer>: - Size in GB for the snapshot + # * 'status'<~String>: - Status of the snapshot i.e. "available" + # * 'volumeId'<~Integer>: - Id of the volume from which the snapshot was created + # * 'createdAt'<~String>: - Timestamp in UTC when volume was created + def list_snapshots + response = request( + :expects => 200, + :method => 'GET', + :path => 'os-snapshots' + ) + response + end + + end + + class Mock # :nodoc:all + + def list_snapshots + response = Excon::Response.new + snapshots = [] + snapshots = self.data[:snapshots].values unless self.data[:snapshots].nil? + + response.status = 200 + response.body = { 'snapshots' => snapshots } + response + end + end + + end + end +end \ No newline at end of file diff --git a/lib/fog/hp/requests/block_storage/list_volumes.rb b/lib/fog/hp/requests/block_storage/list_volumes.rb new file mode 100644 index 000000000..3ce4ecd40 --- /dev/null +++ b/lib/fog/hp/requests/block_storage/list_volumes.rb @@ -0,0 +1,52 @@ +module Fog + module HP + class BlockStorage + class Real + + # List existing block storage volumes + # + # ==== Parameters + # None + # + # ==== Returns + # * response<~Excon::Response>: + # * body<~Array>: + # * volumes<~Hash>: + # * 'id'<~Integer>: - Id for the volume + # * 'displayName'<~String>: - Name of the volume + # * 'displayDescription'<~String>: - Description of the volume + # * 'size'<~Integer>: - Size in GB for the volume + # * 'status'<~String>: - Status of the volume i.e. "available" + # * 'volumeType'<~String>: - Type of the volume + # * 'snapshotId'<~String>: - Id of the volume snapshot + # * 'createdAt'<~String>: - Timestamp in UTC when volume was created + # * 'availabilityZone'<~String>: - Availability zone i.e. "nova" + # * attachments<~Array>: Array of hashes of attachments + # * metadata<~Hash>: Hash of metadata for the volume + def list_volumes + response = request( + :expects => 200, + :method => 'GET', + :path => 'os-volumes' + ) + response + end + + end + + class Mock # :nodoc:all + + def list_volumes + response = Excon::Response.new + volumes = [] + volumes = self.data[:volumes].values unless self.data[:volumes].nil? + + response.status = 200 + response.body = { 'volumes' => volumes } + response + end + end + + end + end +end \ No newline at end of file diff --git a/lib/fog/hp/requests/compute/attach_volume.rb b/lib/fog/hp/requests/compute/attach_volume.rb new file mode 100644 index 000000000..7635b6d37 --- /dev/null +++ b/lib/fog/hp/requests/compute/attach_volume.rb @@ -0,0 +1,78 @@ +module Fog + module Compute + class HP + class Real + + # Attach a block storage volume to an existing server + # + # ==== Parameters + # * server_id<~Integer> - Id of server to attach the volume to + # * volume_id<~Integer> - Id of the volume to be attached to the server + # * device<~String> - Device name that is the mount point that the volume will be attached to. e.g. /dev/sdf + # + # ==== Returns + # * response<~Excon::Response>: + # * body<~Hash>: + # * 'volumeAttachment'<~Hash>: + # * <~Hash> + # * 'volumeId':<~Integer> - The volume id + # * 'device':<~String> - The name of the device + def attach_volume(server_id, volume_id, device) + data = { 'volumeAttachment' => + { 'volumeId' => volume_id, + 'device' => device + } + } + response = request( + :body => Fog::JSON.encode(data), + :expects => 200, + :method => 'POST', + :path => "servers/#{server_id}/os-volume_attachments" + ) + response + end + + end + + class Mock # :nodoc:all + + def attach_volume(server_id, volume_id, device) + response = Excon::Response.new + if server = self.data[:servers][server_id] + # mock the case when the volume is already attached to the server + if server['volumeAttachments'] && server['volumeAttachments'].select {|v| v['id'] == volume_id} + response.status = 400 + response.body = '{"badRequest": {"message": "Volume status must be available", "code": 400}}' + raise(Excon::Errors.status_error({:expects => 200}, response)) + else + resp_data = { "volumeAttachment" => + { + "volumeId" => volume_id, + "id" => volume_id + } + } + response.body = resp_data + response.status = 200 + + data = { + "device" => device, + "serverId" => server_id, + "id" => volume_id, + "volumeId" => volume_id, + } + if server['volumeAttachments'] + server['volumeAttachments'] << data + else + server['volumeAttachments'] = [data] + end + end + else + raise Fog::Compute::HP::NotFound + end + response + end + + end + end + end +end diff --git a/lib/fog/hp/requests/compute/create_image.rb b/lib/fog/hp/requests/compute/create_image.rb index 3f6c4e96f..5ca03bf27 100644 --- a/lib/fog/hp/requests/compute/create_image.rb +++ b/lib/fog/hp/requests/compute/create_image.rb @@ -18,10 +18,7 @@ module Fog def create_image(server_id, name, metadata = {}) body = { 'createImage' => { 'name' => name, - 'metadata' => - { 'ImageType' => metadata[:image_type], - 'ImageVersion' => metadata[:image_version] - } + 'metadata' => metadata } } server_action(server_id, body) @@ -39,19 +36,19 @@ module Fog data = { 'id' => image_id, - 'server' => {"id"=>"3", "links"=>[{"href"=>"http://nova1:8774/v1.1/servers/#{server_id}", "rel"=>"bookmark"}]}, + 'server' => {"id"=>server_id, "links"=>[{"href"=>"http://nova1:8774/v1.1/servers/#{server_id}", "rel"=>"bookmark"}]}, 'links' => [{"href"=>"http://nova1:8774/v1.1/tenantid/images/#{image_id}", "rel"=>"self"}, {"href"=>"http://nova1:8774/tenantid/images/#{image_id}", "rel"=>"bookmark"}], 'metadata' => metadata || {}, 'name' => name || "image_#{rand(999)}", 'progress' => 0, 'status' => 'SAVING', - 'updated' => "", - 'created' => "" + 'updated' => "2012-01-01T13:32:20Z", + 'created' => "2012-01-01T13:32:20Z" } self.data[:last_modified][:images][data['id']] = Time.now self.data[:images][data['id']] = data - response.headers = {'Content-Length' => '0', 'Content-Type' => 'text/html; charset=UTF-8', 'Date' => Time.now, 'Location' => "http://nova1:8774/v1.1/images/#{@image_id}"} + response.headers = {'Content-Length' => '0', 'Content-Type' => 'text/html; charset=UTF-8', 'Date' => Time.now, 'Location' => "http://nova1:8774/v1.1/images/#{image_id}"} response.body = "" # { 'image' => data } no data is sent response end diff --git a/lib/fog/hp/requests/compute/create_persistent_server.rb b/lib/fog/hp/requests/compute/create_persistent_server.rb new file mode 100644 index 000000000..87186fa43 --- /dev/null +++ b/lib/fog/hp/requests/compute/create_persistent_server.rb @@ -0,0 +1,168 @@ +module Fog + module Compute + class HP + class Real + + # Create a new persistent server i.e. use a bootable volume instead of an image + # + # ==== Parameters + # * name<~String> - Name of server + # * flavor_id<~Integer> - Id of flavor for server + # * block_device_mapping<~Array>: Use bootable volumes to create persistent instances + # * <~Hash>: + # * 'volume_size'<~String> - Size of the volume. Ignored, and automatically picked up from the volume + # * 'volume_id'<~String> - Id of the bootable volume to use + # * 'delete_on_termination'<~String> - Setting this to '1' (True) means that the volume gets deleted when the instance is killed. Set it to '0' to preserve the volume. + # * 'device_name'<~String> - Block device name e.g. "vda" + # * options<~Hash>: + # * 'metadata'<~Hash> - Up to 5 key value pairs containing 255 bytes of info + # * 'min_count'<~Integer> - Number of servers to create. Defaults to 1. + # * 'max_count'<~Integer> - Max. number of servers to create. Defaults to being equal to min_count. + # * 'key_name'<~String> - Name of keypair to be used + # * 'security_groups'<~Array> - one or more security groups to be used + # * 'personality'<~Array>: Up to 5 files to customize server + # * 'file'<~Hash>: + # * 'contents'<~String> - Contents of file (10kb total of contents) + # * 'path'<~String> - Path to file (255 bytes total of path strings) + # * 'accessIPv4'<~String> - IPv4 IP address + # * 'accessIPv6'<~String> - IPv6 IP address + # + # ==== Returns + # * response<~Excon::Response>: + # * body<~Hash>: + # * 'server'<~Hash>: + # * 'addresses'<~Hash>: + # * 'private'<~Array> - private and public fixed and floating ip addresses + # * 'flavor'<~Hash> + # * 'id'<~String> - id of the flavor + # * 'links'<~Array> - array of flavor links + # * 'id'<~Integer> - id of server + # * 'links'<~Array> - array of server links + # * 'hostId'<~String> + # * 'metadata'<~Hash> - metadata + # * 'name'<~String> - name of server + # * 'accessIPv4'<~String> - IPv4 ip address + # * 'accessIPv6'<~String> - IPv6 ip address + # * 'progress'<~Integer> - progress through current status + # * 'status'<~String> - current server status + # * 'created'<~String> - created date time stamp + # * 'updated'<~String> - updated date time stamp + # * 'user_id'<~String> - user id + # * 'tenant_id'<~String> - tenant id + # * 'uuid'<~String> - uuid of the server + # * 'config_drive'<~String> - config drive + # * 'security_groups'<~Array of Hash> + # * 'id'<~Integer> - id of the security group + # * 'name'<~String> - name of the security group + # * 'links'<~Array> - array of security group links + # * 'key_name'<~String> - name of the keypair + # * 'adminPass'<~String> - admin password for server + def create_persistent_server(name, flavor_id, block_device_mapping = [], options = {}) + data = { + 'server' => { + 'flavorRef' => flavor_id, + 'imageRef' => nil, + 'name' => name + } + } + if options['metadata'] + data['server']['metadata'] = options['metadata'] + end + if options['accessIPv4'] + data['server']['accessIPv4'] = options['accessIPv4'] + end + if options['accessIPv6'] + data['server']['accessIPv6'] = options['accessIPv6'] + end + if options['personality'] + data['server']['personality'] = [] + for file in options['personality'] + data['server']['personality'] << { + 'contents' => Base64.encode64(file['contents']), + 'path' => file['path'] + } + end + end + min_count = options['min_count'] || 1 + max_count = options['max_count'] || min_count + data['server']['min_count'] = min_count + data['server']['max_count'] = max_count + + if options['key_name'] + data['server']['key_name'] = options['key_name'] + end + if options['security_groups'] + data['server']['security_groups'] = [] + for sg in options['security_groups'] + data['server']['security_groups'] << { + 'name' => sg + } + end + end + if options['config_drive'] + data['server']['config_drive'] = options['config_drive'] + end + if block_device_mapping + data['server']['block_device_mapping'] = block_device_mapping + end + + request( + :body => Fog::JSON.encode(data), + :expects => 200, + :method => 'POST', + :path => 'os-volumes_boot' + ) + end + + end + + class Mock + + def create_persistent_server(name, flavor_id, block_device_mapping = [], options = {}) + response = Excon::Response.new + + if block_device_mapping && !block_device_mapping.empty? + + if options['security_groups'] + sec_group_name = options['security_groups'][0] + else + sec_group_name = "default" + end + + data = { + 'addresses' => { "private"=>[{"version"=>4, "addr"=>Fog::HP::Mock.ip_address}] }, + 'flavor' => {"id"=>"#{flavor_id}", "links"=>[{"href"=>"http://nova1:8774/admin/flavors/#{flavor_id}", "rel"=>"bookmark"}]}, + 'id' => Fog::Mock.random_numbers(6).to_i, + 'links' => [{"href"=>"http://nova1:8774/v1.1/admin/servers/5", "rel"=>"self"}, {"href"=>"http://nova1:8774/admin/servers/5", "rel"=>"bookmark"}], + 'hostId' => "123456789ABCDEF01234567890ABCDEF", + 'metadata' => options['metadata'] || {}, + 'name' => name || "server_#{rand(999)}", + 'accessIPv4' => options['accessIPv4'] || "", + 'accessIPv6' => options['accessIPv6'] || "", + 'progress' => 0, + 'status' => 'BUILD', + 'created' => "2012-01-01T13:32:20Z", + 'updated' => "2012-01-01T13:32:20Z", + 'user_id' => Fog::HP::Mock.user_id.to_s, + 'tenant_id' => Fog::HP::Mock.user_id.to_s, + 'uuid' => "95253a45-9ead-43c6-90b3-65da2ef048b3", + 'config_drive' => "", + 'security_groups' => [{"name"=>"#{sec_group_name}", "links"=>[{"href"=>"http://nova1:8774/v1.1/admin//os-security-groups/111", "rel"=>"bookmark"}], "id"=>111}], + 'key_name' => options['key_name'] || "" + } + self.data[:last_modified][:servers][data['id']] = Time.now + self.data[:servers][data['id']] = data + response.body = { 'server' => data.merge({'adminPass' => 'password'}) } + response.status = 200 + response + else + response.status = 400 + raise(Excon::Errors::BadRequest, "No boot volume or boot image specified") + end + + end + + end + end + end +end diff --git a/lib/fog/hp/requests/compute/create_server.rb b/lib/fog/hp/requests/compute/create_server.rb index 30f926d9a..4487c5eb3 100644 --- a/lib/fog/hp/requests/compute/create_server.rb +++ b/lib/fog/hp/requests/compute/create_server.rb @@ -8,16 +8,15 @@ module Fog # ==== Parameters # * name<~String> - Name of server # * flavor_id<~Integer> - Id of flavor for server - # * image_id<~Integer> - Id of image for server + # * image_id<~Integer> - Id of image for server. If block_device_mapping is passed, this is ignored. # * options<~Hash>: # * 'metadata'<~Hash> - Up to 5 key value pairs containing 255 bytes of info # * 'min_count'<~Integer> - Number of servers to create. Defaults to 1. # * 'max_count'<~Integer> - Max. number of servers to create. Defaults to being equal to min_count. # * 'key_name'<~String> - Name of keypair to be used # * 'security_groups'<~Array> - one or more security groups to be used - # * 'availability_zone'<~String> - the availability zone to be used # * 'personality'<~Array>: Up to 5 files to customize server - # * file<~Hash>: + # * 'file'<~Hash>: # * 'contents'<~String> - Contents of file (10kb total of contents) # * 'path'<~String> - Path to file (255 bytes total of path strings) # * 'accessIPv4'<~String> - IPv4 IP address @@ -28,17 +27,34 @@ module Fog # * body<~Hash>: # * 'server'<~Hash>: # * 'addresses'<~Hash>: - # * 'public'<~Array> - public address strings - # * 'private'<~Array> - private address strings - # * 'adminPass'<~String> - Admin password for server - # * 'flavorId'<~Integer> - Id of servers current flavor + # * 'private'<~Array> - private and public fixed and floating ip addresses + # * 'flavor'<~Hash> + # * 'id'<~String> - id of the flavor + # * 'links'<~Array> - array of flavor links + # * 'id'<~Integer> - id of server + # * 'image'<~Hash> - id of image used to boot server + # * 'id'<~String> - id of the image + # * 'links'<~Array> - array of image links + # * 'links'<~Array> - array of server links # * 'hostId'<~String> - # * 'id'<~Integer> - Id of server - # * 'imageId'<~Integer> - Id of image used to boot server # * 'metadata'<~Hash> - metadata - # * 'name'<~String> - Name of server - # * 'progress'<~Integer> - Progress through current status - # * 'status'<~String> - Current server status + # * 'name'<~String> - name of server + # * 'accessIPv4'<~String> - IPv4 ip address + # * 'accessIPv6'<~String> - IPv6 ip address + # * 'progress'<~Integer> - progress through current status + # * 'status'<~String> - current server status + # * 'created'<~String> - created date time stamp + # * 'updated'<~String> - updated date time stamp + # * 'user_id'<~String> - user id + # * 'tenant_id'<~String> - tenant id + # * 'uuid'<~String> - uuid of the server + # * 'config_drive'<~String> - config drive + # * 'security_groups'<~Array of Hash> + # * 'id'<~Integer> - id of the security group + # * 'name'<~String> - name of the security group + # * 'links'<~Array> - array of security group links + # * 'key_name'<~String> - name of the keypair + # * 'adminPass'<~String> - admin password for server def create_server(name, flavor_id, image_id, options = {}) data = { 'server' => { @@ -81,8 +97,8 @@ module Fog } end end - if options['availability_zone'] - data['server']['availability_zone'] = options['availability_zone'] + if options['config_drive'] + data['server']['config_drive'] = options['config_drive'] end request( @@ -101,11 +117,11 @@ module Fog response = Excon::Response.new response.status = 202 - #if options['security_groups'] - # sec_group_name = options['security_groups'][0] - #else - # sec_group_name = "default" - #end + if options['security_groups'] + sec_group_name = options['security_groups'][0] + else + sec_group_name = "default" + end data = { 'addresses' => { "private"=>[{"version"=>4, "addr"=>Fog::HP::Mock.ip_address}] }, 'flavor' => {"id"=>"#{flavor_id}", "links"=>[{"href"=>"http://nova1:8774/admin/flavors/#{flavor_id}", "rel"=>"bookmark"}]}, @@ -125,7 +141,7 @@ module Fog 'tenant_id' => Fog::HP::Mock.user_id.to_s, 'uuid' => "95253a45-9ead-43c6-90b3-65da2ef048b3", 'config_drive' => "", - #'security_groups' => [{"name"=>"#{sec_group_name}", "links"=>[{"href"=>"http://nova1:8774/v1.1/admin//os-security-groups/111", "rel"=>"bookmark"}], "id"=>111}], + 'security_groups' => [{"name"=>"#{sec_group_name}", "links"=>[{"href"=>"http://nova1:8774/v1.1/admin//os-security-groups/111", "rel"=>"bookmark"}], "id"=>111}], 'key_name' => options['key_name'] || "" } self.data[:last_modified][:servers][data['id']] = Time.now diff --git a/lib/fog/hp/requests/compute/delete_meta.rb b/lib/fog/hp/requests/compute/delete_meta.rb new file mode 100644 index 000000000..7c71f249b --- /dev/null +++ b/lib/fog/hp/requests/compute/delete_meta.rb @@ -0,0 +1,55 @@ +module Fog + module Compute + class HP + class Real + + # Delete metadata item for specific collections + # + # ==== Parameters + # * 'collection_name'<~String> - name of the collection i.e. images, servers for which the metadata is intented. + # * 'parent_id'<~Integer> - id of the collection i.e. image_id or the server_id + # * 'key'<~String> - key for the metadata item + # + # ==== Returns + # * response<~Excon::Response>: + # * body: Empty response body + # + def delete_meta(collection_name, parent_id, key) + request( + :expects => 204, + :method => 'DELETE', + :path => "#{collection_name}/#{parent_id}/metadata/#{key}" + ) + end + + end + + class Mock + + def delete_meta(collection_name, parent_id, key) + if collection_name == "images" then + if get_image_details(parent_id) + self.data[:images][parent_id]['metadata'].delete(key) + else + raise Fog::Compute::HP::NotFound + end + end + + if collection_name == "servers" then + if get_server_details(parent_id) + self.data[:servers][parent_id]['metadata'].delete(key) + else + raise Fog::Compute::HP::NotFound + end + end + + response = Excon::Response.new + response.status = 204 + response + end + + end + + end + end +end diff --git a/lib/fog/hp/requests/compute/delete_security_group_rule.rb b/lib/fog/hp/requests/compute/delete_security_group_rule.rb index ddd4e77c3..af74d6f1d 100644 --- a/lib/fog/hp/requests/compute/delete_security_group_rule.rb +++ b/lib/fog/hp/requests/compute/delete_security_group_rule.rb @@ -25,14 +25,14 @@ module Fog response = Excon::Response.new sg_rule = nil + self.data[:security_groups].each do |_, sgv| if sgv['rules'] - sg_rule_index = sgv['rules'].find_index { |r| !r.nil? && r['id'] == security_group_rule_id } - if sg_rule_index - sg_rule = sgv['rules'].delete_at sg_rule_index - end + sg_rule = sgv['rules'].delete_if { |r| !r.nil? && r['id'] == security_group_rule_id } + break if sg_rule end end + if sg_rule && !sg_rule.empty? response.status = 202 response.body = "202 Accepted\n\nThe request is accepted for processing.\n\n " diff --git a/lib/fog/hp/requests/compute/detach_volume.rb b/lib/fog/hp/requests/compute/detach_volume.rb new file mode 100644 index 000000000..0f07c5e30 --- /dev/null +++ b/lib/fog/hp/requests/compute/detach_volume.rb @@ -0,0 +1,47 @@ +module Fog + module Compute + class HP + class Real + + # Detach a block storage volume from an existing server + # + # ==== Parameters + # * server_id<~Integer> - Id of server to attach the volume to + # * volume_id<~Integer> - Id of the volume to be attached to the server + # + # ==== Returns + # * response<~Excon::Response>: + # * body: Empty + def detach_volume(server_id, volume_id) + response = request( + :expects => 202, + :method => 'DELETE', + :path => "servers/#{server_id}/os-volume_attachments/#{volume_id}" + ) + response + end + + end + + class Mock # :nodoc:all + + def detach_volume(server_id, volume_id) + response = Excon::Response.new + if server = self.data[:servers][server_id] + if server['volumeAttachments'] && server['volumeAttachments'].select {|v| v['volumeId'] == volume_id} + data = server['volumeAttachments'].reject {|v| v['volumeId'] == volume_id} + self.data[:servers][server_id]['volumeAttachments'] = data + response.status = 202 + else + raise Fog::Compute::HP::NotFound + end + else + raise Fog::Compute::HP::NotFound + end + response + end + + end + end + end +end diff --git a/lib/fog/hp/requests/compute/get_console_output.rb b/lib/fog/hp/requests/compute/get_console_output.rb new file mode 100644 index 000000000..4cd4021ef --- /dev/null +++ b/lib/fog/hp/requests/compute/get_console_output.rb @@ -0,0 +1,43 @@ +module Fog + module Compute + class HP + class Real + + require 'fog/aws/parsers/compute/get_console_output' + + # Retrieve console output for specified instance + # + # ==== Parameters + # * server_id<~Integer> - Id of instance to get console output from + # * num_lines<~Integer> - Number of lines of console output from the end + # ==== Returns + # # * response<~Excon::Response>: + # * body<~Hash>: + # * 'output'<~String> - Console output + # + def get_console_output(server_id, num_lines) + body = { 'os-getConsoleOutput' => { 'length' => num_lines }} + server_action(server_id, body, 200) + end + + end + + class Mock + + def get_console_output(server_id, num_lines) + output = "" + response = Excon::Response.new + if list_servers_detail.body['servers'].detect {|_| _['id'] == server_id} + (1..num_lines).each {|i| output += "Console Output Line #{i} \r\n"} + response.body = { 'output' => output } + response.status = 200 + else + raise Fog::Compute::HP::NotFound + end + response + end + + end + end + end +end diff --git a/lib/fog/hp/requests/compute/get_meta.rb b/lib/fog/hp/requests/compute/get_meta.rb new file mode 100644 index 000000000..17ea3234d --- /dev/null +++ b/lib/fog/hp/requests/compute/get_meta.rb @@ -0,0 +1,57 @@ +module Fog + module Compute + class HP + class Real + + # Get metadata item for specific collections + # + # ==== Parameters + # * 'collection_name'<~String> - name of the collection i.e. images, servers for which the metadata is intended. + # * 'parent_id'<~Integer> - id of the collection i.e. image_id or the server_id + # * 'key'<~String> - key for the metadata item + # + # ==== Returns + # * response<~Excon::Response>: + # * body<~Hash>: + # * meta<~Hash>: hash of key/value pair for the metadata item found + # + def get_meta(collection_name, parent_id, key) + request( + :expects => [200, 203], + :method => 'GET', + :path => "#{collection_name}/#{parent_id}/metadata/#{key}" + ) + end + + end + + class Mock + + def get_meta(collection_name, parent_id, key) + if collection_name == "images" then + if get_image_details(parent_id) + raise Fog::Compute::HP::NotFound unless midata = self.data[:images][parent_id]['metadata'].fetch(key, nil) + else + raise Fog::Compute::HP::NotFound + end + end + + if collection_name == "servers" then + if get_server_details(parent_id) + raise Fog::Compute::HP::NotFound unless midata = self.data[:servers][parent_id]['metadata'].fetch(key, nil) + else + raise Fog::Compute::HP::NotFound + end + end + + response = Excon::Response.new + response.status = 200 + response.body = { 'meta' => { key => midata } } + response + end + + end + + end + end +end diff --git a/lib/fog/hp/requests/compute/get_windows_password.rb b/lib/fog/hp/requests/compute/get_windows_password.rb new file mode 100644 index 000000000..564b4efb7 --- /dev/null +++ b/lib/fog/hp/requests/compute/get_windows_password.rb @@ -0,0 +1,43 @@ +module Fog + module Compute + class HP + class Real + + # Retrieves the encrypted administrator password for a server running Windows. + # + # ==== Parameters + # * server_id<~Integer> - Id of server + # + # ==== Returns + # * password_data<~string>: Encrypted password for a server running Windows + # + def get_windows_password(server_id) + # get console output assuming that the server is already in active state + log_output = get_console_output(server_id, 400).body['output'] + # decrypt the log output to extract the encrypted, base64-encoded password + encrypted_password = extract_password_from_log(log_output) + end + + end + + class Mock + + def get_windows_password(server_id) + # need to mock out the private key as well + private_key = OpenSSL::PKey::RSA.generate(1024) + public_key = private_key.public_key + ### The original password is Passw0rd + encoded_password = encrypt_using_public_key("Passw0rd", public_key) + + if list_servers_detail.body['servers'].detect {|_| _['id'] == server_id} + # mock output for this call get_console_output(server_id, 400).body['output'] + log_output = "start junk [cloud-init] Encrypt random password\n-----BEGIN BASE64-ENCODED ENCRYPTED PASSWORD-----\n#{encoded_password}-----END BASE64-ENCODED ENCRYPTED PASSWORD-----\nend junk [cloud-init] Done\n" + encrypted_password = extract_password_from_log(log_output) + else + raise Fog::Compute::HP::NotFound + end + end + end + end + end +end \ No newline at end of file diff --git a/lib/fog/hp/requests/compute/list_metadata.rb b/lib/fog/hp/requests/compute/list_metadata.rb new file mode 100644 index 000000000..cd27836ea --- /dev/null +++ b/lib/fog/hp/requests/compute/list_metadata.rb @@ -0,0 +1,56 @@ +module Fog + module Compute + class HP + class Real + + # List metadata for specific collections + # + # ==== Parameters + # * 'collection_name'<~String> - name of the collection i.e. images, servers for which the metadata is intended. + # * 'parent_id'<~Integer> - id of the collection i.e. image_id or the server_id + # + # ==== Returns + # * response<~Excon::Response>: + # * body<~Hash>: + # * metadata<~Hash>: hash of key/value pair for the metadata items found + # + def list_metadata(collection_name, parent_id) + request( + :expects => [200, 203], + :method => 'GET', + :path => "/#{collection_name}/#{parent_id}/metadata.json" + ) + end + + end + + class Mock + + def list_metadata(collection_name, parent_id) + mdata = {} + if collection_name == "images" then + if get_image_details(parent_id) + mdata = self.data[:images][parent_id]['metadata'] + else + raise Fog::Compute::HP::NotFound + end + end + + if collection_name == "servers" then + if get_server_details(parent_id) + mdata = self.data[:servers][parent_id]['metadata'] + else + raise Fog::Compute::HP::NotFound + end + end + + response = Excon::Response.new + response.status = 200 + response.body = {'metadata' => mdata} + response + end + + end + end + end +end diff --git a/lib/fog/hp/requests/compute/list_server_volumes.rb b/lib/fog/hp/requests/compute/list_server_volumes.rb new file mode 100644 index 000000000..e8422dda8 --- /dev/null +++ b/lib/fog/hp/requests/compute/list_server_volumes.rb @@ -0,0 +1,49 @@ +module Fog + module Compute + class HP + class Real + + # List all volumes attached to a server + # + # ==== Parameters + # * server_id<~Integer> - Id of server to list attached volumes for + # + # ==== Returns + # * response<~Excon::Response>: + # * body<~Hash>: + # * 'volumeAttachments'<~Array>: + # * <~Hash> + # * 'device':<~String> - The name of the device + # * 'serverId':<~Integer> - The server id to which thsi volume is attached + # * 'id':<~Integer> - The volume id + # * 'volumeId':<~Integer> - The volume id + def list_server_volumes(server_id) + response = request( + :expects => 200, + :method => 'GET', + :path => "servers/#{server_id}/os-volume_attachments" + ) + response + end + + end + + class Mock # :nodoc:all + + def list_server_volumes(server_id) + response = Excon::Response.new + volumes = [] + if server = self.data[:servers][server_id] + volumes = server['volumeAttachments'] + response.status = 200 + response.body = { 'volumeAttachments' => volumes } + response + else + raise Fog::Compute::HP::NotFound + end + end + + end + end + end +end diff --git a/lib/fog/hp/requests/compute/set_metadata.rb b/lib/fog/hp/requests/compute/set_metadata.rb new file mode 100644 index 000000000..09ac0fe46 --- /dev/null +++ b/lib/fog/hp/requests/compute/set_metadata.rb @@ -0,0 +1,60 @@ +module Fog + module Compute + class HP + class Real + + # Set metadata for specific collections + # + # ==== Parameters + # * 'collection_name'<~String> - name of the collection i.e. images, servers for which the metadata is intented. + # * 'parent_id'<~Integer> - id of the collection i.e. image_id or the server_id + # * 'metadata'<~Hash> - A hash of key/value pairs containing the metadata + + # ==== Returns + # * response<~Excon::Response>: + # * body<~Hash>: + # * metadata<~Hash> - key/value pairs of metadata items + # + def set_metadata(collection_name, parent_id, metadata = {}) + request( + :body => Fog::JSON.encode({ 'metadata' => metadata }), + :expects => 200, + :method => 'PUT', + :path => "#{collection_name}/#{parent_id}/metadata" + ) + end + + end + + class Mock + + def set_metadata(collection_name, parent_id, metadata = {}) + + if collection_name == "images" then + if get_image_details(parent_id) + self.data[:images][parent_id]['metadata'] = metadata + else + raise Fog::Compute::HP::NotFound + end + end + + if collection_name == "servers" then + if get_server_details(parent_id) + self.data[:servers][parent_id]['metadata'] = metadata + else + raise Fog::Compute::HP::NotFound + end + end + + response = Excon::Response.new + response.body = { "metadata" => metadata } + response.status = 200 + response + + end + + end + + end + end +end diff --git a/lib/fog/hp/requests/compute/update_meta.rb b/lib/fog/hp/requests/compute/update_meta.rb new file mode 100644 index 000000000..f03f8c89e --- /dev/null +++ b/lib/fog/hp/requests/compute/update_meta.rb @@ -0,0 +1,61 @@ +module Fog + module Compute + class HP + class Real + + # Set or update metadata item for specific collections + # + # ==== Parameters + # * 'collection_name'<~String> - name of the collection i.e. images, servers for which the metadata is intented. + # * 'parent_id'<~Integer> - id of the collection i.e. image_id or the server_id + # * 'key'<~String> - key for the metadata item + # * 'value'<~String> - value for the metadata item + # + # ==== Returns + # * response<~Excon::Response>: + # * body<~Hash>: + # * meta<~Hash>: hash of key/value pair for the metadata item updated + # + def update_meta(collection_name, parent_id, key, value) + request( + :body => Fog::JSON.encode({ 'meta' => { key => value }}), + :expects => 200, + :method => 'PUT', + :path => "#{collection_name}/#{parent_id}/metadata/#{key}" + ) + end + + end + + class Mock + + def update_meta(collection_name, parent_id, key, value) + + if collection_name == "images" then + if get_image_details(parent_id) + self.data[:images][parent_id]['metadata'][key] = value + else + raise Fog::Compute::HP::NotFound + end + end + + if collection_name == "servers" then + if get_server_details(parent_id) + self.data[:servers][parent_id]['metadata'][key] = value + else + raise Fog::Compute::HP::NotFound + end + end + + response = Excon::Response.new + response.body = { "meta" => { key => value } } + response.status = 200 + response + + end + + end + + end + end +end diff --git a/lib/fog/hp/requests/compute/update_metadata.rb b/lib/fog/hp/requests/compute/update_metadata.rb new file mode 100644 index 000000000..cac456e2e --- /dev/null +++ b/lib/fog/hp/requests/compute/update_metadata.rb @@ -0,0 +1,60 @@ +module Fog + module Compute + class HP + class Real + + # Update metadata for specific collections + # + # ==== Parameters + # * 'collection_name'<~String> - name of the collection i.e. images, servers for which the metadata is intented. + # * 'parent_id'<~Integer> - id of the collection i.e. image_id or the server_id + # * 'metadata'<~Hash> - A hash of key/value pairs containing the metadata + + # ==== Returns + # * response<~Excon::Response>: + # * body<~Hash>: + # * metadata<~Hash> - all key/value pairs of metadata items merged with existing metadata + # + def update_metadata(collection_name, parent_id, metadata = {}) + request( + :body => Fog::JSON.encode({ 'metadata' => metadata }), + :expects => 200, + :method => 'POST', + :path => "#{collection_name}/#{parent_id}/metadata.json" + ) + end + + end + + class Mock + + def update_metadata(collection_name, parent_id, metadata = {}) + + if collection_name == "images" then + if get_image_details(parent_id) + newmetadata = self.data[:images][parent_id]['metadata'].merge!(metadata) + else + raise Fog::Compute::HP::NotFound + end + end + + if collection_name == "servers" then + if get_server_details(parent_id) + newmetadata = self.data[:servers][parent_id]['metadata'].merge!(metadata) + else + raise Fog::Compute::HP::NotFound + end + end + + response = Excon::Response.new + response.body = { "metadata" => newmetadata } + response.status = 200 + response + + end + + end + + end + end +end diff --git a/lib/fog/hp/requests/storage/delete_shared_object.rb b/lib/fog/hp/requests/storage/delete_shared_object.rb new file mode 100644 index 000000000..f17c5f94d --- /dev/null +++ b/lib/fog/hp/requests/storage/delete_shared_object.rb @@ -0,0 +1,38 @@ +module Fog + module Storage + class HP + class Real + + # Delete a shared object + # + # ==== Parameters + # * shared_object_url<~String> - Url of the shared object + # + def delete_shared_object(shared_object_url) + # split up the shared object url + uri = URI.parse(shared_object_url) + path = uri.path + + response = shared_request( + :expects => 204, + :method => 'DELETE', + :path => path + ) + response + end + + end + + class Mock # :nodoc:all + + def delete_shared_object(shared_object_url) + response = Excon::Response.new + response.status = 204 + response + end + + end + + end + end +end diff --git a/lib/fog/hp/requests/storage/get_object.rb b/lib/fog/hp/requests/storage/get_object.rb index 73760e815..8a8eeafbf 100644 --- a/lib/fog/hp/requests/storage/get_object.rb +++ b/lib/fog/hp/requests/storage/get_object.rb @@ -10,18 +10,21 @@ module Fog # * object<~String> - Name of object to look for # def get_object(container, object, &block) - params = {} - if block_given? - params[:response_block] = Proc.new + response = request( + :response_block => block, + :expects => 200, + :method => 'GET', + :path => "#{Fog::HP.escape(container)}/#{Fog::HP.escape(object)}" + ) + else + response = request({ + :block => block, + :expects => 200, + :method => 'GET', + :path => "#{Fog::HP.escape(container)}/#{Fog::HP.escape(object)}" + }, false, &block) end - - response = request(params.merge!({ - :block => block, - :expects => 200, - :method => 'GET', - :path => "#{Fog::HP.escape(container)}/#{Fog::HP.escape(object)}" - }), false) response end diff --git a/lib/fog/hp/requests/storage/get_object_temp_url.rb b/lib/fog/hp/requests/storage/get_object_temp_url.rb new file mode 100644 index 000000000..019f6c298 --- /dev/null +++ b/lib/fog/hp/requests/storage/get_object_temp_url.rb @@ -0,0 +1,31 @@ +module Fog + module Storage + class HP + + class Real + + # Generate a temporary url for an object + # + # ==== Parameters + # * container<~String> - Name of container + # * object<~String> - Name of object + # * expires<~Integer> - Time the temporary url expire in secs. + # * method<~String> - Allowed HTTP method GET, PUT, HEAD only + def get_object_temp_url(container, object, expires, method) + generate_object_temp_url(container, object, expires, method) + end + + end + + class Mock # :nodoc:all + + def get_object_temp_url(container, object, expires, method) + @hp_storage_uri = "https://swift-cluster.example.com:443/v1/account" + + generate_object_temp_url(container, object, expires, method) + end + end + + end + end +end \ No newline at end of file diff --git a/lib/fog/hp/requests/storage/get_shared_container.rb b/lib/fog/hp/requests/storage/get_shared_container.rb new file mode 100644 index 000000000..6410f46af --- /dev/null +++ b/lib/fog/hp/requests/storage/get_shared_container.rb @@ -0,0 +1,75 @@ +module Fog + module Storage + class HP + class Real + + # Get details for a shared container + # + # ==== Parameters + # * shared_container_url<~String> - Url of the shared container + # * options<~String>: + # * 'limit'<~String> - Maximum number of objects to return + # * 'marker'<~String> - Only return objects whose name is greater than marker + # * 'prefix'<~String> - Limits results to those starting with prefix + # * 'path'<~String> - Return objects nested in the pseudo path + # + # ==== Returns + # * response<~Excon::Response>: + # * headers<~Hash>: + # * 'X-Container-Object-Count'<~String> - Count of objects in container + # * 'X-Container-Bytes-Used'<~String> - Bytes used + # * 'X-Trans-Id'<~String> - Trans Id + # * body<~Array>: + # * item<~Hash>: + # * 'bytes'<~String> - Size of object + # * 'content_type'<~String> Content-Type of object + # * 'hash'<~String> - Hash of object (etag?) + # * 'last_modified'<~String> - Last modified timestamp + # * 'name'<~String> - Name of object + def get_shared_container(shared_container_url, options = {}) + options = options.reject {|key, value| value.nil?} + # split up the shared container url + uri = URI.parse(shared_container_url) + path = uri.path + + response = shared_request( + :expects => 200, + :method => 'GET', + :path => path, + :query => {'format' => 'json'}.merge!(options) + ) + response + end + + end + + class Mock # :nodoc:all + + def get_shared_container(shared_container_url, options = {}) + response = Excon::Response.new + data = { + 'name' => Fog::Mock.random_letters(10), + 'hash' => Fog::HP::Mock.etag, + 'bytes' => 11, + 'content_type' => "text/plain", + 'last_modified' => Time.now + } + response.status = 200 + response.body = [data] + response.headers = { + 'X-Container-Object-Count' => 1, + 'X-Container-Bytes-Used' => 11, + 'Accept-Ranges' => 'bytes', + 'Content-Type' => "application/json", + 'Content-Length' => 11, + 'X-Trans-Id' => "tx#{Fog::Mock.random_hex(32)}" + } + response + + end + + end + + end + end +end diff --git a/lib/fog/hp/requests/storage/get_shared_object.rb b/lib/fog/hp/requests/storage/get_shared_object.rb new file mode 100644 index 000000000..d429f9b04 --- /dev/null +++ b/lib/fog/hp/requests/storage/get_shared_object.rb @@ -0,0 +1,67 @@ +module Fog + module Storage + class HP + class Real + + # Get details for a shared object + # + # ==== Parameters + # * shared_object_url<~String> - Url of the shared object + # + def get_shared_object(shared_object_url, &block) + # split up the shared object url + uri = URI.parse(shared_object_url) + path = uri.path + + if block_given? + response = shared_request( + :response_block => block, + :expects => 200, + :method => 'GET', + :path => path + ) + else + response = shared_request({ + :block => block, + :expects => 200, + :method => 'GET', + :path => path + }, false, &block) + end + response + end + + end + + class Mock # :nodoc:all + + def get_shared_object(shared_object_url, &block) + response = Excon::Response.new + response.status = 200 + response.headers = { + 'Last-Modified' => Date.today.rfc822, + 'Etag' => Fog::HP::Mock.etag, + 'Accept-Ranges' => 'bytes', + 'Content-Type' => "text/plain", + 'Content-Length' => 11, + 'X-Trans-Id' => "tx#{Fog::Mock.random_hex(32)}" + } + unless block_given? + response.body = "This is a sample text.\n" + else + data = StringIO.new("This is a sample text.\n") + remaining = data.length + while remaining > 0 + chunk = data.read([remaining, Excon::CHUNK_SIZE].min) + block.call(chunk) + remaining -= Excon::CHUNK_SIZE + end + end + response + end + + end + + end + end +end diff --git a/lib/fog/hp/requests/storage/head_container.rb b/lib/fog/hp/requests/storage/head_container.rb index 8d1d2e969..220864548 100644 --- a/lib/fog/hp/requests/storage/head_container.rb +++ b/lib/fog/hp/requests/storage/head_container.rb @@ -30,6 +30,7 @@ module Fog def head_container(container_name) response = get_container(container_name) response.body = nil + response.status = 204 response end diff --git a/lib/fog/hp/requests/storage/head_object.rb b/lib/fog/hp/requests/storage/head_object.rb index 1115c420a..badbd215c 100644 --- a/lib/fog/hp/requests/storage/head_object.rb +++ b/lib/fog/hp/requests/storage/head_object.rb @@ -25,6 +25,7 @@ module Fog def head_object(container_name, object_name, options = {}) response = get_object(container_name, object_name, options) response.body = nil + response.status = 200 response end diff --git a/lib/fog/hp/requests/storage/head_shared_container.rb b/lib/fog/hp/requests/storage/head_shared_container.rb new file mode 100644 index 000000000..cf16a5db1 --- /dev/null +++ b/lib/fog/hp/requests/storage/head_shared_container.rb @@ -0,0 +1,45 @@ +module Fog + module Storage + class HP + class Real + + # List number of objects and total bytes stored for a shared container + # + # ==== Parameters + # * shared_container_url<~String> - Url of the shared container + # + # ==== Returns + # * response<~Excon::Response>: + # * headers<~Hash>: + # * 'X-Container-Object-Count'<~String> - Count of containers + # * 'X-Container-Bytes-Used'<~String> - Bytes used + def head_shared_container(shared_container_url) + # split up the shared container url + uri = URI.parse(shared_container_url) + path = uri.path + + response = shared_request( + :expects => 204, + :method => 'HEAD', + :path => path, + :query => {'format' => 'json'} + ) + response + end + + end + + class Mock # :nodoc:all + + def head_shared_container(shared_container_url) + response = get_shared_container(shared_container_url) + response.body = nil + response.status = 204 + response + end + + end + + end + end +end diff --git a/lib/fog/hp/requests/storage/head_shared_object.rb b/lib/fog/hp/requests/storage/head_shared_object.rb new file mode 100644 index 000000000..855d2808a --- /dev/null +++ b/lib/fog/hp/requests/storage/head_shared_object.rb @@ -0,0 +1,39 @@ +module Fog + module Storage + class HP + class Real + + # Get headers for shared object + # + # ==== Parameters + # * * shared_object_url<~String> - Url of the shared object + # + def head_shared_object(shared_object_url) + # split up the shared object url + uri = URI.parse(shared_object_url) + path = uri.path + + response = shared_request({ + :expects => 200, + :method => 'HEAD', + :path => path + }, false) + response + end + + end + + class Mock # :nodoc:all + + def head_shared_object(shared_object_url) + response = get_shared_object(shared_object_url) + response.body = nil + response.status = 200 + response + end + + end + + end + end +end diff --git a/lib/fog/hp/requests/storage/put_container.rb b/lib/fog/hp/requests/storage/put_container.rb index b3d4235fe..7bbb95d62 100644 --- a/lib/fog/hp/requests/storage/put_container.rb +++ b/lib/fog/hp/requests/storage/put_container.rb @@ -22,16 +22,16 @@ module Fog class Mock # :nodoc:all def put_container(container_name, options = {}) - acl = options['X-Container-Read'] || 'private' - if !['private', 'public-read'].include?(acl) - #raise Excon::Errors::BadRequest.new('invalid X-Container-Read') - else - self.data[:acls][:container][container_name] = self.class.acls(acl) + read_h = options['X-Container-Read'] || '' + write_h = options['X-Container-Write'] || '' + unless options + read_acl, write_acl = self.class.header_to_perm_acl(read_h, write_h) + self.data[:acls][:container][container_name] = {:read_acl => read_acl, :write_acl => write_acl} end response = Excon::Response.new container = { - :objects => {}, + :objects => {} } if self.data[:containers][container_name] response.status = 202 diff --git a/lib/fog/hp/requests/storage/put_object.rb b/lib/fog/hp/requests/storage/put_object.rb index a1b9895ab..3a30d7dd0 100644 --- a/lib/fog/hp/requests/storage/put_object.rb +++ b/lib/fog/hp/requests/storage/put_object.rb @@ -8,9 +8,20 @@ module Fog # ==== Parameters # * container<~String> - Name for container, should be < 256 bytes and must not contain '/' # - def put_object(container, object, data, options = {}) + def put_object(container, object, data, options = {}, &block) data = Fog::Storage.parse_data(data) headers = data[:headers].merge!(options) + if block_given? + headers['Transfer-Encoding'] = 'chunked' + headers.delete('Content-Length') + return request( + :request_block => block, + :expects => 201, + :headers => headers, + :method => 'PUT', + :path => "#{Fog::HP.escape(container)}/#{Fog::HP.escape(object)}" + ) + end if headers.has_key?('Transfer-Encoding') headers.delete('Content-Length') end diff --git a/lib/fog/hp/requests/storage/put_shared_object.rb b/lib/fog/hp/requests/storage/put_shared_object.rb new file mode 100644 index 000000000..cee3a9db0 --- /dev/null +++ b/lib/fog/hp/requests/storage/put_shared_object.rb @@ -0,0 +1,85 @@ +module Fog + module Storage + class HP + class Real + + # Create a new object in a shared container + # + # ==== Parameters + # * shared_container_url<~String> - Shared url for the container + # * object<~String> - Name of the object + # * options<~Hash> - header options + # + def put_shared_object(shared_container_url, object_name, data, options = {}, &block) + # split up the shared object url + uri = URI.parse(shared_container_url) + path = uri.path + + data = Fog::Storage.parse_data(data) + headers = data[:headers].merge!(options) + if block_given? + headers['Transfer-Encoding'] = 'chunked' + headers.delete('Content-Length') + return shared_request( + :request_block => block, + :expects => 201, + :headers => headers, + :method => 'PUT', + :path => "#{path}/#{Fog::HP.escape(object_name)}" + ) + end + if headers.has_key?('Transfer-Encoding') + headers.delete('Content-Length') + end + response = shared_request( + :body => data[:body], + :expects => 201, + :headers => headers, + :method => 'PUT', + :path => "#{path}/#{Fog::HP.escape(object_name)}" + ) + response + end + + end + + class Mock # :nodoc:all + + def put_shared_object(shared_container_url, object_name, data, options = {}, &block) + response = Excon::Response.new + data = Fog::Storage.parse_data(data) + unless data[:body].is_a?(String) + data[:body] = data[:body].read + end + response.status = 201 + object = { + :body => data[:body], + 'Content-Type' => options['Content-Type'] || data[:headers]['Content-Type'], + 'ETag' => Fog::HP::Mock.etag, + 'Key' => object_name, + 'Date' => Fog::Time.now.to_date_header, + 'Content-Length' => options['Content-Length'] || data[:headers]['Content-Length'], + } + + for key, value in options + case key + when 'Cache-Control', 'Content-Disposition', 'Content-Encoding', 'Content-MD5', 'Expires', /^X-Object-Meta-/ + object[key] = value + end + end + + response.headers = { + 'Content-Length' => object['Content-Length'], + 'Content-Type' => object['Content-Type'], + 'ETag' => object['ETag'], + 'Date' => object['Date'] + } + + response + end + + end + + end + end +end diff --git a/lib/fog/hp/storage.rb b/lib/fog/hp/storage.rb index a3162949a..b42466500 100644 --- a/lib/fog/hp/storage.rb +++ b/lib/fog/hp/storage.rb @@ -5,28 +5,42 @@ module Fog module Storage class HP < Fog::Service - requires :hp_secret_key, :hp_account_id, :hp_tenant_id - recognizes :hp_auth_uri, :hp_servicenet, :hp_cdn_ssl, :hp_cdn_uri, :persistent, :connection_options, :hp_use_upass_auth_style, :hp_auth_version + requires :hp_secret_key, :hp_tenant_id, :hp_avl_zone + recognizes :hp_auth_uri, :hp_cdn_ssl, :hp_cdn_uri + recognizes :persistent, :connection_options + recognizes :hp_use_upass_auth_style, :hp_auth_version, :user_agent + recognizes :hp_access_key, :hp_account_id # :hp_account_id is deprecated use hp_access_key instead secrets :hp_secret_key model_path 'fog/hp/models/storage' model :directory collection :directories + model :shared_directory + collection :shared_directories model :file collection :files + model :shared_file + collection :shared_files request_path 'fog/hp/requests/storage' request :delete_container request :delete_object + request :delete_shared_object request :get_container request :get_containers request :get_object + request :get_object_temp_url + request :get_shared_container + request :get_shared_object request :head_container request :head_containers request :head_object + request :head_shared_container + request :head_shared_object request :put_container request :put_object + request :put_shared_object module Utils @@ -34,11 +48,12 @@ module Fog unless @hp_cdn_uri.nil? @cdn ||= Fog::CDN.new( :provider => 'HP', - :hp_account_id => @hp_account_id, + :hp_access_key => @hp_access_key, :hp_secret_key => @hp_secret_key, :hp_auth_uri => @hp_auth_uri, :hp_cdn_uri => @hp_cdn_uri, :hp_tenant_id => @hp_tenant_id, + :hp_avl_zone => @hp_avl_zone, :connection_options => @connection_options ) if @cdn.enabled? @@ -53,35 +68,118 @@ module Fog "#{@scheme}://#{@host}:#{@port}#{@path}" end - def acl_to_header(acl) + def public_url(container=nil, object=nil) + public_url = nil + unless container.nil? + if object.nil? + # return container public url + public_url = "#{url}/#{Fog::HP.escape(container)}" + else + # return object public url + public_url = "#{url}/#{Fog::HP.escape(container)}/#{Fog::HP.escape(object)}" + end + end + public_url + end + + def perm_to_acl(perm, users=[]) + read_perm_acl = [] + write_perm_acl = [] + valid_public_perms = ['pr', 'pw', 'prw'] + valid_account_perms = ['r', 'w', 'rw'] + valid_perms = valid_public_perms + valid_account_perms + unless valid_perms.include?(perm) + raise ArgumentError.new("permission must be one of [#{valid_perms.join(', ')}]") + end + # tackle the public access differently + if valid_public_perms.include?(perm) + case perm + when "pr" + read_perm_acl = [".r:*",".rlistings"] + when "pw" + write_perm_acl = ["*"] + when "prw" + read_perm_acl = [".r:*",".rlistings"] + write_perm_acl = ["*"] + end + elsif valid_account_perms.include?(perm) + # tackle the user access differently + unless (users.nil? || users.empty?) + # return the correct acls + tenant_id = "*" # this might change later + acl_array = users.map { |u| "#{tenant_id}:#{u}" } + #acl_string = acl_array.join(',') + case perm + when "r" + read_perm_acl = acl_array + when "w" + write_perm_acl = acl_array + when "rw" + read_perm_acl = acl_array + write_perm_acl = acl_array + end + end + end + return read_perm_acl, write_perm_acl + end + + def perm_acl_to_header(read_perm_acl, write_perm_acl) header = {} - case acl - when "private" - header['X-Container-Read'] = "" - header['X-Container-Write'] = "" - when "public-read" - header['X-Container-Read'] = ".r:*,.rlistings" - when "public-write" - header['X-Container-Write'] = "*" - when "public-read-write" - header['X-Container-Read'] = ".r:*,.rlistings" - header['X-Container-Write'] = "*" + if read_perm_acl.nil? && write_perm_acl.nil? + header = {'X-Container-Read' => "", 'X-Container-Write' => ""} + elsif !read_perm_acl.nil? && write_perm_acl.nil? + header = {'X-Container-Read' => "#{read_perm_acl.join(',')}", 'X-Container-Write' => ""} + elsif read_perm_acl.nil? && !write_perm_acl.nil? + header = {'X-Container-Read' => "", 'X-Container-Write' => "#{write_perm_acl.join(',')}"} + elsif !read_perm_acl.nil? && !write_perm_acl.nil? + header = {'X-Container-Read' => "#{read_perm_acl.join(',')}", 'X-Container-Write' => "#{write_perm_acl.join(',')}"} end header end - def header_to_acl(read_header=nil, write_header=nil) - acl = nil - if read_header.nil? && write_header.nil? - acl = nil - elsif !read_header.nil? && read_header.include?(".r:*") && write_header.nil? - acl = "public-read" - elsif !write_header.nil? && write_header.include?("*") && read_header.nil? - acl = "public-write" - elsif !read_header.nil? && read_header.include?(".r:*") && !write_header.nil? && write_header.include?("*") - acl = "public-read-write" - end + def header_to_perm_acl(read_header=nil, write_header=nil) + read_h, write_h = nil + read_h = read_header.split(',') unless read_header.nil? + write_h = write_header.split(',') unless write_header.nil? + return read_h, write_h end + + def generate_object_temp_url(container, object, expires_secs, method) + return unless (container && object && expires_secs && method) + + # POST not allowed + allowed_methods = %w{GET PUT HEAD} + unless allowed_methods.include?(method) + raise ArgumentError.new("Invalid method '#{method}' specified. Valid methods are: #{allowed_methods.join(', ')}") + end + + expires = (Time.now + expires_secs.to_i).to_i + + # split up the storage uri + uri = URI.parse(@hp_storage_uri) + host = uri.host + path = uri.path + port = uri.port + scheme = uri.scheme + + # do not encode before signature generation, encode after + sig_path = "#{path}/#{container}/#{object}" + encoded_path = "#{path}/#{Fog::HP.escape(container)}/#{Fog::HP.escape(object)}" + + string_to_sign = "#{method}\n#{expires}\n#{sig_path}" + # Only works with 1.9+ Not compatible with 1.8.7 + #signed_string = Digest::HMAC.hexdigest(string_to_sign, @hp_secret_key, Digest::SHA1) + # Compatible with 1.8.7 onwards + hmac = OpenSSL::HMAC.new(@hp_secret_key, OpenSSL::Digest::SHA1.new) + signed_string = hmac.update(string_to_sign).hexdigest + + signature = @hp_tenant_id.to_s + ":" + @hp_access_key.to_s + ":" + signed_string + signature = Fog::HP.escape(signature) + + # generate the temp url using the signature and expiry + temp_url = "#{scheme}://#{host}:#{port}#{encoded_path}?temp_url_sig=#{signature}&temp_url_expires=#{expires}" + end + end class Mock @@ -108,16 +206,25 @@ module Fog def initialize(options={}) require 'mime/types' + # deprecate hp_account_id + if options[:hp_account_id] + Fog::Logger.deprecation(":hp_account_id is deprecated, please use :hp_access_key instead.") + @hp_access_key = options.delete(:hp_account_id) + end + @hp_access_key = options[:hp_access_key] + unless @hp_access_key + raise ArgumentError.new("Missing required arguments: hp_access_key. :hp_account_id is deprecated, please use :hp_access_key instead.") + end @hp_secret_key = options[:hp_secret_key] - @hp_account_id = options[:hp_account_id] + @hp_tenant_id = options[:hp_tenant_id] end def data - self.class.data[@hp_account_id] + self.class.data[@hp_access_key] end def reset_data - self.class.data.delete(@hp_account_id) + self.class.data.delete(@hp_access_key) end end @@ -128,16 +235,25 @@ module Fog def initialize(options={}) require 'mime/types' + # deprecate hp_account_id + if options[:hp_account_id] + Fog::Logger.deprecation(":hp_account_id is deprecated, please use :hp_access_key instead.") + options[:hp_access_key] = options.delete(:hp_account_id) + end + @hp_access_key = options[:hp_access_key] + unless @hp_access_key + raise ArgumentError.new("Missing required arguments: hp_access_key. :hp_account_id is deprecated, please use :hp_access_key instead.") + end @hp_secret_key = options[:hp_secret_key] - @hp_account_id = options[:hp_account_id] @hp_auth_uri = options[:hp_auth_uri] @hp_cdn_ssl = options[:hp_cdn_ssl] @connection_options = options[:connection_options] || {} ### Set an option to use the style of authentication desired; :v1 or :v2 (default) auth_version = options[:hp_auth_version] || :v2 - ### Pass the service type for object storage to the authentication call - options[:hp_service_type] = "object-store" + ### Pass the service name for object storage to the authentication call + options[:hp_service_type] = "Object Storage" @hp_tenant_id = options[:hp_tenant_id] + @hp_avl_zone = options[:hp_avl_zone] ### Make the authentication call if (auth_version == :v2) @@ -157,12 +273,11 @@ module Fog @auth_token = credentials[:auth_token] uri = URI.parse(@hp_storage_uri) - @host = options[:hp_servicenet] == true ? "snet-#{uri.host}" : uri.host + @host = uri.host @path = uri.path @persistent = options[:persistent] || false @port = uri.port @scheme = uri.scheme - Excon.ssl_verify_peer = false if options[:hp_servicenet] == true @connection = Fog::Connection.new("#{@scheme}://#{@host}:#{@port}", @persistent, @connection_options) end @@ -195,6 +310,33 @@ module Fog response end + # this request is used only for get_shared_container and get_shared_object calls + def shared_request(params, parse_json = true, &block) + begin + response = @connection.request(params.merge!({ + :headers => { + 'Content-Type' => 'application/json', + 'X-Auth-Token' => @auth_token + }.merge!(params[:headers] || {}), + :host => @host, + :path => "#{params[:path]}", + }), &block) + rescue Excon::Errors::HTTPStatusError => error + raise case error + when Excon::Errors::NotFound + Fog::Storage::HP::NotFound.slurp(error) + when Excon::Errors::Forbidden + Fog::HP::Errors::Forbidden.slurp(error) + else + error + end + end + if !response.body.empty? && parse_json && response.headers['Content-Type'] =~ %r{application/json} + response.body = Fog::JSON.decode(response.body) + end + response + end + end end end diff --git a/lib/fog/providers.rb b/lib/fog/providers.rb index 62b456fd5..46784c678 100644 --- a/lib/fog/providers.rb +++ b/lib/fog/providers.rb @@ -30,4 +30,4 @@ require 'fog/vmfusion' require 'fog/vsphere' require 'fog/voxel' require 'fog/xenserver' -require 'fog/zerigo' +require 'fog/zerigo' \ No newline at end of file diff --git a/tests/compute/helper.rb b/tests/compute/helper.rb index b58735baa..89d43e0ee 100644 --- a/tests/compute/helper.rb +++ b/tests/compute/helper.rb @@ -91,6 +91,14 @@ def compute_providers :joyent => { :mocked => false }, + :hp => { + :server_attributes => { + :flavor_id => 100, + :image_id => 1242, + :name => "fog_#{Time.now.to_i}" + }, + :mocked => true + }, :ninefold => { :mocked => false }, diff --git a/tests/core/user_agent_tests.rb b/tests/core/user_agent_tests.rb new file mode 100644 index 000000000..3bcfe540b --- /dev/null +++ b/tests/core/user_agent_tests.rb @@ -0,0 +1,6 @@ +Shindo.tests('Fog::Connection', 'core') do + tests('user_agent').returns("fog/#{Fog::VERSION}") do + conn = Fog::Connection.new("http://www.testserviceurl.com", false, {}) + conn.instance_variable_get(:@excon).connection[:headers]['User-Agent'] + end +end diff --git a/tests/helpers/mock_helper.rb b/tests/helpers/mock_helper.rb index 6f2c31023..1aa90fa4a 100644 --- a/tests/helpers/mock_helper.rb +++ b/tests/helpers/mock_helper.rb @@ -39,9 +39,10 @@ if Fog.mock? :go_grid_shared_secret => 'go_grid_shared_secret', :google_storage_access_key_id => 'google_storage_access_key_id', :google_storage_secret_access_key => 'google_storage_secret_access_key', - :hp_account_id => 'hp_account_id', + :hp_access_key => 'hp_access_key', :hp_secret_key => 'hp_secret_key', :hp_tenant_id => 'hp_tenant_id', + :hp_avl_zone => 'hp_avl_zone', :ibm_username => 'ibm_username', :ibm_password => 'ibm_password', :joyent_username => "joyentuser", diff --git a/tests/hp/models/block_storage/bootable_volume_tests.rb b/tests/hp/models/block_storage/bootable_volume_tests.rb new file mode 100644 index 000000000..10a5cde24 --- /dev/null +++ b/tests/hp/models/block_storage/bootable_volume_tests.rb @@ -0,0 +1,23 @@ +Shindo.tests("HP::BlockStorage | bootable volumes", ['hp', 'block_storage', 'volumes']) do + + @base_image_id = ENV["BASE_IMAGE_ID"] || 1242 + + model_tests(HP[:block_storage].bootable_volumes, {:name => "fogbvoltests", :description => "fogbvoltests-desc", :size => 10, :image_id => @base_image_id}, true) + + tests("new volume") do + @volume = HP[:block_storage].bootable_volumes.create(:name => "testbvol", :size => 10, :image_id => @base_image_id) + @volume.wait_for { ready? } unless Fog.mocking? + + test("get(#{@volume.id})") do + HP[:block_storage].bootable_volumes.get(@volume.id) != nil? + end + + test("has_attachments?") do + @volume.has_attachments? == false + end + after do + @volume.destroy + end + end + +end diff --git a/tests/hp/models/block_storage/snapshot_tests.rb b/tests/hp/models/block_storage/snapshot_tests.rb new file mode 100644 index 000000000..4b5935fcb --- /dev/null +++ b/tests/hp/models/block_storage/snapshot_tests.rb @@ -0,0 +1,23 @@ +Shindo.tests("HP::BlockStorage | snapshots", ['hp', 'block_storage', 'snapshots']) do + + @volume = HP[:block_storage].volumes.create(:name => "testsnapvol", :size => 1) + @volume.wait_for { ready? } unless Fog.mocking? + + model_tests(HP[:block_storage].snapshots, {:name => "fogsnaptests", :description => "fogsnaptests-desc", :volume_id => @volume.id}, true) + + tests("new snapshot") do + @snapshot = HP[:block_storage].snapshots.create(:name => "testvol", :volume_id => @volume.id) + @snapshot.wait_for { ready? } unless Fog.mocking? + + test("get(#{@snapshot.id})") do + HP[:block_storage].snapshots.get(@snapshot.id) != nil? + end + + after do + @snapshot.destroy + end + end + + @volume.destroy + +end diff --git a/tests/hp/models/block_storage/volume_tests.rb b/tests/hp/models/block_storage/volume_tests.rb new file mode 100644 index 000000000..cf934707c --- /dev/null +++ b/tests/hp/models/block_storage/volume_tests.rb @@ -0,0 +1,21 @@ +Shindo.tests("HP::BlockStorage | volumes", ['hp', 'block_storage', 'volumes']) do + + model_tests(HP[:block_storage].volumes, {:name => "fogvoltests", :description => "fogvoltests-desc", :size => 1}, true) + + tests("new volume") do + @volume = HP[:block_storage].volumes.create(:name => "testvol", :size => 1) + @volume.wait_for { ready? } unless Fog.mocking? + + test("get(#{@volume.id})") do + HP[:block_storage].volumes.get(@volume.id) != nil? + end + + test("has_attachments?") do + @volume.has_attachments? == false + end + after do + @volume.destroy + end + end + +end diff --git a/tests/hp/models/compute/address_tests.rb b/tests/hp/models/compute/address_tests.rb index 9b820ea14..137ae7463 100644 --- a/tests/hp/models/compute/address_tests.rb +++ b/tests/hp/models/compute/address_tests.rb @@ -1,4 +1,4 @@ -Shindo.tests("Fog::Compute[:hp] | address", ["hp"]) do +Shindo.tests("Fog::Compute[:hp] | address", ['hp']) do @base_image_id = ENV["BASE_IMAGE_ID"] || 1242 diff --git a/tests/hp/models/compute/addresses_tests.rb b/tests/hp/models/compute/addresses_tests.rb index a6356e72b..eb821772b 100644 --- a/tests/hp/models/compute/addresses_tests.rb +++ b/tests/hp/models/compute/addresses_tests.rb @@ -1,4 +1,4 @@ -Shindo.tests("Fog::Compute[:hp] | addresses", ["hp"]) do +Shindo.tests("Fog::Compute[:hp] | addresses", ['hp']) do collection_tests(Fog::Compute[:hp].addresses, {}, true) diff --git a/tests/hp/models/compute/key_pair_tests.rb b/tests/hp/models/compute/key_pair_tests.rb index 27cbcc38b..4b4c49fd3 100644 --- a/tests/hp/models/compute/key_pair_tests.rb +++ b/tests/hp/models/compute/key_pair_tests.rb @@ -1,4 +1,4 @@ -Shindo.tests("Fog::Compute[:hp] | key_pair", ["hp"]) do +Shindo.tests("Fog::Compute[:hp] | key_pair", ['hp']) do model_tests(Fog::Compute[:hp].key_pairs, {:name => 'fogkeyname'}, true) diff --git a/tests/hp/models/compute/key_pairs_tests.rb b/tests/hp/models/compute/key_pairs_tests.rb index 0eafb52c0..60e34ebfa 100644 --- a/tests/hp/models/compute/key_pairs_tests.rb +++ b/tests/hp/models/compute/key_pairs_tests.rb @@ -1,4 +1,4 @@ -Shindo.tests("Fog::Compute[:hp] | key_pairs", ["hp"]) do +Shindo.tests("Fog::Compute[:hp] | key_pairs", ['hp']) do collection_tests(Fog::Compute[:hp].key_pairs, {:name => 'fogkeyname'}, true) diff --git a/tests/hp/models/compute/metadata_image_tests.rb b/tests/hp/models/compute/metadata_image_tests.rb new file mode 100644 index 000000000..5cad29b73 --- /dev/null +++ b/tests/hp/models/compute/metadata_image_tests.rb @@ -0,0 +1,60 @@ +Shindo.tests("Fog::Compute[:hp] | metadata for images", ['hp']) do + + @base_image_id = ENV["BASE_IMAGE_ID"] || 1242 + + @server = Fog::Compute[:hp].servers.create(:name => "fogsermdtests", :flavor_id => 100, :image_id => @base_image_id) + @server.wait_for { ready? } + response = @server.create_image("fogimgmetadatatests", :metadata => {'Meta1' => 'MetaValue1', 'Meta2' => 'MetaValue2'}) + unless Fog.mocking? + sleep(10) + end + new_image_id = response.headers["Location"].split("/")[5] + @image = Fog::Compute[:hp].images.get(new_image_id) + tests('success') do + + tests("#all").succeeds do + @image.metadata.all + end + + tests("#get('Meta1')").succeeds do + pending if Fog.mocking? + @image.metadata.get('Meta1') + end + + tests("#update({'Meta3' => 'MetaValue3'})").succeeds do + @data = @image.metadata.update({'Meta3' => 'MetaValue3'}) + test("metadata has updated correctly") do + @image.metadata.get('Meta3').value == "MetaValue3" + end + end + + tests("#set({'Meta4' => 'MetaValue4'})").succeeds do + @data = @image.metadata.set({'Meta4' => 'MetaValue4'}) + test("metadata has set correctly") do + @image.metadata.get('Meta4').value == "MetaValue4" + end + end + + tests("#save").succeeds do + m = @image.metadata.new(:key => 'Meta5', :value => 'MetaValue5') + @data = m.save + test("metadata has saved correctly") do + @image.metadata.get('Meta5').value == "MetaValue5" + end + end + + tests("#destroy('Meta5')").succeeds do + @image.metadata.destroy('Meta5') + test("metadata has been destroyed") do + @image.metadata.get('Meta5') == nil + end + end + + end + + unless Fog.mocking? + @image.destroy + end + @server.destroy + +end diff --git a/tests/hp/models/compute/metadata_server_tests.rb b/tests/hp/models/compute/metadata_server_tests.rb new file mode 100644 index 000000000..5f01f2948 --- /dev/null +++ b/tests/hp/models/compute/metadata_server_tests.rb @@ -0,0 +1,54 @@ +Shindo.tests("Fog::Compute[:hp] | metadata for servers", ['hp']) do + + @base_image_id = ENV["BASE_IMAGE_ID"] || 1242 + + @server = Fog::Compute[:hp].servers.create(:name => "fogsermetadatatests", :flavor_id => 100, :image_id => @base_image_id, :metadata => {'Meta1' => 'MetaValue1', 'Meta2' => 'MetaValue2'}) + @server.wait_for { ready? } + + tests('success') do + + tests("#all").succeeds do + @server.metadata.all + end + + tests("#get('Meta1')").succeeds do + @data = @server.metadata.get('Meta1') + test("metadata gets correct value") do + @data.value == "MetaValue1" + end + end + + tests("#update({'Meta3' => 'MetaValue3'})").succeeds do + @data = @server.metadata.update({'Meta3' => 'MetaValue3'}) + test("metadata has updated correctly") do + @server.metadata.get('Meta3').value == "MetaValue3" + end + end + + tests("#set({'Meta4' => 'MetaValue4'})").succeeds do + @data = @server.metadata.set({'Meta4' => 'MetaValue4'}) + test("metadata has set correctly") do + @server.metadata.get('Meta4').value == "MetaValue4" + end + end + + tests("#save").succeeds do + m = @server.metadata.new(:key => 'Meta5', :value => 'MetaValue5') + @data = m.save + test("metadata has saved correctly") do + @server.metadata.get('Meta5').value == "MetaValue5" + end + end + + tests("#destroy('Meta5')").succeeds do + @data = @server.metadata.destroy('Meta5') + test("metadata has been destroyed") do + @server.metadata.get('Meta5') == nil + end + end + + end + + @server.destroy + +end diff --git a/tests/hp/models/compute/security_group_tests.rb b/tests/hp/models/compute/security_group_tests.rb index b39972168..43582cb79 100644 --- a/tests/hp/models/compute/security_group_tests.rb +++ b/tests/hp/models/compute/security_group_tests.rb @@ -1,4 +1,4 @@ -Shindo.tests("Fog::Compute[:hp] | security_group", ["hp"]) do +Shindo.tests("Fog::Compute[:hp] | security_group", ['hp']) do model_tests(Fog::Compute[:hp].security_groups, {:name => 'foggroupname', :description => 'foggroupdescription'}, true) diff --git a/tests/hp/models/compute/security_groups_tests.rb b/tests/hp/models/compute/security_groups_tests.rb index 957637f46..bf9f7b151 100644 --- a/tests/hp/models/compute/security_groups_tests.rb +++ b/tests/hp/models/compute/security_groups_tests.rb @@ -1,4 +1,4 @@ -Shindo.tests("Fog::Compute[:hp] | security_groups", ["hp"]) do +Shindo.tests("Fog::Compute[:hp] | security_groups", ['hp']) do collection_tests(Fog::Compute[:hp].security_groups, {:name => 'foggroupname', :description => 'foggroupdescription'}, true) diff --git a/tests/hp/models/storage/directories_tests.rb b/tests/hp/models/storage/directories_tests.rb new file mode 100644 index 000000000..fc8741653 --- /dev/null +++ b/tests/hp/models/storage/directories_tests.rb @@ -0,0 +1,23 @@ +Shindo.tests("Fog::Storage[:hp] | directories", ['hp', 'storage']) do + + collection_tests(Fog::Storage[:hp].directories, {:key => "fogdirtests"}, true) + + tests('success') do + + tests("#create('fogdirtests')").succeeds do + Fog::Storage[:hp].directories.create(:key => 'fogdirtests') + end + + tests("#head('fogdirtests')").succeeds do + Fog::Storage[:hp].directories.head('fogdirtests') + end + + tests("#get('fogdirtests')").succeeds do + @directory = Fog::Storage[:hp].directories.get('fogdirtests') + end + + @directory.destroy + + end + +end \ No newline at end of file diff --git a/tests/hp/models/storage/directory_tests.rb b/tests/hp/models/storage/directory_tests.rb new file mode 100644 index 000000000..63b8137d0 --- /dev/null +++ b/tests/hp/models/storage/directory_tests.rb @@ -0,0 +1,62 @@ +Shindo.tests("Fog::Storage[:hp] | directory", ['hp', 'storage']) do + + model_tests(Fog::Storage[:hp].directories, {:key => "fogdirtests"}, true) do + + tests('success') do + + tests("#grant('pr')").succeeds do + @instance.grant('pr') + tests("public?").returns(true) do + @instance.public? + end + end + + tests("#revoke('pr')").succeeds do + @instance.revoke('pr') + tests("public?").returns(false) do + @instance.public? + end + end + + @instance.files.create(:key => 'sample.txt', :body => lorem_file) + tests("#files").succeeds do + @instance.files + end + @instance.files.get('sample.txt').destroy + + tests("#cdn_enable=(true)").succeeds do + pending if Fog.mocking? + @instance.cdn_enable=(true) + tests("cdn_enabled?").returns(true) do + pending if Fog.mocking? + @instance.cdn_enable? + end + end + + tests("#cdn_public_url").succeeds do + pending if Fog.mocking? + @instance.cdn_public_url + end + + tests("#cdn_public_ssl_url").succeeds do + pending if Fog.mocking? + @instance.cdn_public_ssl_url + end + + end + + tests('failure') do + + tests("#grant('invalid-acl')").raises(ArgumentError) do + @instance.grant('invalid-acl') + end + + tests("#revoke('invalid-acl')").raises(ArgumentError) do + @instance.revoke('invalid-acl') + end + + end + + end + +end diff --git a/tests/hp/models/storage/file_tests.rb b/tests/hp/models/storage/file_tests.rb new file mode 100644 index 000000000..6c2b8f1a5 --- /dev/null +++ b/tests/hp/models/storage/file_tests.rb @@ -0,0 +1,44 @@ +Shindo.tests("Fog::Storage[:hp] | directory", ['hp', 'storage']) do + + file_attributes = { + :key => 'fog_file_tests', + :body => lorem_file, + :public => true + } + + directory_attributes = { + :key => 'fogfilestests' + } + + @directory = Fog::Storage[:hp].directories.create(directory_attributes) + + model_tests(@directory.files, file_attributes, true) do + + @file = @directory.files.get('fog_file_tests') + + tests('success') do + + tests("#directory").returns(@directory.key) do + @file.directory.key + end + + tests("#cdn_public_url").succeeds do + pending if Fog.mocking? + @file.cdn_public_url + end + + tests("#cdn_public_ssl_url").succeeds do + pending if Fog.mocking? + @file.cdn_public_ssl_url + end + + tests("#temp_signed_url(60, 'GET')").succeeds do + @file.temp_signed_url(60, 'GET') + end + + end + end + + @directory.destroy + +end \ No newline at end of file diff --git a/tests/hp/models/storage/files_tests.rb b/tests/hp/models/storage/files_tests.rb new file mode 100644 index 000000000..a211dad0a --- /dev/null +++ b/tests/hp/models/storage/files_tests.rb @@ -0,0 +1,38 @@ +Shindo.tests("Fog::Storage[:hp] | files", ['hp', 'storage']) do + + file_attributes = { + :key => 'fog_files_tests', + :body => lorem_file + } + + directory_attributes = { + :key => 'fogfilestests' + } + + collection_tests(Fog::Storage[:hp].directories.create(directory_attributes).files, file_attributes, true) + + @directory = Fog::Storage[:hp].directories.create(directory_attributes) + @file = @directory.files.create(file_attributes) + + tests('success') do + + tests("#get_url('#{@directory.key}')").succeeds do + @directory.files.get_url(@directory.key) + end + + tests("#get_cdn_url('#{@directory.key}')").succeeds do + pending if Fog.mocking? + @directory.files.get_cdn_url(@directory.key) + end + + tests("#get_cdn_ssl_url('#{@directory.key}')").succeeds do + pending if Fog.mocking? + @directory.files.get_cdn_ssl_url(@directory.key) + end + + end + + @file.destroy + @directory.destroy + +end \ No newline at end of file diff --git a/tests/hp/requests/block_storage/bootable_volume_tests.rb b/tests/hp/requests/block_storage/bootable_volume_tests.rb new file mode 100644 index 000000000..81df3e139 --- /dev/null +++ b/tests/hp/requests/block_storage/bootable_volume_tests.rb @@ -0,0 +1,78 @@ +Shindo.tests("HP::BlockStorage | bootable volume requests", ['hp', 'block_storage', 'volumes']) do + + @volume_format = { + 'status' => String, + 'displayDescription' => Fog::Nullable::String, + 'availabilityZone' => String, + 'displayName' => Fog::Nullable::String, + 'attachments' => [Fog::Nullable::Hash], + 'volumeType' => Fog::Nullable::String, + 'snapshotId' => Fog::Nullable::String, + 'size' => Integer, + 'id' => Integer, + 'createdAt' => String, + 'metadata' => Fog::Nullable::Hash + } + + @boot_volume_format = { + 'status' => String, + 'displayDescription' => Fog::Nullable::String, + 'availabilityZone' => String, + 'displayName' => Fog::Nullable::String, + 'attachments' => [Fog::Nullable::Hash], + 'volumeType' => Fog::Nullable::String, + 'snapshotId' => Fog::Nullable::String, + 'source_image_id' => Fog::Nullable::String, + 'size' => Integer, + 'id' => Integer, + 'createdAt' => String, + 'metadata' => Fog::Nullable::Hash + } + + @volume_attach_format = { + "volumeId" => Integer, + "id" => Integer + } + + tests('success') do + + @volume_id = nil + @volume_name = "fogbvolumetests" + @volume_desc = @volume_name + " desc" + @base_image_id = ENV["BASE_IMAGE_ID"] || 1242 + + tests("#create_volume(#{@volume_name}, #{@volume_desc}, 10, {'imageRef' => '#{@base_image_id}'})").formats(@volume_format) do + data = HP[:block_storage].create_volume(@volume_name, @volume_desc, 10, {'imageRef' => "#{@base_image_id}"}).body['volume'] + @volume_id = data['id'] + data + end + + HP[:block_storage].volumes.get(@volume_id).wait_for { ready? } + tests("#get_bootable_volume_details(#{@volume_id})").formats(@boot_volume_format) do + HP[:block_storage].get_bootable_volume_details(@volume_id).body['volume'] + end + + tests("#list_bootable_volumes").formats({'volumes' => [@boot_volume_format]}) do + HP[:block_storage].list_bootable_volumes.body + end + + HP[:block_storage].volumes.get(@volume_id).wait_for { ready? } + tests("#delete_volume(#{@volume_id})").succeeds do + HP[:block_storage].delete_volume(@volume_id) + end + + end + + tests('failure') do + + tests("#get_bootable_volume_details(0)").raises(Fog::HP::BlockStorage::NotFound) do + HP[:block_storage].get_bootable_volume_details(0) + end + + tests("#delete_volume(0)").raises(Fog::HP::BlockStorage::NotFound) do + HP[:block_storage].delete_volume(0) + end + + end + +end diff --git a/tests/hp/requests/block_storage/snapshot_tests.rb b/tests/hp/requests/block_storage/snapshot_tests.rb new file mode 100644 index 000000000..851faf9d9 --- /dev/null +++ b/tests/hp/requests/block_storage/snapshot_tests.rb @@ -0,0 +1,56 @@ +Shindo.tests('HP::BlockStorage | snapshot requests', ['hp', 'block_storage', 'snapshots']) do + + @snapshot_format = { + 'status' => String, + 'displayDescription' => Fog::Nullable::String, + 'displayName' => Fog::Nullable::String, + 'volumeId' => Integer, + 'size' => Integer, + 'id' => Integer, + 'createdAt' => String + } + + tests('success') do + + @snapshot_id = nil + @snapshot_name = "fogsnapshottests" + @snapshot_desc = @snapshot_name + " desc" + + @volume = HP[:block_storage].volumes.create(:name => 'fogvolforsnap', :size => 1) + @volume.wait_for { ready? } + + tests("#create_snapshot(#{@snapshot_name}, #{@snapshot_desc}, #{@volume.id})").formats(@snapshot_format) do + data = HP[:block_storage].create_snapshot(@snapshot_name, @snapshot_desc, @volume.id).body['snapshot'] + @snapshot_id = data['id'] + data + end + + tests("#get_snapshot_details(#{@snapshot_id})").formats(@snapshot_format) do + HP[:block_storage].get_snapshot_details(@snapshot_id).body['snapshot'] + end + + tests('#list_snapshots').formats({'snapshots' => [@snapshot_format]}) do + HP[:block_storage].list_snapshots.body + end + + tests("#delete_snapshot(#{@snapshot_id})").succeeds do + HP[:block_storage].delete_snapshot(@snapshot_id) + end + + end + + tests('failure') do + + tests('#get_snapshot_details(0)').raises(Fog::HP::BlockStorage::NotFound) do + HP[:block_storage].get_snapshot_details(0) + end + + tests("#delete_snapshot(0)").raises(Fog::HP::BlockStorage::NotFound) do + HP[:block_storage].delete_snapshot(0) + end + + end + + @volume.destroy + +end diff --git a/tests/hp/requests/block_storage/volume_tests.rb b/tests/hp/requests/block_storage/volume_tests.rb new file mode 100644 index 000000000..3a2716bbd --- /dev/null +++ b/tests/hp/requests/block_storage/volume_tests.rb @@ -0,0 +1,94 @@ +Shindo.tests("HP::BlockStorage | volume requests", ['hp', 'block_storage', 'volumes']) do + + @volume_format = { + 'status' => String, + 'displayDescription' => Fog::Nullable::String, + 'availabilityZone' => String, + 'displayName' => Fog::Nullable::String, + 'attachments' => [Fog::Nullable::Hash], + 'volumeType' => Fog::Nullable::String, + 'snapshotId' => Fog::Nullable::String, + 'size' => Integer, + 'id' => Integer, + 'createdAt' => String, + 'metadata' => Fog::Nullable::Hash + } + + @volume_attach_format = { + "volumeId" => Integer, + "id" => Integer + } + + tests('success') do + + @volume_id = nil + @volume_name = "fogvolumetests" + @volume_desc = @volume_name + " desc" + @base_image_id = ENV["BASE_IMAGE_ID"] || 1242 + + @server = Fog::Compute[:hp].servers.create(:name => 'fogvoltests', :flavor_id => 100, :image_id => @base_image_id) + @server.wait_for { ready? } + + tests("#create_volume(#{@volume_name}, #{@volume_desc}, 1)").formats(@volume_format) do + data = HP[:block_storage].create_volume(@volume_name, @volume_desc, 1).body['volume'] + @volume_id = data['id'] + data + end + + HP[:block_storage].volumes.get(@volume_id).wait_for { ready? } + tests("#get_volume_details(#{@volume_id})").formats(@volume_format) do + HP[:block_storage].get_volume_details(@volume_id).body['volume'] + end + + tests('#list_volumes').formats({'volumes' => [@volume_format]}) do + HP[:block_storage].list_volumes.body + end + + HP[:block_storage].volumes.get(@volume_id).wait_for { ready? } + tests("#attach_volume(#{@server.id}, #{@volume_id}, '/dev/sdg')").formats(@volume_attach_format) do + Fog::Compute[:hp].attach_volume(@server.id, @volume_id, "/dev/sdg").body['volumeAttachment'] + end + + HP[:block_storage].volumes.get(@volume_id).wait_for { in_use? } unless Fog.mocking? + tests("#detach_volume(#{@server.id}, #{@volume_id})").succeeds do + Fog::Compute[:hp].detach_volume(@server.id, @volume_id) + end + + HP[:block_storage].volumes.get(@volume_id).wait_for { ready? } + tests("#delete_volume(#{@volume_id})").succeeds do + HP[:block_storage].delete_volume(@volume_id) + end + + end + + tests('failure') do + + tests('#get_volume_details(0)').raises(Fog::HP::BlockStorage::NotFound) do + HP[:block_storage].get_volume_details(0) + end + + tests("#attach_volume(0, 0, '/dev/sdg')").raises(Fog::Compute::HP::NotFound) do + Fog::Compute[:hp].attach_volume(0, 0, "/dev/sdg") + end + tests("#attach_volume(#{@server.id}, 0, '/dev/sdg')").raises(Fog::HP::BlockStorage::NotFound) do + pending if Fog.mocking? + Fog::Compute[:hp].attach_volume(@server.id, 0, "/dev/sdg") + end + + tests("#detach_volume(0, 0)").raises(Fog::Compute::HP::NotFound) do + Fog::Compute[:hp].detach_volume(0, 0) + end + tests("#detach_volume(#{@server.id}, 0)").raises(Fog::HP::BlockStorage::NotFound) do + pending if Fog.mocking? + Fog::Compute[:hp].detach_volume(@server.id, 0) + end + + tests("#delete_volume(0)").raises(Fog::HP::BlockStorage::NotFound) do + HP[:block_storage].delete_volume(0) + end + + end + + @server.destroy + +end diff --git a/tests/hp/requests/cdn/container_tests.rb b/tests/hp/requests/cdn/container_tests.rb index b3fa6cd9b..c1d8910a6 100644 --- a/tests/hp/requests/cdn/container_tests.rb +++ b/tests/hp/requests/cdn/container_tests.rb @@ -1,4 +1,4 @@ -Shindo.tests('Fog::CDN[:hp] | container requests', ["hp"]) do +Shindo.tests("Fog::CDN[:hp] | container requests", ['hp']) do @cdn_containers_format = [{ 'x-cdn-ssl-uri' => String, diff --git a/tests/hp/requests/compute/address_tests.rb b/tests/hp/requests/compute/address_tests.rb index 7ce976a79..84c0b57c0 100644 --- a/tests/hp/requests/compute/address_tests.rb +++ b/tests/hp/requests/compute/address_tests.rb @@ -1,4 +1,4 @@ -Shindo.tests('Fog::Compute[:hp] | address requests', ["hp"]) do +Shindo.tests("Fog::Compute[:hp] | address requests", ['hp', 'address']) do @floating_ips_format = { 'instance_id' => Fog::Nullable::Integer, @@ -30,12 +30,11 @@ Shindo.tests('Fog::Compute[:hp] | address requests', ["hp"]) do @server.wait_for { ready? } tests("#associate_address('#{@server.id}', '#{@ip_address}')").succeeds do - result = Fog::Compute[:hp].associate_address(@server.id, @ip_address) - tests("#get_address").returns(@server.id, "associated to valid instance id") do - pending if Fog.mocking? - Fog::Compute[:hp].get_address(@address_id).body['floating_ip']['instance_id'] + Fog::Compute[:hp].associate_address(@server.id, @ip_address) + tests("#get_address").returns(@ip_address, "server has associated ip address") do + @server.reload + @server.addresses['private'][1]['addr'] end - result end tests("#disassociate_address('#{@server.id}', '#{@ip_address}')").succeeds do diff --git a/tests/hp/requests/compute/flavor_tests.rb b/tests/hp/requests/compute/flavor_tests.rb index 2bb971ada..6e723fa52 100644 --- a/tests/hp/requests/compute/flavor_tests.rb +++ b/tests/hp/requests/compute/flavor_tests.rb @@ -1,4 +1,4 @@ -Shindo.tests('Fog::Compute[:hp] | flavor requests', ["hp"]) do +Shindo.tests("Fog::Compute[:hp] | flavor requests", ['hp']) do @flavor_format = { 'rxtx_quota' => Integer, diff --git a/tests/hp/requests/compute/image_tests.rb b/tests/hp/requests/compute/image_tests.rb index 6b7c6ae63..0520f4159 100644 --- a/tests/hp/requests/compute/image_tests.rb +++ b/tests/hp/requests/compute/image_tests.rb @@ -1,4 +1,4 @@ -Shindo.tests('Fog::Compute[:hp] | image requests', ["hp"]) do +Shindo.tests("Fog::Compute[:hp] | image requests", ['hp']) do @image_format = { 'id' => String, diff --git a/tests/hp/requests/compute/key_pair_tests.rb b/tests/hp/requests/compute/key_pair_tests.rb index 4a3bdfffd..27ae7c4b7 100644 --- a/tests/hp/requests/compute/key_pair_tests.rb +++ b/tests/hp/requests/compute/key_pair_tests.rb @@ -1,4 +1,4 @@ -Shindo.tests('Fog::Compute[:hp] | key pair requests', ["hp"]) do +Shindo.tests("Fog::Compute[:hp] | key pair requests", ['hp']) do tests('success') do diff --git a/tests/hp/requests/compute/metadata_tests.rb b/tests/hp/requests/compute/metadata_tests.rb new file mode 100644 index 000000000..688a6d1cd --- /dev/null +++ b/tests/hp/requests/compute/metadata_tests.rb @@ -0,0 +1,70 @@ +Shindo.tests("Fog::Compute[:hp] | metadata requests", ['hp']) do + + @metadata_format = { + 'metadata' => Fog::Nullable::Hash + } + @metaitem_format = { + 'meta' => Fog::Nullable::Hash + } + + @base_image_id = ENV["BASE_IMAGE_ID"] || 1242 + + tests('success') do + @server_name = "fogmetadatatest" + @server = Fog::Compute[:hp].servers.create(:name => @server_name, :flavor_id => 100, :image_id => @base_image_id, :metadata => {'Meta1' => 'MetaValue1', 'Meta2' => 'MetaValue2'} ) + @server.wait_for { ready? } + + tests("#list_metadata('servers', #{@server.id})").formats(@metadata_format) do + metadata = Fog::Compute[:hp].list_metadata('servers', @server.id).body + test ("metadata exists") do + metadata['metadata']['Meta1'] == "MetaValue1" + end + metadata + end + + tests("#set_metadata('servers', #{@server.id}, {'MetaNew3' => 'MetaNewValue3'})").formats(@metadata_format) do + data = Fog::Compute[:hp].set_metadata('servers', @server.id, {'MetaNew3' => 'MetaNewValue3'}).body + test ("metadata set correctly") do + metadata = Fog::Compute[:hp].list_metadata('servers', @server.id).body + metadata['metadata']['MetaNew3'] == "MetaNewValue3" + end + data + end + + tests("#update_metadata('servers', #{@server.id}, {'MetaUpd4' => 'MetaUpdValue4'})").formats(@metadata_format) do + data = Fog::Compute[:hp].update_metadata('servers', @server.id, {'MetaUpd4' => 'MetaUpdValue4'}).body + test ("metadata updated correctly") do + metadata = Fog::Compute[:hp].list_metadata('servers', @server.id).body + metadata['metadata']['MetaUpd4'] == "MetaUpdValue4" + end + data + end + + tests("#get_meta('servers', #{@server.id}, 'MetaNew3')").formats(@metaitem_format) do + mitem = Fog::Compute[:hp].get_meta('servers', @server.id, 'MetaNew3').body + test ("metadata item exists") do + mitem['meta']['MetaNew3'] == "MetaNewValue3" + end + mitem + end + + tests("#update_meta('servers', #{@server.id}, 'MetaNew3', 'MetaUpdValue3')").formats(@metaitem_format) do + mitem = Fog::Compute[:hp].update_meta('servers', @server.id, 'MetaNew3', 'MetaUpdValue3').body + test ("metadata item updated correctly") do + mitem['meta']['MetaNew3'] == "MetaUpdValue3" + end + mitem + end + + tests("#delete_meta('servers', #{@server.id}, 'MetaNew3')").succeeds do + data = Fog::Compute[:hp].delete_meta('servers', @server.id, 'MetaNew3').body + test ("metadata item deleted correctly") do + metadata = Fog::Compute[:hp].list_metadata('servers', @server.id).body + metadata['metadata'].fetch('MetaNew3', nil) == nil + end + data + end + + @server.destroy + end +end \ No newline at end of file diff --git a/tests/hp/requests/compute/persistent_server_tests.rb b/tests/hp/requests/compute/persistent_server_tests.rb new file mode 100644 index 000000000..5d6df7ed4 --- /dev/null +++ b/tests/hp/requests/compute/persistent_server_tests.rb @@ -0,0 +1,66 @@ +Shindo.tests("Fog::Compute[:hp] | persistent server requests", ['hp', 'compute']) do + + @server_format = { + 'addresses' => Fog::Nullable::Hash, + 'flavor' => Hash, + 'id' => Integer, + 'links' => [Hash], + 'hostId' => String, + 'metadata' => Fog::Nullable::Hash, + 'name' => String, + 'accessIPv4' => Fog::Nullable::String, + 'accessIPv6' => Fog::Nullable::String, + 'progress' => Fog::Nullable::Integer, + 'status' => String, + 'created' => String, + 'updated' => String, + 'user_id' => String, + 'tenant_id' => String, + 'uuid' => String, + 'config_drive' => Fog::Nullable::String, + 'security_groups' => [Hash], + 'key_name' => Fog::Nullable::String + } + + @volume = HP[:block_storage].volumes.create(:name => 'fogvoltests', :description => 'fog vol test desc', :size => 1) + @volume.wait_for { ready? } + + tests('success') do + + @server_id = nil + @server_name = "fogpersservertests" + @block_device_mapping = [{ 'volume_size' => '', + 'volume_id' => "#{@volume.id}", + 'delete_on_termination' => '0', + 'device_name' => 'vda' + }] + + tests("#create_persistent_server(#{@server_name}, 100, #{@block_device_mapping})").formats(@server_format.merge('adminPass' => String)) do + data = Fog::Compute[:hp].create_persistent_server(@server_name, 100, @block_device_mapping).body['server'] + @server_id = data['id'] + data + end + + Fog::Compute[:hp].servers.get(@server_id).wait_for { ready? } + + tests("#get_server_details(#{@server_id})").formats(@server_format) do + Fog::Compute[:hp].get_server_details(@server_id).body['server'] + end + + tests("#delete_server(#{@server_id})").succeeds do + Fog::Compute[:hp].delete_server(@server_id) + end + + end + + tests('failure') do + + tests("#create_persistent_server(#{@server_name}, 100, nil)").raises(Excon::Errors::BadRequest) do + Fog::Compute[:hp].create_persistent_server(@server_name, 100, nil) + end + + end + + HP[:block_storage].delete_volume(@volume.id) + +end diff --git a/tests/hp/requests/compute/security_group_rule_tests.rb b/tests/hp/requests/compute/security_group_rule_tests.rb index 0135448fa..c447c1d52 100644 --- a/tests/hp/requests/compute/security_group_rule_tests.rb +++ b/tests/hp/requests/compute/security_group_rule_tests.rb @@ -1,4 +1,4 @@ -Shindo.tests('Fog::Compute[:hp] | security group requests', ["hp"]) do +Shindo.tests("Fog::Compute[:hp] | security group requests", ['hp']) do @security_group_rule_format = { 'from_port' => Integer, diff --git a/tests/hp/requests/compute/security_group_tests.rb b/tests/hp/requests/compute/security_group_tests.rb index a4fd021c5..0ca237d42 100644 --- a/tests/hp/requests/compute/security_group_tests.rb +++ b/tests/hp/requests/compute/security_group_tests.rb @@ -1,4 +1,4 @@ -Shindo.tests('Fog::Compute[:hp] | security group requests', ["hp"]) do +Shindo.tests("Fog::Compute[:hp] | security group requests", ['hp']) do @security_groups_format = { 'security_groups' => [{ diff --git a/tests/hp/requests/compute/server_address_tests.rb b/tests/hp/requests/compute/server_address_tests.rb index d35d15b2d..2b96994e1 100644 --- a/tests/hp/requests/compute/server_address_tests.rb +++ b/tests/hp/requests/compute/server_address_tests.rb @@ -1,4 +1,4 @@ -Shindo.tests('Fog::Compute[:hp] | address requests', ["hp"]) do +Shindo.tests("Fog::Compute[:hp] | address requests", ['hp']) do @base_image_id = ENV["BASE_IMAGE_ID"] || 1242 diff --git a/tests/hp/requests/compute/server_tests.rb b/tests/hp/requests/compute/server_tests.rb index 0186c5a77..a3ba1a632 100644 --- a/tests/hp/requests/compute/server_tests.rb +++ b/tests/hp/requests/compute/server_tests.rb @@ -1,4 +1,4 @@ -Shindo.tests('Fog::Compute[:hp] | server requests', ["hp"]) do +Shindo.tests("Fog::Compute[:hp] | server requests", ['hp']) do @server_format = { 'addresses' => Fog::Nullable::Hash, @@ -19,7 +19,7 @@ Shindo.tests('Fog::Compute[:hp] | server requests', ["hp"]) do 'tenant_id' => String, 'uuid' => String, 'config_drive' => Fog::Nullable::String, - #'security_groups' => [Hash], + 'security_groups' => [Hash], 'key_name' => Fog::Nullable::String } @@ -31,7 +31,7 @@ Shindo.tests('Fog::Compute[:hp] | server requests', ["hp"]) do } @get_console_output_format = { - + 'output' => String } @base_image_id = ENV["BASE_IMAGE_ID"] || 1242 @@ -87,9 +87,8 @@ Shindo.tests('Fog::Compute[:hp] | server requests', ["hp"]) do Fog::Compute[:hp].servers.get(@server_id).wait_for { ready? } - tests("#get_console_output('#{@server_id}')").formats(@get_console_output_format) do - pending - Fog::Compute[:hp].get_console_output(@server_id).body + tests("#get_console_output('#{@server_id}', 10)").formats(@get_console_output_format) do + Fog::Compute[:hp].get_console_output(@server_id, 10).body end Fog::Compute[:hp].servers.get(@server_id).wait_for { ready? } diff --git a/tests/hp/requests/compute/server_volume_tests.rb b/tests/hp/requests/compute/server_volume_tests.rb new file mode 100644 index 000000000..81ba1fdaf --- /dev/null +++ b/tests/hp/requests/compute/server_volume_tests.rb @@ -0,0 +1,76 @@ +Shindo.tests("Fog::Compute[:hp] | volume requests", ['hp', 'compute', 'volumes']) do + + @list_volume_attachments_format = { + 'volumeAttachments' => [{ + 'device' => String, + 'serverId' => Integer, + 'id' => Integer, + 'volumeId' => Integer + }] + } + + @volume_attachment_format = { + 'volumeAttachment' => { + "volumeId" => Integer, + "id" => Integer + } + } + + @base_image_id = ENV["BASE_IMAGE_ID"] || 1242 + + @server = Fog::Compute[:hp].servers.create(:name => 'fogservoltests', :flavor_id => 100, :image_id => @base_image_id) + @server.wait_for { ready? } + + tests('success') do + response = HP[:block_storage].create_volume('fogvoltest', 'fogvoltest desc', 1) + @volume_id = response.body['volume']['id'] + @device = "\/dev\/sdf" + + HP[:block_storage].volumes.get(@volume_id).wait_for { ready? } + tests("#attach_volume(#{@server.id}, #{@volume_id}, #{@device}").formats(@volume_attachment_format) do + Fog::Compute[:hp].attach_volume(@server.id, @volume_id, @device).body + end + + HP[:block_storage].volumes.get(@volume_id).wait_for { in_use? } unless Fog.mocking? + tests("#detach_volume(#{@server.id}, #{@volume_id}").succeeds do + Fog::Compute[:hp].detach_volume(@server.id, @volume_id) + end + + HP[:block_storage].volumes.get(@volume_id).wait_for { ready? } + tests("#list_server_volumes(#{@server.id})").formats(@list_volume_attachments_format) do + Fog::Compute[:hp].list_server_volumes(@server.id).body + end + + end + + + tests('failure') do + + tests("#list_server_volumes(0)").raises(Fog::Compute::HP::NotFound) do + Fog::Compute[:hp].list_server_volumes(0) + end + + tests("#attach_volume(#{@server.id}, 0, #{@device})").raises(Fog::Compute::HP::NotFound) do + pending if Fog.mocking? + Fog::Compute[:hp].attach_volume(@server.id, 0, @device) + end + + tests("#attach_volume(0, #{@volume_id}, #{@device})").raises(Fog::Compute::HP::NotFound) do + Fog::Compute[:hp].attach_volume(0, @volume_id, @device) + end + + tests("#detach_volume(#{@server.id}, 0)").raises(Fog::Compute::HP::NotFound) do + pending if Fog.mocking? + Fog::Compute[:hp].detach_volume(@server.id, 0) + end + + tests("#detach_volume(0, #{@volume_id})").raises(Fog::Compute::HP::NotFound) do + Fog::Compute[:hp].detach_volume(0, @volume_id) + end + + end + + HP[:block_storage].delete_volume(@volume_id) + Fog::Compute[:hp].delete_server(@server.id) + +end diff --git a/tests/hp/requests/storage/container_tests.rb b/tests/hp/requests/storage/container_tests.rb index 860253ae7..3fffe400a 100644 --- a/tests/hp/requests/storage/container_tests.rb +++ b/tests/hp/requests/storage/container_tests.rb @@ -1,4 +1,4 @@ -Shindo.tests('Fog::Storage[:hp] | container requests', ["hp"]) do +Shindo.tests("Fog::Storage[:hp] | container requests", ['hp']) do @container_format = [String] diff --git a/tests/hp/requests/storage/object_tests.rb b/tests/hp/requests/storage/object_tests.rb index c440d26ab..d5b9a6c60 100644 --- a/tests/hp/requests/storage/object_tests.rb +++ b/tests/hp/requests/storage/object_tests.rb @@ -1,4 +1,4 @@ -Shindo.tests('Fog::Storage[:hp] | object requests', ["hp"]) do +Shindo.tests("Fog::Storage[:hp] | object requests", ['hp', 'storage']) do @directory = Fog::Storage[:hp].directories.create(:key => 'fogobjecttests') @dir_name = @directory.identity @@ -25,6 +25,10 @@ Shindo.tests('Fog::Storage[:hp] | object requests', ["hp"]) do Fog::Storage[:hp].head_object(@dir_name, 'fog_object') end + tests("#get_object_temp_url('#{@dir_name}', 'fog_object', 60, 'GET')").succeeds do + Fog::Storage[:hp].get_object_temp_url(@dir_name, 'fog_object', 60, 'GET') + end + # copy a file within the same container tests("#put_object('#{@dir_name}', 'fog_other_object', nil, {'X-Copy-From' => '/#{@dir_name}/fog_object'})" ).succeeds do Fog::Storage[:hp].put_object(@dir_name, 'fog_other_object', nil, {'X-Copy-From' => "/#{@dir_name}/fog_object"}) @@ -58,6 +62,10 @@ Shindo.tests('Fog::Storage[:hp] | object requests', ["hp"]) do Fog::Storage[:hp].get_object('fognoncontainer', 'fog_non_object') end + tests("#get_object_temp_url('#{@dir_name}', 'fog_object', 60, 'POST')").raises(ArgumentError) do + Fog::Storage[:hp].get_object_temp_url(@dir_name, 'fog_object', 60, 'POST') + end + tests("#head_object('#{@dir_name}', 'fog_non_object')").raises(Fog::Storage::HP::NotFound) do Fog::Storage[:hp].head_object(@dir_name, 'fog_non_object') end diff --git a/tests/hp/user_agent_tests.rb b/tests/hp/user_agent_tests.rb new file mode 100644 index 000000000..2468d993d --- /dev/null +++ b/tests/hp/user_agent_tests.rb @@ -0,0 +1,13 @@ +Shindo.tests('Fog::Compute[:hp] | user agent', ['hp', 'user_agent']) do + tests('default for HP providers').returns("hpfog/#{Fog::HP::VERSION}") do + pending if Fog.mocking? + conn = Fog::Compute[:hp] + conn.instance_variable_get(:@connection_options)[:headers]['User-Agent'] + end + + tests('overriden by clients').returns("hpfog/#{Fog::HP::VERSION} (TesterClient/1.0.0)") do + pending if Fog.mocking? + conn = Fog::Compute::HP.new(:user_agent => "TesterClient/1.0.0") + conn.instance_variable_get(:@connection_options)[:headers]['User-Agent'] + end +end \ No newline at end of file