From e662dbddcdfac339ed271bf5599504863d105586 Mon Sep 17 00:00:00 2001 From: Thomas Kadauke Date: Wed, 29 May 2013 18:08:41 +0200 Subject: [PATCH 01/14] Support filters in images collection --- lib/fog/openstack/models/compute/images.rb | 12 ++++++++++-- .../openstack/requests/compute/list_images_detail.rb | 5 +++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/lib/fog/openstack/models/compute/images.rb b/lib/fog/openstack/models/compute/images.rb index e560ba55f..5425c8fe3 100644 --- a/lib/fog/openstack/models/compute/images.rb +++ b/lib/fog/openstack/models/compute/images.rb @@ -7,12 +7,20 @@ module Fog class Images < Fog::Collection + attribute :filters + model Fog::Compute::OpenStack::Image attribute :server - def all - data = service.list_images_detail.body['images'] + def initialize(attributes) + self.filters ||= {} + super + end + + def all(filters = filters) + self.filters = filters + data = service.list_images_detail(filters).body['images'] images = load(data) if server self.replace(self.select {|image| image.server_id == server.id}) diff --git a/lib/fog/openstack/requests/compute/list_images_detail.rb b/lib/fog/openstack/requests/compute/list_images_detail.rb index 86c840233..b7ea6ba03 100644 --- a/lib/fog/openstack/requests/compute/list_images_detail.rb +++ b/lib/fog/openstack/requests/compute/list_images_detail.rb @@ -3,11 +3,12 @@ module Fog class OpenStack class Real - def list_images_detail + def list_images_detail(filters = {}) request( :expects => [200, 203], :method => 'GET', - :path => 'images/detail.json' + :path => 'images/detail.json', + :query => filters ) end From 3db0aaf84298d1662fe0aed8dcbd3f35f7edb921 Mon Sep 17 00:00:00 2001 From: Thomas Kadauke Date: Thu, 30 May 2013 10:43:43 +0200 Subject: [PATCH 02/14] Fix test --- lib/fog/openstack/requests/compute/list_images_detail.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/fog/openstack/requests/compute/list_images_detail.rb b/lib/fog/openstack/requests/compute/list_images_detail.rb index b7ea6ba03..f340e0e2a 100644 --- a/lib/fog/openstack/requests/compute/list_images_detail.rb +++ b/lib/fog/openstack/requests/compute/list_images_detail.rb @@ -16,7 +16,7 @@ module Fog class Mock - def list_images_detail + def list_images_detail(filters = {}) response = Excon::Response.new images = self.data[:images].values From 85f7b1568c8538ba22a5207c57aa1a5654bae139 Mon Sep 17 00:00:00 2001 From: Nick Osborn Date: Thu, 5 Sep 2013 11:07:58 +0100 Subject: [PATCH 03/14] [vcloud_director] Allow for multiple Orgs. --- lib/fog/vcloud_director/models/compute/organizations.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/fog/vcloud_director/models/compute/organizations.rb b/lib/fog/vcloud_director/models/compute/organizations.rb index 79ee2ed2c..dbb1728b1 100644 --- a/lib/fog/vcloud_director/models/compute/organizations.rb +++ b/lib/fog/vcloud_director/models/compute/organizations.rb @@ -20,12 +20,12 @@ module Fog def item_list data = service.get_organizations.body - org = data[:Org] # there is only a single Org - service.add_id_from_href!(org) - [org] + orgs = data[:Org].is_a?(Array) ? data[:Org] : [data[:Org]] + orgs.each {|org| service.add_id_from_href!(org)} + orgs end end end end -end \ No newline at end of file +end From 751795d9c64c6e98393ede1b40ed5e1187a8c989 Mon Sep 17 00:00:00 2001 From: jschneiderhan Date: Fri, 6 Sep 2013 12:47:51 -0400 Subject: [PATCH 04/14] Add ability to specify availability zone for subnet during creation --- lib/fog/aws/models/compute/subnet.rb | 4 +++- tests/aws/models/compute/subnet_tests.rb | 2 +- tests/aws/models/compute/subnets_tests.rb | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/fog/aws/models/compute/subnet.rb b/lib/fog/aws/models/compute/subnet.rb index c8cf4bd06..176a141f3 100644 --- a/lib/fog/aws/models/compute/subnet.rb +++ b/lib/fog/aws/models/compute/subnet.rb @@ -43,7 +43,9 @@ module Fog def save requires :vpc_id, :cidr_block - data = service.create_subnet(vpc_id, cidr_block).body['subnetSet'].first + options = {} + options['AvailabilityZone'] = availability_zone if availability_zone + data = service.create_subnet(vpc_id, cidr_block, options).body['subnetSet'].first new_attributes = data.reject {|key,value| key == 'requestId'} merge_attributes(new_attributes) true diff --git a/tests/aws/models/compute/subnet_tests.rb b/tests/aws/models/compute/subnet_tests.rb index ebb89282f..a578a5701 100644 --- a/tests/aws/models/compute/subnet_tests.rb +++ b/tests/aws/models/compute/subnet_tests.rb @@ -1,5 +1,5 @@ Shindo.tests("Fog::Compute[:aws] | subnet", ['aws']) do @vpc=Fog::Compute[:aws].vpcs.create('cidr_block' => '10.0.10.0/24') - model_tests(Fog::Compute[:aws].subnets, {:vpc_id => @vpc.id, :cidr_block => '10.0.10.0/28'}, true) + model_tests(Fog::Compute[:aws].subnets, {:vpc_id => @vpc.id, :cidr_block => '10.0.10.0/28', :availability_zone => 'us-east-1b'}, true) @vpc.destroy end diff --git a/tests/aws/models/compute/subnets_tests.rb b/tests/aws/models/compute/subnets_tests.rb index 39675178e..d7846bea3 100644 --- a/tests/aws/models/compute/subnets_tests.rb +++ b/tests/aws/models/compute/subnets_tests.rb @@ -1,6 +1,6 @@ Shindo.tests("Fog::Compute[:aws] | subnets", ['aws']) do @vpc=Fog::Compute[:aws].vpcs.create('cidr_block' => '10.0.10.0/28') - collection_tests(Fog::Compute[:aws].subnets, { :vpc_id => @vpc.id, :cidr_block => '10.0.10.0/28'}, true) + collection_tests(Fog::Compute[:aws].subnets, { :vpc_id => @vpc.id, :cidr_block => '10.0.10.0/28', :availability_zone => 'us-east-1c'}, true) @vpc.destroy end From e3e331cbf94b7c95a094526ff7c09a2a2ef07ee4 Mon Sep 17 00:00:00 2001 From: Brian Nelson Date: Mon, 9 Sep 2013 15:29:35 -0700 Subject: [PATCH 05/14] Attempt to fix parameter group assignment when creating AWS::Elasticache clusters. --- lib/fog/aws/models/elasticache/cluster.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/fog/aws/models/elasticache/cluster.rb b/lib/fog/aws/models/elasticache/cluster.rb index ef617261f..4d843b69f 100644 --- a/lib/fog/aws/models/elasticache/cluster.rb +++ b/lib/fog/aws/models/elasticache/cluster.rb @@ -29,6 +29,8 @@ module Fog attribute :notification_config, :aliases => 'NotificationConfiguration', :type => :hash + attr_accessor :parameter_group_name + def ready? status == 'available' end @@ -57,7 +59,7 @@ module Fog :port => port, :preferred_availablility_zone => zone, :preferred_maintenance_window => maintenance_window, - :parameter_group_name => parameter_group['CacheParameterGroupName'], + :parameter_group_name => parameter_group_name || parameter_group['CacheParameterGroupName'], } ) end From 4aa42db453c226b05959e17d46c31fc1945e4e5c Mon Sep 17 00:00:00 2001 From: Matt Gillooly Date: Tue, 10 Sep 2013 13:05:33 -0400 Subject: [PATCH 06/14] add support for AWS Data Pipeline's GetPipelineDefinition endpoint http://docs.aws.amazon.com/datapipeline/latest/APIReference/API_GetPipelineDefinition.html --- lib/fog/aws/data_pipeline.rb | 1 + .../data_pipeline/get_pipeline_definition.rb | 37 +++++++++++++++++++ tests/aws/requests/data_pipeline/helper.rb | 18 ++++++++- .../requests/data_pipeline/pipeline_tests.rb | 4 ++ 4 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 lib/fog/aws/requests/data_pipeline/get_pipeline_definition.rb diff --git a/lib/fog/aws/data_pipeline.rb b/lib/fog/aws/data_pipeline.rb index 2c5009eb0..6fc1698b1 100644 --- a/lib/fog/aws/data_pipeline.rb +++ b/lib/fog/aws/data_pipeline.rb @@ -15,6 +15,7 @@ module Fog request :describe_pipelines request :list_pipelines request :put_pipeline_definition + request :get_pipeline_definition model_path 'fog/aws/models/data_pipeline' model :pipeline diff --git a/lib/fog/aws/requests/data_pipeline/get_pipeline_definition.rb b/lib/fog/aws/requests/data_pipeline/get_pipeline_definition.rb new file mode 100644 index 000000000..c20cca113 --- /dev/null +++ b/lib/fog/aws/requests/data_pipeline/get_pipeline_definition.rb @@ -0,0 +1,37 @@ +module Fog + module AWS + class DataPipeline + + class Real + + # Get pipeline definition JSON + # http://docs.aws.amazon.com/datapipeline/latest/APIReference/API_GetPipelineDefinition.html + # ==== Parameters + # * PipelineId <~String> - The ID of the pipeline + # ==== Returns + # * response<~Excon::Response>: + # * body<~Hash>: + def get_pipeline_definition(id) + params = { + 'pipelineId' => id, + } + + response = request({ + :body => Fog::JSON.encode(params), + :headers => { 'X-Amz-Target' => 'DataPipeline.GetPipelineDefinition' }, + }) + + Fog::JSON.decode(response.body) + end + + end + + class Mock + def get_pipeline_definition(id, objects) + Fog::Mock.not_implemented + end + end + + end + end +end diff --git a/tests/aws/requests/data_pipeline/helper.rb b/tests/aws/requests/data_pipeline/helper.rb index 08fe714bd..0bfdb5f0b 100644 --- a/tests/aws/requests/data_pipeline/helper.rb +++ b/tests/aws/requests/data_pipeline/helper.rb @@ -38,7 +38,23 @@ class AWS "errored" => Fog::Boolean, "validationErrors" => Fog::Nullable::Array, } - + + GET_PIPELINE_DEFINITION = { + "pipelineObjects" => [ + { + "id" => String, + "name" => String, + "fields" => [ + { + "key" => String, + "refValue" => Fog::Nullable::String, + "stringValue" => Fog::Nullable::String, + } + ] + } + ] + } + end end end diff --git a/tests/aws/requests/data_pipeline/pipeline_tests.rb b/tests/aws/requests/data_pipeline/pipeline_tests.rb index bfdca09b4..cd61b9123 100644 --- a/tests/aws/requests/data_pipeline/pipeline_tests.rb +++ b/tests/aws/requests/data_pipeline/pipeline_tests.rb @@ -46,6 +46,10 @@ Shindo.tests('AWS::DataPipeline | pipeline_tests', ['aws', 'data_pipeline']) do Fog::AWS[:data_pipeline].activate_pipeline(@pipeline_id) end + tests("#get_pipeline_definition").formats(AWS::DataPipeline::Formats::GET_PIPELINE_DEFINITION) do + Fog::AWS[:data_pipeline].get_pipeline_definition(@pipeline_id) + end + tests("#delete_pipeline").returns(true) do Fog::AWS[:data_pipeline].delete_pipeline(@pipeline_id) end From f9f71d54eb379404f332d633d4f12a63c2e31253 Mon Sep 17 00:00:00 2001 From: Matt Gillooly Date: Tue, 10 Sep 2013 13:24:25 -0400 Subject: [PATCH 07/14] add support for AWS Data Pipeline's QueryObjects endpoint http://docs.aws.amazon.com/datapipeline/latest/APIReference/API_QueryObjects.html --- lib/fog/aws/data_pipeline.rb | 1 + .../requests/data_pipeline/query_objects.rb | 41 +++++++++++++++++++ tests/aws/requests/data_pipeline/helper.rb | 6 +++ .../requests/data_pipeline/pipeline_tests.rb | 14 +++++++ 4 files changed, 62 insertions(+) create mode 100644 lib/fog/aws/requests/data_pipeline/query_objects.rb diff --git a/lib/fog/aws/data_pipeline.rb b/lib/fog/aws/data_pipeline.rb index 6fc1698b1..80ef349a2 100644 --- a/lib/fog/aws/data_pipeline.rb +++ b/lib/fog/aws/data_pipeline.rb @@ -16,6 +16,7 @@ module Fog request :list_pipelines request :put_pipeline_definition request :get_pipeline_definition + request :query_objects model_path 'fog/aws/models/data_pipeline' model :pipeline diff --git a/lib/fog/aws/requests/data_pipeline/query_objects.rb b/lib/fog/aws/requests/data_pipeline/query_objects.rb new file mode 100644 index 000000000..3fabe9f8b --- /dev/null +++ b/lib/fog/aws/requests/data_pipeline/query_objects.rb @@ -0,0 +1,41 @@ +module Fog + module AWS + class DataPipeline + + class Real + + # Queries a pipeline for the names of objects that match a specified set of conditions. + # http://docs.aws.amazon.com/datapipeline/latest/APIReference/API_QueryObjects.html + # ==== Parameters + # * PipelineId <~String> - The ID of the pipeline + # * Sphere <~String> - Specifies whether the query applies to components or instances. + # Allowable values: COMPONENT, INSTANCE, ATTEMPT. + # ==== Returns + # * response<~Excon::Response>: + # * body<~Hash>: + def query_objects(id, sphere) + params = { + 'pipelineId' => id, + 'sphere' => sphere, + } + + response = request({ + :body => Fog::JSON.encode(params), + :headers => { 'X-Amz-Target' => 'DataPipeline.QueryObjects' }, + }) + + Fog::JSON.decode(response.body) + end + + end + + class Mock + def query_objects(id, objects) + Fog::Mock.not_implemented + end + end + + end + end +end + diff --git a/tests/aws/requests/data_pipeline/helper.rb b/tests/aws/requests/data_pipeline/helper.rb index 0bfdb5f0b..cad039a65 100644 --- a/tests/aws/requests/data_pipeline/helper.rb +++ b/tests/aws/requests/data_pipeline/helper.rb @@ -17,6 +17,12 @@ class AWS ] } + QUERY_OBJECTS = { + "hasMoreResults" => Fog::Nullable::Boolean, + "marker" => Fog::Nullable::String, + "ids" => Fog::Nullable::Array, + } + DESCRIBE_PIPELINES = { "pipelineDescriptionList" => [ { diff --git a/tests/aws/requests/data_pipeline/pipeline_tests.rb b/tests/aws/requests/data_pipeline/pipeline_tests.rb index cd61b9123..4f89f45ce 100644 --- a/tests/aws/requests/data_pipeline/pipeline_tests.rb +++ b/tests/aws/requests/data_pipeline/pipeline_tests.rb @@ -50,6 +50,20 @@ Shindo.tests('AWS::DataPipeline | pipeline_tests', ['aws', 'data_pipeline']) do Fog::AWS[:data_pipeline].get_pipeline_definition(@pipeline_id) end + tests("#query_objects") do + tests("for COMPONENTs").formats(AWS::DataPipeline::Formats::QUERY_OBJECTS) do + Fog::AWS[:data_pipeline].query_objects(@pipeline_id, 'COMPONENT') + end + + tests("for INSTANCEs").formats(AWS::DataPipeline::Formats::QUERY_OBJECTS) do + Fog::AWS[:data_pipeline].query_objects(@pipeline_id, 'INSTANCE') + end + + tests("for ATTEMPTs").formats(AWS::DataPipeline::Formats::QUERY_OBJECTS) do + Fog::AWS[:data_pipeline].query_objects(@pipeline_id, 'ATTEMPT') + end + end + tests("#delete_pipeline").returns(true) do Fog::AWS[:data_pipeline].delete_pipeline(@pipeline_id) end From 012930a089f4550d937605536b617ab4e7dc33cf Mon Sep 17 00:00:00 2001 From: Matt Gillooly Date: Tue, 10 Sep 2013 14:28:17 -0400 Subject: [PATCH 08/14] add support for AWS Data Pipeline's DescribeObjects endpoint http://docs.aws.amazon.com/datapipeline/latest/APIReference/API_DescribeObjects.html --- lib/fog/aws/data_pipeline.rb | 1 + .../data_pipeline/describe_objects.rb | 41 +++++++++++++++++++ tests/aws/requests/data_pipeline/helper.rb | 36 +++++++++------- .../requests/data_pipeline/pipeline_tests.rb | 6 +++ 4 files changed, 70 insertions(+), 14 deletions(-) create mode 100644 lib/fog/aws/requests/data_pipeline/describe_objects.rb diff --git a/lib/fog/aws/data_pipeline.rb b/lib/fog/aws/data_pipeline.rb index 80ef349a2..a23b96c47 100644 --- a/lib/fog/aws/data_pipeline.rb +++ b/lib/fog/aws/data_pipeline.rb @@ -17,6 +17,7 @@ module Fog request :put_pipeline_definition request :get_pipeline_definition request :query_objects + request :describe_objects model_path 'fog/aws/models/data_pipeline' model :pipeline diff --git a/lib/fog/aws/requests/data_pipeline/describe_objects.rb b/lib/fog/aws/requests/data_pipeline/describe_objects.rb new file mode 100644 index 000000000..cc91e3faf --- /dev/null +++ b/lib/fog/aws/requests/data_pipeline/describe_objects.rb @@ -0,0 +1,41 @@ +module Fog + module AWS + class DataPipeline + + class Real + + # Queries a pipeline for the names of objects that match a specified set of conditions. + # http://docs.aws.amazon.com/datapipeline/latest/APIReference/API_DescribeObjects.html + # ==== Parameters + # * PipelineId <~String> - The ID of the pipeline + # * ObjectIds <~Array> - Identifiers of the pipeline objects that contain the definitions + # to be described. You can pass as many as 25 identifiers in a + # single call to DescribeObjects. + # ==== Returns + # * response<~Excon::Response>: + # * body<~Hash>: + def describe_objects(id, objectIds) + params = { + 'pipelineId' => id, + 'objectIds' => objectIds, + } + + response = request({ + :body => Fog::JSON.encode(params), + :headers => { 'X-Amz-Target' => 'DataPipeline.DescribeObjects' }, + }) + + Fog::JSON.decode(response.body) + end + + end + + class Mock + def describe_objects(id, objects) + Fog::Mock.not_implemented + end + end + + end + end +end diff --git a/tests/aws/requests/data_pipeline/helper.rb b/tests/aws/requests/data_pipeline/helper.rb index cad039a65..d8ee6f58d 100644 --- a/tests/aws/requests/data_pipeline/helper.rb +++ b/tests/aws/requests/data_pipeline/helper.rb @@ -6,6 +6,14 @@ class AWS 'pipelineId' => String, } + FIELDS = [ + { + "key" => String, + "refValue" => Fog::Nullable::String, + "stringValue" => Fog::Nullable::String, + } + ] + LIST_PIPELINES = { "hasMoreResults" => Fog::Nullable::Boolean, "marker" => Fog::Nullable::String, @@ -23,19 +31,25 @@ class AWS "ids" => Fog::Nullable::Array, } + DESCRIBE_OBJECTS = { + "hasMoreResults" => Fog::Nullable::Boolean, + "marker" => Fog::Nullable::String, + "pipelineObjects" => [ + { + 'id' => String, + 'name' => String, + 'fields' => FIELDS, + } + ] + } + DESCRIBE_PIPELINES = { "pipelineDescriptionList" => [ { "description" => Fog::Nullable::String, "name" => String, "pipelineId" => String, - "fields" => [ - { - "key" => String, - "refValue" => Fog::Nullable::String, - "stringValue" => Fog::Nullable::String, - } - ] + "fields" => FIELDS, } ] } @@ -50,13 +64,7 @@ class AWS { "id" => String, "name" => String, - "fields" => [ - { - "key" => String, - "refValue" => Fog::Nullable::String, - "stringValue" => Fog::Nullable::String, - } - ] + "fields" => FIELDS, } ] } diff --git a/tests/aws/requests/data_pipeline/pipeline_tests.rb b/tests/aws/requests/data_pipeline/pipeline_tests.rb index 4f89f45ce..0caf79a19 100644 --- a/tests/aws/requests/data_pipeline/pipeline_tests.rb +++ b/tests/aws/requests/data_pipeline/pipeline_tests.rb @@ -64,6 +64,12 @@ Shindo.tests('AWS::DataPipeline | pipeline_tests', ['aws', 'data_pipeline']) do end end + tests('#describe_objects').formats(AWS::DataPipeline::Formats::DESCRIBE_OBJECTS) do + attempts = Fog::AWS[:data_pipeline].query_objects(@pipeline_id, 'ATTEMPT') + object_ids = attempts['ids'][0..5] + Fog::AWS[:data_pipeline].describe_objects(@pipeline_id, object_ids) + end + tests("#delete_pipeline").returns(true) do Fog::AWS[:data_pipeline].delete_pipeline(@pipeline_id) end From ecef54c8bcf8821a34fa6b1ff17a61ad7367a32e Mon Sep 17 00:00:00 2001 From: Thomas Kadauke Date: Tue, 4 Jun 2013 13:25:51 +0200 Subject: [PATCH 09/14] CRUD for OpenStack heat's Stack model --- lib/fog/core.rb | 1 + lib/fog/openstack.rb | 1 + .../openstack/models/orchestration/stack.rb | 52 +++++ .../openstack/models/orchestration/stacks.rb | 21 ++ lib/fog/openstack/orchestration.rb | 181 ++++++++++++++++++ .../requests/orchestration/create_stack.rb | 43 +++++ .../requests/orchestration/delete_stack.rb | 34 ++++ .../requests/orchestration/list_stacks.rb | 46 +++++ .../requests/orchestration/update_stack.rb | 41 ++++ lib/fog/orchestration.rb | 25 +++ 10 files changed, 445 insertions(+) create mode 100644 lib/fog/openstack/models/orchestration/stack.rb create mode 100644 lib/fog/openstack/models/orchestration/stacks.rb create mode 100644 lib/fog/openstack/orchestration.rb create mode 100644 lib/fog/openstack/requests/orchestration/create_stack.rb create mode 100644 lib/fog/openstack/requests/orchestration/delete_stack.rb create mode 100644 lib/fog/openstack/requests/orchestration/list_stacks.rb create mode 100644 lib/fog/openstack/requests/orchestration/update_stack.rb create mode 100644 lib/fog/orchestration.rb diff --git a/lib/fog/core.rb b/lib/fog/core.rb index 9dc976850..f6eda6cdc 100644 --- a/lib/fog/core.rb +++ b/lib/fog/core.rb @@ -47,3 +47,4 @@ require 'fog/cdn' require 'fog/dns' require 'fog/network' require 'fog/storage' +require 'fog/orchestration' diff --git a/lib/fog/openstack.rb b/lib/fog/openstack.rb index 71d4f45b8..eeea1cecb 100644 --- a/lib/fog/openstack.rb +++ b/lib/fog/openstack.rb @@ -48,6 +48,7 @@ module Fog service(:storage, 'openstack/storage', 'Storage') service(:volume, 'openstack/volume', 'Volume') service(:metering, 'openstack/metering', 'Metering') + service(:orchestration, 'openstack/orchestration', 'Orchestration') def self.authenticate(options, connection_options = {}) case options[:openstack_auth_uri].path diff --git a/lib/fog/openstack/models/orchestration/stack.rb b/lib/fog/openstack/models/orchestration/stack.rb new file mode 100644 index 000000000..ce8ad1928 --- /dev/null +++ b/lib/fog/openstack/models/orchestration/stack.rb @@ -0,0 +1,52 @@ +require 'fog/core/model' + +module Fog + module Orchestration + class OpenStack + class Stack < Fog::Model + identity :id + + attribute :stack_name + attribute :stack_status + attribute :stack_status_reason + attribute :creation_time + attribute :updated_time + attribute :id + + attribute :template_url + attribute :template + attribute :parameters + attribute :timeout_in_minutes + + def initialize(attributes) + # Old 'connection' is renamed as service and should be used instead + prepare_service_value(attributes) + super + end + + def save + requires :stack_name + identity ? update : create + end + + def create + requires :stack_name + service.create_stack(stack_name, self.attributes) + self + end + + def update + requires :stack_name + service.update_stack(stack_name, self.attributes) + self + end + + def destroy + requires :id + service.delete_stack(self.stack_name, self.id) + true + end + end + end + end +end diff --git a/lib/fog/openstack/models/orchestration/stacks.rb b/lib/fog/openstack/models/orchestration/stacks.rb new file mode 100644 index 000000000..74eebfbaa --- /dev/null +++ b/lib/fog/openstack/models/orchestration/stacks.rb @@ -0,0 +1,21 @@ +require 'fog/core/collection' +require 'fog/openstack/models/orchestration/stack' + +module Fog + module Orchestration + class OpenStack + class Stacks < Fog::Collection + model Fog::Orchestration::OpenStack::Stack + + def all + load(service.list_stacks.body['stacks']) + end + + def find_by_id(id) + self.find {|stack| stack.id == id} + end + alias_method :get, :find_by_id + end + end + end +end diff --git a/lib/fog/openstack/orchestration.rb b/lib/fog/openstack/orchestration.rb new file mode 100644 index 000000000..dceb3895f --- /dev/null +++ b/lib/fog/openstack/orchestration.rb @@ -0,0 +1,181 @@ +require 'fog/aws/cloud_formation' + +module Fog + module Orchestration + class OpenStack < Fog::Service + requires :openstack_auth_url + recognizes :openstack_auth_token, :openstack_management_url, + :persistent, :openstack_service_type, :openstack_service_name, + :openstack_tenant, + :openstack_api_key, :openstack_username, :openstack_identity_endpoint, + :current_user, :current_tenant, :openstack_region, + :openstack_endpoint_type + + model_path 'fog/openstack/models/orchestration' + model :stack + collection :stacks + + request_path 'fog/openstack/requests/orchestration' + request :create_stack + request :update_stack + request :delete_stack + request :list_stacks + + class Mock + + def initialize(options={}) + Fog::Mock.not_implemented + end + + end + + class Real + attr_reader :auth_token + attr_reader :auth_token_expiration + attr_reader :current_user + attr_reader :current_tenant + + def initialize(options={}) + @openstack_auth_token = options[:openstack_auth_token] + @auth_token = options[:openstack_auth_token] + @openstack_identity_public_endpoint = options[:openstack_identity_endpoint] + + unless @auth_token + missing_credentials = Array.new + @openstack_api_key = options[:openstack_api_key] + @openstack_username = options[:openstack_username] + + missing_credentials << :openstack_api_key unless @openstack_api_key + missing_credentials << :openstack_username unless @openstack_username + raise ArgumentError, "Missing required arguments: #{missing_credentials.join(', ')}" unless missing_credentials.empty? + end + + @openstack_tenant = options[:openstack_tenant] + @openstack_auth_uri = URI.parse(options[:openstack_auth_url]) + @openstack_management_url = options[:openstack_management_url] + @openstack_must_reauthenticate = false + @openstack_service_type = options[:openstack_service_type] || ['orchestration'] + @openstack_service_name = options[:openstack_service_name] + @openstack_identity_service_type = options[:openstack_identity_service_type] || 'identity' + @openstack_endpoint_type = options[:openstack_endpoint_type] || 'publicURL' + @openstack_region = options[:openstack_region] + + @connection_options = options[:connection_options] || {} + + @current_user = options[:current_user] + @current_tenant = options[:current_tenant] + + authenticate + + @persistent = options[:persistent] || false + @connection = Fog::Connection.new("#{@scheme}://#{@host}:#{@port}", @persistent, @connection_options) + end + + def credentials + { :provider => 'openstack', + :openstack_auth_url => @openstack_auth_uri.to_s, + :openstack_auth_token => @auth_token, + :openstack_management_url => @openstack_management_url, + :openstack_identity_endpoint => @openstack_identity_public_endpoint, + :openstack_region => @openstack_region, + :current_user => @current_user, + :current_tenant => @current_tenant } + end + + def reload + @connection.reset + end + + def request(params) + begin + response = @connection.request(params.merge({ + :headers => { + 'Content-Type' => 'application/json', + 'Accept' => 'application/json', + 'X-Auth-Token' => @auth_token + }.merge!(params[:headers] || {}), + :host => @host, + :path => "#{@path}/#{@tenant_id}/#{params[:path]}", + :query => params[:query] + })) + rescue Excon::Errors::Unauthorized => error + if error.response.body != 'Bad username or password' # token expiration + @openstack_must_reauthenticate = true + authenticate + retry + else # Bad Credentials + raise error + end + rescue Excon::Errors::HTTPStatusError => error + raise case error + when Excon::Errors::NotFound + Fog::Compute::OpenStack::NotFound.slurp(error) + else + error + end + end + + if response.status == 200 && !response.body.empty? + response.body = Fog::JSON.decode(response.body) + end + + response + end + + private + + def authenticate + if !@openstack_management_url || @openstack_must_reauthenticate + options = { + :openstack_api_key => @openstack_api_key, + :openstack_username => @openstack_username, + :openstack_auth_token => @auth_token, + :openstack_auth_uri => @openstack_auth_uri, + :openstack_region => @openstack_region, + :openstack_tenant => @openstack_tenant, + :openstack_service_type => @openstack_service_type, + :openstack_service_name => @openstack_service_name, + :openstack_identity_service_type => @openstack_identity_service_type, + :openstack_endpoint_type => @openstack_endpoint_type + } + + if @openstack_auth_uri.path =~ /\/v2.0\// + + credentials = Fog::OpenStack.authenticate_v2(options, @connection_options) + else + credentials = Fog::OpenStack.authenticate_v1(options, @connection_options) + end + + @current_user = credentials[:user] + @current_tenant = credentials[:tenant] + + @openstack_must_reauthenticate = false + @auth_token = credentials[:token] + @auth_token_expiration = credentials[:expires] + @openstack_management_url = credentials[:server_management_url] + @openstack_identity_public_endpoint = credentials[:identity_public_endpoint] + end + + uri = URI.parse(@openstack_management_url) + @host = uri.host + @path, @tenant_id = uri.path.scan(/(\/.*)\/(.*)/).flatten + + @path.sub!(/\/$/, '') + + @port = uri.port + @scheme = uri.scheme + + # Not all implementations have identity service in the catalog + if @openstack_identity_public_endpoint || @openstack_management_url + @identity_connection = Fog::Connection.new( + @openstack_identity_public_endpoint || @openstack_management_url, + false, @connection_options) + end + + true + end + + end + end + end +end diff --git a/lib/fog/openstack/requests/orchestration/create_stack.rb b/lib/fog/openstack/requests/orchestration/create_stack.rb new file mode 100644 index 000000000..641eebbde --- /dev/null +++ b/lib/fog/openstack/requests/orchestration/create_stack.rb @@ -0,0 +1,43 @@ +module Fog + module Orchestration + class OpenStack + class Real + + # Create a stack. + # + # * stack_name [String] Name of the stack to create. + # * options [Hash]: + # * :template_body [String] Structure containing the template body. + # or (one of the two Template parameters is required) + # * :template_url [String] URL of file containing the template body. + # * :disable_rollback [Boolean] Controls rollback on stack creation failure, defaults to false. + # * :parameters [Hash] Hash of providers to supply to template + # * :timeout_in_minutes [Integer] Minutes to wait before status is set to CREATE_FAILED + # + # @see http://docs.amazonwebservices.com/AWSCloudFormation/latest/APIReference/API_CreateStack.html + + def create_stack(stack_name, options = {}) + params = { + :stack_name => stack_name + }.merge(options) + + request( + :path => 'stacks', + :method => 'POST', + :body => MultiJson.encode(params) + ) + end + + end + + class Mock + def create_stack(stack_name, options = {}) + response = Excon::Response.new + response.status = 202 + response.body = {} + response + end + end + end + end +end diff --git a/lib/fog/openstack/requests/orchestration/delete_stack.rb b/lib/fog/openstack/requests/orchestration/delete_stack.rb new file mode 100644 index 000000000..106e68b38 --- /dev/null +++ b/lib/fog/openstack/requests/orchestration/delete_stack.rb @@ -0,0 +1,34 @@ +module Fog + module Orchestration + class OpenStack + class Real + + # Delete a stack. + # + # @param stack_name [String] Name of the stack to delete. + # @param stack_id [String] ID of the stack to delete. + # + # @return [Excon::Response] + # + # @see http://docs.amazonwebservices.com/AWSCloudFormation/latest/APIReference/API_DeleteStack.html + + def delete_stack(stack_name, stack_id) + request( + :path => "stacks/#{stack_name}/#{stack_id}", + :method => 'DELETE' + ) + end + + end + + class Mock + def delete_stack(stack_name, stack_id) + response = Excon::Response.new + response.status = 202 + response.body = {} + response + end + end + end + end +end diff --git a/lib/fog/openstack/requests/orchestration/list_stacks.rb b/lib/fog/openstack/requests/orchestration/list_stacks.rb new file mode 100644 index 000000000..e8b45634b --- /dev/null +++ b/lib/fog/openstack/requests/orchestration/list_stacks.rb @@ -0,0 +1,46 @@ +module Fog + module Orchestration + class OpenStack + class Real + + # List stacks. + # + # @param options [Hash] + # + # @return [Excon::Response] + # * body [Hash]: + # * stacks [Array] - Matching stacks + # * stack [Hash]: + # * id [String] - + # * stack_name [String] - + # * description [String] + # * links [Array] + # * stack_status [String] - + # * stack_status_reason [String] + # * creation_time [Time] - + # * updated_time [Time] - + # + # + # @see http://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/API_ListStacks.html + + def list_stacks(options = {}) + request( + :path => 'stacks', + :method => 'GET', + :query => options + ) + end + + end + + class Mock + def list_stacks(options = {}) + Excon::Response.new( + :body => { 'stacks' => [] }, + :status => 200 + ) + end + end + end + end +end diff --git a/lib/fog/openstack/requests/orchestration/update_stack.rb b/lib/fog/openstack/requests/orchestration/update_stack.rb new file mode 100644 index 000000000..222ca388f --- /dev/null +++ b/lib/fog/openstack/requests/orchestration/update_stack.rb @@ -0,0 +1,41 @@ +module Fog + module Orchestration + class OpenStack + class Real + + # Update a stack. + # + # @param [String] stack_name Name of the stack to update. + # @param [Hash] options + # * :template_body [String] Structure containing the template body. + # or (one of the two Template parameters is required) + # * :template_url [String] URL of file containing the template body. + # * :parameters [Hash] Hash of providers to supply to template. + # + # @see http://docs.amazonwebservices.com/AWSCloudFormation/latest/APIReference/API_UpdateStack.html + # + def update_stack(stack_name, options = {}) + params = { + :stack_name => stack_name + }.merge(options) + + request( + :path => 'stacks', + :method => 'PUT', + :body => MultiJson.encode(params) + ) + end + + end + + class Mock + def update_stack(stack_name, options = {}) + response = Excon::Response.new + response.status = 202 + response.body = {} + response + end + end + end + end +end diff --git a/lib/fog/orchestration.rb b/lib/fog/orchestration.rb new file mode 100644 index 000000000..bd641bac2 --- /dev/null +++ b/lib/fog/orchestration.rb @@ -0,0 +1,25 @@ +module Fog + module Orchestration + + def self.[](provider) + self.new(:provider => provider) + end + + def self.new(attributes) + attributes = attributes.dup # Prevent delete from having side effects + provider = attributes.delete(:provider).to_s.downcase.to_sym + + if self.providers.include?(provider) + require "fog/#{provider}/network" + return Fog::Orchestration.const_get(Fog.providers[provider]).new(attributes) + end + + raise ArgumentError.new("#{provider} has no orchestration service") + end + + def self.providers + Fog.services[:orchestration] + end + + end +end From 94570893470c0b12ffa4ef634c0bde4cf5306e91 Mon Sep 17 00:00:00 2001 From: Thomas Kadauke Date: Tue, 4 Jun 2013 14:34:55 +0200 Subject: [PATCH 10/14] Tests for previous commit --- lib/fog/bin/openstack.rb | 5 ++ lib/fog/openstack/orchestration.rb | 46 ++++++++++++++++++- .../requests/orchestration/stack_tests.rb | 30 ++++++++++++ 3 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 tests/openstack/requests/orchestration/stack_tests.rb diff --git a/lib/fog/bin/openstack.rb b/lib/fog/bin/openstack.rb index 8b837c2ab..18bb1e9bf 100644 --- a/lib/fog/bin/openstack.rb +++ b/lib/fog/bin/openstack.rb @@ -17,6 +17,8 @@ class OpenStack < Fog::Bin Fog::Volume::OpenStack when :metering Fog::Metering::OpenStack + when :orchestration + Fog::Orchestration::OpenStack else raise ArgumentError, "Unrecognized service: #{key}" end @@ -46,6 +48,9 @@ class OpenStack < Fog::Bin when :metering Fog::Logger.warning("OpenStack[:metering] is not recommended, use Metering[:openstack] for portability") Fog::Metering.new(:provider => 'OpenStack') + when :orchestration + Fog::Logger.warning("OpenStack[:orchestration] is not recommended, use Orchestration[:openstack] for portability") + Fog::Orchestration.new(:provider => 'OpenStack') else raise ArgumentError, "Unrecognized service: #{key.inspect}" end diff --git a/lib/fog/openstack/orchestration.rb b/lib/fog/openstack/orchestration.rb index dceb3895f..c57a83000 100644 --- a/lib/fog/openstack/orchestration.rb +++ b/lib/fog/openstack/orchestration.rb @@ -22,11 +22,53 @@ module Fog request :list_stacks class Mock + attr_reader :auth_token + attr_reader :auth_token_expiration + attr_reader :current_user + attr_reader :current_tenant - def initialize(options={}) - Fog::Mock.not_implemented + def self.data + @data ||= {} end + def self.reset + @data = nil + end + + def initialize(options={}) + @openstack_username = options[:openstack_username] + @openstack_auth_uri = URI.parse(options[:openstack_auth_url]) + + @current_tenant = options[:openstack_tenant] + + @auth_token = Fog::Mock.random_base64(64) + @auth_token_expiration = (Time.now.utc + 86400).iso8601 + + management_url = URI.parse(options[:openstack_auth_url]) + management_url.port = 8774 + management_url.path = '/v1' + @openstack_management_url = management_url.to_s + + identity_public_endpoint = URI.parse(options[:openstack_auth_url]) + identity_public_endpoint.port = 5000 + @openstack_identity_public_endpoint = identity_public_endpoint.to_s + end + + def data + self.class.data["#{@openstack_username}-#{@current_tenant}"] + end + + def reset_data + self.class.data.delete("#{@openstack_username}-#{@current_tenant}") + end + + def credentials + { :provider => 'openstack', + :openstack_auth_url => @openstack_auth_uri.to_s, + :openstack_auth_token => @auth_token, + :openstack_management_url => @openstack_management_url, + :openstack_identity_endpoint => @openstack_identity_public_endpoint } + end end class Real diff --git a/tests/openstack/requests/orchestration/stack_tests.rb b/tests/openstack/requests/orchestration/stack_tests.rb new file mode 100644 index 000000000..bc2677ccc --- /dev/null +++ b/tests/openstack/requests/orchestration/stack_tests.rb @@ -0,0 +1,30 @@ +Shindo.tests('Fog::Orchestration[:openstack] | stack requests', ['openstack']) do + @stack_format = { + 'links' => Array, + 'id' => String, + 'stack_name' => String, + 'description' => String, + 'stack_status' => String, + 'stack_status_reason' => String, + 'creation_time' => Time, + 'updated_time' => Time + } + + tests('success') do + tests('#create_stack("teststack")').formats({}) do + Fog::Orchestration[:openstack].create_stack("teststack").body + end + + tests('#list_stacks').formats({'stacks' => [@stack_format]}) do + Fog::Orchestration[:openstack].list_stacks.body + end + + tests('#update_stack("teststack")').formats({}) do + Fog::Orchestration[:openstack].update_stack("teststack").body + end + + tests('#delete_stack("teststack", "id")').formats({}) do + Fog::Orchestration[:openstack].delete_stack("teststack", "id").body + end + end +end From 704e33e7d81f684b15e61f03506142df4127621d Mon Sep 17 00:00:00 2001 From: Thomas Kadauke Date: Tue, 4 Jun 2013 16:00:39 +0200 Subject: [PATCH 11/14] Store mock stacks in memory to make tests more realistic --- lib/fog/openstack/orchestration.rb | 6 +++++- .../openstack/requests/orchestration/create_stack.rb | 12 ++++++++++++ .../openstack/requests/orchestration/delete_stack.rb | 4 +++- .../openstack/requests/orchestration/list_stacks.rb | 4 +++- .../openstack/requests/orchestration/stack_tests.rb | 2 +- 5 files changed, 24 insertions(+), 4 deletions(-) diff --git a/lib/fog/openstack/orchestration.rb b/lib/fog/openstack/orchestration.rb index c57a83000..3b2fcde9a 100644 --- a/lib/fog/openstack/orchestration.rb +++ b/lib/fog/openstack/orchestration.rb @@ -28,7 +28,11 @@ module Fog attr_reader :current_tenant def self.data - @data ||= {} + @data ||= Hash.new do |hash, key| + hash[key] = { + :stacks => {} + } + end end def self.reset diff --git a/lib/fog/openstack/requests/orchestration/create_stack.rb b/lib/fog/openstack/requests/orchestration/create_stack.rb index 641eebbde..f82e8424a 100644 --- a/lib/fog/openstack/requests/orchestration/create_stack.rb +++ b/lib/fog/openstack/requests/orchestration/create_stack.rb @@ -32,6 +32,18 @@ module Fog class Mock def create_stack(stack_name, options = {}) + stack_id = Fog::Mock.random_hex(32) + stack = self.data[:stacks][stack_id] = { + 'id' => stack_id, + 'stack_name' => stack_name, + 'links' => [], + 'description' => options[:description], + 'stack_status' => 'CREATE_COMPLETE', + 'stack_status_reason' => 'Stack successfully created', + 'creation_time' => Time.now, + 'updated_time' => Time.now + } + response = Excon::Response.new response.status = 202 response.body = {} diff --git a/lib/fog/openstack/requests/orchestration/delete_stack.rb b/lib/fog/openstack/requests/orchestration/delete_stack.rb index 106e68b38..400274375 100644 --- a/lib/fog/openstack/requests/orchestration/delete_stack.rb +++ b/lib/fog/openstack/requests/orchestration/delete_stack.rb @@ -20,9 +20,11 @@ module Fog end end - + class Mock def delete_stack(stack_name, stack_id) + self.data[:stacks].delete(stack_id) + response = Excon::Response.new response.status = 202 response.body = {} diff --git a/lib/fog/openstack/requests/orchestration/list_stacks.rb b/lib/fog/openstack/requests/orchestration/list_stacks.rb index e8b45634b..2b54a238c 100644 --- a/lib/fog/openstack/requests/orchestration/list_stacks.rb +++ b/lib/fog/openstack/requests/orchestration/list_stacks.rb @@ -35,8 +35,10 @@ module Fog class Mock def list_stacks(options = {}) + stacks = self.data[:stacks].values + Excon::Response.new( - :body => { 'stacks' => [] }, + :body => { 'stacks' => stacks }, :status => 200 ) end diff --git a/tests/openstack/requests/orchestration/stack_tests.rb b/tests/openstack/requests/orchestration/stack_tests.rb index bc2677ccc..0e6530e86 100644 --- a/tests/openstack/requests/orchestration/stack_tests.rb +++ b/tests/openstack/requests/orchestration/stack_tests.rb @@ -3,7 +3,7 @@ Shindo.tests('Fog::Orchestration[:openstack] | stack requests', ['openstack']) d 'links' => Array, 'id' => String, 'stack_name' => String, - 'description' => String, + 'description' => Fog::Nullable::String, 'stack_status' => String, 'stack_status_reason' => String, 'creation_time' => Time, From 116d004acfd72fa77be6844bfb5acf517da5ba78 Mon Sep 17 00:00:00 2001 From: Nick Osborn Date: Wed, 11 Sep 2013 12:00:26 +0100 Subject: [PATCH 12/14] [vcloud_director] Fix copy & paste fail. --- lib/fog/vcloud_director/requests/compute/post_task_cancel.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/fog/vcloud_director/requests/compute/post_task_cancel.rb b/lib/fog/vcloud_director/requests/compute/post_task_cancel.rb index 5ec8246bb..e4226ba51 100644 --- a/lib/fog/vcloud_director/requests/compute/post_task_cancel.rb +++ b/lib/fog/vcloud_director/requests/compute/post_task_cancel.rb @@ -10,7 +10,7 @@ module Fog # # === Returns # * response<~Excon::Response> - # # {Amazon API Reference}[http://docs.amazonwebservices.com/AWSEC2/2012-03-01/APIReference/ApiReference-query-DeleteNetworkInterface.html] + # def post_task_cancel(task_id) request( :expects => 204, From 4712c0427e21b152e041bbf18562a3f81bb6ccd5 Mon Sep 17 00:00:00 2001 From: Dan Prince Date: Wed, 11 Sep 2013 08:18:05 -0400 Subject: [PATCH 13/14] Minor whitespace cleanups. --- lib/fog/openstack/orchestration.rb | 2 +- lib/fog/openstack/requests/orchestration/create_stack.rb | 4 ++-- lib/fog/openstack/requests/orchestration/delete_stack.rb | 2 +- lib/fog/openstack/requests/orchestration/list_stacks.rb | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/fog/openstack/orchestration.rb b/lib/fog/openstack/orchestration.rb index 3b2fcde9a..c344afc56 100644 --- a/lib/fog/openstack/orchestration.rb +++ b/lib/fog/openstack/orchestration.rb @@ -210,7 +210,7 @@ module Fog @port = uri.port @scheme = uri.scheme - + # Not all implementations have identity service in the catalog if @openstack_identity_public_endpoint || @openstack_management_url @identity_connection = Fog::Connection.new( diff --git a/lib/fog/openstack/requests/orchestration/create_stack.rb b/lib/fog/openstack/requests/orchestration/create_stack.rb index f82e8424a..e19d35c79 100644 --- a/lib/fog/openstack/requests/orchestration/create_stack.rb +++ b/lib/fog/openstack/requests/orchestration/create_stack.rb @@ -4,7 +4,7 @@ module Fog class Real # Create a stack. - # + # # * stack_name [String] Name of the stack to create. # * options [Hash]: # * :template_body [String] Structure containing the template body. @@ -15,7 +15,7 @@ module Fog # * :timeout_in_minutes [Integer] Minutes to wait before status is set to CREATE_FAILED # # @see http://docs.amazonwebservices.com/AWSCloudFormation/latest/APIReference/API_CreateStack.html - + def create_stack(stack_name, options = {}) params = { :stack_name => stack_name diff --git a/lib/fog/openstack/requests/orchestration/delete_stack.rb b/lib/fog/openstack/requests/orchestration/delete_stack.rb index 400274375..ba179fb4a 100644 --- a/lib/fog/openstack/requests/orchestration/delete_stack.rb +++ b/lib/fog/openstack/requests/orchestration/delete_stack.rb @@ -11,7 +11,7 @@ module Fog # @return [Excon::Response] # # @see http://docs.amazonwebservices.com/AWSCloudFormation/latest/APIReference/API_DeleteStack.html - + def delete_stack(stack_name, stack_id) request( :path => "stacks/#{stack_name}/#{stack_id}", diff --git a/lib/fog/openstack/requests/orchestration/list_stacks.rb b/lib/fog/openstack/requests/orchestration/list_stacks.rb index 2b54a238c..ab083285b 100644 --- a/lib/fog/openstack/requests/orchestration/list_stacks.rb +++ b/lib/fog/openstack/requests/orchestration/list_stacks.rb @@ -4,7 +4,7 @@ module Fog class Real # List stacks. - # + # # @param options [Hash] # # @return [Excon::Response] @@ -22,7 +22,7 @@ module Fog # # # @see http://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/API_ListStacks.html - + def list_stacks(options = {}) request( :path => 'stacks', From a47f11a7ec1f89c6c3874205cc46866d800d6774 Mon Sep 17 00:00:00 2001 From: Nick Osborn Date: Thu, 12 Sep 2013 09:46:42 +0100 Subject: [PATCH 14/14] [vcloud_director] Allow for multiple Orgs, rephrased. --- lib/fog/vcloud_director/models/compute/organizations.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/fog/vcloud_director/models/compute/organizations.rb b/lib/fog/vcloud_director/models/compute/organizations.rb index dbb1728b1..cfe20dfcf 100644 --- a/lib/fog/vcloud_director/models/compute/organizations.rb +++ b/lib/fog/vcloud_director/models/compute/organizations.rb @@ -20,7 +20,7 @@ module Fog def item_list data = service.get_organizations.body - orgs = data[:Org].is_a?(Array) ? data[:Org] : [data[:Org]] + orgs = data[:Org].is_a?(Hash) ? [data[:Org]] : data[:Org] orgs.each {|org| service.add_id_from_href!(org)} orgs end