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.
- 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
|