softlayer_api 1.0.8 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/softlayer_api.rb CHANGED
@@ -22,4 +22,9 @@
22
22
 
23
23
  require 'softlayer/base'
24
24
  require 'softlayer/object_mask_helpers'
25
- require 'softlayer/service'
25
+ require 'softlayer/APIParameterFilter'
26
+ require 'softlayer/ObjectFilter'
27
+ require 'softlayer/Config'
28
+
29
+ require 'softlayer/Client'
30
+ require 'softlayer/Service'
metadata CHANGED
@@ -1,47 +1,47 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: softlayer_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.8
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - SoftLayer Development Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-04-11 00:00:00.000000000 Z
11
+ date: 2014-05-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ~>
18
18
  - !ruby/object:Gem::Version
19
19
  version: '1.8'
20
- - - ">="
20
+ - - '>='
21
21
  - !ruby/object:Gem::Version
22
22
  version: 1.8.1
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
- - - "~>"
27
+ - - ~>
28
28
  - !ruby/object:Gem::Version
29
29
  version: '1.8'
30
- - - ">="
30
+ - - '>='
31
31
  - !ruby/object:Gem::Version
32
32
  version: 1.8.1
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: rake
35
35
  requirement: !ruby/object:Gem::Requirement
36
36
  requirements:
37
- - - ">="
37
+ - - '>='
38
38
  - !ruby/object:Gem::Version
39
39
  version: '0'
40
40
  type: :development
41
41
  prerelease: false
42
42
  version_requirements: !ruby/object:Gem::Requirement
43
43
  requirements:
44
- - - ">="
44
+ - - '>='
45
45
  - !ruby/object:Gem::Version
46
46
  version: '0'
47
47
  description: The softlayer_api gem offers a convenient mechanism for invoking the
@@ -51,16 +51,20 @@ executables: []
51
51
  extensions: []
52
52
  extra_rdoc_files: []
53
53
  files:
54
- - LICENSE.textile
55
54
  - README.textile
56
- - examples/accountInformation.rb
57
- - examples/createTicket.rb
58
- - examples/openTickets.rb
59
- - examples/ticket_info.rb
55
+ - LICENSE.textile
56
+ - lib/softlayer/APIParameterFilter.rb
60
57
  - lib/softlayer/base.rb
58
+ - lib/softlayer/Client.rb
59
+ - lib/softlayer/Config.rb
61
60
  - lib/softlayer/object_mask_helpers.rb
62
- - lib/softlayer/service.rb
61
+ - lib/softlayer/ObjectFilter.rb
62
+ - lib/softlayer/Service.rb
63
63
  - lib/softlayer_api.rb
64
+ - examples/account_info.rb
65
+ - examples/create_ticket.rb
66
+ - examples/open_tickets.rb
67
+ - examples/ticket_info.rb
64
68
  homepage: http://sldn.softlayer.com/
65
69
  licenses:
66
70
  - MIT
@@ -71,17 +75,17 @@ require_paths:
71
75
  - lib
72
76
  required_ruby_version: !ruby/object:Gem::Requirement
73
77
  requirements:
74
- - - ">="
78
+ - - '>='
75
79
  - !ruby/object:Gem::Version
76
80
  version: '0'
77
81
  required_rubygems_version: !ruby/object:Gem::Requirement
78
82
  requirements:
79
- - - ">="
83
+ - - '>='
80
84
  - !ruby/object:Gem::Version
81
85
  version: '0'
82
86
  requirements: []
83
87
  rubyforge_project:
84
- rubygems_version: 2.2.2
88
+ rubygems_version: 2.0.14
85
89
  signing_key:
86
90
  specification_version: 4
87
91
  summary: Library for accessing the SoftLayer portal API
