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.
- checksums.yaml +4 -4
- data/CHANGELOG.textile +36 -0
- data/README.textile +17 -7
- data/examples/account_info.rb +6 -3
- data/examples/account_servers.rb +48 -0
- data/examples/create_ticket.rb +33 -22
- data/examples/open_tickets.rb +14 -19
- data/examples/order_bare_metal_package.rb +154 -0
- data/examples/order_virtual_server.rb +85 -0
- data/examples/ticket_info.rb +13 -14
- data/lib/softlayer/APIParameterFilter.rb +100 -23
- data/lib/softlayer/Account.rb +140 -0
- data/lib/softlayer/BareMetalServer.rb +233 -0
- data/lib/softlayer/BareMetalServerOrder.rb +227 -0
- data/lib/softlayer/BareMetalServerOrder_Package.rb +162 -0
- data/lib/softlayer/Client.rb +54 -9
- data/lib/softlayer/Config.rb +2 -3
- data/lib/softlayer/DynamicAttribute.rb +170 -0
- data/lib/softlayer/ModelBase.rb +141 -0
- data/lib/softlayer/ObjectFilter.rb +61 -21
- data/lib/softlayer/ObjectMaskParser.rb +157 -0
- data/lib/softlayer/ObjectMaskProperty.rb +83 -0
- data/lib/softlayer/ObjectMaskToken.rb +107 -0
- data/lib/softlayer/ObjectMaskTokenizer.rb +88 -0
- data/lib/softlayer/ProductItemCategory.rb +137 -0
- data/lib/softlayer/ProductPackage.rb +196 -0
- data/lib/softlayer/Server.rb +245 -0
- data/lib/softlayer/Service.rb +12 -9
- data/lib/softlayer/Ticket.rb +210 -0
- data/lib/softlayer/VirtualServer.rb +388 -0
- data/lib/softlayer/VirtualServerOrder.rb +263 -0
- data/lib/softlayer/base.rb +9 -9
- data/lib/softlayer/object_mask_helpers.rb +46 -18
- data/lib/softlayer_api.rb +15 -0
- metadata +49 -15
data/examples/ticket_info.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
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
|
-
#
|
6
|
-
# internally by the
|
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
|
31
|
+
# chain when you call a method through a Service
|
11
32
|
#
|
12
|
-
# For example, given a
|
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
|
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
|
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.
|
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
|
-
#
|
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
|
53
|
-
raise ArgumentError, "object_mask expects
|
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
|
-
|
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
|
-
#
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
201
|
+
@target.call_softlayer_api_with_params(method_name, self, args)
|
123
202
|
else
|
124
|
-
|
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
|