softlayer_api 2.2.2 → 3.0.b1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.textile +9 -5
- data/examples/account_servers.rb +2 -2
- data/examples/create_ticket.rb +3 -3
- data/examples/open_tickets.rb +2 -2
- data/examples/ticket_info.rb +2 -2
- data/lib/softlayer/APIParameterFilter.rb +7 -21
- data/lib/softlayer/Account.rb +28 -19
- data/lib/softlayer/BareMetalServer.rb +13 -25
- data/lib/softlayer/BareMetalServerOrder.rb +6 -23
- data/lib/softlayer/BareMetalServerOrder_Package.rb +11 -29
- data/lib/softlayer/Client.rb +17 -23
- data/lib/softlayer/Config.rb +6 -22
- data/lib/softlayer/Datacenter.rb +53 -0
- data/lib/softlayer/DynamicAttribute.rb +3 -19
- data/lib/softlayer/ImageTemplate.rb +384 -0
- data/lib/softlayer/ModelBase.rb +4 -20
- data/lib/softlayer/ObjectFilter.rb +193 -191
- data/lib/softlayer/ObjectMaskParser.rb +3 -19
- data/lib/softlayer/ObjectMaskProperty.rb +3 -19
- data/lib/softlayer/ObjectMaskToken.rb +3 -19
- data/lib/softlayer/ObjectMaskTokenizer.rb +3 -19
- data/lib/softlayer/ProductItemCategory.rb +6 -23
- data/lib/softlayer/ProductPackage.rb +11 -32
- data/lib/softlayer/Server.rb +22 -19
- data/lib/softlayer/Ticket.rb +6 -45
- data/lib/softlayer/VirtualServer.rb +24 -101
- data/lib/softlayer/VirtualServerOrder.rb +14 -31
- data/lib/softlayer/VirtualServerUpgradeOrder.rb +142 -0
- data/lib/softlayer/base.rb +11 -30
- data/lib/softlayer/object_mask_helpers.rb +3 -19
- data/lib/softlayer_api.rb +12 -22
- metadata +7 -4
data/lib/softlayer/ModelBase.rb
CHANGED
@@ -1,24 +1,8 @@
|
|
1
|
-
|
1
|
+
#--
|
2
2
|
# Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved.
|
3
3
|
#
|
4
|
-
#
|
5
|
-
|
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
|
-
#
|
4
|
+
# For licensing information see the LICENSE.md file in the project root.
|
5
|
+
#++
|
22
6
|
|
23
7
|
module SoftLayer
|
24
8
|
##
|
@@ -131,7 +115,7 @@ module SoftLayer
|
|
131
115
|
def softlayer_properties(object_mask = nil)
|
132
116
|
raise "Abstract method softlayer_properties in ModelBase was called"
|
133
117
|
end
|
134
|
-
|
118
|
+
|
135
119
|
##
|
136
120
|
# The softlayer_hash stores the low-level information about an
|
137
121
|
# object as it was retrieved from the SoftLayer API.
|
@@ -1,26 +1,102 @@
|
|
1
|
-
|
1
|
+
#--
|
2
2
|
# Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved.
|
3
3
|
#
|
4
|
-
#
|
5
|
-
|
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
|
-
#
|
4
|
+
# For licensing information see the LICENSE.md file in the project root.
|
5
|
+
#++
|
22
6
|
|
23
7
|
module SoftLayer
|
8
|
+
##
|
9
|
+
# An ObjectFilter is a tool that, when passed to the SoftLayer API
|
10
|
+
# allows the API server to filter, or limit the result set for a call.
|
11
|
+
#
|
12
|
+
# Constructing ObjectFilters is an art that is currently somewhat
|
13
|
+
# arcane. This class tries to simplify filtering for the fundamental
|
14
|
+
# cases, while still allowing for more complex ObjectFilters to be
|
15
|
+
# created.
|
16
|
+
#
|
17
|
+
# To construct an object filter you begin with an instance of the
|
18
|
+
# class. At construction time, or in a "modify" call you can change
|
19
|
+
# the filter criteria using a fancy DSL syntax.
|
20
|
+
#
|
21
|
+
# For example, to filter virtual servers so that you only get ones
|
22
|
+
# whose domains end with "layer.com" you might use:
|
23
|
+
#
|
24
|
+
# object_filter = ObjectFilter.new do |filter|
|
25
|
+
# filter.accept(virtualGuests.domain).when_it ends_with("layer.com")
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# The set of criteria that can be included after "when_it" are defined
|
29
|
+
# by routines in the ObjectFilterDefinitionContext module.
|
30
|
+
class ObjectFilter
|
31
|
+
def initialize(&construction_block)
|
32
|
+
@filter_hash = {}
|
33
|
+
self.modify(&construction_block)
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
def empty?
|
38
|
+
@filter_hash.empty?
|
39
|
+
end
|
40
|
+
|
41
|
+
def modify(&construction_block)
|
42
|
+
ObjectFilterDefinitionContext.module_exec(self, &construction_block) if construction_block
|
43
|
+
end
|
44
|
+
|
45
|
+
def accept(key_path)
|
46
|
+
CriteriaAcceptor.new(self, key_path)
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_h
|
50
|
+
return @filter_hash.dup
|
51
|
+
end
|
52
|
+
|
53
|
+
def criteria_for_key_path(key_path)
|
54
|
+
raise "The key path cannot be empty when searching for criteria" if key_path.nil? || key_path.empty?
|
55
|
+
|
56
|
+
current_level = @filter_hash
|
57
|
+
keys = key_path.split('.')
|
58
|
+
|
59
|
+
while current_level && keys.count > 1
|
60
|
+
current_level = current_level[keys.shift]
|
61
|
+
end
|
62
|
+
|
63
|
+
if current_level
|
64
|
+
current_level[keys[0]]
|
65
|
+
else
|
66
|
+
nil
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def set_criteria_for_key_path(key_path, criteria)
|
71
|
+
current_level = @filter_hash
|
72
|
+
keys = key_path.split('.')
|
73
|
+
|
74
|
+
current_key = keys.shift
|
75
|
+
while current_level && !keys.empty?
|
76
|
+
if !current_level.has_key? current_key
|
77
|
+
current_level[current_key] = {}
|
78
|
+
end
|
79
|
+
current_level = current_level[current_key]
|
80
|
+
current_key = keys.shift
|
81
|
+
end
|
82
|
+
|
83
|
+
current_level[current_key] = criteria
|
84
|
+
end
|
85
|
+
|
86
|
+
class CriteriaAcceptor
|
87
|
+
def initialize(filter, key_path)
|
88
|
+
@filter = filter
|
89
|
+
@key_path = key_path
|
90
|
+
end
|
91
|
+
|
92
|
+
def when_it(criteria)
|
93
|
+
@filter.set_criteria_for_key_path(@key_path, criteria)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end # ObjectFilter
|
97
|
+
|
98
|
+
##
|
99
|
+
# :nodoc:
|
24
100
|
OBJECT_FILTER_OPERATORS = [
|
25
101
|
'*=', # Contains (ignoring case)
|
26
102
|
'^=', # Begins with (ignoring case)
|
@@ -35,192 +111,109 @@ module SoftLayer
|
|
35
111
|
'!~' # Does not Contain (case sensitive)
|
36
112
|
]
|
37
113
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
114
|
+
##
|
115
|
+
# The ObjectFilterDefinitionContext defines a bunch of methods
|
116
|
+
# that allow the property conditions of an object filter to
|
117
|
+
# be defined in a "pretty" way. Each method returns a block
|
118
|
+
# (a lambda, a proc) that, when called and pased the tail property
|
119
|
+
# of a property chain will generate a fragment of an object filter
|
120
|
+
# asking that that property match the given conditions.
|
121
|
+
#
|
122
|
+
# This class, as a whole, is largely an implementation detail
|
123
|
+
# of object filter definitions and there is probably not
|
124
|
+
# a good reason to call into it directly.
|
125
|
+
module ObjectFilterDefinitionContext
|
126
|
+
# Matches when the value in the field is exactly equal to the
|
127
|
+
# given value. This is a case-sensitive match
|
128
|
+
def self.is(value)
|
129
|
+
{ 'operation' => value }
|
53
130
|
end
|
54
131
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
result
|
132
|
+
# Matches is the value in the field does not exactly equal
|
133
|
+
# the value passed in.
|
134
|
+
def self.is_not(value)
|
135
|
+
filter_criteria('!=', value)
|
60
136
|
end
|
61
|
-
end
|
62
137
|
|
63
|
-
# This class defines the routines that are valid within the block provided to a call to
|
64
|
-
# ObjectFilter.build. This allows you to create object filters like:
|
65
|
-
#
|
66
|
-
# object_filter = SoftLayer::ObjectFilter.build("hardware.memory") { is_greater_than(2) }
|
67
|
-
#
|
68
|
-
class ObjectFilterBlockHandler
|
69
138
|
# Matches when the value is found within the field
|
70
139
|
# the search is not case sensitive
|
71
|
-
def contains(value)
|
72
|
-
|
140
|
+
def self.contains(value)
|
141
|
+
filter_criteria('*=', value)
|
73
142
|
end
|
74
143
|
|
75
144
|
# Matches when the value is found at the beginning of the
|
76
145
|
# field. This search is not case sensitive
|
77
|
-
def begins_with(value)
|
78
|
-
|
146
|
+
def self.begins_with(value)
|
147
|
+
filter_criteria('^=', value)
|
79
148
|
end
|
80
149
|
|
81
150
|
# Matches when the value is found at the end of the
|
82
151
|
# field. This search is not case sensitive
|
83
|
-
def ends_with(value)
|
84
|
-
|
152
|
+
def self.ends_with(value)
|
153
|
+
filter_criteria('$=', value)
|
85
154
|
end
|
86
155
|
|
87
|
-
#
|
88
|
-
|
89
|
-
|
90
|
-
ObjectFilterOperation.new('_=', value)
|
91
|
-
end
|
92
|
-
|
93
|
-
# Matches is the value in the field does not exactly equal
|
94
|
-
# the value passed in.
|
95
|
-
def is_not(value)
|
96
|
-
ObjectFilterOperation.new('!=', value)
|
156
|
+
# Maches the given value in a case-insensitive way
|
157
|
+
def self.matches_ignoring_case(value)
|
158
|
+
filter_criteria('_=', value)
|
97
159
|
end
|
98
160
|
|
99
161
|
# Matches when the value in the field is greater than the given value
|
100
|
-
def is_greater_than(value)
|
101
|
-
|
162
|
+
def self.is_greater_than(value)
|
163
|
+
filter_criteria('>', value)
|
102
164
|
end
|
103
165
|
|
104
166
|
# Matches when the value in the field is less than the given value
|
105
|
-
def is_less_than(value)
|
106
|
-
|
167
|
+
def self.is_less_than(value)
|
168
|
+
filter_criteria('<', value)
|
107
169
|
end
|
108
170
|
|
109
171
|
# Matches when the value in the field is greater than or equal to the given value
|
110
|
-
def is_greater_or_equal_to(value)
|
111
|
-
|
172
|
+
def self.is_greater_or_equal_to(value)
|
173
|
+
filter_criteria('>=', value)
|
112
174
|
end
|
113
175
|
|
114
176
|
# Matches when the value in the field is less than or equal to the given value
|
115
|
-
def is_less_or_equal_to(value)
|
116
|
-
|
177
|
+
def self.is_less_or_equal_to(value)
|
178
|
+
filter_criteria('<=', value)
|
117
179
|
end
|
118
180
|
|
119
181
|
# Matches when the value is found within the field
|
120
182
|
# the search _is_ case sensitive
|
121
|
-
def contains_exactly(value)
|
122
|
-
|
183
|
+
def self.contains_exactly(value)
|
184
|
+
filter_criteria('~', value)
|
123
185
|
end
|
124
186
|
|
125
187
|
# Matches when the value is not found within the field
|
126
188
|
# the search _is_ case sensitive
|
127
|
-
def does_not_contain(value)
|
128
|
-
|
189
|
+
def self.does_not_contain(value)
|
190
|
+
filter_criteria('!~', value)
|
129
191
|
end
|
130
|
-
end
|
131
192
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
#
|
136
|
-
# Constructing ObjectFilters is an art that is currently somewhat
|
137
|
-
# arcane. This class tries to simplify filtering for the fundamental
|
138
|
-
# cases, while still allowing for more complex ObjectFilters to be
|
139
|
-
# created.
|
140
|
-
#
|
141
|
-
# The ObjectFilter class is implemented as a hash that, when asked to provide
|
142
|
-
# an value for an unknown key, will create a sub element
|
143
|
-
# at that key which is, itself, an object filter. This allows you to build
|
144
|
-
# up object filters by chaining [] dereference operations.
|
145
|
-
#
|
146
|
-
# Starting empty object filter when you ask for +object_filter["foo"]+
|
147
|
-
# either the value at that hash location will be returned, or a new +foo+ key
|
148
|
-
# will be *added* to the object. The value of that key will be an +ObjectFilter+
|
149
|
-
# and that +ObjectFilter+ will be returned.
|
150
|
-
#
|
151
|
-
# By way of an example of chaining together +[]+ calls:
|
152
|
-
# object_filter["foo"]["bar"]["baz"] = 3
|
153
|
-
# yields an object filter like this:
|
154
|
-
# {"foo" => { "bar" => {"baz" => 3}}}
|
155
|
-
#
|
156
|
-
class ObjectFilter < Hash
|
157
|
-
# The default initialize for a hash is overridden
|
158
|
-
# so that object filters create sub-filters when asked
|
159
|
-
# for missing keys.
|
160
|
-
def initialize
|
161
|
-
super do |hash, key|
|
162
|
-
hash[key] = ObjectFilter.new
|
163
|
-
end
|
193
|
+
# Matches when the property's value is null
|
194
|
+
def self.is_null
|
195
|
+
{ 'operation' => 'is null' }
|
164
196
|
end
|
165
197
|
|
166
|
-
#
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
raise ArgumentError, "The key path to build cannot be empty" if !key_path
|
171
|
-
|
172
|
-
# Split the keypath into its constituent parts and notify the user
|
173
|
-
# if there are no parts
|
174
|
-
keys = key_path.split('.')
|
175
|
-
raise ArgumentError, "The key path to build cannot be empty" if keys.empty?
|
176
|
-
|
177
|
-
# This will be the result of the build
|
178
|
-
result = ObjectFilter.new
|
179
|
-
|
180
|
-
# chase down the key path to the last-but-one key
|
181
|
-
current_level = result
|
182
|
-
while keys.count > 1
|
183
|
-
current_level = current_level[keys.shift]
|
184
|
-
end
|
185
|
-
|
186
|
-
# if there is a block, then the query will come from
|
187
|
-
# calling the block. We warn in debug mode if you override a
|
188
|
-
# query that was passed directly with the value from a block.
|
189
|
-
if block
|
190
|
-
$stderr.puts "The query from the block passed to ObjectFilter:build will override the query passed as a parameter" if $DEBUG && query
|
191
|
-
block_handler = ObjectFilterBlockHandler.new
|
192
|
-
query = block_handler.instance_eval(&block)
|
193
|
-
end
|
194
|
-
|
195
|
-
# If we have a query, we assign its value to the last key
|
196
|
-
# otherwise, we build an emtpy filter at the bottom
|
197
|
-
if query
|
198
|
-
case
|
199
|
-
when query.kind_of?(Numeric)
|
200
|
-
current_level[keys.shift] = { 'operation' => query }
|
201
|
-
when query.kind_of?(SoftLayer::ObjectFilterOperation)
|
202
|
-
current_level[keys.shift] = query.to_h
|
203
|
-
when query.kind_of?(String)
|
204
|
-
current_level[keys.shift] = query_to_filter_operation(query)
|
205
|
-
when query.kind_of?(Hash)
|
206
|
-
current_level[keys.shift] = query
|
207
|
-
else
|
208
|
-
current_level[keys.shift]
|
209
|
-
end
|
210
|
-
else
|
211
|
-
current_level[keys.shift]
|
212
|
-
end
|
198
|
+
# Matches when the property's value is not null
|
199
|
+
def self.is_not_null()
|
200
|
+
{ 'operation' => 'not null' }
|
201
|
+
end
|
213
202
|
|
214
|
-
|
203
|
+
# This is a catch-all criteria matcher that allows for raw object filter conditions
|
204
|
+
# not covered by the more convenient methods above. The name is intentionally, annoyingly
|
205
|
+
# long and you should use this routine with solid knowledge and great care.
|
206
|
+
def self.satisfies_the_raw_condition(condition_hash)
|
207
|
+
condition_hash
|
215
208
|
end
|
216
209
|
|
217
|
-
#
|
218
|
-
#
|
219
|
-
# language into an Object Filter operations
|
210
|
+
# Accepts a query string defined by a simple query language.
|
211
|
+
# It translates strings in that language into criteria blocks
|
220
212
|
#
|
221
|
-
# Object Filter comparisons
|
222
|
-
#
|
223
|
-
#
|
213
|
+
# Object Filter comparisons can be done using operators. The
|
214
|
+
# set of accepted operators is found in the OBJECT_FILTER_OPERATORS
|
215
|
+
# array. The query string can consist of an operator followed
|
216
|
+
# by a space, followed by operand
|
224
217
|
# e.g.
|
225
218
|
# "*= smaug"
|
226
219
|
#
|
@@ -234,39 +227,48 @@ module SoftLayer
|
|
234
227
|
#
|
235
228
|
# This method corresponds to the +query_filter+ method in the SoftLayer-Python
|
236
229
|
# API.
|
237
|
-
def self.
|
238
|
-
|
239
|
-
query.strip!
|
230
|
+
def self.matches_query(query_string)
|
231
|
+
query = query_string.to_s.strip
|
240
232
|
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
end
|
245
|
-
|
246
|
-
operator = OBJECT_FILTER_OPERATORS.find do | operator_string |
|
247
|
-
query[0 ... operator_string.length] == operator_string
|
248
|
-
end
|
233
|
+
operator = OBJECT_FILTER_OPERATORS.find do | operator_string |
|
234
|
+
query[0 ... operator_string.length] == operator_string
|
235
|
+
end
|
249
236
|
|
250
|
-
|
251
|
-
|
252
|
-
else
|
253
|
-
case query
|
254
|
-
when /\A\*(.*)\*\Z/
|
255
|
-
operation = "*= #{$1}"
|
256
|
-
when /\A\*(.*)/
|
257
|
-
operation = "$= #{$1}"
|
258
|
-
when /\A(.*)\*\Z/
|
259
|
-
operation = "^= #{$1}"
|
260
|
-
else
|
261
|
-
operation = "_= #{query}"
|
262
|
-
end #case
|
263
|
-
end #if
|
237
|
+
if operator then
|
238
|
+
filter_criteria(operator, query[operator.length..-1])
|
264
239
|
else
|
265
|
-
|
266
|
-
|
240
|
+
case query
|
241
|
+
when /\A\*(.*)\*\Z/
|
242
|
+
contains($1)
|
243
|
+
when /\A\*(.*)/
|
244
|
+
ends_with($1)
|
245
|
+
when /\A(.*)\*\Z/
|
246
|
+
begins_with($1)
|
247
|
+
else
|
248
|
+
matches_ignoring_case(query)
|
249
|
+
end #case
|
250
|
+
end #if
|
251
|
+
end
|
267
252
|
|
268
|
-
|
269
|
-
end # query_to_filter_operation
|
253
|
+
private
|
270
254
|
|
271
|
-
|
255
|
+
def self.cleaned_up_operand(operand)
|
256
|
+
# try to convert the operand to an integer. If it works, return
|
257
|
+
# that integer
|
258
|
+
begin
|
259
|
+
return Integer(operand)
|
260
|
+
rescue
|
261
|
+
end
|
262
|
+
|
263
|
+
# The operand could not be converted to an integer so we try to make it a string
|
264
|
+
# and clean up the string
|
265
|
+
filter_operand = operand.to_s.strip
|
266
|
+
end
|
267
|
+
|
268
|
+
def self.filter_criteria(with_operator, operand)
|
269
|
+
filter_operand = cleaned_up_operand(operand)
|
270
|
+
filter_condition = "#{with_operator.to_s.strip} #{operand.to_s.strip}"
|
271
|
+
{ 'operation' => filter_condition }
|
272
|
+
end
|
273
|
+
end
|
272
274
|
end # SoftLayer
|