@@ -1,467 +0,0 @@
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
- require 'rubygems'
24
- require 'net/https'
25
- require 'json/add/core'
26
-
27
- module SoftLayer
28
- # A subclass of Exception with nothing new provided. This simply provides
29
- # a unique type for exceptions from the SoftLayer API
30
- class SoftLayerAPIException < RuntimeError
31
- end
32
-
33
- # An APIParameterFilter is an intermediary object that understands how
34
- # to accept the other API parameter filters and carry their values to
35
- # method_missing in Service. Instances of this class are created
36
- # internally by the Service in it's handling of a method call and you
37
- # should not have to create instances of this class directly.
38
- #
39
- # Instead, to use an API filter, you add a filter method to the call
40
- # chain when you call a method through a SoftLayer::Service
41
- #
42
- # For example, given a SoftLayer::Service instance called "account_service"
43
- # you could take advantage of the API filter that identifies a particular
44
- # object known to that service using the 'object_with_id" method :
45
- #
46
- # account_service.object_with_id(91234).getSomeAttribute
47
- #
48
- # The invocation of object_with_id will cause an instance of this
49
- # class to be instantiated with the service as its target.
50
- #
51
- class APIParameterFilter
52
- attr_accessor :target
53
- attr_accessor :parameters
54
-
55
- def initialize
56
- @parameters = {}
57
- end
58
-
59
- def server_object_id
60
- self.parameters[:server_object_id]
61
- end
62
-
63
- def server_object_mask
64
- self.parameters[:object_mask]
65
- end
66
-
67
- def object_with_id(value)
68
- merged_object = APIParameterFilter.new;
69
- merged_object.target = self.target
70
- merged_object.parameters = @parameters.merge({ :server_object_id => value })
71
- merged_object
72
- end
73
-
74
- def object_mask(*args)
75
- merged_object = APIParameterFilter.new;
76
- merged_object.target = self.target
77
- merged_object.parameters = @parameters.merge({ :object_mask => args }) if args && !args.empty?
78
- merged_object
79
- end
80
-
81
- def result_limit(limit)
82
- merged_object = APIParameterFilter.new;
83
- merged_object.target = self.target
84
- merged_object.parameters = @parameters.merge({ :result_limit => limit })
85
- merged_object
86
- end
87
-
88
- def server_result_limit
89
- self.parameters[:result_limit]
90
- end
91
-
92
- def result_offset(offset)
93
- self.parameters[:result_offset] = offset
94
- self
95
- end
96
-
97
- def server_result_offset
98
- self.parameters[:result_offset]
99
- end
100
-
101
- def method_missing(method_name, *args, &block)
102
- return @target.call_softlayer_api_with_params(method_name, self, args, &block)
103
- end
104
- end
105
-
106
- # = SoftLayer API Service
107
- #
108
- # Instances of this class represent services in the SoftLayer API.
109
- #
110
- # You create a service with the name of one of the SoftLayer services
111
- # (documented on the http://sldn.softlayer.com web site). Once created
112
- # you can use the service to make method calls to the SoftLayer API.
113
- #
114
- # A typical use might look something like
115
- #
116
- # account_service = SoftLayer::Service("SoftLayer_Account", :username=>"<your user name here>" :api_key=>"<your api key here>")
117
- #
118
- # then to invoke a method simply call the service:
119
- #
120
- # account_service.getOpenTickets
121
- # => {... lots of information here representing the list of open tickets ...}
122
- #
123
- class Service
124
- # The name of the service that this object calls. Cannot be emtpy or nil.
125
- attr_accessor :service_name
126
-
127
- # A username passed as authentication for each request. Cannot be emtpy or nil.
128
- attr_accessor :username
129
-
130
- # An API key passed as part of the authentication of each request. Cannot be emtpy or nil.
131
- attr_accessor :api_key
132
-
133
- # The base URL for requests that are passed to the server. Cannot be emtpy or nil.
134
- attr_accessor :endpoint_url
135
-
136
- # The User-Agent header sent to SoftLayer API.
137
- attr_accessor :user_agent
138
-
139
- # Initialize an instance of the Client class. You pass in the service name
140
- # and optionally hash arguments specifying how the client should access the
141
- # SoftLayer API.
142
- #
143
- # The following symbols can be used as hash arguments to pass options to the constructor:
144
- # - <tt>:username</tt> - a non-empty string providing the username to use for requests to the service
145
- # - <tt>:api_key</tt> - a non-empty string providing the api key to use for requests to the service
146
- # - <tt>:endpoint_url</tt> - a non-empty string providing the endpoint URL to use for requests to the service
147
- #
148
- # If any of the options above are missing then the constructor will try to use the corresponding
149
- # global variable declared in the SoftLayer Module:
150
- # - <tt>$SL_API_USERNAME</tt>
151
- # - <tt>$SL_API_KEY</tt>
152
- # - <tt>$SL_API_BASE_URL</tt>
153
- #
154
- def initialize(service_name, options = {})
155
- raise SoftLayerAPIException.new("Please provide a service name") if service_name.nil? || service_name.empty?
156
- self.service_name = service_name;
157
-
158
- # pick up the username provided in options or the default one from the *globals*
159
- self.username = options[:username] || $SL_API_USERNAME || ""
160
-
161
- # pick up the api_key provided in options or the default one from the globals
162
- self.api_key = options[:api_key] || $SL_API_KEY || ""
163
-
164
- # pick up the url endpoint from options or the default one in the globals OR the
165
- # public endpoint
166
- self.endpoint_url = options[:endpoint_url] || $SL_API_BASE_URL || API_PUBLIC_ENDPOINT
167
-
168
- @user_agent = {"User-Agent" => options[:user_agent] || "SoftLayer API Ruby Client #{SoftLayer::VERSION}"}
169
-
170
- if($DEBUG)
171
- @method_missing_call_depth = 0
172
- end
173
- end #initalize
174
-
175
- # Use this to set the user agent string for this API client.
176
- def user_agent=(set)
177
- self.user_agent['User-Agent'] = set
178
- end
179
-
180
- # Use this as part of a method call chain to identify a particular
181
- # object as the target of the request. The parameter is the SoftLayer
182
- # object identifier you are interested in. For example, this call
183
- # would return the ticket whose ID is 35212
184
- #
185
- # ticket_service.object_with_id(35212).getObject
186
- #
187
- def object_with_id(object_of_interest)
188
- proxy = APIParameterFilter.new
189
- proxy.target = self
190
-
191
- return proxy.object_with_id(object_of_interest)
192
- end
193
-
194
- # Use this as part of a method call chain to add an object mask to
195
- # the request.The arguments to object mask should be the strings
196
- # that are the keys of the mask:
197
- #
198
- # ticket_service.object_mask("createDate", "modifyDate").getObject
199
- #
200
- # Before being used, the string passed will be url-encoded by this
201
- # routine. (i.e. there is no need to url-encode the strings beforehand)
202
- #
203
- # As an implementation detail, the object_mask becomes part of the
204
- # query on the url sent to the API server
205
- #
206
- def object_mask(*args)
207
- proxy = APIParameterFilter.new
208
- proxy.target = self
209
-
210
- return proxy.object_mask(*args)
211
- end
212
-
213
- def result_limit(limit)
214
- proxy = APIParameterFilter.new
215
- proxy.target = self
216
- return proxy.result_limit(limit)
217
- end
218
-
219
- def result_offset(offset)
220
- proxy = APIParameterFilter.new
221
- proxy.target = self
222
- return proxy.result_offset(offset)
223
- end
224
-
225
- # This is the primary mechanism by which requests are made. If you call
226
- # the service with a method it doesn't understand, it will send a call to
227
- # the endpoint for a method of the same name.
228
- #
229
- def method_missing(method_name, *args, &block)
230
- # During development, if you end up with a stray name in some
231
- # code, you can end up in an infinite recursive loop as method_missing
232
- # tries to resolve that name (believe me... it happens).
233
- # This mechanism looks for what it considers to be an unreasonable call
234
- # depth and kills the loop quickly.
235
- if($DEBUG)
236
- @method_missing_call_depth += 1
237
- if @method_missing_call_depth > 3 # 3 is somewhat arbitrary... really the call depth should only ever be 1
238
- @method_missing_call_depth = 0
239
- raise "stop infinite recursion #{method_name}, #{args.inspect}"
240
- end
241
- end
242
-
243
- # if we're in debug mode, we put out a little helpful information
244
- puts "SoftLayer::Service#method_missing called #{method_name}, #{args.inspect}" if $DEBUG
245
-
246
- result = call_softlayer_api_with_params(method_name, nil, args, &block);
247
-
248
- if($DEBUG)
249
- @method_missing_call_depth -= 1
250
- end
251
-
252
- return result
253
- end
254
-
255
- # Issue an HTTP request to call the given method from the SoftLayer API with
256
- # the parameters and arguments given.
257
- #
258
- # Parameters are information _about_ the call, the object mask or the
259
- # particular object in the SoftLayer API you are calling.
260
- #
261
- # Arguments are the arguments to the SoftLayer method that you wish to
262
- # invoke.
263
- #
264
- # This is intended to be used in the internal
265
- # processing of method_missing and need not be called directly.
266
- def call_softlayer_api_with_params(method_name, parameters, args, &block)
267
- # find out what URL will invoke the method (with the given parameters)
268
- request_url = url_to_call_method(method_name, parameters)
269
-
270
- # marshall the arguments into the http_request
271
- request_body = marshall_arguments_for_call(args)
272
-
273
- # construct an HTTP request for that method with the given URL
274
- http_request = http_request_for_method(method_name, request_url, request_body);
275
- http_request.basic_auth(self.username, self.api_key)
276
-
277
- # Send the url request and recover the results. Parse the response (if any)
278
- # as JSON
279
- json_results = issue_http_request(request_url, http_request, &block)
280
- if json_results
281
- # The JSON parser for Ruby parses JSON "Text" according to RFC 4627, but
282
- # not JSON values. As a result, 'JSON.parse("true")' yields a parsing
283
- # exception. To work around this, we force the result JSON text by
284
- # including it in Array markers, then take the first element of the
285
- # resulting array as the result of the parsing. This should allow values
286
- # like true, false, null, and numbers to parse the same way they would in
287
- # a browser.
288
- parsed_json = JSON.parse("[ #{json_results} ]")[0]
289
-
290
- # if the results indicate an error, convert it into an exception
291
- if parsed_json.kind_of?(Hash) && parsed_json['error']
292
- raise SoftLayerAPIException.new(parsed_json['error'])
293
- end
294
- else
295
- parsed_json = nil
296
- end
297
-
298
- # return the results, if any
299
- return parsed_json
300
- end
301
-
302
- # Marshall the arguments into a JSON string suitable for the body of
303
- # an HTTP message. This is intended to be used in the internal
304
- # processing of method_missing and need not be called directly.
305
- def marshall_arguments_for_call(args)
306
- request_body = nil;
307
-
308
- if(args && !args.empty?)
309
- request_body = {"parameters" => args}.to_json
310
- end
311
-
312
- return request_body
313
- end
314
-
315
- # Given a method name, determine the appropriate HTTP mechanism
316
- # for sending a request to execute that method to the server.
317
- # and create a Net::HTTP request of that type. This is intended
318
- # to be used in the internal processing of method_missing and
319
- # need not be called directly.
320
- def http_request_for_method(method_name, method_url, request_body = nil)
321
- content_type_header = {"Content-Type" => "application/json"}
322
-
323
- # This is a workaround for a potential problem that arises from mis-using the
324
- # API. If you call SoftLayer_Virtual_Guest and you call the getObject method
325
- # but pass a virtual guest as a parameter, what happens is the getObject method
326
- # is called through an HTTP POST verb and the API creates a new CCI that is a copy
327
- # of the one you passed in.
328
- #
329
- # The counter-intuitive creation of a new CCI is unexpected and, even worse,
330
- # is something you can be billed for. To prevent that, we ignore the request
331
- # body on a "getObject" call and print out a warning.
332
- if (method_name == :getObject) && (nil != request_body) then
333
- $stderr.puts "Warning - The getObject method takes no parameters. The parameters you have provided will be ignored."
334
- request_body = nil
335
- end
336
-
337
- if request_body && !request_body.empty?
338
- url_request = Net::HTTP::Post.new(method_url.request_uri(), content_type_header.merge(self.user_agent))
339
- else
340
- url_request = Net::HTTP::Get.new(method_url.request_uri(), self.user_agent)
341
- end
342
-
343
- # This warning should be obsolete as we should be using POST if the user
344
- # has provided parameters. I'm going to leave it in, however, on the off
345
- # chance that it catches a case we aren't expecting.
346
- if request_body && !url_request.request_body_permitted?
347
- $stderr.puts("Warning - The HTTP request for #{method_name} does not allow arguments to be passed to the server")
348
- else
349
- # Otherwise, add the arguments as the body of the request
350
- url_request.body = request_body
351
- end
352
-
353
- url_request
354
- end
355
-
356
- # Connect to the network and request the content of the resource
357
- # specified. This is used to do the actual work of connecting
358
- # to the SoftLayer servers and exchange data. This is intended
359
- # to be used in the internal processing of method_missing and
360
- # need not be called directly.
361
- def issue_http_request(request_url, http_request, &block)
362
- # create and run an SSL request
363
- https = Net::HTTP.new(request_url.host, request_url.port)
364
- https.use_ssl = (request_url.scheme == "https")
365
-
366
- # This line silences an annoying warning message if you're in debug mode
367
- https.verify_mode = OpenSSL::SSL::VERIFY_NONE if $DEBUG
368
-
369
- https.start do |http|
370
-
371
- puts "SoftLayer API issuing an HTTP request for #{request_url}" if $DEBUG
372
-
373
- response = https.request(http_request)
374
-
375
- case response
376
- when Net::HTTPSuccess
377
- return response.body
378
- else
379
- # We have an error. It might have a meaningful error message
380
- # from the server. Check to see if there is a body and whether
381
- # or not that body parses as JSON. If it does, then we return
382
- # that as a result (assuming it's an error)
383
- json_parses = false
384
- body = response.body
385
-
386
- begin
387
- if body
388
- JSON.parse(body)
389
- json_parses = true
390
- end
391
- rescue => json_parse_exception
392
- json_parses = false;
393
- end
394
-
395
- # Let the HTTP library generate and raise an exception if
396
- # the body was empty or could not be parsed as JSON
397
- response.value() if !json_parses
398
-
399
- return body
400
- end
401
- end
402
- end
403
-
404
- # Construct a URL for calling the given method on this endpoint and
405
- # expecting a JSON response. This is intended to be used in the internal
406
- # processing of method_missing and need not be called directly.
407
- def url_to_call_method(method_name, parameters)
408
- method_path = method_name.to_s
409
-
410
- # if there's an object ID on the parameters, add that to the URL
411
- if(parameters && parameters.server_object_id)
412
- method_path = parameters.server_object_id.to_s + "/" + method_path
413
- end
414
-
415
- # tag ".json" onto the method path (if it's not already there)
416
- method_path.sub!(%r|(\.json){0,1}$|, ".json")
417
-
418
- # put the whole thing together into a URL
419
- # (reusing a variation on the clever regular expression above. This one appends a "slash"
420
- # to the service name if theres not already one there otherwise. Without it URI.join
421
- # doesn't do the right thing)
422
- uri = URI.join(self.endpoint_url, self.service_name.sub(%r{/*$},"/"), method_path)
423
-
424
- query_string = nil
425
-
426
- if(parameters && parameters.server_object_mask)
427
- mask_value = parameters.server_object_mask.to_sl_object_mask.map { |mask_key| URI.encode(mask_key.to_s.strip) }.join(";")
428
- query_string = "objectMask=#{mask_value}"
429
- end
430
-
431
- if (parameters && parameters.server_result_limit)
432
- resultLimit = parameters.server_result_limit
433
- resultOffset = parameters.server_result_offset
434
- resultOffset = 0 if resultOffset.nil?
435
- limit_string = "resultLimit=#{resultOffset},#{resultLimit}"
436
- if query_string.nil?
437
- query_string = limit_string
438
- else
439
- query_string << "&#{limit_string}"
440
- end
441
- end
442
-
443
- uri.query = query_string
444
-
445
- return uri
446
- end
447
-
448
- # Change the username. The username cannot be nil or the empty string.
449
- def username= (name)
450
- raise SoftLayerAPIException.new("Please provide a username") if name.nil? || name.empty?
451
- @username = name.strip
452
- end
453
-
454
- # Change the api_key. It cannot be nil or the empty string.
455
- def api_key= (new_key)
456
- raise SoftLayerAPIException.new("Please provide an api_key") if new_key.nil? || new_key.empty?
457
- @api_key = new_key.strip
458
- end
459
-
460
- # Change the endpoint_url. It cannot be nil or the empty string.
461
- def endpoint_url= (new_url)
462
- raise SoftLayerAPIException.new("The endpoint url cannot be nil or empty") if new_url.nil? || new_url.empty?
463
- @endpoint_url = new_url.strip
464
- end
465
- end # class Service
466
- end # module SoftLayer
467
-