From 982ebcbdb381ff93a0d0e1c628898060a1ac6d8d Mon Sep 17 00:00:00 2001 From: Scott Thompson Date: Fri, 20 Jun 2014 16:25:36 -0500 Subject: [PATCH 01/32] Added a method to reboot servers --- lib/softlayer/Server.rb | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/lib/softlayer/Server.rb b/lib/softlayer/Server.rb index cb1737b..6493d3a 100644 --- a/lib/softlayer/Server.rb +++ b/lib/softlayer/Server.rb @@ -82,6 +82,25 @@ def initialize(softlayer_client, network_hash) super end end + + ## + # Reboot the server. This action is taken immediately. + # Servers can be rebooted in three different ways: + # :default_reboot - (Try soft, then hard) Attempts to reboot a server using the :os_reboot technique then, if that is not successful, tries the :power_cycle method + # :os_reboot - (aka. soft rebot) instructs the server's host operating system to reboot + # :power_cycle - (aka. hard reboot) The actual (for hardware) or metaphorical (for virtual servers) equivalent to pulling the plug on the server then plugging it back in. + def reboot!(reboot_technique = :default_reboot) + case reboot_technique + when :default_reboot + self.service.rebootDefault + when :os_reboot + self.service.rebootSoft + when :power_cycle + self.service.rebootHard + else + raise RuntimeError, "Unrecognized reboot technique in SoftLayer::Server#reboot!}" + end + end ## # Make an API request to SoftLayer and return the latest properties hash From 452eff3b310e7e102189896d3b9acaafa85e448c Mon Sep 17 00:00:00 2001 From: Scott Thompson Date: Thu, 26 Jun 2014 15:37:59 -0500 Subject: [PATCH 02/32] Added the ImageTemplates file which revealed a problem with ObjectFilters that was not straightforward. Substantially changed the implementation of object filters to make them easier to create, and easy to modify properly --- lib/softlayer/APIParameterFilter.rb | 4 +- lib/softlayer/Account.rb | 12 + lib/softlayer/BareMetalServer.rb | 9 +- lib/softlayer/ImageTemplate.rb | 259 +++++++++++++++++++++ lib/softlayer/ObjectFilter.rb | 336 +++++++++++++++------------- lib/softlayer/ProductPackage.rb | 5 +- lib/softlayer/VirtualServer.rb | 15 +- lib/softlayer_api.rb | 1 + spec/APIParameterFilter_spec.rb | 2 +- spec/ObjectFilter_spec.rb | 245 +++++++++++++------- spec/Service_spec.rb | 9 +- 11 files changed, 636 insertions(+), 261 deletions(-) create mode 100644 lib/softlayer/ImageTemplate.rb diff --git a/lib/softlayer/APIParameterFilter.rb b/lib/softlayer/APIParameterFilter.rb index 631386f..df468db 100644 --- a/lib/softlayer/APIParameterFilter.rb +++ b/lib/softlayer/APIParameterFilter.rb @@ -119,7 +119,7 @@ def result_limit(offset, limit) # to specify criteria which are used to filter the results returned # by the server. def object_filter(filter) - raise ArgumentError, "Object mask expects mask properties" if filter.nil? + raise ArgumentError, "object_filter expects an instance of SoftLayer::ObjectFilter" if filter.nil? || !filter.kind_of?(SoftLayer::ObjectFilter) # we create a new object in case the user wants to store off the # filter chain and reuse it later @@ -187,7 +187,7 @@ def server_result_offset ## # A utility method that returns the object filter (if any) stored with this filter. def server_object_filter - self.parameters[:object_filter] + self.parameters[:object_filter].to_h end ## diff --git a/lib/softlayer/Account.rb b/lib/softlayer/Account.rb index 828cca2..27a399c 100644 --- a/lib/softlayer/Account.rb +++ b/lib/softlayer/Account.rb @@ -113,6 +113,18 @@ class Account < SoftLayer::ModelBase end end + sl_dynamic_attr :image_templates do |image_templates| + image_templates.should_update? do + @last_image_template_update ||= Time.at(0) + (Time.now - @last_image_template_update) > 5 * 60 # update every 5 minutes + end + + image_templates.to_update do + @last_image_template_update ||= Time.now + ImageTemplate.find_private_templates(:client => self.softlayer_client) + end + end + def service softlayer_client["Account"].object_with_id(self.id) end diff --git a/lib/softlayer/BareMetalServer.rb b/lib/softlayer/BareMetalServer.rb index ebe36e0..f6147f7 100644 --- a/lib/softlayer/BareMetalServer.rb +++ b/lib/softlayer/BareMetalServer.rb @@ -178,8 +178,9 @@ def self.find_servers(options_hash = {}) if(options_hash.has_key? :object_filter) object_filter = options_hash[:object_filter] + raise "Expected an instance of SoftLayer::ObjectFilter" unless object_filter.kind_of?(SoftLayer::ObjectFilter) else - object_filter = {} + object_filter = ObjectFilter.new() end option_to_filter_path = { @@ -197,18 +198,18 @@ def self.find_servers(options_hash = {}) # that particular option, add a clause to the object filter that filters for the matching # value option_to_filter_path.each do |option, filter_path| - object_filter.merge!(SoftLayer::ObjectFilter.build(filter_path, options_hash[option])) if options_hash.has_key?(option) + object_filter.modify { |filter| filter.accept(filter_path).when_it is(options_hash[option])} if options_hash[option] end # Tags get a much more complex object filter operation so we handle them separately if options_hash.has_key?(:tags) - object_filter.merge!(SoftLayer::ObjectFilter.build("hardware.tagReferences.tag.name", { + object_filter.set_criteria_for_key_path("hardware.tagReferences.tag.name", { 'operation' => 'in', 'options' => [{ 'name' => 'data', 'value' => options_hash[:tags] }] - } )); + } ); end account_service = softlayer_client['Account'] diff --git a/lib/softlayer/ImageTemplate.rb b/lib/softlayer/ImageTemplate.rb new file mode 100644 index 0000000..29d6653 --- /dev/null +++ b/lib/softlayer/ImageTemplate.rb @@ -0,0 +1,259 @@ +# +# Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +module SoftLayer + ## + # Represents a virtual server image template. + # rougly corresponds to SoftLayer_Virtual_Guest_Block_Device_Template_Group + class ImageTemplate < SoftLayer::ModelBase + ## + # :attr_reader: + # The 'friendly name' given to the template when it was created + sl_attr :name + + ## + # :attr_reader: + # The notes, if any, that are attached to the template. Can be nil. + sl_attr :notes, "note" + + ## + # :attr_reader: + # The universally unique identifier (if any) for the template. Can be nil. + sl_attr :global_id, 'globalIdentifier' + + # Change the name of the template + def rename!(new_name) + end + + ## + # true if the image template is a flex image + # Note that the publicFlag property comes back as an integer (0 or 1) + def public? + self["publicFlag"] != 0 + end + + ## + # true if the image template is a flex image + # Note that the flexImageFlag property comes back as a boolean + def flex_image? + !!self["flexImageFlag"] + end + + def notes= + end + + # Get and set an array of the tags on the image + def tags + end + + def tags= + end + + ## + # Works with an array of data center names (short names) + # where the image template is available + # + # Should this be datacenters and "add_datacenters, remove_datacenters" + def datacenters + end + + def datacenters= + end + + ## + # Wait until transactions related to the image group are finsihed + def wait_until_ready(max_trials, seconds_between_tries = 2) + end + + ## + # Works with an array of account IDs (or should use master user names i.e. "SL232279" ) + # that the image template is shared with + # + # Should this be datacenters an "share_with_accounts, stop_sharing_with_accounts" + def shared_with_accounts + end + + def shared_with_accounts= + end + + ## + # Retrieve a list of the private image templates from the account. + # + # The options parameter should contain: + # + # +:client+ - The client used to connect to the API + # + # If no client is given, then the routine will try to use Client.default_client + # If no client can be found the routine will raise an error. + def ImageTemplate.find_private_templates(options_hash = {}) + softlayer_client = options_hash[:client] || Client.default_client + raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client + + if(options_hash.has_key? :object_filter) + object_filter = options_hash[:object_filter] + raise "Expected an instance of SoftLayer::ObjectFilter" unless object_filter.kind_of?(SoftLayer::ObjectFilter) + else + object_filter = ObjectFilter.new() + end + + option_to_filter_path = { + :name => "privateBlockDeviceTemplateGroups.name", + :global_id => "privateBlockDeviceTemplateGroups.globalIdentifier", + } + + # For each of the options in the option_to_filter_path map, if the options hash includes + # that particular option, add a clause to the object filter that filters for the matching + # value + option_to_filter_path.each do |option, filter_path| + object_filter.modify { |filter| filter.accept(filter_path).when_it is(options_hash[option])} if options_hash[option] + end + + # Tags get a much more complex object filter operation so we handle them separately + if options_hash.has_key?(:tags) + object_filter.set_criteria_for_key_path("privateBlockDeviceTemplateGroups.tagReferences.tag.name", { + 'operation' => 'in', + 'options' => [{ + 'name' => 'data', + 'value' => options_hash[:tags] + }] + } ); + end + + account_service = softlayer_client['Account'] + account_service = account_service.object_filter(object_filter) unless object_filter.empty? + account_service = account_service.object_mask(default_object_mask) + + if options_hash.has_key? :object_mask + account_service = account_service.object_mask(options_hash[:object_mask]) + end + + if options_hash.has_key?(:result_limit) + offset = options[:result_limit][:offset] + limit = options[:result_limit][:limit] + + account_service = account_service.result_limit(offset, limit) + end + + templates_data = account_service.getPrivateBlockDeviceTemplateGroups + templates_data.collect { |template_data| new(softlayer_client, template_data) } + end + + ## + # Retrieve a list of public image templates + # + # The options parameter should contain: + # + # +:client+ - The client used to connect to the API + # + # If no client is given, then the routine will try to use Client.default_client + # If no client can be found the routine will raise an error. + def ImageTemplate.find_public_templates(options_hash = {}) + softlayer_client = options_hash[:client] || Client.default_client + raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client + softlayer_client = options_hash[:client] || Client.default_client + raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client + + if(options_hash.has_key? :object_filter) + object_filter = options_hash[:object_filter] + raise "Expected an instance of SoftLayer::ObjectFilter" unless object_filter.kind_of?(SoftLayer::ObjectFilter) + else + object_filter = ObjectFilter.new() + end + + option_to_filter_path = { + :name => "publicImages.name", + :global_id => "publicImages.globalIdentifier", + } + + # For each of the options in the option_to_filter_path map, if the options hash includes + # that particular option, add a clause to the object filter that filters for the matching + # value + option_to_filter_path.each do |option, filter_path| + object_filter.modify { |filter| filter.accept(filter_path).when_it is(options_hash[option])} if options_hash[option] + end + + # Tags get a much more complex object filter operation so we handle them separately + if options_hash.has_key?(:tags) + object_filter.set_criteria_for_key_path("publicImages.tagReferences.tag.name", { + 'operation' => 'in', + 'options' => [{ + 'name' => 'data', + 'value' => options_hash[:tags] + }] + } ); + end + + template_service = softlayer_client['Virtual_Guest_Block_Device_Template_Group'] + template_service = template_service.object_filter(object_filter) unless object_filter.empty? + template_service = template_service.object_mask(default_object_mask) + + if options_hash.has_key? :object_mask + template_service = template_service.object_mask(options_hash[:object_mask]) + end + + if options_hash.has_key?(:result_limit) + offset = options[:result_limit][:offset] + limit = options[:result_limit][:limit] + + template_service = template_service.result_limit(offset, limit) + end + + templates_data = template_service.getPublicImages + templates_data.collect { |template_data| new(softlayer_client, template_data) } + end + + ## + # Retrive the Image Template with the given ID + # (Note! This is the service ID, not the globalIdentifier. + # To find a template by global identifier, use find_templates) + # + # The options parameter should contain: + # + # +:client+ - The client used to connect to the API + # + # If no client is given, then the routine will try to use Client.default_client + # If no client can be found the routine will raise an error. + # + # The options may include the following keys + # * +:object_mask+ (string) - A object mask of properties, in addition to the default properties, that you wish to retrieve for the server + def ImageTemplate.template_with_id(id, options_hash = {}) + softlayer_client = options_hash[:client] || Client.default_client + raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client + + service = softlayer_client['Virtual_Guest_Block_Device_Template_Group'].object_with_id(id) + service.object_mask(default_object_mask) + + if options_hash.has_key? :object_mask + service = service.object_mask(options_hash[:object_mask]) + end + + template_data = service.getObject + new(softlayer_client, template_data) + end + + protected + + def ImageTemplate.default_object_mask + return "mask[id,name,note,globalIdentifier,datacenters,blockDevices,tagReferences,publicFlag,flexImageFlag]" + end + end +end \ No newline at end of file diff --git a/lib/softlayer/ObjectFilter.rb b/lib/softlayer/ObjectFilter.rb index bb22088..c452b48 100644 --- a/lib/softlayer/ObjectFilter.rb +++ b/lib/softlayer/ObjectFilter.rb @@ -21,6 +21,96 @@ # module SoftLayer + ## + # An ObjectFilter is a tool that, when passed to the SoftLayer API + # allows the API server to filter, or limit the result set for a call. + # + # Constructing ObjectFilters is an art that is currently somewhat + # arcane. This class tries to simplify filtering for the fundamental + # cases, while still allowing for more complex ObjectFilters to be + # created. + # + # To construct an object filter you begin with an instance of the + # class. At construction time, or in a "modify" call you can change + # the filter criteria using a fancy DSL syntax. + # + # For example, to filter virtual servers so that you only get ones + # whose domains end with "layer.com" you might use: + # + # object_filter = ObjectFilter.new do |filter| + # filter.accept(virtualGuests.domain).when_it ends_with("layer.com") + # end + # + # The set of criteria that can be included after "when_it" are defined + # by routines in the ObjectFilterDefinitionContext module. + class ObjectFilter + def initialize(&construction_block) + @filter_hash = {} + self.modify(&construction_block) + self + end + + def empty? + @filter_hash.empty? + end + + def modify(&construction_block) + ObjectFilterDefinitionContext.module_exec(self, &construction_block) if construction_block + end + + def accept(key_path) + CriteriaAcceptor.new(self, key_path) + end + + def to_h + return @filter_hash.dup + end + + def criteria_for_key_path(key_path) + raise "The key path cannot be empty when searching for criteria" if key_path.nil? || key_path.empty? + + current_level = @filter_hash + keys = key_path.split('.') + + while current_level && keys.count > 1 + current_level = current_level[keys.shift] + end + + if current_level + current_level[keys[0]] + else + nil + end + end + + def set_criteria_for_key_path(key_path, criteria) + current_level = @filter_hash + keys = key_path.split('.') + + current_key = keys.shift + while current_level && !keys.empty? + if !current_level.has_key? current_key + current_level[current_key] = {} + end + current_level = current_level[current_key] + current_key = keys.shift + end + + current_level[current_key] = criteria + end + + class CriteriaAcceptor + def initialize(filter, key_path) + @filter = filter + @key_path = key_path + end + + def when_it(criteria) + @filter.set_criteria_for_key_path(@key_path, criteria) + end + end + end # ObjectFilter + OBJECT_FILTER_OPERATORS = [ '*=', # Contains (ignoring case) '^=', # Begins with (ignoring case) @@ -35,192 +125,109 @@ module SoftLayer '!~' # Does not Contain (case sensitive) ] - # A class whose instances represent an Object Filter operator and the value it is applied to. - class ObjectFilterOperation - - # The operator, should be a member of the SoftLayer::OBJECT_FILTER_OPERATORS array - attr_reader :operator - - # The operand of the operator - attr_reader :value - - def initialize(operator, value) - raise ArgumentException, "An unknown operator was given" if !OBJECT_FILTER_OPERATORS.include?(operator.strip) - raise ArgumentException, "Expected a value" if value.nil? || (value.respond_to?(:empty?) && value.empty?) - - @operator = operator.strip - @value = value.strip + ## + # The ObjectFilterDefinitionContext defines a bunch of methods + # that allow the property conditions of an object filter to + # be defined in a "pretty" way. Each method returns a block + # (a lambda, a proc) that, when called and pased the tail property + # of a property chain will generate a fragment of an object filter + # asking that that property match the given conditions. + # + # This class, as a whole, is largely an implementation detail + # of object filter definitions and there is probably not + # a good reason to call into it directly. + module ObjectFilterDefinitionContext + # Matches when the value in the field is exactly equal to the + # given value. This is a case-sensitive match + def self.is(value) + { 'operation' => value } end - def to_h - result = ObjectFilter.new - result['operation'] = "#{operator} #{value}" - - result - end + # Matches is the value in the field does not exactly equal + # the value passed in. + def self.is_not(value) + filter_criteria('!=', value) end - # This class defines the routines that are valid within the block provided to a call to - # ObjectFilter.build. This allows you to create object filters like: - # - # object_filter = SoftLayer::ObjectFilter.build("hardware.memory") { is_greater_than(2) } - # - class ObjectFilterBlockHandler # Matches when the value is found within the field # the search is not case sensitive - def contains(value) - ObjectFilterOperation.new('*=', value) + def self.contains(value) + filter_criteria('*=', value) end # Matches when the value is found at the beginning of the # field. This search is not case sensitive - def begins_with(value) - ObjectFilterOperation.new('^=', value) + def self.begins_with(value) + filter_criteria('^=', value) end # Matches when the value is found at the end of the # field. This search is not case sensitive - def ends_with(value) - ObjectFilterOperation.new('$=', value) - end - - # Matches when the value in the field is exactly equal to the - # given value. This is a case-sensitive match - def is(value) - ObjectFilterOperation.new('_=', value) + def self.ends_with(value) + filter_criteria('$=', value) end - # Matches is the value in the field does not exactly equal - # the value passed in. - def is_not(value) - ObjectFilterOperation.new('!=', value) + # Maches the given value in a case-insensitive way + def self.matches_ignoring_case(value) + filter_criteria('_=', value) end # Matches when the value in the field is greater than the given value - def is_greater_than(value) - ObjectFilterOperation.new('>', value) + def self.is_greater_than(value) + filter_criteria('>', value) end # Matches when the value in the field is less than the given value - def is_less_than(value) - ObjectFilterOperation.new('<', value) + def self.is_less_than(value) + filter_criteria('<', value) end # Matches when the value in the field is greater than or equal to the given value - def is_greater_or_equal_to(value) - ObjectFilterOperation.new('>=', value) + def self.is_greater_or_equal_to(value) + filter_criteria('>=', value) end # Matches when the value in the field is less than or equal to the given value - def is_less_or_equal_to(value) - ObjectFilterOperation.new('<=', value) + def self.is_less_or_equal_to(value) + filter_criteria('<=', value) end # Matches when the value is found within the field # the search _is_ case sensitive - def contains_exactly(value) - ObjectFilterOperation.new('~', value) + def self.contains_exactly(value) + filter_criteria('~', value) end # Matches when the value is not found within the field # the search _is_ case sensitive - def does_not_contain(value) - ObjectFilterOperation.new('!~', value) - end - end - - # - # An ObjectFilter is a tool that, when passed to the SoftLayer API - # allows the API server to filter, or limit the result set for a call. - # - # Constructing ObjectFilters is an art that is currently somewhat - # arcane. This class tries to simplify filtering for the fundamental - # cases, while still allowing for more complex ObjectFilters to be - # created. - # - # The ObjectFilter class is implemented as a hash that, when asked to provide - # an value for an unknown key, will create a sub element - # at that key which is, itself, an object filter. This allows you to build - # up object filters by chaining [] dereference operations. - # - # Starting empty object filter when you ask for +object_filter["foo"]+ - # either the value at that hash location will be returned, or a new +foo+ key - # will be *added* to the object. The value of that key will be an +ObjectFilter+ - # and that +ObjectFilter+ will be returned. - # - # By way of an example of chaining together +[]+ calls: - # object_filter["foo"]["bar"]["baz"] = 3 - # yields an object filter like this: - # {"foo" => { "bar" => {"baz" => 3}}} - # - class ObjectFilter < Hash - # The default initialize for a hash is overridden - # so that object filters create sub-filters when asked - # for missing keys. - def initialize - super do |hash, key| - hash[key] = ObjectFilter.new - end - end - - # Builds an object filter with the given key path, a dot separated list of property keys. - # The filter itself can be provided as a query string (in the query parameter) - # or by providing a block that calls routines in the ObjectFilterBlockHandler class. - def self.build(key_path, query = nil, &block) - raise ArgumentError, "The key path to build cannot be empty" if !key_path - - # Split the keypath into its constituent parts and notify the user - # if there are no parts - keys = key_path.split('.') - raise ArgumentError, "The key path to build cannot be empty" if keys.empty? - - # This will be the result of the build - result = ObjectFilter.new - - # chase down the key path to the last-but-one key - current_level = result - while keys.count > 1 - current_level = current_level[keys.shift] + def self.does_not_contain(value) + filter_criteria('!~', value) end - # if there is a block, then the query will come from - # calling the block. We warn in debug mode if you override a - # query that was passed directly with the value from a block. - if block - $stderr.puts "The query from the block passed to ObjectFilter:build will override the query passed as a parameter" if $DEBUG && query - block_handler = ObjectFilterBlockHandler.new - query = block_handler.instance_eval(&block) + # Matches when the property's value is null + def self.is_null + { 'operation' => 'is null' } end - # If we have a query, we assign its value to the last key - # otherwise, we build an emtpy filter at the bottom - if query - case - when query.kind_of?(Numeric) - current_level[keys.shift] = { 'operation' => query } - when query.kind_of?(SoftLayer::ObjectFilterOperation) - current_level[keys.shift] = query.to_h - when query.kind_of?(String) - current_level[keys.shift] = query_to_filter_operation(query) - when query.kind_of?(Hash) - current_level[keys.shift] = query - else - current_level[keys.shift] - end - else - current_level[keys.shift] + # Matches when the property's value is not null + def self.is_not_null() + { 'operation' => 'not null' } end - result + # This is a catch-all criteria matcher that allows for raw object filter conditions + # not covered by the more convenient methods above. The name is intentionally, annoyingly + # long and you should use this routine with solid knowledge and great care. + def self.satisfies_the_raw_condition(condition_hash) + condition_hash end - # This method simplifies creating correct object filter structures - # by defining a simple query language. It translates strings in that - # language into an Object Filter operations + # Accepts a query string defined by a simple query language. + # It translates strings in that language into criteria blocks # - # Object Filter comparisons are done using operators. Some operators make - # case sensitive comparisons and some do not. The general form of an Object - # Filter operation is an operator follwed by the value used in the comparison. + # Object Filter comparisons can be done using operators. The + # set of accepted operators is found in the OBJECT_FILTER_OPERATORS + # array. The query string can consist of an operator followed + # by a space, followed by operand # e.g. # "*= smaug" # @@ -234,39 +241,48 @@ def self.build(key_path, query = nil, &block) # # This method corresponds to the +query_filter+ method in the SoftLayer-Python # API. - def self.query_to_filter_operation(query) - if query.kind_of? String then - query.strip! - - begin - return { 'operation' => Integer(query) } - rescue - end + def self.matches_query(query_string) + query = query_string.to_s.strip operator = OBJECT_FILTER_OPERATORS.find do | operator_string | query[0 ... operator_string.length] == operator_string end if operator then - operation = "#{operator} #{query[operator.length..-1].strip}" + filter_criteria(operator, query[operator.length..-1]) else case query when /\A\*(.*)\*\Z/ - operation = "*= #{$1}" + contains($1) when /\A\*(.*)/ - operation = "$= #{$1}" + ends_with($1) when /\A(.*)\*\Z/ - operation = "^= #{$1}" + begins_with($1) else - operation = "_= #{query}" + matches_ignoring_case(query) end #case end #if - else - operation = query.to_i - end # query is string + end - { 'operation' => operation } - end # query_to_filter_operation + private - end # ObjectFilter + def self.cleaned_up_operand(operand) + # try to convert the operand to an integer. If it works, return + # that integer + begin + return Integer(operand) + rescue + end + + # The operand could not be converted to an integer so we try to make it a string + # and clean up the string + filter_operand = operand.to_s.strip + end + + def self.filter_criteria(with_operator, operand) + filter_operand = cleaned_up_operand(operand) + filter_condition = "#{with_operator.to_s.strip} #{operand.to_s.strip}" + { 'operation' => filter_condition } + end + end end # SoftLayer diff --git a/lib/softlayer/ProductPackage.rb b/lib/softlayer/ProductPackage.rb index 1cca71c..bd6bd9b 100644 --- a/lib/softlayer/ProductPackage.rb +++ b/lib/softlayer/ProductPackage.rb @@ -142,7 +142,10 @@ def self.packages_with_key_name(key_name, client = nil) softlayer_client = client || Client.default_client raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client - filter = SoftLayer::ObjectFilter.build('type.keyName', key_name) + filter = SoftLayer::ObjectFilter.new do |filter| + filter.accept('type.keyName').when_it is(key_name) + end + filtered_service = softlayer_client['Product_Package'].object_filter(filter).object_mask(self.default_object_mask('mask')) packages_data = filtered_service.getAllObjects packages_data.collect { |package_data| ProductPackage.new(softlayer_client, package_data) } diff --git a/lib/softlayer/VirtualServer.rb b/lib/softlayer/VirtualServer.rb index 54001a3..a699534 100644 --- a/lib/softlayer/VirtualServer.rb +++ b/lib/softlayer/VirtualServer.rb @@ -258,10 +258,15 @@ def self.find_servers(options_hash = {}) softlayer_client = options_hash[:client] || Client.default_client raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client - object_filter = {} + if(options_hash.has_key? :object_filter) + object_filter = options_hash[:object_filter] + raise "Expected an instance of SoftLayer::ObjectFilter" unless object_filter.kind_of?(SoftLayer::ObjectFilter) + else + object_filter = ObjectFilter.new() + end option_to_filter_path = { - :cpus => "virtualGuests.maxCpu", + :cores => "virtualGuests.maxCpu", :memory => "virtualGuests.maxMemory", :hostname => "virtualGuests.hostname", :domain => "virtualGuests.domain", @@ -280,18 +285,18 @@ def self.find_servers(options_hash = {}) # that particular option, add a clause to the object filter that filters for the matching # value option_to_filter_path.each do |option, filter_path| - object_filter.merge!(SoftLayer::ObjectFilter.build(filter_path, options_hash[option])) if options_hash.has_key?(option) + object_filter.modify { |filter| filter.accept(filter_path).when_it is(options_hash[option])} if options_hash[option] end # Tags get a much more complex object filter operation so we handle them separately if options_hash.has_key?(:tags) - object_filter.merge!(SoftLayer::ObjectFilter.build("virtualGuests.tagReferences.tag.name", { + object_filter.set_criteria_for_key_path("virtualGuests.tagReferences.tag.name", { 'operation' => 'in', 'options' => [{ 'name' => 'data', 'value' => options_hash[:tags] }] - } )); + } ); end required_properties_mask = 'mask.id' diff --git a/lib/softlayer_api.rb b/lib/softlayer_api.rb index 70b20ca..f4ca558 100755 --- a/lib/softlayer_api.rb +++ b/lib/softlayer_api.rb @@ -39,6 +39,7 @@ require 'softlayer/BareMetalServer' require 'softlayer/BareMetalServerOrder' require 'softlayer/BareMetalServerOrder_Package' +require 'softlayer/ImageTemplate' require 'softlayer/ProductPackage' require 'softlayer/ProductItemCategory' require 'softlayer/VirtualServer' diff --git a/spec/APIParameterFilter_spec.rb b/spec/APIParameterFilter_spec.rb index 5cc1b37..0e1bb1a 100644 --- a/spec/APIParameterFilter_spec.rb +++ b/spec/APIParameterFilter_spec.rb @@ -94,7 +94,7 @@ it "stores its value in server_object_filter when called" do test_filter = SoftLayer::ObjectFilter.new() - test_filter["fish"] = "cow" + test_filter.set_criteria_for_key_path("fish", "cow") result = filter.object_filter(test_filter) expect(result.server_object_filter).to eq({"fish" => "cow"}) diff --git a/spec/ObjectFilter_spec.rb b/spec/ObjectFilter_spec.rb index bf92b8b..72113a1 100644 --- a/spec/ObjectFilter_spec.rb +++ b/spec/ObjectFilter_spec.rb @@ -27,115 +27,192 @@ require 'rspec' describe SoftLayer::ObjectFilter do - it "is empty hash when created" do - test_filter = SoftLayer::ObjectFilter.new() - expect(test_filter).to eq({}) - end - - it "adds empty object filter sub-elements for unknown keys" do - test_filter = SoftLayer::ObjectFilter.new() - value = test_filter["foo"] + it "calls its construction block" do + block_called = false; + filter = SoftLayer::ObjectFilter.new() { + block_called = true; + } - expect(value).to_not be_nil - expect(value).to eq({}) - expect(value).to be_kind_of(SoftLayer::ObjectFilter) + expect(block_called).to be(true) end + + it "expects the methods in the ObjectFilterDefinitionContext to be available in its block" do + stuff_defined = false; + filter = SoftLayer::ObjectFilter.new() { + stuff_defined = !!defined?(satisfies_the_raw_condition); + } - describe ":build" do - it "builds object filters from a key path and query string" do - object_filter = SoftLayer::ObjectFilter.build("hardware.domain", '*riak*'); - expect(object_filter).to eq({ - "hardware" => { - "domain" => { - 'operation' => '*= riak' - }}}) + expect(stuff_defined).to be(true) + end + + it "is empty when no criteria have been added" do + filter = SoftLayer::ObjectFilter.new() + expect(filter.empty?).to be(true) + end + + it "is not empty criteria have been added" do + filter = SoftLayer::ObjectFilter.new do |filter| + filter.accept("foobar").when_it is("baz") end + + expect(filter.empty?).to be(false) + end + + it "returns criteria for a given key path" do + test_hash = { 'one' => { 'two' => {'three' => 3}}} - it "builds object filters from a key path and a hash" do - object_filter = SoftLayer::ObjectFilter.build("hardware.domain", {'bogus' => 'but fun'}); - expect(object_filter).to eq({ - "hardware" => { - "domain" => { - 'bogus' => 'but fun' - }}}) + filter = SoftLayer::ObjectFilter.new() + filter.instance_eval do + @filter_hash = test_hash end + + expect(filter.criteria_for_key_path("one")).to eq({'two' => {'three' => 3}}) + expect(filter.criteria_for_key_path("one.two")).to eq({'three' => 3}) + expect(filter.criteria_for_key_path("one.two.three")).to eq(3) + end + + it "returns nil when asked for criteria that don't exist" do + filter = SoftLayer::ObjectFilter.new() + filter.set_criteria_for_key_path("some.key.path", 3) + + expect(filter.criteria_for_key_path("some.key.path")).to eq(3) + expect(filter.criteria_for_key_path("does.not.exist")).to be_nil + + expect(filter.to_h).to eq({ 'some' => { 'key' => {'path' => 3}}}) + end + + it "changes criteria for a given key path" do + filter = SoftLayer::ObjectFilter.new() + filter.set_criteria_for_key_path("one.two.three", 3) - it "builds object filters from a key path and am ObjectFilterOperation" do - filter_operation = SoftLayer::ObjectFilterOperation.new('~', 'wwdc') - object_filter = SoftLayer::ObjectFilter.build("hardware.domain", filter_operation); - expect(object_filter).to eq ({ - "hardware" => { - "domain" => { - 'operation' => '~ wwdc' - }}}) + expect(filter.criteria_for_key_path("one")).to eq({'two' => {'three' => 3}}) + expect(filter.criteria_for_key_path("one.two")).to eq({'three' => 3}) + expect(filter.criteria_for_key_path("one.two.three")).to eq(3) + + filter.set_criteria_for_key_path("one.two.also_two", 2) + expect(filter.criteria_for_key_path("one.two")).to eq({'also_two' => 2, 'three' => 3}) + expect(filter.criteria_for_key_path("one.two.also_two")).to eq(2) + + expect(filter.to_h).to eq({"one"=>{"two"=>{"three"=>3, "also_two"=>2}}}) + end + + it "sets criteria in the initializer with the fancy syntax" do + filter = SoftLayer::ObjectFilter.new do |filter| + filter.accept("some.key.path").when_it is(3) end + + expect(filter.criteria_for_key_path("some.key.path")).to eq({'operation' => 3}) + expect(filter.to_h).to eq({"some"=>{"key"=>{"path"=>{"operation"=>3}}}}) + end + + it "allows the fancy syntax in a modify block" do + filter = SoftLayer::ObjectFilter.new() - it "builds object filters from a key path and block" do - object_filter = SoftLayer::ObjectFilter.build("hardware.domain") { contains 'riak' }; - expect(object_filter).to eq ({ - "hardware" => { - "domain" => { - 'operation' => '*= riak' - } - } - }) + expect(filter.criteria_for_key_path("some.key.path")).to be_nil + + filter.modify do |filter| + filter.accept("some.key.path").when_it is(3) end + + expect(filter.criteria_for_key_path("some.key.path")).to eq({'operation' => 3}) + + # can replace a criterion + filter.modify do |filter| + filter.accept("some.key.path").when_it is(4) end - describe ":query_to_filter_operation" do - it "translates sample strings into valid operation structures" do - expect(SoftLayer::ObjectFilter.query_to_filter_operation('3')).to eq({'operation' => 3 }) - expect(SoftLayer::ObjectFilter.query_to_filter_operation('value')).to eq({'operation' => "_= value" }) - expect(SoftLayer::ObjectFilter.query_to_filter_operation('value*')).to eq({'operation' => "^= value" }) - expect(SoftLayer::ObjectFilter.query_to_filter_operation('*value')).to eq({'operation' => "$= value" }) - expect(SoftLayer::ObjectFilter.query_to_filter_operation('*value*')).to eq({'operation' => "*= value" }) - expect(SoftLayer::ObjectFilter.query_to_filter_operation('~ value')).to eq({'operation' => "~ value" }) - expect(SoftLayer::ObjectFilter.query_to_filter_operation('> value')).to eq({'operation' => "> value" }) - expect(SoftLayer::ObjectFilter.query_to_filter_operation('< value')).to eq({'operation' => "< value" }) - expect(SoftLayer::ObjectFilter.query_to_filter_operation('>= value')).to eq({'operation' => ">= value" }) - expect(SoftLayer::ObjectFilter.query_to_filter_operation('<= value')).to eq({'operation' => "<= value" }) - expect(SoftLayer::ObjectFilter.query_to_filter_operation('*= value')).to eq({'operation' => "*= value" }) - expect(SoftLayer::ObjectFilter.query_to_filter_operation('^= value')).to eq({'operation' => "^= value" }) - expect(SoftLayer::ObjectFilter.query_to_filter_operation('$= value')).to eq({'operation' => "$= value" }) - expect(SoftLayer::ObjectFilter.query_to_filter_operation('_= value')).to eq({'operation' => "_= value" }) - expect(SoftLayer::ObjectFilter.query_to_filter_operation('!~ value')).to eq({'operation' => "!~ value" }) + expect(filter.criteria_for_key_path("some.key.path")).to eq({'operation' => 4}) + end end + +describe SoftLayer::ObjectFilterDefinitionContext do + it "defines the is matcher" do + expect(SoftLayer::ObjectFilterDefinitionContext.is(" fred")).to eq({ 'operation' => ' fred' }) + expect(SoftLayer::ObjectFilterDefinitionContext.is(42)).to eq({ 'operation' => 42 }) + end + + it "defines the is_not matcher" do + expect(SoftLayer::ObjectFilterDefinitionContext.is_not(" fred ")).to eq({ 'operation' => '!= fred' }) + end + + it "defines the contains matcher" do + expect(SoftLayer::ObjectFilterDefinitionContext.contains(" fred ")).to eq({ 'operation' => '*= fred' }) + end + + it "defines the begins_with matcher" do + expect(SoftLayer::ObjectFilterDefinitionContext.begins_with(" fred ")).to eq({ 'operation' => '^= fred' }) end - describe ":build operations translate to correct operators" do - it "handles the common operators" do - object_filter = SoftLayer::ObjectFilter.build("domain") { contains 'value ' } - expect(object_filter).to eq({ "domain" => { 'operation' => "*= value"} }) + it "defines the ends_with matcher" do + expect(SoftLayer::ObjectFilterDefinitionContext.ends_with(" fred ")).to eq({ 'operation' => '$= fred' }) + end - object_filter = SoftLayer::ObjectFilter.build("domain") { begins_with ' value' } - expect(object_filter).to eq({ "domain" => { 'operation' => "^= value"} }) + it "defines the matches_ignoring_case matcher" do + expect(SoftLayer::ObjectFilterDefinitionContext.matches_ignoring_case(" fred ")).to eq({ 'operation' => '_= fred' }) + end - object_filter = SoftLayer::ObjectFilter.build("domain") { ends_with ' value' } - expect(object_filter).to eq({ "domain" => { 'operation' => "$= value"} }) + it "defines the is_greater_than matcher" do + expect(SoftLayer::ObjectFilterDefinitionContext.is_greater_than(" fred ")).to eq({ 'operation' => '> fred' }) + expect(SoftLayer::ObjectFilterDefinitionContext.is_greater_than(100)).to eq({ 'operation' => '> 100' }) + end - object_filter = SoftLayer::ObjectFilter.build("domain") { is 'value ' } - expect(object_filter).to eq({ "domain" => { 'operation' => "_= value"} }) + it "defines the is_less_than matcher" do + expect(SoftLayer::ObjectFilterDefinitionContext.is_less_than(" fred ")).to eq({ 'operation' => '< fred' }) + expect(SoftLayer::ObjectFilterDefinitionContext.is_less_than(100)).to eq({ 'operation' => '< 100' }) + end - object_filter = SoftLayer::ObjectFilter.build("domain") { is_not ' value' } - expect(object_filter).to eq({ "domain" => { 'operation' => "!= value"} }) + it "defines the is_greater_or_equal_to matcher" do + expect(SoftLayer::ObjectFilterDefinitionContext.is_greater_or_equal_to(" fred ")).to eq({ 'operation' => '>= fred' }) + expect(SoftLayer::ObjectFilterDefinitionContext.is_greater_or_equal_to(100)).to eq({ 'operation' => '>= 100' }) + end - object_filter = SoftLayer::ObjectFilter.build("domain") { is_greater_than 'value ' } - expect(object_filter).to eq({ "domain" => { 'operation' => "> value"} }) + it "defines the is_less_or_equal_to matcher" do + expect(SoftLayer::ObjectFilterDefinitionContext.is_less_or_equal_to(" fred ")).to eq({ 'operation' => '<= fred' }) + expect(SoftLayer::ObjectFilterDefinitionContext.is_less_or_equal_to(100)).to eq({ 'operation' => '<= 100' }) + end - object_filter = SoftLayer::ObjectFilter.build("domain") { is_less_than ' value' } - expect(object_filter).to eq({ "domain" => { 'operation' => "< value"} }) + it "defines the contains_exactly matcher" do + expect(SoftLayer::ObjectFilterDefinitionContext.contains_exactly(" fred ")).to eq({ 'operation' => '~ fred' }) + end - object_filter = SoftLayer::ObjectFilter.build("domain") { is_greater_or_equal_to ' value' } - expect(object_filter).to eq({ "domain" => { 'operation' => ">= value"} }) + it "defines the does_not_contain matcher" do + expect(SoftLayer::ObjectFilterDefinitionContext.does_not_contain(" fred ")).to eq({ 'operation' => '!~ fred' }) + end - object_filter = SoftLayer::ObjectFilter.build("domain") { is_less_or_equal_to 'value ' } - expect(object_filter).to eq({ "domain" => { 'operation' => "<= value"} }) + it "defines the is_null matcher" do + expect(SoftLayer::ObjectFilterDefinitionContext.is_null()).to eq({ 'operation' => 'is null' }) + end - object_filter = SoftLayer::ObjectFilter.build("domain") { contains_exactly ' value' } - expect(object_filter).to eq({ "domain" => { 'operation' => "~ value"} }) + it "defines the is_not_null matcher" do + expect(SoftLayer::ObjectFilterDefinitionContext.is_not_null()).to eq({ 'operation' => 'not null' }) + end - object_filter = SoftLayer::ObjectFilter.build("domain") { does_not_contain ' value' } - expect(object_filter).to eq({ "domain" => { 'operation' => "!~ value"} }) + it "defines the satisfies_the_raw_condition matcher" do + expect(SoftLayer::ObjectFilterDefinitionContext.satisfies_the_raw_condition( + { 'operation' => 'some_complex_operation_goes_here'})).to eq({ 'operation' => 'some_complex_operation_goes_here'}) + end + + it "allows 'matches_query' strings with operators" do + SoftLayer::OBJECT_FILTER_OPERATORS.each do |operator| + fake_string = "#{operator} fred " + expect(SoftLayer::ObjectFilterDefinitionContext.matches_query(fake_string)).to eq({ 'operation' => "#{operator} fred"}) end end + + it "allows 'matches_query' strings for exact value match" do + criteria = + expect(SoftLayer::ObjectFilterDefinitionContext.matches_query(" fred")).to eq({ 'operation' => "_= fred"}) + end + + it "allows 'matches_query' strings for begins_with" do + expect(SoftLayer::ObjectFilterDefinitionContext.matches_query("fred*")).to eq({ 'operation' => "^= fred"}) + end + + it "allows 'matches_query' strings for ends_with" do + expect(SoftLayer::ObjectFilterDefinitionContext.matches_query("*fred")).to eq({ 'operation' => "$= fred"}) + end + + it "allows 'matches_query' strings for contains" do + expect(SoftLayer::ObjectFilterDefinitionContext.matches_query("*fred*")).to eq({ 'operation' => "*= fred"}) + end end diff --git a/spec/Service_spec.rb b/spec/Service_spec.rb index dd48fb7..d7e11ce 100644 --- a/spec/Service_spec.rb +++ b/spec/Service_spec.rb @@ -164,8 +164,9 @@ describe "#object_filter" do let (:object_filter) do - object_filter = SoftLayer::ObjectFilter.new() - object_filter["key"] = "value" + object_filter = SoftLayer::ObjectFilter.new() do |filter| + filter.set_criteria_for_key_path("key", "value") + end object_filter end @@ -173,12 +174,12 @@ parameter_filter = service.object_filter(object_filter) expect(parameter_filter).to_not be_nil expect(parameter_filter.target).to eq service - expect(parameter_filter.server_object_filter).to eq object_filter + expect(parameter_filter.server_object_filter).to eq object_filter.to_h end it "passes an object filter through to an API call" do expect(service).to receive(:call_softlayer_api_with_params).with(:getObject, an_instance_of(SoftLayer::APIParameterFilter),[]) do |method_name, parameters, args| - expect(parameters.server_object_filter).to eq object_filter + expect(parameters.server_object_filter).to eq object_filter.to_h end service.object_filter(object_filter).getObject From d227df8e03aef7514e6b9c24d9d3958485b627ab Mon Sep 17 00:00:00 2001 From: Scott Thompson Date: Thu, 26 Jun 2014 15:38:36 -0500 Subject: [PATCH 03/32] Stray whitespace cleanup --- doc_src/Foundation.md | 6 +-- doc_src/Model Layer.md | 6 +-- doc_src/Welcome.md | 2 +- examples/account_servers.rb | 4 +- examples/create_ticket.rb | 6 +-- examples/open_tickets.rb | 4 +- examples/ticket_info.rb | 4 +- lib/softlayer/Config.rb | 6 +-- lib/softlayer/ImageTemplate.rb | 8 ++-- lib/softlayer/ModelBase.rb | 2 +- lib/softlayer/ObjectFilter.rb | 48 ++++++++++++------------ lib/softlayer/ProductItemCategory.rb | 4 +- lib/softlayer/ProductPackage.rb | 4 +- lib/softlayer/Server.rb | 2 +- lib/softlayer/Ticket.rb | 8 ++-- spec/Account_spec.rb | 2 +- spec/Client_spec.rb | 10 ++--- spec/ObjectFilter_spec.rb | 56 ++++++++++++++-------------- spec/ProductPackage_spec.rb | 4 +- spec/VirtualServer_spec.rb | 14 +++---- 20 files changed, 100 insertions(+), 100 deletions(-) diff --git a/doc_src/Foundation.md b/doc_src/Foundation.md index d8f62a3..99871f6 100644 --- a/doc_src/Foundation.md +++ b/doc_src/Foundation.md @@ -21,7 +21,7 @@ SoftLayer provides two different endpoint URLs to scripts. One is associated wit # The base URL of the SoftLayer API available to the public internet. API_PUBLIC_ENDPOINT = 'https://api.softlayer.com/xmlrpc/v3/' - + # The base URL of the SoftLayer API available through SoftLayer's private network API_PRIVATE_ENDPOINT = 'https://api.service.softlayer.com/xmlrpc/v3/' @@ -65,7 +65,7 @@ The open_tickets variable should receive an array of hashes representing the ope softlayer_client = SoftLayer::Client.new( :username => "joecustomer", api_key => "feeddeadbeefbadf00d...) open_tickets = softlayer_client["Account"].getOpenTickets - + open_tickets.each { |ticket_hash| puts ticket_hash["title"] } This short example shows the essence of working with the Foundation API, you create a client, obtain a service, and make calls to the network SoftLayer API through that service. @@ -82,7 +82,7 @@ Calls to the network SoftLayer API that result in errors being returned by the s ## Troubleshooting -Communication with the SoftLayer servers is handled through the XML-RPC client that is built into the Ruby Core library. As a consequence the network communication is also handled by Core library classes. +Communication with the SoftLayer servers is handled through the XML-RPC client that is built into the Ruby Core library. As a consequence the network communication is also handled by Core library classes. One aspect of network communication that the `softlayer_api` relies on the Ruby Core library to provide is SSL certificate authentication. Problems with this authentication often arise if your Ruby environment is not properly configured with SSL root certificates. If you find you are having trouble communicating with the network SoftLayer API, and the error messages point to SSL certificate authentication, please consider a web search using your specific error message as a search string. This will often reveal answers that can help you resolve networking issues your Ruby environment. diff --git a/doc_src/Model Layer.md b/doc_src/Model Layer.md index 0f66822..675fd98 100644 --- a/doc_src/Model Layer.md +++ b/doc_src/Model Layer.md @@ -10,7 +10,7 @@ The details of the individual classes that for the object class hierarchy of the The ModelBase is the abstract base class of object class hierarchy that forms the Model Layer of the `softlayer_api` Gem. An instance of ModelBase represents a single entity within the SoftLayer API. -In the Foundation layer, SoftLayer entities are represented as a Ruby hash whose keys and values are the are property names and property values of the entity. In the Model Layer, SoftLayer entities are represented by instances of the concrete subclasses of the Model Base class. +In the Foundation layer, SoftLayer entities are represented as a Ruby hash whose keys and values are the are property names and property values of the entity. In the Model Layer, SoftLayer entities are represented by instances of the concrete subclasses of the Model Base class. In implementation terms, an instance of the ModelBase class (or more accurately and instance of a concrete subclass of the ModelBase class) encapsulates the hashes of the Foundation layer defines the attributes and operations that form a convenient model for working with the underlying entity. @@ -24,7 +24,7 @@ The initializer for classes in the ModelBase hierarchy are declared: … end -The first argument is the client that the object may use to make requests to the network API. The second is the `network_hash`, the hash representation of the entity as returned by the network API. +The first argument is the client that the object may use to make requests to the network API. The second is the `network_hash`, the hash representation of the entity as returned by the network API. The hash used to initialize an instance of ModelBase *must* contain a key, `id`, whose value is the `id` of the SoftLayer entity that the object model instance will represent. Correspondingly, the ModelBase class defines the `id` as having the same value as the `id` property in the network hash. @@ -43,7 +43,7 @@ The ModelBase class defines the subscript operator (`[]`) to accept a property n ticket = SoftLayer::Ticket.ticket_with_id(123456) service_provider = ticket['serviceProvider'] -In this case we ask the ticket for the value of the `serviceProvider` property. Note that the argument to the subscript operator is a string containing the property name. +In this case we ask the ticket for the value of the `serviceProvider` property. Note that the argument to the subscript operator is a string containing the property name. This technique can only return values stored in the `softlayer_hash` encapsulated in the ModelBase class. Many classes in the Model Layer limit the information retrieved from the network (using object masks) to a subset of the full set of properties available through the network API. Scripts can check whether or not a given property is included in the underlying hash by calling the `has_sl_property?` method of ModelBase. diff --git a/doc_src/Welcome.md b/doc_src/Welcome.md index 2b16413..43e6511 100644 --- a/doc_src/Welcome.md +++ b/doc_src/Welcome.md @@ -18,6 +18,6 @@ The Foundation layer, makes use of the [XMLRPC client](http://www.ruby-doc.org/s The Model layer is built atop the foundation as object class hierarchy. The class hierarchy models the structures found in the SoftLayer environment using the object-oriented features of Ruby. It does this to abstract out some of the implementation detail that a developer would commonly have to work with to communicate with SoftLayer through the foundation layer. -The Model layer is by no means complete; quite to the contrary it is in its infancy and we believe that much of the development effort in the Gem will focus on incorporating new models into this layer. Because it is incomplete, however, we have put some effort into bridges from the functionality of the model, down to the lower level foundation, without trouble. Also, as a result of this, developers interested in using the Model layer should also should familiarize themselves with the Foundation. +The Model layer is by no means complete; quite to the contrary it is in its infancy and we believe that much of the development effort in the Gem will focus on incorporating new models into this layer. Because it is incomplete, however, we have put some effort into bridges from the functionality of the model, down to the lower level foundation, without trouble. Also, as a result of this, developers interested in using the Model layer should also should familiarize themselves with the Foundation. All developers should continue their exploration of the `softlayer_api` gem by examining the Foundation documentation. Clients that wish to make use of the abstractions provided in the object hierarchy may continue their exploration by looking at the Model Layer documentation. Developers who wish to expand the models found in the `softlayer_api` Gem should read the [Contribution Guide](ContributionGuide_md.html) \ No newline at end of file diff --git a/examples/account_servers.rb b/examples/account_servers.rb index 93f8570..9d0a52a 100644 --- a/examples/account_servers.rb +++ b/examples/account_servers.rb @@ -23,8 +23,8 @@ require 'rubygems' require 'softlayer_api' require 'pp' - - # We can set the default client to be our client and that way + + # We can set the default client to be our client and that way # we can avoid supplying it later SoftLayer::Client.default_client = SoftLayer::Client.new( # :username => "joecustomer" # enter your username here diff --git a/examples/create_ticket.rb b/examples/create_ticket.rb index b60775d..231f7ed 100755 --- a/examples/create_ticket.rb +++ b/examples/create_ticket.rb @@ -35,7 +35,7 @@ account = SoftLayer::Account.account_for_client(softlayer_client) account_user = account.service.getCurrentUser my_user_id = account_user["id"] - + # We also need a subject for the ticket. Subjects are specified by id # This code prints out a table of all the ticket subjects with their # ids: @@ -43,7 +43,7 @@ ticket_subjects.each do |subject| puts "#{subject['id']}\t#{subject['name']}" end - + # For this example we'll use 'Public Network Question' as the subject. That's id 1022 public_network_question_id = 1022 @@ -58,7 +58,7 @@ ) puts "Created a new ticket : #{new_ticket.id} - #{new_ticket.title}" - + # we can also add an update to the ticket: new_ticket.update("This is a ticket update sent from the Ruby library") diff --git a/examples/open_tickets.rb b/examples/open_tickets.rb index 7aa82e2..4f469b1 100644 --- a/examples/open_tickets.rb +++ b/examples/open_tickets.rb @@ -30,14 +30,14 @@ # $SL_API_KEY = "feeddeadbeefbadf00d..." # enter your api key here # The client constructed here must get it's credentials from somewhere -# In this script you might uncomment the globals above and assign your +# In this script you might uncomment the globals above and assign your # credentials there SoftLayer::Client.default_client = SoftLayer::Client.new() # The openTickets routine will pick up the default client established above. open_tickets = SoftLayer::Ticket.open_tickets() -open_tickets.sort!{ |lhs, rhs| -(lhs.lastEditDate <=> rhs.lastEditDate) } +open_tickets.sort!{ |lhs, rhs| -(lhs.lastEditDate <=> rhs.lastEditDate) } open_tickets.each do |ticket| printf "#{ticket.id} - #{ticket.title}" diff --git a/examples/ticket_info.rb b/examples/ticket_info.rb index 197d355..31d31f9 100644 --- a/examples/ticket_info.rb +++ b/examples/ticket_info.rb @@ -34,13 +34,13 @@ # at information. In this case we are talking directly to the ticket # service ticket_service = softlayer_client.service_named("Ticket"); - + # Retrive a particular ticket by ID (you will have to substitute an existing ticket's ID here) ticket_ref = ticket_service.object_with_id(12345) # Retrive very specific information about the ticket ticket = ticket_ref.object_mask("mask[updates[entry,createDate],assignedUserId,attachedHardware.datacenter]").getObject - + pp ticket rescue Exception => exception puts "Unable to retrieve the ticket #{exception}" diff --git a/lib/softlayer/Config.rb b/lib/softlayer/Config.rb index a3404b4..4d83fe7 100644 --- a/lib/softlayer/Config.rb +++ b/lib/softlayer/Config.rb @@ -24,11 +24,11 @@ module SoftLayer - # The SoftLayer Config class is responsible for providing the key information + # The SoftLayer Config class is responsible for providing the key information # the library needs to communicate with the network SoftLayer API. Those three crucial # pieces of information are the Username, the API Key, and the endpoint_url. This information # is collected in a hash with the keys `:username`, `:api_key`, and `:endpoint_url` repsectively. - # + # # The routine used to retrieve this information from a Config object is Config.client_settings # # There are several locations that the Config class looks for this information: @@ -57,7 +57,7 @@ module SoftLayer # # = Environment Variables # - # The config class will search the environment variables SL_USERNAME and SL_API_KEY for + # The config class will search the environment variables SL_USERNAME and SL_API_KEY for # the username and API key respectively. The endpoint_url may not be set thorugh # environment variables. # diff --git a/lib/softlayer/ImageTemplate.rb b/lib/softlayer/ImageTemplate.rb index 29d6653..ec838a8 100644 --- a/lib/softlayer/ImageTemplate.rb +++ b/lib/softlayer/ImageTemplate.rb @@ -154,9 +154,9 @@ def ImageTemplate.find_private_templates(options_hash = {}) end templates_data = account_service.getPrivateBlockDeviceTemplateGroups - templates_data.collect { |template_data| new(softlayer_client, template_data) } + templates_data.collect { |template_data| new(softlayer_client, template_data) } end - + ## # Retrieve a list of public image templates # @@ -218,9 +218,9 @@ def ImageTemplate.find_public_templates(options_hash = {}) end templates_data = template_service.getPublicImages - templates_data.collect { |template_data| new(softlayer_client, template_data) } + templates_data.collect { |template_data| new(softlayer_client, template_data) } end - + ## # Retrive the Image Template with the given ID # (Note! This is the service ID, not the globalIdentifier. diff --git a/lib/softlayer/ModelBase.rb b/lib/softlayer/ModelBase.rb index d4a499b..f60c219 100644 --- a/lib/softlayer/ModelBase.rb +++ b/lib/softlayer/ModelBase.rb @@ -131,7 +131,7 @@ def to_ary() def softlayer_properties(object_mask = nil) raise "Abstract method softlayer_properties in ModelBase was called" end - + ## # The softlayer_hash stores the low-level information about an # object as it was retrieved from the SoftLayer API. diff --git a/lib/softlayer/ObjectFilter.rb b/lib/softlayer/ObjectFilter.rb index c452b48..a2c87e9 100644 --- a/lib/softlayer/ObjectFilter.rb +++ b/lib/softlayer/ObjectFilter.rb @@ -49,7 +49,7 @@ def initialize(&construction_block) self.modify(&construction_block) self end - + def empty? @filter_hash.empty? end @@ -61,19 +61,19 @@ def modify(&construction_block) def accept(key_path) CriteriaAcceptor.new(self, key_path) end - + def to_h return @filter_hash.dup end def criteria_for_key_path(key_path) - raise "The key path cannot be empty when searching for criteria" if key_path.nil? || key_path.empty? + raise "The key path cannot be empty when searching for criteria" if key_path.nil? || key_path.empty? current_level = @filter_hash keys = key_path.split('.') - + while current_level && keys.count > 1 - current_level = current_level[keys.shift] + current_level = current_level[keys.shift] end if current_level @@ -98,19 +98,19 @@ def set_criteria_for_key_path(key_path, criteria) current_level[current_key] = criteria end - + class CriteriaAcceptor def initialize(filter, key_path) @filter = filter @key_path = key_path end - + def when_it(criteria) @filter.set_criteria_for_key_path(@key_path, criteria) end end end # ObjectFilter - + OBJECT_FILTER_OPERATORS = [ '*=', # Contains (ignoring case) '^=', # Begins with (ignoring case) @@ -147,7 +147,7 @@ def self.is(value) # the value passed in. def self.is_not(value) filter_criteria('!=', value) - end + end # Matches when the value is found within the field # the search is not case sensitive @@ -202,17 +202,17 @@ def self.contains_exactly(value) # the search _is_ case sensitive def self.does_not_contain(value) filter_criteria('!~', value) - end + end # Matches when the property's value is null def self.is_null { 'operation' => 'is null' } - end + end # Matches when the property's value is not null def self.is_not_null() { 'operation' => 'not null' } - end + end # This is a catch-all criteria matcher that allows for raw object filter conditions # not covered by the more convenient methods above. The name is intentionally, annoyingly @@ -244,24 +244,24 @@ def self.satisfies_the_raw_condition(condition_hash) def self.matches_query(query_string) query = query_string.to_s.strip - operator = OBJECT_FILTER_OPERATORS.find do | operator_string | - query[0 ... operator_string.length] == operator_string - end + operator = OBJECT_FILTER_OPERATORS.find do | operator_string | + query[0 ... operator_string.length] == operator_string + end - if operator then + if operator then filter_criteria(operator, query[operator.length..-1]) - else - case query - when /\A\*(.*)\*\Z/ + else + case query + when /\A\*(.*)\*\Z/ contains($1) - when /\A\*(.*)/ + when /\A\*(.*)/ ends_with($1) - when /\A(.*)\*\Z/ + when /\A(.*)\*\Z/ begins_with($1) - else + else matches_ignoring_case(query) - end #case - end #if + end #case + end #if end private diff --git a/lib/softlayer/ProductItemCategory.rb b/lib/softlayer/ProductItemCategory.rb index b9e925d..5b9712f 100644 --- a/lib/softlayer/ProductItemCategory.rb +++ b/lib/softlayer/ProductItemCategory.rb @@ -48,13 +48,13 @@ def free? # service. class ProductItemCategory < ModelBase include ::SoftLayer::DynamicAttribute - + ## # :attr_reader: # The categoryCode is a primary identifier for a particular # category. It is a string like 'os' or 'ram' sl_attr :categoryCode - + ## # :attr_reader: # The name of a category is a friendly, readable string diff --git a/lib/softlayer/ProductPackage.rb b/lib/softlayer/ProductPackage.rb index bd6bd9b..4a490eb 100644 --- a/lib/softlayer/ProductPackage.rb +++ b/lib/softlayer/ProductPackage.rb @@ -141,7 +141,7 @@ def service def self.packages_with_key_name(key_name, client = nil) softlayer_client = client || Client.default_client raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client - + filter = SoftLayer::ObjectFilter.new do |filter| filter.accept('type.keyName').when_it is(key_name) end @@ -158,7 +158,7 @@ def self.packages_with_key_name(key_name, client = nil) def self.package_with_id(package_id, client = nil) softlayer_client = client || Client.default_client raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client - + package_data = softlayer_client['Product_Package'].object_with_id(package_id).object_mask(self.default_object_mask('mask')).getObject ProductPackage.new(softlayer_client, package_data) end diff --git a/lib/softlayer/Server.rb b/lib/softlayer/Server.rb index 6493d3a..3b9ba8c 100644 --- a/lib/softlayer/Server.rb +++ b/lib/softlayer/Server.rb @@ -82,7 +82,7 @@ def initialize(softlayer_client, network_hash) super end end - + ## # Reboot the server. This action is taken immediately. # Servers can be rebooted in three different ways: diff --git a/lib/softlayer/Ticket.rb b/lib/softlayer/Ticket.rb index c7a9a05..422c32b 100644 --- a/lib/softlayer/Ticket.rb +++ b/lib/softlayer/Ticket.rb @@ -32,10 +32,10 @@ class Ticket < SoftLayer::ModelBase # :attr_reader: # The ticket system maintains a fixed set of subjects for tickets that are used to ensure tickets make it to the right folks quickly sl_attr :subject - + ## # :attr_reader: - # The date the ticket was last updated. + # The date the ticket was last updated. sl_attr :lastEditDate ## @@ -126,7 +126,7 @@ def self.ticket_subjects(client = nil) # +:client+ - the client in which to search for the ticket # # If a client is not provided then the routine will search Client::default_client - # If Client::default_client is also nil the routine will raise an error. + # If Client::default_client is also nil the routine will raise an error. def self.open_tickets(options = {}) softlayer_client = options[:client] || Client.default_client raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client @@ -140,7 +140,7 @@ def self.open_tickets(options = {}) open_tickets_data = softlayer_client["Account"].object_mask(object_mask).getOpenTickets open_tickets_data.collect { |ticket_data| new(softlayer_client, ticket_data) } end - + ## # Find the ticket with the given ID and return it # diff --git a/spec/Account_spec.rb b/spec/Account_spec.rb index 59e8402..52d95bb 100644 --- a/spec/Account_spec.rb +++ b/spec/Account_spec.rb @@ -109,7 +109,7 @@ fixture_from_json("test_virtual_servers") when :getObject fixture_from_json("test_account") - end + end end test_account = SoftLayer::Account.account_for_client(mock_client) diff --git a/spec/Client_spec.rb b/spec/Client_spec.rb index 10594e3..97c6c0e 100644 --- a/spec/Client_spec.rb +++ b/spec/Client_spec.rb @@ -164,20 +164,20 @@ expect(first_account_service).to be(second_account_service) end - + it "recognizes a symbol as an acceptable service name" do account_service = test_client[:Account] expect(account_service).to_not be_nil - + trying_again = test_client['Account'] expect(trying_again).to be(account_service) - + yet_again = test_client['SoftLayer_Account'] expect(yet_again).to be(account_service) - + once_more = test_client[:SoftLayer_Account] expect(once_more).to be(account_service) end - + end end diff --git a/spec/ObjectFilter_spec.rb b/spec/ObjectFilter_spec.rb index 72113a1..1bdee80 100644 --- a/spec/ObjectFilter_spec.rb +++ b/spec/ObjectFilter_spec.rb @@ -29,55 +29,55 @@ describe SoftLayer::ObjectFilter do it "calls its construction block" do block_called = false; - filter = SoftLayer::ObjectFilter.new() { + filter = SoftLayer::ObjectFilter.new() { block_called = true; - } + } expect(block_called).to be(true) end - + it "expects the methods in the ObjectFilterDefinitionContext to be available in its block" do stuff_defined = false; - filter = SoftLayer::ObjectFilter.new() { + filter = SoftLayer::ObjectFilter.new() { stuff_defined = !!defined?(satisfies_the_raw_condition); - } + } expect(stuff_defined).to be(true) end - + it "is empty when no criteria have been added" do filter = SoftLayer::ObjectFilter.new() expect(filter.empty?).to be(true) end - + it "is not empty criteria have been added" do filter = SoftLayer::ObjectFilter.new do |filter| filter.accept("foobar").when_it is("baz") end - + expect(filter.empty?).to be(false) end - + it "returns criteria for a given key path" do test_hash = { 'one' => { 'two' => {'three' => 3}}} filter = SoftLayer::ObjectFilter.new() - filter.instance_eval do + filter.instance_eval do @filter_hash = test_hash end - + expect(filter.criteria_for_key_path("one")).to eq({'two' => {'three' => 3}}) expect(filter.criteria_for_key_path("one.two")).to eq({'three' => 3}) expect(filter.criteria_for_key_path("one.two.three")).to eq(3) end - + it "returns nil when asked for criteria that don't exist" do filter = SoftLayer::ObjectFilter.new() filter.set_criteria_for_key_path("some.key.path", 3) expect(filter.criteria_for_key_path("some.key.path")).to eq(3) expect(filter.criteria_for_key_path("does.not.exist")).to be_nil - + expect(filter.to_h).to eq({ 'some' => { 'key' => {'path' => 3}}}) end @@ -92,19 +92,19 @@ filter.set_criteria_for_key_path("one.two.also_two", 2) expect(filter.criteria_for_key_path("one.two")).to eq({'also_two' => 2, 'three' => 3}) expect(filter.criteria_for_key_path("one.two.also_two")).to eq(2) - + expect(filter.to_h).to eq({"one"=>{"two"=>{"three"=>3, "also_two"=>2}}}) end - + it "sets criteria in the initializer with the fancy syntax" do filter = SoftLayer::ObjectFilter.new do |filter| filter.accept("some.key.path").when_it is(3) end - + expect(filter.criteria_for_key_path("some.key.path")).to eq({'operation' => 3}) expect(filter.to_h).to eq({"some"=>{"key"=>{"path"=>{"operation"=>3}}}}) end - + it "allows the fancy syntax in a modify block" do filter = SoftLayer::ObjectFilter.new() @@ -113,17 +113,17 @@ filter.modify do |filter| filter.accept("some.key.path").when_it is(3) end - + expect(filter.criteria_for_key_path("some.key.path")).to eq({'operation' => 3}) # can replace a criterion filter.modify do |filter| filter.accept("some.key.path").when_it is(4) - end + end expect(filter.criteria_for_key_path("some.key.path")).to eq({'operation' => 4}) end - end +end describe SoftLayer::ObjectFilterDefinitionContext do it "defines the is matcher" do @@ -177,7 +177,7 @@ it "defines the does_not_contain matcher" do expect(SoftLayer::ObjectFilterDefinitionContext.does_not_contain(" fred ")).to eq({ 'operation' => '!~ fred' }) - end + end it "defines the is_null matcher" do expect(SoftLayer::ObjectFilterDefinitionContext.is_null()).to eq({ 'operation' => 'is null' }) @@ -191,28 +191,28 @@ expect(SoftLayer::ObjectFilterDefinitionContext.satisfies_the_raw_condition( { 'operation' => 'some_complex_operation_goes_here'})).to eq({ 'operation' => 'some_complex_operation_goes_here'}) end - + it "allows 'matches_query' strings with operators" do SoftLayer::OBJECT_FILTER_OPERATORS.each do |operator| fake_string = "#{operator} fred " expect(SoftLayer::ObjectFilterDefinitionContext.matches_query(fake_string)).to eq({ 'operation' => "#{operator} fred"}) end end - + it "allows 'matches_query' strings for exact value match" do - criteria = + criteria = expect(SoftLayer::ObjectFilterDefinitionContext.matches_query(" fred")).to eq({ 'operation' => "_= fred"}) end - + it "allows 'matches_query' strings for begins_with" do expect(SoftLayer::ObjectFilterDefinitionContext.matches_query("fred*")).to eq({ 'operation' => "^= fred"}) end - + it "allows 'matches_query' strings for ends_with" do expect(SoftLayer::ObjectFilterDefinitionContext.matches_query("*fred")).to eq({ 'operation' => "$= fred"}) end - + it "allows 'matches_query' strings for contains" do expect(SoftLayer::ObjectFilterDefinitionContext.matches_query("*fred*")).to eq({ 'operation' => "*= fred"}) end -end +end \ No newline at end of file diff --git a/spec/ProductPackage_spec.rb b/spec/ProductPackage_spec.rb index f3f01c9..ed224eb 100644 --- a/spec/ProductPackage_spec.rb +++ b/spec/ProductPackage_spec.rb @@ -39,7 +39,7 @@ [] end - SoftLayer::ProductPackage.packages_with_key_name('FAKE_KEY_NAME', client) + SoftLayer::ProductPackage.packages_with_key_name('FAKE_KEY_NAME', client) end it "identifies itself with the Product_Package service" do @@ -59,7 +59,7 @@ expect(fake_package.service.server_object_id).to eq(12345) expect(fake_package.service.target.service_name).to eq "SoftLayer_Product_Package" end - + describe "class methods for getting to packages" do let(:mock_client) do client = SoftLayer::Client.new(:username => "fake_user", :api_key => "BADKEY") diff --git a/spec/VirtualServer_spec.rb b/spec/VirtualServer_spec.rb index c8cda3f..3abf677 100644 --- a/spec/VirtualServer_spec.rb +++ b/spec/VirtualServer_spec.rb @@ -68,12 +68,12 @@ it_behaves_like "server with mutable hostname" do let (:server) { sample_server } end - + describe "component upgrades" do let(:mock_client) do mock_client = SoftLayer::Client.new(:username => "fakeuser", :api_key => "DEADBEEFBADF00D") virtual_guest_service = mock_client[:Virtual_Guest] - + allow(virtual_guest_service).to receive(:call_softlayer_api_with_params) do |api_method, parameters, api_arguments| api_return = nil @@ -83,7 +83,7 @@ else fail "Unexpected call to the SoftLayer_Virtual_Guest service" end - + api_return end @@ -107,21 +107,21 @@ expect(api_method).to_not be_empty end end - + it "upgrades cores" do fake_virtual_server = SoftLayer::VirtualServer.new(mock_client, {"id" => 12345}) fake_virtual_server.upgrade_cores!(8) end - + it "upgrades ram" do fake_virtual_server = SoftLayer::VirtualServer.new(mock_client, {"id" => 12345}) fake_virtual_server.upgrade_RAM!(4) end - + it "upgrades max port speed" do fake_virtual_server = SoftLayer::VirtualServer.new(mock_client, {"id" => 12345}) fake_virtual_server.upgrade_max_port_speed!(100) end end # individual component upgrades - end + end end \ No newline at end of file From aa896b5d476151e8ec45628e09972b3a6bf3cbe8 Mon Sep 17 00:00:00 2001 From: Scott Thompson Date: Thu, 26 Jun 2014 16:56:27 -0500 Subject: [PATCH 04/32] Updated the license headers on all the files. They now refer to the main license file --- lib/softlayer/APIParameterFilter.rb | 24 ++------- lib/softlayer/Account.rb | 24 ++------- lib/softlayer/BareMetalServer.rb | 27 +++------- lib/softlayer/BareMetalServerOrder.rb | 24 ++------- lib/softlayer/BareMetalServerOrder_Package.rb | 24 ++------- lib/softlayer/Client.rb | 40 +++++++-------- lib/softlayer/Config.rb | 24 ++------- lib/softlayer/DynamicAttribute.rb | 24 ++------- lib/softlayer/ImageTemplate.rb | 51 +++++++++---------- lib/softlayer/ModelBase.rb | 24 ++------- lib/softlayer/ObjectFilter.rb | 24 ++------- lib/softlayer/ObjectMaskParser.rb | 24 ++------- lib/softlayer/ObjectMaskProperty.rb | 24 ++------- lib/softlayer/ObjectMaskToken.rb | 24 ++------- lib/softlayer/ObjectMaskTokenizer.rb | 24 ++------- lib/softlayer/ProductItemCategory.rb | 24 ++------- lib/softlayer/ProductPackage.rb | 24 ++------- lib/softlayer/Server.rb | 24 ++------- lib/softlayer/Ticket.rb | 24 ++------- lib/softlayer/VirtualServer.rb | 24 ++------- lib/softlayer/VirtualServerOrder.rb | 24 ++------- lib/softlayer/base.rb | 37 +++----------- lib/softlayer/object_mask_helpers.rb | 24 ++------- lib/softlayer_api.rb | 23 ++------- rakefile | 24 ++------- softlayer_api.gemspec | 24 ++------- spec/APIParameterFilter_spec.rb | 24 ++------- spec/Account_spec.rb | 24 ++------- spec/BareMetalServerOrder_spec.rb | 24 ++------- spec/BareMetalServer_spec.rb | 24 ++------- spec/Config_spec.rb | 24 ++------- spec/DynamicAttribute_spec.rb | 24 ++------- spec/ModelBase_spec.rb | 24 ++------- spec/ObjectMaskParser_spec.rb | 24 ++------- spec/ObjectMaskProperty_spec.rb | 24 ++------- spec/ProductPackage_spec.rb | 24 ++------- spec/Server_spec.rb | 24 ++------- spec/Service_spec.rb | 24 ++------- spec/Ticket_spec.rb | 24 ++------- spec/VirtualServerOrder_spec.rb | 24 ++------- spec/VirtualServer_spec.rb | 24 ++------- spec/XMLRPC_Convert_spec.rb | 24 ++------- spec/object_mask_helpers_spec.rb | 24 ++------- 43 files changed, 249 insertions(+), 841 deletions(-) diff --git a/lib/softlayer/APIParameterFilter.rb b/lib/softlayer/APIParameterFilter.rb index df468db..197f601 100644 --- a/lib/softlayer/APIParameterFilter.rb +++ b/lib/softlayer/APIParameterFilter.rb @@ -1,24 +1,10 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ + + module SoftLayer # An +APIParameterFilter+ is an intermediary object that understands how diff --git a/lib/softlayer/Account.rb b/lib/softlayer/Account.rb index 27a399c..1dec02b 100644 --- a/lib/softlayer/Account.rb +++ b/lib/softlayer/Account.rb @@ -1,24 +1,10 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ + + module SoftLayer class Account < SoftLayer::ModelBase diff --git a/lib/softlayer/BareMetalServer.rb b/lib/softlayer/BareMetalServer.rb index f6147f7..06ecd40 100644 --- a/lib/softlayer/BareMetalServer.rb +++ b/lib/softlayer/BareMetalServer.rb @@ -1,24 +1,10 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ + + module SoftLayer # @@ -27,6 +13,7 @@ module SoftLayer # +SoftLayer_Hardware_Server+ services in the SoftLayer API # # http://sldn.softlayer.com/reference/datatypes/SoftLayer_Hardware + # # http://sldn.softlayer.com/reference/datatypes/SoftLayer_Hardware_Server # class BareMetalServer < Server @@ -51,7 +38,7 @@ def bare_metal_instance? # removed from the account). # # The +cancellation_reason+ parameter should be a key from the hash returned - # by +BareMetalServer::cancellation_reasons+. + # by BareMetalServer::cancellation_reasons. # # You may add your own, more specific reasons for cancelling a server in the # +comments+ parameter. diff --git a/lib/softlayer/BareMetalServerOrder.rb b/lib/softlayer/BareMetalServerOrder.rb index 3e924fe..5fd44ed 100644 --- a/lib/softlayer/BareMetalServerOrder.rb +++ b/lib/softlayer/BareMetalServerOrder.rb @@ -1,24 +1,10 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ + + module SoftLayer # diff --git a/lib/softlayer/BareMetalServerOrder_Package.rb b/lib/softlayer/BareMetalServerOrder_Package.rb index 6f323f0..1c83077 100644 --- a/lib/softlayer/BareMetalServerOrder_Package.rb +++ b/lib/softlayer/BareMetalServerOrder_Package.rb @@ -1,24 +1,10 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ + + module SoftLayer # diff --git a/lib/softlayer/Client.rb b/lib/softlayer/Client.rb index 20bbd6c..0f51eb7 100644 --- a/lib/softlayer/Client.rb +++ b/lib/softlayer/Client.rb @@ -1,31 +1,18 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ module SoftLayer - # Initialize an instance of the Client class. You pass in the service name - # and optionally hash arguments specifying how the client should access the - # SoftLayer API. + # A client is responsible for storing authentication information for API calls and + # it serves as a centeral repository for the Service instances that call into the + # network API. # - # The following symbols can be used as hash arguments to pass options to the constructor: + # When you create a client, you pass in hash arguments specifying how the client + # should access the SoftLayer API. + # + # The following symbols are the keys for options you pass to the constructor: # - +:username+ - a non-empty string providing the username to use for requests to the client # - +:api_key+ - a non-empty string providing the api key to use for requests to the client # - +:endpoint_url+ - a non-empty string providing the endpoint URL to use for requests to the client @@ -51,6 +38,13 @@ class Client # will be used by many methods if you do not provide an explicit client. @@default_client = nil + ## + # :attr_accessor: + # The client class can maintain a single instance of Client as the "default client" + # Other parts of the library that accept a client as part of their calling sequence + # will look for the default client if one is not provided in the call + # + # This routine returns the client set as the default client. It can be nil def self.default_client return @@default_client end diff --git a/lib/softlayer/Config.rb b/lib/softlayer/Config.rb index 4d83fe7..14c46d0 100644 --- a/lib/softlayer/Config.rb +++ b/lib/softlayer/Config.rb @@ -1,24 +1,10 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ + + require 'configparser' diff --git a/lib/softlayer/DynamicAttribute.rb b/lib/softlayer/DynamicAttribute.rb index 788b2f1..43c91db 100644 --- a/lib/softlayer/DynamicAttribute.rb +++ b/lib/softlayer/DynamicAttribute.rb @@ -1,24 +1,10 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ + + module SoftLayer diff --git a/lib/softlayer/ImageTemplate.rb b/lib/softlayer/ImageTemplate.rb index ec838a8..79f2641 100644 --- a/lib/softlayer/ImageTemplate.rb +++ b/lib/softlayer/ImageTemplate.rb @@ -1,29 +1,21 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ + module SoftLayer ## - # Represents a virtual server image template. - # rougly corresponds to SoftLayer_Virtual_Guest_Block_Device_Template_Group + # A Virtual Server Image Template. + # + # This class rougly corresponds to the unwieldily named + # +SoftLayer_Virtual_Guest_Block_Device_Template_Group+ + # service: + # + # http://sldn.softlayer.com/reference/services/SoftLayer_Virtual_Guest_Block_Device_Template_Group + # + # class ImageTemplate < SoftLayer::ModelBase ## # :attr_reader: @@ -80,7 +72,7 @@ def datacenters= end ## - # Wait until transactions related to the image group are finsihed + # Wait until transactions related to the image template are finsihed def wait_until_ready(max_trials, seconds_between_tries = 2) end @@ -102,8 +94,12 @@ def shared_with_accounts= # # +:client+ - The client used to connect to the API # - # If no client is given, then the routine will try to use Client.default_client + # If no client is given, then the routine will try to use Client.default_client. # If no client can be found the routine will raise an error. + # + # Additional options that may be provided: + # * +:name+ (string) - Return templates with the given name + # * +:global_id+ (string) - Return templates with the given global identfier def ImageTemplate.find_private_templates(options_hash = {}) softlayer_client = options_hash[:client] || Client.default_client raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client @@ -166,11 +162,13 @@ def ImageTemplate.find_private_templates(options_hash = {}) # # If no client is given, then the routine will try to use Client.default_client # If no client can be found the routine will raise an error. + # + # Additional options that may be provided: + # * +:name+ (string) - Return templates with the given name + # * +:global_id+ (string) - Return templates with the given global identfier def ImageTemplate.find_public_templates(options_hash = {}) softlayer_client = options_hash[:client] || Client.default_client raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client - softlayer_client = options_hash[:client] || Client.default_client - raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client if(options_hash.has_key? :object_filter) object_filter = options_hash[:object_filter] @@ -223,8 +221,7 @@ def ImageTemplate.find_public_templates(options_hash = {}) ## # Retrive the Image Template with the given ID - # (Note! This is the service ID, not the globalIdentifier. - # To find a template by global identifier, use find_templates) + # (Note! This is the service ID, not the globalIdentifier!) # # The options parameter should contain: # diff --git a/lib/softlayer/ModelBase.rb b/lib/softlayer/ModelBase.rb index f60c219..584eaf1 100644 --- a/lib/softlayer/ModelBase.rb +++ b/lib/softlayer/ModelBase.rb @@ -1,24 +1,10 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ + + module SoftLayer ## diff --git a/lib/softlayer/ObjectFilter.rb b/lib/softlayer/ObjectFilter.rb index a2c87e9..176cbb6 100644 --- a/lib/softlayer/ObjectFilter.rb +++ b/lib/softlayer/ObjectFilter.rb @@ -1,24 +1,10 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ + + module SoftLayer ## diff --git a/lib/softlayer/ObjectMaskParser.rb b/lib/softlayer/ObjectMaskParser.rb index cde8c6a..3e5b366 100644 --- a/lib/softlayer/ObjectMaskParser.rb +++ b/lib/softlayer/ObjectMaskParser.rb @@ -1,24 +1,10 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ + + require "softlayer/ObjectMaskTokenizer" require "softlayer/ObjectMaskProperty" diff --git a/lib/softlayer/ObjectMaskProperty.rb b/lib/softlayer/ObjectMaskProperty.rb index 6ff0b0f..957f182 100644 --- a/lib/softlayer/ObjectMaskProperty.rb +++ b/lib/softlayer/ObjectMaskProperty.rb @@ -1,24 +1,10 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ + + module SoftLayer # diff --git a/lib/softlayer/ObjectMaskToken.rb b/lib/softlayer/ObjectMaskToken.rb index ba931e4..57720b5 100644 --- a/lib/softlayer/ObjectMaskToken.rb +++ b/lib/softlayer/ObjectMaskToken.rb @@ -1,24 +1,10 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ + + module SoftLayer # diff --git a/lib/softlayer/ObjectMaskTokenizer.rb b/lib/softlayer/ObjectMaskTokenizer.rb index ab7914f..b852af7 100644 --- a/lib/softlayer/ObjectMaskTokenizer.rb +++ b/lib/softlayer/ObjectMaskTokenizer.rb @@ -1,24 +1,10 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ + + require 'softlayer/ObjectMaskToken' require 'strscan' diff --git a/lib/softlayer/ProductItemCategory.rb b/lib/softlayer/ProductItemCategory.rb index 5b9712f..3df1843 100644 --- a/lib/softlayer/ProductItemCategory.rb +++ b/lib/softlayer/ProductItemCategory.rb @@ -1,24 +1,10 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ + + module SoftLayer # This struct represents a configuration option that can be included in diff --git a/lib/softlayer/ProductPackage.rb b/lib/softlayer/ProductPackage.rb index 4a490eb..6f934c4 100644 --- a/lib/softlayer/ProductPackage.rb +++ b/lib/softlayer/ProductPackage.rb @@ -1,24 +1,10 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ + + require 'json' diff --git a/lib/softlayer/Server.rb b/lib/softlayer/Server.rb index 3b9ba8c..0d75d3e 100644 --- a/lib/softlayer/Server.rb +++ b/lib/softlayer/Server.rb @@ -1,24 +1,10 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ + + module SoftLayer # Server is the base class for VirtualServer and BareMetalServer. diff --git a/lib/softlayer/Ticket.rb b/lib/softlayer/Ticket.rb index 422c32b..d3bf0ac 100644 --- a/lib/softlayer/Ticket.rb +++ b/lib/softlayer/Ticket.rb @@ -1,24 +1,10 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ + + module SoftLayer class Ticket < SoftLayer::ModelBase diff --git a/lib/softlayer/VirtualServer.rb b/lib/softlayer/VirtualServer.rb index a699534..5aef517 100644 --- a/lib/softlayer/VirtualServer.rb +++ b/lib/softlayer/VirtualServer.rb @@ -1,24 +1,10 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ + + require 'time' diff --git a/lib/softlayer/VirtualServerOrder.rb b/lib/softlayer/VirtualServerOrder.rb index 4675484..1684b62 100644 --- a/lib/softlayer/VirtualServerOrder.rb +++ b/lib/softlayer/VirtualServerOrder.rb @@ -1,24 +1,10 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ + + module SoftLayer # diff --git a/lib/softlayer/base.rb b/lib/softlayer/base.rb index cf2dd32..ae68590 100644 --- a/lib/softlayer/base.rb +++ b/lib/softlayer/base.rb @@ -1,34 +1,13 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ require 'rubygems' -# This module is used to provide a namespace for SoftLayer code. It also declares a number of -# global variables: -# - +$SL_API_USERNAME+ - The default username passed by clients to the server for authentication. -# Set this if you want to use the same username for all clients and don't want to have to specify it when the client is created -# - +$SL_API_KEY+ - The default API key passed by clients to the server for authentication. -# Set this if you want to use the same api for all clients and don't want to have to specify it when the client is created -# - +$SL_API_BASE_URL+- The default URL used to access the SoftLayer API. This defaults to the value of +SoftLayer::API_PUBLIC_ENDPOINT+ +## +# The SoftLayer module provides a namespace for SoftLayer code. # module SoftLayer VERSION = "2.1.1" # version history in the CHANGELOG.textile file at the root of the source @@ -39,9 +18,9 @@ module SoftLayer # The base URL of the SoftLayer API available through SoftLayer's private network API_PRIVATE_ENDPOINT = 'https://api.service.softlayer.com/xmlrpc/v3/' - # + #--- # These globals can be used to simplify client creation - # + #+++ # Set this if you want to provide a default username for each client as it is created. # usernames provided to the client initializer will override the global @@ -59,4 +38,4 @@ module SoftLayer # # History: # -# The history has been moved to the CHANGELOG.textile file in the source directory +# The history can be found in the CHANGELOG.textile file in the project root directory diff --git a/lib/softlayer/object_mask_helpers.rb b/lib/softlayer/object_mask_helpers.rb index a668ac8..7e070b9 100644 --- a/lib/softlayer/object_mask_helpers.rb +++ b/lib/softlayer/object_mask_helpers.rb @@ -1,24 +1,10 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ + + ## # This extension to the Hash class to allows object masks to be constructed diff --git a/lib/softlayer_api.rb b/lib/softlayer_api.rb index f4ca558..3a0a687 100755 --- a/lib/softlayer_api.rb +++ b/lib/softlayer_api.rb @@ -1,24 +1,9 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ + require 'softlayer/base' require 'softlayer/object_mask_helpers' diff --git a/rakefile b/rakefile index 95f4d09..1df680b 100644 --- a/rakefile +++ b/rakefile @@ -1,24 +1,10 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ + + $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__))) diff --git a/softlayer_api.gemspec b/softlayer_api.gemspec index 2fd6ad3..411a1c8 100644 --- a/softlayer_api.gemspec +++ b/softlayer_api.gemspec @@ -1,24 +1,8 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__))) require 'lib/softlayer/base' @@ -29,7 +13,7 @@ Gem::Specification.new do |s| s.author = "SoftLayer Development Team" s.email = %q{sldn@softlayer.com} s.description = %q{The softlayer_api gem offers a convenient mechanism for invoking the services of the SoftLayer API from Ruby.} - s.summary = %q{Library for accessing the SoftLayer portal API} + s.summary = %q{Library for accessing the SoftLayer API} s.homepage = %q{http://sldn.softlayer.com/} s.license = %q{MIT} diff --git a/spec/APIParameterFilter_spec.rb b/spec/APIParameterFilter_spec.rb index 0e1bb1a..56dc1bc 100644 --- a/spec/APIParameterFilter_spec.rb +++ b/spec/APIParameterFilter_spec.rb @@ -1,24 +1,10 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ + + $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) diff --git a/spec/Account_spec.rb b/spec/Account_spec.rb index 52d95bb..0d3a2c8 100644 --- a/spec/Account_spec.rb +++ b/spec/Account_spec.rb @@ -1,24 +1,10 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ + + $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) diff --git a/spec/BareMetalServerOrder_spec.rb b/spec/BareMetalServerOrder_spec.rb index e6c4f9b..7023fea 100644 --- a/spec/BareMetalServerOrder_spec.rb +++ b/spec/BareMetalServerOrder_spec.rb @@ -1,24 +1,10 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ + + $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) diff --git a/spec/BareMetalServer_spec.rb b/spec/BareMetalServer_spec.rb index 14591a7..d32d0f0 100644 --- a/spec/BareMetalServer_spec.rb +++ b/spec/BareMetalServer_spec.rb @@ -1,24 +1,10 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ + + $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) diff --git a/spec/Config_spec.rb b/spec/Config_spec.rb index 9618a49..33e049d 100644 --- a/spec/Config_spec.rb +++ b/spec/Config_spec.rb @@ -1,24 +1,10 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ + + $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) diff --git a/spec/DynamicAttribute_spec.rb b/spec/DynamicAttribute_spec.rb index f8cc135..0007bfb 100644 --- a/spec/DynamicAttribute_spec.rb +++ b/spec/DynamicAttribute_spec.rb @@ -1,24 +1,10 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ + + $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) diff --git a/spec/ModelBase_spec.rb b/spec/ModelBase_spec.rb index 826f915..a141355 100644 --- a/spec/ModelBase_spec.rb +++ b/spec/ModelBase_spec.rb @@ -1,24 +1,10 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ + + $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) diff --git a/spec/ObjectMaskParser_spec.rb b/spec/ObjectMaskParser_spec.rb index d2480f6..1f3fa1e 100644 --- a/spec/ObjectMaskParser_spec.rb +++ b/spec/ObjectMaskParser_spec.rb @@ -1,24 +1,10 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ + + $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) diff --git a/spec/ObjectMaskProperty_spec.rb b/spec/ObjectMaskProperty_spec.rb index 090301f..b6ecd56 100644 --- a/spec/ObjectMaskProperty_spec.rb +++ b/spec/ObjectMaskProperty_spec.rb @@ -1,24 +1,10 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ + + $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) diff --git a/spec/ProductPackage_spec.rb b/spec/ProductPackage_spec.rb index ed224eb..9cf5958 100644 --- a/spec/ProductPackage_spec.rb +++ b/spec/ProductPackage_spec.rb @@ -1,24 +1,10 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ + + $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) diff --git a/spec/Server_spec.rb b/spec/Server_spec.rb index 7276773..c866fd4 100644 --- a/spec/Server_spec.rb +++ b/spec/Server_spec.rb @@ -1,24 +1,10 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ + + $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) diff --git a/spec/Service_spec.rb b/spec/Service_spec.rb index d7e11ce..83aa105 100644 --- a/spec/Service_spec.rb +++ b/spec/Service_spec.rb @@ -1,24 +1,10 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ + + $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) diff --git a/spec/Ticket_spec.rb b/spec/Ticket_spec.rb index efe35cd..5768909 100644 --- a/spec/Ticket_spec.rb +++ b/spec/Ticket_spec.rb @@ -1,24 +1,10 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ + + $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) diff --git a/spec/VirtualServerOrder_spec.rb b/spec/VirtualServerOrder_spec.rb index d50995f..25f596e 100644 --- a/spec/VirtualServerOrder_spec.rb +++ b/spec/VirtualServerOrder_spec.rb @@ -1,24 +1,10 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ + + $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) diff --git a/spec/VirtualServer_spec.rb b/spec/VirtualServer_spec.rb index 3abf677..ae17d26 100644 --- a/spec/VirtualServer_spec.rb +++ b/spec/VirtualServer_spec.rb @@ -1,24 +1,10 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ + + $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) diff --git a/spec/XMLRPC_Convert_spec.rb b/spec/XMLRPC_Convert_spec.rb index f25f1e3..e6144f7 100644 --- a/spec/XMLRPC_Convert_spec.rb +++ b/spec/XMLRPC_Convert_spec.rb @@ -1,24 +1,10 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ + + $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) diff --git a/spec/object_mask_helpers_spec.rb b/spec/object_mask_helpers_spec.rb index ea4a2ca..85a0f40 100644 --- a/spec/object_mask_helpers_spec.rb +++ b/spec/object_mask_helpers_spec.rb @@ -1,24 +1,10 @@ -# +#-- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. # -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# +# For licensing information see the LICENSE.md file in the project root. +#++ + + $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) From 29c197300a80d7fe6de765772c80436be437eb11 Mon Sep 17 00:00:00 2001 From: Scott Thompson Date: Thu, 26 Jun 2014 17:20:40 -0500 Subject: [PATCH 05/32] Updated version information to 3.0 and added change log --- CHANGELOG.textile | 4 +++- lib/softlayer/base.rb | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.textile b/CHANGELOG.textile index 1b4fb44..89e0079 100644 --- a/CHANGELOG.textile +++ b/CHANGELOG.textile @@ -1,5 +1,7 @@ -*2.2* +*3.0* * Added a method to reboot servers. +* Added a model for Virtual Server Image Templates +* Substantially rewrote the ObjectFilter class. *2.1.1* * Virtual server upgrades no longer raise exceptions diff --git a/lib/softlayer/base.rb b/lib/softlayer/base.rb index ae68590..527b28a 100644 --- a/lib/softlayer/base.rb +++ b/lib/softlayer/base.rb @@ -10,7 +10,7 @@ # The SoftLayer module provides a namespace for SoftLayer code. # module SoftLayer - VERSION = "2.1.1" # version history in the CHANGELOG.textile file at the root of the source + VERSION = "3.0.0" # version history in the CHANGELOG.textile file at the root of the source # The base URL of the SoftLayer API available to the public internet. API_PUBLIC_ENDPOINT = 'https://api.softlayer.com/xmlrpc/v3/' @@ -18,9 +18,9 @@ module SoftLayer # The base URL of the SoftLayer API available through SoftLayer's private network API_PRIVATE_ENDPOINT = 'https://api.service.softlayer.com/xmlrpc/v3/' - #--- + #-- # These globals can be used to simplify client creation - #+++ + #++ # Set this if you want to provide a default username for each client as it is created. # usernames provided to the client initializer will override the global From 5fefbc7a63e12b4ff6393876bb59125e663669e9 Mon Sep 17 00:00:00 2001 From: Scott Thompson Date: Fri, 27 Jun 2014 11:50:35 -0500 Subject: [PATCH 06/32] Added coveralls support --- .coveralls.yml | 1 + lib/softlayer/ImageTemplate.rb | 17 +++++++++++++---- softlayer_api.gemspec | 1 + spec/spec_helper.rb | 2 ++ 4 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 .coveralls.yml diff --git a/.coveralls.yml b/.coveralls.yml new file mode 100644 index 0000000..6e64999 --- /dev/null +++ b/.coveralls.yml @@ -0,0 +1 @@ +service_name: travis-ci \ No newline at end of file diff --git a/lib/softlayer/ImageTemplate.rb b/lib/softlayer/ImageTemplate.rb index 79f2641..afff5b3 100644 --- a/lib/softlayer/ImageTemplate.rb +++ b/lib/softlayer/ImageTemplate.rb @@ -86,6 +86,15 @@ def shared_with_accounts def shared_with_accounts= end + + # ModelBase protocol methods + def service + softlayer_client['Virtual_Guest_Block_Device_Template_Group'].object_with_id(self.id) + end + + def softlayer_properties(object_mask = nil) + self.service.object_mask(self.class.default_object_mask).getObject + end ## # Retrieve a list of the private image templates from the account. @@ -100,7 +109,7 @@ def shared_with_accounts= # Additional options that may be provided: # * +:name+ (string) - Return templates with the given name # * +:global_id+ (string) - Return templates with the given global identfier - def ImageTemplate.find_private_templates(options_hash = {}) + def self.find_private_templates(options_hash = {}) softlayer_client = options_hash[:client] || Client.default_client raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client @@ -166,7 +175,7 @@ def ImageTemplate.find_private_templates(options_hash = {}) # Additional options that may be provided: # * +:name+ (string) - Return templates with the given name # * +:global_id+ (string) - Return templates with the given global identfier - def ImageTemplate.find_public_templates(options_hash = {}) + def self.find_public_templates(options_hash = {}) softlayer_client = options_hash[:client] || Client.default_client raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client @@ -232,7 +241,7 @@ def ImageTemplate.find_public_templates(options_hash = {}) # # The options may include the following keys # * +:object_mask+ (string) - A object mask of properties, in addition to the default properties, that you wish to retrieve for the server - def ImageTemplate.template_with_id(id, options_hash = {}) + def self.template_with_id(id, options_hash = {}) softlayer_client = options_hash[:client] || Client.default_client raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client @@ -249,7 +258,7 @@ def ImageTemplate.template_with_id(id, options_hash = {}) protected - def ImageTemplate.default_object_mask + def self.default_object_mask return "mask[id,name,note,globalIdentifier,datacenters,blockDevices,tagReferences,publicFlag,flexImageFlag]" end end diff --git a/softlayer_api.gemspec b/softlayer_api.gemspec index 411a1c8..00746e0 100644 --- a/softlayer_api.gemspec +++ b/softlayer_api.gemspec @@ -27,4 +27,5 @@ Gem::Specification.new do |s| s.add_development_dependency 'rspec' s.add_development_dependency 'rdoc', '>=2.4.2' s.add_development_dependency 'json', '~> 1.8', '>= 1.8.1' + s.add_development_dependency 'coveralls' end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 65d2249..aae6812 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,3 +1,5 @@ +require 'coveralls' +Coveralls.wear! require 'json' From 070ef0c6464d72dbcb8dc8ccd47ac7a8b916f9cd Mon Sep 17 00:00:00 2001 From: Scott Thompson Date: Fri, 27 Jun 2014 12:02:34 -0500 Subject: [PATCH 07/32] Added the coveralls coverage directory to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 3b18490..2f51b1f 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ log/ internal_examples/ doc/ +coverage/ Gemfile.lock \ No newline at end of file From a26bdd8099659a78ecc43d35ec8d1580087a8a25 Mon Sep 17 00:00:00 2001 From: Scott Thompson Date: Fri, 27 Jun 2014 12:12:04 -0500 Subject: [PATCH 08/32] Added badges to readme and removed unused portions of a server spect test --- README.md | 4 ++++ spec/Server_spec.rb | 6 ------ 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 7699bd0..6359ff3 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ +[![Gem Version](https://badge.fury.io/rb/softlayer_api.svg)](http://badge.fury.io/rb/softlayer_api) +[![Build Status](https://travis-ci.org/softlayer/softlayer-ruby.svg?branch=master)](https://travis-ci.org/SLsthompson/softlayer-ruby) +[![Coverage Status](https://coveralls.io/repos/softlayer/softlayer-ruby/badge.png?branch=master)](https://coveralls.io/r/SLsthompson/softlayer-ruby?branch=master) + # SoftLayer API for Ruby The softlayer-ruby project creates a [Ruby Gem](http://rubygems.org/gems/softlayer_api) which provides language bindings to the [SoftLayer API](http://sldn.softlayer.com/article/The_SoftLayer_API) for the [Ruby](http://www.ruby-lang.org) programming language. diff --git a/spec/Server_spec.rb b/spec/Server_spec.rb index c866fd4..7d0e56c 100644 --- a/spec/Server_spec.rb +++ b/spec/Server_spec.rb @@ -15,12 +15,6 @@ describe SoftLayer::Server do it "is an abstract base class" do mock_client = SoftLayer::Client.new(:username => "fakeuser", :api_key => "DEADBEEFBADF00D") - allow(mock_client).to receive(:[]) do |service_name| - service = mock_client.service_named(service_name) - allow(service).to receive(:call_softlayer_api_with_params) - service - end - expect { SoftLayer::Server.new(mock_client, { "id" => 12345 }) }.to raise_error end end \ No newline at end of file From 1773f4b1e959343028911cc7940757ff7628dd6c Mon Sep 17 00:00:00 2001 From: Scott Thompson Date: Fri, 27 Jun 2014 15:51:08 -0500 Subject: [PATCH 09/32] Fleshed out the behavior of Image Templates. Added a Datacenter model. --- lib/softlayer/BareMetalServer.rb | 2 +- lib/softlayer/Datacenter.rb | 64 +++++++++++++++++++++ lib/softlayer/ImageTemplate.rb | 97 ++++++++++++++++++++++++++------ lib/softlayer/ModelBase.rb | 2 +- lib/softlayer/VirtualServer.rb | 2 +- lib/softlayer_api.rb | 1 + 6 files changed, 149 insertions(+), 19 deletions(-) create mode 100644 lib/softlayer/Datacenter.rb diff --git a/lib/softlayer/BareMetalServer.rb b/lib/softlayer/BareMetalServer.rb index 06ecd40..7d46218 100644 --- a/lib/softlayer/BareMetalServer.rb +++ b/lib/softlayer/BareMetalServer.rb @@ -194,7 +194,7 @@ def self.find_servers(options_hash = {}) 'operation' => 'in', 'options' => [{ 'name' => 'data', - 'value' => options_hash[:tags] + 'value' => options_hash[:tags].collect{ |tag_value| tag_value.to_s } }] } ); end diff --git a/lib/softlayer/Datacenter.rb b/lib/softlayer/Datacenter.rb new file mode 100644 index 0000000..fc060ea --- /dev/null +++ b/lib/softlayer/Datacenter.rb @@ -0,0 +1,64 @@ +#-- +# Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. +# +# For licensing information see the LICENSE.md file in the project root. +#++ + +module SoftLayer + ## + # A Data Center in the SoftLayer network + # + # This class corresponds to the +SoftLayer_Location+ data type: + # + # http://sldn.softlayer.com/reference/datatypes/SoftLayer_Location + # + # Although in this context it is used to represent a data center and + # not the more general class of locations that that data type can + # represent. + + class Datacenter < SoftLayer::ModelBase + sl_attr :name + sl_attr :long_name, "longName" + + ## + # An array with the datacenter names of datacenters that can host + # image templates + IMAGE_TEMPLATE_DATACENTERS = ['ams01', 'dal01', 'dal05', 'dal07', 'hou02', 'sea01', 'sjc01', 'sng01', 'wdc01']; + + ## + # Returns true if image templates can be copied to this data center + def available_for_image_templates? + IMAGE_TEMPLATE_DATACENTERS.include?(self.name) + end + + ## + # Return the datacenter with the given name ('sng01' or 'dal05') + def self.datacenter_named(name, client = nil) + datacenters(client).find{ | datacenter | datacenter.name == name.to_s.downcase } + end + + ## + # Return a list of all the datacenters + # + # If the client parameter is not provided, the routine + # will try to use Client::defult_client. If no client + # can be found, the routine will raise an exception + # + # This routine will only retrieve the list of datacenters from + # the network once and keep it in memory unless you + # pass in force_reload as true. + # + @@data_centers = nil + def self.datacenters(client = nil, force_reload = false) + softlayer_client = client || Client.default_client + raise "Datacenter.datacenters requires a client to call the network API" if !softlayer_client + + if(!@@data_centers || force_reload) + datacenters_data = softlayer_client[:Location].getDatacenters + @@data_centers = datacenters_data.collect { | datacenter_data | self.new(softlayer_client, datacenter_data) } + end + + @@data_centers + end + end +end \ No newline at end of file diff --git a/lib/softlayer/ImageTemplate.rb b/lib/softlayer/ImageTemplate.rb index afff5b3..9295d0b 100644 --- a/lib/softlayer/ImageTemplate.rb +++ b/lib/softlayer/ImageTemplate.rb @@ -34,6 +34,7 @@ class ImageTemplate < SoftLayer::ModelBase # Change the name of the template def rename!(new_name) + self.service.editObject({ "name" => new_name.to_s}) end ## @@ -50,32 +51,53 @@ def flex_image? !!self["flexImageFlag"] end - def notes= + ## + # Changes the notes on an template to be the given strings + def notes=(new_notes) + # it is not a typo that this sets the "note" property. The + # property in the network api is "note", the model exposes it as + # 'notes' for self-consistency + self.service.editObject({ "note" => new_notes.to_s}) end - # Get and set an array of the tags on the image + ## + # Returns an array of the tags set on the image def tags + return self["tagReferences"].collect{ |tag_reference| tag_reference["tag"]["name"] } end - def tags= + ## + # Sets the tags on the template. Note: a pre-existing tag will be + # removed from the template if it does not appear in the array given. + # The list of tags must be comprehensive. + def tags=(tags_array) + as_strings = tags_array.collect { |tag| tag.to_s } + self.service.setTags(as_strings.join(',')) end ## - # Works with an array of data center names (short names) - # where the image template is available - # - # Should this be datacenters and "add_datacenters, remove_datacenters" + # Returns the an array containing the datacenters where this image is available. def datacenters - end - - def datacenters= + self["datacenters"].collect{ |datacenter_data| SoftLayer::Datacenter.datacenter_named(datacenter_data["name"])} end ## - # Wait until transactions related to the image template are finsihed - def wait_until_ready(max_trials, seconds_between_tries = 2) - end + # Accepts an array of datacenters (instances of SoftLayer::Datacenter) where this + # image should be made available. The call will kick off transactions to make + # the image available in the given datacenters. These transactions can take + # some time to complete. + # + # Note that the template will be REMOVED from any datacenter that does not + # appear in this array! The list given must be comprehensive. + def datacenters=(datacenters_array) + datacenter_data = datacenters_array.collect do |datacenter| + raise "Image templates cannot be copied to the data center #{datacenter.name}" unless datacenter.available_for_image_templates? + { "id" => datacenter.id } + end + self.service.setAvailableLocations(datacenter_data.compact) + end + ## # Works with an array of account IDs (or should use master user names i.e. "SL232279" ) # that the image template is shared with @@ -86,6 +108,49 @@ def shared_with_accounts def shared_with_accounts= end + + ## + # Creates a transaction to delete the image template and + # all the disk images associated with it. + # + # This is a final action and cannot be undone. + # the transaction will proceed immediately. + # + # Call it with extreme care! + def delete! + self.service.deleteObject + end + + ## + # Wait until transactions related to the image template are finished + # + # A template is not ready until all the transactions on the template + # itself, and all its children are complete. + # + # At each trial, the routine will yield to a block if one is given + # The block is passed one parameter, a boolean flag indicating + # whether or not the image template is 'ready'. Interim invocations + # of the block should receive +false+, the final invocation should + # receieve +true+ + # + def wait_until_ready(max_trials, seconds_between_tries = 2) + # pessimistically assume the server is not ready + num_trials = 0 + begin + self.refresh_details() + + parent_ready = !(has_sl_property? :transactionId) || (self[:transactionId] == "") + children_ready = (nil == self["children"].find { |child| child["transactionId"] != "" }) + + ready = parent_ready && children_ready + yield ready if block_given? + + num_trials = num_trials + 1 + sleep(seconds_between_tries) if !ready && (num_trials <= max_trials) + end until ready || (num_trials >= max_trials) + + ready + end # ModelBase protocol methods def service @@ -138,7 +203,7 @@ def self.find_private_templates(options_hash = {}) 'operation' => 'in', 'options' => [{ 'name' => 'data', - 'value' => options_hash[:tags] + 'value' => options_hash[:tags].collect{ |tag_value| tag_value.to_s } }] } ); end @@ -204,7 +269,7 @@ def self.find_public_templates(options_hash = {}) 'operation' => 'in', 'options' => [{ 'name' => 'data', - 'value' => options_hash[:tags] + 'value' => options_hash[:tags].collect{ |tag_value| tag_value.to_s } }] } ); end @@ -259,7 +324,7 @@ def self.template_with_id(id, options_hash = {}) protected def self.default_object_mask - return "mask[id,name,note,globalIdentifier,datacenters,blockDevices,tagReferences,publicFlag,flexImageFlag]" + return "mask[id,name,note,globalIdentifier,datacenters,blockDevices,tagReferences,publicFlag,flexImageFlag,transactionId,children.transactionId]" end end end \ No newline at end of file diff --git a/lib/softlayer/ModelBase.rb b/lib/softlayer/ModelBase.rb index 584eaf1..af9e2d9 100644 --- a/lib/softlayer/ModelBase.rb +++ b/lib/softlayer/ModelBase.rb @@ -116,7 +116,7 @@ def to_ary() # def softlayer_properties(object_mask = nil) raise "Abstract method softlayer_properties in ModelBase was called" - end + end ## # The softlayer_hash stores the low-level information about an diff --git a/lib/softlayer/VirtualServer.rb b/lib/softlayer/VirtualServer.rb index 5aef517..dd2e1cc 100644 --- a/lib/softlayer/VirtualServer.rb +++ b/lib/softlayer/VirtualServer.rb @@ -280,7 +280,7 @@ def self.find_servers(options_hash = {}) 'operation' => 'in', 'options' => [{ 'name' => 'data', - 'value' => options_hash[:tags] + 'value' => options_hash[:tags].collect{ |tag_value| tag_value.to_s } }] } ); end diff --git a/lib/softlayer_api.rb b/lib/softlayer_api.rb index 3a0a687..6b6ce0b 100755 --- a/lib/softlayer_api.rb +++ b/lib/softlayer_api.rb @@ -17,6 +17,7 @@ # model classes require 'softlayer/ModelBase' +require 'softlayer/Datacenter' require 'softlayer/DynamicAttribute' require 'softlayer/Account' require 'softlayer/Ticket' From fb50dae19e85500b352b61770e140fc9e4105389 Mon Sep 17 00:00:00 2001 From: Scott Thompson Date: Mon, 30 Jun 2014 16:20:32 -0500 Subject: [PATCH 10/32] Added the ability to share image templates with other accounts --- lib/softlayer/Datacenter.rb | 11 ------- lib/softlayer/ImageTemplate.rb | 60 +++++++++++++++++++++++++--------- lib/softlayer/Server.rb | 2 -- lib/softlayer/VirtualServer.rb | 12 +++++-- 4 files changed, 54 insertions(+), 31 deletions(-) diff --git a/lib/softlayer/Datacenter.rb b/lib/softlayer/Datacenter.rb index fc060ea..c573486 100644 --- a/lib/softlayer/Datacenter.rb +++ b/lib/softlayer/Datacenter.rb @@ -20,17 +20,6 @@ class Datacenter < SoftLayer::ModelBase sl_attr :name sl_attr :long_name, "longName" - ## - # An array with the datacenter names of datacenters that can host - # image templates - IMAGE_TEMPLATE_DATACENTERS = ['ams01', 'dal01', 'dal05', 'dal07', 'hou02', 'sea01', 'sjc01', 'sng01', 'wdc01']; - - ## - # Returns true if image templates can be copied to this data center - def available_for_image_templates? - IMAGE_TEMPLATE_DATACENTERS.include?(self.name) - end - ## # Return the datacenter with the given name ('sng01' or 'dal05') def self.datacenter_named(name, client = nil) diff --git a/lib/softlayer/ImageTemplate.rb b/lib/softlayer/ImageTemplate.rb index 9295d0b..7c746ab 100644 --- a/lib/softlayer/ImageTemplate.rb +++ b/lib/softlayer/ImageTemplate.rb @@ -83,15 +83,17 @@ def datacenters ## # Accepts an array of datacenters (instances of SoftLayer::Datacenter) where this - # image should be made available. The call will kick off transactions to make - # the image available in the given datacenters. These transactions can take + # image should be made available. The call will kick off one or more transactions + # to make the image available in the given datacenters. These transactions can take # some time to complete. # # Note that the template will be REMOVED from any datacenter that does not - # appear in this array! The list given must be comprehensive. + # appear in this array! The list given must be comprehensive. + # + # The available_datacenters call returns a list of the values that are valid + # whithin this array. def datacenters=(datacenters_array) - datacenter_data = datacenters_array.collect do |datacenter| - raise "Image templates cannot be copied to the data center #{datacenter.name}" unless datacenter.available_for_image_templates? + datacenter_data = datacenters_array.collect do |datacenter| { "id" => datacenter.id } end @@ -99,14 +101,41 @@ def datacenters=(datacenters_array) end ## - # Works with an array of account IDs (or should use master user names i.e. "SL232279" ) - # that the image template is shared with + # Returns an array of the datacenters that this image can be stored in. + # This is the set of datacenters that you may choose from, when putting + # together a list you will send to the datacenters= setter. + # + def available_datacenters + datacenters_data = self.service.getStorageLocations() + datacenters_data.collect { |datacenter_data| SoftLayer::Datacenter.datacenter_named(datacenter_data["name"]) } + end + + ## + # Share this image template with another account # - # Should this be datacenters an "share_with_accounts, stop_sharing_with_accounts" - def shared_with_accounts + # The id of another account can usually be determined + # by the user name of the master user which is typically + # "SL" or something similar. + # + # Note that this routine raises an exception if you call + # it with an account that is already in the list of + # shared accounts. + def start_sharing(with_account_id) + self.service.permitSharingAccess(with_account_id) end - def shared_with_accounts= + ## + # Stop sharing this image with the account_id given + def stop_sharing(with_account_id) + raise "You cannot stop sharing an image template with the account that owns it" if with_account_id == self["accountId"] + self.service.denySharingAccess(with_account_id) + end + + ## + # Return an array with the id's of accounts that this image is shared with + def shared_with + accounts_data = self.service.getAccountReferences + accounts_data.collect { |account_data| account_data["accountId"] } end ## @@ -122,16 +151,15 @@ def delete! end ## - # Wait until transactions related to the image template are finished + # Repeatedly poll the netwokr API until transactions related to this image + # template are finished # - # A template is not ready until all the transactions on the template + # A template is not 'ready' until all the transactions on the template # itself, and all its children are complete. # # At each trial, the routine will yield to a block if one is given # The block is passed one parameter, a boolean flag indicating - # whether or not the image template is 'ready'. Interim invocations - # of the block should receive +false+, the final invocation should - # receieve +true+ + # whether or not the image template is 'ready'. # def wait_until_ready(max_trials, seconds_between_tries = 2) # pessimistically assume the server is not ready @@ -324,7 +352,7 @@ def self.template_with_id(id, options_hash = {}) protected def self.default_object_mask - return "mask[id,name,note,globalIdentifier,datacenters,blockDevices,tagReferences,publicFlag,flexImageFlag,transactionId,children.transactionId]" + return "mask[id,accountId,name,note,globalIdentifier,datacenters,blockDevices,tagReferences,publicFlag,flexImageFlag,transactionId,children.transactionId]" end end end \ No newline at end of file diff --git a/lib/softlayer/Server.rb b/lib/softlayer/Server.rb index 0d75d3e..90a31a2 100644 --- a/lib/softlayer/Server.rb +++ b/lib/softlayer/Server.rb @@ -4,8 +4,6 @@ # For licensing information see the LICENSE.md file in the project root. #++ - - module SoftLayer # Server is the base class for VirtualServer and BareMetalServer. # It implements some functionality common to both those classes. diff --git a/lib/softlayer/VirtualServer.rb b/lib/softlayer/VirtualServer.rb index dd2e1cc..655b8ec 100644 --- a/lib/softlayer/VirtualServer.rb +++ b/lib/softlayer/VirtualServer.rb @@ -129,13 +129,21 @@ def upgrade_max_port_speed!(network_speed_in_Mbps) # # The image_notes should be a string and will be added to the image as notes. # + # The routine returns the instance of SoftLayer::ImageTemplate that is + # created. That image template will probably not be available immediately, however. + # You may use the wait_until_ready routine of SoftLayer::ImageTemplate to + # wait on it. + # def capture_image(image_name, include_attached_storage = false, image_notes = nil) disk_filter = lambda { |disk| disk['device'] == '0' } - disk_filter = lambda { |disk| disk['device'] == '1' } if include_attached_storage + disk_filter = lambda { |disk| disk['device'] != '1' } if include_attached_storage disks = self.blockDevices.select(&disk_filter) - self.service.createArchiveTransaction(image_name, disks, notes) if disks && !disks.empty? + self.service.createArchiveTransaction(image_name, disks, notes ? notes : "") if disks && !disks.empty? + + image_templates = SoftLayer::ImageTemplate.find_private_templates(:name => image_name) + image_templates[0] if !image_templates.empty? end ## From 05694a5430409283116a91f4c7f64ca569ceacae Mon Sep 17 00:00:00 2001 From: Scott Thompson Date: Mon, 30 Jun 2014 16:41:39 -0500 Subject: [PATCH 11/32] Changed the shared accounts to be array based instead of forcing individual calls --- lib/softlayer/ImageTemplate.rb | 39 ++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/lib/softlayer/ImageTemplate.rb b/lib/softlayer/ImageTemplate.rb index 7c746ab..89a6dab 100644 --- a/lib/softlayer/ImageTemplate.rb +++ b/lib/softlayer/ImageTemplate.rb @@ -110,6 +110,43 @@ def available_datacenters datacenters_data.collect { |datacenter_data| SoftLayer::Datacenter.datacenter_named(datacenter_data["name"]) } end + + ## + # Returns a list of the accounts (identified by account ID numbers) + # that this image is shared with + def shared_with_accounts + accounts_data = self.service.getAccountReferences + accounts_data.collect { |account_data| account_data["accountId"] } + end + + ## + # Change the set of accounts that this image is shared with. + # The parameter is an array of account ID's. + # + # Note that this routine will "unshare" with any accounts + # not included in the list passed in so the list should + # be comprehensive + # + def shared_with_accounts= (account_id_list) + already_sharing_with = self.shared_with_accounts + + accounts_to_add = account_id_list.select { |account_id| !already_sharing_with.include?(account_id) } + + # Note, using the network API, it is possible to "unshare" an image template + # with the account that owns it, however, this leads to a rather odd state + # where the image has allocated resources (that the account may be charged for) + # but no way to delete those resources. For that reason this model + # always includes the account ID that owns the image in the list of + # accounts the image will be shared with. + my_account_id = self['accountId'] + accounts_to_add.push(my_account_id) if !already_sharing_with.include?(my_account_id) && !accounts_to_add.include?(my_account_id) + + accounts_to_remove = already_sharing_with.select { |account_id| (account_id != my_account_id) && !account_id_list.include?(account_id) } + + accounts_to_add.each {|account_id| self.service.permitSharingAccess account_id } + accounts_to_remove.each {|account_id| self.service.denySharingAccess account_id } + end + ## # Share this image template with another account # @@ -134,8 +171,6 @@ def stop_sharing(with_account_id) ## # Return an array with the id's of accounts that this image is shared with def shared_with - accounts_data = self.service.getAccountReferences - accounts_data.collect { |account_data| account_data["accountId"] } end ## From 20f9d6c7a50418a9724875bfecb47f83cbe0f3ec Mon Sep 17 00:00:00 2001 From: Scott Thompson Date: Mon, 30 Jun 2014 16:42:14 -0500 Subject: [PATCH 12/32] Cleaned up cruft from unfinished change --- lib/softlayer/ImageTemplate.rb | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/lib/softlayer/ImageTemplate.rb b/lib/softlayer/ImageTemplate.rb index 89a6dab..3ba909c 100644 --- a/lib/softlayer/ImageTemplate.rb +++ b/lib/softlayer/ImageTemplate.rb @@ -147,32 +147,6 @@ def shared_with_accounts= (account_id_list) accounts_to_remove.each {|account_id| self.service.denySharingAccess account_id } end - ## - # Share this image template with another account - # - # The id of another account can usually be determined - # by the user name of the master user which is typically - # "SL" or something similar. - # - # Note that this routine raises an exception if you call - # it with an account that is already in the list of - # shared accounts. - def start_sharing(with_account_id) - self.service.permitSharingAccess(with_account_id) - end - - ## - # Stop sharing this image with the account_id given - def stop_sharing(with_account_id) - raise "You cannot stop sharing an image template with the account that owns it" if with_account_id == self["accountId"] - self.service.denySharingAccess(with_account_id) - end - - ## - # Return an array with the id's of accounts that this image is shared with - def shared_with - end - ## # Creates a transaction to delete the image template and # all the disk images associated with it. From c521644c46882d10c1b0c430dc15638ca066f241 Mon Sep 17 00:00:00 2001 From: Scott Thompson Date: Tue, 8 Jul 2014 15:24:19 -0500 Subject: [PATCH 13/32] Propogate the DataCenter and ImageTemplate models so they are used consistently in the API. Made notes in the changelog about the changes. --- CHANGELOG.textile | 5 +- doc_src/Contribution Guide.md | 4 +- lib/softlayer/BareMetalServerOrder.rb | 7 ++- lib/softlayer/BareMetalServerOrder_Package.rb | 14 +++--- lib/softlayer/Datacenter.rb | 4 +- lib/softlayer/ImageTemplate.rb | 47 +++++++++++++------ lib/softlayer/ModelBase.rb | 2 +- lib/softlayer/ProductPackage.rb | 12 +---- lib/softlayer/VirtualServer.rb | 4 +- lib/softlayer/VirtualServerOrder.rb | 23 +++++---- spec/BareMetalServerOrder_Package_spec.rb | 15 ++++-- spec/BareMetalServerOrder_spec.rb | 19 ++++---- spec/VirtualServerOrder_spec.rb | 20 ++++---- spec/fixtures/datacenter_locations.json | 1 + 14 files changed, 98 insertions(+), 79 deletions(-) create mode 100644 spec/fixtures/datacenter_locations.json diff --git a/CHANGELOG.textile b/CHANGELOG.textile index 89e0079..2db624c 100644 --- a/CHANGELOG.textile +++ b/CHANGELOG.textile @@ -1,7 +1,8 @@ *3.0* * Added a method to reboot servers. -* Added a model for Virtual Server Image Templates -* Substantially rewrote the ObjectFilter class. +* Substantially rewrote the ObjectFilter class. ObjectFilters used to be hashes which made it easy to manipulate their content incorrectly. The new implementation has a strict interface that makes it harder to manipulate filters incorrectly. +* Added a model for Virtual Server Image Templates (SoftLayer::ImageTemplate) - VirtualServerOrder now requires an instance of this class rather than allowing you to provide just the global_id of an image +* Added a model for data centers (SoftLayer::Datacenter). Bare Metal, Bare Metal Package, and Virtual server orders now use an instance of Datacenter to identify where their servers will be provisioned. The routines in those classes which used to provide lists of valid data center names now return data center objects. *2.1.1* * Virtual server upgrades no longer raise exceptions diff --git a/doc_src/Contribution Guide.md b/doc_src/Contribution Guide.md index 249d731..a31028a 100644 --- a/doc_src/Contribution Guide.md +++ b/doc_src/Contribution Guide.md @@ -8,7 +8,7 @@ Any requests for enhancements, new features, or bug reports should be entered in # Development Environment -As a Ruby project, your first step will be to install the [Ruby Programming Language](https://www.ruby-lang.org/en/). Many Unix-derived environments, including Mac OS X, have a version or Ruby installed by default, however, the default version may be out-of-date. Please visit the main Ruby language [site](https://www.ruby-lang.org/en/) for instructions on installing an up-to-date build of Ruby for your computing environment. +As a Ruby project, your first step will be to install the [Ruby Programming Language](https://www.ruby-lang.org/en/). Many Unix-derived environments, including Mac OS X, have a version or Ruby installed by default, however, the default version may be out-of-date. Please visit the main Ruby language [site](https://www.ruby-lang.org/en/) for instructions on installing an up-to-date build of Ruby for your computing environment. The Gem supports multiple versions of Ruby, and we recommend using Ruby 2.0 or later. The [Ruby Version Manager (rvm)](https://rvm.io) is an invaluable tool to help keep track of multiple versions of Ruby. The Gem no longer supports Ruby 1.8.7. Support for Ruby 1.9 will continue for a time, but the Core Ruby team is already withdrawing their support for that version. @@ -93,7 +93,7 @@ The basic directory structure for the source tree is as follows softlayer # Folder containing most of the gem's actual source code log # RVM will create a log folder when running commands across multiple ruby versions. pkg # Created when the gem is built, contains built versions of the gem - spec # Source directory for the RSpec testing specifications + spec # Source directory for the RSpec testing specifications fixtures # Files used by the unit tests to mock responses from the SoftLayer network API Most of the source files that implement the gem are found in `lib/softlayer`. If you wish to add new functionality, or edit existing functionality, you will probably edit the class files in this directory. Unit tests using Rspec are found in the spec folder and should generally follow the naming convention of _spec.rb diff --git a/lib/softlayer/BareMetalServerOrder.rb b/lib/softlayer/BareMetalServerOrder.rb index 5fd44ed..5942b43 100644 --- a/lib/softlayer/BareMetalServerOrder.rb +++ b/lib/softlayer/BareMetalServerOrder.rb @@ -32,8 +32,7 @@ class BareMetalServerOrder # a Bare Metal Instance #++ - # String, short name of the data center that will house the new Bare Metal Instance (e.g. "dal05" or "sea01") - # Corresponds to +datacenter.name+ in the documentation for +createObject+. + # An instance of SoftLayer::Datacenter. The server will be provisioned in this data center attr_accessor :datacenter # String, The hostname to assign to the new server @@ -152,7 +151,7 @@ def hardware_instance_template template["privateNetworkOnlyFlag"] = true if @private_network_only - template["datacenter"] = {"name" => @datacenter} if @datacenter + template["datacenter"] = {"name" => @datacenter.name} if @datacenter template['userData'] = [{'value' => @user_metadata}] if @user_metadata template['networkComponents'] = [{'maxSpeed'=> @max_port_speed}] if @max_port_speed template['postInstallScriptUri'] = @provision_script_URI.to_s if @provision_script_URI @@ -184,7 +183,7 @@ def self.create_object_options(client = nil) ## # Return a list of values that are valid for the :datacenter attribute def self.datacenter_options(client = nil) - create_object_options(client)["datacenters"].collect { |datacenter_spec| datacenter_spec['template']['datacenter']["name"] }.uniq.sort! + create_object_options(client)["datacenters"].collect { |datacenter_spec| Datacenter.datacenter_named(datacenter_spec['template']['datacenter']['name'], client) }.uniq end def self.core_options(client = nil) diff --git a/lib/softlayer/BareMetalServerOrder_Package.rb b/lib/softlayer/BareMetalServerOrder_Package.rb index 1c83077..aba93c4 100644 --- a/lib/softlayer/BareMetalServerOrder_Package.rb +++ b/lib/softlayer/BareMetalServerOrder_Package.rb @@ -28,13 +28,14 @@ module SoftLayer class BareMetalServerOrder_Package < Server # The following properties are required in a server order. - # The product package identifying the base configuration for the server. - # a list of Bare Metal Server product packages is returned by + # The product package object (an instance of SoftLayer::ProductPackage) identifying the base + # configuration for the server. A list of Bare Metal Server product packages is returned by # SoftLayer::ProductPackage.bare_metal_server_packages attr_reader :package - # String, short name of the data center that will house the new virtual server (e.g. "dal05" or "sea01") - # A list of valid data centers can be found in ProductPackage#datacenter_options + # An instance of SoftLayer::Datacenter. The server will be provisioned in this data center. + # The set of datacenters available is determined by the package and may be obtained from + # the SoftLayer::ProductPackage object using the #datacenter_options method. attr_accessor :datacenter # The hostname of the server being created (i.e. 'sldn' is the hostname of sldn.softlayer.com). @@ -125,8 +126,7 @@ def hardware_order } } - product_order['location'] = @package.location_id_for_datacenter_name(@datacenter.downcase) if @datacenter - + product_order['location'] = @datacenter.id if @datacenter product_order['sshKeys'] = [{ 'sshKeyIds' => @ssh_key_ids }] if @ssh_key_ids product_order['provisionScripts'] = [@provision_script_URI.to_s] if @provision_script_URI @@ -142,7 +142,5 @@ def hardware_order product_order end - end # BareMetalServerOrder_Package - end # SoftLayer \ No newline at end of file diff --git a/lib/softlayer/Datacenter.rb b/lib/softlayer/Datacenter.rb index c573486..152bf21 100644 --- a/lib/softlayer/Datacenter.rb +++ b/lib/softlayer/Datacenter.rb @@ -8,8 +8,8 @@ module SoftLayer ## # A Data Center in the SoftLayer network # - # This class corresponds to the +SoftLayer_Location+ data type: - # + # This class corresponds to the SoftLayer_Location++ data type: + # # http://sldn.softlayer.com/reference/datatypes/SoftLayer_Location # # Although in this context it is used to represent a data center and diff --git a/lib/softlayer/ImageTemplate.rb b/lib/softlayer/ImageTemplate.rb index 3ba909c..d38be63 100644 --- a/lib/softlayer/ImageTemplate.rb +++ b/lib/softlayer/ImageTemplate.rb @@ -54,7 +54,7 @@ def flex_image? ## # Changes the notes on an template to be the given strings def notes=(new_notes) - # it is not a typo that this sets the "note" property. The + # it is not a typo that this sets the "note" property. The # property in the network api is "note", the model exposes it as # 'notes' for self-consistency self.service.editObject({ "note" => new_notes.to_s}) @@ -67,11 +67,11 @@ def tags end ## - # Sets the tags on the template. Note: a pre-existing tag will be + # Sets the tags on the template. Note: a pre-existing tag will be # removed from the template if it does not appear in the array given. # The list of tags must be comprehensive. def tags=(tags_array) - as_strings = tags_array.collect { |tag| tag.to_s } + as_strings = tags_array.collect { |tag| tag.to_s } self.service.setTags(as_strings.join(',')) end @@ -83,7 +83,7 @@ def datacenters ## # Accepts an array of datacenters (instances of SoftLayer::Datacenter) where this - # image should be made available. The call will kick off one or more transactions + # image should be made available. The call will kick off one or more transactions # to make the image available in the given datacenters. These transactions can take # some time to complete. # @@ -99,10 +99,10 @@ def datacenters=(datacenters_array) self.service.setAvailableLocations(datacenter_data.compact) end - + ## # Returns an array of the datacenters that this image can be stored in. - # This is the set of datacenters that you may choose from, when putting + # This is the set of datacenters that you may choose from, when putting # together a list you will send to the datacenters= setter. # def available_datacenters @@ -118,7 +118,7 @@ def shared_with_accounts accounts_data = self.service.getAccountReferences accounts_data.collect { |account_data| account_data["accountId"] } end - + ## # Change the set of accounts that this image is shared with. # The parameter is an array of account ID's. @@ -136,7 +136,7 @@ def shared_with_accounts= (account_id_list) # with the account that owns it, however, this leads to a rather odd state # where the image has allocated resources (that the account may be charged for) # but no way to delete those resources. For that reason this model - # always includes the account ID that owns the image in the list of + # always includes the account ID that owns the image in the list of # accounts the image will be shared with. my_account_id = self['accountId'] accounts_to_add.push(my_account_id) if !already_sharing_with.include?(my_account_id) && !accounts_to_add.include?(my_account_id) @@ -158,9 +158,9 @@ def shared_with_accounts= (account_id_list) def delete! self.service.deleteObject end - + ## - # Repeatedly poll the netwokr API until transactions related to this image + # Repeatedly poll the netwokr API until transactions related to this image # template are finished # # A template is not 'ready' until all the transactions on the template @@ -179,7 +179,7 @@ def wait_until_ready(max_trials, seconds_between_tries = 2) parent_ready = !(has_sl_property? :transactionId) || (self[:transactionId] == "") children_ready = (nil == self["children"].find { |child| child["transactionId"] != "" }) - ready = parent_ready && children_ready + ready = parent_ready && children_ready yield ready if block_given? num_trials = num_trials + 1 @@ -188,12 +188,12 @@ def wait_until_ready(max_trials, seconds_between_tries = 2) ready end - + # ModelBase protocol methods def service softlayer_client['Virtual_Guest_Block_Device_Template_Group'].object_with_id(self.id) end - + def softlayer_properties(object_mask = nil) self.service.object_mask(self.class.default_object_mask).getObject end @@ -342,7 +342,7 @@ def self.find_public_templates(options_hash = {}) # If no client can be found the routine will raise an error. # # The options may include the following keys - # * +:object_mask+ (string) - A object mask of properties, in addition to the default properties, that you wish to retrieve for the server + # * +:object_mask+ (string) - A object mask of properties, in addition to the default properties, that you wish to retrieve for the template def self.template_with_id(id, options_hash = {}) softlayer_client = options_hash[:client] || Client.default_client raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client @@ -358,10 +358,27 @@ def self.template_with_id(id, options_hash = {}) new(softlayer_client, template_data) end + ## + # Retrieve the image template with the given global ID + # + # The options parameter should contain: + # + # +:client+ - The client used to connect to the API + # + # If no client is given, then the routine will try to use Client.default_client + # If no client can be found the routine will raise an error. + # + # The options may include the following keys + # * +:object_mask+ (string) - A object mask of properties, in addition to the default properties, that you wish to retrieve for the template + def self.template_with_global_id(global_id, options_hash = {}) + templates = find_public_templates(options_hash.merge(:global_id => global_id)) + templates.empty? ? nil : templates[0] + end + protected def self.default_object_mask return "mask[id,accountId,name,note,globalIdentifier,datacenters,blockDevices,tagReferences,publicFlag,flexImageFlag,transactionId,children.transactionId]" end - end + end end \ No newline at end of file diff --git a/lib/softlayer/ModelBase.rb b/lib/softlayer/ModelBase.rb index af9e2d9..584eaf1 100644 --- a/lib/softlayer/ModelBase.rb +++ b/lib/softlayer/ModelBase.rb @@ -116,7 +116,7 @@ def to_ary() # def softlayer_properties(object_mask = nil) raise "Abstract method softlayer_properties in ModelBase was called" - end + end ## # The softlayer_hash stores the low-level information about an diff --git a/lib/softlayer/ProductPackage.rb b/lib/softlayer/ProductPackage.rb index 6f934c4..6b94807 100644 --- a/lib/softlayer/ProductPackage.rb +++ b/lib/softlayer/ProductPackage.rb @@ -45,7 +45,7 @@ class ProductPackage < ModelBase ## # The list of locations where this product package is available. - sl_attr :availableLocations + sl_attr :available_locations, 'availableLocations' ## # The set of product categories needed to make an order for this product package. @@ -105,15 +105,7 @@ def category(category_code) end def datacenter_options - availableLocations.collect { |location_data| location_data["location"]["name"] } - end - - ## - # Given a datacenter name that was returned by datacenter_options, use information - # in the package to retrieve a location id. - def location_id_for_datacenter_name(datacenter_name) - location_data = availableLocations.find { |location_data| location_data["location"]["name"] == datacenter_name } - location_data["locationId"] + available_locations.collect { |location_data| Datacenter::datacenter_named(location_data["location"]["name"], self.softlayer_client) }.compact end def service diff --git a/lib/softlayer/VirtualServer.rb b/lib/softlayer/VirtualServer.rb index 655b8ec..d613bbb 100644 --- a/lib/softlayer/VirtualServer.rb +++ b/lib/softlayer/VirtualServer.rb @@ -131,7 +131,7 @@ def upgrade_max_port_speed!(network_speed_in_Mbps) # # The routine returns the instance of SoftLayer::ImageTemplate that is # created. That image template will probably not be available immediately, however. - # You may use the wait_until_ready routine of SoftLayer::ImageTemplate to + # You may use the wait_until_ready routine of SoftLayer::ImageTemplate to # wait on it. # def capture_image(image_name, include_attached_storage = false, image_notes = nil) @@ -141,7 +141,7 @@ def capture_image(image_name, include_attached_storage = false, image_notes = ni disks = self.blockDevices.select(&disk_filter) self.service.createArchiveTransaction(image_name, disks, notes ? notes : "") if disks && !disks.empty? - + image_templates = SoftLayer::ImageTemplate.find_private_templates(:name => image_name) image_templates[0] if !image_templates.empty? end diff --git a/lib/softlayer/VirtualServerOrder.rb b/lib/softlayer/VirtualServerOrder.rb index 1684b62..228b6b9 100644 --- a/lib/softlayer/VirtualServerOrder.rb +++ b/lib/softlayer/VirtualServerOrder.rb @@ -24,9 +24,7 @@ class VirtualServerOrder # a virtual server #++ - # String, short name of the data center that will house the new virtual server (e.g. "dal05" or "sea01") - # Corresponds to +datacenter.name+ in the documentation for createObject. If not provided, the server will - # be provisioned in the first available data center. + # An instance of SoftLayer::Datacenter. The server will be provisioned in that Datacenter. attr_accessor :datacenter # String, The hostname to assign to the new server @@ -44,17 +42,18 @@ class VirtualServerOrder attr_accessor :memory #-- - # These two options are mutually exclusive, but one or the other must be provided. - # If you provide both, the image_global_id will be added to the order and the os_reference_code will be ignored + # These two options are mutually exclusive, but one of them must be provided. + # If you provide both, the image_template will be added to the order and the + # os_reference_code will be ignored #++ # String, An OS reference code for the operating system to install on the virtual server # Corresponds to +operatingSystemReferenceCode+ in the +createObject+ documentation attr_accessor :os_reference_code - # String, The globalIdentifier of a disk image to put on the newly created server - # Corresponds to +blockDeviceTemplateGroup.globalIdentifier+ in the +createObject+ documentation - attr_accessor :image_global_id + # An instance of the SoftLayer::ImageTemplate class. Represents the image template that should + # be installed on the server. + attr_accessor :image_template #-- # Optional attributes @@ -159,7 +158,7 @@ def virtual_guest_template template["dedicatedAccountHostOnlyFlag"] = true if @dedicated_host_only template["privateNetworkOnlyFlag"] = true if @private_network_only - template["datacenter"] = {"name" => @datacenter} if @datacenter + template["datacenter"] = {"name" => @datacenter.name} if @datacenter template['userData'] = [{'value' => @user_metadata}] if @user_metadata template['networkComponents'] = [{'maxSpeed'=> @max_port_speed}] if @max_port_speed template['postInstallScriptUri'] = @provision_script_URI.to_s if @provision_script_URI @@ -167,8 +166,8 @@ def virtual_guest_template template['primaryNetworkComponent'] = { "networkVlan" => { "id" => @public_vlan_id.to_i } } if @public_vlan_id template["primaryBackendNetworkComponent"] = { "networkVlan" => {"id" => @private_vlan_id.to_i } } if @private_vlan_id - if @image_global_id - template["blockDeviceTemplateGroup"] = {"globalIdentifier" => @image_global_id} + if @image_template + template["blockDeviceTemplateGroup"] = {"globalIdentifier" => @image_template.global_id} elsif @os_reference_code template["operatingSystemReferenceCode"] = @os_reference_code end @@ -213,7 +212,7 @@ def self.create_object_options(client = nil) ## # Return a list of values that are valid for the :datacenter attribute def self.datacenter_options(client = nil) - create_object_options(client)["datacenters"].collect { |datacenter_spec| datacenter_spec['template']['datacenter']["name"] }.uniq.sort! + create_object_options(client)["datacenters"].collect { |datacenter_spec| Datacenter.datacenter_named(datacenter_spec['template']['datacenter']['name'], client) }.uniq end ## diff --git a/spec/BareMetalServerOrder_Package_spec.rb b/spec/BareMetalServerOrder_Package_spec.rb index b8dcb2c..68df249 100644 --- a/spec/BareMetalServerOrder_Package_spec.rb +++ b/spec/BareMetalServerOrder_Package_spec.rb @@ -38,6 +38,11 @@ package = SoftLayer::ProductPackage.new(client, fixture_from_json("Product_Package")) SoftLayer::BareMetalServerOrder_Package.new(package, client) end + + let (:test_datacenter) do + client = SoftLayer::Client.new(:username => "fakeusername", :api_key => 'DEADBEEFBADF00D') + SoftLayer::Datacenter.new(client,'id' => 224092, 'name' => 'sng01') + end it 'places the package id from which it was ordered into the order template' do expect(test_order.hardware_order["packageId"]).to eq 32 @@ -45,7 +50,7 @@ it "places its :datacenter attribute into the order template" do expect(test_order.hardware_order["location"]).to be_nil - test_order.datacenter = "sng01" + test_order.datacenter = test_datacenter expect(test_order.hardware_order["location"]).to eq 224092 end @@ -108,7 +113,7 @@ def config_option_2.price_id allow(order_service).to receive(:call_softlayer_api_with_params) test_order = SoftLayer::BareMetalServerOrder_Package.new(package, client) - test_order.datacenter = 'sng01' + test_order.datacenter = test_datacenter test_order.hostname = "ruby-client-test" test_order.domain = "kitchentools.com" test_order.configuration_options = { 'category' => 123 } @@ -125,7 +130,7 @@ def config_option_2.price_id allow(order_service).to receive(:call_softlayer_api_with_params) test_order = SoftLayer::BareMetalServerOrder_Package.new(package, client) - test_order.datacenter = 'sng01' + test_order.datacenter = test_datacenter test_order.hostname = "ruby-client-test" test_order.domain = "kitchentools.com" test_order.configuration_options = { 'category' => 123 } @@ -142,7 +147,7 @@ def config_option_2.price_id allow(order_service).to receive(:call_softlayer_api_with_params) test_order = SoftLayer::BareMetalServerOrder_Package.new(package, client) - test_order.datacenter = 'sng01' + test_order.datacenter = test_datacenter test_order.hostname = "ruby-client-test" test_order.domain = "kitchentools.com" test_order.configuration_options = { 'category' => 123 } @@ -160,7 +165,7 @@ def config_option_2.price_id allow(order_service).to receive(:call_softlayer_api_with_params) test_order = SoftLayer::BareMetalServerOrder_Package.new(package, client) - test_order.datacenter = 'sng01' + test_order.datacenter = test_datacenter test_order.hostname = "ruby-client-test" test_order.domain = "kitchentools.com" test_order.configuration_options = { 'category' => 123 } diff --git a/spec/BareMetalServerOrder_spec.rb b/spec/BareMetalServerOrder_spec.rb index 7023fea..2de053a 100644 --- a/spec/BareMetalServerOrder_spec.rb +++ b/spec/BareMetalServerOrder_spec.rb @@ -4,8 +4,6 @@ # For licensing information see the LICENSE.md file in the project root. #++ - - $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) require 'rubygems' @@ -36,8 +34,10 @@ end it "places its :datacenter attribute into the order template" do + client = SoftLayer::Client.new(:username => "fakeusername", :api_key => 'DEADBEEFBADF00D') + expect(subject.hardware_instance_template["datacenter"]).to be_nil - subject.datacenter = "dal05" + subject.datacenter = SoftLayer::Datacenter.new(client, 'id' => 42, 'name' => "dal05") expect(subject.hardware_instance_template["datacenter"]).to eq({ "name" => "dal05" }) end @@ -211,11 +211,12 @@ client = SoftLayer::Client.new(:username => "fakeusername", :api_key => 'DEADBEEFBADF00D') virtual_guest_service = client["Hardware"] allow(virtual_guest_service).to receive(:call_softlayer_api_with_params) + fake_options = + allow(virtual_guest_service).to receive(:getCreateObjectOptions) { fixture_from_json("Hardware_createObjectOptions") } - fake_options = fixture_from_json("Hardware_createObjectOptions") - allow(virtual_guest_service).to receive(:getCreateObjectOptions) { - fake_options - } + location_service = client[:Location] + allow(location_service).to receive(:call_softlayer_api_with_params) + allow(location_service).to receive(:getDatacenters) {fixture_from_json("datacenter_locations")} client end @@ -225,7 +226,9 @@ end it "transmogrifies the datacenter options for the :datacenter attribute" do - expect(SoftLayer::BareMetalServerOrder.datacenter_options(client)).to eq ["ams01", "dal01", "dal05", "dal06", "sea01", "sjc01", "sng01", "wdc01"] + datacenter_options = SoftLayer::BareMetalServerOrder.datacenter_options(client) + datacenter_names = datacenter_options.map { |datacenter| datacenter.name }.sort + expect(datacenter_names).to eq ["ams01", "dal01", "dal05", "dal06", "sea01", "sjc01", "sng01", "wdc01"] end it "transmogrifies the processor create object options for the cores attribute" do diff --git a/spec/VirtualServerOrder_spec.rb b/spec/VirtualServerOrder_spec.rb index 25f596e..ae86c6c 100644 --- a/spec/VirtualServerOrder_spec.rb +++ b/spec/VirtualServerOrder_spec.rb @@ -4,8 +4,6 @@ # For licensing information see the LICENSE.md file in the project root. #++ - - $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) require 'rubygems' @@ -36,8 +34,9 @@ end it "places its :datacenter attribute into the order template" do + client = SoftLayer::Client.new(:username => "fakeusername", :api_key => 'DEADBEEFBADF00D') expect(subject.virtual_guest_template["datacenter"]).to be_nil - subject.datacenter = "dal05" + subject.datacenter = SoftLayer::Datacenter.new(client, 'id' => 42, 'name' => "dal05") expect(subject.virtual_guest_template["datacenter"]).to eq({ "name" => "dal05" }) end @@ -70,16 +69,18 @@ end it "places an image template global identifier in the template as blockDeviceTemplateGroup.globalIdentifier" do + client = SoftLayer::Client.new(:username => "fakeusername", :api_key => 'DEADBEEFBADF00D') expect(subject.virtual_guest_template["blockDeviceTemplateGroup"]).to be_nil - subject.image_global_id = "12345-abcd-eatatjoes" + subject.image_template = SoftLayer::ImageTemplate.new(client, 'id' => 42, 'globalIdentifier' => '12345-abcd-eatatjoes'); expect(subject.virtual_guest_template['blockDeviceTemplateGroup']).to eq({'globalIdentifier' => '12345-abcd-eatatjoes'}) end - it "allows an image global id to override an os reference code when both are provided" do + it "allows an image template to override an os reference code when both are provided" do expect(subject.virtual_guest_template["blockDeviceTemplateGroup"]).to be_nil expect(subject.virtual_guest_template["operatingSystemReferenceCode"]).to be_nil - subject.image_global_id = "12345-abcd-eatatjoes" + client = SoftLayer::Client.new(:username => "fakeusername", :api_key => 'DEADBEEFBADF00D') + subject.image_template = SoftLayer::ImageTemplate.new(client, 'id' => 42, 'globalIdentifier' => '12345-abcd-eatatjoes'); subject.os_reference_code = 'UBUNTU_12_64' expect(subject.virtual_guest_template['blockDeviceTemplateGroup']).to eq({'globalIdentifier' => '12345-abcd-eatatjoes'}) @@ -243,7 +244,7 @@ describe "methods returning available options for attributes" do let (:client) do client = SoftLayer::Client.new(:username => "fakeusername", :api_key => 'DEADBEEFBADF00D') - virtual_guest_service = client["Virtual_Guest"] + virtual_guest_service = client[:Virtual_Guest] allow(virtual_guest_service).to receive(:call_softlayer_api_with_params) fake_options = fixture_from_json("Virtual_Guest_createObjectOptions") @@ -264,7 +265,10 @@ end it "transmogrifies the datacenter options for the cores attribute" do - expect(SoftLayer::VirtualServerOrder.datacenter_options(client)).to eq ["ams01", "dal01", "dal05", "dal06", "sea01", "sjc01", "sng01", "wdc01"] + datacenter_options = SoftLayer::VirtualServerOrder.datacenter_options(client) + datacenter_names = datacenter_options.map { |datacenter| datacenter.name } + + expect(datacenter_names.sort).to eq ["ams01", "dal01", "dal05", "dal06", "sea01", "sjc01", "sng01", "wdc01"] end it "transmogrifies the processor options for the cores attribute" do diff --git a/spec/fixtures/datacenter_locations.json b/spec/fixtures/datacenter_locations.json new file mode 100644 index 0000000..294e4d4 --- /dev/null +++ b/spec/fixtures/datacenter_locations.json @@ -0,0 +1 @@ +[{"id":265592,"longName":"Amsterdam 1","name":"ams01"},{"id":358698,"longName":"Ashburn 3","name":"wdc03"},{"id":3,"longName":"Dallas 1","name":"dal01"},{"id":154770,"longName":"Dallas 2","name":"dal02"},{"id":167092,"longName":"Dallas 4","name":"dal04"},{"id":138124,"longName":"Dallas 5","name":"dal05"},{"id":154820,"longName":"Dallas 6","name":"dal06"},{"id":142776,"longName":"Dallas 7","name":"dal07"},{"id":352494,"longName":"Hong Kong 2","name":"hkg02"},{"id":142775,"longName":"Houston 2","name":"hou02"},{"id":358694,"longName":"London 2","name":"lon02"},{"id":168642,"longName":"San Jose 1","name":"sjc01"},{"id":18171,"longName":"Seattle","name":"sea01"},{"id":224092,"longName":"Singapore 1","name":"sng01"},{"id":448994,"longName":"Toronto 1","name":"tor01"},{"id":37473,"longName":"Washington 1","name":"wdc01"}] \ No newline at end of file From 3f0a4bdfc101df6aa9434d3bdd699bf73a18b5f5 Mon Sep 17 00:00:00 2001 From: Scott Thompson Date: Wed, 9 Jul 2014 11:24:34 -0500 Subject: [PATCH 14/32] Moved the open_tickets to the Account rather than the Ticket class --- CHANGELOG.textile | 1 + lib/softlayer/Account.rb | 15 +++++++++++++-- lib/softlayer/Ticket.rb | 27 +-------------------------- spec/Account_spec.rb | 17 +++++++++++++++-- spec/Ticket_spec.rb | 13 ------------- 5 files changed, 30 insertions(+), 43 deletions(-) diff --git a/CHANGELOG.textile b/CHANGELOG.textile index 2db624c..4bcd5f0 100644 --- a/CHANGELOG.textile +++ b/CHANGELOG.textile @@ -3,6 +3,7 @@ * Substantially rewrote the ObjectFilter class. ObjectFilters used to be hashes which made it easy to manipulate their content incorrectly. The new implementation has a strict interface that makes it harder to manipulate filters incorrectly. * Added a model for Virtual Server Image Templates (SoftLayer::ImageTemplate) - VirtualServerOrder now requires an instance of this class rather than allowing you to provide just the global_id of an image * Added a model for data centers (SoftLayer::Datacenter). Bare Metal, Bare Metal Package, and Virtual server orders now use an instance of Datacenter to identify where their servers will be provisioned. The routines in those classes which used to provide lists of valid data center names now return data center objects. +* The routine to retreive the open tickets on an account has been moved from the Ticket class to a dynamic property of an account *2.1.1* * Virtual server upgrades no longer raise exceptions diff --git a/lib/softlayer/Account.rb b/lib/softlayer/Account.rb index 1dec02b..708c033 100644 --- a/lib/softlayer/Account.rb +++ b/lib/softlayer/Account.rb @@ -4,8 +4,6 @@ # For licensing information see the LICENSE.md file in the project root. #++ - - module SoftLayer class Account < SoftLayer::ModelBase include ::SoftLayer::DynamicAttribute @@ -111,6 +109,19 @@ class Account < SoftLayer::ModelBase end end + sl_dynamic_attr :open_tickets do |open_tickets| + open_tickets.should_update? do + @last_open_tickets_update ||= Time.at(0) + (Time.now - @last_open_tickets_update) > 5 * 60 # update every 5 minutes + end + + open_tickets.to_update do + @last_open_tickets_update ||= Time.now + open_tickets_data = self.service.object_mask(SoftLayer::Ticket.default_object_mask).getOpenTickets + open_tickets_data.collect { |ticket_data| SoftLayer::Ticket.new(self.softlayer_client, ticket_data) } + end + end + def service softlayer_client["Account"].object_with_id(self.id) end diff --git a/lib/softlayer/Ticket.rb b/lib/softlayer/Ticket.rb index d3bf0ac..278d6f2 100644 --- a/lib/softlayer/Ticket.rb +++ b/lib/softlayer/Ticket.rb @@ -4,8 +4,6 @@ # For licensing information see the LICENSE.md file in the project root. #++ - - module SoftLayer class Ticket < SoftLayer::ModelBase @@ -85,7 +83,7 @@ def self.default_object_mask 'awaitingUserResponseFlag', # This comes in from the server as a Boolean value 'serverAdministrationFlag', # This comes in from the server as an integer :-( ] - } + }.to_sl_object_mask end ## @@ -104,29 +102,6 @@ def self.ticket_subjects(client = nil) @ticket_subjects end - ## - # Returns the set of currently open tickets - # - # Options should contain: - # - # +:client+ - the client in which to search for the ticket - # - # If a client is not provided then the routine will search Client::default_client - # If Client::default_client is also nil the routine will raise an error. - def self.open_tickets(options = {}) - softlayer_client = options[:client] || Client.default_client - raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client - - if options.has_key?(:object_mask) - object_mask = options[:object_mask] - else - object_mask = default_object_mask.to_sl_object_mask - end - - open_tickets_data = softlayer_client["Account"].object_mask(object_mask).getOpenTickets - open_tickets_data.collect { |ticket_data| new(softlayer_client, ticket_data) } - end - ## # Find the ticket with the given ID and return it # diff --git a/spec/Account_spec.rb b/spec/Account_spec.rb index 0d3a2c8..6230860 100644 --- a/spec/Account_spec.rb +++ b/spec/Account_spec.rb @@ -4,8 +4,6 @@ # For licensing information see the LICENSE.md file in the project root. #++ - - $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) require 'rubygems' @@ -81,6 +79,21 @@ expect(test_account.officePhone).to eq "555.123.4567" end end + + it "fetches a list of open tickets" do + mock_client = SoftLayer::Client.new(:username => "fakeuser", :api_key => "fake_api_key") + account_service = mock_client["Account"] + + expect(account_service).to receive(:call_softlayer_api_with_params).with(:getOpenTickets, instance_of(SoftLayer::APIParameterFilter),[]) do + fixture_from_json("test_tickets") + end + + test_account = SoftLayer::Account.new(mock_client, fixture_from_json("test_account")) + open_tickets = nil + expect { open_tickets = test_account.open_tickets }.to_not raise_error + ticket_ids = open_tickets.collect { |ticket| ticket.id } + expect(ticket_ids.sort).to eq [12345, 12346, 12347, 12348, 12349].sort + end describe "relationship to servers" do it "should respond to a request for servers" do diff --git a/spec/Ticket_spec.rb b/spec/Ticket_spec.rb index 5768909..3301a41 100644 --- a/spec/Ticket_spec.rb +++ b/spec/Ticket_spec.rb @@ -4,8 +4,6 @@ # For licensing information see the LICENSE.md file in the project root. #++ - - $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) require 'rubygems' @@ -19,17 +17,6 @@ SoftLayer::Ticket.instance_eval { @ticket_subjects = nil } end - it "fetches a list of open tickets" do - mock_client = SoftLayer::Client.new(:username => "fakeuser", :api_key => "fake_api_key") - account_service = mock_client["Account"] - - expect(account_service).to receive(:call_softlayer_api_with_params).with(:getOpenTickets, instance_of(SoftLayer::APIParameterFilter),[]) do - fixture_from_json("test_tickets") - end - - SoftLayer::Ticket.open_tickets(:client => mock_client) - end - it "retrieves ticket subjects from API once" do fakeTicketSubjects = fixture_from_json("ticket_subjects") From 37b0f03ac985e639426091b68ec65f3764297d98 Mon Sep 17 00:00:00 2001 From: Scott Thompson Date: Wed, 9 Jul 2014 11:27:02 -0500 Subject: [PATCH 15/32] Cleaned up extraneous whitespace --- lib/softlayer/BareMetalServerOrder.rb | 2 -- lib/softlayer/BareMetalServerOrder_Package.rb | 4 +--- lib/softlayer/Config.rb | 2 -- lib/softlayer/DynamicAttribute.rb | 2 -- lib/softlayer/ImageTemplate.rb | 2 +- lib/softlayer/ModelBase.rb | 2 -- lib/softlayer/ObjectFilter.rb | 2 -- lib/softlayer/ObjectMaskParser.rb | 2 -- lib/softlayer/ObjectMaskProperty.rb | 2 -- lib/softlayer/ObjectMaskToken.rb | 2 -- lib/softlayer/ObjectMaskTokenizer.rb | 2 -- lib/softlayer/ProductItemCategory.rb | 2 -- lib/softlayer/ProductPackage.rb | 2 -- lib/softlayer/VirtualServer.rb | 2 -- lib/softlayer/VirtualServerOrder.rb | 4 +--- lib/softlayer/object_mask_helpers.rb | 2 -- rakefile | 2 -- softlayer_api.gemspec | 2 +- spec/APIParameterFilter_spec.rb | 2 -- spec/Account_spec.rb | 2 +- spec/BareMetalServerOrder_Package_spec.rb | 2 +- spec/BareMetalServerOrder_spec.rb | 4 ++-- spec/Config_spec.rb | 2 -- spec/DynamicAttribute_spec.rb | 2 -- spec/ModelBase_spec.rb | 2 -- spec/ObjectMaskParser_spec.rb | 2 -- spec/ObjectMaskProperty_spec.rb | 2 -- spec/ProductPackage_spec.rb | 2 -- spec/Server_spec.rb | 2 -- spec/Service_spec.rb | 2 -- spec/VirtualServer_spec.rb | 2 -- spec/XMLRPC_Convert_spec.rb | 2 -- spec/object_mask_helpers_spec.rb | 2 -- 33 files changed, 8 insertions(+), 64 deletions(-) diff --git a/lib/softlayer/BareMetalServerOrder.rb b/lib/softlayer/BareMetalServerOrder.rb index 5942b43..f1fe47f 100644 --- a/lib/softlayer/BareMetalServerOrder.rb +++ b/lib/softlayer/BareMetalServerOrder.rb @@ -4,8 +4,6 @@ # For licensing information see the LICENSE.md file in the project root. #++ - - module SoftLayer # # This class allows you to order a Bare Metal Server by providing diff --git a/lib/softlayer/BareMetalServerOrder_Package.rb b/lib/softlayer/BareMetalServerOrder_Package.rb index aba93c4..80c7f16 100644 --- a/lib/softlayer/BareMetalServerOrder_Package.rb +++ b/lib/softlayer/BareMetalServerOrder_Package.rb @@ -4,8 +4,6 @@ # For licensing information see the LICENSE.md file in the project root. #++ - - module SoftLayer # # This class is used to order a hardware server using a product package. @@ -28,7 +26,7 @@ module SoftLayer class BareMetalServerOrder_Package < Server # The following properties are required in a server order. - # The product package object (an instance of SoftLayer::ProductPackage) identifying the base + # The product package object (an instance of SoftLayer::ProductPackage) identifying the base # configuration for the server. A list of Bare Metal Server product packages is returned by # SoftLayer::ProductPackage.bare_metal_server_packages attr_reader :package diff --git a/lib/softlayer/Config.rb b/lib/softlayer/Config.rb index 14c46d0..e92af69 100644 --- a/lib/softlayer/Config.rb +++ b/lib/softlayer/Config.rb @@ -4,8 +4,6 @@ # For licensing information see the LICENSE.md file in the project root. #++ - - require 'configparser' module SoftLayer diff --git a/lib/softlayer/DynamicAttribute.rb b/lib/softlayer/DynamicAttribute.rb index 43c91db..e0ee1cd 100644 --- a/lib/softlayer/DynamicAttribute.rb +++ b/lib/softlayer/DynamicAttribute.rb @@ -4,8 +4,6 @@ # For licensing information see the LICENSE.md file in the project root. #++ - - module SoftLayer ## diff --git a/lib/softlayer/ImageTemplate.rb b/lib/softlayer/ImageTemplate.rb index d38be63..e780b87 100644 --- a/lib/softlayer/ImageTemplate.rb +++ b/lib/softlayer/ImageTemplate.rb @@ -380,5 +380,5 @@ def self.template_with_global_id(global_id, options_hash = {}) def self.default_object_mask return "mask[id,accountId,name,note,globalIdentifier,datacenters,blockDevices,tagReferences,publicFlag,flexImageFlag,transactionId,children.transactionId]" end - end + end end \ No newline at end of file diff --git a/lib/softlayer/ModelBase.rb b/lib/softlayer/ModelBase.rb index 584eaf1..93ba406 100644 --- a/lib/softlayer/ModelBase.rb +++ b/lib/softlayer/ModelBase.rb @@ -4,8 +4,6 @@ # For licensing information see the LICENSE.md file in the project root. #++ - - module SoftLayer ## # The SoftLayer Gem defines an Object Hierarchy representing entities in diff --git a/lib/softlayer/ObjectFilter.rb b/lib/softlayer/ObjectFilter.rb index 176cbb6..b5e2746 100644 --- a/lib/softlayer/ObjectFilter.rb +++ b/lib/softlayer/ObjectFilter.rb @@ -4,8 +4,6 @@ # For licensing information see the LICENSE.md file in the project root. #++ - - module SoftLayer ## # An ObjectFilter is a tool that, when passed to the SoftLayer API diff --git a/lib/softlayer/ObjectMaskParser.rb b/lib/softlayer/ObjectMaskParser.rb index 3e5b366..3d4b1e8 100644 --- a/lib/softlayer/ObjectMaskParser.rb +++ b/lib/softlayer/ObjectMaskParser.rb @@ -4,8 +4,6 @@ # For licensing information see the LICENSE.md file in the project root. #++ - - require "softlayer/ObjectMaskTokenizer" require "softlayer/ObjectMaskProperty" diff --git a/lib/softlayer/ObjectMaskProperty.rb b/lib/softlayer/ObjectMaskProperty.rb index 957f182..272c41b 100644 --- a/lib/softlayer/ObjectMaskProperty.rb +++ b/lib/softlayer/ObjectMaskProperty.rb @@ -4,8 +4,6 @@ # For licensing information see the LICENSE.md file in the project root. #++ - - module SoftLayer # # A class representing a SoftLayer Object's property as represented diff --git a/lib/softlayer/ObjectMaskToken.rb b/lib/softlayer/ObjectMaskToken.rb index 57720b5..854731d 100644 --- a/lib/softlayer/ObjectMaskToken.rb +++ b/lib/softlayer/ObjectMaskToken.rb @@ -4,8 +4,6 @@ # For licensing information see the LICENSE.md file in the project root. #++ - - module SoftLayer # # This class is an implementation detail of the Object Mask Parser diff --git a/lib/softlayer/ObjectMaskTokenizer.rb b/lib/softlayer/ObjectMaskTokenizer.rb index b852af7..af12096 100644 --- a/lib/softlayer/ObjectMaskTokenizer.rb +++ b/lib/softlayer/ObjectMaskTokenizer.rb @@ -4,8 +4,6 @@ # For licensing information see the LICENSE.md file in the project root. #++ - - require 'softlayer/ObjectMaskToken' require 'strscan' diff --git a/lib/softlayer/ProductItemCategory.rb b/lib/softlayer/ProductItemCategory.rb index 3df1843..bb6cfe3 100644 --- a/lib/softlayer/ProductItemCategory.rb +++ b/lib/softlayer/ProductItemCategory.rb @@ -4,8 +4,6 @@ # For licensing information see the LICENSE.md file in the project root. #++ - - module SoftLayer # This struct represents a configuration option that can be included in # a product order. Strictly speaking the only information required for diff --git a/lib/softlayer/ProductPackage.rb b/lib/softlayer/ProductPackage.rb index 6b94807..5ab53c3 100644 --- a/lib/softlayer/ProductPackage.rb +++ b/lib/softlayer/ProductPackage.rb @@ -4,8 +4,6 @@ # For licensing information see the LICENSE.md file in the project root. #++ - - require 'json' module SoftLayer diff --git a/lib/softlayer/VirtualServer.rb b/lib/softlayer/VirtualServer.rb index d613bbb..2b85201 100644 --- a/lib/softlayer/VirtualServer.rb +++ b/lib/softlayer/VirtualServer.rb @@ -4,8 +4,6 @@ # For licensing information see the LICENSE.md file in the project root. #++ - - require 'time' module SoftLayer diff --git a/lib/softlayer/VirtualServerOrder.rb b/lib/softlayer/VirtualServerOrder.rb index 228b6b9..b827131 100644 --- a/lib/softlayer/VirtualServerOrder.rb +++ b/lib/softlayer/VirtualServerOrder.rb @@ -4,8 +4,6 @@ # For licensing information see the LICENSE.md file in the project root. #++ - - module SoftLayer # # VirtualServerOrder orders virtual servers using SoftLayer_Virtual_Guest::createObject. @@ -43,7 +41,7 @@ class VirtualServerOrder #-- # These two options are mutually exclusive, but one of them must be provided. - # If you provide both, the image_template will be added to the order and the + # If you provide both, the image_template will be added to the order and the # os_reference_code will be ignored #++ diff --git a/lib/softlayer/object_mask_helpers.rb b/lib/softlayer/object_mask_helpers.rb index 7e070b9..7f70faf 100644 --- a/lib/softlayer/object_mask_helpers.rb +++ b/lib/softlayer/object_mask_helpers.rb @@ -4,8 +4,6 @@ # For licensing information see the LICENSE.md file in the project root. #++ - - ## # This extension to the Hash class to allows object masks to be constructed # from built-in Ruby types and converted to object masks strings for presentation diff --git a/rakefile b/rakefile index 1df680b..6a1a4ce 100644 --- a/rakefile +++ b/rakefile @@ -4,8 +4,6 @@ # For licensing information see the LICENSE.md file in the project root. #++ - - $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__))) require 'rubygems' diff --git a/softlayer_api.gemspec b/softlayer_api.gemspec index 00746e0..831fbe3 100644 --- a/softlayer_api.gemspec +++ b/softlayer_api.gemspec @@ -27,5 +27,5 @@ Gem::Specification.new do |s| s.add_development_dependency 'rspec' s.add_development_dependency 'rdoc', '>=2.4.2' s.add_development_dependency 'json', '~> 1.8', '>= 1.8.1' - s.add_development_dependency 'coveralls' + s.add_development_dependency 'coveralls' end diff --git a/spec/APIParameterFilter_spec.rb b/spec/APIParameterFilter_spec.rb index 56dc1bc..9e46c9e 100644 --- a/spec/APIParameterFilter_spec.rb +++ b/spec/APIParameterFilter_spec.rb @@ -4,8 +4,6 @@ # For licensing information see the LICENSE.md file in the project root. #++ - - $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) require 'rubygems' diff --git a/spec/Account_spec.rb b/spec/Account_spec.rb index 6230860..385a8bf 100644 --- a/spec/Account_spec.rb +++ b/spec/Account_spec.rb @@ -79,7 +79,7 @@ expect(test_account.officePhone).to eq "555.123.4567" end end - + it "fetches a list of open tickets" do mock_client = SoftLayer::Client.new(:username => "fakeuser", :api_key => "fake_api_key") account_service = mock_client["Account"] diff --git a/spec/BareMetalServerOrder_Package_spec.rb b/spec/BareMetalServerOrder_Package_spec.rb index 68df249..977b806 100644 --- a/spec/BareMetalServerOrder_Package_spec.rb +++ b/spec/BareMetalServerOrder_Package_spec.rb @@ -38,7 +38,7 @@ package = SoftLayer::ProductPackage.new(client, fixture_from_json("Product_Package")) SoftLayer::BareMetalServerOrder_Package.new(package, client) end - + let (:test_datacenter) do client = SoftLayer::Client.new(:username => "fakeusername", :api_key => 'DEADBEEFBADF00D') SoftLayer::Datacenter.new(client,'id' => 224092, 'name' => 'sng01') diff --git a/spec/BareMetalServerOrder_spec.rb b/spec/BareMetalServerOrder_spec.rb index 2de053a..e75c89c 100644 --- a/spec/BareMetalServerOrder_spec.rb +++ b/spec/BareMetalServerOrder_spec.rb @@ -35,7 +35,7 @@ it "places its :datacenter attribute into the order template" do client = SoftLayer::Client.new(:username => "fakeusername", :api_key => 'DEADBEEFBADF00D') - + expect(subject.hardware_instance_template["datacenter"]).to be_nil subject.datacenter = SoftLayer::Datacenter.new(client, 'id' => 42, 'name' => "dal05") expect(subject.hardware_instance_template["datacenter"]).to eq({ "name" => "dal05" }) @@ -211,7 +211,7 @@ client = SoftLayer::Client.new(:username => "fakeusername", :api_key => 'DEADBEEFBADF00D') virtual_guest_service = client["Hardware"] allow(virtual_guest_service).to receive(:call_softlayer_api_with_params) - fake_options = + fake_options = allow(virtual_guest_service).to receive(:getCreateObjectOptions) { fixture_from_json("Hardware_createObjectOptions") } location_service = client[:Location] diff --git a/spec/Config_spec.rb b/spec/Config_spec.rb index 33e049d..668ab2d 100644 --- a/spec/Config_spec.rb +++ b/spec/Config_spec.rb @@ -4,8 +4,6 @@ # For licensing information see the LICENSE.md file in the project root. #++ - - $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) require 'rubygems' diff --git a/spec/DynamicAttribute_spec.rb b/spec/DynamicAttribute_spec.rb index 0007bfb..738f3d2 100644 --- a/spec/DynamicAttribute_spec.rb +++ b/spec/DynamicAttribute_spec.rb @@ -4,8 +4,6 @@ # For licensing information see the LICENSE.md file in the project root. #++ - - $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) require 'rubygems' diff --git a/spec/ModelBase_spec.rb b/spec/ModelBase_spec.rb index a141355..120e7c7 100644 --- a/spec/ModelBase_spec.rb +++ b/spec/ModelBase_spec.rb @@ -4,8 +4,6 @@ # For licensing information see the LICENSE.md file in the project root. #++ - - $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) require 'rubygems' diff --git a/spec/ObjectMaskParser_spec.rb b/spec/ObjectMaskParser_spec.rb index 1f3fa1e..56c378f 100644 --- a/spec/ObjectMaskParser_spec.rb +++ b/spec/ObjectMaskParser_spec.rb @@ -4,8 +4,6 @@ # For licensing information see the LICENSE.md file in the project root. #++ - - $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) require 'rubygems' diff --git a/spec/ObjectMaskProperty_spec.rb b/spec/ObjectMaskProperty_spec.rb index b6ecd56..44d09af 100644 --- a/spec/ObjectMaskProperty_spec.rb +++ b/spec/ObjectMaskProperty_spec.rb @@ -4,8 +4,6 @@ # For licensing information see the LICENSE.md file in the project root. #++ - - $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) require 'rubygems' diff --git a/spec/ProductPackage_spec.rb b/spec/ProductPackage_spec.rb index 9cf5958..1e5d4df 100644 --- a/spec/ProductPackage_spec.rb +++ b/spec/ProductPackage_spec.rb @@ -4,8 +4,6 @@ # For licensing information see the LICENSE.md file in the project root. #++ - - $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) require 'rubygems' diff --git a/spec/Server_spec.rb b/spec/Server_spec.rb index 7d0e56c..d40d5b9 100644 --- a/spec/Server_spec.rb +++ b/spec/Server_spec.rb @@ -4,8 +4,6 @@ # For licensing information see the LICENSE.md file in the project root. #++ - - $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) require 'rubygems' diff --git a/spec/Service_spec.rb b/spec/Service_spec.rb index 83aa105..647824c 100644 --- a/spec/Service_spec.rb +++ b/spec/Service_spec.rb @@ -4,8 +4,6 @@ # For licensing information see the LICENSE.md file in the project root. #++ - - $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) require 'rubygems' diff --git a/spec/VirtualServer_spec.rb b/spec/VirtualServer_spec.rb index ae17d26..fe8ba3a 100644 --- a/spec/VirtualServer_spec.rb +++ b/spec/VirtualServer_spec.rb @@ -4,8 +4,6 @@ # For licensing information see the LICENSE.md file in the project root. #++ - - $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) require 'rubygems' diff --git a/spec/XMLRPC_Convert_spec.rb b/spec/XMLRPC_Convert_spec.rb index e6144f7..7d93496 100644 --- a/spec/XMLRPC_Convert_spec.rb +++ b/spec/XMLRPC_Convert_spec.rb @@ -4,8 +4,6 @@ # For licensing information see the LICENSE.md file in the project root. #++ - - $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) require 'rubygems' diff --git a/spec/object_mask_helpers_spec.rb b/spec/object_mask_helpers_spec.rb index 85a0f40..b0b1a4a 100644 --- a/spec/object_mask_helpers_spec.rb +++ b/spec/object_mask_helpers_spec.rb @@ -4,8 +4,6 @@ # For licensing information see the LICENSE.md file in the project root. #++ - - $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) require 'rubygems' From 498459f0cfa279a5e75915f857e469bf9f072014 Mon Sep 17 00:00:00 2001 From: Scott Thompson Date: Wed, 9 Jul 2014 14:10:10 -0500 Subject: [PATCH 16/32] Added unit tests for datacenter class --- spec/Datacenter_spec.rb | 50 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 spec/Datacenter_spec.rb diff --git a/spec/Datacenter_spec.rb b/spec/Datacenter_spec.rb new file mode 100644 index 0000000..a229588 --- /dev/null +++ b/spec/Datacenter_spec.rb @@ -0,0 +1,50 @@ +# +# Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the 'Software'), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +$LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), '../lib')) + +require 'rubygems' +require 'softlayer_api' +require 'rspec' + +describe SoftLayer::Datacenter do + let (:mock_client) do + mock_client = SoftLayer::Client.new(:username => "fakeuser", :api_key => "DEADBEEFBADF00D") + allow(mock_client[:Location]).to receive(:call_softlayer_api_with_params) do |method_name, parameters, args| + fixture_from_json('datacenter_locations.json') + end + + mock_client + end + + it "retrieves a list of datacenters" do + datacenters = SoftLayer::Datacenter.datacenters(mock_client) + names = datacenters.collect { |datacenter| datacenter.name } + expect(names.sort).to eq ["ams01", "dal01", "dal02", "dal04", "dal05", "dal06", "dal07", "hkg02", "hou02", "lon02", "sea01", "sjc01", "sng01", "tor01", "wdc01", "wdc03"] + end + + it "retrieves a particular datacenter by name" do + dal05 = SoftLayer::Datacenter.datacenter_named("dal05", mock_client) + expect(dal05.name).to eq "dal05" + expect(dal05.id).to be 138124 + end +end From 40e5c8fdbc458234c29decf3bd58ea4a9fde36a1 Mon Sep 17 00:00:00 2001 From: Scott Thompson Date: Mon, 21 Jul 2014 10:09:57 -0500 Subject: [PATCH 17/32] An empty commit to add issue-fix labels for GitHub's benefit. To wit: Fixes #37 Fixes #24 Fixes #40 Fixes #41 From 8ccc78786012605b2046d82c5b16fe5ee0484cc0 Mon Sep 17 00:00:00 2001 From: Scott Thompson Date: Mon, 21 Jul 2014 11:14:46 -0500 Subject: [PATCH 18/32] Updates to the documentation --- README.md | 4 ++++ lib/softlayer/ObjectFilter.rb | 2 ++ lib/softlayer/ProductItemCategory.rb | 3 +-- lib/softlayer/base.rb | 2 ++ 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6359ff3..5b61fa1 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,10 @@ This software is written by the SoftLayer Development Team [sldn@softlayer.com]( Please join us in the [SoftLayer Developer Network forums](http://forums.softlayer.com/forum/softlayer-developer-network) +# Contributer License Agreement + +Contributions to the softlayer-ruby project require the submission of a contributer license agreement. Please generate the documentation and carefully refer to the Contribution Guide to participate. + # Copyright and License The `softlayer_api` Ruby Gem and it's source files are Copyright © 2010-2014 [SoftLayer Technologies, Inc](http://www.softlayer.com/). The software is provided under an MIT license. Details of that license can be found in the embedded LICENSE.md file. diff --git a/lib/softlayer/ObjectFilter.rb b/lib/softlayer/ObjectFilter.rb index b5e2746..791876a 100644 --- a/lib/softlayer/ObjectFilter.rb +++ b/lib/softlayer/ObjectFilter.rb @@ -95,6 +95,8 @@ def when_it(criteria) end end # ObjectFilter + ## + # :nodoc: OBJECT_FILTER_OPERATORS = [ '*=', # Contains (ignoring case) '^=', # Begins with (ignoring case) diff --git a/lib/softlayer/ProductItemCategory.rb b/lib/softlayer/ProductItemCategory.rb index bb6cfe3..5c574d4 100644 --- a/lib/softlayer/ProductItemCategory.rb +++ b/lib/softlayer/ProductItemCategory.rb @@ -10,8 +10,7 @@ module SoftLayer # the product order is the price_id, the rest of the information is provided # to make the object friendly to humans who may be searching for the # meaning of a given price_id. - ProductConfigurationOption = Struct.new(:price_id, :description, :capacity, :units, :setupFee, :laborFee, :oneTimeFee, :recurringFee, :hourlyRecurringFee) do - + class ProductConfigurationOption < Struct.new(:price_id, :description, :capacity, :units, :setupFee, :laborFee, :oneTimeFee, :recurringFee, :hourlyRecurringFee) # Is it evil, or just incongruous to give methods to a struct? # returns true if the configurtion option has no fees associated with it. diff --git a/lib/softlayer/base.rb b/lib/softlayer/base.rb index 527b28a..6f012a7 100644 --- a/lib/softlayer/base.rb +++ b/lib/softlayer/base.rb @@ -10,6 +10,8 @@ # The SoftLayer module provides a namespace for SoftLayer code. # module SoftLayer + # The version number (including major, minor, and bugfix numbers) + # This should change in accordance with the concept of Semantic Versioning VERSION = "3.0.0" # version history in the CHANGELOG.textile file at the root of the source # The base URL of the SoftLayer API available to the public internet. From f594af1d85dd8e65380cfdd8c6c0f4bc4155d4da Mon Sep 17 00:00:00 2001 From: Scott Thompson Date: Wed, 23 Jul 2014 10:43:39 -0500 Subject: [PATCH 19/32] Added a new class to perform Virtual Server Upgrades. By splitting this out into a separate class we get two benefits: 1 You can upgrade more than one attribute of the server at a time so you don't have to wait on separate transactions for each upgrade 2 It gets information about product ordering out of the Virtual Server class and into its own class. --- lib/softlayer/VirtualServer.rb | 72 ---------- lib/softlayer/VirtualServerUpgradeOrder.rb | 117 ++++++++++++++++ lib/softlayer_api.rb | 9 +- spec/VirtualServerUpgradeOrder_spec.rb | 154 +++++++++++++++++++++ spec/VirtualServer_spec.rb | 25 ---- 5 files changed, 277 insertions(+), 100 deletions(-) create mode 100644 lib/softlayer/VirtualServerUpgradeOrder.rb create mode 100644 spec/VirtualServerUpgradeOrder_spec.rb diff --git a/lib/softlayer/VirtualServer.rb b/lib/softlayer/VirtualServer.rb index 2b85201..8453124 100644 --- a/lib/softlayer/VirtualServer.rb +++ b/lib/softlayer/VirtualServer.rb @@ -4,8 +4,6 @@ # For licensing information see the LICENSE.md file in the project root. #++ -require 'time' - module SoftLayer ## # Instance of this class represent servers that are virtual machines in the @@ -74,49 +72,6 @@ def cancel! self.service.deleteObject() end - ## - # This routine submits an order to upgrade the cpu count of the virtual server. - # The order may result in additional charges being applied to SoftLayer account - # - # This routine can also "downgrade" servers (set their cpu count lower) - # - # The routine returns true if the order is placed and false if it is not - # - def upgrade_cores!(num_cores) - upgrade_item_price = _item_price_in_category("guest_core", num_cores) - _order_upgrade_item!(upgrade_item_price) if upgrade_item_price - nil != upgrade_item_price - end - - ## - # This routine submits an order to change the RAM available to the virtual server. - # Pass in the desired amount of RAM for the server in Gigabytes - # - # The order may result in additional charges being applied to SoftLayer account - # - # The routine returns true if the order is placed and false if it is not - # - def upgrade_RAM!(ram_in_GB) - upgrade_item_price = _item_price_in_category("ram", ram_in_GB) - _order_upgrade_item!(upgrade_item_price) if upgrade_item_price - nil != upgrade_item_price - end - - ## - # This routine submits an order to change the maximum nic speed of the server - # Pass in the desired speed in Megabits per second (typically 10, 100, or 1000) - # (since you may choose a slower speed this routine can also be used for "downgrades") - # - # The order may result in additional charges being applied to SoftLayer account - # - # The routine returns true if the order is placed and false if it is not - # - def upgrade_max_port_speed!(network_speed_in_Mbps) - upgrade_item_price = _item_price_in_category("port_speed", network_speed_in_Mbps) - _order_upgrade_item!(upgrade_item_price) if upgrade_item_price - nil != upgrade_item_price - end - ## # Capture a disk image of this virtual server for use with other servers. # @@ -354,32 +309,5 @@ def self.default_object_mask def service return softlayer_client["Virtual_Guest"].object_with_id(self.id) end - - private - - ## - # Searches through the upgrade items pricess known to this server for the one that is in a particular category - # and whose capacity matches the value given. Returns the item_price or nil - # - def _item_price_in_category(which_category, capacity) - item_prices_in_category = self.upgrade_options.select { |item_price| item_price["categories"].find { |category| category["categoryCode"] == which_category } } - item_prices_in_category.find { |ram_item| ram_item["item"]["capacity"].to_i == capacity} - end - - ## - # Constructs an upgrade order to order the given item price. - # The order is built to execute immediately - # - def _order_upgrade_item!(upgrade_item_price) - # put together an order - upgrade_order = { - 'complexType' => 'SoftLayer_Container_Product_Order_Virtual_Guest_Upgrade', - 'virtualGuests' => [{'id' => self.id }], - 'properties' => [{'name' => 'MAINTENANCE_WINDOW', 'value' => Time.now.iso8601}], - 'prices' => [ upgrade_item_price ] - } - - self.softlayer_client["Product_Order"].placeOrder(upgrade_order) - end end #class VirtualServer end \ No newline at end of file diff --git a/lib/softlayer/VirtualServerUpgradeOrder.rb b/lib/softlayer/VirtualServerUpgradeOrder.rb new file mode 100644 index 0000000..48e73eb --- /dev/null +++ b/lib/softlayer/VirtualServerUpgradeOrder.rb @@ -0,0 +1,117 @@ +#-- +# Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. +# +# For licensing information see the LICENSE.md file in the project root. +#++ + + +module SoftLayer + # This class is used to order changes to a virtual server. Although + # the class is named "upgrade" this class can also be used for "downgrades" + # (i.e. changing attributes to a smaller, or slower, value) + # + # The class can also be used to discover what upgrades are available + # for a given virtual server. + # + class VirtualServerUpgradeOrder + # The virtual server that this order is designed to upgrade. + attr_reader :virtual_server + + # The number of cores the server should have after the upgrade. + # If this is nil, the the number of cores will not change + attr_accessor :cores + + # The amount of RAM (in GB) that the server should have after the upgrade + # If this is nil, the ram will not change + attr_accessor :ram + + # The port speed (in Mega bits per second) that the server should have + # after the upgrade. This is typically a value like 100, or 1000 + # If this is nil, the port speeds will not change + attr_accessor :max_port_speed + + # The date and time when you would like the upgrade to be processed. + # This should simply be a Time object. If nil then the upgrade + # will be performed immediately + attr_accessor :upgrade_at + + def initialize(virtual_server) + raise "A virtual server must be provided at the time a virtual server order is created" if !virtual_server || !virtual_server.kind_of?(SoftLayer::VirtualServer) + @virtual_server = virtual_server + end + + def verify() + if has_order_items? + order_object = self.order_object + order_object = yield order_object if block_given? + + @virtual_server.softlayer_client["Product_Order"].verifyOrder(order_object) + end + end + + def place_order!() + if has_order_items? + order_object = self.order_object + order_object = yield order_object if block_given? + + @virtual_server.softlayer_client["Product_Order"].placeOrder(order_object) + end + end + + ## + # Return a list of values that are valid for the :cores attribute + def core_options() + self._item_prices_in_category("guest_core").map { |item_price| item_price["item"]["capacity"].to_i}.sort.uniq + end + + ## + # Return a list of values that are valid for the :memory attribute + def memory_options() + self._item_prices_in_category("ram").map { |item_price| item_price["item"]["capacity"].to_i}.sort.uniq + end + + ## + # Returns a list of valid values for max_port_speed + def max_port_speed_options(client = nil) + self._item_prices_in_category("port_speed").map { |item_price| item_price["item"]["capacity"].to_i}.sort.uniq + end + + private + + def has_order_items? + @cores != nil || @ram != nil || @max_port_speed != nil + end + + def _item_prices_in_category(which_category) + @virtual_server.upgrade_options.select { |item_price| item_price["categories"].find { |category| category["categoryCode"] == which_category } } + end + + ## + # Searches through the upgrade items pricess known to this server for the one that is in a particular category + # and whose capacity matches the value given. Returns the item_price or nil + # + def _item_price_with_capacity(which_category, capacity) + self._item_prices_in_category(which_category).find { |item_price| item_price["item"]["capacity"].to_i == capacity} + end + + def order_object + prices = [] + + cores_price_item = @cores ? _item_price_with_capacity("guest_core", @cores) : nil + ram_price_item = @ram ? _item_price_with_capacity("ram", @ram) : nil + max_port_speed_price_item = @max_port_speed ? _item_price_with_capacity("port_speed", @max_port_speed) : nil + + prices << { "id" => cores_price_item["id"] } if cores_price_item + prices << { "id" => ram_price_item["id"] } if ram_price_item + prices << { "id" => max_port_speed_price_item["id"] } if max_port_speed_price_item + + # put together an order + upgrade_order = { + 'complexType' => 'SoftLayer_Container_Product_Order_Virtual_Guest_Upgrade', + 'virtualGuests' => [{'id' => @virtual_server.id }], + 'properties' => [{'name' => 'MAINTENANCE_WINDOW', 'value' => @upgrade_at ? @upgrade_at.iso8601 : Time.now.iso8601}], + 'prices' => prices + } + end + end # VirtualServerUpgradeOrder +end # SoftLayer Module diff --git a/lib/softlayer_api.rb b/lib/softlayer_api.rb index 6b6ce0b..69a9eba 100755 --- a/lib/softlayer_api.rb +++ b/lib/softlayer_api.rb @@ -4,23 +4,24 @@ # For licensing information see the LICENSE.md file in the project root. #++ +# requirements from the core libraries +require 'time' +# Requirements for the Foundation Layer require 'softlayer/base' require 'softlayer/object_mask_helpers' require 'softlayer/APIParameterFilter' require 'softlayer/ObjectFilter' require 'softlayer/ObjectMaskParser' require 'softlayer/Config' - require 'softlayer/Client' require 'softlayer/Service' -# model classes +# Requirements for the Model Layer require 'softlayer/ModelBase' require 'softlayer/Datacenter' require 'softlayer/DynamicAttribute' require 'softlayer/Account' -require 'softlayer/Ticket' require 'softlayer/Server' require 'softlayer/BareMetalServer' require 'softlayer/BareMetalServerOrder' @@ -28,5 +29,7 @@ require 'softlayer/ImageTemplate' require 'softlayer/ProductPackage' require 'softlayer/ProductItemCategory' +require 'softlayer/Ticket' require 'softlayer/VirtualServer' require 'softlayer/VirtualServerOrder' +require 'softlayer/VirtualServerUpgradeOrder' diff --git a/spec/VirtualServerUpgradeOrder_spec.rb b/spec/VirtualServerUpgradeOrder_spec.rb new file mode 100644 index 0000000..0bc97d0 --- /dev/null +++ b/spec/VirtualServerUpgradeOrder_spec.rb @@ -0,0 +1,154 @@ +#-- +# Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. +# +# For licensing information see the LICENSE.md file in the project root. +#++ + +$LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) + +require 'rubygems' +require 'softlayer_api' +require 'rspec' + +describe SoftLayer::VirtualServerUpgradeOrder do + before(:each) do + SoftLayer::VirtualServerUpgradeOrder.send(:public, *SoftLayer::VirtualServerUpgradeOrder.private_instance_methods) + end + + let(:test_virtual_server) do + mock_client = SoftLayer::Client.new(:username => "fakeuser", :api_key => "DEADBEEFBADF00D") + virtual_guest_service = mock_client[:Virtual_Guest] + allow(virtual_guest_service).to receive(:call_softlayer_api_with_params) do |api_method, parameters, api_arguments| + api_return = nil + + case api_method + when :getUpgradeItemPrices + api_return = fixture_from_json('virtual_server_upgrade_options') + else + fail "Unexpected call to the SoftLayer_Virtual_Guest service" + end + + api_return + end + + test_servers = fixture_from_json('test_virtual_servers') + SoftLayer::VirtualServer.new(mock_client, test_servers.first) + end + + it "requires a virtual server when initialized" do + expect { SoftLayer::VirtualServerUpgradeOrder.new(nil) }.to raise_error + expect { SoftLayer::VirtualServerUpgradeOrder.new("foo") }.to raise_error + expect { SoftLayer::VirtualServerUpgradeOrder.new(test_virtual_server) }.to_not raise_error + end + + it "initializes with none of the upgrades specified" do + upgrade_order = SoftLayer::VirtualServerUpgradeOrder.new(test_virtual_server) + expect(upgrade_order.cores == nil) + expect(upgrade_order.ram == nil) + expect(upgrade_order.max_port_speed == nil) + expect(upgrade_order.upgrade_at == nil) + end + + it "identifies what options are available for upgrading the number of cores" do + sample_order = SoftLayer::VirtualServerUpgradeOrder.new(test_virtual_server) + expect(sample_order.core_options).to eq [1, 2, 4, 8, 12, 16] + end + + it "identifies what options are available for upgrading ram" do + sample_order = SoftLayer::VirtualServerUpgradeOrder.new(test_virtual_server) + expect(sample_order.memory_options).to eq [1, 2, 4, 6, 8, 12, 16, 32, 48, 64] + end + + it "identifies what options are available for upgrading max port speed" do + sample_order = SoftLayer::VirtualServerUpgradeOrder.new(test_virtual_server) + expect(sample_order.max_port_speed_options).to eq [10, 100, 1000] + end + + it "places the number of cores asked for into the order template" do + sample_order = SoftLayer::VirtualServerUpgradeOrder.new(test_virtual_server) + sample_order.core_options.each do |num_cores| + sample_order.cores = num_cores + test_template = sample_order.order_object + expect(sample_order.order_object["prices"].length).to be(1) + + item = sample_order._item_price_with_capacity("guest_core", num_cores) + expect(test_template["prices"].first["id"]).to eq item["id"] + end + end + + it "places the amount of RAM asked for into the order template" do + sample_order = SoftLayer::VirtualServerUpgradeOrder.new(test_virtual_server) + sample_order.memory_options.each do |ram_in_GB| + sample_order.ram = ram_in_GB + test_template = sample_order.order_object + expect(sample_order.order_object["prices"].length).to be(1) + + item = sample_order._item_price_with_capacity("ram", ram_in_GB) + expect(test_template["prices"].first["id"]).to eq item["id"] + end + end + + it "places the port speed asked for into the order template" do + sample_order = SoftLayer::VirtualServerUpgradeOrder.new(test_virtual_server) + sample_order.max_port_speed_options.each do |port_speed| + sample_order.max_port_speed = port_speed + test_template = sample_order.order_object + expect(sample_order.order_object["prices"].length).to be(1) + + item = sample_order._item_price_with_capacity("port_speed", port_speed) + expect(test_template["prices"].first["id"]).to eq item["id"] + end + end + + it "adds the default maintenance window of 'now' if none is given" do + sample_order = SoftLayer::VirtualServerUpgradeOrder.new(test_virtual_server) + sample_order.cores = 2 + test_template = sample_order.order_object + + expect(sample_order.order_object["properties"].first["name"]).to eq('MAINTENANCE_WINDOW') + + time_string = sample_order.order_object["properties"].first["value"] + maintenance_time = Time.iso8601(time_string) + + expect((Time.now - maintenance_time) <= 1.0).to be(true) + end + + it "adds the appointed maintenance window one is given" do + sample_order = SoftLayer::VirtualServerUpgradeOrder.new(test_virtual_server) + sample_order.cores = 2 + + upgrade_time = Time.now + 3600 # in an hour + sample_order.upgrade_at = upgrade_time + + test_template = sample_order.order_object + + expect(sample_order.order_object["properties"].first["name"]).to eq('MAINTENANCE_WINDOW') + + time_string = sample_order.order_object["properties"].first["value"] + expect(time_string).to eq upgrade_time.iso8601 + end + + it "verifies product orders" do + product_order_service = test_virtual_server.softlayer_client[:Product_Order] + + sample_order = SoftLayer::VirtualServerUpgradeOrder.new(test_virtual_server) + sample_order.cores = 2 + + order_object = sample_order.order_object + expect(product_order_service).to receive(:call_softlayer_api_with_params).with(:verifyOrder, anything, [order_object]) + + sample_order.verify() + end + + it "places product orders" do + product_order_service = test_virtual_server.softlayer_client[:Product_Order] + + sample_order = SoftLayer::VirtualServerUpgradeOrder.new(test_virtual_server) + sample_order.cores = 2 + + order_object = sample_order.order_object + expect(product_order_service).to receive(:call_softlayer_api_with_params).with(:placeOrder, anything, [order_object]) + + sample_order.place_order!() + end +end \ No newline at end of file diff --git a/spec/VirtualServer_spec.rb b/spec/VirtualServer_spec.rb index fe8ba3a..4ffe1d8 100644 --- a/spec/VirtualServer_spec.rb +++ b/spec/VirtualServer_spec.rb @@ -82,30 +82,5 @@ expect(mock_client[:Virtual_Guest]).to_not receive(:call_softlayer_api_with_params) fake_virtual_server.upgrade_options end - - describe "individual component upgrades" do - before(:each) do - expect(mock_client[:Product_Order]).to receive(:call_softlayer_api_with_params) do |api_method, parameters, api_arguments| - expect(api_method).to be(:placeOrder) - expect(parameters).to be_nil - expect(api_method).to_not be_empty - end - end - - it "upgrades cores" do - fake_virtual_server = SoftLayer::VirtualServer.new(mock_client, {"id" => 12345}) - fake_virtual_server.upgrade_cores!(8) - end - - it "upgrades ram" do - fake_virtual_server = SoftLayer::VirtualServer.new(mock_client, {"id" => 12345}) - fake_virtual_server.upgrade_RAM!(4) - end - - it "upgrades max port speed" do - fake_virtual_server = SoftLayer::VirtualServer.new(mock_client, {"id" => 12345}) - fake_virtual_server.upgrade_max_port_speed!(100) - end - end # individual component upgrades end end \ No newline at end of file From c6b960aa12d0ecc1eeb7760f2685e0c596dd38f0 Mon Sep 17 00:00:00 2001 From: Scott Thompson Date: Thu, 24 Jul 2014 09:51:41 -0500 Subject: [PATCH 20/32] Code cleanup and commenting for the sake of documentation --- lib/softlayer/VirtualServerUpgradeOrder.rb | 27 +++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/lib/softlayer/VirtualServerUpgradeOrder.rb b/lib/softlayer/VirtualServerUpgradeOrder.rb index 48e73eb..c1f7fe3 100644 --- a/lib/softlayer/VirtualServerUpgradeOrder.rb +++ b/lib/softlayer/VirtualServerUpgradeOrder.rb @@ -4,7 +4,6 @@ # For licensing information see the LICENSE.md file in the project root. #++ - module SoftLayer # This class is used to order changes to a virtual server. Although # the class is named "upgrade" this class can also be used for "downgrades" @@ -35,11 +34,21 @@ class VirtualServerUpgradeOrder # will be performed immediately attr_accessor :upgrade_at + ## + # Create an upgrade order for the virtual server provided. + # def initialize(virtual_server) raise "A virtual server must be provided at the time a virtual server order is created" if !virtual_server || !virtual_server.kind_of?(SoftLayer::VirtualServer) @virtual_server = virtual_server end + ## + # Sends the order represented by this object to SoftLayer for validation. + # + # If a block is passed to verify, the code will send the order template + # being constructed to the block before the order is actually sent for + # validation. + # def verify() if has_order_items? order_object = self.order_object @@ -49,6 +58,13 @@ def verify() end end + ## + # Places the order represented by this object. This is likely to + # involve a change to the charges on an account. + # + # If a block is passed to this routine, the code will send the order template + # being constructed to that block before the order is sent + # def place_order!() if has_order_items? order_object = self.order_object @@ -78,10 +94,16 @@ def max_port_speed_options(client = nil) private + ## + # Returns true if this order object has any upgrades specified + # def has_order_items? @cores != nil || @ram != nil || @max_port_speed != nil end + ## + # Returns a list of the update item prices, in the given category, for the server + # def _item_prices_in_category(which_category) @virtual_server.upgrade_options.select { |item_price| item_price["categories"].find { |category| category["categoryCode"] == which_category } } end @@ -94,6 +116,9 @@ def _item_price_with_capacity(which_category, capacity) self._item_prices_in_category(which_category).find { |item_price| item_price["item"]["capacity"].to_i == capacity} end + ## + # construct an order object + # def order_object prices = [] From f5a9f9e9f811666cb591a5a335af263fbc3f5cf4 Mon Sep 17 00:00:00 2001 From: Scott Thompson Date: Thu, 31 Jul 2014 08:44:21 -0500 Subject: [PATCH 21/32] Updated CHANGELOG --- CHANGELOG.textile | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.textile b/CHANGELOG.textile index 4bcd5f0..1ff7c1a 100644 --- a/CHANGELOG.textile +++ b/CHANGELOG.textile @@ -1,13 +1,14 @@ *3.0* -* Added a method to reboot servers. -* Substantially rewrote the ObjectFilter class. ObjectFilters used to be hashes which made it easy to manipulate their content incorrectly. The new implementation has a strict interface that makes it harder to manipulate filters incorrectly. +* Substantially rewrote the ObjectFilter class. ObjectFilters used to be hashes which made it easy to manipulate their content incorrectly. The new implementation has a strict interface that makes it harder to manipulate filters incorrectly. * Added a model for Virtual Server Image Templates (SoftLayer::ImageTemplate) - VirtualServerOrder now requires an instance of this class rather than allowing you to provide just the global_id of an image * Added a model for data centers (SoftLayer::Datacenter). Bare Metal, Bare Metal Package, and Virtual server orders now use an instance of Datacenter to identify where their servers will be provisioned. The routines in those classes which used to provide lists of valid data center names now return data center objects. -* The routine to retreive the open tickets on an account has been moved from the Ticket class to a dynamic property of an account +* Virtual Server Upgrades are now handled by the VirtualServerUpgradeOrder class and not the VirtualServer class. This change was made for several reasons. Firt and foremost, it allows multiple aspects of a virtual server to be upgraded at once without having to wait on separate transactions to complete between upgrades. Secondly it opens the door for additional upgrades (for example, to disk configuration) to be added in the future. +* Added a method to reboot servers. +* The routine to retreive the open tickets on an account has been moved from the Ticket class. The set of open tickets is now a dynamic property of an account object. *2.1.1* * Virtual server upgrades no longer raise exceptions -* Formalized the RDoc documentation process. Added overview and welcome documentation and changed the README so it directs folks to the new documentation. +* Formalized the RDoc documentation process. Added overview and welcome documentation and changed the README so it directs folks to the new documentation. *2.1.0* * Began implementing a model framework that allows Ruby developers to work with elements in the SoftLayer API in a more object-oriented fashion. The first release of this framework includes the Ticket, VirtualServer, and BareMetalServer classes. From 6578276b6b702cdc87b98167a675ed5b186d79be Mon Sep 17 00:00:00 2001 From: Scott Thompson Date: Fri, 8 Aug 2014 15:24:16 -0500 Subject: [PATCH 22/32] Added VLANFirewalls and some related models --- examples/order_bare_metal_package.rb | 4 +- lib/softlayer/APIParameterFilter.rb | 2 +- lib/softlayer/Client.rb | 6 +- lib/softlayer/NetworkComponent.rb | 14 ++++ lib/softlayer/ProductItemCategory.rb | 28 ++++---- lib/softlayer/ProductPackage.rb | 58 +++++++++++++++-- lib/softlayer/Server.rb | 12 ++++ lib/softlayer/Service.rb | 28 ++++---- lib/softlayer/VLANFirewall.rb | 75 ++++++++++++++++++++++ lib/softlayer/VLANFirewallRuleset.rb | 40 ++++++++++++ lib/softlayer/VirtualServerUpgradeOrder.rb | 22 +++---- lib/softlayer_api.rb | 3 + spec/Client_spec.rb | 2 +- spec/Datacenter_spec.rb | 8 +-- spec/ObjectMaskParser_spec.rb | 4 +- spec/Service_spec.rb | 2 +- spec/VLANFirewall_spec.rb | 17 +++++ spec/VirtualServerUpgradeOrder_spec.rb | 30 ++++----- 18 files changed, 288 insertions(+), 67 deletions(-) create mode 100644 lib/softlayer/NetworkComponent.rb create mode 100644 lib/softlayer/VLANFirewall.rb create mode 100644 lib/softlayer/VLANFirewallRuleset.rb create mode 100644 spec/VLANFirewall_spec.rb diff --git a/examples/order_bare_metal_package.rb b/examples/order_bare_metal_package.rb index d13cc97..e49ce17 100644 --- a/examples/order_bare_metal_package.rb +++ b/examples/order_bare_metal_package.rb @@ -129,11 +129,11 @@ def tl_dr_version # We have a configuration for the server, we also need a location for the new server. # The package can give us a list of locations. Let's print out that list puts "\nData Centers for '#{quad_intel_package.name}':" - quad_intel_package.datacenter_options.each { |location| puts "\t#{location}"} + quad_intel_package.datacenter_options.each { |datacenter| puts "\t#{datacenter.name}"} # With all the config options in place we can now construct the product order. server_order = SoftLayer::BareMetalServerOrder_Package.new(quad_intel_package, client) - server_order.datacenter = 'sng01' + server_order.datacenter = SoftLayer::Datacenter.datacenter_named 'sng01', client server_order.hostname = 'sample' server_order.domain = 'softlayerapi.org' server_order.configuration_options = config_options diff --git a/lib/softlayer/APIParameterFilter.rb b/lib/softlayer/APIParameterFilter.rb index 197f601..bb7ca8e 100644 --- a/lib/softlayer/APIParameterFilter.rb +++ b/lib/softlayer/APIParameterFilter.rb @@ -173,7 +173,7 @@ def server_result_offset ## # A utility method that returns the object filter (if any) stored with this filter. def server_object_filter - self.parameters[:object_filter].to_h + self.parameters[:object_filter].to_h if self.parameters.has_key?(:object_filter) end ## diff --git a/lib/softlayer/Client.rb b/lib/softlayer/Client.rb index 8928e73..bc32293 100644 --- a/lib/softlayer/Client.rb +++ b/lib/softlayer/Client.rb @@ -32,7 +32,7 @@ class Client # A string passsed as the value for the User-Agent header when requests are sent to SoftLayer API. attr_accessor :user_agent - + # An integer value (in seconds). The number of seconds to wait for HTTP requests to the network API # until they timeout. This value can be nil in which case the timeout will be the default value for # the library handling network communication (often 30 seconds) @@ -84,8 +84,10 @@ def initialize(options = {}) # and the endpoint url @endpoint_url = settings[:endpoint_url] || API_PUBLIC_ENDPOINT + # set the user agent to the one provided, or set it to a default one @user_agent = settings[:user_agent] || "softlayer_api gem/#{SoftLayer::VERSION} (Ruby #{RUBY_PLATFORM}/#{RUBY_VERSION})" - + + # and assign a time out if the settings offer one @network_timeout = settings[:timeout] if settings.has_key?(:timeout) raise "A SoftLayer Client requires a username" if !@username || @username.empty? diff --git a/lib/softlayer/NetworkComponent.rb b/lib/softlayer/NetworkComponent.rb new file mode 100644 index 0000000..3d2dbc0 --- /dev/null +++ b/lib/softlayer/NetworkComponent.rb @@ -0,0 +1,14 @@ +#-- +# Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. +# +# For licensing information see the LICENSE.md file in the project root. +#++ + +module SoftLayer + class NetworkComponent < SoftLayer::ModelBase + sl_attr :name + sl_attr :port + sl_attr :speed + sl_attr :maxSpeed + end +end \ No newline at end of file diff --git a/lib/softlayer/ProductItemCategory.rb b/lib/softlayer/ProductItemCategory.rb index 5c574d4..4d6bf7b 100644 --- a/lib/softlayer/ProductItemCategory.rb +++ b/lib/softlayer/ProductItemCategory.rb @@ -10,9 +10,23 @@ module SoftLayer # the product order is the price_id, the rest of the information is provided # to make the object friendly to humans who may be searching for the # meaning of a given price_id. - class ProductConfigurationOption < Struct.new(:price_id, :description, :capacity, :units, :setupFee, :laborFee, :oneTimeFee, :recurringFee, :hourlyRecurringFee) + class ProductConfigurationOption < Struct.new(:price_id, :description, :capacity, :units, :setupFee, :laborFee, + :oneTimeFee, :recurringFee, :hourlyRecurringFee) # Is it evil, or just incongruous to give methods to a struct? + def initialize(package_item_data, price_item_data) + self.description = package_item_data['description'] + self.capacity = package_item_data['capacity'] + self.units = package_item_data['units'] + + self.price_id = price_item_data['id'] + self.setupFee = price_item_data['setupFee'] ? price_item_data['setupFee'].to_f : 0.0 + self.laborFee = price_item_data['laborFee'] ? price_item_data['laborFee'].to_f : 0.0 + self.oneTimeFee = price_item_data['oneTimeFee'] ? price_item_data['oneTimeFee'].to_f : 0.0 + self.recurringFee = price_item_data['recurringFee'] ? price_item_data['recurringFee'].to_f : 0.0 + self.hourlyRecurringFee = price_item_data['hourlyRecurringFee'] ? price_item_data['hourlyRecurringFee'].to_f : 0.0 + end + # returns true if the configurtion option has no fees associated with it. def free? self.setupFee == 0 && self.laborFee == 0 && self.oneTimeFee == 0 && self.recurringFee == 0 && self.hourlyRecurringFee == 0 @@ -63,17 +77,7 @@ class ProductItemCategory < ModelBase # web UI), but this code collapses the groups. self['groups'].collect do |group| group['prices'].sort{|lhs,rhs| lhs['sort'] <=> rhs['sort']}.collect do |price_item| - ProductConfigurationOption.new( - price_item['id'], - price_item['item']['description'], - price_item['item']['capacity'], - price_item['item']['units'], - price_item['setupFee'] ? price_item['setupFee'].to_f : 0.0, - price_item['laborFee'] ? price_item['laborFee'].to_f : 0.0, - price_item['oneTimeFee'] ? price_item['oneTimeFee'].to_f : 0.0, - price_item['recurringFee'] ? price_item['recurringFee'].to_f : 0.0, - price_item['hourlyRecurringFee'] ? price_item['hourlyRecurringFee'].to_f : 0.0 - ) + ProductConfigurationOption.new(price_item['item'], price_item) end end.flatten # flatten out the individual group arrays. end diff --git a/lib/softlayer/ProductPackage.rb b/lib/softlayer/ProductPackage.rb index 5ab53c3..1dd9d62 100644 --- a/lib/softlayer/ProductPackage.rb +++ b/lib/softlayer/ProductPackage.rb @@ -80,32 +80,75 @@ class ProductPackage < ModelBase # Run though the categories and for each one that's in our config, create a SoftLayer::ProductItemCategory object. # Conveniently the +keys+ of the required_by_category_code gives us a list of the category codes in the configuration config_categories = required_by_category_code.keys - categories_data.collect do |category_data| + + # collect all the categories into an array + @categories = categories_data.collect do |category_data| if config_categories.include? category_data['categoryCode'] SoftLayer::ProductItemCategory.new(softlayer_client, category_data, required_by_category_code[category_data['categoryCode']]) else - nil + SoftLayer::ProductItemCategory.new(softlayer_client, category_data, false) end end.compact + + # The configuration consists of only those categories that are required. + @categories.select { |category| category.required? } + end # to_update + end # configuration + + ## + # The full set of product categories contained in the package + # + sl_dynamic_attr :categories do |resource| + resource.should_update? do + @categories == nil + end + + resource.to_update do + # This is a bit ugly, but what we do is ask for the configuration + # which updates all the categories for the package (and marks those + # that are required) + self.configuration + + # return the value constructed by the configuraiton + @categories end end ## # Returns an array of the required categories in this package def required_categories - configuration.select { |category| category.required? } + configuration end ## # Returns the product category with the given category code (or nil if one cannot be found) def category(category_code) - configuration.find { |category| category.categoryCode == category_code } + categories.find { |category| category.categoryCode == category_code } end + ## + # Returns a list of the datacenters that this package is available in def datacenter_options available_locations.collect { |location_data| Datacenter::datacenter_named(location_data["location"]["name"], self.softlayer_client) }.compact end + ## + # Returns the package items with the given description + # Currently this is returning the low-level hash representation directly from the Network API + # + def items_with_description(expected_description) + filter = ObjectFilter.new { |filter| filter.accept("items.description").when_it is(expected_description) } + items_data = self.service.object_filter(filter).getItems() + + items_data.collect do |item_data| + first_price = item_data['prices'][0] + ProductConfigurationOption.new(item_data, first_price) + end + end + + ## + # Returns the service for interacting with this package through the network API + # def service softlayer_client['Product_Package'].object_with_id(self.id) end @@ -168,6 +211,13 @@ def self.bare_metal_server_packages(client = nil) packages_with_key_name('BARE_METAL_CPU', client) end + ## + # The "Additional Products" package is a grab-bag of products + # and services. It has a "well known" id of 0 + def self.additional_products_package(client = nil) + return package_with_id(0, client) + end + protected def self.default_object_mask(root) diff --git a/lib/softlayer/Server.rb b/lib/softlayer/Server.rb index 90a31a2..9fd3ddb 100644 --- a/lib/softlayer/Server.rb +++ b/lib/softlayer/Server.rb @@ -16,6 +16,7 @@ module SoftLayer # ancestry. As a result there is no SoftLayer API analog # to this class. class Server < SoftLayer::ModelBase + include ::SoftLayer::DynamicAttribute ## # :attr_reader: @@ -52,6 +53,17 @@ class Server < SoftLayer::ModelBase # Notes about these server (for use by the customer) sl_attr :notes + sl_dynamic_attr :primary_network_component do |primary_component| + primary_component.should_update? do + return @primary_network_component == nil + end + + primary_component.to_update do + component_data = self.service.getPrimaryNetworkComponent(); + SoftLayer::NetworkComponent.new(self.softlayer_client, component_data) + end + end + ## # Construct a server from the given client using the network data found in +network_hash+ # diff --git a/lib/softlayer/Service.rb b/lib/softlayer/Service.rb index 51145d4..a9ac0e7 100644 --- a/lib/softlayer/Service.rb +++ b/lib/softlayer/Service.rb @@ -20,6 +20,19 @@ require 'xmlrpc/client' +# utility routine for swapping constants without warnings. +def with_warnings(flag) + old_verbose, $VERBOSE = $VERBOSE, flag + yield +ensure + $VERBOSE = old_verbose +end + +# enable parsing of "nil" values in structures returned from the API +with_warnings(nil) { + XMLRPC::Config.const_set('ENABLE_NIL_PARSER', true) +} + # The XML-RPC spec calls for the "faultCode" in faults to be an integer # but the SoftLayer XML-RPC API can return strings as the "faultCode" # @@ -39,16 +52,6 @@ def self.fault(hash) end end -# The XMLRPC client uses a fixed user agent string, but we want to -# supply our own, so we add a method to XMLRPC::Client that lets -# us change it. -class XMLRPC::Client - def self.set_user_agent(new_agent) - remove_const(:USER_AGENT) if const_defined?(:USER_AGENT) - const_set(:USER_AGENT, new_agent) - end -end - module SoftLayer # = SoftLayer API Service # @@ -233,7 +236,7 @@ def call_softlayer_api_with_params(method_name, parameters, args) # The client knows about authentication, so ask him for the auth headers authentication_headers = self.client.authentication_headers additional_headers.merge!(authentication_headers) - + if parameters && parameters.server_object_filter additional_headers.merge!("#{@service_name}ObjectFilter" => parameters.server_object_filter) end @@ -315,10 +318,11 @@ def http end @xmlrpc_client.http.set_debug_output($stderr) + @xmlrpc_client.http.instance_variable_set(:@verify_mode, OpenSSL::SSL::VERIFY_NONE) end # $DEBUG end @xmlrpc_client end - end # Service class + end # Service class end # module SoftLayer diff --git a/lib/softlayer/VLANFirewall.rb b/lib/softlayer/VLANFirewall.rb new file mode 100644 index 0000000..1b62514 --- /dev/null +++ b/lib/softlayer/VLANFirewall.rb @@ -0,0 +1,75 @@ +#-- +# Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. +# +# For licensing information see the LICENSE.md file in the project root. +#++ + +module SoftLayer + class VLANFirewall < SoftLayer::ModelBase + sl_attr :VLAN_number, 'vlanNumber' + + ## + # return the name of the router the firewall is attached to + def primaryRouter + return self['primaryRouter']['hostname'] + end + + ## + # The fully qualified domain name of the firewall + def fullyQualifiedDomainName + if self.has_sl_property?('networkVlanFirewall') + return self['networkVlanFirewall']['fullyQualifiedDomainName'] + else + return @softlayer_hash + end + end + + ## + # returns true if the firewall has the high availability flag set + # + def high_availability? + # note that highAvailabilityFirewallFlag is a boolean in the softlayer hash + return self.has_sl_property?('highAvailabilityFirewallFlag') && self['highAvailabilityFirewallFlag'] + end + + def rule_set + rule_set = nil + + # Search down through the firewall's data to find the AccessControlList (ACL) for the + # "outside" interface which handles "in"-wardly directed traffic. This is the list that + # has the rules we're interested in. + outside_interface_data = self["firewallInterfaces"].find { |firewall_interface_data| firewall_interface_data['name'] == 'outside' } + if outside_interface_data + incoming_ACL = outside_interface_data['firewallContextAccessControlLists'].find { |firewallACL_data| firewallACL_data['direction'] == 'in' } + + firewall_ACL = self.softlayer_client[:Network_Firewall_AccessControlList].object_with_id(incoming_ACL['id']).getObject + rule_set = VLANFirewallRuleset.new(self.softlayer_client, firewall_ACL) + end + + return rule_set + end + + ## + # collect a list of the firewalls on the account + # + def self.find_firewalls(client) + softlayer_client = client || Client.default_client + raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client + + vlan_firewall_filter = SoftLayer::ObjectFilter.new() { |filter| + filter.accept("networkVlans.networkVlanFirewall").when_it is_not_null + } + + vlan_firewalls = client[:Account].object_mask(vlan_firewall_mask).object_filter(vlan_firewall_filter).getNetworkVlans + vlan_firewalls.collect { |firewall_data| SoftLayer::VLANFirewall.new(client, firewall_data)} + end + + private + + def self.vlan_firewall_mask + return "mask[primaryRouter,dedicatedFirewallFlag,highAvailabilityFirewallFlag,"+ + "firewallInterfaces.firewallContextAccessControlLists," + + "networkVlanFirewall[id, datacenter, primaryIpAddress, firewallType, fullyQualifiedDomainName]]" + end + end # class Firewall +end # module SoftLayer \ No newline at end of file diff --git a/lib/softlayer/VLANFirewallRuleset.rb b/lib/softlayer/VLANFirewallRuleset.rb new file mode 100644 index 0000000..23b82a9 --- /dev/null +++ b/lib/softlayer/VLANFirewallRuleset.rb @@ -0,0 +1,40 @@ +#-- +# Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. +# +# For licensing information see the LICENSE.md file in the project root. +#++ + +module SoftLayer + class VLANFirewallRuleset < SoftLayer::ModelBase + + # The initial set of rules is taken from the network API, + # but then the user is free to manipulate the rules and + # ask the system to update the firewall. As a result + # the rules property can get out-of-sync with the actual rules + # on the network. To re-fetch the set of rules from the network + # use the #refresh_rules method. + attr_accessor :rules + + def initialize(softlayer_client, network_hash) + super(softlayer_client, network_hash) + self.refresh_rules + end + + def service + return self.softlayer_client[:Network_Firewall_AccessControlList].object_with_id(self.id) + end + + def softlayer_properties(object_mask = nil) + self.service.object_mask(self.class.default_object_mask).getObject + end + + def self.default_rules_mask + return "mask[orderValue,action,destinationIpAddress,destinationIpSubnetMask,protocol,destinationPortRangeStart,destinationPortRangeEnd,sourceIpAddress,sourceIpSubnetMask,version]" + end + + def refresh_rules + self.rules = self.softlayer_client[:Network_Firewall_AccessControlList].object_with_id(self.id).object_mask(self.class.default_rules_mask).getRules + end + end +end + diff --git a/lib/softlayer/VirtualServerUpgradeOrder.rb b/lib/softlayer/VirtualServerUpgradeOrder.rb index c1f7fe3..4a7c0b9 100644 --- a/lib/softlayer/VirtualServerUpgradeOrder.rb +++ b/lib/softlayer/VirtualServerUpgradeOrder.rb @@ -15,25 +15,25 @@ module SoftLayer class VirtualServerUpgradeOrder # The virtual server that this order is designed to upgrade. attr_reader :virtual_server - + # The number of cores the server should have after the upgrade. # If this is nil, the the number of cores will not change attr_accessor :cores - + # The amount of RAM (in GB) that the server should have after the upgrade # If this is nil, the ram will not change attr_accessor :ram - + # The port speed (in Mega bits per second) that the server should have # after the upgrade. This is typically a value like 100, or 1000 # If this is nil, the port speeds will not change attr_accessor :max_port_speed - + # The date and time when you would like the upgrade to be processed. # This should simply be a Time object. If nil then the upgrade # will be performed immediately attr_accessor :upgrade_at - + ## # Create an upgrade order for the virtual server provided. # @@ -59,7 +59,7 @@ def verify() end ## - # Places the order represented by this object. This is likely to + # Places the order represented by this object. This is likely to # involve a change to the charges on an account. # # If a block is passed to this routine, the code will send the order template @@ -107,7 +107,7 @@ def has_order_items? def _item_prices_in_category(which_category) @virtual_server.upgrade_options.select { |item_price| item_price["categories"].find { |category| category["categoryCode"] == which_category } } end - + ## # Searches through the upgrade items pricess known to this server for the one that is in a particular category # and whose capacity matches the value given. Returns the item_price or nil @@ -115,17 +115,17 @@ def _item_prices_in_category(which_category) def _item_price_with_capacity(which_category, capacity) self._item_prices_in_category(which_category).find { |item_price| item_price["item"]["capacity"].to_i == capacity} end - - ## + + ## # construct an order object # def order_object prices = [] - + cores_price_item = @cores ? _item_price_with_capacity("guest_core", @cores) : nil ram_price_item = @ram ? _item_price_with_capacity("ram", @ram) : nil max_port_speed_price_item = @max_port_speed ? _item_price_with_capacity("port_speed", @max_port_speed) : nil - + prices << { "id" => cores_price_item["id"] } if cores_price_item prices << { "id" => ram_price_item["id"] } if ram_price_item prices << { "id" => max_port_speed_price_item["id"] } if max_port_speed_price_item diff --git a/lib/softlayer_api.rb b/lib/softlayer_api.rb index 69a9eba..9011717 100755 --- a/lib/softlayer_api.rb +++ b/lib/softlayer_api.rb @@ -26,7 +26,10 @@ require 'softlayer/BareMetalServer' require 'softlayer/BareMetalServerOrder' require 'softlayer/BareMetalServerOrder_Package' +require 'softlayer/VLANFirewall' +require 'softlayer/VLANFirewallRuleset' require 'softlayer/ImageTemplate' +require 'softlayer/NetworkComponent' require 'softlayer/ProductPackage' require 'softlayer/ProductItemCategory' require 'softlayer/Ticket' diff --git a/spec/Client_spec.rb b/spec/Client_spec.rb index c33fc65..ab006aa 100644 --- a/spec/Client_spec.rb +++ b/spec/Client_spec.rb @@ -104,7 +104,7 @@ client = SoftLayer::Client.new(:username => 'fake_user', :api_key => 'fake_key', :endpoint_url => 'http://fakeurl.org/', :timeout => 60) expect(client.network_timeout).to eq 60 end - + it 'gets the default endpoint even if none is provided' do $SL_API_BASE_URL = nil client = SoftLayer::Client.new(:username => 'fake_user', :api_key => 'fake_key') diff --git a/spec/Datacenter_spec.rb b/spec/Datacenter_spec.rb index a229588..c09cd5d 100644 --- a/spec/Datacenter_spec.rb +++ b/spec/Datacenter_spec.rb @@ -27,21 +27,21 @@ require 'rspec' describe SoftLayer::Datacenter do - let (:mock_client) do + let (:mock_client) do mock_client = SoftLayer::Client.new(:username => "fakeuser", :api_key => "DEADBEEFBADF00D") allow(mock_client[:Location]).to receive(:call_softlayer_api_with_params) do |method_name, parameters, args| fixture_from_json('datacenter_locations.json') end - + mock_client end - + it "retrieves a list of datacenters" do datacenters = SoftLayer::Datacenter.datacenters(mock_client) names = datacenters.collect { |datacenter| datacenter.name } expect(names.sort).to eq ["ams01", "dal01", "dal02", "dal04", "dal05", "dal06", "dal07", "hkg02", "hou02", "lon02", "sea01", "sjc01", "sng01", "tor01", "wdc01", "wdc03"] end - + it "retrieves a particular datacenter by name" do dal05 = SoftLayer::Datacenter.datacenter_named("dal05", mock_client) expect(dal05.name).to eq "dal05" diff --git a/spec/ObjectMaskParser_spec.rb b/spec/ObjectMaskParser_spec.rb index 92de20f..9ebdc62 100644 --- a/spec/ObjectMaskParser_spec.rb +++ b/spec/ObjectMaskParser_spec.rb @@ -45,7 +45,7 @@ expect(result.name).to eq 'filterMask' expect(result.children[0].name).to eq 'simple1' end - + it "should parse a mask set with fiterMask" do result = nil expect { result = subject.parse("[filterMask.simple1, filterMask.simple2]") }.to_not raise_error @@ -58,7 +58,7 @@ expect(result[1].name).to eq 'filterMask' expect(result[1].children.count).to eq 1 expect(result[1].children[0].name).to eq "simple2" - end + end end describe SoftLayer::ObjectMaskParser, "#parse_property_set" do diff --git a/spec/Service_spec.rb b/spec/Service_spec.rb index d37e78f..e76273d 100644 --- a/spec/Service_spec.rb +++ b/spec/Service_spec.rb @@ -68,7 +68,7 @@ before(:each) do SoftLayer::Service.send(:public, :xmlrpc_client) end - + it "Constructs an XMLRPC client with a given timeout value based on the timeout of the client" do client = SoftLayer::Client.new(:username => 'fake_user', :api_key => 'fake_key', :timeout => 60) ticket_service = client[:Ticket] diff --git a/spec/VLANFirewall_spec.rb b/spec/VLANFirewall_spec.rb new file mode 100644 index 0000000..5f8b21c --- /dev/null +++ b/spec/VLANFirewall_spec.rb @@ -0,0 +1,17 @@ +#-- +# Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. +# +# For licensing information see the LICENSE.md file in the project root. +#++ + +$LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) + +require 'rubygems' +require 'softlayer_api' +require 'rspec' + +describe SoftLayer::Firewall do + it "should have a class representing a firewall" do + expect{ SoftLayer::Firewall.new("not really a client", { "id" => 12345 }) }.to_not raise_error + end +end \ No newline at end of file diff --git a/spec/VirtualServerUpgradeOrder_spec.rb b/spec/VirtualServerUpgradeOrder_spec.rb index 0bc97d0..ab2b73e 100644 --- a/spec/VirtualServerUpgradeOrder_spec.rb +++ b/spec/VirtualServerUpgradeOrder_spec.rb @@ -40,7 +40,7 @@ expect { SoftLayer::VirtualServerUpgradeOrder.new("foo") }.to raise_error expect { SoftLayer::VirtualServerUpgradeOrder.new(test_virtual_server) }.to_not raise_error end - + it "initializes with none of the upgrades specified" do upgrade_order = SoftLayer::VirtualServerUpgradeOrder.new(test_virtual_server) expect(upgrade_order.cores == nil) @@ -48,7 +48,7 @@ expect(upgrade_order.max_port_speed == nil) expect(upgrade_order.upgrade_at == nil) end - + it "identifies what options are available for upgrading the number of cores" do sample_order = SoftLayer::VirtualServerUpgradeOrder.new(test_virtual_server) expect(sample_order.core_options).to eq [1, 2, 4, 8, 12, 16] @@ -58,19 +58,19 @@ sample_order = SoftLayer::VirtualServerUpgradeOrder.new(test_virtual_server) expect(sample_order.memory_options).to eq [1, 2, 4, 6, 8, 12, 16, 32, 48, 64] end - + it "identifies what options are available for upgrading max port speed" do sample_order = SoftLayer::VirtualServerUpgradeOrder.new(test_virtual_server) expect(sample_order.max_port_speed_options).to eq [10, 100, 1000] end - + it "places the number of cores asked for into the order template" do sample_order = SoftLayer::VirtualServerUpgradeOrder.new(test_virtual_server) sample_order.core_options.each do |num_cores| sample_order.cores = num_cores test_template = sample_order.order_object expect(sample_order.order_object["prices"].length).to be(1) - + item = sample_order._item_price_with_capacity("guest_core", num_cores) expect(test_template["prices"].first["id"]).to eq item["id"] end @@ -82,7 +82,7 @@ sample_order.ram = ram_in_GB test_template = sample_order.order_object expect(sample_order.order_object["prices"].length).to be(1) - + item = sample_order._item_price_with_capacity("ram", ram_in_GB) expect(test_template["prices"].first["id"]).to eq item["id"] end @@ -94,40 +94,40 @@ sample_order.max_port_speed = port_speed test_template = sample_order.order_object expect(sample_order.order_object["prices"].length).to be(1) - + item = sample_order._item_price_with_capacity("port_speed", port_speed) expect(test_template["prices"].first["id"]).to eq item["id"] end end - + it "adds the default maintenance window of 'now' if none is given" do sample_order = SoftLayer::VirtualServerUpgradeOrder.new(test_virtual_server) sample_order.cores = 2 test_template = sample_order.order_object - + expect(sample_order.order_object["properties"].first["name"]).to eq('MAINTENANCE_WINDOW') - + time_string = sample_order.order_object["properties"].first["value"] maintenance_time = Time.iso8601(time_string) expect((Time.now - maintenance_time) <= 1.0).to be(true) end - + it "adds the appointed maintenance window one is given" do sample_order = SoftLayer::VirtualServerUpgradeOrder.new(test_virtual_server) sample_order.cores = 2 - + upgrade_time = Time.now + 3600 # in an hour sample_order.upgrade_at = upgrade_time test_template = sample_order.order_object - + expect(sample_order.order_object["properties"].first["name"]).to eq('MAINTENANCE_WINDOW') - + time_string = sample_order.order_object["properties"].first["value"] expect(time_string).to eq upgrade_time.iso8601 end - + it "verifies product orders" do product_order_service = test_virtual_server.softlayer_client[:Product_Order] From 942a39668c860bbb7656c2d1f55d6e7158cad754 Mon Sep 17 00:00:00 2001 From: Scott Thompson Date: Mon, 11 Aug 2014 10:08:03 -0500 Subject: [PATCH 23/32] Added server firewalls --- lib/softlayer/ServerFirewall.rb | 166 ++++++++++++++++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 lib/softlayer/ServerFirewall.rb diff --git a/lib/softlayer/ServerFirewall.rb b/lib/softlayer/ServerFirewall.rb new file mode 100644 index 0000000..809bdd1 --- /dev/null +++ b/lib/softlayer/ServerFirewall.rb @@ -0,0 +1,166 @@ +#-- +# Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. +# +# For licensing information see the LICENSE.md file in the project root. +#++ + +module SoftLayer + + ## + # The ServerFirewall class represents a firewall in the + # SoftLayer environment that exists in a 1 to 1 relationship + # with a particular server (either Bare Metal or Virtual). + # + # This is also called a "Shared Firewall" in some documentation + # + # Instances of this class rougly correspond to instances of the + # SoftLayer_Network_Component_Firewall service entity + # + class ServerFirewall < SoftLayer::ModelBase + include ::SoftLayer::DynamicAttribute + + ## + # :attr_reader: + # The firewall rules assigned to this firewall. These rules will + # be read from the network API every time you ask for the value + # of this property. To change the rules on the server use the + # asymmetric method change_rules! + sl_dynamic_attr :rules do |firewall_rules| + firewall_rules.should_update? do + # firewall rules update every time you ask for them. + return true + end + + firewall_rules.to_update do + rules_data = self.service.object_mask(self.class.default_rules_mask).getRules() + + # For some reason (at the time of this writing) the object mask is not + # applied to the rules properly. (this has been reported as a bug to the + # proper development team). This extra step does filtering that should + # have been done by the object mask. + rules_keys = self.class.default_rules_mask_keys + new_rules = rules_data.inject([]) do |new_rules, current_rule| + new_rule = current_rule.delete_if { |key, value| !(rules_keys.include? key) } + new_rules << new_rule + end + + new_rules.sort { |lhs, rhs| lhs['orderValue'] <=> rhs['orderValue'] } + end + end + + ## + # :attr_reader: + # The server that this firewall is attached to. The result may be + # either a bare metal or virtual server. + # + sl_dynamic_attr :protected_server do |protected_server| + protected_server.should_update? do + @protected_server == nil + end + + protected_server.to_update do + if has_sl_property?('networkComponent') + @protected_server = SoftLayer::BareMetalServer.server_with_id(self['networkComponent']['downlinkComponent']['hardwareId'], :client => softlayer_client) + end + + if has_sl_property?('guestNetworkComponent') + @protected_server = SoftLayer::VirtualServer.server_with_id(self['guestNetworkComponent']['guest']['id'], :client => softlayer_client) + end + + @protected_server + end + end + + def initialize(client, network_hash) + super(client, network_hash) + @protected_server = nil + end + + ## + # Change the set of rules for the firewall. + # The rules_data parameter should be an array of hashes where + # each hash gives the conditions of the rule. The keys of the + # hashes should be entries from the array returned by + # SoftLayer::ServerFirewall.default_rules_mask_keys + # + # *NOTE!* The rules themselves have an "orderValue" property. + # It is this property, and *not* the order that the rules are + # found in the rules_data array, which will determine in which + # order the firewall applies it's rules to incomming traffic. + # + # *NOTE!* Changes to the rules are not applied immediately + # on the server side. Instead, they are enqueued by the + # firewall update service and updated periodically. A typical + # update will take about one minute to apply, but times may vary + # depending on the system load and other circumstances. + def change_rules!(rules_data) + change_object = { + "networkComponentFirewallId" => self.id, + "rules" => rules_data + } + + self.softlayer_client[:Network_Firewall_Update_Request].createObject(change_object) + end + + + ## + # Locate and return all the server firewalls in the environment. + # + # These are a bit trick to track down. The strategy we take here is + # to look at the account and find all the VLANs that do NOT have their + # "dedicatedFirewallFlag" set. + # + # With the list of VLANs in hand we check each to see if it has an + # firewallNetworkComponents (corresponding to bare metal servers) or + # firewallGuestNetworkComponents (corresponding to virtual servers) that + # have a status of "allow_edit". Each such component is a firewall + # interface on the VLAN with rules that the customer can edit. + # + # The collection of all those VLANs becomes the set of firewalls + # for the account. + # + def self.find_firewalls(client) + softlayer_client = client || Client.default_client + raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client + + # Note that the dedicatedFirewallFlag is actually an integer and not a boolean + # so we compare it against 0 + shared_vlans_filter = SoftLayer::ObjectFilter.new() { |filter| + filter.accept("networkVlans.dedicatedFirewallFlag").when_it is(0) + } + + bare_metal_firewalls_data = [] + virtual_firewalls_data = [] + + shared_vlans = softlayer_client[:Account].object_mask("mask[firewallNetworkComponents[id,status,networkComponent[downlinkComponent[hardwareId]]],firewallGuestNetworkComponents[id,status,guestNetworkComponent[id,guest.id]]]").object_filter(shared_vlans_filter).getNetworkVlans + shared_vlans.each do |vlan_data| + bare_metal_firewalls_data.concat vlan_data["firewallNetworkComponents"].select { |network_component| network_component['status'] == 'allow_edit'} + virtual_firewalls_data.concat vlan_data["firewallGuestNetworkComponents"].select { |network_component| network_component['status'] == 'allow_edit'} + end + + bare_metal_firewalls = bare_metal_firewalls_data.collect { |bare_metal_firewall_data| + self.new(softlayer_client, bare_metal_firewall_data) + } + + virtual_server_firewalls = virtual_firewalls_data.collect { |virtual_firewall_data| + self.new(softlayer_client, virtual_firewall_data) + } + + return bare_metal_firewalls + virtual_server_firewalls + end + + def service + self.softlayer_client[:Network_Component_Firewall].object_with_id(self.id) + end + + private + + def self.default_rules_mask + return { "mask" => default_rules_mask_keys }.to_sl_object_mask + end + + def self.default_rules_mask_keys + ['orderValue','action','destinationIpAddress','destinationIpSubnetMask',"protocol","destinationPortRangeStart","destinationPortRangeEnd",'sourceIpAddress',"sourceIpSubnetMask","version"] + end + end # ServerFirewall class +end # SoftLayer module \ No newline at end of file From b497d55b8ddd6f4b0546b317a5d8199024cdc449 Mon Sep 17 00:00:00 2001 From: Scott Thompson Date: Mon, 11 Aug 2014 10:08:14 -0500 Subject: [PATCH 24/32] Added server firewalls --- lib/softlayer/ProductItemCategory.rb | 2 +- lib/softlayer/ProductPackage.rb | 2 +- lib/softlayer/ServerFirewall.rb | 44 +++++++-- lib/softlayer/Service.rb | 4 +- lib/softlayer/VLANFirewall.rb | 137 ++++++++++++++++++++++----- lib/softlayer/VLANFirewallRuleset.rb | 40 -------- lib/softlayer_api.rb | 4 +- spec/VLANFirewall_spec.rb | 4 +- 8 files changed, 156 insertions(+), 81 deletions(-) delete mode 100644 lib/softlayer/VLANFirewallRuleset.rb diff --git a/lib/softlayer/ProductItemCategory.rb b/lib/softlayer/ProductItemCategory.rb index 4d6bf7b..f9f33e2 100644 --- a/lib/softlayer/ProductItemCategory.rb +++ b/lib/softlayer/ProductItemCategory.rb @@ -26,7 +26,7 @@ def initialize(package_item_data, price_item_data) self.recurringFee = price_item_data['recurringFee'] ? price_item_data['recurringFee'].to_f : 0.0 self.hourlyRecurringFee = price_item_data['hourlyRecurringFee'] ? price_item_data['hourlyRecurringFee'].to_f : 0.0 end - + # returns true if the configurtion option has no fees associated with it. def free? self.setupFee == 0 && self.laborFee == 0 && self.oneTimeFee == 0 && self.recurringFee == 0 && self.hourlyRecurringFee == 0 diff --git a/lib/softlayer/ProductPackage.rb b/lib/softlayer/ProductPackage.rb index 1dd9d62..ac946b5 100644 --- a/lib/softlayer/ProductPackage.rb +++ b/lib/softlayer/ProductPackage.rb @@ -139,7 +139,7 @@ def datacenter_options def items_with_description(expected_description) filter = ObjectFilter.new { |filter| filter.accept("items.description").when_it is(expected_description) } items_data = self.service.object_filter(filter).getItems() - + items_data.collect do |item_data| first_price = item_data['prices'][0] ProductConfigurationOption.new(item_data, first_price) diff --git a/lib/softlayer/ServerFirewall.rb b/lib/softlayer/ServerFirewall.rb index 809bdd1..63ec77a 100644 --- a/lib/softlayer/ServerFirewall.rb +++ b/lib/softlayer/ServerFirewall.rb @@ -5,7 +5,6 @@ #++ module SoftLayer - ## # The ServerFirewall class represents a firewall in the # SoftLayer environment that exists in a 1 to 1 relationship @@ -19,6 +18,14 @@ module SoftLayer class ServerFirewall < SoftLayer::ModelBase include ::SoftLayer::DynamicAttribute + ## + # :attr_reader: + # The state of the firewall, includes whether or not the rules are + # editable and whether or not the firewall rules are applied or bypassed + # Can at least be 'allow_edit', 'bypass' or 'no_edit'. + # This list may not be exhaustive + sl_attr :status + ## # :attr_reader: # The firewall rules assigned to this firewall. These rules will @@ -34,10 +41,10 @@ class ServerFirewall < SoftLayer::ModelBase firewall_rules.to_update do rules_data = self.service.object_mask(self.class.default_rules_mask).getRules() - # For some reason (at the time of this writing) the object mask is not - # applied to the rules properly. (this has been reported as a bug to the - # proper development team). This extra step does filtering that should - # have been done by the object mask. + # At the time of this writing, the object mask sent to getRules is not + # applied properly. This has been reported as a bug to the proper + # development team. In the mean time, this extra step does filtering + # that should have been done by the object mask. rules_keys = self.class.default_rules_mask_keys new_rules = rules_data.inject([]) do |new_rules, current_rule| new_rule = current_rule.delete_if { |key, value| !(rules_keys.include? key) } @@ -71,6 +78,9 @@ class ServerFirewall < SoftLayer::ModelBase end end + ## + # Calls super to initialize the object then initializes some + # properties def initialize(client, network_hash) super(client, network_hash) @protected_server = nil @@ -102,11 +112,10 @@ def change_rules!(rules_data) self.softlayer_client[:Network_Firewall_Update_Request].createObject(change_object) end - ## # Locate and return all the server firewalls in the environment. # - # These are a bit trick to track down. The strategy we take here is + # These are a bit tricky to track down. The strategy we take here is # to look at the account and find all the VLANs that do NOT have their # "dedicatedFirewallFlag" set. # @@ -132,10 +141,10 @@ def self.find_firewalls(client) bare_metal_firewalls_data = [] virtual_firewalls_data = [] - shared_vlans = softlayer_client[:Account].object_mask("mask[firewallNetworkComponents[id,status,networkComponent[downlinkComponent[hardwareId]]],firewallGuestNetworkComponents[id,status,guestNetworkComponent[id,guest.id]]]").object_filter(shared_vlans_filter).getNetworkVlans + shared_vlans = softlayer_client[:Account].object_mask(network_vlan_mask).object_filter(shared_vlans_filter).getNetworkVlans shared_vlans.each do |vlan_data| - bare_metal_firewalls_data.concat vlan_data["firewallNetworkComponents"].select { |network_component| network_component['status'] == 'allow_edit'} - virtual_firewalls_data.concat vlan_data["firewallGuestNetworkComponents"].select { |network_component| network_component['status'] == 'allow_edit'} + bare_metal_firewalls_data.concat vlan_data["firewallNetworkComponents"].select { |network_component| network_component['status'] != 'no_edit'} + virtual_firewalls_data.concat vlan_data["firewallGuestNetworkComponents"].select { |network_component| network_component['status'] != 'no_edit'} end bare_metal_firewalls = bare_metal_firewalls_data.collect { |bare_metal_firewall_data| @@ -152,9 +161,24 @@ def self.find_firewalls(client) def service self.softlayer_client[:Network_Component_Firewall].object_with_id(self.id) end + + def softlayer_properties(object_mask = nil) + service = self.service + service = service.object_mask(object_mask) if object_mask + + if self.has_sl_property?('networkComponent') + service.object_mask("mask[id,status,networkComponent.downlinkComponent.hardwareId]").getObject + else + service.object_mask("mask[id,status,guestNetworkComponent.guest.id]").getObject + end + end private + def self.network_vlan_mask + "mask[firewallNetworkComponents[id,status,networkComponent.downlinkComponent.hardwareId],firewallGuestNetworkComponents[id,status,guestNetworkComponent.guest.id]]" + end + def self.default_rules_mask return { "mask" => default_rules_mask_keys }.to_sl_object_mask end diff --git a/lib/softlayer/Service.rb b/lib/softlayer/Service.rb index a9ac0e7..a633097 100644 --- a/lib/softlayer/Service.rb +++ b/lib/softlayer/Service.rb @@ -236,7 +236,7 @@ def call_softlayer_api_with_params(method_name, parameters, args) # The client knows about authentication, so ask him for the auth headers authentication_headers = self.client.authentication_headers additional_headers.merge!(authentication_headers) - + if parameters && parameters.server_object_filter additional_headers.merge!("#{@service_name}ObjectFilter" => parameters.server_object_filter) end @@ -324,5 +324,5 @@ def http @xmlrpc_client end - end # Service class + end # Service class end # module SoftLayer diff --git a/lib/softlayer/VLANFirewall.rb b/lib/softlayer/VLANFirewall.rb index 1b62514..3b6ab74 100644 --- a/lib/softlayer/VLANFirewall.rb +++ b/lib/softlayer/VLANFirewall.rb @@ -5,17 +5,88 @@ #++ module SoftLayer + # The VLANFirewall class represents the firewall that protects + # all the servers on a VLAN in the SoftLayer Environment. It is + # also known as a "Dedicated Firewall" in some documentation. + # + # Instances of this class are a bit odd because they actually represent + # VLANs (the VLAN protected by the firewall) and not the physical hardware + # implementing the firewall itself. (although the device is accessible as + # the "networkVlanFirewall" property) + # + # As a result, instances of this class correspond to certain instances + # in the SoftLayer_Network_Vlan service. + # class VLANFirewall < SoftLayer::ModelBase + include ::SoftLayer::DynamicAttribute + + ## + #:attr_reader: + # + # The number of the VLAN protected by this firewall + # sl_attr :VLAN_number, 'vlanNumber' ## - # return the name of the router the firewall is attached to + # :attr_reader: + # + # The set of rules applied by this firewall to incoming traffic. + # The object will retrieve the rules from the network API every + # time you ask it for the rules. + # + # The code will sort the rules by their "orderValue" which is the + # order that the firewall applies the rules, however please see + # the important note in change_rules! concerning the "orderValue" + # property of the rules. + sl_dynamic_attr :rules do |firewall_rules| + firewall_rules.should_update? do + # firewall rules update every time you ask for them. + return true + end + + firewall_rules.to_update do + acl_id = rules_ACL_id() + rules_data = self.softlayer_client[:Network_Firewall_AccessControlList].object_with_id(acl_id).object_mask(self.class.default_rules_mask).getRules + rules_data.sort { |lhs, rhs| lhs['orderValue'] <=> rhs['orderValue'] } + end + end + + ## + # Change the set of rules for the firewall. + # The rules_data parameter should be an array of hashes where + # each hash gives the conditions of the rule. The keys of the + # hashes should be entries from the array returned by + # SoftLayer::ServerFirewall.default_rules_mask_keys + # + # *NOTE!* The rules themselves have an "orderValue" property. + # It is this property, and *not* the order that the rules are + # found in the rules_data array, which will determine in which + # order the firewall applies it's rules to incomming traffic. + # + # *NOTE!* Changes to the rules are not applied immediately + # on the server side. Instead, they are enqueued by the + # firewall update service and updated periodically. A typical + # update will take about one minute to apply, but times may vary + # depending on the system load and other circumstances. + def change_rules!(rules_data) + change_object = { + "firewallContextAccessControlListId" => self.id, + "rules" => rules_data + } + + self.softlayer_client[:Network_Firewall_Update_Request].createObject(change_object) + end + + ## + # Returns the name of the primary router the firewall is attached to. + # This is often a "customer router" in one of the datacenters. def primaryRouter return self['primaryRouter']['hostname'] end ## - # The fully qualified domain name of the firewall + # The fully qualified domain name of the physical device the + # firewall is implemented by. def fullyQualifiedDomainName if self.has_sl_property?('networkVlanFirewall') return self['networkVlanFirewall']['fullyQualifiedDomainName'] @@ -25,37 +96,23 @@ def fullyQualifiedDomainName end ## - # returns true if the firewall has the high availability flag set - # + # Returns true if this is a "high availability" firewall, that is a firewall + # that exists as one member of a redundant pair. def high_availability? # note that highAvailabilityFirewallFlag is a boolean in the softlayer hash return self.has_sl_property?('highAvailabilityFirewallFlag') && self['highAvailabilityFirewallFlag'] end - def rule_set - rule_set = nil - - # Search down through the firewall's data to find the AccessControlList (ACL) for the - # "outside" interface which handles "in"-wardly directed traffic. This is the list that - # has the rules we're interested in. - outside_interface_data = self["firewallInterfaces"].find { |firewall_interface_data| firewall_interface_data['name'] == 'outside' } - if outside_interface_data - incoming_ACL = outside_interface_data['firewallContextAccessControlLists'].find { |firewallACL_data| firewallACL_data['direction'] == 'in' } - - firewall_ACL = self.softlayer_client[:Network_Firewall_AccessControlList].object_with_id(incoming_ACL['id']).getObject - rule_set = VLANFirewallRuleset.new(self.softlayer_client, firewall_ACL) - end - - return rule_set - end - ## - # collect a list of the firewalls on the account + # Collect a list of the firewalls on the account. # + # This list is obtained by asking the account for all the VLANs + # it has that also have a networkVlanFirewall component. def self.find_firewalls(client) softlayer_client = client || Client.default_client raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client + # only VLAN firewallas have a networkVlanFirewall component vlan_firewall_filter = SoftLayer::ObjectFilter.new() { |filter| filter.accept("networkVlans.networkVlanFirewall").when_it is_not_null } @@ -63,13 +120,47 @@ def self.find_firewalls(client) vlan_firewalls = client[:Account].object_mask(vlan_firewall_mask).object_filter(vlan_firewall_filter).getNetworkVlans vlan_firewalls.collect { |firewall_data| SoftLayer::VLANFirewall.new(client, firewall_data)} end + + def service + # Objects of this class are a bit odd because they actually represent VLANs (the VLAN protected by the firewall) + # and not the physical hardware implementing the firewall itself. (although the device is accessible as the + # "networkVlanFirewall" property) + self.softlayer_client[:Network_Vlan].object_with_id(self.id) + end + + def softlayer_properties(object_mask = nil) + service = self.service + service = service.object_mask(object_mask) if object_mask + service.object_mask(self.class.vlan_firewall_mask).getObject + end private + # Searches the set of access control lists for the firewall device in order to locate the one that + # sits on the "outside" side of the network and handles 'in'coming traffic. + def rules_ACL_id + outside_interface_data = self['firewallInterfaces'].find { |interface_data| interface_data['name'] == 'outside' } + incoming_ACL = outside_interface_data['firewallContextAccessControlLists'].find { |firewallACL_data| firewallACL_data['direction'] == 'in' } if outside_interface_data + + if incoming_ACL + return incoming_ACL['id'] + else + return nil + end + end + def self.vlan_firewall_mask - return "mask[primaryRouter,dedicatedFirewallFlag,highAvailabilityFirewallFlag,"+ + return "mask[primaryRouter,highAvailabilityFirewallFlag,"+ "firewallInterfaces.firewallContextAccessControlLists," + "networkVlanFirewall[id, datacenter, primaryIpAddress, firewallType, fullyQualifiedDomainName]]" end + + def self.default_rules_mask + return { "mask" => default_rules_mask_keys }.to_sl_object_mask + end + + def self.default_rules_mask_keys + ['orderValue','action','destinationIpAddress','destinationIpSubnetMask',"protocol","destinationPortRangeStart","destinationPortRangeEnd",'sourceIpAddress',"sourceIpSubnetMask","version"] + end end # class Firewall end # module SoftLayer \ No newline at end of file diff --git a/lib/softlayer/VLANFirewallRuleset.rb b/lib/softlayer/VLANFirewallRuleset.rb deleted file mode 100644 index 23b82a9..0000000 --- a/lib/softlayer/VLANFirewallRuleset.rb +++ /dev/null @@ -1,40 +0,0 @@ -#-- -# Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. -# -# For licensing information see the LICENSE.md file in the project root. -#++ - -module SoftLayer - class VLANFirewallRuleset < SoftLayer::ModelBase - - # The initial set of rules is taken from the network API, - # but then the user is free to manipulate the rules and - # ask the system to update the firewall. As a result - # the rules property can get out-of-sync with the actual rules - # on the network. To re-fetch the set of rules from the network - # use the #refresh_rules method. - attr_accessor :rules - - def initialize(softlayer_client, network_hash) - super(softlayer_client, network_hash) - self.refresh_rules - end - - def service - return self.softlayer_client[:Network_Firewall_AccessControlList].object_with_id(self.id) - end - - def softlayer_properties(object_mask = nil) - self.service.object_mask(self.class.default_object_mask).getObject - end - - def self.default_rules_mask - return "mask[orderValue,action,destinationIpAddress,destinationIpSubnetMask,protocol,destinationPortRangeStart,destinationPortRangeEnd,sourceIpAddress,sourceIpSubnetMask,version]" - end - - def refresh_rules - self.rules = self.softlayer_client[:Network_Firewall_AccessControlList].object_with_id(self.id).object_mask(self.class.default_rules_mask).getRules - end - end -end - diff --git a/lib/softlayer_api.rb b/lib/softlayer_api.rb index 9011717..c5135c9 100755 --- a/lib/softlayer_api.rb +++ b/lib/softlayer_api.rb @@ -26,13 +26,13 @@ require 'softlayer/BareMetalServer' require 'softlayer/BareMetalServerOrder' require 'softlayer/BareMetalServerOrder_Package' -require 'softlayer/VLANFirewall' -require 'softlayer/VLANFirewallRuleset' require 'softlayer/ImageTemplate' require 'softlayer/NetworkComponent' require 'softlayer/ProductPackage' require 'softlayer/ProductItemCategory' +require 'softlayer/ServerFirewall' require 'softlayer/Ticket' require 'softlayer/VirtualServer' require 'softlayer/VirtualServerOrder' require 'softlayer/VirtualServerUpgradeOrder' +require 'softlayer/VLANFirewall' diff --git a/spec/VLANFirewall_spec.rb b/spec/VLANFirewall_spec.rb index 5f8b21c..8788d98 100644 --- a/spec/VLANFirewall_spec.rb +++ b/spec/VLANFirewall_spec.rb @@ -10,8 +10,8 @@ require 'softlayer_api' require 'rspec' -describe SoftLayer::Firewall do +describe SoftLayer::VLANFirewall do it "should have a class representing a firewall" do - expect{ SoftLayer::Firewall.new("not really a client", { "id" => 12345 }) }.to_not raise_error + expect{ SoftLayer::VLANFirewall.new("not really a client", { "id" => 12345 }) }.to_not raise_error end end \ No newline at end of file From 5a7a606e88d77776c2c385943c02dd0780656175 Mon Sep 17 00:00:00 2001 From: Scott Thompson Date: Tue, 12 Aug 2014 10:05:15 -0500 Subject: [PATCH 25/32] A large number of cosmetic changes. Clients are now passed symbols representing the SoftLayer services. This is because they are in essece fixed, well known constants and symbols more properly capture that nature. Also cases were we dereference properties now use single quotes instead of double quotes to capture the fact that they too are largely constants and not 'composeable' arbitrary strings. Finally this change includes the latest round of killing off trailing whitespace --- lib/softlayer/Account.rb | 4 +- lib/softlayer/BareMetalServer.rb | 12 +-- lib/softlayer/BareMetalServerOrder.rb | 24 +++--- lib/softlayer/BareMetalServerOrder_Package.rb | 6 +- lib/softlayer/Config.rb | 2 +- lib/softlayer/ImageTemplate.rb | 22 +++--- lib/softlayer/ModelBase.rb | 2 +- lib/softlayer/ProductItemCategory.rb | 2 +- lib/softlayer/ProductPackage.rb | 12 +-- lib/softlayer/ServerFirewall.rb | 30 +++++--- lib/softlayer/Service.rb | 6 +- lib/softlayer/Ticket.rb | 16 ++-- lib/softlayer/VLANFirewall.rb | 23 ++++-- lib/softlayer/VirtualServer.rb | 6 +- lib/softlayer/VirtualServerOrder.rb | 32 ++++---- lib/softlayer/VirtualServerUpgradeOrder.rb | 20 ++--- spec/APIParameterFilter_spec.rb | 4 +- spec/Account_spec.rb | 8 +- spec/BareMetalServerOrder_Package_spec.rb | 14 ++-- spec/BareMetalServerOrder_spec.rb | 60 +++++++-------- spec/BareMetalServer_spec.rb | 2 +- spec/Client_spec.rb | 8 +- spec/ModelBase_spec.rb | 2 +- spec/ProductPackage_spec.rb | 6 +- spec/Service_spec.rb | 2 +- spec/Ticket_spec.rb | 4 +- spec/VirtualServerOrder_spec.rb | 74 +++++++++---------- spec/VirtualServerUpgradeOrder_spec.rb | 20 ++--- spec/XMLRPC_Convert_spec.rb | 4 +- 29 files changed, 220 insertions(+), 207 deletions(-) diff --git a/lib/softlayer/Account.rb b/lib/softlayer/Account.rb index 708c033..f7823da 100644 --- a/lib/softlayer/Account.rb +++ b/lib/softlayer/Account.rb @@ -123,7 +123,7 @@ class Account < SoftLayer::ModelBase end def service - softlayer_client["Account"].object_with_id(self.id) + softlayer_client[:Account].object_with_id(self.id) end ## @@ -134,7 +134,7 @@ def self.account_for_client(client = nil) softlayer_client = client || Client.default_client raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client - account_service = softlayer_client['Account'] + account_service = softlayer_client[:Account] network_hash = account_service.getObject() new(softlayer_client, network_hash) end diff --git a/lib/softlayer/BareMetalServer.rb b/lib/softlayer/BareMetalServer.rb index 7d46218..f408fd0 100644 --- a/lib/softlayer/BareMetalServer.rb +++ b/lib/softlayer/BareMetalServer.rb @@ -27,7 +27,7 @@ class BareMetalServer < Server # def bare_metal_instance? if has_sl_property?(:bareMetalInstanceFlag) - self["bareMetalInstanceFlag"] != 0 + self['bareMetalInstanceFlag'] != 0 else false end @@ -47,10 +47,10 @@ def cancel!(reason = :unneeded, comment = '') if !bare_metal_instance? then cancellation_reasons = self.class.cancellation_reasons() cancel_reason = cancellation_reasons[reason] || cancellation_reasons[:unneeded] - softlayer_client["Ticket"].createCancelServerTicket(self.id, cancel_reason, comment, true, 'HARDWARE') + softlayer_client[:Ticket].createCancelServerTicket(self.id, cancel_reason, comment, true, 'HARDWARE') else # Note that reason and comment are ignored in this case, unfortunately - softlayer_client['Billing_Item'].object_with_id(self.billingItem['id'].to_i).cancelService() + softlayer_client[:Billing_Item].object_with_id(self.billingItem['id'].to_i).cancelService() end end @@ -59,7 +59,7 @@ def cancel!(reason = :unneeded, comment = '') # For Bare Metal Servers that is +SoftLayer_Hardware+ though in some special cases # you may have to use +SoftLayer_Hardware_Server+ as a type or service. def service - return softlayer_client["Hardware"].object_with_id(self.id) + return softlayer_client[:Hardware].object_with_id(self.id) end ## @@ -120,7 +120,7 @@ def self.server_with_id(server_id, options = {}) softlayer_client = options[:client] || Client.default_client raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client - hardware_service = softlayer_client["Hardware"] + hardware_service = softlayer_client[:Hardware] hardware_service = hardware_service.object_mask(default_object_mask.to_sl_object_mask) if options.has_key?(:object_mask) @@ -199,7 +199,7 @@ def self.find_servers(options_hash = {}) } ); end - account_service = softlayer_client['Account'] + account_service = softlayer_client[:Account] account_service = account_service.object_filter(object_filter) unless object_filter.empty? account_service = account_service.object_mask(default_object_mask.to_sl_object_mask) diff --git a/lib/softlayer/BareMetalServerOrder.rb b/lib/softlayer/BareMetalServerOrder.rb index f1fe47f..9be737c 100644 --- a/lib/softlayer/BareMetalServerOrder.rb +++ b/lib/softlayer/BareMetalServerOrder.rb @@ -111,7 +111,7 @@ def verify() order_template = hardware_instance_template order_template = yield order_template if block_given? - @softlayer_client["Hardware"].generateOrderTemplate(order_template) + @softlayer_client[:Hardware].generateOrderTemplate(order_template) end ## @@ -124,8 +124,8 @@ def place_order!() order_template = hardware_instance_template order_template = yield order_template if block_given? - server_hash = @softlayer_client["Hardware"].createObject(order_template) - SoftLayer::BareMetalServer.server_with_id(server_hash["id"], :client => @softlayer_client) if server_hash + server_hash = @softlayer_client[:Hardware].createObject(order_template) + SoftLayer::BareMetalServer.server_with_id(server_hash['id'], :client => @softlayer_client) if server_hash end protected @@ -147,15 +147,15 @@ def hardware_instance_template "hourlyBillingFlag" => !!@hourly } - template["privateNetworkOnlyFlag"] = true if @private_network_only + template['privateNetworkOnlyFlag'] = true if @private_network_only - template["datacenter"] = {"name" => @datacenter.name} if @datacenter + template['datacenter'] = {"name" => @datacenter.name} if @datacenter template['userData'] = [{'value' => @user_metadata}] if @user_metadata template['networkComponents'] = [{'maxSpeed'=> @max_port_speed}] if @max_port_speed template['postInstallScriptUri'] = @provision_script_URI.to_s if @provision_script_URI template['sshKeys'] = @ssh_key_ids.collect { |ssh_key| {'id'=> ssh_key.to_i } } if @ssh_key_ids template['primaryNetworkComponent'] = { "networkVlan" => { "id" => @public_vlan_id.to_i } } if @public_vlan_id - template["primaryBackendNetworkComponent"] = { "networkVlan" => {"id" => @private_vlan_id.to_i } } if @private_vlan_id + template['primaryBackendNetworkComponent'] = { "networkVlan" => {"id" => @private_vlan_id.to_i } } if @private_vlan_id if @disks && !@disks.empty? template['hardDrives'] = @disks.collect do |disk| @@ -174,36 +174,36 @@ def self.create_object_options(client = nil) raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client @@create_object_options ||= nil - @@create_object_options = softlayer_client["Hardware"].getCreateObjectOptions() if !@@create_object_options + @@create_object_options = softlayer_client[:Hardware].getCreateObjectOptions() if !@@create_object_options @@create_object_options end ## # Return a list of values that are valid for the :datacenter attribute def self.datacenter_options(client = nil) - create_object_options(client)["datacenters"].collect { |datacenter_spec| Datacenter.datacenter_named(datacenter_spec['template']['datacenter']['name'], client) }.uniq + create_object_options(client)['datacenters'].collect { |datacenter_spec| Datacenter.datacenter_named(datacenter_spec['template']['datacenter']['name'], client) }.uniq end def self.core_options(client = nil) - create_object_options(client)["processors"].collect { |processor_spec| processor_spec['template']['processorCoreAmount'] }.uniq.sort! + create_object_options(client)['processors'].collect { |processor_spec| processor_spec['template']['processorCoreAmount'] }.uniq.sort! end ## # Return a list of values that are valid the array given to the :disks def self.disk_options(client = nil) - create_object_options(client)["hardDrives"].collect { |disk_spec| disk_spec['template']['hardDrives'][0]['capacity'].to_i}.uniq.sort! + create_object_options(client)['hardDrives'].collect { |disk_spec| disk_spec['template']['hardDrives'][0]['capacity'].to_i}.uniq.sort! end ## # Returns a list of the valid :os_refrence_codes def self.os_reference_code_options(client = nil) - create_object_options(client)["operatingSystems"].collect { |os_spec| os_spec['template']['operatingSystemReferenceCode'] }.uniq.sort! + create_object_options(client)['operatingSystems'].collect { |os_spec| os_spec['template']['operatingSystemReferenceCode'] }.uniq.sort! end ## # Returns a list of the :max_port_speeds def self.max_port_speed_options(client = nil) - create_object_options(client)["networkComponents"].collect { |component_spec| component_spec['template']['networkComponents'][0]['maxSpeed'] } + create_object_options(client)['networkComponents'].collect { |component_spec| component_spec['template']['networkComponents'][0]['maxSpeed'] } end end # class BareMetalServerOrder diff --git a/lib/softlayer/BareMetalServerOrder_Package.rb b/lib/softlayer/BareMetalServerOrder_Package.rb index 80c7f16..4943811 100644 --- a/lib/softlayer/BareMetalServerOrder_Package.rb +++ b/lib/softlayer/BareMetalServerOrder_Package.rb @@ -58,7 +58,7 @@ class BareMetalServerOrder_Package < Server # An array of the ids of SSH keys to install on the server upon provisioning # To obtain a list of existing SSH keys, call getSshKeys on the SoftLayer_Account service: - # client['Account'].getSshKeys() + # client[:Account].getSshKeys() attr_accessor :ssh_key_ids # The URI of a script to execute on the server after it has been provisioned. This may be @@ -88,7 +88,7 @@ def initialize(package, client = nil) def verify product_order = hardware_order product_order = yield product_order if block_given? - softlayer_client["Product_Order"].verifyOrder(product_order) + softlayer_client[:Product_Order].verifyOrder(product_order) end ## @@ -106,7 +106,7 @@ def verify def place_order! product_order = hardware_order product_order = yield product_order if block_given? - softlayer_client["Product_Order"].placeOrder(product_order) + softlayer_client[:Product_Order].placeOrder(product_order) end protected diff --git a/lib/softlayer/Config.rb b/lib/softlayer/Config.rb index fdac1bc..f9e97e2 100644 --- a/lib/softlayer/Config.rb +++ b/lib/softlayer/Config.rb @@ -89,7 +89,7 @@ def Config.file_settings(*additional_files) search_path.each do |file_path| if File.readable? file_path config = ConfigParser.new file_path - softlayer_section = config["softlayer"] + softlayer_section = config['softlayer'] if softlayer_section result[:username] = softlayer_section['username'] if softlayer_section['username'] diff --git a/lib/softlayer/ImageTemplate.rb b/lib/softlayer/ImageTemplate.rb index e780b87..4d18073 100644 --- a/lib/softlayer/ImageTemplate.rb +++ b/lib/softlayer/ImageTemplate.rb @@ -41,14 +41,14 @@ def rename!(new_name) # true if the image template is a flex image # Note that the publicFlag property comes back as an integer (0 or 1) def public? - self["publicFlag"] != 0 + self['publicFlag'] != 0 end ## # true if the image template is a flex image # Note that the flexImageFlag property comes back as a boolean def flex_image? - !!self["flexImageFlag"] + !!self['flexImageFlag'] end ## @@ -63,7 +63,7 @@ def notes=(new_notes) ## # Returns an array of the tags set on the image def tags - return self["tagReferences"].collect{ |tag_reference| tag_reference["tag"]["name"] } + return self['tagReferences'].collect{ |tag_reference| tag_reference['tag']['name'] } end ## @@ -78,7 +78,7 @@ def tags=(tags_array) ## # Returns the an array containing the datacenters where this image is available. def datacenters - self["datacenters"].collect{ |datacenter_data| SoftLayer::Datacenter.datacenter_named(datacenter_data["name"])} + self['datacenters'].collect{ |datacenter_data| SoftLayer::Datacenter.datacenter_named(datacenter_data['name'])} end ## @@ -107,7 +107,7 @@ def datacenters=(datacenters_array) # def available_datacenters datacenters_data = self.service.getStorageLocations() - datacenters_data.collect { |datacenter_data| SoftLayer::Datacenter.datacenter_named(datacenter_data["name"]) } + datacenters_data.collect { |datacenter_data| SoftLayer::Datacenter.datacenter_named(datacenter_data['name']) } end @@ -116,7 +116,7 @@ def available_datacenters # that this image is shared with def shared_with_accounts accounts_data = self.service.getAccountReferences - accounts_data.collect { |account_data| account_data["accountId"] } + accounts_data.collect { |account_data| account_data['accountId'] } end ## @@ -177,7 +177,7 @@ def wait_until_ready(max_trials, seconds_between_tries = 2) self.refresh_details() parent_ready = !(has_sl_property? :transactionId) || (self[:transactionId] == "") - children_ready = (nil == self["children"].find { |child| child["transactionId"] != "" }) + children_ready = (nil == self['children'].find { |child| child['transactionId'] != "" }) ready = parent_ready && children_ready yield ready if block_given? @@ -191,7 +191,7 @@ def wait_until_ready(max_trials, seconds_between_tries = 2) # ModelBase protocol methods def service - softlayer_client['Virtual_Guest_Block_Device_Template_Group'].object_with_id(self.id) + softlayer_client[:Virtual_Guest_Block_Device_Template_Group].object_with_id(self.id) end def softlayer_properties(object_mask = nil) @@ -245,7 +245,7 @@ def self.find_private_templates(options_hash = {}) } ); end - account_service = softlayer_client['Account'] + account_service = softlayer_client[:Account] account_service = account_service.object_filter(object_filter) unless object_filter.empty? account_service = account_service.object_mask(default_object_mask) @@ -311,7 +311,7 @@ def self.find_public_templates(options_hash = {}) } ); end - template_service = softlayer_client['Virtual_Guest_Block_Device_Template_Group'] + template_service = softlayer_client[:Virtual_Guest_Block_Device_Template_Group] template_service = template_service.object_filter(object_filter) unless object_filter.empty? template_service = template_service.object_mask(default_object_mask) @@ -347,7 +347,7 @@ def self.template_with_id(id, options_hash = {}) softlayer_client = options_hash[:client] || Client.default_client raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client - service = softlayer_client['Virtual_Guest_Block_Device_Template_Group'].object_with_id(id) + service = softlayer_client[:Virtual_Guest_Block_Device_Template_Group].object_with_id(id) service.object_mask(default_object_mask) if options_hash.has_key? :object_mask diff --git a/lib/softlayer/ModelBase.rb b/lib/softlayer/ModelBase.rb index 93ba406..5c105f3 100644 --- a/lib/softlayer/ModelBase.rb +++ b/lib/softlayer/ModelBase.rb @@ -47,7 +47,7 @@ def initialize(softlayer_client, network_hash) # a particular entity in the SoftLayer_Ticket service. The particular # entity is identified by its id so the Ticket class would return # - # softlayer_client["Ticket"].object_with_id + # softlayer_client[:Ticket].object_with_id # # which is a service which would allow calls to the ticket service # through that particular object. diff --git a/lib/softlayer/ProductItemCategory.rb b/lib/softlayer/ProductItemCategory.rb index f9f33e2..7c26b99 100644 --- a/lib/softlayer/ProductItemCategory.rb +++ b/lib/softlayer/ProductItemCategory.rb @@ -84,7 +84,7 @@ class ProductItemCategory < ModelBase end def service - softlayer_client["SoftLayer_Product_Item_Category"].object_with_id(self.id) + softlayer_client[:SoftLayer_Product_Item_Category].object_with_id(self.id) end ## diff --git a/lib/softlayer/ProductPackage.rb b/lib/softlayer/ProductPackage.rb index ac946b5..58189b4 100644 --- a/lib/softlayer/ProductPackage.rb +++ b/lib/softlayer/ProductPackage.rb @@ -65,7 +65,7 @@ class ProductPackage < ModelBase # filtering mechanism on the server side to give us a list of the categories, groups, and prices that are valid for the current # account at the current time. We construct the ProductItemCategory objects from the results we get back. # - configuration_data = softlayer_client['Product_Package'].object_with_id(self.id).object_mask("mask[isRequired,itemCategory.categoryCode]").getConfiguration() + configuration_data = softlayer_client[:Product_Package].object_with_id(self.id).object_mask("mask[isRequired,itemCategory.categoryCode]").getConfiguration() # We sort of invert the information and create a map from category codes to a boolean representing # whether or not they are required. @@ -75,7 +75,7 @@ class ProductPackage < ModelBase end # This call to getCategories is the one that does lots of fancy back-end filtering for us - categories_data = softlayer_client['Product_Package'].object_with_id(self.id).getCategories() + categories_data = softlayer_client[:Product_Package].object_with_id(self.id).getCategories() # Run though the categories and for each one that's in our config, create a SoftLayer::ProductItemCategory object. # Conveniently the +keys+ of the required_by_category_code gives us a list of the category codes in the configuration @@ -129,7 +129,7 @@ def category(category_code) ## # Returns a list of the datacenters that this package is available in def datacenter_options - available_locations.collect { |location_data| Datacenter::datacenter_named(location_data["location"]["name"], self.softlayer_client) }.compact + available_locations.collect { |location_data| Datacenter::datacenter_named(location_data['location']['name'], self.softlayer_client) }.compact end ## @@ -150,7 +150,7 @@ def items_with_description(expected_description) # Returns the service for interacting with this package through the network API # def service - softlayer_client['Product_Package'].object_with_id(self.id) + softlayer_client[:Product_Package].object_with_id(self.id) end ## @@ -165,7 +165,7 @@ def self.packages_with_key_name(key_name, client = nil) filter.accept('type.keyName').when_it is(key_name) end - filtered_service = softlayer_client['Product_Package'].object_filter(filter).object_mask(self.default_object_mask('mask')) + filtered_service = softlayer_client[:Product_Package].object_filter(filter).object_mask(self.default_object_mask('mask')) packages_data = filtered_service.getAllObjects packages_data.collect { |package_data| ProductPackage.new(softlayer_client, package_data) } end @@ -178,7 +178,7 @@ def self.package_with_id(package_id, client = nil) softlayer_client = client || Client.default_client raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client - package_data = softlayer_client['Product_Package'].object_with_id(package_id).object_mask(self.default_object_mask('mask')).getObject + package_data = softlayer_client[:Product_Package].object_with_id(package_id).object_mask(self.default_object_mask('mask')).getObject ProductPackage.new(softlayer_client, package_data) end diff --git a/lib/softlayer/ServerFirewall.rb b/lib/softlayer/ServerFirewall.rb index 63ec77a..a556ea7 100644 --- a/lib/softlayer/ServerFirewall.rb +++ b/lib/softlayer/ServerFirewall.rb @@ -6,13 +6,13 @@ module SoftLayer ## - # The ServerFirewall class represents a firewall in the + # The ServerFirewall class represents a firewall in the # SoftLayer environment that exists in a 1 to 1 relationship # with a particular server (either Bare Metal or Virtual). # # This is also called a "Shared Firewall" in some documentation # - # Instances of this class rougly correspond to instances of the + # Instances of this class rougly correspond to instances of the # SoftLayer_Network_Component_Firewall service entity # class ServerFirewall < SoftLayer::ModelBase @@ -20,9 +20,9 @@ class ServerFirewall < SoftLayer::ModelBase ## # :attr_reader: - # The state of the firewall, includes whether or not the rules are + # The state of the firewall, includes whether or not the rules are # editable and whether or not the firewall rules are applied or bypassed - # Can at least be 'allow_edit', 'bypass' or 'no_edit'. + # Can at least be 'allow_edit', 'bypass' or 'no_edit'. # This list may not be exhaustive sl_attr :status @@ -42,8 +42,8 @@ class ServerFirewall < SoftLayer::ModelBase rules_data = self.service.object_mask(self.class.default_rules_mask).getRules() # At the time of this writing, the object mask sent to getRules is not - # applied properly. This has been reported as a bug to the proper - # development team. In the mean time, this extra step does filtering + # applied properly. This has been reported as a bug to the proper + # development team. In the mean time, this extra step does filtering # that should have been done by the object mask. rules_keys = self.class.default_rules_mask_keys new_rules = rules_data.inject([]) do |new_rules, current_rule| @@ -114,13 +114,13 @@ def change_rules!(rules_data) ## # Locate and return all the server firewalls in the environment. - # + # # These are a bit tricky to track down. The strategy we take here is # to look at the account and find all the VLANs that do NOT have their # "dedicatedFirewallFlag" set. # # With the list of VLANs in hand we check each to see if it has an - # firewallNetworkComponents (corresponding to bare metal servers) or + # firewallNetworkComponents (corresponding to bare metal servers) or # firewallGuestNetworkComponents (corresponding to virtual servers) that # have a status of "allow_edit". Each such component is a firewall # interface on the VLAN with rules that the customer can edit. @@ -143,8 +143,8 @@ def self.find_firewalls(client) shared_vlans = softlayer_client[:Account].object_mask(network_vlan_mask).object_filter(shared_vlans_filter).getNetworkVlans shared_vlans.each do |vlan_data| - bare_metal_firewalls_data.concat vlan_data["firewallNetworkComponents"].select { |network_component| network_component['status'] != 'no_edit'} - virtual_firewalls_data.concat vlan_data["firewallGuestNetworkComponents"].select { |network_component| network_component['status'] != 'no_edit'} + bare_metal_firewalls_data.concat vlan_data['firewallNetworkComponents'].select { |network_component| network_component['status'] != 'no_edit'} + virtual_firewalls_data.concat vlan_data['firewallGuestNetworkComponents'].select { |network_component| network_component['status'] != 'no_edit'} end bare_metal_firewalls = bare_metal_firewalls_data.collect { |bare_metal_firewall_data| @@ -158,14 +158,18 @@ def self.find_firewalls(client) return bare_metal_firewalls + virtual_server_firewalls end + #-- + # Methods for the SoftLayer model + #++ + def service self.softlayer_client[:Network_Component_Firewall].object_with_id(self.id) end - + def softlayer_properties(object_mask = nil) service = self.service service = service.object_mask(object_mask) if object_mask - + if self.has_sl_property?('networkComponent') service.object_mask("mask[id,status,networkComponent.downlinkComponent.hardwareId]").getObject else @@ -173,6 +177,8 @@ def softlayer_properties(object_mask = nil) end end + #-- + #++ private def self.network_vlan_mask diff --git a/lib/softlayer/Service.rb b/lib/softlayer/Service.rb index a633097..6e20664 100644 --- a/lib/softlayer/Service.rb +++ b/lib/softlayer/Service.rb @@ -43,9 +43,9 @@ module XMLRPC::Convert def self.fault(hash) if hash.kind_of? Hash and hash.size == 2 and hash.has_key? "faultCode" and hash.has_key? "faultString" and - (hash["faultCode"].kind_of?(Integer) || hash["faultCode"].kind_of?(String)) and hash["faultString"].kind_of? String + (hash['faultCode'].kind_of?(Integer) || hash['faultCode'].kind_of?(String)) and hash['faultString'].kind_of? String - XMLRPC::FaultException.new(hash["faultCode"], hash["faultString"]) + XMLRPC::FaultException.new(hash['faultCode'], hash['faultString']) else super end @@ -65,7 +65,7 @@ module SoftLayer # # client = SoftLayer::Client.new(:username => "Joe", :api_key=>"feeddeadbeefbadfood...") # account_service = client.service_named("Account") # returns the SoftLayer_Account service - # account_service = client['Account'] # Exactly the same as above + # account_service = client[:Account] # Exactly the same as above # # For backward compatibility, a service can be constructed by passing # client initialization options, however if you do so you will need to diff --git a/lib/softlayer/Ticket.rb b/lib/softlayer/Ticket.rb index 278d6f2..080bef8 100644 --- a/lib/softlayer/Ticket.rb +++ b/lib/softlayer/Ticket.rb @@ -25,14 +25,14 @@ class Ticket < SoftLayer::ModelBase ## # Returns true if the ticket has "unread" updates def has_updates? - self["newUpdatesFlag"] + self['newUpdatesFlag'] end ## # Returns true if the ticket is a server admin ticket def server_admin_ticket? # note that serverAdministrationFlag comes from the server as an Integer (0, or 1) - self["serverAdministrationFlag"] != 0 + self['serverAdministrationFlag'] != 0 end ## @@ -46,7 +46,7 @@ def update(body = nil) # Override of service from ModelBase. Returns the SoftLayer_Ticket service # set up to talk to the ticket with my ID. def service - return softlayer_client["Ticket"].object_with_id(self.id) + return softlayer_client[:Ticket].object_with_id(self.id) end ## @@ -96,7 +96,7 @@ def self.ticket_subjects(client = nil) softlayer_client = client || Client.default_client raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client - @ticket_subjects = softlayer_client['Ticket_Subject'].getAllObjects(); + @ticket_subjects = softlayer_client[:Ticket_Subject].getAllObjects(); end @ticket_subjects @@ -122,7 +122,7 @@ def self.ticket_with_id(ticket_id, options = {}) object_mask = default_object_mask.to_sl_object_mask end - ticket_data = softlayer_client["Ticket"].object_with_id(ticket_id).object_mask(object_mask).getObject() + ticket_data = softlayer_client[:Ticket].object_with_id(ticket_id).object_mask(object_mask).getObject() return new(softlayer_client, ticket_data) end @@ -153,8 +153,8 @@ def self.create_standard_ticket(options = {}) assigned_user_id = options[:assigned_user_id] if(nil == assigned_user_id) - current_user = softlayer_client["Account"].object_mask("id").getCurrentUser() - assigned_user_id = current_user["id"] + current_user = softlayer_client[:Account].object_mask("id").getCurrentUser() + assigned_user_id = current_user['id'] end new_ticket = { @@ -164,7 +164,7 @@ def self.create_standard_ticket(options = {}) 'title' => title } - ticket_data = softlayer_client["Ticket"].createStandardTicket(new_ticket, body) + ticket_data = softlayer_client[:Ticket].createStandardTicket(new_ticket, body) return new(softlayer_client, ticket_data) end end diff --git a/lib/softlayer/VLANFirewall.rb b/lib/softlayer/VLANFirewall.rb index 3b6ab74..8532863 100644 --- a/lib/softlayer/VLANFirewall.rb +++ b/lib/softlayer/VLANFirewall.rb @@ -8,10 +8,10 @@ module SoftLayer # The VLANFirewall class represents the firewall that protects # all the servers on a VLAN in the SoftLayer Environment. It is # also known as a "Dedicated Firewall" in some documentation. - # - # Instances of this class are a bit odd because they actually represent - # VLANs (the VLAN protected by the firewall) and not the physical hardware - # implementing the firewall itself. (although the device is accessible as + # + # Instances of this class are a bit odd because they actually represent + # VLANs (the VLAN protected by the firewall) and not the physical hardware + # implementing the firewall itself. (although the device is accessible as # the "networkVlanFirewall" property) # # As a result, instances of this class correspond to certain instances @@ -19,7 +19,7 @@ module SoftLayer # class VLANFirewall < SoftLayer::ModelBase include ::SoftLayer::DynamicAttribute - + ## #:attr_reader: # @@ -35,8 +35,8 @@ class VLANFirewall < SoftLayer::ModelBase # time you ask it for the rules. # # The code will sort the rules by their "orderValue" which is the - # order that the firewall applies the rules, however please see - # the important note in change_rules! concerning the "orderValue" + # order that the firewall applies the rules, however please see + # the important note in change_rules! concerning the "orderValue" # property of the rules. sl_dynamic_attr :rules do |firewall_rules| firewall_rules.should_update? do @@ -120,7 +120,12 @@ def self.find_firewalls(client) vlan_firewalls = client[:Account].object_mask(vlan_firewall_mask).object_filter(vlan_firewall_filter).getNetworkVlans vlan_firewalls.collect { |firewall_data| SoftLayer::VLANFirewall.new(client, firewall_data)} end - + + + #-- + # Methods for the SoftLayer model + #++ + def service # Objects of this class are a bit odd because they actually represent VLANs (the VLAN protected by the firewall) # and not the physical hardware implementing the firewall itself. (although the device is accessible as the @@ -134,6 +139,8 @@ def softlayer_properties(object_mask = nil) service.object_mask(self.class.vlan_firewall_mask).getObject end + #-- + #++ private # Searches the set of access control lists for the firewall device in order to locate the one that diff --git a/lib/softlayer/VirtualServer.rb b/lib/softlayer/VirtualServer.rb index 764e2fc..5116997 100644 --- a/lib/softlayer/VirtualServer.rb +++ b/lib/softlayer/VirtualServer.rb @@ -164,7 +164,7 @@ def self.server_with_id(server_id, options = {}) softlayer_client = options[:client] || Client.default_client raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client - vg_service = softlayer_client["Virtual_Guest"] + vg_service = softlayer_client[:Virtual_Guest] vg_service = vg_service.object_mask(default_object_mask.to_sl_object_mask) if options.has_key?(:object_mask) @@ -251,7 +251,7 @@ def self.find_servers(options_hash = {}) required_properties_mask = 'mask.id' - account_service = softlayer_client['Account'] + account_service = softlayer_client[:Account] account_service = account_service.object_filter(object_filter) unless object_filter.empty? account_service = account_service.object_mask(default_object_mask.to_sl_object_mask) @@ -310,7 +310,7 @@ def self.default_object_mask # For VirtualServers the service is +SoftLayer_Virtual_Guest+ and # addressing this object is done by id. def service - return softlayer_client["Virtual_Guest"].object_with_id(self.id) + return softlayer_client[:Virtual_Guest].object_with_id(self.id) end end #class VirtualServer end \ No newline at end of file diff --git a/lib/softlayer/VirtualServerOrder.rb b/lib/softlayer/VirtualServerOrder.rb index b827131..7463a63 100644 --- a/lib/softlayer/VirtualServerOrder.rb +++ b/lib/softlayer/VirtualServerOrder.rb @@ -119,7 +119,7 @@ def verify() order_template = virtual_guest_template order_template = yield order_template if block_given? - @softlayer_client["Virtual_Guest"].generateOrderTemplate(order_template) + @softlayer_client[:Virtual_Guest].generateOrderTemplate(order_template) end # Calls the SoftLayer API to place an order for a new virtual server based on the template in this @@ -132,8 +132,8 @@ def place_order!() order_template = virtual_guest_template order_template = yield order_template if block_given? - virtual_server_hash = @softlayer_client["Virtual_Guest"].createObject(order_template) - SoftLayer::VirtualServer.server_with_id(virtual_server_hash["id"], :client => @softlayer_client) if virtual_server_hash + virtual_server_hash = @softlayer_client[:Virtual_Guest].createObject(order_template) + SoftLayer::VirtualServer.server_with_id(virtual_server_hash['id'], :client => @softlayer_client) if virtual_server_hash end protected @@ -153,21 +153,21 @@ def virtual_guest_template "hourlyBillingFlag" => !!@hourly } - template["dedicatedAccountHostOnlyFlag"] = true if @dedicated_host_only - template["privateNetworkOnlyFlag"] = true if @private_network_only + template['dedicatedAccountHostOnlyFlag'] = true if @dedicated_host_only + template['privateNetworkOnlyFlag'] = true if @private_network_only - template["datacenter"] = {"name" => @datacenter.name} if @datacenter + template['datacenter'] = {"name" => @datacenter.name} if @datacenter template['userData'] = [{'value' => @user_metadata}] if @user_metadata template['networkComponents'] = [{'maxSpeed'=> @max_port_speed}] if @max_port_speed template['postInstallScriptUri'] = @provision_script_URI.to_s if @provision_script_URI template['sshKeys'] = @ssh_key_ids.collect { |ssh_key_id| {'id'=> ssh_key_id.to_i } } if @ssh_key_ids template['primaryNetworkComponent'] = { "networkVlan" => { "id" => @public_vlan_id.to_i } } if @public_vlan_id - template["primaryBackendNetworkComponent"] = { "networkVlan" => {"id" => @private_vlan_id.to_i } } if @private_vlan_id + template['primaryBackendNetworkComponent'] = { "networkVlan" => {"id" => @private_vlan_id.to_i } } if @private_vlan_id if @image_template - template["blockDeviceTemplateGroup"] = {"globalIdentifier" => @image_template.global_id} + template['blockDeviceTemplateGroup'] = {"globalIdentifier" => @image_template.global_id} elsif @os_reference_code - template["operatingSystemReferenceCode"] = @os_reference_code + template['operatingSystemReferenceCode'] = @os_reference_code end if @disks && !@disks.empty? @@ -196,7 +196,7 @@ def self.create_object_options(client = nil) raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client @@create_object_options ||= nil - @@create_object_options = softlayer_client["Virtual_Guest"].getCreateObjectOptions() if !@@create_object_options + @@create_object_options = softlayer_client[:Virtual_Guest].getCreateObjectOptions() if !@@create_object_options @@create_object_options end @@ -210,37 +210,37 @@ def self.create_object_options(client = nil) ## # Return a list of values that are valid for the :datacenter attribute def self.datacenter_options(client = nil) - create_object_options(client)["datacenters"].collect { |datacenter_spec| Datacenter.datacenter_named(datacenter_spec['template']['datacenter']['name'], client) }.uniq + create_object_options(client)['datacenters'].collect { |datacenter_spec| Datacenter.datacenter_named(datacenter_spec['template']['datacenter']['name'], client) }.uniq end ## # Return a list of values that are valid for the :cores attribute def self.core_options(client = nil) - create_object_options(client)["processors"].collect { |processor_spec| processor_spec['template']['startCpus'] }.uniq.sort! + create_object_options(client)['processors'].collect { |processor_spec| processor_spec['template']['startCpus'] }.uniq.sort! end ## # Return a list of values that are valid for the :memory attribute def self.memory_options(client = nil) - create_object_options(client)["memory"].collect { |memory_spec| memory_spec['template']['maxMemory'].to_i / 1024}.uniq.sort! + create_object_options(client)['memory'].collect { |memory_spec| memory_spec['template']['maxMemory'].to_i / 1024}.uniq.sort! end ## # Return a list of values that are valid the array given to the :disks def self.disk_options(client = nil) - create_object_options(client)["blockDevices"].collect { |block_device_spec| block_device_spec['template']['blockDevices'][0]['diskImage']['capacity']}.uniq.sort! + create_object_options(client)['blockDevices'].collect { |block_device_spec| block_device_spec['template']['blockDevices'][0]['diskImage']['capacity']}.uniq.sort! end ## # Returns a list of the valid :os_refrence_codes def self.os_reference_code_options(client = nil) - create_object_options(client)["operatingSystems"].collect { |os_spec| os_spec['template']['operatingSystemReferenceCode'] }.uniq.sort! + create_object_options(client)['operatingSystems'].collect { |os_spec| os_spec['template']['operatingSystemReferenceCode'] }.uniq.sort! end ## # Returns a list of the :max_port_speeds def self.max_port_speed_options(client = nil) - create_object_options(client)["networkComponents"].collect { |component_spec| component_spec['template']['networkComponents'][0]['maxSpeed'] } + create_object_options(client)['networkComponents'].collect { |component_spec| component_spec['template']['networkComponents'][0]['maxSpeed'] } end end # class VirtualServerOrder end # module SoftLayer diff --git a/lib/softlayer/VirtualServerUpgradeOrder.rb b/lib/softlayer/VirtualServerUpgradeOrder.rb index 4a7c0b9..9c56da2 100644 --- a/lib/softlayer/VirtualServerUpgradeOrder.rb +++ b/lib/softlayer/VirtualServerUpgradeOrder.rb @@ -54,7 +54,7 @@ def verify() order_object = self.order_object order_object = yield order_object if block_given? - @virtual_server.softlayer_client["Product_Order"].verifyOrder(order_object) + @virtual_server.softlayer_client[:Product_Order].verifyOrder(order_object) end end @@ -70,26 +70,26 @@ def place_order!() order_object = self.order_object order_object = yield order_object if block_given? - @virtual_server.softlayer_client["Product_Order"].placeOrder(order_object) + @virtual_server.softlayer_client[:Product_Order].placeOrder(order_object) end end ## # Return a list of values that are valid for the :cores attribute def core_options() - self._item_prices_in_category("guest_core").map { |item_price| item_price["item"]["capacity"].to_i}.sort.uniq + self._item_prices_in_category("guest_core").map { |item_price| item_price['item']['capacity'].to_i}.sort.uniq end ## # Return a list of values that are valid for the :memory attribute def memory_options() - self._item_prices_in_category("ram").map { |item_price| item_price["item"]["capacity"].to_i}.sort.uniq + self._item_prices_in_category("ram").map { |item_price| item_price['item']['capacity'].to_i}.sort.uniq end ## # Returns a list of valid values for max_port_speed def max_port_speed_options(client = nil) - self._item_prices_in_category("port_speed").map { |item_price| item_price["item"]["capacity"].to_i}.sort.uniq + self._item_prices_in_category("port_speed").map { |item_price| item_price['item']['capacity'].to_i}.sort.uniq end private @@ -105,7 +105,7 @@ def has_order_items? # Returns a list of the update item prices, in the given category, for the server # def _item_prices_in_category(which_category) - @virtual_server.upgrade_options.select { |item_price| item_price["categories"].find { |category| category["categoryCode"] == which_category } } + @virtual_server.upgrade_options.select { |item_price| item_price['categories'].find { |category| category['categoryCode'] == which_category } } end ## @@ -113,7 +113,7 @@ def _item_prices_in_category(which_category) # and whose capacity matches the value given. Returns the item_price or nil # def _item_price_with_capacity(which_category, capacity) - self._item_prices_in_category(which_category).find { |item_price| item_price["item"]["capacity"].to_i == capacity} + self._item_prices_in_category(which_category).find { |item_price| item_price['item']['capacity'].to_i == capacity} end ## @@ -126,9 +126,9 @@ def order_object ram_price_item = @ram ? _item_price_with_capacity("ram", @ram) : nil max_port_speed_price_item = @max_port_speed ? _item_price_with_capacity("port_speed", @max_port_speed) : nil - prices << { "id" => cores_price_item["id"] } if cores_price_item - prices << { "id" => ram_price_item["id"] } if ram_price_item - prices << { "id" => max_port_speed_price_item["id"] } if max_port_speed_price_item + prices << { "id" => cores_price_item['id'] } if cores_price_item + prices << { "id" => ram_price_item['id'] } if ram_price_item + prices << { "id" => max_port_speed_price_item['id'] } if max_port_speed_price_item # put together an order upgrade_order = { diff --git a/spec/APIParameterFilter_spec.rb b/spec/APIParameterFilter_spec.rb index 9e46c9e..493b2eb 100644 --- a/spec/APIParameterFilter_spec.rb +++ b/spec/APIParameterFilter_spec.rb @@ -42,7 +42,7 @@ end it "rejects calls that pass things other than strings" do - expect { filter.object_mask(["anArray"]) }.to raise_error + expect { filter.object_mask(['anArray']) }.to raise_error expect { filter.object_mask({"a" => "hash"}) }.to raise_error expect { filter.object_mask(Object.new) }.to raise_error end @@ -90,7 +90,7 @@ target = double("method_missing_target") filter = SoftLayer::APIParameterFilter.new(target).object_mask("mask.fish", "mask[cow]", "mask(typed).duck", "mask(typed)[chicken]").object_with_id(12345) - expect(target).to receive(:call_softlayer_api_with_params).with(:getObject, filter, ["marshmallow"]) + expect(target).to receive(:call_softlayer_api_with_params).with(:getObject, filter, ['marshmallow']) filter.getObject("marshmallow") end diff --git a/spec/Account_spec.rb b/spec/Account_spec.rb index 385a8bf..118d11a 100644 --- a/spec/Account_spec.rb +++ b/spec/Account_spec.rb @@ -26,7 +26,7 @@ it "identifies itself with the Account service" do mock_client = SoftLayer::Client.new(:username => "fake_user", :api_key => "BADKEY") allow(mock_client).to receive(:[]) do |service_name| - expect(service_name).to eq "Account" + expect(service_name).to eq :Account mock_service = SoftLayer::Service.new("SoftLayer_Account", :client => mock_client) # mock out call_softlayer_api_with_params so the service doesn't actually try to @@ -44,7 +44,7 @@ it "should allow the user to get the default account for a service" do test_client = double("mockClient") allow(test_client).to receive(:[]) do |service_name| - expect(service_name).to eq "Account" + expect(service_name).to eq :Account test_service = double("mockService") allow(test_service).to receive(:getObject) do @@ -82,7 +82,7 @@ it "fetches a list of open tickets" do mock_client = SoftLayer::Client.new(:username => "fakeuser", :api_key => "fake_api_key") - account_service = mock_client["Account"] + account_service = mock_client[:Account] expect(account_service).to receive(:call_softlayer_api_with_params).with(:getOpenTickets, instance_of(SoftLayer::APIParameterFilter),[]) do fixture_from_json("test_tickets") @@ -98,7 +98,7 @@ describe "relationship to servers" do it "should respond to a request for servers" do mock_client = SoftLayer::Client.new(:username => "fakeuser", :api_key => "fake_api_key") - account_service = mock_client["Account"] + account_service = mock_client[:Account] allow(account_service).to receive(:getObject).and_return(fixture_from_json("test_account")) allow(account_service).to receive(:call_softlayer_api_with_params) do |api_method, api_filter, arguments| case api_method diff --git a/spec/BareMetalServerOrder_Package_spec.rb b/spec/BareMetalServerOrder_Package_spec.rb index 977b806..41a95ec 100644 --- a/spec/BareMetalServerOrder_Package_spec.rb +++ b/spec/BareMetalServerOrder_Package_spec.rb @@ -45,13 +45,13 @@ end it 'places the package id from which it was ordered into the order template' do - expect(test_order.hardware_order["packageId"]).to eq 32 + expect(test_order.hardware_order['packageId']).to eq 32 end it "places its :datacenter attribute into the order template" do - expect(test_order.hardware_order["location"]).to be_nil + expect(test_order.hardware_order['location']).to be_nil test_order.datacenter = test_datacenter - expect(test_order.hardware_order["location"]).to eq 224092 + expect(test_order.hardware_order['location']).to eq 224092 end it "places its :hostname attribute into the hardware template in the order" do @@ -109,7 +109,7 @@ def config_option_2.price_id client = SoftLayer::Client.new(:username => "fakeusername", :api_key => 'DEADBEEFBADF00D') package = SoftLayer::ProductPackage.new(client, fixture_from_json("Product_Package")) - order_service = client["Product_Order"] + order_service = client[:Product_Order] allow(order_service).to receive(:call_softlayer_api_with_params) test_order = SoftLayer::BareMetalServerOrder_Package.new(package, client) @@ -126,7 +126,7 @@ def config_option_2.price_id client = SoftLayer::Client.new(:username => "fakeusername", :api_key => 'DEADBEEFBADF00D') package = SoftLayer::ProductPackage.new(client, fixture_from_json("Product_Package")) - order_service = client["Product_Order"] + order_service = client[:Product_Order] allow(order_service).to receive(:call_softlayer_api_with_params) test_order = SoftLayer::BareMetalServerOrder_Package.new(package, client) @@ -143,7 +143,7 @@ def config_option_2.price_id client = SoftLayer::Client.new(:username => "fakeusername", :api_key => 'DEADBEEFBADF00D') package = SoftLayer::ProductPackage.new(client, fixture_from_json("Product_Package")) - order_service = client["Product_Order"] + order_service = client[:Product_Order] allow(order_service).to receive(:call_softlayer_api_with_params) test_order = SoftLayer::BareMetalServerOrder_Package.new(package, client) @@ -161,7 +161,7 @@ def config_option_2.price_id client = SoftLayer::Client.new(:username => "fakeusername", :api_key => 'DEADBEEFBADF00D') package = SoftLayer::ProductPackage.new(client, fixture_from_json("Product_Package")) - order_service = client["Product_Order"] + order_service = client[:Product_Order] allow(order_service).to receive(:call_softlayer_api_with_params) test_order = SoftLayer::BareMetalServerOrder_Package.new(package, client) diff --git a/spec/BareMetalServerOrder_spec.rb b/spec/BareMetalServerOrder_spec.rb index e75c89c..a5c5b66 100644 --- a/spec/BareMetalServerOrder_spec.rb +++ b/spec/BareMetalServerOrder_spec.rb @@ -36,68 +36,68 @@ it "places its :datacenter attribute into the order template" do client = SoftLayer::Client.new(:username => "fakeusername", :api_key => 'DEADBEEFBADF00D') - expect(subject.hardware_instance_template["datacenter"]).to be_nil + expect(subject.hardware_instance_template['datacenter']).to be_nil subject.datacenter = SoftLayer::Datacenter.new(client, 'id' => 42, 'name' => "dal05") - expect(subject.hardware_instance_template["datacenter"]).to eq({ "name" => "dal05" }) + expect(subject.hardware_instance_template['datacenter']).to eq({ "name" => "dal05" }) end it "places its :hostname attribute into the order template" do - expect(subject.hardware_instance_template["hostname"]).to be_nil + expect(subject.hardware_instance_template['hostname']).to be_nil subject.hostname = "testhostname" - expect(subject.hardware_instance_template["hostname"]).to eq "testhostname" + expect(subject.hardware_instance_template['hostname']).to eq "testhostname" end it "places its :domain attribute into the order template" do - expect(subject.hardware_instance_template["domain"]).to be_nil + expect(subject.hardware_instance_template['domain']).to be_nil subject.domain = "softlayer.com" - expect(subject.hardware_instance_template["domain"]).to eq "softlayer.com" + expect(subject.hardware_instance_template['domain']).to eq "softlayer.com" end it "places its :cores attribute into the order template as startCpus" do subject.cores = 4 - expect(subject.hardware_instance_template["processorCoreAmount"]).to eq 4 + expect(subject.hardware_instance_template['processorCoreAmount']).to eq 4 end it "places the :memory attrbute in the template as memoryCapacity" do subject.memory = 4 - expect(subject.hardware_instance_template["memoryCapacity"]).to eq 4 + expect(subject.hardware_instance_template['memoryCapacity']).to eq 4 end it "places an OS identifier into the order template as the operatingSystemReferenceCode" do - expect(subject.hardware_instance_template["operatingSystemReferenceCode"]).to be_nil + expect(subject.hardware_instance_template['operatingSystemReferenceCode']).to be_nil subject.os_reference_code = 'UBUNTU_12_64' expect(subject.hardware_instance_template['operatingSystemReferenceCode']).to eq 'UBUNTU_12_64' end it "places the attribute :hourly into the template as hourlyBillingFlag converting the value to a boolean constant" do # note, we don't want the flag to be nil we want it to be eotjer false or true - expect(subject.hardware_instance_template["hourlyBillingFlag"]).to be(false) + expect(subject.hardware_instance_template['hourlyBillingFlag']).to be(false) subject.hourly = true - expect(subject.hardware_instance_template["hourlyBillingFlag"]).to be(true) + expect(subject.hardware_instance_template['hourlyBillingFlag']).to be(true) subject.hourly = false - expect(subject.hardware_instance_template["hourlyBillingFlag"]).to be(false) + expect(subject.hardware_instance_template['hourlyBillingFlag']).to be(false) end it "puts the public VLAN id into an order template as primaryNetworkComponent.networkVlan.id" do - expect(subject.hardware_instance_template["primaryNetworkComponent"]).to be_nil + expect(subject.hardware_instance_template['primaryNetworkComponent']).to be_nil subject.public_vlan_id = 12345 - expect(subject.hardware_instance_template["primaryNetworkComponent"]).to eq({ "networkVlan" => { "id" => 12345 } }) + expect(subject.hardware_instance_template['primaryNetworkComponent']).to eq({ "networkVlan" => { "id" => 12345 } }) end it "puts the private VLAN id into an order template as primaryBackendNetworkComponent.networkVlan.id" do - expect(subject.hardware_instance_template["primaryBackendNetworkComponent"]).to be_nil + expect(subject.hardware_instance_template['primaryBackendNetworkComponent']).to be_nil subject.private_vlan_id = 12345 - expect(subject.hardware_instance_template["primaryBackendNetworkComponent"]).to eq({ "networkVlan" => { "id" => 12345 } }) + expect(subject.hardware_instance_template['primaryBackendNetworkComponent']).to eq({ "networkVlan" => { "id" => 12345 } }) end it "sets up disks in the order template as hardDrives" do - expect(subject.hardware_instance_template["hardDrives"]).to be_nil + expect(subject.hardware_instance_template['hardDrives']).to be_nil subject.disks = [2, 25, 50] # note that device id 1 should be skipped as SoftLayer reserves that id for OS swap space. - expect(subject.hardware_instance_template["hardDrives"]).to eq [ + expect(subject.hardware_instance_template['hardDrives']).to eq [ {"capacity"=>2}, {"capacity"=>25}, {"capacity"=>50} @@ -105,37 +105,37 @@ end it "puts the :ssh_key_ids in the template as sshKeys and breaks out the ids into objects" do - expect(subject.hardware_instance_template["sshKeys"]).to be_nil + expect(subject.hardware_instance_template['sshKeys']).to be_nil subject.ssh_key_ids = [123, 456, 789] expect(subject.hardware_instance_template['sshKeys']).to eq [{'id' => 123}, {'id' => 456}, {'id' => 789}] end it "puts the :provision_script_URI property into the template as postInstallScriptUri" do - expect(subject.hardware_instance_template["postInstallScriptUri"]).to be_nil + expect(subject.hardware_instance_template['postInstallScriptUri']).to be_nil subject.provision_script_URI = 'http:/provisionhome.mydomain.com/fancyscript.sh' expect(subject.hardware_instance_template['postInstallScriptUri']).to eq 'http:/provisionhome.mydomain.com/fancyscript.sh' end it "accepts URI objects for the provision script URI" do - expect(subject.hardware_instance_template["postInstallScriptUri"]).to be_nil + expect(subject.hardware_instance_template['postInstallScriptUri']).to be_nil subject.provision_script_URI = URI.parse('http:/provisionhome.mydomain.com/fancyscript.sh') expect(subject.hardware_instance_template['postInstallScriptUri']).to eq 'http:/provisionhome.mydomain.com/fancyscript.sh' end it "places the private_network_only attribute in the template as privateNetworkOnlyFlag" do - expect(subject.hardware_instance_template["privateNetworkOnlyFlag"]).to be_nil + expect(subject.hardware_instance_template['privateNetworkOnlyFlag']).to be_nil subject.private_network_only = true - expect(subject.hardware_instance_template["privateNetworkOnlyFlag"]).to be(true) + expect(subject.hardware_instance_template['privateNetworkOnlyFlag']).to be(true) end it "puts the user metadata string into the template as userData" do - expect(subject.hardware_instance_template["userData"]).to be_nil + expect(subject.hardware_instance_template['userData']).to be_nil subject.user_metadata = "MetadataValue" expect(subject.hardware_instance_template['userData']).to eq [{'value' => 'MetadataValue'}] end it "puts the max_port_speed attribute into the template as networkComponents.maxSpeed" do - expect(subject.hardware_instance_template["networkComponents"]).to be_nil + expect(subject.hardware_instance_template['networkComponents']).to be_nil subject.max_port_speed = 1000 expect(subject.hardware_instance_template['networkComponents']).to eq [{'maxSpeed' => 1000}] end @@ -149,7 +149,7 @@ test_order.hostname = "ruby-client-test" test_order.domain = "kitchentools.com" - hardware_service = client["Hardware"] + hardware_service = client[:Hardware] allow(hardware_service).to receive(:call_softlayer_api_with_params) expect(hardware_service).to receive(:generateOrderTemplate).with(test_order.hardware_instance_template) @@ -165,7 +165,7 @@ test_order.hostname = "ruby-client-test" test_order.domain = "kitchentools.com" - hardware_service = client["Hardware"] + hardware_service = client[:Hardware] allow(hardware_service).to receive(:call_softlayer_api_with_params) expect(hardware_service).to receive(:createObject).with(test_order.hardware_instance_template) @@ -181,7 +181,7 @@ test_order.hostname = "ruby-client-test" test_order.domain = "kitchentools.com" - hardware_service = client["Hardware"] + hardware_service = client[:Hardware] allow(hardware_service).to receive(:call_softlayer_api_with_params) substituted_order_template = { 'aFake' => 'andBogusOrderTemplate' } @@ -198,7 +198,7 @@ test_order.hostname = "ruby-client-test" test_order.domain = "kitchentools.com" - hardware_service = client["Hardware"] + hardware_service = client[:Hardware] allow(hardware_service).to receive(:call_softlayer_api_with_params) substituted_order_template = { 'aFake' => 'andBogusOrderTemplate' } @@ -209,7 +209,7 @@ describe "methods returning available options for attributes" do let (:client) do client = SoftLayer::Client.new(:username => "fakeusername", :api_key => 'DEADBEEFBADF00D') - virtual_guest_service = client["Hardware"] + virtual_guest_service = client[:Hardware] allow(virtual_guest_service).to receive(:call_softlayer_api_with_params) fake_options = allow(virtual_guest_service).to receive(:getCreateObjectOptions) { fixture_from_json("Hardware_createObjectOptions") } diff --git a/spec/BareMetalServer_spec.rb b/spec/BareMetalServer_spec.rb index d32d0f0..c016258 100644 --- a/spec/BareMetalServer_spec.rb +++ b/spec/BareMetalServer_spec.rb @@ -39,7 +39,7 @@ it "can be cancelled" do mock_client = SoftLayer::Client.new(:username => "fakeuser", :api_key => "DEADBEEFBADF00D") allow(mock_client).to receive(:[]) do |service_name| - expect(service_name).to eq "Ticket" + expect(service_name).to eq :Ticket service = mock_client.service_named(service_name) expect(service).to receive(:createCancelServerTicket).with(12345, 'Migrating to larger server', 'moving on up!', true, 'HARDWARE') diff --git a/spec/Client_spec.rb b/spec/Client_spec.rb index ab006aa..9181a52 100644 --- a/spec/Client_spec.rb +++ b/spec/Client_spec.rb @@ -162,14 +162,14 @@ end it "allows bracket dereferences as an alternate service syntax" do - test_service = test_client['Account'] + test_service = test_client[:Account] expect(test_service).to_not be_nil expect(test_service.service_name).to eq "SoftLayer_Account" expect(test_service.client).to be(test_client) end it "returns the same service repeatedly when asked more than once" do - first_account_service = test_client['Account'] + first_account_service = test_client[:Account] second_account_service = test_client.service_named('Account') expect(first_account_service).to be(second_account_service) @@ -179,10 +179,10 @@ account_service = test_client[:Account] expect(account_service).to_not be_nil - trying_again = test_client['Account'] + trying_again = test_client[:Account] expect(trying_again).to be(account_service) - yet_again = test_client['SoftLayer_Account'] + yet_again = test_client[:SoftLayer_Account] expect(yet_again).to be(account_service) once_more = test_client[:SoftLayer_Account] diff --git a/spec/ModelBase_spec.rb b/spec/ModelBase_spec.rb index 120e7c7..ede8988 100644 --- a/spec/ModelBase_spec.rb +++ b/spec/ModelBase_spec.rb @@ -38,7 +38,7 @@ mock_client = double("Mock SoftLayer Client") test_model = SoftLayer::ModelBase.new(mock_client, { "id" => "12345"}); expect(test_model[:id]).to eq("12345") - expect(test_model["id"]).to eq("12345") + expect(test_model['id']).to eq("12345") end it "allows access to exposed softlayer properties" do diff --git a/spec/ProductPackage_spec.rb b/spec/ProductPackage_spec.rb index 1e5d4df..43fbab4 100644 --- a/spec/ProductPackage_spec.rb +++ b/spec/ProductPackage_spec.rb @@ -13,7 +13,7 @@ describe SoftLayer::ProductPackage do it "requests packages by key name" do client = SoftLayer::Client.new(:username => "fake_user", :api_key => "BADKEY") - product_package_service = client['Product_Package'] + product_package_service = client[:Product_Package] expect(product_package_service).to receive(:call_softlayer_api_with_params) do |method_name, parameters, args| expect(method_name).to be(:getAllObjects) @@ -29,7 +29,7 @@ it "identifies itself with the Product_Package service" do mock_client = SoftLayer::Client.new(:username => "fake_user", :api_key => "BADKEY") allow(mock_client).to receive(:[]) do |service_name| - expect(service_name).to eq "Product_Package" + expect(service_name).to eq :Product_Package mock_service = SoftLayer::Service.new("SoftLayer_Product_Package", :client => mock_client) # mock out call_softlayer_api_with_params so the service doesn't actually try to @@ -47,7 +47,7 @@ describe "class methods for getting to packages" do let(:mock_client) do client = SoftLayer::Client.new(:username => "fake_user", :api_key => "BADKEY") - product_package_service = client['Product_Package'] + product_package_service = client[:Product_Package] allow(product_package_service).to receive(:call_softlayer_api_with_params).with(:getAllObjects, instance_of(SoftLayer::APIParameterFilter), []).and_return([fixture_from_json("Product_Package")]) client diff --git a/spec/Service_spec.rb b/spec/Service_spec.rb index e76273d..e966426 100644 --- a/spec/Service_spec.rb +++ b/spec/Service_spec.rb @@ -84,7 +84,7 @@ describe "#missing_method" do it "translates unknown methods into api calls" do - expect(service).to receive(:call_softlayer_api_with_params).with(:getOpenTickets, nil, ["marshmallow"]) + expect(service).to receive(:call_softlayer_api_with_params).with(:getOpenTickets, nil, ['marshmallow']) response = service.getOpenTickets("marshmallow") end end diff --git a/spec/Ticket_spec.rb b/spec/Ticket_spec.rb index 3301a41..862569d 100644 --- a/spec/Ticket_spec.rb +++ b/spec/Ticket_spec.rb @@ -22,7 +22,7 @@ mock_client = SoftLayer::Client.new(:username => "fakeuser", :api_key=> 'fakekey') allow(mock_client).to receive(:[]) do |service_name| - expect(service_name).to eq "Ticket_Subject" + expect(service_name).to eq :Ticket_Subject mock_service = SoftLayer::Service.new("SoftLayer_Ticket_Subject", :client => mock_client) expect(mock_service).to receive(:getAllObjects).once.and_return(fakeTicketSubjects) @@ -48,7 +48,7 @@ mock_client = SoftLayer::Client.new(:username => "fakeuser", :api_key=> 'fakekey') allow(mock_client).to receive(:[]) do |service_name| - expect(service_name).to eq "Ticket" + expect(service_name).to eq :Ticket mock_service = SoftLayer::Service.new("SoftLayer_Ticket", :client => mock_client) # mock out call_softlayer_api_with_params so the service doesn't actually try to diff --git a/spec/VirtualServerOrder_spec.rb b/spec/VirtualServerOrder_spec.rb index ae86c6c..b940266 100644 --- a/spec/VirtualServerOrder_spec.rb +++ b/spec/VirtualServerOrder_spec.rb @@ -35,49 +35,49 @@ it "places its :datacenter attribute into the order template" do client = SoftLayer::Client.new(:username => "fakeusername", :api_key => 'DEADBEEFBADF00D') - expect(subject.virtual_guest_template["datacenter"]).to be_nil + expect(subject.virtual_guest_template['datacenter']).to be_nil subject.datacenter = SoftLayer::Datacenter.new(client, 'id' => 42, 'name' => "dal05") - expect(subject.virtual_guest_template["datacenter"]).to eq({ "name" => "dal05" }) + expect(subject.virtual_guest_template['datacenter']).to eq({ "name" => "dal05" }) end it "places its :hostname attribute into the order template" do - expect(subject.virtual_guest_template["hostname"]).to be_nil + expect(subject.virtual_guest_template['hostname']).to be_nil subject.hostname = "testhostname" - expect(subject.virtual_guest_template["hostname"]).to eq "testhostname" + expect(subject.virtual_guest_template['hostname']).to eq "testhostname" end it "places its :domain attribute into the order template" do - expect(subject.virtual_guest_template["domain"]).to be_nil + expect(subject.virtual_guest_template['domain']).to be_nil subject.domain = "softlayer.com" - expect(subject.virtual_guest_template["domain"]).to eq "softlayer.com" + expect(subject.virtual_guest_template['domain']).to eq "softlayer.com" end it "places its :cores attribute into the order template as startCpus" do subject.cores = 4 - expect(subject.virtual_guest_template["startCpus"]).to eq 4 + expect(subject.virtual_guest_template['startCpus']).to eq 4 end it "places the MB value of the :memory attrbute in the template as maxMemory" do subject.memory = 4 - expect(subject.virtual_guest_template["maxMemory"]).to eq 4096 + expect(subject.virtual_guest_template['maxMemory']).to eq 4096 end it "places an OS identifier into the order template as the operatingSystemReferenceCode" do - expect(subject.virtual_guest_template["operatingSystemReferenceCode"]).to be_nil + expect(subject.virtual_guest_template['operatingSystemReferenceCode']).to be_nil subject.os_reference_code = 'UBUNTU_12_64' expect(subject.virtual_guest_template['operatingSystemReferenceCode']).to eq 'UBUNTU_12_64' end it "places an image template global identifier in the template as blockDeviceTemplateGroup.globalIdentifier" do client = SoftLayer::Client.new(:username => "fakeusername", :api_key => 'DEADBEEFBADF00D') - expect(subject.virtual_guest_template["blockDeviceTemplateGroup"]).to be_nil + expect(subject.virtual_guest_template['blockDeviceTemplateGroup']).to be_nil subject.image_template = SoftLayer::ImageTemplate.new(client, 'id' => 42, 'globalIdentifier' => '12345-abcd-eatatjoes'); expect(subject.virtual_guest_template['blockDeviceTemplateGroup']).to eq({'globalIdentifier' => '12345-abcd-eatatjoes'}) end it "allows an image template to override an os reference code when both are provided" do - expect(subject.virtual_guest_template["blockDeviceTemplateGroup"]).to be_nil - expect(subject.virtual_guest_template["operatingSystemReferenceCode"]).to be_nil + expect(subject.virtual_guest_template['blockDeviceTemplateGroup']).to be_nil + expect(subject.virtual_guest_template['operatingSystemReferenceCode']).to be_nil client = SoftLayer::Client.new(:username => "fakeusername", :api_key => 'DEADBEEFBADF00D') subject.image_template = SoftLayer::ImageTemplate.new(client, 'id' => 42, 'globalIdentifier' => '12345-abcd-eatatjoes'); @@ -89,50 +89,50 @@ it "places the attribute :hourly into the template as hourlyBillingFlag converting the value to a boolean constant" do # note, we don't want the flag to be nil we want it to be eotjer false or true - expect(subject.virtual_guest_template["hourlyBillingFlag"]).to be(false) + expect(subject.virtual_guest_template['hourlyBillingFlag']).to be(false) subject.hourly = true - expect(subject.virtual_guest_template["hourlyBillingFlag"]).to be(true) + expect(subject.virtual_guest_template['hourlyBillingFlag']).to be(true) subject.hourly = false - expect(subject.virtual_guest_template["hourlyBillingFlag"]).to be(false) + expect(subject.virtual_guest_template['hourlyBillingFlag']).to be(false) end it "places the attribute :use_local_disk in the template as the localDiskFlag" do # note, we don't want the flag to be nil we want it to be false or true - expect(subject.virtual_guest_template["localDiskFlag"]).to be(false) + expect(subject.virtual_guest_template['localDiskFlag']).to be(false) subject.use_local_disk = true - expect(subject.virtual_guest_template["localDiskFlag"]).to be(true) + expect(subject.virtual_guest_template['localDiskFlag']).to be(true) subject.use_local_disk = false - expect(subject.virtual_guest_template["localDiskFlag"]).to be(false) + expect(subject.virtual_guest_template['localDiskFlag']).to be(false) end it "places the attribute :dedicated_host_only in the template as dedicatedAccountHostOnlyFlag" do - expect(subject.virtual_guest_template["dedicatedAccountHostOnlyFlag"]).to be_nil + expect(subject.virtual_guest_template['dedicatedAccountHostOnlyFlag']).to be_nil subject.dedicated_host_only = true - expect(subject.virtual_guest_template["dedicatedAccountHostOnlyFlag"]).to be(true) + expect(subject.virtual_guest_template['dedicatedAccountHostOnlyFlag']).to be(true) end it "puts the public VLAN id into an order template as primaryNetworkComponent.networkVlan.id" do - expect(subject.virtual_guest_template["primaryNetworkComponent"]).to be_nil + expect(subject.virtual_guest_template['primaryNetworkComponent']).to be_nil subject.public_vlan_id = 12345 - expect(subject.virtual_guest_template["primaryNetworkComponent"]).to eq({ "networkVlan" => { "id" => 12345 } }) + expect(subject.virtual_guest_template['primaryNetworkComponent']).to eq({ "networkVlan" => { "id" => 12345 } }) end it "puts the private VLAN id into an order template as primaryBackendNetworkComponent.networkVlan.id" do - expect(subject.virtual_guest_template["primaryBackendNetworkComponent"]).to be_nil + expect(subject.virtual_guest_template['primaryBackendNetworkComponent']).to be_nil subject.private_vlan_id = 12345 - expect(subject.virtual_guest_template["primaryBackendNetworkComponent"]).to eq({ "networkVlan" => { "id" => 12345 } }) + expect(subject.virtual_guest_template['primaryBackendNetworkComponent']).to eq({ "networkVlan" => { "id" => 12345 } }) end it "sets up disks in the order template as blockDevices" do - expect(subject.virtual_guest_template["blockDevices"]).to be_nil + expect(subject.virtual_guest_template['blockDevices']).to be_nil subject.disks = [2, 25, 50] # note that device id 1 should be skipped as SoftLayer reserves that id for OS swap space. - expect(subject.virtual_guest_template["blockDevices"]).to eq [ + expect(subject.virtual_guest_template['blockDevices']).to eq [ {"device"=>"0", "diskImage"=>{"capacity"=>2}}, {"device"=>"2", "diskImage"=>{"capacity"=>25}}, {"device"=>"3", "diskImage"=>{"capacity"=>50}} @@ -140,37 +140,37 @@ end it "puts the :ssh_key_ids in the template as sshKeys and breaks out the ids into objects" do - expect(subject.virtual_guest_template["sshKeys"]).to be_nil + expect(subject.virtual_guest_template['sshKeys']).to be_nil subject.ssh_key_ids = [123, 456, 789] expect(subject.virtual_guest_template['sshKeys']).to eq [{'id' => 123}, {'id' => 456}, {'id' => 789}] end it "puts the :provision_script_URI property into the template as postInstallScriptUri" do - expect(subject.virtual_guest_template["postInstallScriptUri"]).to be_nil + expect(subject.virtual_guest_template['postInstallScriptUri']).to be_nil subject.provision_script_URI = 'http:/provisionhome.mydomain.com/fancyscript.sh' expect(subject.virtual_guest_template['postInstallScriptUri']).to eq 'http:/provisionhome.mydomain.com/fancyscript.sh' end it "accepts URI objects for the provision script URI" do - expect(subject.virtual_guest_template["postInstallScriptUri"]).to be_nil + expect(subject.virtual_guest_template['postInstallScriptUri']).to be_nil subject.provision_script_URI = URI.parse('http:/provisionhome.mydomain.com/fancyscript.sh') expect(subject.virtual_guest_template['postInstallScriptUri']).to eq 'http:/provisionhome.mydomain.com/fancyscript.sh' end it "places the private_network_only attribute in the template as privateNetworkOnlyFlag" do - expect(subject.virtual_guest_template["privateNetworkOnlyFlag"]).to be_nil + expect(subject.virtual_guest_template['privateNetworkOnlyFlag']).to be_nil subject.private_network_only = true - expect(subject.virtual_guest_template["privateNetworkOnlyFlag"]).to be(true) + expect(subject.virtual_guest_template['privateNetworkOnlyFlag']).to be(true) end it "puts the user metadata string into the template as userData" do - expect(subject.virtual_guest_template["userData"]).to be_nil + expect(subject.virtual_guest_template['userData']).to be_nil subject.user_metadata = "MetadataValue" expect(subject.virtual_guest_template['userData']).to eq [{'value' => 'MetadataValue'}] end it "puts the max_port_speed attribute into the template as networkComponents.maxSpeed" do - expect(subject.virtual_guest_template["networkComponents"]).to be_nil + expect(subject.virtual_guest_template['networkComponents']).to be_nil subject.max_port_speed = 1000 expect(subject.virtual_guest_template['networkComponents']).to eq [{'maxSpeed' => 1000}] end @@ -184,7 +184,7 @@ test_order.hostname = "ruby-client-test" test_order.domain = "kitchentools.com" - virtual_guest_service = client["Virtual_Guest"] + virtual_guest_service = client[:Virtual_Guest] allow(virtual_guest_service).to receive(:call_softlayer_api_with_params) expect(virtual_guest_service).to receive(:generateOrderTemplate).with(test_order.virtual_guest_template) @@ -200,7 +200,7 @@ test_order.hostname = "ruby-client-test" test_order.domain = "kitchentools.com" - virtual_guest_service = client["Virtual_Guest"] + virtual_guest_service = client[:Virtual_Guest] allow(virtual_guest_service).to receive(:call_softlayer_api_with_params) expect(virtual_guest_service).to receive(:createObject).with(test_order.virtual_guest_template) @@ -216,7 +216,7 @@ test_order.hostname = "ruby-client-test" test_order.domain = "kitchentools.com" - virtual_guest_service = client["Virtual_Guest"] + virtual_guest_service = client[:Virtual_Guest] allow(virtual_guest_service).to receive(:call_softlayer_api_with_params) substituted_order_template = { 'aFake' => 'andBogusOrderTemplate' } @@ -233,7 +233,7 @@ test_order.hostname = "ruby-client-test" test_order.domain = "kitchentools.com" - virtual_guest_service = client["Virtual_Guest"] + virtual_guest_service = client[:Virtual_Guest] allow(virtual_guest_service).to receive(:call_softlayer_api_with_params) substituted_order_template = { 'aFake' => 'andBogusOrderTemplate' } diff --git a/spec/VirtualServerUpgradeOrder_spec.rb b/spec/VirtualServerUpgradeOrder_spec.rb index ab2b73e..9a8bede 100644 --- a/spec/VirtualServerUpgradeOrder_spec.rb +++ b/spec/VirtualServerUpgradeOrder_spec.rb @@ -69,10 +69,10 @@ sample_order.core_options.each do |num_cores| sample_order.cores = num_cores test_template = sample_order.order_object - expect(sample_order.order_object["prices"].length).to be(1) + expect(sample_order.order_object['prices'].length).to be(1) item = sample_order._item_price_with_capacity("guest_core", num_cores) - expect(test_template["prices"].first["id"]).to eq item["id"] + expect(test_template['prices'].first['id']).to eq item['id'] end end @@ -81,10 +81,10 @@ sample_order.memory_options.each do |ram_in_GB| sample_order.ram = ram_in_GB test_template = sample_order.order_object - expect(sample_order.order_object["prices"].length).to be(1) + expect(sample_order.order_object['prices'].length).to be(1) item = sample_order._item_price_with_capacity("ram", ram_in_GB) - expect(test_template["prices"].first["id"]).to eq item["id"] + expect(test_template['prices'].first['id']).to eq item['id'] end end @@ -93,10 +93,10 @@ sample_order.max_port_speed_options.each do |port_speed| sample_order.max_port_speed = port_speed test_template = sample_order.order_object - expect(sample_order.order_object["prices"].length).to be(1) + expect(sample_order.order_object['prices'].length).to be(1) item = sample_order._item_price_with_capacity("port_speed", port_speed) - expect(test_template["prices"].first["id"]).to eq item["id"] + expect(test_template['prices'].first['id']).to eq item['id'] end end @@ -105,9 +105,9 @@ sample_order.cores = 2 test_template = sample_order.order_object - expect(sample_order.order_object["properties"].first["name"]).to eq('MAINTENANCE_WINDOW') + expect(sample_order.order_object['properties'].first['name']).to eq('MAINTENANCE_WINDOW') - time_string = sample_order.order_object["properties"].first["value"] + time_string = sample_order.order_object['properties'].first['value'] maintenance_time = Time.iso8601(time_string) expect((Time.now - maintenance_time) <= 1.0).to be(true) @@ -122,9 +122,9 @@ test_template = sample_order.order_object - expect(sample_order.order_object["properties"].first["name"]).to eq('MAINTENANCE_WINDOW') + expect(sample_order.order_object['properties'].first['name']).to eq('MAINTENANCE_WINDOW') - time_string = sample_order.order_object["properties"].first["value"] + time_string = sample_order.order_object['properties'].first['value'] expect(time_string).to eq upgrade_time.iso8601 end diff --git a/spec/XMLRPC_Convert_spec.rb b/spec/XMLRPC_Convert_spec.rb index 7d93496..8aae24f 100644 --- a/spec/XMLRPC_Convert_spec.rb +++ b/spec/XMLRPC_Convert_spec.rb @@ -16,7 +16,7 @@ expect { XMLRPC::Convert.fault(fault_hash) }.not_to raise_error exception = XMLRPC::Convert.fault(fault_hash) expect(exception).to be_kind_of(XMLRPC::FaultException) - expect(exception.faultCode).to eq(fault_hash["faultCode"]) - expect(exception.faultString).to eq(fault_hash["faultString"]) + expect(exception.faultCode).to eq(fault_hash['faultCode']) + expect(exception.faultString).to eq(fault_hash['faultString']) end end From cecc3d26d8c89a7980d1481f6a1849e172de38ac Mon Sep 17 00:00:00 2001 From: Scott Thompson Date: Fri, 22 Aug 2014 10:55:19 -0500 Subject: [PATCH 26/32] Added firewall ordering --- CHANGELOG.textile | 3 +- examples/order_server_firewall.rb | 63 +++++++++++++++++++ lib/softlayer/APIParameterFilter.rb | 7 +++ lib/softlayer/Account.rb | 18 ++++++ lib/softlayer/BareMetalServer.rb | 64 ++++++++++++++++++- lib/softlayer/Server.rb | 20 +++--- lib/softlayer/ServerFirewallOrder.rb | 84 +++++++++++++++++++++++++ lib/softlayer/VLANFirewallOrder.rb | 93 ++++++++++++++++++++++++++++ lib/softlayer/VirtualServer.rb | 21 +++++++ lib/softlayer_api.rb | 2 + 10 files changed, 364 insertions(+), 11 deletions(-) create mode 100644 examples/order_server_firewall.rb create mode 100644 lib/softlayer/ServerFirewallOrder.rb create mode 100644 lib/softlayer/VLANFirewallOrder.rb diff --git a/CHANGELOG.textile b/CHANGELOG.textile index ecaaeb9..0801495 100644 --- a/CHANGELOG.textile +++ b/CHANGELOG.textile @@ -1,10 +1,11 @@ *3.0* * Substantially rewrote the ObjectFilter class. ObjectFilters used to be hashes which made it easy to manipulate their content incorrectly. The new implementation has a strict interface that makes it harder to manipulate filters incorrectly. -* Added a model for Virtual Server Image Templates (SoftLayer::ImageTemplate) - VirtualServerOrder now requires an instance of this class rather than allowing you to provide just the global_id of an image +* Added a model for Virtual Server Image Templates (SoftLayer::ImageTemplate) - VirtualServerOrder now requires an instance of this class rather than allowing you to provide the global_id of an image * Added a model for data centers (SoftLayer::Datacenter). Bare Metal, Bare Metal Package, and Virtual server orders now use an instance of Datacenter to identify where their servers will be provisioned. The routines in those classes which used to provide lists of valid data center names now return data center objects. * Virtual Server Upgrades are now handled by the VirtualServerUpgradeOrder class and not the VirtualServer class. This change was made for several reasons. Firt and foremost, it allows multiple aspects of a virtual server to be upgraded at once without having to wait on separate transactions to complete between upgrades. Secondly it opens the door for additional upgrades (for example, to disk configuration) to be added in the future. * Added a method to reboot servers. * The routine to retreive the open tickets on an account has been moved from the Ticket class. The set of open tickets is now a dynamic property of an account object. +* The Model Layer now includes models for Server (aka. Shared) and VLAN (aka. Dedicated) firewalls in the ServerFirewall, and VLANFireall classes respectively. There are corresponding classes for ordering firewalls (ServerFirewallOrder and VLANFirewallOrder). To facilitate the process of locating the 'id' for a firewall, the Account class includes the find_VLAN_with_number routine which lets you look up the segments of a firewall from the VLAN nubmer. *2.2* * Added the ability to set a timout for network requests. The timeout is given when a client is created by passing the :timeout hash parameter when creating a client. The value of the parameter is an integer number of seconds. diff --git a/examples/order_server_firewall.rb b/examples/order_server_firewall.rb new file mode 100644 index 0000000..9d41bf5 --- /dev/null +++ b/examples/order_server_firewall.rb @@ -0,0 +1,63 @@ +# +# Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +require 'rubygems' +require 'softlayer_api' +require 'pp' + +# This is the id of the server you want to protect with a firewall. +# The server can be Bare Metal or Virtual. It should have a public +# network interface, and it should not already have a firewall on it. +server_id = 257696 # 12345 + +# In this example, we assume this is a Bare Metal Server +is_virtual_server = false + +# Work with the SoftLayer API begins with a client. By setting +# the "default" client we avoid having to specify the client repeatedly +# in calls that follow. +SoftLayer::Client.default_client = SoftLayer::Client.new( + # :username => "joecustomer" # enter your username here + # :api_key => "feeddeadbeefbadf00d..." # enter your api key here +) + +# in this case we go straight to the appropriate class to find the server +# an alternative might be to create the account for this client and +# search the list of servers for the one with the appropriate ID. +if is_virtual_server + server = SoftLayer::VirtualServer.server_with_id(server_id) +else + server = SoftLayer::BareMetalServer.server_with_id(server_id) +end + +# Create an instance of SoftLayer::ServerFirewallOrder +order = SoftLayer::ServerFirewallOrder.new(server) + +begin + # this example calls order.verify which will build the order, submit it + # to the network API, and will throw an exception if the order is + # invalid. + order.verify() + puts "Firewall order is good for #{server.fullyQualifiedDomainName}" +rescue => exception + puts "Firewall order failed for #{server.fullyQualifiedDomainName} because #{exception}" +end \ No newline at end of file diff --git a/lib/softlayer/APIParameterFilter.rb b/lib/softlayer/APIParameterFilter.rb index bb7ca8e..3f72282 100644 --- a/lib/softlayer/APIParameterFilter.rb +++ b/lib/softlayer/APIParameterFilter.rb @@ -43,6 +43,13 @@ def initialize(target, starting_parameters = nil) @parameters = starting_parameters || {} end + ## + # API Parameter filters will call through to a particular service + # but that service is defined by their target + def service_name + return @target.service_name + end + ## # Adds an API filter that narrows the scope of a call to an object with # a particular ID. For example, if you want to get the ticket diff --git a/lib/softlayer/Account.rb b/lib/softlayer/Account.rb index f7823da..f91c478 100644 --- a/lib/softlayer/Account.rb +++ b/lib/softlayer/Account.rb @@ -126,6 +126,24 @@ def service softlayer_client[:Account].object_with_id(self.id) end + ## + # Searches the account's list of VLANs for the ones with the given + # vlan number. This may return multiple results because a VLAN can + # span different routers and you will get a separate segment for + # each router. + # + # The IDs of the different segments can be helpful for ordering + # firewalls. + # + def find_VLAN_with_number(vlan_number) + filter = SoftLayer::ObjectFilter.new() { |filter| + filter.accept('networkVlans.vlanNumber').when_it is vlan_number + } + + vlan_data = self.service.object_mask("mask[id,vlanNumber,primaryRouter,networkSpace]").object_filter(filter).getNetworkVlans + return vlan_data + end + ## # Using the login credentials in the client, retrieve # the account associated with those credentials. diff --git a/lib/softlayer/BareMetalServer.rb b/lib/softlayer/BareMetalServer.rb index f408fd0..7983c0d 100644 --- a/lib/softlayer/BareMetalServer.rb +++ b/lib/softlayer/BareMetalServer.rb @@ -55,13 +55,22 @@ def cancel!(reason = :unneeded, comment = '') end ## - # Returns the SoftLayer Service used to work with this Server + # Returns the typical Service used to work with this Server # For Bare Metal Servers that is +SoftLayer_Hardware+ though in some special cases - # you may have to use +SoftLayer_Hardware_Server+ as a type or service. + # you may have to use +SoftLayer_Hardware_Server+ as a type or service. That + # service object is available thorugh the hardware_server_service method def service return softlayer_client[:Hardware].object_with_id(self.id) end + ## + # Returns the SoftLayer_Hardware_Server service for this bare metal server + # This service is used less often than SoftLayer_Hardware, but may be required + # for some operations + def hardware_server_service + self.softlayer_client[:Hardware_Server].object_with_id(self.id) + end + ## # Returns the default object mask used when fetching servers from the API when an # explicit object mask is not provided. @@ -106,6 +115,57 @@ def self.cancellation_reasons } end + ## + # Returns the max port speed of the public network interfaces of the server taking into account + # bound interface pairs (redundant network cards). + def firewall_port_speed + network_components = self.service.object_mask("mask[id,maxSpeed,networkComponentGroup.networkComponents]").getFrontendNetworkComponents() + + # Split the interfaces into grouped and ungrouped interfaces. The max speed of a group will be the sum + # of the individual speeds in that group. The max speed of ungrouped interfaces is simply the max speed + # of that interface. + grouped_interfaces, ungrouped_interfaces = network_components.partition{ |interface| interface.has_key?("networkComponentGroup") } + + if !grouped_interfaces.empty? + group_speeds = grouped_interfaces.collect do |interface| + interface['networkComponentGroup']['networkComponents'].inject(0) {|total_speed, component| total_speed += component['maxSpeed']} + end + + max_group_speed = group_speeds.max + else + max_group_speed = 0 + end + + if !ungrouped_interfaces.empty? + max_ungrouped_speed = ungrouped_interfaces.collect { |interface| interface['maxSpeed']}.max + else + max_ungrouped_speed = 0 + end + + return [max_group_speed, max_ungrouped_speed].max + end + + ## + # Change the current port speed of the server + # + # +new_speed+ is expressed Mbps and should be 0, 10, 100, or 1000. + # Ports have a maximum speed that will limit the actual speed set + # on the port. + # + # Set +public+ to +false+ in order to change the speed of the + # primary private network interface. + # + def change_port_speed(new_speed, public = true) + if public + self.hardware_server_service.setPublicNetworkInterfaceSpeed(new_speed) + else + self.hardware_server_service.setPrivateNetworkInterfaceSpeed(new_speed) + end + + self.refresh_details() + self + end + ## # Retrive the bare metal server with the given server ID from the # SoftLayer API diff --git a/lib/softlayer/Server.rb b/lib/softlayer/Server.rb index 9fd3ddb..1345083 100644 --- a/lib/softlayer/Server.rb +++ b/lib/softlayer/Server.rb @@ -170,6 +170,16 @@ def set_domain!(new_domain) self.refresh_details() end + ## + # Returns the max port speed of the public network interfaces of the server taking into account + # bound interface pairs (redundant network cards). + def firewall_port_speed + network_components = self.service.object_mask("mask[id,maxSpeed]").getFrontendNetworkComponents() + max_speeds = network_components.collect { |component| component['maxSpeed'] } + + max_speeds.empty? ? 0 : max_speeds.max + end + ## # Change the current port speed of the server # @@ -180,15 +190,9 @@ def set_domain!(new_domain) # Set +public+ to +false+ in order to change the speed of the # primary private network interface. # + # This is an abstract method implemented by subclasses def change_port_speed(new_speed, public = true) - if public - self.service.setPublicNetworkInterfaceSpeed(new_speed) - else - self.service.setPrivateNetworkInterfaceSpeed(new_speed) - end - - self.refresh_details() - self + raise "The abstract change_port_speed method of SoftLayer::Server was called. Subclasses should implement the method for their particular service" end ## diff --git a/lib/softlayer/ServerFirewallOrder.rb b/lib/softlayer/ServerFirewallOrder.rb new file mode 100644 index 0000000..14ab9ed --- /dev/null +++ b/lib/softlayer/ServerFirewallOrder.rb @@ -0,0 +1,84 @@ +#-- +# Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. +# +# For licensing information see the LICENSE.md file in the project root. +#++ + +module SoftLayer + # + # This class allows you to order a Firewall for a server + # + class ServerFirewallOrder + # The server that you are ordering the firewall for. + attr_reader :server + + ## + # Create a new order for the given server + def initialize (server) + @server = server + + raise ArgumentError.new("Server does not have an active Public interface") if server.firewall_port_speed == 0 + end + + ## + # Calls the SoftLayer API to verify that the template provided by this order is valid + # This routine will return the order template generated by the API or will throw an exception + # + # This routine will not actually create a Bare Metal Instance and will not affect billing. + # + # If you provide a block, it will receive the order template as a parameter and + # the block may make changes to the template before it is submitted. + def verify() + order_template = firewall_order_template + order_template = yield order_template if block_given? + + server.softlayer_client[:Product_Order].verifyOrder(order_template) + end + + ## + # Calls the SoftLayer API to place an order for a new server based on the template in this + # order. If this succeeds then you will be billed for the new server. + # + # If you provide a block, it will receive the order template as a parameter and + # the block may make changes to the template before it is submitted. + def place_order!() + order_template = firewall_order_template + order_template = yield order_template if block_given? + + server.softlayer_client[:Product_Order].placeOrder(order_template) + end + + protected + + ## + # Returns a hash of the creation options formatted to be sent *to* + # the SoftLayer API for either verification or completion + def firewall_order_template + client = server.softlayer_client + additional_products_package = SoftLayer::ProductPackage.additional_products_package(client) + + template = { + 'complexType' => 'SoftLayer_Container_Product_Order_Network_Protection_Firewall', + 'quantity' => 1, + 'packageId' => additional_products_package.id + } + + if @server.service.service_name == "SoftLayer_Virtual_Guest" + template['virtualGuests'] = [{'id' => @server.id}] + else + template['hardware'] = [{'id' => @server.id}] + end + + expected_description = "#{@server.firewall_port_speed}Mbps Hardware Firewall" + firewall_items = additional_products_package.items_with_description(expected_description) + + raise "Could not find a price item matching the description '#{expected_description}'" if firewall_items.empty? + + firewall_item = firewall_items[0] + + template['prices'] = [{ 'id' => firewall_item.price_id }] if firewall_item.respond_to?(:price_id) + + template + end + end # class ServerFirewallOrder +end # module SoftLayer diff --git a/lib/softlayer/VLANFirewallOrder.rb b/lib/softlayer/VLANFirewallOrder.rb new file mode 100644 index 0000000..549e2bf --- /dev/null +++ b/lib/softlayer/VLANFirewallOrder.rb @@ -0,0 +1,93 @@ +#-- +# Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. +# +# For licensing information see the LICENSE.md file in the project root. +#++ + +module SoftLayer + # + # This class allows you to order a Firewall for a VLAN + # + class VLANFirewallOrder + ## + # The VLAN that you are ordering the firewall for. + attr_reader :vlan_id + + ## + # Set high_availabilty to true if you want redundant + # firewall devices (defaults to false, no high_availability) + attr_accessor :high_availability + + ## + # Create a new order for the given VLAN + # Note that the vlan_id is NOT the same as the vlan number. + def initialize (vlan_id, client = nil) + @softlayer_client = client || Client.default_client + raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !@softlayer_client + + @vlan_id = vlan_id + @high_availability = false + end + + ## + # Calls the SoftLayer API to verify that the template provided by this order is valid + # This routine will return the order template generated by the API or will throw an exception + # + # This routine will not actually create a Bare Metal Instance and will not affect billing. + # + # If you provide a block, it will receive the order template as a parameter and + # the block may make changes to the template before it is submitted. + def verify() + order_template = firewall_order_template + order_template = yield order_template if block_given? + + @softlayer_client[:Product_Order].verifyOrder(order_template) + end + + ## + # Calls the SoftLayer API to place an order for a new server based on the template in this + # order. If this succeeds then you will be billed for the new server. + # + # If you provide a block, it will receive the order template as a parameter and + # the block may make changes to the template before it is submitted. + def place_order!() + order_template = firewall_order_template + order_template = yield order_template if block_given? + + @softlayer_client[:Product_Order].placeOrder(order_template) + end + + protected + + ## + # Returns a hash of the creation options formatted to be sent to + # the SoftLayer API for either verification or completion + def firewall_order_template + client = @softlayer_client + additional_products_package = SoftLayer::ProductPackage.additional_products_package(client) + + template = { + 'complexType' => 'SoftLayer_Container_Product_Order_Network_Protection_Firewall_Dedicated', + 'quantity' => 1, + 'packageId' => additional_products_package.id, + 'vlanId' => @vlan_id + } + + if @high_availability + expected_description = "Hardware Firewall (High Availability)" + else + expected_description = "Hardware Firewall (Dedicated)" + end + + firewall_items = additional_products_package.items_with_description(expected_description) + + raise "Could not find a price item matching the description '#{expected_description}'" if firewall_items.empty? + + firewall_item = firewall_items[0] + + template['prices'] = [{ 'id' => firewall_item.price_id }] if firewall_item.respond_to?(:price_id) + + template + end + end # class VLANFirewallOrder +end # module SoftLayer diff --git a/lib/softlayer/VirtualServer.rb b/lib/softlayer/VirtualServer.rb index 5116997..606ca39 100644 --- a/lib/softlayer/VirtualServer.rb +++ b/lib/softlayer/VirtualServer.rb @@ -65,6 +65,27 @@ class VirtualServer < Server end end + ## + # Change the current port speed of the server + # + # +new_speed+ is expressed Mbps and should be 0, 10, 100, or 1000. + # Ports have a maximum speed that will limit the actual speed set + # on the port. + # + # Set +public+ to +false+ in order to change the speed of the + # primary private network interface. + # + def change_port_speed(new_speed, public = true) + if public + self.service.setPublicNetworkInterfaceSpeed(new_speed) + else + self.service.setPrivateNetworkInterfaceSpeed(new_speed) + end + + self.refresh_details() + self + end + ## # IMMEDIATELY cancel this virtual server # diff --git a/lib/softlayer_api.rb b/lib/softlayer_api.rb index c5135c9..e362867 100755 --- a/lib/softlayer_api.rb +++ b/lib/softlayer_api.rb @@ -31,8 +31,10 @@ require 'softlayer/ProductPackage' require 'softlayer/ProductItemCategory' require 'softlayer/ServerFirewall' +require 'softlayer/ServerFirewallOrder' require 'softlayer/Ticket' require 'softlayer/VirtualServer' require 'softlayer/VirtualServerOrder' require 'softlayer/VirtualServerUpgradeOrder' require 'softlayer/VLANFirewall' +require 'softlayer/VLANFirewallOrder' From 42431a146301f0c1c2c8266c2a488f13ca570872 Mon Sep 17 00:00:00 2001 From: Scott Thompson Date: Fri, 22 Aug 2014 14:20:41 -0500 Subject: [PATCH 27/32] Added routines to bypass and route around firewalls. Added unit tests to ensure those routines must be used carefully --- lib/softlayer/BareMetalServer.rb | 33 +-------- lib/softlayer/Config.rb | 4 +- lib/softlayer/Server.rb | 27 ++++--- lib/softlayer/ServerFirewall.rb | 36 ++++++++- lib/softlayer/ServerFirewallOrder.rb | 2 +- lib/softlayer/VLANFirewall.rb | 86 ++++++++++++++++++++-- lib/softlayer/VirtualServer.rb | 21 ------ spec/BareMetalServer_spec.rb | 8 +- spec/ServerFirewall_spec.rb | 68 +++++++++++++++++ spec/VLANFirewall_spec.rb | 105 +++++++++++++++++++++++++++ 10 files changed, 315 insertions(+), 75 deletions(-) create mode 100644 spec/ServerFirewall_spec.rb diff --git a/lib/softlayer/BareMetalServer.rb b/lib/softlayer/BareMetalServer.rb index 7983c0d..f94af7e 100644 --- a/lib/softlayer/BareMetalServer.rb +++ b/lib/softlayer/BareMetalServer.rb @@ -60,15 +60,7 @@ def cancel!(reason = :unneeded, comment = '') # you may have to use +SoftLayer_Hardware_Server+ as a type or service. That # service object is available thorugh the hardware_server_service method def service - return softlayer_client[:Hardware].object_with_id(self.id) - end - - ## - # Returns the SoftLayer_Hardware_Server service for this bare metal server - # This service is used less often than SoftLayer_Hardware, but may be required - # for some operations - def hardware_server_service - self.softlayer_client[:Hardware_Server].object_with_id(self.id) + return softlayer_client[:Hardware_Server].object_with_id(self.id) end ## @@ -145,27 +137,6 @@ def firewall_port_speed return [max_group_speed, max_ungrouped_speed].max end - ## - # Change the current port speed of the server - # - # +new_speed+ is expressed Mbps and should be 0, 10, 100, or 1000. - # Ports have a maximum speed that will limit the actual speed set - # on the port. - # - # Set +public+ to +false+ in order to change the speed of the - # primary private network interface. - # - def change_port_speed(new_speed, public = true) - if public - self.hardware_server_service.setPublicNetworkInterfaceSpeed(new_speed) - else - self.hardware_server_service.setPrivateNetworkInterfaceSpeed(new_speed) - end - - self.refresh_details() - self - end - ## # Retrive the bare metal server with the given server ID from the # SoftLayer API @@ -180,7 +151,7 @@ def self.server_with_id(server_id, options = {}) softlayer_client = options[:client] || Client.default_client raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client - hardware_service = softlayer_client[:Hardware] + hardware_service = softlayer_client[:Hardware_Server] hardware_service = hardware_service.object_mask(default_object_mask.to_sl_object_mask) if options.has_key?(:object_mask) diff --git a/lib/softlayer/Config.rb b/lib/softlayer/Config.rb index f9e97e2..c892ab0 100644 --- a/lib/softlayer/Config.rb +++ b/lib/softlayer/Config.rb @@ -72,8 +72,8 @@ def Config.globals_settings def Config.environment_settings result = {} - result[:username] = ENV["SL_USERNAME"] if ENV["SL_USERNAME"] - result[:api_key] = ENV["SL_API_KEY"] if ENV["SL_API_KEY"] + result[:username] = ENV['SL_USERNAME'] if ENV['SL_USERNAME'] + result[:api_key] = ENV['SL_API_KEY'] if ENV['SL_API_KEY'] result end diff --git a/lib/softlayer/Server.rb b/lib/softlayer/Server.rb index 1345083..a75b2c6 100644 --- a/lib/softlayer/Server.rb +++ b/lib/softlayer/Server.rb @@ -94,7 +94,7 @@ def reboot!(reboot_technique = :default_reboot) when :power_cycle self.service.rebootHard else - raise RuntimeError, "Unrecognized reboot technique in SoftLayer::Server#reboot!}" + raise ArgumentError, "Unrecognized reboot technique in SoftLayer::Server#reboot!}" end end @@ -117,7 +117,7 @@ def softlayer_properties(object_mask = nil) # Change the notes of the server # raises ArgumentError if you pass nil as the notes def notes=(new_notes) - raise ArgumentError.new("The new notes cannot be nil") unless new_notes + raise ArgumentError, "The new notes cannot be nil" unless new_notes edit_template = { "notes" => new_notes @@ -131,7 +131,7 @@ def notes=(new_notes) # Change the user metadata for the server. # def user_metadata=(new_metadata) - raise ArgumentError.new("Cannot set user metadata to nil") unless new_metadata + raise ArgumentError, "Cannot set user metadata to nil" unless new_metadata self.service.setUserMetadata([new_metadata]) self.refresh_details() end @@ -141,8 +141,8 @@ def user_metadata=(new_metadata) # Raises an ArgumentError if the new hostname is nil or empty # def set_hostname!(new_hostname) - raise ArgumentError.new("The new hostname cannot be nil") unless new_hostname - raise ArgumentError.new("The new hostname cannot be empty") if new_hostname.empty? + raise ArgumentError, "The new hostname cannot be nil" unless new_hostname + raise ArgumentError, "The new hostname cannot be empty" if new_hostname.empty? edit_template = { "hostname" => new_hostname @@ -159,8 +159,8 @@ def set_hostname!(new_hostname) # no further validation is done on the domain name # def set_domain!(new_domain) - raise ArgumentError.new("The new hostname cannot be nil") unless new_domain - raise ArgumentError.new("The new hostname cannot be empty") if new_domain.empty? + raise ArgumentError, "The new hostname cannot be nil" unless new_domain + raise ArgumentError, "The new hostname cannot be empty" if new_domain.empty? edit_template = { "domain" => new_domain @@ -188,11 +188,16 @@ def firewall_port_speed # on the port. # # Set +public+ to +false+ in order to change the speed of the - # primary private network interface. - # - # This is an abstract method implemented by subclasses + # private network interface. def change_port_speed(new_speed, public = true) - raise "The abstract change_port_speed method of SoftLayer::Server was called. Subclasses should implement the method for their particular service" + if public + self.service.setPublicNetworkInterfaceSpeed(new_speed) + else + self.service.setPrivateNetworkInterfaceSpeed(new_speed) + end + + self.refresh_details() + self end ## diff --git a/lib/softlayer/ServerFirewall.rb b/lib/softlayer/ServerFirewall.rb index a556ea7..547ecf9 100644 --- a/lib/softlayer/ServerFirewall.rb +++ b/lib/softlayer/ServerFirewall.rb @@ -112,6 +112,40 @@ def change_rules!(rules_data) self.softlayer_client[:Network_Firewall_Update_Request].createObject(change_object) end + ## + # This method asks the firewall to ignore its rule set and pass all traffic + # through the firewall. Compare the behavior of this routine with + # change_routing_bypass! + # + # It is important to note that changing the bypass to :bypass_firewall_rules + # removes ALL the protection offered by the firewall. This routine should be + # used with extreme discretion. + # + # Note that this routine queues a rule change and rule changes may take + # time to process. The change will probably not take effect immediately + # + # The two symbols accepted as arguments by this routine are: + # :apply_firewall_rules - The rules of the firewall are applied to traffic. This is the default operating mode of the firewall + # :bypass_firewall_rules - The rules of the firewall are ignored. In this configuration the firewall provides no protection. + # + def change_rules_bypass!(bypass_symbol) + change_object = { + "networkComponentFirewallId" => self.id, + "rules" => self.rules + } + + case bypass_symbol + when :apply_firewall_rules + change_object['bypassFlag'] = false + self.softlayer_client[:Network_Firewall_Update_Request].createObject(change_object) + when :bypass_firewall_rules + change_object['bypassFlag'] = true + self.softlayer_client[:Network_Firewall_Update_Request].createObject(change_object) + else + raise ArgumentError, "An invalid parameter was sent to #{__method__}. It accepts :apply_firewall_rules and :bypass_firewall_rules" + end + end + ## # Locate and return all the server firewalls in the environment. # @@ -128,7 +162,7 @@ def change_rules!(rules_data) # The collection of all those VLANs becomes the set of firewalls # for the account. # - def self.find_firewalls(client) + def self.find_firewalls(client = nil) softlayer_client = client || Client.default_client raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client diff --git a/lib/softlayer/ServerFirewallOrder.rb b/lib/softlayer/ServerFirewallOrder.rb index 14ab9ed..202f174 100644 --- a/lib/softlayer/ServerFirewallOrder.rb +++ b/lib/softlayer/ServerFirewallOrder.rb @@ -17,7 +17,7 @@ class ServerFirewallOrder def initialize (server) @server = server - raise ArgumentError.new("Server does not have an active Public interface") if server.firewall_port_speed == 0 + raise ArgumentError, "Server does not have an active Public interface" if server.firewall_port_speed == 0 end ## diff --git a/lib/softlayer/VLANFirewall.rb b/lib/softlayer/VLANFirewall.rb index 8532863..c9aa859 100644 --- a/lib/softlayer/VLANFirewall.rb +++ b/lib/softlayer/VLANFirewall.rb @@ -70,13 +70,78 @@ class VLANFirewall < SoftLayer::ModelBase # depending on the system load and other circumstances. def change_rules!(rules_data) change_object = { - "firewallContextAccessControlListId" => self.id, + "firewallContextAccessControlListId" => rules_ACL_id(), "rules" => rules_data } self.softlayer_client[:Network_Firewall_Update_Request].createObject(change_object) end + ## + # This method asks the firewall to ignore its rule set and pass all traffic + # through the firewall. Compare the behavior of this routine with + # change_routing_bypass! + # + # It is important to note that changing the bypass to :bypass_firewall_rules + # removes ALL the protection offered by the firewall. This routine should be + # used with extreme discretion. + # + # Note that this routine queues a rule change and rule changes may take + # time to process. The change will probably not take effect immediately + # + # The two symbols accepted as arguments by this routine are: + # :apply_firewall_rules - The rules of the firewall are applied to traffic. This is the default operating mode of the firewall + # :bypass_firewall_rules - The rules of the firewall are ignored. In this configuration the firewall provides no protection. + # + def change_rules_bypass!(bypass_symbol) + change_object = { + "firewallContextAccessControlListId" => rules_ACL_id(), + "rules" => self.rules + } + + case bypass_symbol + when :apply_firewall_rules + change_object['bypassFlag'] = false + self.softlayer_client[:Network_Firewall_Update_Request].createObject(change_object) + when :bypass_firewall_rules + change_object['bypassFlag'] = true + self.softlayer_client[:Network_Firewall_Update_Request].createObject(change_object) + else + raise ArgumentError, "An invalid parameter was sent to #{__method__}. It accepts :apply_firewall_rules and :bypass_firewall_rules" + end + end + + ## + # This method allows you to route traffic around the firewall + # and directly to the servers it protects. Compare the behavior of this routine with + # change_rules_bypass! + # + # It is important to note that changing the routing to :route_around_firewall + # removes ALL the protection offered by the firewall. This routine should be + # used with extreme discretion. + # + # Note that this routine constructs a transaction. The Routing change + # may not happen immediately. + # + # The two symbols accepted as arguments by the routine are: + # :route_through_firewall - Network traffic is sent through the firewall to the servers in the VLAN segment it protects. This is the usual operating mode of the firewall. + # :route_around_firewall - Network traffic will be sent directly to the servers in the VLAN segment protected by this firewall. This means that the firewall will *NOT* be protecting those servers. + # + def change_routing_bypass!(routing_symbol) + vlan_firewall_id = self['networkVlanFirewall']['id'] + + raise "Could not identify the device for a VLAN firewall" if !vlan_firewall_id + + case routing_symbol + when :route_through_firewall + self.softlayer_client[:Network_Vlan_Firewall].object_with_id(vlan_firewall_id).updateRouteBypass(false) + when :route_around_firewall + self.softlayer_client[:Network_Vlan_Firewall].object_with_id(vlan_firewall_id).updateRouteBypass(true) + else + raise ArgumentError, "An invalid parameter was sent to #{__method__}. It accepts :route_through_firewall and :route_around_firewall" + end + end + ## # Returns the name of the primary router the firewall is attached to. # This is often a "customer router" in one of the datacenters. @@ -108,7 +173,7 @@ def high_availability? # # This list is obtained by asking the account for all the VLANs # it has that also have a networkVlanFirewall component. - def self.find_firewalls(client) + def self.find_firewalls(client = nil) softlayer_client = client || Client.default_client raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client @@ -117,8 +182,8 @@ def self.find_firewalls(client) filter.accept("networkVlans.networkVlanFirewall").when_it is_not_null } - vlan_firewalls = client[:Account].object_mask(vlan_firewall_mask).object_filter(vlan_firewall_filter).getNetworkVlans - vlan_firewalls.collect { |firewall_data| SoftLayer::VLANFirewall.new(client, firewall_data)} + vlan_firewalls = softlayer_client[:Account].object_mask(vlan_firewall_mask).object_filter(vlan_firewall_filter).getNetworkVlans + vlan_firewalls.collect { |firewall_data| SoftLayer::VLANFirewall.new(softlayer_client, firewall_data)} end @@ -157,7 +222,7 @@ def rules_ACL_id end def self.vlan_firewall_mask - return "mask[primaryRouter,highAvailabilityFirewallFlag,"+ + return "mask[primaryRouter,highAvailabilityFirewallFlag," + "firewallInterfaces.firewallContextAccessControlLists," + "networkVlanFirewall[id, datacenter, primaryIpAddress, firewallType, fullyQualifiedDomainName]]" end @@ -167,7 +232,16 @@ def self.default_rules_mask end def self.default_rules_mask_keys - ['orderValue','action','destinationIpAddress','destinationIpSubnetMask',"protocol","destinationPortRangeStart","destinationPortRangeEnd",'sourceIpAddress',"sourceIpSubnetMask","version"] + ['orderValue', + 'action', + 'destinationIpAddress', + 'destinationIpSubnetMask', + 'protocol', + 'destinationPortRangeStart', + 'destinationPortRangeEnd', + 'sourceIpAddress', + 'sourceIpSubnetMask', + 'version'] end end # class Firewall end # module SoftLayer \ No newline at end of file diff --git a/lib/softlayer/VirtualServer.rb b/lib/softlayer/VirtualServer.rb index 606ca39..5116997 100644 --- a/lib/softlayer/VirtualServer.rb +++ b/lib/softlayer/VirtualServer.rb @@ -65,27 +65,6 @@ class VirtualServer < Server end end - ## - # Change the current port speed of the server - # - # +new_speed+ is expressed Mbps and should be 0, 10, 100, or 1000. - # Ports have a maximum speed that will limit the actual speed set - # on the port. - # - # Set +public+ to +false+ in order to change the speed of the - # primary private network interface. - # - def change_port_speed(new_speed, public = true) - if public - self.service.setPublicNetworkInterfaceSpeed(new_speed) - else - self.service.setPrivateNetworkInterfaceSpeed(new_speed) - end - - self.refresh_details() - self - end - ## # IMMEDIATELY cancel this virtual server # diff --git a/spec/BareMetalServer_spec.rb b/spec/BareMetalServer_spec.rb index c016258..169c60f 100644 --- a/spec/BareMetalServer_spec.rb +++ b/spec/BareMetalServer_spec.rb @@ -26,16 +26,20 @@ SoftLayer::BareMetalServer.new(mock_client, { "id" => 12345 }) end - it "identifies itself with the SoftLayer_Hardware service" do + it "identifies itself with the SoftLayer_Hardware_Server service" do service = sample_server.service expect(service.server_object_id).to eq(12345) - expect(service.target.service_name).to eq "SoftLayer_Hardware" + expect(service.target.service_name).to eq "SoftLayer_Hardware_Server" end it_behaves_like "server with port speed" do let (:server) { sample_server } end + it_behaves_like "server with mutable hostname" do + let (:server) { sample_server } + end + it "can be cancelled" do mock_client = SoftLayer::Client.new(:username => "fakeuser", :api_key => "DEADBEEFBADF00D") allow(mock_client).to receive(:[]) do |service_name| diff --git a/spec/ServerFirewall_spec.rb b/spec/ServerFirewall_spec.rb new file mode 100644 index 0000000..e293180 --- /dev/null +++ b/spec/ServerFirewall_spec.rb @@ -0,0 +1,68 @@ +#-- +# Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. +# +# For licensing information see the LICENSE.md file in the project root. +#++ + +$LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) + +require 'rubygems' +require 'softlayer_api' +require 'rspec' + +describe SoftLayer::ServerFirewall do + describe "firewall rules bypass" do + let(:mock_client) { + mock_client = SoftLayer::Client.new(:username => "fake_user", :api_key => "BADKEY") + } + + it "responds to the method change_routing_bypass!" do + mock_firewall = SoftLayer::ServerFirewall.new("not really a client", { "id" => 12345 }) + expect(mock_firewall).to respond_to(:change_rules_bypass!) + end + + it "accepts :apply_firewall_rules" do + mock_firewall = SoftLayer::ServerFirewall.new(mock_client, {"id" => 12345}) + allow(mock_firewall).to receive(:rules) { {} } + + firewall_update_service = mock_client[:Network_Firewall_Update_Request] + + expect(firewall_update_service).to receive(:call_softlayer_api_with_params) do |method, parameters, arguments| + expect(arguments[0]['bypassFlag']).to be(false) + end + + mock_firewall.change_rules_bypass!(:apply_firewall_rules) + end + + it "accepts :bypass_firewall_rules!" do + mock_firewall = SoftLayer::ServerFirewall.new(mock_client, {"id" => 12345}) + allow(mock_firewall).to receive(:rules) { {} } + + firewall_update_service = mock_client[:Network_Firewall_Update_Request] + expect(firewall_update_service).to receive(:call_softlayer_api_with_params) do |method, parameters, arguments| + expect(arguments[0]['bypassFlag']).to be(true) + end + + mock_firewall.change_rules_bypass!(:bypass_firewall_rules) + end + + it "rejects other parameters (particularly true and false)" do + mock_firewall = SoftLayer::ServerFirewall.new("not really a client", { "id" => 12345 }) + allow(mock_firewall).to receive(:rules) { {} } + + firewall_update_service = mock_client[:Network_Firewall_Update_Request] + + allow(firewall_update_service).to receive(:call_softlayer_api_with_params) + + expect{ mock_firewall.change_rules_bypass!(true) }.to raise_error + expect{ mock_firewall.change_rules_bypass!(false) }.to raise_error + expect{ mock_firewall.change_rules_bypass!(:route_around_firewall) }.to raise_error + expect{ mock_firewall.change_rules_bypass!(:route_through_firewall) }.to raise_error + expect{ mock_firewall.change_rules_bypass!("apply_firewall_rules") }.to raise_error + expect{ mock_firewall.change_rules_bypass!("bypass_firewall_rules") }.to raise_error + expect{ mock_firewall.change_rules_bypass!(nil) }.to raise_error + expect{ mock_firewall.change_rules_bypass!(1) }.to raise_error + expect{ mock_firewall.change_rules_bypass!(0) }.to raise_error + end + end +end \ No newline at end of file diff --git a/spec/VLANFirewall_spec.rb b/spec/VLANFirewall_spec.rb index 8788d98..913460f 100644 --- a/spec/VLANFirewall_spec.rb +++ b/spec/VLANFirewall_spec.rb @@ -14,4 +14,109 @@ it "should have a class representing a firewall" do expect{ SoftLayer::VLANFirewall.new("not really a client", { "id" => 12345 }) }.to_not raise_error end + + describe "firewall rules bypass" do + let(:mock_client) { + mock_client = SoftLayer::Client.new(:username => "fake_user", :api_key => "BADKEY") + } + + it "responds to the method change_routing_bypass!" do + mock_firewall = SoftLayer::VLANFirewall.new("not really a client", { "id" => 12345 }) + expect(mock_firewall).to respond_to(:change_rules_bypass!) + end + + it "accepts :apply_firewall_rules" do + mock_firewall = SoftLayer::VLANFirewall.new(mock_client, { "id" => 12345, 'networkVlanFirewall' => {'id' => 67890}}) + allow(mock_firewall).to receive(:rules_ACL_id) { 0 } + allow(mock_firewall).to receive(:rules) { {} } + + firewall_update_service = mock_client[:Network_Firewall_Update_Request] + + expect(firewall_update_service).to receive(:call_softlayer_api_with_params) do |method, parameters, arguments| + expect(arguments[0]['bypassFlag']).to be(false) + end + + mock_firewall.change_rules_bypass!(:apply_firewall_rules) + end + + it "accepts :bypass_firewall_rules!" do + mock_firewall = SoftLayer::VLANFirewall.new(mock_client, { "id" => 12345, 'networkVlanFirewall' => {'id' => 67890}}) + allow(mock_firewall).to receive(:rules_ACL_id) { 0 } + allow(mock_firewall).to receive(:rules) { {} } + + firewall_update_service = mock_client[:Network_Firewall_Update_Request] + expect(firewall_update_service).to receive(:call_softlayer_api_with_params) do |method, parameters, arguments| + expect(arguments[0]['bypassFlag']).to be(true) + end + + mock_firewall.change_rules_bypass!(:bypass_firewall_rules) + end + + it "rejects other parameters (particularly true and false)" do + mock_firewall = SoftLayer::VLANFirewall.new("not really a client", { "id" => 12345 }) + allow(mock_firewall).to receive(:rules) { {} } + + firewall_update_service = mock_client[:Network_Firewall_Update_Request] + + allow(firewall_update_service).to receive(:call_softlayer_api_with_params) + + expect{ mock_firewall.change_rules_bypass!(true) }.to raise_error + expect{ mock_firewall.change_rules_bypass!(false) }.to raise_error + expect{ mock_firewall.change_rules_bypass!(:route_around_firewall) }.to raise_error + expect{ mock_firewall.change_rules_bypass!(:route_through_firewall) }.to raise_error + expect{ mock_firewall.change_rules_bypass!("apply_firewall_rules") }.to raise_error + expect{ mock_firewall.change_rules_bypass!("bypass_firewall_rules") }.to raise_error + expect{ mock_firewall.change_rules_bypass!(nil) }.to raise_error + expect{ mock_firewall.change_rules_bypass!(1) }.to raise_error + expect{ mock_firewall.change_rules_bypass!(0) }.to raise_error + end + end + + describe "firewall routing changes" do + let(:mock_client) { + mock_client = SoftLayer::Client.new(:username => "fake_user", :api_key => "BADKEY") + } + + it "responds to the method change_routing_bypass!" do + mock_firewall = SoftLayer::VLANFirewall.new("not really a client", { "id" => 12345 }) + expect(mock_firewall).to respond_to(:change_routing_bypass!) + end + + it "accepts :route_through_firewall" do + mock_firewall = SoftLayer::VLANFirewall.new(mock_client, { "id" => 12345, 'networkVlanFirewall' => {'id' => 67890}}) + allow(mock_firewall).to receive(:rules_ACL_id) { 0 } + vlan_firewall_service = mock_client[:Network_Vlan_Firewall] + + expect(vlan_firewall_service).to receive(:call_softlayer_api_with_params).with(:updateRouteBypass,an_instance_of(SoftLayer::APIParameterFilter),[false]) + mock_firewall.change_routing_bypass!(:route_through_firewall) + end + + it "accepts :route_around_firewall" do + mock_firewall = SoftLayer::VLANFirewall.new(mock_client, { "id" => 12345, 'networkVlanFirewall' => {'id' => 67890}}) + allow(mock_firewall).to receive(:rules_ACL_id) { 0 } + vlan_firewall_service = mock_client[:Network_Vlan_Firewall] + + expect(vlan_firewall_service).to receive(:call_softlayer_api_with_params).with(:updateRouteBypass,an_instance_of(SoftLayer::APIParameterFilter),[true]) + mock_firewall.change_routing_bypass!(:route_around_firewall) + end + + it "rejects other parameters (particularly true and false)" do + mock_firewall = SoftLayer::VLANFirewall.new("not really a client", { "id" => 12345 }) + allow(mock_firewall).to receive(:rules_ACL_id) { 0 } + + vlan_firewall_service = mock_client[:Network_Vlan_Firewall] + + allow(vlan_firewall_service).to receive(:call_softlayer_api_with_params) + + expect{ mock_firewall.change_routing_bypass!(true) }.to raise_error + expect{ mock_firewall.change_routing_bypass!(false) }.to raise_error + expect{ mock_firewall.change_routing_bypass!(:apply_firewall_rules) }.to raise_error + expect{ mock_firewall.change_routing_bypass!(:bypass_firewall_rules) }.to raise_error + expect{ mock_firewall.change_routing_bypass!("route_around_firewall") }.to raise_error + expect{ mock_firewall.change_routing_bypass!("route_through_firewall") }.to raise_error + expect{ mock_firewall.change_routing_bypass!(nil) }.to raise_error + expect{ mock_firewall.change_routing_bypass!(1) }.to raise_error + expect{ mock_firewall.change_routing_bypass!(0) }.to raise_error + end + end end \ No newline at end of file From 7c9f5b8bb2538804886d7f1ef13f4fff311e6c74 Mon Sep 17 00:00:00 2001 From: Scott Thompson Date: Fri, 22 Aug 2014 15:28:28 -0500 Subject: [PATCH 28/32] Added the ability to cancel firewalls --- lib/softlayer/ServerFirewall.rb | 40 ++++++++++++++++++++++++++++----- lib/softlayer/VLANFirewall.rb | 36 +++++++++++++++++++++++++---- 2 files changed, 66 insertions(+), 10 deletions(-) diff --git a/lib/softlayer/ServerFirewall.rb b/lib/softlayer/ServerFirewall.rb index 547ecf9..7f106b7 100644 --- a/lib/softlayer/ServerFirewall.rb +++ b/lib/softlayer/ServerFirewall.rb @@ -86,6 +86,34 @@ def initialize(client, network_hash) @protected_server = nil end + ## + # Cancel the firewall + # + # This method cancels the firewall and releases its + # resources. The cancellation is processed immediately! + # Call this method with careful deliberation! + # + # Notes is a string that describes the reason for the + # cancellation. If empty or nil, a default string will + # be added + # + def cancel!(notes = nil) + user = self.softlayer_client[:Account].object_mask("mask[id,account]").getCurrentUser + notes = "Cancelled by a call to #{__method__} in the softlayer_api gem" if notes == nil || notes == "" + + cancellation_request = { + 'accountId' => user['account']['id'], + 'userId' => user['id'], + 'items' => [ { + 'billingItemId' => self['billingItem']['id'], + 'immediateCancellationFlag' => true + } ], + 'notes' => notes + } + + self.softlayer_client[:Billing_Item_Cancellation_Request].createObject(cancellation_request) + end + ## # Change the set of rules for the firewall. # The rules_data parameter should be an array of hashes where @@ -117,9 +145,9 @@ def change_rules!(rules_data) # through the firewall. Compare the behavior of this routine with # change_routing_bypass! # - # It is important to note that changing the bypass to :bypass_firewall_rules - # removes ALL the protection offered by the firewall. This routine should be - # used with extreme discretion. + # It is important to note that changing the bypass to :bypass_firewall_rules + # removes ALL the protection offered by the firewall. This routine should be + # used with careful deliberation. # # Note that this routine queues a rule change and rule changes may take # time to process. The change will probably not take effect immediately @@ -205,9 +233,9 @@ def softlayer_properties(object_mask = nil) service = service.object_mask(object_mask) if object_mask if self.has_sl_property?('networkComponent') - service.object_mask("mask[id,status,networkComponent.downlinkComponent.hardwareId]").getObject + service.object_mask("mask[id,status,billingItem.id,networkComponent.downlinkComponent.hardwareId]").getObject else - service.object_mask("mask[id,status,guestNetworkComponent.guest.id]").getObject + service.object_mask("mask[id,status,billingItem.id,guestNetworkComponent.guest.id]").getObject end end @@ -216,7 +244,7 @@ def softlayer_properties(object_mask = nil) private def self.network_vlan_mask - "mask[firewallNetworkComponents[id,status,networkComponent.downlinkComponent.hardwareId],firewallGuestNetworkComponents[id,status,guestNetworkComponent.guest.id]]" + "mask[firewallNetworkComponents[id,status,billingItem.id,networkComponent.downlinkComponent.hardwareId],firewallGuestNetworkComponents[id,status,billingItem.id,guestNetworkComponent.guest.id]]" end def self.default_rules_mask diff --git a/lib/softlayer/VLANFirewall.rb b/lib/softlayer/VLANFirewall.rb index c9aa859..7f834a0 100644 --- a/lib/softlayer/VLANFirewall.rb +++ b/lib/softlayer/VLANFirewall.rb @@ -51,6 +51,34 @@ class VLANFirewall < SoftLayer::ModelBase end end + ## + # Cancel the firewall + # + # This method cancels the firewall and releases its + # resources. The cancellation is processed immediately! + # Call this method with careful deliberation! + # + # Notes is a string that describes the reason for the + # cancellation. If empty or nil, a default string will + # be added + # + def cancel!(notes = nil) + user = self.softlayer_client[:Account].object_mask("mask[id,account.id]").getCurrentUser + notes = "Cancelled by a call to #{__method__} in the softlayer_api gem" if notes == nil || notes == "" + + cancellation_request = { + 'accountId' => user['account']['id'], + 'userId' => user['id'], + 'items' => [ { + 'billingItemId' => self['networkVlanFirewall']['billingItem']['id'], + 'immediateCancellationFlag' => true + } ], + 'notes' => notes + } + + self.softlayer_client[:Billing_Item_Cancellation_Request].createObject(cancellation_request) + end + ## # Change the set of rules for the firewall. # The rules_data parameter should be an array of hashes where @@ -82,8 +110,8 @@ def change_rules!(rules_data) # through the firewall. Compare the behavior of this routine with # change_routing_bypass! # - # It is important to note that changing the bypass to :bypass_firewall_rules - # removes ALL the protection offered by the firewall. This routine should be + # It is important to note that changing the bypass to :bypass_firewall_rules + # removes ALL the protection offered by the firewall. This routine should be # used with extreme discretion. # # Note that this routine queues a rule change and rule changes may take @@ -117,7 +145,7 @@ def change_rules_bypass!(bypass_symbol) # change_rules_bypass! # # It is important to note that changing the routing to :route_around_firewall - # removes ALL the protection offered by the firewall. This routine should be + # removes ALL the protection offered by the firewall. This routine should be # used with extreme discretion. # # Note that this routine constructs a transaction. The Routing change @@ -224,7 +252,7 @@ def rules_ACL_id def self.vlan_firewall_mask return "mask[primaryRouter,highAvailabilityFirewallFlag," + "firewallInterfaces.firewallContextAccessControlLists," + - "networkVlanFirewall[id, datacenter, primaryIpAddress, firewallType, fullyQualifiedDomainName]]" + "networkVlanFirewall[id,datacenter,primaryIpAddress,firewallType,fullyQualifiedDomainName,billingItem.id]]" end def self.default_rules_mask From b61e1101d82f7a90379795405a48c82ec873b1d9 Mon Sep 17 00:00:00 2001 From: Scott Thompson Date: Fri, 22 Aug 2014 15:35:30 -0500 Subject: [PATCH 29/32] Completes #18 ? --- lib/softlayer/Account.rb | 2 +- lib/softlayer/VLANFirewall.rb | 52 +++++++++++++++++------------------ 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/lib/softlayer/Account.rb b/lib/softlayer/Account.rb index f91c478..3c5a8fe 100644 --- a/lib/softlayer/Account.rb +++ b/lib/softlayer/Account.rb @@ -143,7 +143,7 @@ def find_VLAN_with_number(vlan_number) vlan_data = self.service.object_mask("mask[id,vlanNumber,primaryRouter,networkSpace]").object_filter(filter).getNetworkVlans return vlan_data end - + ## # Using the login credentials in the client, retrieve # the account associated with those credentials. diff --git a/lib/softlayer/VLANFirewall.rb b/lib/softlayer/VLANFirewall.rb index 7f834a0..a63f9ea 100644 --- a/lib/softlayer/VLANFirewall.rb +++ b/lib/softlayer/VLANFirewall.rb @@ -51,6 +51,32 @@ class VLANFirewall < SoftLayer::ModelBase end end + ## + # Returns the name of the primary router the firewall is attached to. + # This is often a "customer router" in one of the datacenters. + def primaryRouter + return self['primaryRouter']['hostname'] + end + + ## + # The fully qualified domain name of the physical device the + # firewall is implemented by. + def fullyQualifiedDomainName + if self.has_sl_property?('networkVlanFirewall') + return self['networkVlanFirewall']['fullyQualifiedDomainName'] + else + return @softlayer_hash + end + end + + ## + # Returns true if this is a "high availability" firewall, that is a firewall + # that exists as one member of a redundant pair. + def high_availability? + # note that highAvailabilityFirewallFlag is a boolean in the softlayer hash + return self.has_sl_property?('highAvailabilityFirewallFlag') && self['highAvailabilityFirewallFlag'] + end + ## # Cancel the firewall # @@ -170,32 +196,6 @@ def change_routing_bypass!(routing_symbol) end end - ## - # Returns the name of the primary router the firewall is attached to. - # This is often a "customer router" in one of the datacenters. - def primaryRouter - return self['primaryRouter']['hostname'] - end - - ## - # The fully qualified domain name of the physical device the - # firewall is implemented by. - def fullyQualifiedDomainName - if self.has_sl_property?('networkVlanFirewall') - return self['networkVlanFirewall']['fullyQualifiedDomainName'] - else - return @softlayer_hash - end - end - - ## - # Returns true if this is a "high availability" firewall, that is a firewall - # that exists as one member of a redundant pair. - def high_availability? - # note that highAvailabilityFirewallFlag is a boolean in the softlayer hash - return self.has_sl_property?('highAvailabilityFirewallFlag') && self['highAvailabilityFirewallFlag'] - end - ## # Collect a list of the firewalls on the account. # From 028df3a6c154e9bbe8ae1d52fce2c43146c74dba Mon Sep 17 00:00:00 2001 From: Scott Thompson Date: Tue, 2 Sep 2014 11:09:20 -0500 Subject: [PATCH 30/32] Fixed Grammar problems with some of the documentation --- lib/softlayer/ServerFirewall.rb | 19 ++++++++++++------- lib/softlayer/VLANFirewall.rb | 23 ++++++++++++++--------- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/lib/softlayer/ServerFirewall.rb b/lib/softlayer/ServerFirewall.rb index 7f106b7..0fcf9f2 100644 --- a/lib/softlayer/ServerFirewall.rb +++ b/lib/softlayer/ServerFirewall.rb @@ -10,10 +10,10 @@ module SoftLayer # SoftLayer environment that exists in a 1 to 1 relationship # with a particular server (either Bare Metal or Virtual). # - # This is also called a "Shared Firewall" in some documentation + # This is also called a "Shared Firewall" in some documentation. # # Instances of this class rougly correspond to instances of the - # SoftLayer_Network_Component_Firewall service entity + # SoftLayer_Network_Component_Firewall service entity. # class ServerFirewall < SoftLayer::ModelBase include ::SoftLayer::DynamicAttribute @@ -28,9 +28,9 @@ class ServerFirewall < SoftLayer::ModelBase ## # :attr_reader: - # The firewall rules assigned to this firewall. These rules will + # The firewall rules assigned to this firewall. These rules will # be read from the network API every time you ask for the value - # of this property. To change the rules on the server use the + # of this property. To change the rules on the server use the # asymmetric method change_rules! sl_dynamic_attr :rules do |firewall_rules| firewall_rules.should_update? do @@ -90,7 +90,7 @@ def initialize(client, network_hash) # Cancel the firewall # # This method cancels the firewall and releases its - # resources. The cancellation is processed immediately! + # resources. The cancellation is processed immediately! # Call this method with careful deliberation! # # Notes is a string that describes the reason for the @@ -121,13 +121,18 @@ def cancel!(notes = nil) # hashes should be entries from the array returned by # SoftLayer::ServerFirewall.default_rules_mask_keys # + # *NOTE!* When changing the rules on the firewall, you must + # pass in a complete set of rules each time. The rules you + # submit will replace the entire ruleset on the destination + # firewall. + # # *NOTE!* The rules themselves have an "orderValue" property. # It is this property, and *not* the order that the rules are # found in the rules_data array, which will determine in which # order the firewall applies it's rules to incomming traffic. # # *NOTE!* Changes to the rules are not applied immediately - # on the server side. Instead, they are enqueued by the + # on the server side. Instead, they are enqueued by the # firewall update service and updated periodically. A typical # update will take about one minute to apply, but times may vary # depending on the system load and other circumstances. @@ -150,7 +155,7 @@ def change_rules!(rules_data) # used with careful deliberation. # # Note that this routine queues a rule change and rule changes may take - # time to process. The change will probably not take effect immediately + # time to process. The change will probably not take effect immediately. # # The two symbols accepted as arguments by this routine are: # :apply_firewall_rules - The rules of the firewall are applied to traffic. This is the default operating mode of the firewall diff --git a/lib/softlayer/VLANFirewall.rb b/lib/softlayer/VLANFirewall.rb index a63f9ea..311f37f 100644 --- a/lib/softlayer/VLANFirewall.rb +++ b/lib/softlayer/VLANFirewall.rb @@ -9,10 +9,10 @@ module SoftLayer # all the servers on a VLAN in the SoftLayer Environment. It is # also known as a "Dedicated Firewall" in some documentation. # - # Instances of this class are a bit odd because they actually represent - # VLANs (the VLAN protected by the firewall) and not the physical hardware - # implementing the firewall itself. (although the device is accessible as - # the "networkVlanFirewall" property) + # Instances of this class are a bit odd because they actually represent a + # VLAN (the VLAN protected by the firewall to be specific), and not the + # physical hardware implementing the firewall itself. (although the device + # is accessible as the "networkVlanFirewall" property) # # As a result, instances of this class correspond to certain instances # in the SoftLayer_Network_Vlan service. @@ -23,7 +23,7 @@ class VLANFirewall < SoftLayer::ModelBase ## #:attr_reader: # - # The number of the VLAN protected by this firewall + # The number of the VLAN protected by this firewall. # sl_attr :VLAN_number, 'vlanNumber' @@ -81,12 +81,12 @@ def high_availability? # Cancel the firewall # # This method cancels the firewall and releases its - # resources. The cancellation is processed immediately! + # resources. The cancellation is processed immediately! # Call this method with careful deliberation! # # Notes is a string that describes the reason for the # cancellation. If empty or nil, a default string will - # be added + # be added. # def cancel!(notes = nil) user = self.softlayer_client[:Account].object_mask("mask[id,account.id]").getCurrentUser @@ -112,10 +112,15 @@ def cancel!(notes = nil) # hashes should be entries from the array returned by # SoftLayer::ServerFirewall.default_rules_mask_keys # + # *NOTE!* When changing the rules on the firewall, you must + # pass in a complete set of rules each time. The rules you + # submit will replace the entire ruleset on the destination + # firewall. + # # *NOTE!* The rules themselves have an "orderValue" property. # It is this property, and *not* the order that the rules are # found in the rules_data array, which will determine in which - # order the firewall applies it's rules to incomming traffic. + # order the firewall applies its rules to incomming traffic. # # *NOTE!* Changes to the rules are not applied immediately # on the server side. Instead, they are enqueued by the @@ -141,7 +146,7 @@ def change_rules!(rules_data) # used with extreme discretion. # # Note that this routine queues a rule change and rule changes may take - # time to process. The change will probably not take effect immediately + # time to process. The change will probably not take effect immediately. # # The two symbols accepted as arguments by this routine are: # :apply_firewall_rules - The rules of the firewall are applied to traffic. This is the default operating mode of the firewall From 244295dffd1c82187f677f25412dbe56d51f3d65 Mon Sep 17 00:00:00 2001 From: Scott Thompson Date: Thu, 18 Sep 2014 10:03:33 -0500 Subject: [PATCH 31/32] Changed gem label for the 3.0.b2 build --- lib/softlayer/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/softlayer/base.rb b/lib/softlayer/base.rb index 6f012a7..9ecd4a7 100644 --- a/lib/softlayer/base.rb +++ b/lib/softlayer/base.rb @@ -12,7 +12,7 @@ module SoftLayer # The version number (including major, minor, and bugfix numbers) # This should change in accordance with the concept of Semantic Versioning - VERSION = "3.0.0" # version history in the CHANGELOG.textile file at the root of the source + VERSION = "3.0.b2" # version history in the CHANGELOG.textile file at the root of the source # The base URL of the SoftLayer API available to the public internet. API_PUBLIC_ENDPOINT = 'https://api.softlayer.com/xmlrpc/v3/' From 18759676fee43b634c050689877ea0b66bfaae7c Mon Sep 17 00:00:00 2001 From: Scott Thompson Date: Tue, 30 Sep 2014 09:48:40 -0500 Subject: [PATCH 32/32] Revert the label to 3.0.0 for the release --- lib/softlayer/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/softlayer/base.rb b/lib/softlayer/base.rb index 9ecd4a7..6f012a7 100644 --- a/lib/softlayer/base.rb +++ b/lib/softlayer/base.rb @@ -12,7 +12,7 @@ module SoftLayer # The version number (including major, minor, and bugfix numbers) # This should change in accordance with the concept of Semantic Versioning - VERSION = "3.0.b2" # version history in the CHANGELOG.textile file at the root of the source + VERSION = "3.0.0" # version history in the CHANGELOG.textile file at the root of the source # The base URL of the SoftLayer API available to the public internet. API_PUBLIC_ENDPOINT = 'https://api.softlayer.com/xmlrpc/v3/'