diff --git a/lib/fog/openstack/docs/orchestration.md b/lib/fog/openstack/docs/orchestration.md new file mode 100644 index 000000000..33363ce6a --- /dev/null +++ b/lib/fog/openstack/docs/orchestration.md @@ -0,0 +1,318 @@ +# OpenStack Orchestration +The mission of the OpenStack Orchestration program is to create a human- and machine-accessible service for managing the entire lifecycle of infrastructure and applications within OpenStack clouds. + +## Heat +Heat is the main project in the OpenStack Orchestration program. It implements an orchestration engine to launch multiple composite cloud applications based on templates in the form of text files that can be treated like code. A native Heat template format is evolving, but Heat also endeavours to provide compatibility with the AWS CloudFormation template format, so that many existing CloudFormation templates can be launched on OpenStack. Heat provides both an OpenStack-native ReST API and a CloudFormation-compatible Query API. + +*Why ‘Heat’? It makes the clouds rise!* + +**How it works** + +* A Heat template describes the infrastructure for a cloud application in a text file that is readable and writable by humans, and can be checked into version control, diffed, &c. +* Infrastructure resources that can be described include: servers, floating ips, volumes, security groups, users, etc. +* Heat also provides an autoscaling service that integrates with Ceilometer, so you can include a scaling group as a resource in a template. +* Templates can also specify the relationships between resources (e.g. this volume is connected to this server). This enables Heat to call out to the OpenStack APIs to create all of your infrastructure in the correct order to completely launch your application. +* Heat manages the whole lifecycle of the application - when you need to change your infrastructure, simply modify the template and use it to update your existing stack. Heat knows how to make the necessary changes. It will delete all of the resources when you are finished with the application, too. +* Heat primarily manages infrastructure, but the templates integrate well with software configuration management tools such as Puppet and Chef. The Heat team is working on providing even better integration between infrastructure and software. + +_Source: [OpenStack Wiki](https://wiki.openstack.org/wiki/Heat)_ + +# OpenStack Orchestration (Heat) Client + +[Full OpenStack Orchestration/Heat API Docs](http://developer.openstack.org/api-ref-orchestration-v1.html) + +## Orchestration Service +Get a handle on the Orchestration service: + +```ruby +service = Fog::Orchestration::OpenStack.new({ + :openstack_auth_url => 'http://KEYSTONE_HOST:KEYSTONE_PORT/v2.0/tokens', # OpenStack Keystone endpoint + :openstack_username => OPEN_STACK_USER, # Your OpenStack Username + :openstack_tenant => OPEN_STACK_TENANT, # Your tenant id + :openstack_api_key => OPEN_STACK_PASSWORD, # Your OpenStack Password + :connection_options => {} # Optional +}) +``` +We will use this `service` to interact with the Orchestration resources, `stack`, `event`, `resource`, and `template` + +## Stacks + +Get a list of stacks you own: + +```ruby +service.stacks +``` +This returns a list of stacks with minimum attributes, leaving other attributes empty +```ruby +=> "http://10.8.96.4:8004/v1/5d139d95546240748508b2a518aa5bef/stacks/stack4/0b8e4060-419b-416b-a927-097d4afbf26d", "rel"=>"self"}], + notification_topics=nil, + outputs=nil, + parameters=nil, + stack_name="stack4", + stack_status="UPDATE_COMPLETE", + stack_status_reason="Stack successfully updated", + template_description=nil, + timeout_mins=nil, + creation_time="2014-08-27T21:25:56Z", + updated_time="2015-01-30T20:10:43Z" + >, + ... +``` + +Create a new `stack` with a [Heat Template (HOT)](http://docs.openstack.org/developer/heat/template_guide/hot_guide.html) or an AWS CloudFormation Template (CFN): + +```ruby +raw_template = File.read(TEMPLATE_FILE) +service.stacks.new.save({ + :stack_name => "a_name_for_stack", + :template => raw_template, + :parameters => {"flavor" => "m1.small", "image" => "cirror"} +}) +``` +This returns a JSON blob filled with information about our new stack: + +```ruby + {"id"=>"53b35fbe-34f7-4837-b0f8-8863b7263b7d", + "links"=>[{"href"=>"http://10.8.96.4:8004/v1/5d139d95546240748508b2a518aa5bef/stacks/a_name_for_stack/53b35fbe-34f7-4837-b0f8-8863b7263b7d", + "rel"=>"self"}]} + ``` + +We can get a reference to the stack using its `stack_name` and `id`: + +```ruby +stack = service.stacks.get("stack4", "0b8e4060-419b-416b-a927-097d4afbf26d") +``` +This returns a stack with all attributes filled +```ruby +=> "http://10.8.96.4:8004/v1/5d139d95546240748508b2a518aa5bef/stacks/stack4/0b8e4060-419b-416b-a927-097d4afbf26d", "rel"=>"self"}], + notification_topics=[], + outputs=[], + parameters={"AWS::StackId"=>"arn:openstack:heat::5d139d95546240748508b2a518aa5bef:stacks/stack4/0b8e4060-419b-416b-a927-097d4afbf26d", "AWS::Region"=>"ap-southeast-1", "AWS::StackName"=>"stack4"}, + stack_name="stack4", + stack_status="UPDATE_COMPLETE", + stack_status_reason="Stack successfully updated", + template_description="Simple template to deploy a single compute instance", + timeout_mins=60, + creation_time="2014-08-27T21:25:56Z", + updated_time="2015-01-30T20:10:43Z" + > +``` +It can be also obtained through the details method of a simple stack object +```ruby +stack.details +``` + +A stack knows about related `events`: + +```ruby +stack.events +=> "http://10.8.96.4:8004/v1/5d139d95546240748508b2a518aa5bef/stacks/a_name_for_stack/53b35fbe-34f7-4837-b0f8-8863b7263b7d/resources/my_instance/events/251", "rel"=>"self"}, {"href"=>"http://10.8.96.4:8004/v1/5d139d95546240748508b2a518aa5bef/stacks/a_name_for_stack/53b35fbe-34f7-4837-b0f8-8863b7263b7d/resources/my_instance", "rel"=>"resource"}, {"href"=>"http://10.8.96.4:8004/v1/5d139d95546240748508b2a518aa5bef/stacks/a_name_for_stack/53b35fbe-34f7-4837-b0f8-8863b7263b7d", "rel"=>"stack"}], + logical_resource_id="my_instance", + resource_status="CREATE_IN_PROGRESS", + resource_status_reason="state changed", + physical_resource_id=nil + >, +``` +A stack knows about related `resources`: + +```ruby +stack.resources +=> "http://10.8.96.4:8004/v1/5d139d95546240748508b2a518aa5bef/stacks/progenerated/0c9ee370-ef64-4a80-a6cc-65d2277caeb9/resources/my_instance", "rel"=>"self"}, {"href"=>"http://10.8.96.4:8004/v1/5d139d95546240748508b2a518aa5bef/stacks/progenerated/0c9ee370-ef64-4a80-a6cc-65d2277caeb9", "rel"=>"stack"}], + logical_resource_id="my_instance", + resource_status="CREATE_COMPLETE", + updated_time="2014-09-12T20:44:06Z", + required_by=[], + resource_status_reason="state changed", + resource_type="OS::Nova::Server" + > + ] + > +``` + +You can get a stack's `template` + +```ruby +stack.template +=> #"", :headers=>{"Content-Type"=>"text/html; charset=UTF-8", "Content-Length"=>"0", "Date"=>"Wed, 21 Jan 2015 20:38:00 GMT"}, :status=>204, :reason_phrase=>"No Content", :remote_ip=>"10.8.96.4", :local_port=>59628, :local_address=>"10.17.68.186"}, @body="", @headers={"Content-Type"=>"text/html; charset=UTF-8", "Content-Length"=>"0", "Date"=>"Wed, 21 Jan 2015 20:38:00 GMT"}, @status=204, @remote_ip="10.8.96.4", @local_port=59628, @local_address="10.17.68.186"> +``` + +Reload any object by calling `reload` on it: + +```ruby +stacks.reload +=> +``` + +## Events + +You can list `Events` of a `stack`: + +```ruby +stack.events +=> "http://10.8.96.4:8004/v1/5d139d95546240748508b2a518aa5bef/stacks/progenerated/0c9ee370-ef64-4a80-a6cc-65d2277caeb9/resources/my_instance/events/15", "rel"=>"self"}, {"href"=>"http://10.8.96.4:8004/v1/5d139d95546240748508b2a518aa5bef/stacks/progenerated/0c9ee370-ef64-4a80-a6cc-65d2277caeb9/resources/my_instance", "rel"=>"resource"}, {"href"=>"http://10.8.96.4:8004/v1/5d139d95546240748508b2a518aa5bef/stacks/progenerated/0c9ee370-ef64-4a80-a6cc-65d2277caeb9", "rel"=>"stack"}], + logical_resource_id="my_instance", + resource_status="CREATE_IN_PROGRESS", + resource_status_reason="state changed", + physical_resource_id=nil + >, +``` + +`Event` can be got through corresponding `resource` + +```ruby +event = service.events.get(stack, resource, event_id) +=> "http://10.8.96.4:8004/v1/5d139d95546240748508b2a518aa5bef/stacks/progenerated/0c9ee370-ef64-4a80-a6cc-65d2277caeb9/resources/my_instance/events/15", "rel"=>"self"}, {"href"=>"http://10.8.96.4:8004/v1/5d139d95546240748508b2a518aa5bef/stacks/progenerated/0c9ee370-ef64-4a80-a6cc-65d2277caeb9/resources/my_instance", "rel"=>"resource"}, {"href"=>"http://10.8.96.4:8004/v1/5d139d95546240748508b2a518aa5bef/stacks/progenerated/0c9ee370-ef64-4a80-a6cc-65d2277caeb9", "rel"=>"stack"}], + logical_resource_id="my_instance", + resource_status="CREATE_IN_PROGRESS", + resource_status_reason="state changed", + physical_resource_id=nil + > +``` + +An `event` knows about its associated `stack`: + +```ruby +event.stack +=> "http://10.8.96.4:8004/v1/5d139d95546240748508b2a518aa5bef/stacks/progenerated/0c9ee370-ef64-4a80-a6cc-65d2277caeb9", "rel"=>"self"}], + stack_status_reason="Stack create completed successfully", + stack_name="progenerated", + creation_time="2014-09-12T20:43:58Z", + updated_time="2014-09-12T20:44:06Z" + > +``` +An `event` has an associated `resource`: + +```ruby +resource = event.resource +=> "http://10.8.96.4:8004/v1/5d139d95546240748508b2a518aa5bef/stacks/progenerated/0c9ee370-ef64-4a80-a6cc-65d2277caeb9/resources/my_instance", "rel"=>"self"}, {"href"=>"http://10.8.96.4:8004/v1/5d139d95546240748508b2a518aa5bef/stacks/progenerated/0c9ee370-ef64-4a80-a6cc-65d2277caeb9", "rel"=>"stack"}], + logical_resource_id="my_instance", + resource_status="CREATE_COMPLETE", + updated_time="2014-09-12T20:44:06Z", + required_by=[], + resource_status_reason="state changed", + resource_type="OS::Nova::Server" + > +``` + +## Resource + +`resources` might be nested: + +```ruby +service.resources.all(stack, {:nested_depth => 1}) +=> "http://10.8.96.4:8004/v1/5d139d95546240748508b2a518aa5bef/stacks/progenerated/0c9ee370-ef64-4a80-a6cc-65d2277caeb9/resources/my_instance", "rel"=>"self"}, {"href"=>"http://10.8.96.4:8004/v1/5d139d95546240748508b2a518aa5bef/stacks/progenerated/0c9ee370-ef64-4a80-a6cc-65d2277caeb9", "rel"=>"stack"}], + logical_resource_id="my_instance", + resource_status="CREATE_COMPLETE", + updated_time="2014-09-12T20:44:06Z", + required_by=[], + resource_status_reason="state changed", + resource_type="OS::Nova::Server" + > + ] + > +``` + +A `resource` knows about its associated `stack`: + +```ruby +resource.stack +=> "http://10.8.96.4:8004/v1/5d139d95546240748508b2a518aa5bef/stacks/progenerated/0c9ee370-ef64-4a80-a6cc-65d2277caeb9", "rel"=>"self"}], + stack_status_reason="Stack create completed successfully", + stack_name="progenerated", + creation_time="2014-09-12T20:43:58Z", + updated_time="2014-09-12T20:44:06Z" + > +``` + +Resource metadata is visible: + +```ruby +irb: resource.metadata +=> {} +``` + +A `resource's` template is visible (if one exists) + +```ruby +irb: resource.template +=> nil +``` + +## Validation +You can validate a template (either HOT or CFN) before using it: + +```ruby +service.templates.validate(:template => content) +=> +``` diff --git a/lib/fog/openstack/models/orchestration/event.rb b/lib/fog/openstack/models/orchestration/event.rb new file mode 100644 index 000000000..c84e78d23 --- /dev/null +++ b/lib/fog/openstack/models/orchestration/event.rb @@ -0,0 +1,20 @@ +require 'fog/core/model' + +module Fog + module Orchestration + class OpenStack + class Event < Fog::Model + + include Reflectable + + identity :id + + %w{resource_name event_time links logical_resource_id resource_status + resource_status_reason physical_resource_id}.each do |a| + attribute a.to_sym + end + + end + end + end +end diff --git a/lib/fog/openstack/models/orchestration/events.rb b/lib/fog/openstack/models/orchestration/events.rb new file mode 100644 index 000000000..1e57951c4 --- /dev/null +++ b/lib/fog/openstack/models/orchestration/events.rb @@ -0,0 +1,28 @@ +require 'fog/openstack/models/orchestration/event' + +module Fog + module Orchestration + class OpenStack + class Events < Fog::Collection + model Fog::Orchestration::OpenStack::Event + + def all(obj, options={}) + data = if obj.is_a?(Stack) + service.list_stack_events(obj, options).body['events'] + else + service.list_resource_events(obj.stack, obj, options).body['events'] + end + + load data + end + + def get(stack, resource, event_id) + data = service.show_event_details(stack, resource, event_id).body['event'] + new(data) + rescue Fog::Compute::OpenStack::NotFound + nil + end + end + end + end +end diff --git a/lib/fog/openstack/models/orchestration/resource.rb b/lib/fog/openstack/models/orchestration/resource.rb new file mode 100644 index 000000000..802de6d60 --- /dev/null +++ b/lib/fog/openstack/models/orchestration/resource.rb @@ -0,0 +1,32 @@ +require 'fog/core/model' + +module Fog + module Orchestration + class OpenStack + class Resource < Fog::Model + + include Reflectable + + identity :id + + %w{resource_name description links logical_resource_id resource_status + updated_time required_by resource_status_reason resource_type}.each do |a| + attribute a.to_sym + end + + def events(options={}) + @events ||= service.events.all(self, options) + end + + def metadata + @metadata ||= service.show_resource_metadata(stack, self.resource_name).body['metadata'] + end + + def template + @template ||= service.templates.get(self) + end + + end + end + end +end diff --git a/lib/fog/openstack/models/orchestration/resource_schemas.rb b/lib/fog/openstack/models/orchestration/resource_schemas.rb new file mode 100644 index 000000000..a0e6ee23f --- /dev/null +++ b/lib/fog/openstack/models/orchestration/resource_schemas.rb @@ -0,0 +1,17 @@ +require 'fog/core/collection' + +module Fog + module Orchestration + class OpenStack + class ResourceSchemas < Fog::Collection + + def get(resource_type) + service.show_resource_schema(resource_type).body + rescue Fog::Compute::OpenStack::NotFound + nil + end + + end + end + end +end diff --git a/lib/fog/openstack/models/orchestration/resources.rb b/lib/fog/openstack/models/orchestration/resources.rb new file mode 100644 index 000000000..cee81fe91 --- /dev/null +++ b/lib/fog/openstack/models/orchestration/resources.rb @@ -0,0 +1,35 @@ +require 'fog/openstack/models/orchestration/resource' + +module Fog + module Orchestration + class OpenStack + class Resources < Fog::Collection + model Fog::Orchestration::OpenStack::Resource + + def types + service.list_resource_types.body['resource_types'].sort + end + + def all(stack, options={}) + data = service.list_resources(stack, options).body['resources'] + load(data) + end + + def get(resource_name, stack=nil) + stack = self.first.stack if stack.nil? + data = service.show_resource_data(stack.stack_name, stack.id, resource_name).body['resource'] + new(data) + rescue Fog::Compute::OpenStack::NotFound + nil + end + + def metadata(stack_name, stack_id, resource_name) + service.show_resource_metadata(stack_name, stack_id, resource_name).body['resource'] + rescue Fog::Compute::OpenStack::NotFound + nil + end + + end + end + end +end diff --git a/lib/fog/openstack/models/orchestration/stack.rb b/lib/fog/openstack/models/orchestration/stack.rb index ce8ad1928..d0cad2a6c 100644 --- a/lib/fog/openstack/models/orchestration/stack.rb +++ b/lib/fog/openstack/models/orchestration/stack.rb @@ -1,22 +1,15 @@ -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 + %w{capabilities description disable_rollback links notification_topics outputs parameters + stack_name stack_status stack_status_reason template_description timeout_mins + creation_time updated_time}.each do |a| + attribute a.to_sym + end def initialize(attributes) # Old 'connection' is renamed as service and should be used instead @@ -24,28 +17,102 @@ module Fog super end - def save - requires :stack_name - identity ? update : create + def save(options={}) + if persisted? + service.update_stack(self, default_options.merge(options)).body['stack'] + else + service.stacks.create(default_options.merge(options)) + end end + # Deprecated def create + Fog::Logger.deprecation("#create is deprecated, use #save(options) instead [light_black](#{caller.first})[/]") requires :stack_name - service.create_stack(stack_name, self.attributes) - self + service.stacks.create(default_options) end + # Deprecated def update + Fog::Logger.deprecation("#update is deprecated, use #save(options) instead [light_black](#{caller.first})[/]") requires :stack_name - service.update_stack(stack_name, self.attributes) - self + service.update_stack(self, default_options).body['stack'] end - def destroy - requires :id - service.delete_stack(self.stack_name, self.id) - true + def delete + service.delete_stack(self) end + alias_method :destroy, :delete + + def details + @details ||= service.stacks.get(self.stack_name, self.id) + end + + def resources + @resources ||= service.resources.all(self) + end + + def events(options={}) + @events ||= service.events.all(self, options) + end + + def template + @template ||= service.templates.get(self) + end + + def abandon + service.abandon_stack(self) + end + + + # Deprecated + def template_url + Fog::Logger.deprecation("#template_url is deprecated, use it in options for #save(options) instead [light_black](#{caller.first})[/]") + @template_url + end + + # Deprecated + def template_url=(url) + Fog::Logger.deprecation("#template_url= is deprecated, use it in options for #save(options) instead [light_black](#{caller.first})[/]") + @template_url = url + end + + # Deprecated + def template=(content) + Fog::Logger.deprecation("#template=(content) is deprecated, use it in options for #save(options) instead [light_black](#{caller.first})[/]") + @template = content + end + + # Deprecated + def timeout_in_minutes + Fog::Logger.deprecation("#timeout_in_minutes is deprecated, set timeout_mins in options for save(options) instead [light_black](#{caller.first})[/]") + timeout_mins + end + + # Deprecated + def timeout_in_minutes=(minutes) + Fog::Logger.deprecation("#timeout_in_minutes=(minutes) is deprecated, set timeout_mins in options for save(options) instead [light_black](#{caller.first})[/]") + timeout_mins = minutes + end + + # build options to create or update stack + def default_options + template_content = + if template && template.is_a?(Fog::Orchestration::OpenStack::Template) + template.content + else + template + end + + { + :stack_name => stack_name, + :disable_rollback => disable_rollback, + :template_url => @template_url, + :template => template_content, + :timeout_mins => timeout_mins + } + end + private :default_options end end end diff --git a/lib/fog/openstack/models/orchestration/stacks.rb b/lib/fog/openstack/models/orchestration/stacks.rb index 74eebfbaa..5fa30835f 100644 --- a/lib/fog/openstack/models/orchestration/stacks.rb +++ b/lib/fog/openstack/models/orchestration/stacks.rb @@ -1,4 +1,3 @@ -require 'fog/core/collection' require 'fog/openstack/models/orchestration/stack' module Fog @@ -7,14 +6,49 @@ module Fog class Stacks < Fog::Collection model Fog::Orchestration::OpenStack::Stack - def all - load(service.list_stacks.body['stacks']) + def all(options={}) + data = service.list_stack_data(options).body['stacks'] + load(data) end + # Deprecated def find_by_id(id) + Fog::Logger.deprecation("#find_by_id(id) is deprecated, use #get(name, id) instead [light_black](#{caller.first})[/]") self.find {|stack| stack.id == id} end - alias_method :get, :find_by_id + + def get(arg1, arg2 = nil) + if arg2.nil? + # Deprecated: get(id) + Fog::Logger.deprecation("#get(id) is deprecated, use #get(name, id) instead [light_black](#{caller.first})[/]") + return find_by_id(arg1) + end + + # Normal use: get(name, id) + name = arg1 + id = arg2 + data = service.show_stack_details(name, id).body['stack'] + new(data) + rescue Fog::Compute::OpenStack::NotFound + nil + end + + def adopt(options={}) + service.create_stack(options) + end + + def create(options={}) + service.create_stack(options).body['stack'] + end + + def preview(options={}) + data = service.preview_stack(options).body['stack'] + new(data) + end + + def build_info + service.build_info.body + end end end end diff --git a/lib/fog/openstack/models/orchestration/template.rb b/lib/fog/openstack/models/orchestration/template.rb new file mode 100644 index 000000000..bba81e097 --- /dev/null +++ b/lib/fog/openstack/models/orchestration/template.rb @@ -0,0 +1,15 @@ +require 'fog/core/model' + +module Fog + module Orchestration + class OpenStack + class Template < Fog::Model + + %w{format description template_version parameters resources content}.each do |a| + attribute a.to_sym + end + + end + end + end +end diff --git a/lib/fog/openstack/models/orchestration/templates.rb b/lib/fog/openstack/models/orchestration/templates.rb new file mode 100644 index 000000000..565ff4d84 --- /dev/null +++ b/lib/fog/openstack/models/orchestration/templates.rb @@ -0,0 +1,44 @@ +require 'fog/openstack/models/orchestration/template' + +module Fog + module Orchestration + class OpenStack + class Templates < Fog::Collection + model Fog::Orchestration::OpenStack::Template + + def get(obj) + data = if obj.is_a?(Stack) + service.get_stack_template(obj).body + else + service.show_resource_template(obj.resource_name).body + end + + if data.has_key?('AWSTemplateFormatVersion') + data['content'] = data.to_json + data['format'] = 'CFN' + data['template_version'] = data.delete('AWSTemplateFormatVersion') + data['description'] = data.delete('Description') + data['parameter'] = data.delete('Parameters') + data['resources'] = data.delete('Resources') + else + data['content'] = data.to_yaml + data['format'] = 'HOT' + data['template_version'] = data.delete('heat_template_version') + end + + new(data) + rescue Fog::Compute::OpenStack::NotFound + nil + end + + def validate(options={}) + data = service.validate_template(options).body + temp = new + temp.parameters = data['Parameters'] + temp.description = data['Description'] + temp + end + end + end + end +end diff --git a/lib/fog/openstack/orchestration.rb b/lib/fog/openstack/orchestration.rb index 29efdc136..daec9fcf0 100644 --- a/lib/fog/openstack/orchestration.rb +++ b/lib/fog/openstack/orchestration.rb @@ -15,11 +15,57 @@ module Fog model :stack collection :stacks + model :resource + collection :resources + + collection :resource_schemas + + model :event + collection :events + + model :template + collection :templates + request_path 'fog/openstack/requests/orchestration' + request :abandon_stack + request :build_info request :create_stack - request :update_stack request :delete_stack - request :list_stacks + request :get_stack_template + request :list_resource_events + request :list_resource_types + request :list_resources + request :list_stack_data + request :list_stack_events + request :preview_stack + request :show_event_details + request :show_resource_data + request :show_resource_metadata + request :show_resource_schema + request :show_resource_template + request :show_stack_details + request :update_stack + request :validate_template + + module Reflectable + + REFLECTION_REGEX = /\/stacks\/(\w+)\/([\w|-]+)\/resources\/(\w+)/ + + def resource + @resource ||= service.resources.get(r[3], stack) + end + + def stack + @stack ||= service.stacks.get(r[1], r[2]) + end + + private + + def reflection + @reflection ||= REFLECTION_REGEX.match(self.links[0]['href']) + end + alias :r :reflection + end class Mock attr_reader :auth_token diff --git a/lib/fog/openstack/requests/orchestration/abandon_stack.rb b/lib/fog/openstack/requests/orchestration/abandon_stack.rb new file mode 100644 index 000000000..370be9f31 --- /dev/null +++ b/lib/fog/openstack/requests/orchestration/abandon_stack.rb @@ -0,0 +1,15 @@ +module Fog + module Orchestration + class OpenStack + class Real + def abandon_stack(stack) + request( + :expects => [200], + :method => 'DELETE', + :path => "stacks/#{stack.stack_name}/#{stack.id}/abandon" + ) + end + end + end + end +end diff --git a/lib/fog/openstack/requests/orchestration/build_info.rb b/lib/fog/openstack/requests/orchestration/build_info.rb new file mode 100644 index 000000000..0f65d96ab --- /dev/null +++ b/lib/fog/openstack/requests/orchestration/build_info.rb @@ -0,0 +1,15 @@ +module Fog + module Orchestration + class OpenStack + class Real + def build_info + request( + :expects => [200], + :method => 'GET', + :path => 'build_info' + ) + 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 index 40ac5d5f5..a9face868 100644 --- a/lib/fog/openstack/requests/orchestration/create_stack.rb +++ b/lib/fog/openstack/requests/orchestration/create_stack.rb @@ -4,37 +4,56 @@ 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. + # * :stack_name [String] Name of the stack to create. + # * :template [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 + # * :timeout_mins [Integer] Minutes to wait before status is set to CREATE_FAILED # - # @see http://docs.amazonwebservices.com/AWSCloudFormation/latest/APIReference/API_CreateStack.html + # @see http://developer.openstack.org/api-ref-orchestration-v1.html - def create_stack(stack_name, options = {}) - params = { - :stack_name => stack_name - }.merge(options) + def create_stack(arg1, arg2 = nil) + if arg1.is_a?(Hash) + # Normal use: create_stack(options) + options = arg1 + else + # Deprecated: create_stack(stack_name, options = {}) + Fog::Logger.deprecation("#create_stack(stack_name, options) is deprecated, use #create_stack(options) instead [light_black](#{caller.first})[/]") + options = { + :stack_name => arg1 + }.merge(arg2.nil? ? {} : arg2) + end request( :expects => 201, :path => 'stacks', :method => 'POST', - :body => Fog::JSON.encode(params) + :body => Fog::JSON.encode(options) ) end end class Mock - def create_stack(stack_name, options = {}) + def create_stack(arg1, arg2 = nil) + if arg1.is_a?(Hash) + # Normal use: create_stack(options) + options = arg1 + else + # Deprecated: create_stack(stack_name, options = {}) + Fog::Logger.deprecation("#create_stack(stack_name, options) is deprecated, use #create_stack(options) instead [light_black](#{caller.first})[/]") + options = { + :stack_name => arg1 + }.merge(arg2.nil? ? {} : arg2) + end + stack_id = Fog::Mock.random_hex(32) stack = self.data[:stacks][stack_id] = { 'id' => stack_id, - 'stack_name' => stack_name, + 'stack_name' => options[:stack_name], 'links' => [], 'description' => options[:description], 'stack_status' => 'CREATE_COMPLETE', @@ -47,7 +66,7 @@ module Fog response.status = 201 response.body = { 'id' => stack_id, - 'links'=>[{"href"=>"http://localhost:8004/v1/fake_tenant_id/stacks/#{stack_name}/#{stack_id}", "rel"=>"self"}]} + 'links'=>[{"href"=>"http://localhost:8004/v1/fake_tenant_id/stacks/#{options[:stack_name]}/#{stack_id}", "rel"=>"self"}]} response end end diff --git a/lib/fog/openstack/requests/orchestration/delete_stack.rb b/lib/fog/openstack/requests/orchestration/delete_stack.rb index bc35213a1..83a528b96 100644 --- a/lib/fog/openstack/requests/orchestration/delete_stack.rb +++ b/lib/fog/openstack/requests/orchestration/delete_stack.rb @@ -4,14 +4,25 @@ module Fog 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. + # @param [Stack] Stack to be deleted # # @return [Excon::Response] # - # @see http://docs.amazonwebservices.com/AWSCloudFormation/latest/APIReference/API_DeleteStack.html + # @see http://developer.openstack.org/api-ref-orchestration-v1.html + + def delete_stack(arg1, arg2 = nil) + if arg1.is_a?(Stack) + # Normal use: delete_stack(stack) + stack = arg1 + stack_name = stack.stack_name + stack_id = stack.id + else + # Deprecated: delete_stack(stack_name, stack_id) + Fog::Logger.deprecation("#delete_stack(stack_name, stack_id) is deprecated, use #delete_stack(stack) instead [light_black](#{caller.first})[/]") + stack_name = arg1 + stack_id = arg2 + end - def delete_stack(stack_name, stack_id) request( :expects => 204, :path => "stacks/#{stack_name}/#{stack_id}", @@ -21,7 +32,19 @@ module Fog end class Mock - def delete_stack(stack_name, stack_id) + def delete_stack(arg1, arg2 = nil) + if arg1.is_a?(Stack) + # Normal use: delete_stack(stack) + stack = arg1 + stack_name = stack.stack_name + stack_id = stack.id + else + # Deprecated: delete_stack(stack_name, stack_id) + Fog::Logger.deprecation("#delete_stack(stack_name, stack_id) is deprecated, use #delete_stack(stack) instead [light_black](#{caller.first})[/]") + stack_name = arg1 + stack_id = arg2 + end + self.data[:stacks].delete(stack_id) response = Excon::Response.new diff --git a/lib/fog/openstack/requests/orchestration/get_stack_template.rb b/lib/fog/openstack/requests/orchestration/get_stack_template.rb new file mode 100644 index 000000000..8551f778c --- /dev/null +++ b/lib/fog/openstack/requests/orchestration/get_stack_template.rb @@ -0,0 +1,20 @@ +module Fog + module Orchestration + class OpenStack + class Real + def get_stack_template(stack) + request( + :method => 'GET', + :path => "stacks/#{stack.stack_name}/#{stack.id}/template", + :expects => 200 + ) + end + end + + class Mock + def get_stack_template(stack) + end + end + end + end +end diff --git a/lib/fog/openstack/requests/orchestration/list_resource_events.rb b/lib/fog/openstack/requests/orchestration/list_resource_events.rb new file mode 100644 index 000000000..5322c3f68 --- /dev/null +++ b/lib/fog/openstack/requests/orchestration/list_resource_events.rb @@ -0,0 +1,23 @@ +module Fog + module Orchestration + class OpenStack + class Real + def list_resource_events(stack, resource, options={}) + uri = "stacks/#{stack.stack_name}/#{stack.id}/resources/#{resource.resource_name}/events" + request(:method => 'GET', :path => uri, :expects => 200, :query => options) + end + end + + class Mock + def list_stack_events + events = self.data[:events].values + + Excon::Response.new( + :body => { 'events' => events }, + :status => 200 + ) + end + end + end + end +end diff --git a/lib/fog/openstack/requests/orchestration/list_resource_types.rb b/lib/fog/openstack/requests/orchestration/list_resource_types.rb new file mode 100644 index 000000000..c456fa3b1 --- /dev/null +++ b/lib/fog/openstack/requests/orchestration/list_resource_types.rb @@ -0,0 +1,26 @@ +module Fog + module Orchestration + class OpenStack + class Real + def list_resource_types + request( + :method => 'GET', + :path => "resource_types", + :expects => 200 + ) + end + end + + class Mock + def list_resource_types + resources = self.data[:resource_types].values + + Excon::Response.new( + :body => { 'resource_types' => resources }, + :status => 200 + ) + end + end + end + end +end diff --git a/lib/fog/openstack/requests/orchestration/list_resources.rb b/lib/fog/openstack/requests/orchestration/list_resources.rb new file mode 100644 index 000000000..ed9ea693c --- /dev/null +++ b/lib/fog/openstack/requests/orchestration/list_resources.rb @@ -0,0 +1,23 @@ +module Fog + module Orchestration + class OpenStack + class Real + def list_resources(stack, options={}) + uri = "stacks/#{stack.stack_name}/#{stack.id}/resources" + request(:method => 'GET', :path => uri, :expects => 200, :query => options) + end + end + + class Mock + def list_resources(stack) + resources = self.data[:resources].values + + Excon::Response.new( + :body => { 'resources' => resources }, + :status => 200 + ) + end + end + end + end +end diff --git a/lib/fog/openstack/requests/orchestration/list_stack_data.rb b/lib/fog/openstack/requests/orchestration/list_stack_data.rb new file mode 100644 index 000000000..f203f265a --- /dev/null +++ b/lib/fog/openstack/requests/orchestration/list_stack_data.rb @@ -0,0 +1,27 @@ +module Fog + module Orchestration + class OpenStack + class Real + def list_stack_data(options={}) + request( + :method => 'GET', + :path => 'stacks', + :expects => 200, + :query => options + ) + end + end + + class Mock + def list_stack_data + stacks = self.data[:stacks].values + + Excon::Response.new( + :body => { 'stacks' => stacks }, + :status => 200 + ) + end + end + end + end +end diff --git a/lib/fog/openstack/requests/orchestration/list_stack_events.rb b/lib/fog/openstack/requests/orchestration/list_stack_events.rb new file mode 100644 index 000000000..fece7f355 --- /dev/null +++ b/lib/fog/openstack/requests/orchestration/list_stack_events.rb @@ -0,0 +1,23 @@ +module Fog + module Orchestration + class OpenStack + class Real + def list_stack_events(stack, options={}) + uri = "stacks/#{stack.stack_name}/#{stack.id}/events" + request(:method => 'GET', :path => uri, :expects => 200, :query => options ) + end + end + + class Mock + def list_stack_events + events = self.data[:events].values + + Excon::Response.new( + :body => { 'events' => events }, + :status => 200 + ) + 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 deleted file mode 100644 index cc0c64cd8..000000000 --- a/lib/fog/openstack/requests/orchestration/list_stacks.rb +++ /dev/null @@ -1,47 +0,0 @@ -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( - :expects => 200, - :path => 'stacks', - :method => 'GET', - :query => options - ) - end - end - - class Mock - def list_stacks(options = {}) - stacks = self.data[:stacks].values - - Excon::Response.new( - :body => { 'stacks' => stacks }, - :status => 200 - ) - end - end - end - end -end diff --git a/lib/fog/openstack/requests/orchestration/preview_stack.rb b/lib/fog/openstack/requests/orchestration/preview_stack.rb new file mode 100644 index 000000000..b13011cf2 --- /dev/null +++ b/lib/fog/openstack/requests/orchestration/preview_stack.rb @@ -0,0 +1,16 @@ +module Fog + module Orchestration + class OpenStack + class Real + def preview_stack(options = {}) + request( + :body => Fog::JSON.encode(options), + :expects => [200], + :method => 'POST', + :path => 'stacks/preview' + ) + end + end + end + end +end diff --git a/lib/fog/openstack/requests/orchestration/show_event_details.rb b/lib/fog/openstack/requests/orchestration/show_event_details.rb new file mode 100644 index 000000000..f5f58180d --- /dev/null +++ b/lib/fog/openstack/requests/orchestration/show_event_details.rb @@ -0,0 +1,26 @@ +module Fog + module Orchestration + class OpenStack + class Real + def show_event_details(stack, resource, event_id) + request( + :method => 'GET', + :path => "stacks/#{stack.stack_name}/#{stack.id}/resources/#{resource.resource_name}/events/#{event_id}", + :expects => 200 + ) + end + end + + class Mock + def show_event_details(stack, event) + events = self.data[:events].values + + Excon::Response.new( + :body => { 'events' => events }, + :status => 200 + ) + end + end + end + end +end diff --git a/lib/fog/openstack/requests/orchestration/show_resource_data.rb b/lib/fog/openstack/requests/orchestration/show_resource_data.rb new file mode 100644 index 000000000..48b77e6c5 --- /dev/null +++ b/lib/fog/openstack/requests/orchestration/show_resource_data.rb @@ -0,0 +1,26 @@ +module Fog + module Orchestration + class OpenStack + class Real + def show_resource_data(stack_name, stack_id, resource_name) + request( + :method => 'GET', + :path => "stacks/#{stack_name}/#{stack_id}/resources/#{resource_name}", + :expects => 200 + ) + end + end + + class Mock + def show_resource_data(stack_name, stack_id, resource_name) + resources = self.data[:resources].values + + Excon::Response.new( + :body => { 'resources' => resources }, + :status => 200 + ) + end + end + end + end +end diff --git a/lib/fog/openstack/requests/orchestration/show_resource_metadata.rb b/lib/fog/openstack/requests/orchestration/show_resource_metadata.rb new file mode 100644 index 000000000..17bf376aa --- /dev/null +++ b/lib/fog/openstack/requests/orchestration/show_resource_metadata.rb @@ -0,0 +1,26 @@ +module Fog + module Orchestration + class OpenStack + class Real + def show_resource_metadata(stack, resource_name) + request( + :method => 'GET', + :path => "stacks/#{stack.stack_name}/#{stack.id}/resources/#{resource_name}/metadata", + :expects => 200 + ) + end + end + + class Mock + def show_resource_metadata(stack, resource_name) + resources = self.data[:resources].values + + Excon::Response.new( + :body => { 'resources' => resources }, + :status => 200 + ) + end + end + end + end +end diff --git a/lib/fog/openstack/requests/orchestration/show_resource_schema.rb b/lib/fog/openstack/requests/orchestration/show_resource_schema.rb new file mode 100644 index 000000000..822fcc11a --- /dev/null +++ b/lib/fog/openstack/requests/orchestration/show_resource_schema.rb @@ -0,0 +1,15 @@ +module Fog + module Orchestration + class OpenStack + class Real + def show_resource_schema(name) + request( + :method => 'GET', + :path => "resource_types/#{name}", + :expects => 200 + ) + end + end + end + end +end diff --git a/lib/fog/openstack/requests/orchestration/show_resource_template.rb b/lib/fog/openstack/requests/orchestration/show_resource_template.rb new file mode 100644 index 000000000..60f23c81c --- /dev/null +++ b/lib/fog/openstack/requests/orchestration/show_resource_template.rb @@ -0,0 +1,20 @@ +module Fog + module Orchestration + class OpenStack + class Real + def show_resource_template(name) + request( + :method => 'GET', + :path => "resource_types/#{name}/template", + :expects => 200 + ) + end + end + + class Mock + def show_resource_template(name) + end + end + end + end +end diff --git a/lib/fog/openstack/requests/orchestration/show_stack_details.rb b/lib/fog/openstack/requests/orchestration/show_stack_details.rb new file mode 100644 index 000000000..0adb63a88 --- /dev/null +++ b/lib/fog/openstack/requests/orchestration/show_stack_details.rb @@ -0,0 +1,26 @@ +module Fog + module Orchestration + class OpenStack + class Real + def show_stack_details(name, id) + request( + :method => 'GET', + :path => "stacks/#{name}/#{id}", + :expects => 200 + ) + end + end + + class Mock + def show_stack_details(name, id) + stack = self.data[:stack].values + + Excon::Response.new( + :body => { 'stack' => stack }, + :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 index c75fa0f23..cb5d5d409 100644 --- a/lib/fog/openstack/requests/orchestration/update_stack.rb +++ b/lib/fog/openstack/requests/orchestration/update_stack.rb @@ -4,18 +4,29 @@ module Fog class Real # Update a stack. # - # @param [String] stack_id ID of the stack to update. - # @param [String] stack_name Name of the stack to update. + # @param [Fog::Orchestration::OpenStack::Stack] the stack to update. # @param [Hash] options # * :template [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. # - def update_stack(stack_id, stack_name, options = {}) - params = { - :stack_name => stack_name - }.merge(options) + def update_stack(arg1, arg2 = nil, arg3 = nil) + if arg1.is_a?(Stack) + # Normal use, update_stack(stack, options = {}) + stack = arg1 + stack_name = stack.stack_name + stack_id = stack.id + params = arg2.nil? ? {} : arg2 + else + # Deprecated, update_stack(stack_id, stack_name, options = {}) + Fog::Logger.deprecation("#update_stack(stack_id, stack_name, options) is deprecated, use #update_stack(stack, options) instead [light_black](#{caller.first})[/]") + stack_id = arg1 + stack_name = arg2 + params = { + :stack_name => stack_name + }.merge(arg3.nil? ? {} : arg3) + end request( :expects => 202, @@ -27,7 +38,23 @@ module Fog end class Mock - def update_stack(stack_name, options = {}) + def update_stack(arg1, arg2 = nil, arg3 = nil) + if arg1.is_a?(Stack) + # Normal use, update_stack(stack, options = {}) + stack = arg1 + stack_name = stack.stack_name + stack_id = stack.id + params = arg2.nil? ? {} : arg2 + else + # Deprecated, update_stack(stack_id, stack_name, options = {}) + Fog::Logger.deprecation("#update_stack(stack_id, stack_name, options) is deprecated, use #update_stack(stack, options) instead [light_black](#{caller.first})[/]") + stack_id = arg1 + stack_name = arg2 + params = { + :stack_name => stack_name + }.merge(arg3.nil? ? {} : arg3) + end + response = Excon::Response.new response.status = 202 response.body = {} diff --git a/lib/fog/openstack/requests/orchestration/validate_template.rb b/lib/fog/openstack/requests/orchestration/validate_template.rb new file mode 100644 index 000000000..2e87a4e88 --- /dev/null +++ b/lib/fog/openstack/requests/orchestration/validate_template.rb @@ -0,0 +1,16 @@ +module Fog + module Orchestration + class OpenStack + class Real + def validate_template(options = {}) + request( + :body => Fog::JSON.encode(options), + :expects => [200], + :method => 'POST', + :path => 'validate' + ) + end + end + end + end +end diff --git a/tests/openstack/requests/orchestration/stack_tests.rb b/tests/openstack/requests/orchestration/stack_tests.rb index 73535c7f5..07d7578f8 100644 --- a/tests/openstack/requests/orchestration/stack_tests.rb +++ b/tests/openstack/requests/orchestration/stack_tests.rb @@ -20,8 +20,8 @@ Shindo.tests('Fog::Orchestration[:openstack] | stack requests', ['openstack']) d Fog::Orchestration[:openstack].create_stack("teststack").body end - tests('#list_stacks').formats({'stacks' => [@stack_format]}) do - Fog::Orchestration[:openstack].list_stacks.body + tests('#list_stack_data').formats({'stacks' => [@stack_format]}) do + Fog::Orchestration[:openstack].list_stack_data.body end tests('#update_stack("teststack")').formats({}) do