diff --git a/lib/manageiq/api/client.rb b/lib/manageiq/api/client.rb index 4db91ef..0d350c9 100644 --- a/lib/manageiq/api/client.rb +++ b/lib/manageiq/api/client.rb @@ -10,6 +10,9 @@ require "manageiq/api/client/client" require "manageiq/api/client/mixins/action_mixin" require "manageiq/api/client/mixins/custom_inspect_mixin" +require "manageiq/api/client/mixins/resource_action_mixin" +require "manageiq/api/client/mixins/collection_action_mixin" +require "manageiq/api/client/mixins/queryable_mixin" require "manageiq/api/client/api" require "manageiq/api/client/action" @@ -25,4 +28,6 @@ require "manageiq/api/client/product_info" require "manageiq/api/client/resource" require "manageiq/api/client/server_info" +require "manageiq/api/client/subcollection" +require "manageiq/api/client/subresource" require "manageiq/api/client/version" diff --git a/lib/manageiq/api/client/authentication.rb b/lib/manageiq/api/client/authentication.rb index 6054c9f..d7b59c9 100644 --- a/lib/manageiq/api/client/authentication.rb +++ b/lib/manageiq/api/client/authentication.rb @@ -14,7 +14,7 @@ class Authentication }.freeze CUSTOM_INSPECT_EXCLUSIONS = [:@password].freeze - include CustomInspectMixin + include ManageIQ::API::CustomInspectMixin def initialize(options = {}) @user, @password = fetch_credentials(options) diff --git a/lib/manageiq/api/client/collection.rb b/lib/manageiq/api/client/collection.rb index 1f6de60..40c6ecb 100644 --- a/lib/manageiq/api/client/collection.rb +++ b/lib/manageiq/api/client/collection.rb @@ -2,14 +2,12 @@ module ManageIQ module API class Client class Collection - include ActionMixin + include ManageIQ::API::CollectionActionMixin include Enumerable - include QueryRelation::Queryable + include ManageIQ::API::QueryableMixin CUSTOM_INSPECT_EXCLUSIONS = [:@client].freeze - include CustomInspectMixin - - ACTIONS_RETURNING_RESOURCES = %w(create query).freeze + include ManageIQ::API::CustomInspectMixin attr_reader :client @@ -25,47 +23,6 @@ def initialize(client, collection_spec) clear_actions end - def each(&block) - all.each(&block) - end - - # find(#) returns the object - # find([#]) returns an array of the object - # find(#, #, ...) or find([#, #, ...]) returns an array of the objects - def find(*args) - request_array = args.size == 1 && args[0].kind_of?(Array) - args = args.flatten - case args.size - when 0 - raise "Couldn't find resource without an 'id'" - when 1 - res = limit(1).where(:id => args[0]).to_a - raise "Couldn't find resource with 'id' #{args}" if res.blank? - request_array ? res : res.first - else - raise "Multiple resource find is not supported" unless respond_to?(:query) - query(args.collect { |id| { "id" => id } }) - end - end - - def find_by(args) - limit(1).where(args).first - end - - def pluck(*attrs) - select(*attrs).to_a.pluck(*attrs) - end - - def self.subclass(name) - name = name.camelize - - if const_defined?(name, false) - const_get(name, false) - else - const_set(name, Class.new(self)) - end - end - def get(options = {}) options[:expand] = (String(options[:expand]).split(",") | %w(resources)).join(",") options[:filter] = Array(options[:filter]) if options[:filter].is_a?(String) @@ -77,17 +34,6 @@ def get(options = {}) end end - def search(mode, options) - options[:limit] = 1 if mode == :first - result = get(parameters_from_query_relation(options)) - case mode - when :first then result.first - when :last then result.last - when :all then result - else raise "Invalid mode #{mode} specified for search" - end - end - def options @collection_options ||= CollectionOptions.new(client.options(name)) end diff --git a/lib/manageiq/api/client/collection_options.rb b/lib/manageiq/api/client/collection_options.rb index 81b9643..418bc29 100644 --- a/lib/manageiq/api/client/collection_options.rb +++ b/lib/manageiq/api/client/collection_options.rb @@ -5,11 +5,12 @@ class CollectionOptions attr_reader :attributes attr_reader :virtual_attributes attr_reader :relationships + attr_reader :subcollections attr_reader :data def initialize(options = {}) - @attributes, @virtual_attributes, @relationships, @data = - options.values_at("attributes", "virtual_attributes", "relationships", "data") + @attributes, @virtual_attributes, @relationships, @subcollections, @data = + options.values_at("attributes", "virtual_attributes", "relationships", "subcollections", "data") end end end diff --git a/lib/manageiq/api/client/mixins/action_mixin.rb b/lib/manageiq/api/client/mixins/action_mixin.rb index 210283d..98a87d7 100644 --- a/lib/manageiq/api/client/mixins/action_mixin.rb +++ b/lib/manageiq/api/client/mixins/action_mixin.rb @@ -1,4 +1,4 @@ -module ActionMixin +module ManageIQ::API::ActionMixin extend ActiveSupport::Concern private diff --git a/lib/manageiq/api/client/mixins/collection_action_mixin.rb b/lib/manageiq/api/client/mixins/collection_action_mixin.rb new file mode 100644 index 0000000..6b8897a --- /dev/null +++ b/lib/manageiq/api/client/mixins/collection_action_mixin.rb @@ -0,0 +1,72 @@ +module ManageIQ::API::CollectionActionMixin + include ManageIQ::API::ActionMixin + + ACTIONS_RETURNING_RESOURCES = %w(create query).freeze + + def each(&block) + all.each(&block) + end + + def self.included(base) + base.extend ClassMethods + end + + module ClassMethods + def subclass(name) + name = name.camelize + + if const_defined?(name, false) + const_get(name, false) + else + const_set(name, Class.new(self)) + end + end + end + + private + + def exec_action(name, *args, &block) + action = find_action(name) + body = action_body(action.name, *args, &block) + bulk_request = body.key?("resources") + res = client.send(action.method, URI(action.href)) { body } + if ACTIONS_RETURNING_RESOURCES.include?(action.name) && res.key?("results") + klass = ManageIQ::API::Client::Resource.subclass(self.name) + res = results_to_objects(res["results"], klass) + res = res[0] if !bulk_request && res.size == 1 + else + res = res["results"].collect { |result| action_result(result) } + end + res + end + + def results_to_objects(results, klass) + results.collect do |resource_hash| + if ManageIQ::API::Client::ActionResult.an_action_result?(resource_hash) + ManageIQ::API::Client::ActionResult.new(resource_hash) + else + klass.new(self, resource_hash) + end + end + end + + def action_body(action_name, *args, &block) + args = args.flatten + args = args.first if args.size == 1 && args.first.kind_of?(Hash) + args = {} if args.blank? + block_data = block ? block.call : {} + body = { "action" => action_name } + if block_data.present? + if block_data.kind_of?(Array) + body["resources"] = block_data.collect { |resource| resource.merge(args) } + elsif args.present? && args.kind_of?(Array) + body["resources"] = args.collect { |resource| resource.merge(block_data) } + else + body["resource"] = args.dup.merge!(block_data) + end + elsif args.present? + body[args.kind_of?(Array) ? "resources" : "resource"] = args + end + body + end +end diff --git a/lib/manageiq/api/client/mixins/custom_inspect_mixin.rb b/lib/manageiq/api/client/mixins/custom_inspect_mixin.rb index 54cefd2..aaca8af 100644 --- a/lib/manageiq/api/client/mixins/custom_inspect_mixin.rb +++ b/lib/manageiq/api/client/mixins/custom_inspect_mixin.rb @@ -1,4 +1,4 @@ -module CustomInspectMixin +module ManageIQ::API::CustomInspectMixin extend ActiveSupport::Concern def inspect diff --git a/lib/manageiq/api/client/mixins/queryable_mixin.rb b/lib/manageiq/api/client/mixins/queryable_mixin.rb new file mode 100644 index 0000000..ecc0c49 --- /dev/null +++ b/lib/manageiq/api/client/mixins/queryable_mixin.rb @@ -0,0 +1,95 @@ +module ManageIQ::API::QueryableMixin + include QueryRelation::Queryable + + # find(#) returns the object + # find([#]) returns an array of the object + # find(#, #, ...) or find([#, #, ...]) returns an array of the objects + def find(*args) + request_array = args.size == 1 && args[0].kind_of?(Array) + args = args.flatten + case args.size + when 0 + raise "Couldn't find resource without an 'id'" + when 1 + res = limit(1).where(:id => args[0]).to_a + raise "Couldn't find resource with 'id' #{args}" if res.blank? + request_array ? res : res.first + else + raise "Multiple resource find is not supported" unless respond_to?(:query) + query(args.collect { |id| { "id" => id } }) + end + end + + def find_by(args) + limit(1).where(args).first + end + + def pluck(*attrs) + select(*attrs).to_a.pluck(*attrs) + end + + def search(mode, options) + options[:limit] = 1 if mode == :first + result = get(parameters_from_query_relation(options)) + case mode + when :first then result.first + when :last then result.last + when :all then result + else raise "Invalid mode #{mode} specified for search" + end + end + + private + + def parameters_from_query_relation(options) + api_params = {} + [:offset, :limit].each { |opt| api_params[opt] = options[opt] if options[opt] } + api_params[:attributes] = options[:select].join(",") if options[:select].present? + if options[:where] + api_params[:filter] ||= [] + api_params[:filter] += filters_from_query_relation("=", options[:where]) + end + if options[:not] + api_params[:filter] ||= [] + api_params[:filter] += filters_from_query_relation("!=", options[:not]) + end + if options[:order] + order_parameters_from_query_relation(options[:order]).each { |param, value| api_params[param] = value } + end + api_params + end + + def filters_from_query_relation(condition, option) + filters = [] + option.each do |attr, values| + Array(values).each do |value| + value = "'#{value}'" if value.kind_of?(String) && !value.match(/^(NULL|nil)$/i) + filters << "#{attr}#{condition}#{value}" + end + end + filters + end + + def order_parameters_from_query_relation(option) + query_relation_option = + if option.kind_of?(Array) + option.each_with_object({}) { |name, hash| hash[name] = "asc" } + else + option.dup + end + + res_sort_by = [] + res_sort_order = [] + query_relation_option.each do |sort_attr, sort_order| + res_sort_by << sort_attr + sort_order = + case sort_order + when /^asc/i then "asc" + when /^desc/i then "desc" + else raise "Invalid sort order #{sort_order} specified for attribute #{sort_attr}" + end + res_sort_order << sort_order + end + { :sort_by => res_sort_by.join(","), :sort_order => res_sort_order.join(",") } + end +end diff --git a/lib/manageiq/api/client/mixins/resource_action_mixin.rb b/lib/manageiq/api/client/mixins/resource_action_mixin.rb new file mode 100644 index 0000000..fabe977 --- /dev/null +++ b/lib/manageiq/api/client/mixins/resource_action_mixin.rb @@ -0,0 +1,25 @@ +module ManageIQ::API::ResourceActionMixin + include ManageIQ::API::ActionMixin + + private + + def exec_action(name, args = nil, &block) + args ||= {} + raise "Action #{name} parameters must be a hash" if !args.kind_of?(Hash) + action = find_action(name) + res = client.send(action.method, URI(action.href)) do + body = { "action" => action.name } + resource = args.dup + resource.merge!(block.call) if block + resource.present? ? body.merge("resource" => resource) : body + end + action_result(res) + end + + def reload_actions + return unless attributes.key?("href") + resource_hash = client.get(attributes["href"]) + @attributes = resource_hash.except("actions") + fetch_actions(resource_hash) + end +end diff --git a/lib/manageiq/api/client/resource.rb b/lib/manageiq/api/client/resource.rb index 529deff..0647c3c 100644 --- a/lib/manageiq/api/client/resource.rb +++ b/lib/manageiq/api/client/resource.rb @@ -2,10 +2,10 @@ module ManageIQ module API class Client class Resource - include ActionMixin + include ManageIQ::API::ResourceActionMixin CUSTOM_INSPECT_EXCLUSIONS = [:@collection].freeze - include CustomInspectMixin + include ManageIQ::API::CustomInspectMixin def self.subclass(name) name = name.classify @@ -39,31 +39,21 @@ def [](attr) private def method_missing(sym, *args, &block) - reload_actions unless actions_present? - if attributes.key?(sym.to_s) - attributes[sym.to_s] - elsif action_defined?(sym) - exec_action(sym, *args, &block) + return attributes[sym.to_s] if attributes.key?(sym.to_s) + if subcollection_defined?(sym) + invoke_subcollection(sym) else - super + reload_actions unless actions_present? + if action_defined?(sym) + exec_action(sym, *args, &block) + else + super + end end end def respond_to_missing?(sym, *_) - attributes.key?(sym.to_s) || action_defined?(sym) || super - end - - def exec_action(name, args = nil, &block) - args ||= {} - raise "Action #{name} parameters must be a hash" if !args.kind_of?(Hash) - action = find_action(name) - res = client.send(action.method, URI(action.href)) do - body = { "action" => action.name } - resource = args.dup - resource.merge!(block.call) if block - resource.present? ? body.merge("resource" => resource) : body - end - action_result(res) + attributes.key?(sym.to_s) || action_defined?(sym) || subcollection_defined?(sym) || super end # Let's add href's here if not yet defined by the server @@ -73,11 +63,13 @@ def add_href attributes["href"] = client.connection.api_path("#{collection.name}/#{attributes['id']}") end - def reload_actions - return unless attributes.key?("href") - resource_hash = client.get(attributes["href"]) - @attributes = resource_hash.except("actions") - fetch_actions(resource_hash) + def subcollection_defined?(name) + collection.options.subcollections.include?(name.to_s) + end + + def invoke_subcollection(name) + @_subcollections ||= {} + @_subcollections[name.to_s] ||= ManageIQ::API::Client::Subcollection.subclass(name.to_s).new(name.to_s, self) end end end diff --git a/lib/manageiq/api/client/subcollection.rb b/lib/manageiq/api/client/subcollection.rb new file mode 100644 index 0000000..025e16e --- /dev/null +++ b/lib/manageiq/api/client/subcollection.rb @@ -0,0 +1,52 @@ +module ManageIQ + module API + class Client + class Subcollection + include ManageIQ::API::CollectionActionMixin + include Enumerable + include ManageIQ::API::QueryableMixin + + CUSTOM_INSPECT_EXCLUSIONS = [:@resource].freeze + include ManageIQ::API::CustomInspectMixin + + attr_reader :name + attr_reader :href + attr_reader :resource + + delegate :client, :to => :resource + + def initialize(name, resource) + @name, @resource, @href = name.to_s, resource, "#{resource.href}/#{name}" + clear_actions + result_hash = client.get(href, :hide => "resources") + fetch_actions(result_hash) + end + + def get(options = {}) + options[:expand] = (String(options[:expand]).split(",") | %w(resources)).join(",") + options[:filter] = Array(options[:filter]) if options[:filter].is_a?(String) + result_hash = client.get(href, options) + fetch_actions(result_hash) + klass = ManageIQ::API::Client::Subresource.subclass(name) + result_hash["resources"].collect do |resource_hash| + klass.new(self, resource_hash) + end + end + + private + + def method_missing(sym, *args, &block) + if action_defined?(sym) + exec_action(sym, *args, &block) + else + super + end + end + + def respond_to_missing?(sym, *_) + action_defined?(sym) || super + end + end + end + end +end diff --git a/lib/manageiq/api/client/subresource.rb b/lib/manageiq/api/client/subresource.rb new file mode 100644 index 0000000..8af9470 --- /dev/null +++ b/lib/manageiq/api/client/subresource.rb @@ -0,0 +1,65 @@ +module ManageIQ + module API + class Client + class Subresource + include ManageIQ::API::ResourceActionMixin + + CUSTOM_INSPECT_EXCLUSIONS = [:@resource].freeze + include ManageIQ::API::CustomInspectMixin + + def self.subclass(name) + name = name.classify + + if const_defined?(name, false) + const_get(name, false) + else + const_set(name, Class.new(self)) + end + end + + attr_reader :attributes + attr_reader :subcollection + attr_reader :actions + + delegate :client, :to => :resource + delegate :resource, :to => :subcollection + + def initialize(subcollection, resource_hash) + raise "Cannot instantiate a Subresource directly" if instance_of?(Subresource) + @subcollection = subcollection + @attributes = resource_hash.except("actions") + add_href + fetch_actions(resource_hash) + end + + def [](attr) + attr_str = attr.to_s + attributes[attr_str] if attributes.key?(attr_str) + end + + private + + def method_missing(sym, *args, &block) + return attributes[sym.to_s] if attributes.key?(sym.to_s) + reload_actions unless actions_present? + if action_defined?(sym) + exec_action(sym, *args, &block) + else + super + end + end + + def respond_to_missing?(sym, *_) + attributes.key?(sym.to_s) || action_defined?(sym) || super + end + + # Let's add href's here if not yet defined by the server + def add_href + return if attributes.key?("href") + return unless attributes.key?("id") + attributes["href"] = "#{resource.href}/#{self.class.name}/#{attributes['id']}" + end + end + end + end +end diff --git a/spec/fixtures/api/responses/actions_vm_tags.json b/spec/fixtures/api/responses/actions_vm_tags.json new file mode 100644 index 0000000..59131d2 --- /dev/null +++ b/spec/fixtures/api/responses/actions_vm_tags.json @@ -0,0 +1,28 @@ +{ + "results": [ + { + "success": true, + "message": "Assigning Tag: category:'location' name:'ny'", + "href": "http://localhost:3000/api/vms/185", + "tag_category": "location", + "tag_name": "ny", + "tag_href": "http://localhost:3000/api/tags/4" + }, + { + "success": true, + "message": "Assigning Tag: category:'function' name:'desktop'", + "href": "http://localhost:3000/api/vms/185", + "tag_category": "function", + "tag_name": "desktop", + "tag_href": "http://localhost:3000/api/tags/9" + }, + { + "success": true, + "message": "Assigning Tag: category:'owner' name:'prod_linux'", + "href": "http://localhost:3000/api/vms/185", + "tag_category": "owner", + "tag_name": "prod_linux", + "tag_href": "http://localhost:3000/api/tags/19" + } + ] +} diff --git a/spec/fixtures/api/responses/actions_vms.json b/spec/fixtures/api/responses/actions_vms.json new file mode 100644 index 0000000..e6c937c --- /dev/null +++ b/spec/fixtures/api/responses/actions_vms.json @@ -0,0 +1,29 @@ +{ + "results": [ + { + "success": true, + "message": "setting ownership of vms id 161 to owner: Administrator", + "href": "http://localhost:3000/api/vms/161" + }, + { + "success": true, + "message": "setting ownership of vms id 165 to owner: Administrator", + "href": "http://localhost:3000/api/vms/165" + }, + { + "success": true, + "message": "setting ownership of vms id 166 to owner: Administrator", + "href": "http://localhost:3000/api/vms/166" + }, + { + "success": true, + "message": "setting ownership of vms id 185 to owner: Administrator", + "href": "http://localhost:3000/api/vms/185" + }, + { + "success": true, + "message": "setting ownership of vms id 163 to owner: Administrator", + "href": "http://localhost:3000/api/vms/163" + } + ] +} diff --git a/spec/fixtures/api/responses/get_dev2_vms.json b/spec/fixtures/api/responses/get_dev2_vms.json new file mode 100644 index 0000000..5c5113d --- /dev/null +++ b/spec/fixtures/api/responses/get_dev2_vms.json @@ -0,0 +1,257 @@ +{ + "name": "vms", + "count": 5, + "subcount": 1, + "resources": [ + { + "href": "http://localhost:3000/api/vms/165", + "id": 165, + "vendor": "vmware", + "name": "aab-dev2", + "location": "aab-dev2/aab-dev2.vmx", + "host_id": 14, + "created_on": "2017-01-25T23:39:52Z", + "updated_on": "2017-01-25T23:39:52Z", + "storage_id": 13, + "guid": "91503368-e357-11e6-b22f-000a27020067", + "ems_id": 3, + "uid_ems": "564d8be8-84bf-03ab-c421-a503da6db700", + "boot_time": "2017-01-03T21:08:47Z", + "tools_status": "toolsOk", + "standby_action": "checkpoint", + "power_state": "on", + "state_changed_on": "2017-01-25T23:39:52Z", + "connection_state": "connected", + "memory_reserve": 0, + "memory_reserve_expand": false, + "memory_limit": -1, + "memory_shares": 40960, + "memory_shares_level": "normal", + "cpu_reserve": 0, + "cpu_reserve_expand": false, + "cpu_limit": -1, + "cpu_shares": 2000, + "cpu_shares_level": "normal", + "template": false, + "ems_ref_obj": "vm-62", + "miq_group_id": 2, + "linked_clone": true, + "fault_tolerance": false, + "type": "ManageIQ::Providers::Vmware::InfraManager::Vm", + "ems_ref": "vm-62", + "ems_cluster_id": 5, + "cloud": false, + "raw_power_state": "poweredOn", + "tenant_id": 1, + "cpu_hot_add_enabled": true, + "cpu_hot_remove_enabled": false, + "memory_hot_add_enabled": true, + "memory_hot_add_limit": 65536, + "memory_hot_add_increment": 128, + "actions": [ + { + "name": "add_lifecycle_event", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "add_event", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "refresh", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "shutdown_guest", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "reboot_guest", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "start", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "stop", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "suspend", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "shelve", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "shelve_offload", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "pause", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "request_console", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "reset", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "retire", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "delete", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "set_owner", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "set_ownership", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "scan", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "delete", + "method": "delete", + "href": "http://localhost:3000/api/vms/165" + } + ] + } + ], + "actions": [ + { + "name": "query", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "add_lifecycle_event", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "add_event", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "refresh", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "shutdown_guest", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "reboot_guest", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "start", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "stop", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "suspend", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "shelve", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "shelve_offload", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "pause", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "request_console", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "reset", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "retire", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "set_owner", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "set_ownership", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "scan", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "delete", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "assign_tags", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "unassign_tags", + "method": "post", + "href": "http://localhost:3000/api/vms" + } + ] +} diff --git a/spec/fixtures/api/responses/get_no_vms.json b/spec/fixtures/api/responses/get_no_vms.json new file mode 100644 index 0000000..f4f7347 --- /dev/null +++ b/spec/fixtures/api/responses/get_no_vms.json @@ -0,0 +1,114 @@ +{ + "name": "vms", + "count": 5, + "subcount": 0, + "resources": [ + ], + "actions": [ + { + "name": "query", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "add_lifecycle_event", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "add_event", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "refresh", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "shutdown_guest", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "reboot_guest", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "start", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "stop", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "suspend", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "shelve", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "shelve_offload", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "pause", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "request_console", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "reset", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "retire", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "set_owner", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "set_ownership", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "scan", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "delete", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "assign_tags", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "unassign_tags", + "method": "post", + "href": "http://localhost:3000/api/vms" + } + ] +} diff --git a/spec/fixtures/api/responses/get_test1_vm.json b/spec/fixtures/api/responses/get_test1_vm.json new file mode 100644 index 0000000..8e93bfa --- /dev/null +++ b/spec/fixtures/api/responses/get_test1_vm.json @@ -0,0 +1,143 @@ +{ + "href": "http://localhost:3000/api/vms/185", + "id": 185, + "vendor": "vmware", + "name": "aab-test1", + "location": "aab-test1/aab-test1.vmx", + "host_id": 14, + "created_on": "2017-01-25T23:39:53Z", + "updated_on": "2017-01-25T23:39:53Z", + "storage_id": 13, + "guid": "91f78e74-e357-11e6-b22f-000a27020067", + "ems_id": 3, + "uid_ems": "420c8f0d-4a8f-0b6d-fc3a-0c3cfab7b33f", + "boot_time": "2017-01-03T21:10:55Z", + "tools_status": "toolsOk", + "standby_action": "checkpoint", + "power_state": "on", + "state_changed_on": "2017-01-25T23:39:53Z", + "connection_state": "connected", + "memory_reserve": 0, + "memory_reserve_expand": false, + "memory_limit": -1, + "memory_shares": 40960, + "memory_shares_level": "normal", + "cpu_reserve": 0, + "cpu_reserve_expand": false, + "cpu_limit": -1, + "cpu_shares": 2000, + "cpu_shares_level": "normal", + "template": false, + "ems_ref_obj": "vm-825", + "miq_group_id": 2, + "linked_clone": true, + "fault_tolerance": false, + "type": "ManageIQ::Providers::Vmware::InfraManager::Vm", + "ems_ref": "vm-825", + "ems_cluster_id": 5, + "cloud": false, + "raw_power_state": "poweredOn", + "tenant_id": 1, + "cpu_hot_add_enabled": true, + "cpu_hot_remove_enabled": false, + "memory_hot_add_enabled": true, + "memory_hot_add_limit": 65536, + "memory_hot_add_increment": 128, + "actions": [ + { + "name": "add_lifecycle_event", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "add_event", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "refresh", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "shutdown_guest", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "reboot_guest", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "start", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "stop", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "suspend", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "shelve", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "shelve_offload", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "pause", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "request_console", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "reset", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "retire", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "delete", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "set_owner", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "set_ownership", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "scan", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "delete", + "method": "delete", + "href": "http://localhost:3000/api/vms/185" + } + ] +} diff --git a/spec/fixtures/api/responses/get_test1_vm_no_tags.json b/spec/fixtures/api/responses/get_test1_vm_no_tags.json new file mode 100644 index 0000000..bd28226 --- /dev/null +++ b/spec/fixtures/api/responses/get_test1_vm_no_tags.json @@ -0,0 +1,20 @@ +{ + "name": "tags", + "count": 160, + "subcount": 0, + "resources": [ + ], + "actions": [ + { + "name": "assign", + "method": "post", + "href": "http://localhost:3000/api/vms/185/tags" + }, + { + "name": "unassign", + "method": "post", + "href": "http://localhost:3000/api/vms/185/tags" + } + ] +} + diff --git a/spec/fixtures/api/responses/get_test1_vm_tags.json b/spec/fixtures/api/responses/get_test1_vm_tags.json new file mode 100644 index 0000000..3151d42 --- /dev/null +++ b/spec/fixtures/api/responses/get_test1_vm_tags.json @@ -0,0 +1,54 @@ +{ + "name": "tags", + "count": 160, + "subcount": 2, + "resources": [ + { + "href": "http://localhost:3000/api/vms/185/tags/131", + "id": 131, + "name": "/managed/folder_path_yellow/datacenters", + "actions": [ + { + "name": "assign", + "method": "post", + "href": "http://localhost:3000/api/vms/185/tags/131" + }, + { + "name": "unassign", + "method": "post", + "href": "http://localhost:3000/api/vms/185/tags/131" + } + ] + }, + { + "href": "http://localhost:3000/api/vms/185/tags/138", + "id": 138, + "name": "/managed/folder_path_blue/datacenters:dev-vc60-dc:vm:alberto-dev", + "actions": [ + { + "name": "assign", + "method": "post", + "href": "http://localhost:3000/api/vms/185/tags/138" + }, + { + "name": "unassign", + "method": "post", + "href": "http://localhost:3000/api/vms/185/tags/138" + } + ] + } + ], + "actions": [ + { + "name": "assign", + "method": "post", + "href": "http://localhost:3000/api/vms/185/tags" + }, + { + "name": "unassign", + "method": "post", + "href": "http://localhost:3000/api/vms/185/tags" + } + ] +} + diff --git a/spec/fixtures/api/responses/get_test1_vms.json b/spec/fixtures/api/responses/get_test1_vms.json new file mode 100644 index 0000000..25ff781 --- /dev/null +++ b/spec/fixtures/api/responses/get_test1_vms.json @@ -0,0 +1,257 @@ +{ + "name": "vms", + "count": 5, + "subcount": 1, + "resources": [ + { + "href": "http://localhost:3000/api/vms/185", + "id": 185, + "vendor": "vmware", + "name": "aab-test1", + "location": "aab-test1/aab-test1.vmx", + "host_id": 14, + "created_on": "2017-01-25T23:39:53Z", + "updated_on": "2017-01-25T23:39:53Z", + "storage_id": 13, + "guid": "91f78e74-e357-11e6-b22f-000a27020067", + "ems_id": 3, + "uid_ems": "420c8f0d-4a8f-0b6d-fc3a-0c3cfab7b33f", + "boot_time": "2017-01-03T21:10:55Z", + "tools_status": "toolsOk", + "standby_action": "checkpoint", + "power_state": "on", + "state_changed_on": "2017-01-25T23:39:53Z", + "connection_state": "connected", + "memory_reserve": 0, + "memory_reserve_expand": false, + "memory_limit": -1, + "memory_shares": 40960, + "memory_shares_level": "normal", + "cpu_reserve": 0, + "cpu_reserve_expand": false, + "cpu_limit": -1, + "cpu_shares": 2000, + "cpu_shares_level": "normal", + "template": false, + "ems_ref_obj": "vm-825", + "miq_group_id": 2, + "linked_clone": true, + "fault_tolerance": false, + "type": "ManageIQ::Providers::Vmware::InfraManager::Vm", + "ems_ref": "vm-825", + "ems_cluster_id": 5, + "cloud": false, + "raw_power_state": "poweredOn", + "tenant_id": 1, + "cpu_hot_add_enabled": true, + "cpu_hot_remove_enabled": false, + "memory_hot_add_enabled": true, + "memory_hot_add_limit": 65536, + "memory_hot_add_increment": 128, + "actions": [ + { + "name": "add_lifecycle_event", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "add_event", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "refresh", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "shutdown_guest", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "reboot_guest", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "start", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "stop", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "suspend", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "shelve", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "shelve_offload", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "pause", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "request_console", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "reset", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "retire", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "delete", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "set_owner", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "set_ownership", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "scan", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "delete", + "method": "delete", + "href": "http://localhost:3000/api/vms/185" + } + ] + } + ], + "actions": [ + { + "name": "query", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "add_lifecycle_event", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "add_event", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "refresh", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "shutdown_guest", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "reboot_guest", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "start", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "stop", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "suspend", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "shelve", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "shelve_offload", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "pause", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "request_console", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "reset", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "retire", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "set_owner", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "set_ownership", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "scan", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "delete", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "assign_tags", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "unassign_tags", + "method": "post", + "href": "http://localhost:3000/api/vms" + } + ] +} diff --git a/spec/fixtures/api/responses/get_vms.json b/spec/fixtures/api/responses/get_vms.json new file mode 100644 index 0000000..c3a2f39 --- /dev/null +++ b/spec/fixtures/api/responses/get_vms.json @@ -0,0 +1,826 @@ +{ + "name": "vms", + "count": 5, + "subcount": 5, + "resources": [ + { + "href": "http://localhost:3000/api/vms/161", + "id": 161, + "vendor": "vmware", + "name": "aab-dev1", + "location": "aab-dev1/aab-dev1.vmx", + "host_id": 11, + "created_on": "2017-01-25T23:39:51Z", + "updated_on": "2017-01-25T23:39:51Z", + "storage_id": 13, + "guid": "91282134-e357-11e6-b22f-000a27020067", + "ems_id": 3, + "uid_ems": "564d5056-49ea-a5aa-a71d-cf9b8138430b", + "boot_time": "2017-01-03T20:53:30Z", + "tools_status": "toolsOk", + "standby_action": "checkpoint", + "power_state": "on", + "state_changed_on": "2017-01-25T23:39:51Z", + "connection_state": "connected", + "memory_reserve": 0, + "memory_reserve_expand": false, + "memory_limit": -1, + "memory_shares": 163840, + "memory_shares_level": "normal", + "cpu_reserve": 0, + "cpu_reserve_expand": false, + "cpu_limit": -1, + "cpu_shares": 4000, + "cpu_shares_level": "normal", + "template": false, + "ems_ref_obj": "vm-53", + "miq_group_id": 2, + "linked_clone": true, + "fault_tolerance": false, + "type": "ManageIQ::Providers::Vmware::InfraManager::Vm", + "ems_ref": "vm-53", + "ems_cluster_id": 5, + "cloud": false, + "raw_power_state": "poweredOn", + "tenant_id": 1, + "cpu_hot_add_enabled": true, + "cpu_hot_remove_enabled": false, + "memory_hot_add_enabled": true, + "memory_hot_add_limit": 262144, + "memory_hot_add_increment": 128, + "actions": [ + { + "name": "add_lifecycle_event", + "method": "post", + "href": "http://localhost:3000/api/vms/161" + }, + { + "name": "add_event", + "method": "post", + "href": "http://localhost:3000/api/vms/161" + }, + { + "name": "refresh", + "method": "post", + "href": "http://localhost:3000/api/vms/161" + }, + { + "name": "shutdown_guest", + "method": "post", + "href": "http://localhost:3000/api/vms/161" + }, + { + "name": "reboot_guest", + "method": "post", + "href": "http://localhost:3000/api/vms/161" + }, + { + "name": "start", + "method": "post", + "href": "http://localhost:3000/api/vms/161" + }, + { + "name": "stop", + "method": "post", + "href": "http://localhost:3000/api/vms/161" + }, + { + "name": "suspend", + "method": "post", + "href": "http://localhost:3000/api/vms/161" + }, + { + "name": "shelve", + "method": "post", + "href": "http://localhost:3000/api/vms/161" + }, + { + "name": "shelve_offload", + "method": "post", + "href": "http://localhost:3000/api/vms/161" + }, + { + "name": "pause", + "method": "post", + "href": "http://localhost:3000/api/vms/161" + }, + { + "name": "request_console", + "method": "post", + "href": "http://localhost:3000/api/vms/161" + }, + { + "name": "reset", + "method": "post", + "href": "http://localhost:3000/api/vms/161" + }, + { + "name": "retire", + "method": "post", + "href": "http://localhost:3000/api/vms/161" + }, + { + "name": "delete", + "method": "post", + "href": "http://localhost:3000/api/vms/161" + }, + { + "name": "set_owner", + "method": "post", + "href": "http://localhost:3000/api/vms/161" + }, + { + "name": "set_ownership", + "method": "post", + "href": "http://localhost:3000/api/vms/161" + }, + { + "name": "scan", + "method": "post", + "href": "http://localhost:3000/api/vms/161" + }, + { + "name": "delete", + "method": "delete", + "href": "http://localhost:3000/api/vms/161" + } + ] + }, + { + "href": "http://localhost:3000/api/vms/165", + "id": 165, + "vendor": "vmware", + "name": "aab-dev2", + "location": "aab-dev2/aab-dev2.vmx", + "host_id": 14, + "created_on": "2017-01-25T23:39:52Z", + "updated_on": "2017-01-25T23:39:52Z", + "storage_id": 13, + "guid": "91503368-e357-11e6-b22f-000a27020067", + "ems_id": 3, + "uid_ems": "564d8be8-84bf-03ab-c421-a503da6db700", + "boot_time": "2017-01-03T21:08:47Z", + "tools_status": "toolsOk", + "standby_action": "checkpoint", + "power_state": "on", + "state_changed_on": "2017-01-25T23:39:52Z", + "connection_state": "connected", + "memory_reserve": 0, + "memory_reserve_expand": false, + "memory_limit": -1, + "memory_shares": 40960, + "memory_shares_level": "normal", + "cpu_reserve": 0, + "cpu_reserve_expand": false, + "cpu_limit": -1, + "cpu_shares": 2000, + "cpu_shares_level": "normal", + "template": false, + "ems_ref_obj": "vm-62", + "miq_group_id": 2, + "linked_clone": true, + "fault_tolerance": false, + "type": "ManageIQ::Providers::Vmware::InfraManager::Vm", + "ems_ref": "vm-62", + "ems_cluster_id": 5, + "cloud": false, + "raw_power_state": "poweredOn", + "tenant_id": 1, + "cpu_hot_add_enabled": true, + "cpu_hot_remove_enabled": false, + "memory_hot_add_enabled": true, + "memory_hot_add_limit": 65536, + "memory_hot_add_increment": 128, + "actions": [ + { + "name": "add_lifecycle_event", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "add_event", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "refresh", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "shutdown_guest", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "reboot_guest", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "start", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "stop", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "suspend", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "shelve", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "shelve_offload", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "pause", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "request_console", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "reset", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "retire", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "delete", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "set_owner", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "set_ownership", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "scan", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "delete", + "method": "delete", + "href": "http://localhost:3000/api/vms/165" + } + ] + }, + { + "href": "http://localhost:3000/api/vms/166", + "id": 166, + "vendor": "vmware", + "name": "aab-dev3", + "location": "aab-dev3/aab-dev3.vmx", + "host_id": 12, + "created_on": "2017-01-25T23:39:52Z", + "updated_on": "2017-01-25T23:39:52Z", + "storage_id": 13, + "guid": "9158bc4a-e357-11e6-b22f-000a27020067", + "ems_id": 3, + "uid_ems": "42332064-1b21-3bc2-ee0b-09ae0799bc3b", + "tools_status": "toolsNotRunning", + "standby_action": "checkpoint", + "power_state": "off", + "state_changed_on": "2017-01-25T23:39:52Z", + "connection_state": "connected", + "memory_reserve": 0, + "memory_reserve_expand": false, + "memory_limit": -1, + "memory_shares": 61440, + "memory_shares_level": "normal", + "cpu_reserve": 0, + "cpu_reserve_expand": false, + "cpu_limit": -1, + "cpu_shares": 4000, + "cpu_shares_level": "normal", + "template": false, + "ems_ref_obj": "vm-63", + "miq_group_id": 2, + "linked_clone": true, + "fault_tolerance": false, + "type": "ManageIQ::Providers::Vmware::InfraManager::Vm", + "ems_ref": "vm-63", + "ems_cluster_id": 5, + "cloud": false, + "raw_power_state": "poweredOff", + "tenant_id": 1, + "cpu_hot_add_enabled": false, + "cpu_hot_remove_enabled": false, + "memory_hot_add_enabled": false, + "actions": [ + { + "name": "add_lifecycle_event", + "method": "post", + "href": "http://localhost:3000/api/vms/166" + }, + { + "name": "add_event", + "method": "post", + "href": "http://localhost:3000/api/vms/166" + }, + { + "name": "refresh", + "method": "post", + "href": "http://localhost:3000/api/vms/166" + }, + { + "name": "shutdown_guest", + "method": "post", + "href": "http://localhost:3000/api/vms/166" + }, + { + "name": "reboot_guest", + "method": "post", + "href": "http://localhost:3000/api/vms/166" + }, + { + "name": "start", + "method": "post", + "href": "http://localhost:3000/api/vms/166" + }, + { + "name": "stop", + "method": "post", + "href": "http://localhost:3000/api/vms/166" + }, + { + "name": "suspend", + "method": "post", + "href": "http://localhost:3000/api/vms/166" + }, + { + "name": "shelve", + "method": "post", + "href": "http://localhost:3000/api/vms/166" + }, + { + "name": "shelve_offload", + "method": "post", + "href": "http://localhost:3000/api/vms/166" + }, + { + "name": "pause", + "method": "post", + "href": "http://localhost:3000/api/vms/166" + }, + { + "name": "request_console", + "method": "post", + "href": "http://localhost:3000/api/vms/166" + }, + { + "name": "reset", + "method": "post", + "href": "http://localhost:3000/api/vms/166" + }, + { + "name": "retire", + "method": "post", + "href": "http://localhost:3000/api/vms/166" + }, + { + "name": "delete", + "method": "post", + "href": "http://localhost:3000/api/vms/166" + }, + { + "name": "set_owner", + "method": "post", + "href": "http://localhost:3000/api/vms/166" + }, + { + "name": "set_ownership", + "method": "post", + "href": "http://localhost:3000/api/vms/166" + }, + { + "name": "scan", + "method": "post", + "href": "http://localhost:3000/api/vms/166" + }, + { + "name": "delete", + "method": "delete", + "href": "http://localhost:3000/api/vms/166" + } + ] + }, + { + "href": "http://localhost:3000/api/vms/185", + "id": 185, + "vendor": "vmware", + "name": "aab-test1", + "location": "aab-test1/aab-test1.vmx", + "host_id": 14, + "created_on": "2017-01-25T23:39:53Z", + "updated_on": "2017-01-25T23:39:53Z", + "storage_id": 13, + "guid": "91f78e74-e357-11e6-b22f-000a27020067", + "ems_id": 3, + "uid_ems": "420c8f0d-4a8f-0b6d-fc3a-0c3cfab7b33f", + "boot_time": "2017-01-03T21:10:55Z", + "tools_status": "toolsOk", + "standby_action": "checkpoint", + "power_state": "on", + "state_changed_on": "2017-01-25T23:39:53Z", + "connection_state": "connected", + "memory_reserve": 0, + "memory_reserve_expand": false, + "memory_limit": -1, + "memory_shares": 40960, + "memory_shares_level": "normal", + "cpu_reserve": 0, + "cpu_reserve_expand": false, + "cpu_limit": -1, + "cpu_shares": 2000, + "cpu_shares_level": "normal", + "template": false, + "ems_ref_obj": "vm-825", + "miq_group_id": 2, + "linked_clone": true, + "fault_tolerance": false, + "type": "ManageIQ::Providers::Vmware::InfraManager::Vm", + "ems_ref": "vm-825", + "ems_cluster_id": 5, + "cloud": false, + "raw_power_state": "poweredOn", + "tenant_id": 1, + "cpu_hot_add_enabled": true, + "cpu_hot_remove_enabled": false, + "memory_hot_add_enabled": true, + "memory_hot_add_limit": 65536, + "memory_hot_add_increment": 128, + "actions": [ + { + "name": "add_lifecycle_event", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "add_event", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "refresh", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "shutdown_guest", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "reboot_guest", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "start", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "stop", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "suspend", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "shelve", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "shelve_offload", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "pause", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "request_console", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "reset", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "retire", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "delete", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "set_owner", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "set_ownership", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "scan", + "method": "post", + "href": "http://localhost:3000/api/vms/185" + }, + { + "name": "delete", + "method": "delete", + "href": "http://localhost:3000/api/vms/185" + } + ] + }, + { + "href": "http://localhost:3000/api/vms/163", + "id": 163, + "vendor": "vmware", + "name": "aab-test2", + "location": "aab-test2/aab-test2.vmx", + "host_id": 12, + "created_on": "2017-01-25T23:39:51Z", + "updated_on": "2017-01-26T18:48:11Z", + "storage_id": 13, + "guid": "913d2548-e357-11e6-b22f-000a27020067", + "ems_id": 3, + "uid_ems": "4233467e-9249-f495-f348-3ca09711bac1", + "boot_time": "2017-01-04T14:26:18Z", + "tools_status": "toolsOk", + "standby_action": "checkpoint", + "power_state": "on", + "state_changed_on": "2017-01-25T23:39:51Z", + "connection_state": "connected", + "memory_reserve": 0, + "memory_reserve_expand": false, + "memory_limit": -1, + "memory_shares": 40960, + "memory_shares_level": "normal", + "cpu_reserve": 0, + "cpu_reserve_expand": false, + "cpu_limit": -1, + "cpu_shares": 2000, + "cpu_shares_level": "normal", + "template": false, + "ems_ref_obj": "vm-60", + "miq_group_id": 2, + "linked_clone": true, + "fault_tolerance": false, + "type": "ManageIQ::Providers::Vmware::InfraManager::Vm", + "ems_ref": "vm-60", + "ems_cluster_id": 5, + "cloud": false, + "raw_power_state": "poweredOn", + "tenant_id": 1, + "cpu_hot_add_enabled": false, + "cpu_hot_remove_enabled": false, + "memory_hot_add_enabled": false, + "memory_hot_add_limit": 4096, + "memory_hot_add_increment": 0, + "actions": [ + { + "name": "add_lifecycle_event", + "method": "post", + "href": "http://localhost:3000/api/vms/163" + }, + { + "name": "add_event", + "method": "post", + "href": "http://localhost:3000/api/vms/163" + }, + { + "name": "refresh", + "method": "post", + "href": "http://localhost:3000/api/vms/163" + }, + { + "name": "shutdown_guest", + "method": "post", + "href": "http://localhost:3000/api/vms/163" + }, + { + "name": "reboot_guest", + "method": "post", + "href": "http://localhost:3000/api/vms/163" + }, + { + "name": "start", + "method": "post", + "href": "http://localhost:3000/api/vms/163" + }, + { + "name": "stop", + "method": "post", + "href": "http://localhost:3000/api/vms/163" + }, + { + "name": "suspend", + "method": "post", + "href": "http://localhost:3000/api/vms/163" + }, + { + "name": "shelve", + "method": "post", + "href": "http://localhost:3000/api/vms/163" + }, + { + "name": "shelve_offload", + "method": "post", + "href": "http://localhost:3000/api/vms/163" + }, + { + "name": "pause", + "method": "post", + "href": "http://localhost:3000/api/vms/163" + }, + { + "name": "request_console", + "method": "post", + "href": "http://localhost:3000/api/vms/163" + }, + { + "name": "reset", + "method": "post", + "href": "http://localhost:3000/api/vms/163" + }, + { + "name": "retire", + "method": "post", + "href": "http://localhost:3000/api/vms/163" + }, + { + "name": "delete", + "method": "post", + "href": "http://localhost:3000/api/vms/163" + }, + { + "name": "set_owner", + "method": "post", + "href": "http://localhost:3000/api/vms/163" + }, + { + "name": "set_ownership", + "method": "post", + "href": "http://localhost:3000/api/vms/163" + }, + { + "name": "scan", + "method": "post", + "href": "http://localhost:3000/api/vms/163" + }, + { + "name": "delete", + "method": "delete", + "href": "http://localhost:3000/api/vms/163" + } + ] + } + ], + "actions": [ + { + "name": "query", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "add_lifecycle_event", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "add_event", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "refresh", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "shutdown_guest", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "reboot_guest", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "start", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "stop", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "suspend", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "shelve", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "shelve_offload", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "pause", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "request_console", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "reset", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "retire", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "set_owner", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "set_ownership", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "scan", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "delete", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "assign_tags", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "unassign_tags", + "method": "post", + "href": "http://localhost:3000/api/vms" + } + ] +} diff --git a/spec/fixtures/api/responses/options_groups.json b/spec/fixtures/api/responses/options_groups.json index 366be37..91b0aa8 100644 --- a/spec/fixtures/api/responses/options_groups.json +++ b/spec/fixtures/api/responses/options_groups.json @@ -46,6 +46,9 @@ "users", "vms" ], + "subcollections": [ + "tags" + ], "data": { "required_fields": [ "id", diff --git a/spec/fixtures/api/responses/options_vms.json b/spec/fixtures/api/responses/options_vms.json new file mode 100644 index 0000000..164d163 --- /dev/null +++ b/spec/fixtures/api/responses/options_vms.json @@ -0,0 +1,374 @@ +{ + "attributes": [ + "autostart", + "availability_zone_id", + "boot_time", + "busy", + "cloud", + "cloud_network_id", + "cloud_subnet_id", + "cloud_tenant_id", + "config_xml", + "connection_state", + "cpu_affinity", + "cpu_hot_add_enabled", + "cpu_hot_remove_enabled", + "cpu_limit", + "cpu_reserve", + "cpu_reserve_expand", + "cpu_shares", + "cpu_shares_level", + "created_on", + "deprecated", + "description", + "ems_cluster_id", + "ems_created_on", + "ems_id", + "ems_ref", + "ems_ref_obj", + "evm_owner_id", + "fault_tolerance", + "flavor_id", + "format", + "guid", + "host_id", + "id", + "last_perf_capture_on", + "last_scan_attempt_on", + "last_scan_on", + "last_sync_on", + "linked_clone", + "location", + "memory_hot_add_enabled", + "memory_hot_add_increment", + "memory_hot_add_limit", + "memory_limit", + "memory_reserve", + "memory_reserve_expand", + "memory_shares", + "memory_shares_level", + "miq_group_id", + "name", + "orchestration_stack_id", + "power_state", + "previous_state", + "publicly_available", + "raw_power_state", + "registered", + "resource_group_id", + "retired", + "retirement_last_warn", + "retirement_requester", + "retirement_state", + "retirement_warn", + "retires_on", + "smart", + "standby_action", + "state_changed_on", + "storage_id", + "storage_profile_id", + "template", + "tenant_id", + "tools_status", + "type", + "uid_ems", + "updated_on", + "vendor", + "version", + "vnc_port" + ], + "virtual_attributes": [ + "active", + "aggressive_mem_recommended_change", + "aggressive_mem_recommended_change_pct", + "aggressive_recommended_mem", + "aggressive_recommended_vcpus", + "aggressive_vcpus_recommended_change", + "aggressive_vcpus_recommended_change_pct", + "allocated_disk_storage", + "archived", + "conservative_mem_recommended_change", + "conservative_mem_recommended_change_pct", + "conservative_recommended_mem", + "conservative_recommended_vcpus", + "conservative_vcpus_recommended_change", + "conservative_vcpus_recommended_change_pct", + "cpu_cores_per_socket", + "cpu_total_cores", + "cpu_usagemhz_rate_average_avg_over_time_period", + "cpu_usagemhz_rate_average_high_over_time_period", + "cpu_usagemhz_rate_average_low_over_time_period", + "cpu_usagemhz_rate_average_max_over_time_period", + "custom_1", + "custom_2", + "custom_3", + "custom_4", + "custom_5", + "custom_6", + "custom_7", + "custom_8", + "custom_9", + "debris_size", + "derived_memory_used_avg_over_time_period", + "derived_memory_used_high_over_time_period", + "derived_memory_used_low_over_time_period", + "derived_memory_used_max_over_time_period", + "disconnected", + "disk_1_disk_type", + "disk_1_mode", + "disk_1_partitions_aligned", + "disk_1_size", + "disk_1_size_on_disk", + "disk_1_used_percent_of_provisioned", + "disk_2_disk_type", + "disk_2_mode", + "disk_2_partitions_aligned", + "disk_2_size", + "disk_2_size_on_disk", + "disk_2_used_percent_of_provisioned", + "disk_3_disk_type", + "disk_3_mode", + "disk_3_partitions_aligned", + "disk_3_size", + "disk_3_size_on_disk", + "disk_3_used_percent_of_provisioned", + "disk_4_disk_type", + "disk_4_mode", + "disk_4_partitions_aligned", + "disk_4_size", + "disk_4_size_on_disk", + "disk_4_used_percent_of_provisioned", + "disk_5_disk_type", + "disk_5_mode", + "disk_5_partitions_aligned", + "disk_5_size", + "disk_5_size_on_disk", + "disk_5_used_percent_of_provisioned", + "disk_6_disk_type", + "disk_6_mode", + "disk_6_partitions_aligned", + "disk_6_size", + "disk_6_size_on_disk", + "disk_6_used_percent_of_provisioned", + "disk_7_disk_type", + "disk_7_mode", + "disk_7_partitions_aligned", + "disk_7_size", + "disk_7_size_on_disk", + "disk_7_used_percent_of_provisioned", + "disk_8_disk_type", + "disk_8_mode", + "disk_8_partitions_aligned", + "disk_8_size", + "disk_8_size_on_disk", + "disk_8_used_percent_of_provisioned", + "disk_9_disk_type", + "disk_9_mode", + "disk_9_partitions_aligned", + "disk_9_size", + "disk_9_size_on_disk", + "disk_9_used_percent_of_provisioned", + "disk_size", + "disks_aligned", + "ems_cluster_name", + "evm_owner_email", + "evm_owner_name", + "evm_owner_userid", + "first_drift_state_timestamp", + "has_rdm_disk", + "host_name", + "hostnames", + "ipaddresses", + "is_evm_appliance", + "last_compliance_status", + "last_compliance_timestamp", + "last_drift_state_timestamp", + "mac_addresses", + "max_cpu_usage_rate_average_avg_over_time_period", + "max_cpu_usage_rate_average_avg_over_time_period_without_overhead", + "max_cpu_usage_rate_average_high_over_time_period", + "max_cpu_usage_rate_average_high_over_time_period_without_overhead", + "max_cpu_usage_rate_average_low_over_time_period", + "max_cpu_usage_rate_average_low_over_time_period_without_overhead", + "max_cpu_usage_rate_average_max_over_time_period", + "max_mem_usage_absolute_average_avg_over_time_period", + "max_mem_usage_absolute_average_avg_over_time_period_without_overhead", + "max_mem_usage_absolute_average_high_over_time_period", + "max_mem_usage_absolute_average_high_over_time_period_without_overhead", + "max_mem_usage_absolute_average_low_over_time_period", + "max_mem_usage_absolute_average_low_over_time_period_without_overhead", + "max_mem_usage_absolute_average_max_over_time_period", + "mem_cpu", + "memory_exceeds_current_host_headroom", + "moderate_mem_recommended_change", + "moderate_mem_recommended_change_pct", + "moderate_recommended_mem", + "moderate_recommended_vcpus", + "moderate_vcpus_recommended_change", + "moderate_vcpus_recommended_change_pct", + "num_cpu", + "num_disks", + "num_hard_disks", + "orphaned", + "os_image_name", + "overallocated_mem_pct", + "overallocated_vcpus_pct", + "owned_by_current_ldap_group", + "owned_by_current_user", + "owning_ldap_group", + "paravirtualization", + "parent_blue_folder_1_name", + "parent_blue_folder_2_name", + "parent_blue_folder_3_name", + "parent_blue_folder_4_name", + "parent_blue_folder_5_name", + "parent_blue_folder_6_name", + "parent_blue_folder_7_name", + "parent_blue_folder_8_name", + "parent_blue_folder_9_name", + "platform", + "provisioned_storage", + "ram_size", + "ram_size_in_bytes", + "recommended_mem", + "recommended_vcpus", + "region_description", + "region_number", + "snapshot_size", + "storage_name", + "thin_provisioned", + "uncommitted_storage", + "used_disk_storage", + "used_storage", + "used_storage_by_state", + "v_annotation", + "v_datastore_path", + "v_host_vmm_product", + "v_is_a_template", + "v_owning_blue_folder", + "v_owning_blue_folder_path", + "v_owning_cluster", + "v_owning_datacenter", + "v_owning_folder", + "v_owning_folder_path", + "v_owning_resource_pool", + "v_pct_free_disk_space", + "v_pct_used_disk_space", + "v_snapshot_newest_description", + "v_snapshot_newest_name", + "v_snapshot_newest_timestamp", + "v_snapshot_newest_total_size", + "v_snapshot_oldest_description", + "v_snapshot_oldest_name", + "v_snapshot_oldest_timestamp", + "v_snapshot_oldest_total_size", + "v_total_snapshots", + "vendor_display", + "vm_misc_size", + "vm_ram_size", + "vmsafe_agent_address", + "vmsafe_agent_port", + "vmsafe_enable", + "vmsafe_fail_open", + "vmsafe_immutable_vm", + "vmsafe_timeout_ms" + ], + "relationships": [ + "accounts", + "advanced_settings", + "all_relationships", + "base_storage_extents", + "compliances", + "connected_shares", + "container_deployment", + "container_deployment_node", + "counterparts", + "custom_attributes", + "debris_files", + "direct_service", + "direct_services", + "directories", + "disk_files", + "disks", + "drift_states", + "ems_cluster", + "ems_custom_attributes", + "ems_events", + "ems_events_dest", + "ems_events_src", + "event_logs", + "evm_owner", + "ext_management_system", + "file_shares", + "files", + "filesystem_drivers", + "filesystems", + "first_drift_state", + "first_drift_state_timestamp_rec", + "groups", + "guest_applications", + "hardware", + "host", + "kernel_drivers", + "lans", + "last_compliance", + "last_drift_state", + "last_drift_state_timestamp_rec", + "lifecycle_events", + "linux_initprocesses", + "logical_disks", + "metric_rollups", + "metrics", + "miq_alert_statuses", + "miq_cim_instance", + "miq_custom_attributes", + "miq_events", + "miq_group", + "miq_provision", + "miq_provision_requests", + "miq_provision_template", + "miq_provision_vms", + "miq_provisions_from_template", + "miq_server", + "operating_system", + "parent_resource_pool", + "patches", + "policy_events", + "processes", + "registry_items", + "scan_histories", + "service", + "service_resources", + "snapshot_files", + "snapshots", + "storage", + "storage_files", + "storage_files_files", + "storage_profile", + "storage_systems", + "storage_volumes", + "storages", + "system_services", + "taggings", + "tags", + "tenant", + "users", + "vim_performance_operating_ranges", + "vim_performance_states", + "vm_misc_files", + "vm_ram_files", + "win32_services" + ], + "subcollections": [ + "accounts", + "custom_attributes", + "policies", + "policy_profiles", + "snapshots", + "software", + "tags" + ], + "data": { + } +} + diff --git a/spec/fixtures/api/responses/query_dev_vms.json b/spec/fixtures/api/responses/query_dev_vms.json new file mode 100644 index 0000000..0bbd07a --- /dev/null +++ b/spec/fixtures/api/responses/query_dev_vms.json @@ -0,0 +1,430 @@ +{ + "results": [ + { + "href": "http://localhost:3000/api/vms/161", + "id": 161, + "vendor": "vmware", + "name": "aab-dev1", + "location": "aab-dev1/aab-dev1.vmx", + "host_id": 11, + "created_on": "2017-01-25T23:39:51Z", + "updated_on": "2017-01-25T23:39:51Z", + "storage_id": 13, + "guid": "91282134-e357-11e6-b22f-000a27020067", + "ems_id": 3, + "uid_ems": "564d5056-49ea-a5aa-a71d-cf9b8138430b", + "boot_time": "2017-01-03T20:53:30Z", + "tools_status": "toolsOk", + "standby_action": "checkpoint", + "power_state": "on", + "state_changed_on": "2017-01-25T23:39:51Z", + "connection_state": "connected", + "memory_reserve": 0, + "memory_reserve_expand": false, + "memory_limit": -1, + "memory_shares": 163840, + "memory_shares_level": "normal", + "cpu_reserve": 0, + "cpu_reserve_expand": false, + "cpu_limit": -1, + "cpu_shares": 4000, + "cpu_shares_level": "normal", + "template": false, + "ems_ref_obj": "vm-53", + "miq_group_id": 2, + "linked_clone": true, + "fault_tolerance": false, + "type": "ManageIQ::Providers::Vmware::InfraManager::Vm", + "ems_ref": "vm-53", + "ems_cluster_id": 5, + "cloud": false, + "raw_power_state": "poweredOn", + "tenant_id": 1, + "cpu_hot_add_enabled": true, + "cpu_hot_remove_enabled": false, + "memory_hot_add_enabled": true, + "memory_hot_add_limit": 262144, + "memory_hot_add_increment": 128, + "actions": [ + { + "name": "add_lifecycle_event", + "method": "post", + "href": "http://localhost:3000/api/vms/161" + }, + { + "name": "add_event", + "method": "post", + "href": "http://localhost:3000/api/vms/161" + }, + { + "name": "refresh", + "method": "post", + "href": "http://localhost:3000/api/vms/161" + }, + { + "name": "shutdown_guest", + "method": "post", + "href": "http://localhost:3000/api/vms/161" + }, + { + "name": "reboot_guest", + "method": "post", + "href": "http://localhost:3000/api/vms/161" + }, + { + "name": "start", + "method": "post", + "href": "http://localhost:3000/api/vms/161" + }, + { + "name": "stop", + "method": "post", + "href": "http://localhost:3000/api/vms/161" + }, + { + "name": "suspend", + "method": "post", + "href": "http://localhost:3000/api/vms/161" + }, + { + "name": "shelve", + "method": "post", + "href": "http://localhost:3000/api/vms/161" + }, + { + "name": "shelve_offload", + "method": "post", + "href": "http://localhost:3000/api/vms/161" + }, + { + "name": "pause", + "method": "post", + "href": "http://localhost:3000/api/vms/161" + }, + { + "name": "request_console", + "method": "post", + "href": "http://localhost:3000/api/vms/161" + }, + { + "name": "reset", + "method": "post", + "href": "http://localhost:3000/api/vms/161" + }, + { + "name": "retire", + "method": "post", + "href": "http://localhost:3000/api/vms/161" + }, + { + "name": "delete", + "method": "post", + "href": "http://localhost:3000/api/vms/161" + }, + { + "name": "set_owner", + "method": "post", + "href": "http://localhost:3000/api/vms/161" + }, + { + "name": "set_ownership", + "method": "post", + "href": "http://localhost:3000/api/vms/161" + }, + { + "name": "scan", + "method": "post", + "href": "http://localhost:3000/api/vms/161" + }, + { + "name": "delete", + "method": "delete", + "href": "http://localhost:3000/api/vms/161" + } + ] + }, + { + "href": "http://localhost:3000/api/vms/165", + "id": 165, + "vendor": "vmware", + "name": "aab-dev2", + "location": "aab-dev2/aab-dev2.vmx", + "host_id": 14, + "created_on": "2017-01-25T23:39:52Z", + "updated_on": "2017-01-25T23:39:52Z", + "storage_id": 13, + "guid": "91503368-e357-11e6-b22f-000a27020067", + "ems_id": 3, + "uid_ems": "564d8be8-84bf-03ab-c421-a503da6db700", + "boot_time": "2017-01-03T21:08:47Z", + "tools_status": "toolsOk", + "standby_action": "checkpoint", + "power_state": "on", + "state_changed_on": "2017-01-25T23:39:52Z", + "connection_state": "connected", + "memory_reserve": 0, + "memory_reserve_expand": false, + "memory_limit": -1, + "memory_shares": 40960, + "memory_shares_level": "normal", + "cpu_reserve": 0, + "cpu_reserve_expand": false, + "cpu_limit": -1, + "cpu_shares": 2000, + "cpu_shares_level": "normal", + "template": false, + "ems_ref_obj": "vm-62", + "miq_group_id": 2, + "linked_clone": true, + "fault_tolerance": false, + "type": "ManageIQ::Providers::Vmware::InfraManager::Vm", + "ems_ref": "vm-62", + "ems_cluster_id": 5, + "cloud": false, + "raw_power_state": "poweredOn", + "tenant_id": 1, + "cpu_hot_add_enabled": true, + "cpu_hot_remove_enabled": false, + "memory_hot_add_enabled": true, + "memory_hot_add_limit": 65536, + "memory_hot_add_increment": 128, + "actions": [ + { + "name": "add_lifecycle_event", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "add_event", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "refresh", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "shutdown_guest", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "reboot_guest", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "start", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "stop", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "suspend", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "shelve", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "shelve_offload", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "pause", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "request_console", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "reset", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "retire", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "delete", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "set_owner", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "set_ownership", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "scan", + "method": "post", + "href": "http://localhost:3000/api/vms/165" + }, + { + "name": "delete", + "method": "delete", + "href": "http://localhost:3000/api/vms/165" + } + ] + }, + { + "href": "http://localhost:3000/api/vms/166", + "id": 166, + "vendor": "vmware", + "name": "aab-dev3", + "location": "aab-dev3/aab-dev3.vmx", + "host_id": 12, + "created_on": "2017-01-25T23:39:52Z", + "updated_on": "2017-01-25T23:39:52Z", + "storage_id": 13, + "guid": "9158bc4a-e357-11e6-b22f-000a27020067", + "ems_id": 3, + "uid_ems": "42332064-1b21-3bc2-ee0b-09ae0799bc3b", + "tools_status": "toolsNotRunning", + "standby_action": "checkpoint", + "power_state": "off", + "state_changed_on": "2017-01-25T23:39:52Z", + "connection_state": "connected", + "memory_reserve": 0, + "memory_reserve_expand": false, + "memory_limit": -1, + "memory_shares": 61440, + "memory_shares_level": "normal", + "cpu_reserve": 0, + "cpu_reserve_expand": false, + "cpu_limit": -1, + "cpu_shares": 4000, + "cpu_shares_level": "normal", + "template": false, + "ems_ref_obj": "vm-63", + "miq_group_id": 2, + "linked_clone": true, + "fault_tolerance": false, + "type": "ManageIQ::Providers::Vmware::InfraManager::Vm", + "ems_ref": "vm-63", + "ems_cluster_id": 5, + "cloud": false, + "raw_power_state": "poweredOff", + "tenant_id": 1, + "cpu_hot_add_enabled": false, + "cpu_hot_remove_enabled": false, + "memory_hot_add_enabled": false, + "actions": [ + { + "name": "add_lifecycle_event", + "method": "post", + "href": "http://localhost:3000/api/vms/166" + }, + { + "name": "add_event", + "method": "post", + "href": "http://localhost:3000/api/vms/166" + }, + { + "name": "refresh", + "method": "post", + "href": "http://localhost:3000/api/vms/166" + }, + { + "name": "shutdown_guest", + "method": "post", + "href": "http://localhost:3000/api/vms/166" + }, + { + "name": "reboot_guest", + "method": "post", + "href": "http://localhost:3000/api/vms/166" + }, + { + "name": "start", + "method": "post", + "href": "http://localhost:3000/api/vms/166" + }, + { + "name": "stop", + "method": "post", + "href": "http://localhost:3000/api/vms/166" + }, + { + "name": "suspend", + "method": "post", + "href": "http://localhost:3000/api/vms/166" + }, + { + "name": "shelve", + "method": "post", + "href": "http://localhost:3000/api/vms/166" + }, + { + "name": "shelve_offload", + "method": "post", + "href": "http://localhost:3000/api/vms/166" + }, + { + "name": "pause", + "method": "post", + "href": "http://localhost:3000/api/vms/166" + }, + { + "name": "request_console", + "method": "post", + "href": "http://localhost:3000/api/vms/166" + }, + { + "name": "reset", + "method": "post", + "href": "http://localhost:3000/api/vms/166" + }, + { + "name": "retire", + "method": "post", + "href": "http://localhost:3000/api/vms/166" + }, + { + "name": "delete", + "method": "post", + "href": "http://localhost:3000/api/vms/166" + }, + { + "name": "set_owner", + "method": "post", + "href": "http://localhost:3000/api/vms/166" + }, + { + "name": "set_ownership", + "method": "post", + "href": "http://localhost:3000/api/vms/166" + }, + { + "name": "scan", + "method": "post", + "href": "http://localhost:3000/api/vms/166" + }, + { + "name": "delete", + "method": "delete", + "href": "http://localhost:3000/api/vms/166" + } + ] + } + ] +} diff --git a/spec/fixtures/api/responses/single_vm_query.json b/spec/fixtures/api/responses/single_vm_query.json new file mode 100644 index 0000000..8e1433d --- /dev/null +++ b/spec/fixtures/api/responses/single_vm_query.json @@ -0,0 +1,117 @@ +{ + "name": "vms", + "count": 5, + "subcount": 1, + "resources": [ + { + "href": "http://localhost:3000/api/vms/161" + } + ], + "actions": [ + { + "name": "query", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "add_lifecycle_event", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "add_event", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "refresh", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "shutdown_guest", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "reboot_guest", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "start", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "stop", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "suspend", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "shelve", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "shelve_offload", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "pause", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "request_console", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "reset", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "retire", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "set_owner", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "set_ownership", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "scan", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "delete", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "assign_tags", + "method": "post", + "href": "http://localhost:3000/api/vms" + }, + { + "name": "unassign_tags", + "method": "post", + "href": "http://localhost:3000/api/vms" + } + ] +} diff --git a/spec/manageiq/api/collection_spec.rb b/spec/manageiq/api/collection_spec.rb index 64a20d3..e854f0e 100644 --- a/spec/manageiq/api/collection_spec.rb +++ b/spec/manageiq/api/collection_spec.rb @@ -4,13 +4,46 @@ let(:groups_url) { "#{api_url}/groups" } let(:single_group_query_url) { "#{api_url}/groups?limit=1" } + let(:vms_url) { "#{api_url}/vms" } + let(:vms_expand_url) { "#{vms_url}?expand=resources" } + let(:single_vm_query_url) { "#{api_url}/vms?limit=1" } + let(:entrypoint_response) { api_file_fixture("responses/entrypoint.json") } + let(:get_vms_response) { api_file_fixture("responses/get_vms.json") } + let(:get_no_vms_response) { api_file_fixture("responses/get_no_vms.json") } + let(:query_dev_vms_response) { api_file_fixture("responses/query_dev_vms.json") } + let(:actions_vms_response) { api_file_fixture("responses/actions_vms.json") } + let(:single_vm_query_response) { api_file_fixture("responses/single_vm_query.json") } let(:single_group_query_response) { api_file_fixture("responses/single_group_query.json") } + let(:options_groups_response) { api_file_fixture("responses/options_groups.json") } + let(:options_vms_response) { api_file_fixture("responses/options_vms.json") } + + describe ".new" do + it "client creates collections" do + stub_request(:get, entrypoint_request_url) + .to_return(:status => 200, :body => entrypoint_response, :headers => {}) + + miq = ManageIQ::API::Client.new + collections = JSON.parse(entrypoint_response)["collections"] + collection_names = collections.collect { |cspec| cspec["name"] } + expect(miq.collections.collect(&:name)).to match_array(collection_names) + + miq.collections.each do |collection| + expect(collection).to be_a(described_class.subclass(collection.name)) + end + end + end + describe "actions" do before do stub_request(:get, entrypoint_request_url) .to_return(:status => 200, :body => entrypoint_response, :headers => {}) + + stub_request(:get, single_vm_query_url) + .to_return(:status => 200, :body => single_vm_query_response, :headers => {}) + + @vms_hash = JSON.parse(get_vms_response) end it "fetch a single resource for getting collection actions" do @@ -25,5 +58,269 @@ miq = ManageIQ::API::Client.new miq.groups.create(:description => "sample group") end + + it "exposed by new collection" do + miq = ManageIQ::API::Client.new + vms = miq.vms + + @vms_hash["actions"].each do |aspec| + if aspec["method"] == "post" + expect(vms.respond_to?(aspec["name"].to_sym)).to be_truthy + end + end + end + + it "supported by collection" do + action = @vms_hash["actions"].detect { |aspec| aspec["method"] == "post" && aspec["name"] != "query" } + + stub_request(:post, vms_url) + .with(:body => {"action" => action["name"]}, :headers => {'Content-Type' => 'application/json'}) + .to_return(:status => 200, :body => actions_vms_response, :headers => {}) + + miq = ManageIQ::API::Client.new + miq.vms.public_send(action["name"]) + end + + it "supported for single resources" do + action = @vms_hash["actions"].detect { |aspec| aspec["method"] == "post" } + vm_id = @vms_hash["resources"].first["id"] + + stub_request(:post, vms_url) + .with(:body => {"action" => action["name"], "resource" => { "id" => vm_id }}, + :headers => {'Content-Type' => 'application/json'}) + .to_return(:status => 200, :body => actions_vms_response, :headers => {}) + + miq = ManageIQ::API::Client.new + miq.vms.public_send(action["name"], :id => vm_id) + end + + it "supported for multiple resources" do + action = @vms_hash["actions"].detect { |aspec| aspec["method"] == "post" } + vm_ids = @vms_hash["resources"][0, 3].collect { |vm| vm["id"] } + vm_resources = vm_ids.collect { |id| { "id" => id, "parameter" => "value" } } + + stub_request(:post, vms_url) + .with(:body => {"action" => action["name"], "resources" => vm_resources}, + :headers => {'Content-Type' => 'application/json'}) + .to_return(:status => 200, :body => actions_vms_response, :headers => {}) + + miq = ManageIQ::API::Client.new + miq.vms.public_send(action["name"], vm_resources) + end + end + + describe "options" do + let(:group_options_hash) { JSON.parse(options_groups_response) } + + it "are exposed for collections" do + stub_request(:get, entrypoint_request_url) + .to_return(:status => 200, :body => entrypoint_response, :headers => {}) + + stub_request(:options, groups_url) + .to_return(:status => 200, :body => options_groups_response, :headers => {}) + + miq = ManageIQ::API::Client.new + group_options = miq.groups.options + + expect(group_options.attributes).to match_array(group_options_hash['attributes']) + expect(group_options.virtual_attributes).to match_array(group_options_hash['virtual_attributes']) + expect(group_options.relationships).to match_array(group_options_hash['relationships']) + expect(group_options.subcollections).to match_array(group_options_hash['subcollections']) + expect(group_options.data).to eq(group_options_hash['data']) + end + end + + describe "get" do + it "returns array of resources" do + stub_request(:get, entrypoint_request_url) + .to_return(:status => 200, :body => entrypoint_response, :headers => {}) + + stub_request(:get, vms_expand_url) + .to_return(:status => 200, :body => get_vms_response, :headers => {}) + + miq = ManageIQ::API::Client.new + miq.vms.get.collect do |resource| + expect(resource).to be_a(ManageIQ::API::Client::Resource::Vm) + end + end + + it "returns valid set of resources" do + stub_request(:get, entrypoint_request_url) + .to_return(:status => 200, :body => entrypoint_response, :headers => {}) + + stub_request(:get, vms_expand_url) + .to_return(:status => 200, :body => get_vms_response, :headers => {}) + + miq = ManageIQ::API::Client.new + vms_hash = JSON.parse(get_vms_response) + + expect(miq.vms.collect(&:name)).to match_array(vms_hash["resources"].collect { |vm| vm["name"] }) + end + end + + describe "queryable" do + let(:vms_test1_response) { api_file_fixture("responses/get_test1_vms.json") } + + before do + stub_request(:get, entrypoint_request_url) + .to_return(:status => 200, :body => entrypoint_response, :headers => {}) + + stub_request(:options, vms_url) + .to_return(:status => 200, :body => options_vms_response, :headers => {}) + + @miq = ManageIQ::API::Client.new + end + + it "supports select" do + stub_request(:get, "#{vms_expand_url}&attributes=name") + .to_return(:status => 200, :body => get_vms_response, :headers => {}) + + vm_names = JSON.parse(get_vms_response)["resources"].collect { |vm| vm["name"] } + + expect(@miq.vms.select(:name).collect(&:name)).to match_array(vm_names) + end + + it "supports offset" do + stub_request(:get, "#{vms_expand_url}&offset=100") + .to_return(:status => 200, :body => get_no_vms_response, :headers => {}) + + @miq.vms.offset(100).collect(&:name) + end + + it "supports offset and limit" do + stub_request(:get, "#{vms_expand_url}&offset=100&limit=50") + .to_return(:status => 200, :body => get_no_vms_response, :headers => {}) + + @miq.vms.offset(100).limit(50).collect(&:name) + end + + it "supports where" do + stub_request(:get, "#{vms_expand_url}&filter[]=name='aab-test1'") + .to_return(:status => 200, :body => vms_test1_response, :headers => {}) + + expect(@miq.vms.where(:name => "aab-test1").collect(&:name).first).to eq("aab-test1") + end + + it "supports chainable where filters" do + stub_request(:get, "#{vms_expand_url}"\ + "&filter[]=name='bad-dev'"\ + "&filter[]=memory_shares=8192"\ + "&filter[]=power_state=nil") + .to_return(:status => 200, :body => get_no_vms_response, :headers => {}) + + @miq.vms.where(:name => "bad-dev").where(:power_state => 'nil').where(:memory_shares => 8192).collect(&:name) + end + + it "supports first" do + stub_request(:get, "#{vms_expand_url}&filter[]=name='aab-test1'&limit=1") + .to_return(:status => 200, :body => vms_test1_response, :headers => {}) + + expect(@miq.vms.where(:name => "aab-test1").first.name).to eq("aab-test1") + end + + it "supports last" do + stub_request(:get, vms_expand_url) + .to_return(:status => 200, :body => get_vms_response, :headers => {}) + + vms = JSON.parse(get_vms_response)["resources"] + vm_names = vms.collect { |vm| vm["name"] } + + expect(@miq.vms.last.name).to eq(vm_names.last) + end + + it "supports pluck" do + stub_request(:get, "#{vms_expand_url}&attributes=guid") + .to_return(:status => 200, :body => get_vms_response, :headers => {}) + + vms = JSON.parse(get_vms_response)["resources"] + vm_guids = vms.collect { |vm| vm["guid"] } + + expect(@miq.vms.pluck(:guid)).to match_array(vm_guids) + end + + it "supports find" do + vms_dev2_response = api_file_fixture("responses/get_dev2_vms.json") + dev2_vm = JSON.parse(vms_dev2_response)["resources"].first + dev2_id = dev2_vm["id"] + + stub_request(:get, "#{vms_expand_url}&filter[]=id=#{dev2_id}&limit=1") + .to_return(:status => 200, :body => vms_dev2_response, :headers => {}) + + expect(@miq.vms.find(dev2_vm["id"]).name).to eq("aab-dev2") + end + + it "supports find with multiple ids" do + dev_vms = JSON.parse(query_dev_vms_response)["results"] + dev_ids = dev_vms.collect { |vm| vm["id"] } + dev_names = dev_vms.collect { |vm| vm["name"] } + + stub_request(:get, single_vm_query_url) + .to_return(:status => 200, :body => single_vm_query_response, :headers => {}) + + stub_request(:post, vms_url) + .with(:body => {"action" => "query", "resources" => dev_ids.collect { |id| { "id" => id } }}, + :headers => {'Content-Type' => 'application/json'}) + .to_return(:status => 200, :body => query_dev_vms_response, :headers => {}) + + expect(@miq.vms.find(dev_ids).collect(&:name)).to match_array(dev_names) + end + + it "supports find_by" do + stub_request(:get, "#{vms_expand_url}&filter[]=name='aab-test1'&limit=1") + .to_return(:status => 200, :body => vms_test1_response, :headers => {}) + + expect(@miq.vms.find_by(:name => "aab-test1").name).to eq("aab-test1") + end + + it "supports order" do + stub_request(:get, "#{vms_expand_url}&sort_by=name&sort_order=asc") + .to_return(:status => 200, :body => get_no_vms_response, :headers => {}) + + @miq.vms.order(:name).collect(&:name) + end + + it "supports descending order" do + stub_request(:get, "#{vms_expand_url}&sort_by=name&sort_order=desc") + .to_return(:status => 200, :body => get_no_vms_response, :headers => {}) + + @miq.vms.order(:name => "descending").collect(&:name) + end + + it "supports multiple order" do + stub_request(:get, "#{vms_expand_url}&sort_by=name,vendor&sort_order=asc,asc") + .to_return(:status => 200, :body => get_no_vms_response, :headers => {}) + + @miq.vms.order(:name, :vendor).collect(&:name) + end + + it "supports multiple order with mixed ascending and descending" do + stub_request(:get, "#{vms_expand_url}&sort_by=name,vendor&sort_order=asc,desc") + .to_return(:status => 200, :body => get_no_vms_response, :headers => {}) + + @miq.vms.order(:name => "ascending", :vendor => "descending").collect(&:name) + end + + it "supports chainable order with mixed ascending and descending" do + stub_request(:get, "#{vms_expand_url}&sort_by=name,vendor&sort_order=desc,asc") + .to_return(:status => 200, :body => get_no_vms_response, :headers => {}) + + @miq.vms.order(:name => "DESC").order(:vendor => "ASC").collect(&:name) + end + + it "supports compound chaining" do + stub_request(:get, "#{vms_expand_url}"\ + "&attributes=name,vendor,guid,power_state"\ + "&sort_by=name,vendor&sort_order=asc,desc"\ + "&filter[]=name='prod*'&filter[]=memory_shares=4096"\ + "&offset=100&limit=25") + .to_return(:status => 200, :body => get_no_vms_response, :headers => {}) + + @miq.vms + .select(:name, :vendor, :guid, :power_state) + .order(:name).order(:vendor => "descending") + .where(:name => "prod*").where(:memory_shares => 4096) + .offset(100).limit(25) + .collect(&:name) + end end end diff --git a/spec/manageiq/api/resource_spec.rb b/spec/manageiq/api/resource_spec.rb new file mode 100644 index 0000000..6ae264a --- /dev/null +++ b/spec/manageiq/api/resource_spec.rb @@ -0,0 +1,82 @@ +describe ManageIQ::API::Client::Resource do + let(:api_url) { "http://localhost:3000/api" } + let(:vms_url) { "#{api_url}/vms" } + let(:vms_expand_url) { "#{vms_url}?expand=resources" } + let(:entrypoint_request_url) { "#{api_url}?attributes=authorization" } + + let(:entrypoint_response) { api_file_fixture("responses/entrypoint.json") } + let(:get_test1_vms_response) { api_file_fixture("responses/get_test1_vms.json") } + let(:actions_vms_response) { api_file_fixture("responses/actions_vms.json") } + + describe "resource" do + before do + stub_request(:get, entrypoint_request_url) + .to_return(:status => 200, :body => entrypoint_response, :headers => {}) + + stub_request(:get, "#{vms_expand_url}&filter[]=name='aab-test1'&limit=1") + .to_return(:status => 200, :body => get_test1_vms_response, :headers => {}) + + miq = ManageIQ::API::Client.new + @vm = miq.vms.where(:name => "aab-test1").first + @vm_hash = JSON.parse(get_test1_vms_response)["resources"].first + end + + it "is of valid type" do + expect(@vm).to be_a(ManageIQ::API::Client::Resource::Vm) + end + + it "exposes attributes hash" do + expect(@vm.attributes).to match(a_hash_including(@vm_hash.except("actions"))) + end + + it "exposes action objects" do + expect(@vm.actions.first).to be_a(ManageIQ::API::Client::Action) + end + + it "exposes related collection" do + expect(@vm.collection).to be_a(ManageIQ::API::Client::Collection::Vms) + end + + it "exposes attributes" do + expect(@vm.id).to eq(@vm_hash["id"]) + expect(@vm.name).to eq(@vm_hash["name"]) + expect(@vm.vendor).to eq(@vm_hash["vendor"]) + end + + it "exposes attributes via []" do + expect(@vm["id"]).to eq(@vm_hash["id"]) + expect(@vm["name"]).to eq(@vm_hash["name"]) + expect(@vm["vendor"]).to eq(@vm_hash["vendor"]) + end + + it "responds to actions" do + @vm_hash["actions"].each do |aspec| + if aspec["method"] == "post" + expect(@vm.respond_to?(aspec["name"].to_sym)).to be_truthy + end + end + end + + it "supports invoking actions" do + action = @vm_hash["actions"].detect { |aspec| aspec["method"] == "post" } + + stub_request(:post, @vm_hash["href"]) + .with(:body => {"action" => action["name"]}, :headers => {'Content-Type' => 'application/json'}) + .to_return(:status => 200, :body => actions_vms_response, :headers => {}) + + @vm.public_send(action["name"]) + end + + it "supports invoking actions with parameters" do + action = @vm_hash["actions"].detect { |aspec| aspec["method"] == "post" } + + stub_request(:post, @vm_hash["href"]) + .with(:body => {"action" => action["name"], + "resource" => { "parameter" => "value" }}, + :headers => {'Content-Type' => 'application/json'}) + .to_return(:status => 200, :body => actions_vms_response, :headers => {}) + + @vm.public_send(action["name"], :parameter => "value") + end + end +end diff --git a/spec/manageiq/api/subcollection_spec.rb b/spec/manageiq/api/subcollection_spec.rb new file mode 100644 index 0000000..03a8a7d --- /dev/null +++ b/spec/manageiq/api/subcollection_spec.rb @@ -0,0 +1,253 @@ +describe ManageIQ::API::Client::Subcollection do + let(:api_url) { "http://localhost:3000/api" } + let(:entrypoint_request_url) { "#{api_url}?attributes=authorization" } + let(:vms_url) { "#{api_url}/vms" } + let(:vms_expand_url) { "#{vms_url}?expand=resources" } + + let(:entrypoint_response) { api_file_fixture("responses/entrypoint.json") } + let(:options_vms_response) { api_file_fixture("responses/options_vms.json") } + let(:get_test1_vms_response) { api_file_fixture("responses/get_test1_vms.json") } + let(:get_test1_tags_response) { api_file_fixture("responses/get_test1_vm_tags.json") } + let(:get_test1_no_tags_response) { api_file_fixture("responses/get_test1_vm_no_tags.json") } + let(:actions_vm_tags_response) { api_file_fixture("responses/actions_vm_tags.json") } + + before do + stub_request(:get, entrypoint_request_url) + .to_return(:status => 200, :body => entrypoint_response, :headers => {}) + + stub_request(:get, "#{vms_expand_url}&filter[]=name='aab-test1'&limit=1") + .to_return(:status => 200, :body => get_test1_vms_response, :headers => {}) + + stub_request(:options, vms_url) + .to_return(:status => 200, :body => options_vms_response, :headers => {}) + + miq = ManageIQ::API::Client.new + @vm = miq.vms.where(:name => "aab-test1").first + @vm_options = JSON.parse(options_vms_response) + @vm_hash = JSON.parse(get_test1_vms_response)["resources"].first + end + + describe "resource" do + it "responds to subcollections" do + @vm_options["subcollections"].each do |subcollection| + expect(@vm.respond_to?(subcollection.to_sym)).to be_truthy + end + end + + it "creates subcollections of correct type" do + stub_request(:get, "#{vms_url}/#{@vm.id}/tags?hide=resources") + .to_return(:status => 200, :body => get_test1_tags_response, :headers => {}) + + expect(@vm.tags).to be_a(described_class.subclass("tags")) + end + end + + describe "subcollection actions" do + before do + stub_request(:get, "#{vms_url}/#{@vm.id}/tags?hide=resources") + .to_return(:status => 200, :body => get_test1_tags_response, :headers => {}) + + @tag_actions = JSON.parse(get_test1_tags_response)["actions"] + .select { |aspec| aspec["method"] == "post" } + .collect { |aspec| aspec["name"] } + end + + it "are exposed by subcollection" do + @tag_actions.each do |action| + expect(@vm.tags.respond_to?(action.to_sym)).to be_truthy + end + end + + it "post the correct request to the resource subcollection" do + @tag_actions.each do |action| + stub_request(:post, "#{vms_url}/#{@vm.id}/tags") + .with(:body => {"action" => action}, :headers => {'Content-Type' => 'application/json'}) + .to_return(:status => 200, :body => actions_vm_tags_response, :headers => {}) + + @vm.tags.public_send(action.to_sym) + end + end + + it "post single resource to the subcollection" do + @tag_actions.each do |action| + stub_request(:post, "#{vms_url}/#{@vm.id}/tags") + .with(:body => {"action" => action, "resource" => { "id" => 11 }}, + :headers => {'Content-Type' => 'application/json'}) + .to_return(:status => 200, :body => actions_vm_tags_response, :headers => {}) + + @vm.tags.public_send(action.to_sym, :id => 11) + end + end + + it "post multiple resources to the subcollection" do + @tag_actions.each do |action| + stub_request(:post, "#{vms_url}/#{@vm.id}/tags") + .with(:body => {"action" => action, "resources" => [{ "id" => 11 }, { "id" => 12 }]}, + :headers => {'Content-Type' => 'application/json'}) + .to_return(:status => 200, :body => actions_vm_tags_response, :headers => {}) + + @vm.tags.public_send(action.to_sym, [{:id => 11}, {:id => 12}]) + end + end + end + + describe "get" do + before do + stub_request(:get, "#{vms_url}/#{@vm.id}/tags?hide=resources") + .to_return(:status => 200, :body => get_test1_tags_response, :headers => {}) + + stub_request(:get, "#{vms_url}/#{@vm.id}/tags?expand=resources") + .to_return(:status => 200, :body => get_test1_tags_response, :headers => {}) + end + + it "returns array of subresources" do + @vm.tags.collect do |resource| + expect(resource).to be_a(ManageIQ::API::Client::Subresource::Tag) + end + end + + it "returns valid set of subresources" do + vm_tags = JSON.parse(get_test1_tags_response)["resources"] + expect(@vm.tags.collect(&:name)).to match_array(vm_tags.collect { |tag| tag["name"] }) + end + end + + describe "queryable" do + let(:vm_tags_url) { "#{vms_url}/#{@vm.id}/tags" } + + before do + stub_request(:get, "#{vm_tags_url}?hide=resources") + .to_return(:status => 200, :body => get_test1_tags_response, :headers => {}) + + stub_request(:get, "#{vm_tags_url}?expand=resources") + .to_return(:status => 200, :body => get_test1_tags_response, :headers => {}) + + @vm_tags = JSON.parse(get_test1_tags_response)["resources"] + end + + it "supports select" do + stub_request(:get, "#{vm_tags_url}?expand=resources&attributes=name") + .to_return(:status => 200, :body => get_test1_tags_response, :headers => {}) + + expect(@vm.tags.select(:name).collect(&:name)).to match_array(@vm_tags.collect { |tag| tag["name"] }) + end + + it "supports offset" do + stub_request(:get, "#{vm_tags_url}?expand=resources&offset=100") + .to_return(:status => 200, :body => get_test1_no_tags_response, :headers => {}) + + @vm.tags.offset(100).collect(&:name) + end + + it "supports offset and limit" do + stub_request(:get, "#{vm_tags_url}?expand=resources&offset=100&limit=50") + .to_return(:status => 200, :body => get_test1_no_tags_response, :headers => {}) + + @vm.tags.offset(100).limit(50).collect(&:name) + end + + it "supports where" do + stub_request(:get, "#{vm_tags_url}?expand=resources&filter[]=name='/managed/location/hawaii'") + .to_return(:status => 200, :body => get_test1_no_tags_response, :headers => {}) + + @vm.tags.where(:name => "/managed/location/hawaii").collect(&:name) + end + + it "supports chainable where filters" do + stub_request(:get, "#{vm_tags_url}?expand=resources"\ + "&filter[]=id=200"\ + "&filter[]=name='/managed/cost_center/test'") + .to_return(:status => 200, :body => get_test1_no_tags_response, :headers => {}) + + @vm.tags.where(:id => 200).where(:name => '/managed/cost_center/test').collect(&:name) + end + + it "supports first" do + stub_request(:get, "#{vm_tags_url}?expand=resources&limit=1") + .to_return(:status => 200, :body => get_test1_tags_response, :headers => {}) + + expect(@vm.tags.first.name).to eq(@vm_tags.first["name"]) + end + + it "supports last" do + stub_request(:get, "#{vm_tags_url}?expand=resources") + .to_return(:status => 200, :body => get_test1_tags_response, :headers => {}) + + expect(@vm.tags.last.name).to eq(@vm_tags.last["name"]) + end + + it "supports pluck" do + stub_request(:get, "#{vm_tags_url}?expand=resources&attributes=name") + .to_return(:status => 200, :body => get_test1_tags_response, :headers => {}) + + expect(@vm.tags.pluck(:name)).to match_array(@vm_tags.collect { |tag| tag["name"] }) + end + + it "supports find" do + tag = @vm_tags.first + + stub_request(:get, "#{vm_tags_url}?expand=resources&filter[]=id=#{tag['id']}&limit=1") + .to_return(:status => 200, :body => get_test1_tags_response, :headers => {}) + + expect(@vm.tags.find(tag['id']).name).to eq(tag['name']) + end + + it "supports find_by" do + stub_request(:get, "#{vm_tags_url}?expand=resources&filter[]=name='/managed/location/bogus'&limit=1") + .to_return(:status => 200, :body => get_test1_no_tags_response, :headers => {}) + + @vm.tags.find_by(:name => "/managed/location/bogus") + end + + it "supports order" do + stub_request(:get, "#{vm_tags_url}?expand=resources&sort_by=name&sort_order=asc") + .to_return(:status => 200, :body => get_test1_no_tags_response, :headers => {}) + + @vm.tags.order(:name).collect(&:name) + end + + it "supports descending order" do + stub_request(:get, "#{vm_tags_url}?expand=resources&sort_by=name&sort_order=desc") + .to_return(:status => 200, :body => get_test1_no_tags_response, :headers => {}) + + @vm.tags.order(:name => "descending").collect(&:name) + end + + it "supports multiple order" do + stub_request(:get, "#{vm_tags_url}?expand=resources&sort_by=category,name&sort_order=asc,asc") + .to_return(:status => 200, :body => get_test1_no_tags_response, :headers => {}) + + @vm.tags.order(:category, :name).collect(&:name) + end + + it "supports multiple order with mixed ascending and descending" do + stub_request(:get, "#{vm_tags_url}?expand=resources&sort_by=category,name&sort_order=desc,asc") + .to_return(:status => 200, :body => get_test1_no_tags_response, :headers => {}) + + @vm.tags.order(:category => "descending", :name => "ascending").collect(&:name) + end + + it "supports chainable order with mixed ascending and descending" do + stub_request(:get, "#{vm_tags_url}?expand=resources&sort_by=category,name&sort_order=desc,asc") + .to_return(:status => 200, :body => get_test1_no_tags_response, :headers => {}) + + @vm.tags.order(:category => "DESC").order(:name => "ASC").collect(&:name) + end + + it "supports compound chaining" do + stub_request(:get, "#{vm_tags_url}?expand=resources"\ + "&attributes=category,name"\ + "&sort_by=category,name&sort_order=desc,asc"\ + "&filter[]=name='/managed/*'&filter[]=category='location'"\ + "&offset=100&limit=25") + .to_return(:status => 200, :body => get_test1_no_tags_response, :headers => {}) + + @vm.tags + .select(:category, :name) + .order(:category => "descending").order(:name) + .where(:name => "/managed/*").where(:category => "location") + .offset(100).limit(25) + .collect(&:name) + end + end +end diff --git a/spec/manageiq/api/subresource_spec.rb b/spec/manageiq/api/subresource_spec.rb new file mode 100644 index 0000000..91f3010 --- /dev/null +++ b/spec/manageiq/api/subresource_spec.rb @@ -0,0 +1,97 @@ +describe ManageIQ::API::Client::Subresource do + let(:api_url) { "http://localhost:3000/api" } + let(:vms_url) { "#{api_url}/vms" } + let(:vms_expand_url) { "#{vms_url}?expand=resources" } + let(:entrypoint_request_url) { "#{api_url}?attributes=authorization" } + + let(:entrypoint_response) { api_file_fixture("responses/entrypoint.json") } + let(:options_vms_response) { api_file_fixture("responses/options_vms.json") } + let(:get_test1_vms_response) { api_file_fixture("responses/get_test1_vms.json") } + let(:get_test1_tags_response) { api_file_fixture("responses/get_test1_vm_tags.json") } + let(:actions_tags_response) { api_file_fixture("responses/actions_vm_tags.json") } + + before do + stub_request(:get, entrypoint_request_url) + .to_return(:status => 200, :body => entrypoint_response, :headers => {}) + + stub_request(:get, "#{vms_expand_url}&filter[]=name='aab-test1'&limit=1") + .to_return(:status => 200, :body => get_test1_vms_response, :headers => {}) + + stub_request(:options, vms_url) + .to_return(:status => 200, :body => options_vms_response, :headers => {}) + + miq = ManageIQ::API::Client.new + @vm = miq.vms.where(:name => "aab-test1").first + @vm_options = JSON.parse(options_vms_response) + @vm_hash = JSON.parse(get_test1_vms_response)["resources"].first + + stub_request(:get, "#{vms_url}/#{@vm.id}/tags?hide=resources") + .to_return(:status => 200, :body => get_test1_tags_response, :headers => {}) + + stub_request(:get, "#{vms_url}/#{@vm.id}/tags?expand=resources&limit=1") + .to_return(:status => 200, :body => get_test1_tags_response, :headers => {}) + + @vm_tag = @vm.tags.first + @vm_tag1 = JSON.parse(get_test1_tags_response)["resources"].first + end + + describe "subresource" do + it "is of valid type" do + expect(@vm_tag).to be_a(ManageIQ::API::Client::Subresource::Tag) + end + + it "exposes attributes hash" do + expect(@vm_tag.attributes).to match(a_hash_including(@vm_tag1.except("actions"))) + end + + it "exposes action objects" do + expect(@vm_tag.actions.first).to be_a(ManageIQ::API::Client::Action) + end + + it "exposes related subcollection" do + expect(@vm_tag.subcollection).to be_a(ManageIQ::API::Client::Subcollection::Tags) + end + + it "exposes attributes" do + expect(@vm_tag.id).to eq(@vm_tag1["id"]) + expect(@vm_tag.name).to eq(@vm_tag1["name"]) + expect(@vm_tag.href).to eq(@vm_tag1["href"]) + end + + it "exposes attributes via []" do + expect(@vm_tag["id"]).to eq(@vm_tag1["id"]) + expect(@vm_tag["name"]).to eq(@vm_tag1["name"]) + expect(@vm_tag["href"]).to eq(@vm_tag1["href"]) + end + + it "responds to actions" do + @vm_tag1["actions"].each do |aspec| + if aspec["method"] == "post" + expect(@vm_tag.respond_to?(aspec["name"].to_sym)).to be_truthy + end + end + end + + it "supports invoking actions" do + action = @vm_tag1["actions"].detect { |aspec| aspec["method"] == "post" } + + stub_request(:post, @vm_tag["href"]) + .with(:body => {"action" => action["name"]}, :headers => {'Content-Type' => 'application/json'}) + .to_return(:status => 200, :body => actions_tags_response, :headers => {}) + + @vm_tag.public_send(action["name"]) + end + + it "supports invoking actions with parameters" do + action = @vm_tag1["actions"].detect { |aspec| aspec["method"] == "post" } + + stub_request(:post, @vm_tag["href"]) + .with(:body => {"action" => action["name"], + "resource" => { "parameter" => "value" }}, + :headers => {'Content-Type' => 'application/json'}) + .to_return(:status => 200, :body => actions_tags_response, :headers => {}) + + @vm_tag.public_send(action["name"], :parameter => "value") + end + end +end