softlayer_api 2.0.1 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -25,24 +25,23 @@ require 'softlayer_api'
25
25
  require 'pp'
26
26
 
27
27
  softlayer_client = SoftLayer::Client.new(
28
- :username => "joecustomer", # enter your username here
29
- :api_key => "feeddeadbeefbadf00d..." # enter your api key here
28
+ # :username => "joecustomer", # enter your username here
29
+ # :api_key => "feeddeadbeefbadf00d..." # enter your api key here
30
30
  )
31
31
 
32
32
  begin
33
+ # Demonstrates using the low-level capabilities of the gem to get
34
+ # at information. In this case we are talking directly to the ticket
35
+ # service
33
36
  ticket_service = softlayer_client.service_named("Ticket");
34
- ticket_ref = ticket_service.object_with_id(8172109)
37
+
38
+ # Retrive a particular ticket by ID (you will have to substitute an existing ticket's ID here)
39
+ ticket_ref = ticket_service.object_with_id(12345)
35
40
 
36
- ticket = ticket_ref.object_mask("mask[updates[entry,createDate],assignedUserId,attachedHardware.datacenter]".getObject
41
+ # Retrive very specific information about the ticket
42
+ ticket = ticket_ref.object_mask("mask[updates[entry,createDate],assignedUserId,attachedHardware.datacenter]").getObject
43
+
37
44
  pp ticket
38
45
  rescue Exception => exception
39
- puts "Unable to retrieve the ticket"
40
- end
41
-
42
- # update the ticket
43
- begin
44
- updates = ticket_ref.addUpdate({"entry" => "An update from the Ruby client!"})
45
- puts "Update ticket 123456. The new update's id is #{updates[0]['id']}"
46
- rescue Exception => exception
47
- puts "Unable to update the ticket: #{exception}"
48
- end
46
+ puts "Unable to retrieve the ticket #{exception}"
47
+ end
@@ -1,43 +1,76 @@
1
+ #
2
+ # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ # THE SOFTWARE.
21
+ #
1
22
 
2
23
  module SoftLayer
3
- # An <tt>APIParameterFilter</tt> is an intermediary object that understands how
24
+ # An +APIParameterFilter+ is an intermediary object that understands how
4
25
  # to accept the other API parameter filters and carry their values to
5
- # <tt>method_missing</tt> in <tt>Service</tt>. Instances of this class are created
6
- # internally by the <tt>Service</tt> in its handling of a method call and you
26
+ # method_missing in Service. Instances of this class are created
27
+ # internally by the Service in its handling of a method call and you
7
28
  # should not have to create instances of this class directly.
8
29
  #
9
30
  # Instead, to use an API filter, you add a filter method to the call
10
- # chain when you call a method through a <tt>SoftLayer::Service</tt>
31
+ # chain when you call a method through a Service
11
32
  #
12
- # For example, given a <tt>SoftLayer::Service</tt> instance called <tt>account_service</tt>
33
+ # For example, given a Service instance called +account_service+
13
34
  # you could take advantage of the API filter that identifies a particular
14
- # object known to that service using the <tt>object_with_id</tt> method :
35
+ # object known to that service using the object_with_id method :
15
36
  #
16
37
  # account_service.object_with_id(91234).getSomeAttribute
17
38
  #
18
- # The invocation of <tt>object_with_id</tt> will cause an instance of this
39
+ # The invocation of object_with_id will cause an instance of this
19
40
  # class to be created with the service as its target.
20
41
  #
21
42
  class APIParameterFilter
43
+ # The target of this API Parameter Filter. Should the filter
44
+ # receive an unknown method call, method_missing will forward
45
+ # the call on to the target. This is supposed to be an instance of
46
+ # the SoftLayer::Service class.
22
47
  attr_reader :target
48
+
49
+ # The collected parameters represented by this filter. These parameters
50
+ # are passed along to the target when method_missing is forwarding
51
+ # a message.
23
52
  attr_reader :parameters
24
53
 
54
+ # Construct a filter with the given target (and starting parameters if given)
25
55
  def initialize(target, starting_parameters = nil)
26
56
  @target = target
27
57
  @parameters = starting_parameters || {}
28
58
  end
29
59
 
60
+ ##
30
61
  # Adds an API filter that narrows the scope of a call to an object with
31
- # a particular ID. For example, if you want to get the ticket
62
+ # a particular ID. For example, if you want to get the ticket
32
63
  # with an ID of 12345 from the ticket service you might use
33
64
  #
34
- # ticket_service.object_with_id(12345).getObject
65
+ # ticket_service.object_with_id(12345).getObject
66
+ #
35
67
  def object_with_id(value)
36
68
  # we create a new object in case the user wants to store off the
37
69
  # filter chain and reuse it later
38
70
  APIParameterFilter.new(self.target, @parameters.merge({ :server_object_id => value }))
39
71
  end
40
72
 
73
+ ##
41
74
  # Use this as part of a method call chain to add an object mask to
42
75
  # the request. The arguments to object mask should be well formed
43
76
  # Extended Object Mask strings:
@@ -47,18 +80,24 @@ class APIParameterFilter
47
80
  # "mask(SoftLayer_Some_Type).aProperty").getObject
48
81
  #
49
82
  # The object_mask becomes part of the request sent to the server
83
+ # The object mask strings are parsed into ObjectMaskProperty trees
84
+ # and those trees are stored with the parameters. The trees are
85
+ # converted to strings immediately before the mask is used in a call
50
86
  #
51
87
  def object_mask(*args)
52
- raise ArgumentError, "object_mask expects well-formatted root object mask strings" if args.empty? || (1 == args.count && !args[0])
53
- raise ArgumentError, "object_mask expects well-formatted root object mask strings" if args.find { |arg| !(arg.kind_of?(String)) }
88
+ raise ArgumentError, "object_mask expects object mask strings" if args.empty? || (1 == args.count && !args[0])
89
+ raise ArgumentError, "object_mask expects strings" if args.find{ |arg| !arg.kind_of?(String) }
54
90
 
55
- object_mask = (@parameters[:object_mask] || []) + args
91
+ mask_parser = ObjectMaskParser.new()
92
+ object_masks = args.collect { |mask_string| mask_parser.parse(mask_string)}.flatten
93
+ object_mask = (@parameters[:object_mask] || []) + object_masks
56
94
 
57
95
  # we create a new object in case the user wants to store off the
58
96
  # filter chain and reuse it later
59
97
  APIParameterFilter.new(self.target, @parameters.merge({ :object_mask => object_mask }));
60
98
  end
61
99
 
100
+ ##
62
101
  # Adds a result limit which helps you page through a long list of entities
63
102
  #
64
103
  # The offset is the index of the first item you wish to have returned
@@ -67,14 +106,16 @@ class APIParameterFilter
67
106
  # For example, if you wanted to get five open tickets from the account
68
107
  # starting with the tenth item in the open tickets list you might call
69
108
  #
70
- # account_service.result_limit(10, 5).getOpenTickets
109
+ # account_service.result_limit(10, 5).getOpenTickets
110
+ #
71
111
  def result_limit(offset, limit)
72
112
  # we create a new object in case the user wants to store off the
73
113
  # filter chain and reuse it later
74
114
  APIParameterFilter.new(self.target, @parameters.merge({ :result_offset => offset, :result_limit => limit }))
75
115
  end
76
116
 
77
- # Adds an object_filter to the result. An Object Filter allows you
117
+ ##
118
+ # Adds an object_filter to the result. An Object Filter allows you
78
119
  # to specify criteria which are used to filter the results returned
79
120
  # by the server.
80
121
  def object_filter(filter)
@@ -85,46 +126,82 @@ class APIParameterFilter
85
126
  APIParameterFilter.new(self.target, @parameters.merge({:object_filter => filter}));
86
127
  end
87
128
 
129
+ ##
88
130
  # A utility method that returns the server object ID (if any) stored
89
131
  # in this parameter set.
90
132
  def server_object_id
91
133
  self.parameters[:server_object_id]
92
134
  end
93
135
 
94
- # a utility method that returns the object mask (if any) stored
136
+ ##
137
+ # A utility method that returns the object mask (if any) stored
95
138
  # in this parameter set.
96
139
  def server_object_mask
97
- self.parameters[:object_mask]
140
+ if parameters[:object_mask] && !parameters[:object_mask].empty?
141
+
142
+ # Reduce the masks found in this object to a minimal set
143
+ #
144
+ # If you pass the API a mask that asks for the same property twice (within
145
+ # the same type scope), the API treats it as an error (and throws an exception)
146
+ #
147
+ # We get around that by parsing the various masks that have been given to us
148
+ # merging their properties where possible, thereby removing the duplicates
149
+ # from the mask that actually gets passed to the server. As a side benefit,
150
+ # the mask we send to the server will be streamlined; without too many extraneous
151
+ # characters
152
+ reduced_masks = parameters[:object_mask].inject([]) do |merged_masks, object_mask|
153
+ mergeable_mask = merged_masks.find { |mask| mask.can_merge_with? object_mask }
154
+ if mergeable_mask
155
+ mergeable_mask.merge object_mask
156
+ else
157
+ merged_masks.push object_mask
158
+ end
159
+
160
+ merged_masks
161
+ end
162
+
163
+ if reduced_masks.count == 1
164
+ reduced_masks[0].to_s
165
+ else
166
+ "[#{reduced_masks.collect{|mask| mask.to_s}.join(',')}]"
167
+ end
168
+ else
169
+ nil
170
+ end
98
171
  end
99
172
 
100
- # a utility method that returns the starting index of the result limit (if any) stored
173
+ ##
174
+ # A utility method that returns the starting index of the result limit (if any) stored
101
175
  # in this parameter set.
102
176
  def server_result_limit
103
177
  self.parameters[:result_limit]
104
178
  end
105
179
 
106
- # a utility method that returns the starting index of the result limit offset (if any) stored
180
+ ##
181
+ # A utility method that returns the starting index of the result limit offset (if any) stored
107
182
  # in this parameter set.
108
183
  def server_result_offset
109
184
  self.parameters[:result_offset]
110
185
  end
111
186
 
187
+ ##
188
+ # A utility method that returns the object filter (if any) stored with this filter.
112
189
  def server_object_filter
113
190
  self.parameters[:object_filter]
114
191
  end
115
192
 
193
+ ##
116
194
  # This allows the filters to be used at the end of a long chain of calls that ends
117
- # at a service.
195
+ # at a service. It forwards the message and the parameters to the target of this
196
+ # method (presumably a Service instance)
118
197
  def method_missing(method_name, *args, &block)
119
198
  puts "SoftLayer::APIParameterFilter#method_missing called #{method_name}, #{args.inspect}" if $DEBUG
120
199
 
121
200
  if(!block && method_name.to_s.match(/[[:alnum:]]+/))
122
- result = @target.call_softlayer_api_with_params(method_name, self, args)
201
+ @target.call_softlayer_api_with_params(method_name, self, args)
123
202
  else
124
- result = super
203
+ super
125
204
  end
126
-
127
- result
128
205
  end
129
206
  end
130
207
 
@@ -0,0 +1,140 @@
1
+ #
2
+ # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ # THE SOFTWARE.
21
+ #
22
+
23
+ module SoftLayer
24
+ class Account < SoftLayer::ModelBase
25
+ include ::SoftLayer::DynamicAttribute
26
+
27
+ ##
28
+ # :attr_reader:
29
+ # The company name of the primary contact
30
+ sl_attr :companyName
31
+
32
+ ##
33
+ # :attr_reader:
34
+ # The given name name of the primary contact
35
+ sl_attr :firstName
36
+
37
+ ##
38
+ # :attr_reader:
39
+ # The surname of the primary contact
40
+ sl_attr :lastName
41
+
42
+ ##
43
+ # :attr_reader:
44
+ # The first address line for the primary contact's address
45
+ sl_attr :address1
46
+
47
+ ##
48
+ # :attr_reader:
49
+ # The second address line (if any, may be nil) for the primary contact's address
50
+ sl_attr :address2
51
+
52
+ ##
53
+ # :attr_reader:
54
+ # The city stored as part of the primary contact's address
55
+ sl_attr :city
56
+
57
+ ##
58
+ # :attr_reader:
59
+ # The two character abbreviation for the state, province, or other similar national
60
+ # division that is part of the address of the primary contact. For addresses
61
+ # outside of the US and Canada, where there may not be an equivalent to a state,
62
+ # this may be 'NA' (for not applicable)
63
+ sl_attr :state
64
+
65
+ ##
66
+ # :attr_reader:
67
+ # The country stored as part of the primary contact's address
68
+ sl_attr :country
69
+
70
+ ##
71
+ # :attr_reader:
72
+ # The postal code (in the US, aka. zip code) of the primary contact's address
73
+ sl_attr :postalCode
74
+
75
+ ##
76
+ # :attr_reader:
77
+ # The office phone nubmer listed for the primary contact
78
+ sl_attr :officePhone
79
+
80
+ ##
81
+ # The Bare Metal Servers (physical hardware) associated with the
82
+ # account. Unless you force these to update, they will be refreshed every
83
+ # five minutes.
84
+ # :call-seq:
85
+ # bare_metal_servers(force_update=false)
86
+ sl_dynamic_attr :bare_metal_servers do |bare_metal|
87
+ bare_metal.should_update? do
88
+ @last_bare_metal_update ||= Time.at(0)
89
+ (Time.now - @last_bare_metal_update) > 5 * 60 # update every 5 minutes
90
+ end
91
+
92
+ bare_metal.to_update do
93
+ @last_bare_metal_update = Time.now
94
+ BareMetalServer.find_servers(:client => self.softlayer_client)
95
+ end
96
+ end
97
+
98
+ ##
99
+ # The virtual servers (aka. CCIs or Virtual_Guests) associated with the
100
+ # account. Unless you force these to update, they will be refreshed every
101
+ # five minutes.
102
+ # :call-seq:
103
+ # virtual_servers(force_update=false)
104
+ sl_dynamic_attr :virtual_servers do |virtual_servers|
105
+ virtual_servers.should_update? do
106
+ @last_virtual_server_update ||= Time.at(0)
107
+ (Time.now - @last_virtual_server_update) > 5 * 60 # update every 5 minutes
108
+ end
109
+
110
+ virtual_servers.to_update do
111
+ @last_virtual_server_update = Time.now
112
+ VirtualServer.find_servers(:client => self.softlayer_client)
113
+ end
114
+ end
115
+
116
+ def service
117
+ softlayer_client["Account"].object_with_id(self.id)
118
+ end
119
+
120
+ ##
121
+ # Using the login credentials in the client, retrieve
122
+ # the account associated with those credentials.
123
+ #
124
+ def self.account_for_client(client = nil)
125
+ softlayer_client = client || Client.default_client
126
+ raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client
127
+
128
+ account_service = softlayer_client['Account']
129
+ network_hash = account_service.getObject()
130
+ new(softlayer_client, network_hash)
131
+ end
132
+
133
+ ##
134
+ # Get a list of the servers for the account. The list returned
135
+ # includes both bare metal and virtual servers
136
+ def servers
137
+ return self.bare_metal_servers + self.virtual_servers
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,233 @@
1
+ #
2
+ # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ # THE SOFTWARE.
21
+ #
22
+
23
+ module SoftLayer
24
+ #
25
+ # This class represents a Bare Metal Server, a hardware server in contrast to a virtual machine,
26
+ # in the SoftLayer Environment. It corresponds rougly to the +SoftLayer_Hardware+ and
27
+ # +SoftLayer_Hardware_Server+ services in the SoftLayer API
28
+ #
29
+ # http://sldn.softlayer.com/reference/datatypes/SoftLayer_Hardware
30
+ # http://sldn.softlayer.com/reference/datatypes/SoftLayer_Hardware_Server
31
+ #
32
+ class BareMetalServer < Server
33
+
34
+ ##
35
+ # Returns true if this +BareMetalServer+ is actually a Bare Metal Instance
36
+ # a Bare Metal Instance is physical, hardware server that is is provisioned to
37
+ # match a profile with characteristics similar to a Virtual Server
38
+ #
39
+ # This is an important distincition in rare cases, like cancelling the server.
40
+ #
41
+ def bare_metal_instance?
42
+ if has_sl_property?(:bareMetalInstanceFlag)
43
+ self["bareMetalInstanceFlag"] != 0
44
+ else
45
+ false
46
+ end
47
+ end
48
+
49
+ ##
50
+ # Sends a ticket asking that a server be cancelled (i.e. shutdown and
51
+ # removed from the account).
52
+ #
53
+ # The +cancellation_reason+ parameter should be a key from the hash returned
54
+ # by +BareMetalServer::cancellation_reasons+.
55
+ #
56
+ # You may add your own, more specific reasons for cancelling a server in the
57
+ # +comments+ parameter.
58
+ #
59
+ def cancel!(reason = :unneeded, comment = '')
60
+ if !bare_metal_instance? then
61
+ cancellation_reasons = self.class.cancellation_reasons()
62
+ cancel_reason = cancellation_reasons[reason] || cancellation_reasons[:unneeded]
63
+ softlayer_client["Ticket"].createCancelServerTicket(self.id, cancel_reason, comment, true, 'HARDWARE')
64
+ else
65
+ # Note that reason and comment are ignored in this case, unfortunately
66
+ softlayer_client['Billing_Item'].object_with_id(self.billingItem['id'].to_i).cancelService()
67
+ end
68
+ end
69
+
70
+ ##
71
+ # Returns the SoftLayer Service used to work with this Server
72
+ # For Bare Metal Servers that is +SoftLayer_Hardware+ though in some special cases
73
+ # you may have to use +SoftLayer_Hardware_Server+ as a type or service.
74
+ def service
75
+ return softlayer_client["Hardware"].object_with_id(self.id)
76
+ end
77
+
78
+ ##
79
+ # Returns the default object mask used when fetching servers from the API when an
80
+ # explicit object mask is not provided.
81
+ def self.default_object_mask
82
+ sub_mask = {
83
+ "mask(SoftLayer_Hardware_Server)" => [
84
+ 'bareMetalInstanceFlag',
85
+ 'provisionDate',
86
+ 'hardwareStatus',
87
+ 'memoryCapacity',
88
+ 'processorPhysicalCoreAmount',
89
+ 'networkManagementIpAddress',
90
+ 'networkComponents[id, status, speed, maxSpeed, name, ipmiMacAddress, ipmiIpAddress, macAddress, primaryIpAddress, port, primarySubnet]',
91
+ 'activeTransaction[id, transactionStatus[friendlyName,name]]',
92
+ 'hardwareChassis[id, name]'
93
+ ]
94
+ }
95
+
96
+ super.merge(sub_mask)
97
+ end
98
+
99
+ ##
100
+ # Returns a list of the cancellation reasons to use when cancelling a server.
101
+ #
102
+ # When cancelling a server with the cancel! method, the first parameter is the reason and
103
+ # should be one of the keys in the hash returned by this method. This, in turn
104
+ # will be translated into a string which is, for all intents and purposes, a
105
+ # literal string constant with special meaning to the SoftLayer API.
106
+ #
107
+ def self.cancellation_reasons
108
+ {
109
+ :unneeded => 'No longer needed',
110
+ :closing => 'Business closing down',
111
+ :cost => 'Server / Upgrade Costs',
112
+ :migrate_larger => 'Migrating to larger server',
113
+ :migrate_smaller => 'Migrating to smaller server',
114
+ :datacenter => 'Migrating to a different SoftLayer datacenter',
115
+ :performance => 'Network performance / latency',
116
+ :support => 'Support response / timing',
117
+ :sales => 'Sales process / upgrades',
118
+ :moving => 'Moving to competitor',
119
+ }
120
+ end
121
+
122
+ ##
123
+ # Retrive the bare metal server with the given server ID from the
124
+ # SoftLayer API
125
+ #
126
+ # The options parameter should contain:
127
+ #
128
+ # <b>+:client+</b> - The client used to connect to the API
129
+ #
130
+ # If no client is given, then the routine will try to use Client.default_client
131
+ # If no client can be found the routine will raise an error.
132
+ def self.server_with_id(server_id, options = {})
133
+ softlayer_client = options[:client] || Client.default_client
134
+ raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client
135
+
136
+ hardware_service = softlayer_client["Hardware"]
137
+ hardware_service = hardware_service.object_mask(default_object_mask.to_sl_object_mask)
138
+
139
+ if options.has_key?(:object_mask)
140
+ object_mask = hardware_service.object_mask(options[:object_mask])
141
+ end
142
+
143
+ server_data = hardware_service.object_with_id(server_id).getObject()
144
+
145
+ return BareMetalServer.new(softlayer_client, server_data)
146
+ end
147
+
148
+ ##
149
+ # Retrieve a list of Bare Metal servers from the account
150
+ #
151
+ # The options parameter should contain:
152
+ #
153
+ # <b>+:client+</b> - The client used to connect to the API
154
+ #
155
+ # If no client is given, then the routine will try to use Client.default_client
156
+ # If no client can be found the routine will raise an error.
157
+ #
158
+ # You may filter the list returned by adding options:
159
+ #
160
+ # * <b>+:tags+</b> (array) - an array of strings representing tags to search for on the instances
161
+ # * <b>+:cpus+</b> (int) - return servers with the given number of (virtual) CPUs
162
+ # * <b>+:memory+</b> (int) - return servers with at least the given amount of memory (in Gigabytes)
163
+ # * <b>+:hostname+</b> (string) - return servers whose hostnames match the query string given (see ObjectFilter::query_to_filter_operation)
164
+ # * <b>+:domain+</b> (string) - filter servers to those whose domain matches the query string given (see ObjectFilter::query_to_filter_operation)
165
+ # * <b>+:datacenter+</b> (string) - find servers whose data center name matches the query string given (see ObjectFilter::query_to_filter_operation)
166
+ # * <b>+:nic_speed+</b> (int) - include servers with the given nic speed (in Mbps)
167
+ # * <b>+:public_ip+</b> (string) - return servers whose public IP address matches the query string given (see ObjectFilter::query_to_filter_operation)
168
+ # * <b>+:private_ip+</b> (string) - same as :public_ip, but for private IP addresses
169
+ #
170
+ # Additionally you may provide options related to the request itself:
171
+ #
172
+ # * <b>+:object_mask+</b> (string, hash, or array) - The object mask of properties you wish to receive for the items returned If not provided, the result will use the default object mask
173
+ # * <b>+:result_limit+</b> (hash with :limit, and :offset keys) - Limit the scope of results returned.
174
+ #
175
+ def self.find_servers(options_hash = {})
176
+ softlayer_client = options_hash[:client] || Client.default_client
177
+ raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client
178
+
179
+ if(options_hash.has_key? :object_filter)
180
+ object_filter = options_hash[:object_filter]
181
+ else
182
+ object_filter = {}
183
+ end
184
+
185
+ option_to_filter_path = {
186
+ :cpus => "hardware.processorPhysicalCoreAmount",
187
+ :memory => "hardware.memoryCapacity",
188
+ :hostname => "hardware.hostname",
189
+ :domain => "hardware.domain",
190
+ :datacenter => "hardware.datacenter.name",
191
+ :nic_speed => "hardware.networkComponents.maxSpeed",
192
+ :public_ip => "hardware.primaryIpAddress",
193
+ :private_ip => "hardware.primaryBackendIpAddress"
194
+ }
195
+
196
+ # For each of the options in the option_to_filter_path map, if the options hash includes
197
+ # that particular option, add a clause to the object filter that filters for the matching
198
+ # value
199
+ option_to_filter_path.each do |option, filter_path|
200
+ object_filter.merge!(SoftLayer::ObjectFilter.build(filter_path, options_hash[option])) if options_hash.has_key?(option)
201
+ end
202
+
203
+ # Tags get a much more complex object filter operation so we handle them separately
204
+ if options_hash.has_key?(:tags)
205
+ object_filter.merge!(SoftLayer::ObjectFilter.build("hardware.tagReferences.tag.name", {
206
+ 'operation' => 'in',
207
+ 'options' => [{
208
+ 'name' => 'data',
209
+ 'value' => options_hash[:tags]
210
+ }]
211
+ } ));
212
+ end
213
+
214
+ account_service = softlayer_client['Account']
215
+ account_service = account_service.object_filter(object_filter) unless object_filter.empty?
216
+ account_service = account_service.object_mask(default_object_mask.to_sl_object_mask)
217
+
218
+ if(options_hash.has_key? :object_mask)
219
+ account_service = account_service.object_mask(options_hash[:object_mask])
220
+ end
221
+
222
+ if options_hash.has_key?(:result_limit)
223
+ offset = options[:result_limit][:offset]
224
+ limit = options[:result_limit][:limit]
225
+
226
+ account_service = account_service.result_limit(offset, limit)
227
+ end
228
+
229
+ bare_metal_data = account_service.getHardware()
230
+ bare_metal_data.collect { |server_data| BareMetalServer.new(softlayer_client, server_data) }
231
+ end
232
+ end #BareMetalServer
233
+ end