softlayer_api 2.0.1 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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