ravello-sdk 0.9 → 0.9.1

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.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/ravello-sdk.rb +230 -25
  3. metadata +2 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8e22bc14b505f0312ca1ec12dab8dc3ccaaebfce
4
- data.tar.gz: 4f0a9072a6547b515dc3afb55e1c2e7b19b02cf8
3
+ metadata.gz: 8a0e3fca67a534a87c88d788893a19010bb7d943
4
+ data.tar.gz: 1d3af88858452d9b64380a7be31ec77e72000fed
5
5
  SHA512:
6
- metadata.gz: e4f35dbecf51404a26d46851e67ec46a417940aeca29d6143ea6d2dc4900b74f89959c460e3d80bd5531f761603c58cbd4128fa56bc9581543d14dae62413b6d
7
- data.tar.gz: 55c6a7da642fe7d0f70136e28285e82d55cb7eea6073563ef43a6fce74ef4ad60b661c5b3d8156e57c8a2f75ac0d3e6448738ecf16226ea7dbf599d12b887573
6
+ metadata.gz: 5d40f979fe440eeaf9875c4bf109de76c11be8fa96a5bfe7d2d9d77e1480b0a3c146b01a551ac7fc49803476904712b04aef8f0d4950c48eea51ec5f9a8b79d5
7
+ data.tar.gz: 74331233fbfa7d3863c6029a461e992ee7a3924f0a47b29b8269745f52e2d5f7b202cec65881ee413ece5ee84d12b82a7cf86cba5b136ebaf67e9617d0441367
@@ -7,10 +7,13 @@ require "net/http"
7
7
 
8
8
  class Net::HTTPResponse
9
9
 
10
+ # The parsed entity, if any.
10
11
  def entity
11
12
  @entity if defined? @entity
12
13
  end
13
14
 
15
+ # Set the parsed entity.
16
+ # @private
14
17
  def entity=(value)
15
18
  @entity = value
16
19
  end
@@ -19,18 +22,22 @@ end
19
22
 
20
23
  module Ravello
21
24
 
25
+ # The default service endpoint used by the Client.
22
26
  DEFAULT_URL = "https://cloud.ravellosystems.com/services"
23
27
 
24
- @@prng = Random.new
25
-
28
+ # Return a deep copy of an object.
29
+ # @param o [Hash] The object to copy.
30
+ # @return [Hash] A deep copy of the object.
26
31
  def deep_copy(o)
27
32
  Marshal.load(Marshal.dump(o))
28
33
  end
29
34
 
35
+ # Generate a new random local ID.
30
36
  def random_luid
31
37
  @@prng.rand 1<<63
32
38
  end
33
39
 
40
+ # Like #update_luids, but replaces ID attributes in place.
34
41
  def update_luids!(o)
35
42
  if o.is_a? Hash
36
43
  o.each_pair do |key,value|
@@ -42,10 +49,21 @@ module Ravello
42
49
  o
43
50
  end
44
51
 
52
+ # Recursively replace all attributes called "id" in the supplied hash with a
53
+ # new random ID. This is useful when adding a VM or other object to an
54
+ # application's design. All objects in a design require a unique ID. The term
55
+ # "luid" means "local id".
56
+ #
57
+ # @param o [Hash] The object to
58
+ # return [Hash] A copy of the object with new id attributes.
45
59
  def update_luids(o)
46
60
  update_luids!(deep_copy o)
47
61
  end
48
62
 
63
+ # Return the consolidated state of an application.
64
+ # @return [String, Array<String>, nil] A single string if all VMs in the
65
+ # deployment are in the same state, or a list of strings if there are
66
+ # multiple states. In case there are no VMs in the deployment, return nil.
49
67
  def application_state(app)
50
68
  if !app.is_a? Hash
51
69
  raise ArgumentError, "expecting a Hash, got #{app}"
@@ -60,16 +78,65 @@ module Ravello
60
78
 
61
79
  module_function :deep_copy, :random_luid, :update_luids, :update_luids!
62
80
 
81
+ # A client for the Ravello API. The client is a thin layer on top of the
82
+ # RESTful API. The client manages a single HTTPS connection, and implements
83
+ # login and retry functionality.
84
+ #
85
+ # Some general comments on how the RESTful API is mapped:
86
+ #
87
+ # * The calls are named "<verb>_<noun>", for example, "get_keypair".
88
+ # * There is no client-side object model. The return value from any API call
89
+ # is simply the parsed JSON response.
90
+ # * Resources are returned as a Hash, containing the key/value pairs of
91
+ # the resource. It is common that resources contain nested hashes.
92
+ # * Lists of resources are returned as an Array of zero or more hashes. It is
93
+ # common that resources in an Array only contain a subset of all available
94
+ # attributes.
95
+ # * Resources are identifed by a numeric ID.
96
+ # * Methods that retrieve a single resource take this ID parameter as a
97
+ # parameter. As a special case, it is also allowed to pass in a hash with
98
+ # an "id" key. These methods return a Hash, or nil if the resource was not
99
+ # found.
100
+ # * Methods that retrieve multiple resources can be passed a filter. The
101
+ # filter consists of hash style arguments. Only resources that have the
102
+ # specified key/value pairs will included in the response.
103
+ # * Methods that create a resource return a represenation of the created
104
+ # resources. It is common that this represenation contains (a lot) more
105
+ # attributes than you passed in. The extra attributes are defaults filled
106
+ # in by the system.
107
+ # * API errors are HTTP response codes in the 4xx or 5xx range. All API
108
+ # errors are turned into exceptions, except for the 404 (Not Found) code
109
+ # for a "get" call. In this case, nil is returned.
110
+ # * In case a certain API call is not yet mapped as a method, a fallback
111
+ # #request method is available that allows you to invoke arbitrary
112
+ # requests.
113
+
63
114
  class Client
64
115
 
65
- attr_accessor :retries, :timeout
66
- attr_reader :url, :user_info
116
+ # The default network timeout.
117
+ attr_accessor :timeout
67
118
 
119
+ # The number of times to retry an API call before giving up. Only
120
+ # idempotent API calls are retried.
121
+ attr_accessor :retries
122
+
123
+ # The parsed URL used in the current connection.
124
+ attr_reader :url
125
+
126
+ # A hash containing user information. Available when the client is logged
127
+ # in.
128
+ attr_reader :user_info
129
+
130
+ # Create a new API client. The following options are supported:
131
+ # @param :username [String] The API user name.
132
+ # @param :password [String] The API password.
133
+ # @param :timeout [Integer] The network timeout.
134
+ # @param :retries [Integer] Times to retry a failed operation.
68
135
  def initialize(options={})
69
136
  @username = options[:username]
70
137
  @password = options[:password]
71
- @retries = options.fetch(:retries, 3)
72
138
  @timeout = options.fetch(:timeout, 60)
139
+ @retries = options.fetch(:retries, 3)
73
140
  @logger = Logger.new(STDERR)
74
141
  @logger.level = if $DEBUG then Logger::DEBUG \
75
142
  elsif $VERBOSE then Logger::INFO else Logger::WARN end
@@ -81,27 +148,29 @@ module Ravello
81
148
  set_url(options.fetch(:url, DEFAULT_URL))
82
149
  end
83
150
 
151
+ # @return [Boolean] Whether the client is connected.
84
152
  def connected?
85
153
  !@connection.nil?
86
154
  end
87
155
 
88
- def closed?
89
- @connection.nil?
90
- end
91
-
156
+ # @return [Boolean] Whether the client is logged in.
92
157
  def logged_in?
93
158
  !@cookies.nil?
94
159
  end
95
160
 
96
- def url=(url)
97
- set_url(url)
98
- end
99
-
161
+ # Connect to the API.
162
+ # @param url [String] The URL to connect to. If not provided, use the
163
+ # default service end point.
100
164
  def connect(url=nil)
101
165
  url = url unless url.nil?
102
166
  do_connect
103
167
  end
104
168
 
169
+ # Authenticate to the API. Authentication is required for most operations.
170
+ # @param username The API username. If not provided, use the username
171
+ # specified in the constructor.
172
+ # @param password The API password If not provided, use the password
173
+ # specified in the constructor.
105
174
  def login(username=nil, password=nil)
106
175
  raise "already logged in" if logged_in?
107
176
  @username = username unless username.nil?
@@ -110,22 +179,37 @@ module Ravello
110
179
  do_login
111
180
  end
112
181
 
182
+ # Logout. The connection is maintained.
113
183
  def logout
114
184
  do_logout if logged_in?
115
185
  end
116
186
 
187
+ # Close the connection to the API.
117
188
  def close
118
189
  @connection.finish if !@connection.nil?
119
190
  @connection = nil
120
191
  @cookies = nil
121
192
  end
122
193
 
194
+ # Issue a call to the API. This is a low-level method that you should only
195
+ # use in case the call you want is not mapped as a method.
196
+ # @param method [Symbol] The HTTP method, e.g. :GET or :POST.
197
+ # @param path [String] The path relative to the API root.
198
+ # @param entity [Hash] A JSON serializable hash that is specific to
199
+ # the method and path.
200
+ # @return [Hash,Array<Hash>] The parsed JSON response.
123
201
  def request(method, path, entity=nil)
124
202
  body = JSON.generate(entity) unless entity.nil?
125
203
  response = make_request(method, path, body)
126
204
  response.entity
127
205
  end
128
206
 
207
+ # Wait until a condition on the object evaluates to true.
208
+ # @param obj [Hash] The object to wait for. It must be a hash returned by
209
+ # this client that has a "href" attribute.
210
+ # @param timeout [Numeric] The number of seconds to wait.
211
+ # @param block [Block] A block that evaluates the condition. The block must
212
+ # return true if the condition holds, false otherwise.
129
213
  def wait_for(obj, timeout, &block)
130
214
  end_time = Time.now + timeout
131
215
  raise 'object requires a "href" key' if !obj.key? 'href'
@@ -137,142 +221,259 @@ module Ravello
137
221
  raise 'timeout' if end_time < Time.now
138
222
  end
139
223
 
224
+ # Get an application by its ID.
225
+ # @param id [Integer, Hash] The numeric ID of the application. This may
226
+ # also be a hash, in which case it must have a numeric "id" key.
227
+ # @return [Hash, nil] The application, or nil if the application does not
228
+ # exist.
140
229
  def get_application(id)
141
230
  if id.is_a? Hash then id = id['id'] end
142
231
  request(:GET, "/applications/#{id}")
143
232
  end
144
233
 
234
+ # Get a list of all applications.
235
+ # @param filter [Hash] A hash containing key/value pairs the application
236
+ # must have.
237
+ # @return [Array<Hash>] A list of applications.
145
238
  def get_applications(filter={})
146
239
  result = request(:GET, '/applications')
147
240
  result.select! do |app| match_filter(app, filter) end if !filter.empty?
148
241
  result
149
242
  end
150
243
 
244
+ # Create a new application.
245
+ # @param app [Hash] The application.
246
+ # @return [Hash] The application that was created.
151
247
  def create_application(app)
152
248
  request(:POST, '/applications', app)
153
249
  end
154
250
 
251
+ # Update an existing application
252
+ # @param app [Hash] The application to update.
253
+ # @return [Hash] The updated application.
155
254
  def update_application(app)
156
255
  request(:PUT, "/applications/#{app['id']}", app)
157
256
  end
158
257
 
258
+ # Delete an application
259
+ # @param id [Numeric,Hash] The ID of the application. This parameter may
260
+ # also be a Hash with a numeric "id" key.
261
+ # @return nil
159
262
  def delete_application(id)
160
263
  if id.is_a? Hash then id = id['id'] end
161
264
  request(:DELETE, "/applications/#{id}")
162
265
  end
163
266
 
267
+ # Publish an application
268
+ # @param id [Numeric, Hash] The ID of the application to publish. This
269
+ # parameter may also be a Hash with a numeric "id" key.
270
+ # @param req [Hash] The publish request.
271
+ # @return [Hash] The publish response.
164
272
  def publish_application(id, req={})
165
273
  if id.is_a? Hash then id = id['id'] end
166
274
  request(:POST, "/applications/#{id}/publish", req)
167
275
  end
168
276
 
277
+ # Start an application.
278
+ # @param id [Numeric, Hash] The ID of the application to start. This
279
+ # parameter may also be a Hash with a numeric "id" key.
280
+ # @return [Hash] The start response.
169
281
  def start_application(id)
170
282
  if id.is_a? Hash then id = id['id'] end
171
283
  request(:POST, "/applications/#{id}/start")
172
284
  end
173
285
 
286
+ # Stop an application.
287
+ # @param id [Numeric, Hash] The ID of the application to stop. This
288
+ # parameter may also be a Hash with a numeric "id" key.
289
+ # @return [Hash] The stop response.
174
290
  def stop_application(id)
175
291
  if id.is_a? Hash then id = id['id'] end
176
292
  request(:POST, "/applications/#{id}/stop")
177
293
  end
178
294
 
295
+ # Restart an application.
296
+ # @param id [Numeric, Hash] The ID of the application to restart. This
297
+ # parameter may also be a Hash with a numeric "id" key.
298
+ # @return [Hash] The restart response.
179
299
  def restart_application(id)
180
300
  if id.is_a? Hash then id = id['id'] end
181
301
  request(:POST, "/applications/#{id}/restart")
182
302
  end
183
303
 
304
+ # Publish application updates.
305
+ # @param id [Numeric, Hash] The ID of the application to publish updates
306
+ # for. This parameter may also be a Hash with a numeric "id" key.
307
+ # @return [Hash] The publish updates response.
184
308
  def publish_application_updates(id)
185
309
  if id.is_a? Hash then id = id['id'] end
186
310
  request(:POST, "/applications/#{id}/publishUpdates")
187
311
  end
188
312
 
313
+ # Set the application expiration time.
314
+ # @param id [Numeric, Hash] The ID of the application to set the
315
+ # expiration time of. This parameter may also be a Hash with a numeric
316
+ # "id" key.
317
+ # @param req [Hash] The set expiration time request.
318
+ # @return [Hash] The set expiration time response.
189
319
  def set_application_expiration(id, req)
190
320
  if id.is_a? Hash then id = id['id'] end
191
321
  request(:POST, "/applications/#{id}/setExpiration", req)
192
322
  end
193
323
 
324
+ # Start a virtual machine
325
+ # @param appid [Numeric, Hash] The ID of the application that contains the
326
+ # VM. This parameter may also be a Hash with an "id" key.
327
+ # @param vmid [Numeric, Hash] The ID of the VM to start. This parameter
328
+ # may also be a Hash with an "id" key.
329
+ # @return [Hash] The start VM response.
194
330
  def start_vm(appid, vmid)
195
331
  if appid.is_a? Hash then appid = appid['id'] end
196
332
  if vmid.is_a? Hash then vmid = vmid['id'] end
197
333
  request(:POST, "/applications/#{appid}/vms/#{vmid}/start")
198
334
  end
199
335
 
336
+ # Stop a virtual machine
337
+ # @param appid [Numeric, Hash] The ID of the application that contains the
338
+ # VM. This parameter may also be a Hash with an "id" key.
339
+ # @param vmid [Numeric, Hash] The ID of the VM to stop. This parameter
340
+ # may also be a Hash with an "id" key.
341
+ # @return [Hash] The stop VM response.
200
342
  def stop_vm(appid, vmid)
201
343
  if appid.is_a? Hash then appid = appid['id'] end
202
344
  if vmid.is_a? Hash then vmid = vmid['id'] end
203
345
  request(:POST, "/applications/#{appid}/vms/#{vmid}/stop")
204
346
  end
205
347
 
348
+ # Restart a virtual machine
349
+ # @param appid [Numeric, Hash] The ID of the application that contains the
350
+ # VM. This parameter may also be a Hash with an "id" key.
351
+ # @param vmid [Numeric, Hash] The ID of the VM to restart. This parameter
352
+ # may also be a Hash with an "id" key.
353
+ # @return [Hash] The restart VM response.
206
354
  def restart_vm(appid, vmid)
207
355
  if appid.is_a? Hash then appid = appid['id'] end
208
356
  if vmid.is_a? Hash then vmid = vmid['id'] end
209
357
  request(:POST, "/applications/#{appid}/vms/#{vmid}/restart")
210
358
  end
211
359
 
360
+ # Get a blueprint.
361
+ # @param id [Integer, Hash] The ID of the blueprint. This may also be a
362
+ # Hash, in which case it must have an "id" key.
363
+ # @return [Hash, nil] The blueprint as a Hash, or nil in case the blueprint
364
+ # does not exist.
212
365
  def get_blueprint(id)
213
366
  if id.is_a? Hash then id = id['id'] end
214
367
  request(:GET, "/blueprints/#{id}")
215
368
  end
216
369
 
370
+ # Get a list of all blueprints.
371
+ # @param filter [Hash] An optional filter. Only blueprints that have
372
+ # key/value pairs as specified in the filter will be returned.
373
+ # @return [Array<Hash>] A list of blueprints.
217
374
  def get_blueprints(filter={})
218
375
  result = request(:GET, '/blueprints')
219
376
  result.select! do |bp| match_filter(bp, filter) end if !filter.empty?
220
377
  result
221
378
  end
222
379
 
380
+ # Create a new blueprint.
381
+ # @param req [Hash] The blueprint creation request.
382
+ # @return [Hash] The blueprint that was created.
223
383
  def create_blueprint(req)
224
384
  request(:POST, '/blueprints', req)
225
385
  end
226
386
 
387
+ # Delete a blueprint.
388
+ # @param id [Integer, Hash] The ID of the blueprint to delete. This
389
+ # parameter may also be a Hash, in which case it must have an "id" key.
390
+ # @return nil
227
391
  def delete_blueprint(id)
228
392
  if id.is_a? Hash then id = id['id'] end
229
393
  request(:DELETE, "/blueprints/#{id}")
230
394
  end
231
395
 
396
+ # Get an image.
397
+ # @param id [Integer, Hash] The ID of the image. This may also be a
398
+ # Hash, in which case it must have an "id" key.
399
+ # @return [Hash, nil] The image as a Hash, or nil in case the image
400
+ # does not exist.
232
401
  def get_image(id)
233
402
  if id.is_a? Hash then id = id['id'] end
234
403
  request(:GET, "/images/#{id}")
235
404
  end
236
405
 
406
+ # Get a list of all images.
407
+ # @param filter [Hash] An optional filter. Only images that have
408
+ # key/value pairs as specified in the filter will be returned.
409
+ # @return [Array<Hash>] A list of images.
237
410
  def get_images(filter={})
238
411
  result = request(:GET, '/images')
239
412
  result.select! do |img| match_filter(img, filter) end if !filter.empty?
240
413
  result
241
414
  end
242
415
 
416
+ # Update an image.
417
+ # @param image [Hash] The image to update.
418
+ # @return [Hash] The updated image.
243
419
  def update_image(image)
244
420
  request(:PUT, "/images/#{image['id']}", image)
245
421
  end
246
422
 
423
+ # Delete an image.
424
+ # @param id [Numeric, Hash] The Id of the image to delete. This may also be
425
+ # a Hash, in which case it must have an "id" key.
426
+ # @return nil
247
427
  def delete_image(id)
248
428
  if id.is_a? Hash then id = id['id'] end
249
429
  request(:DELETE, "/images/#{id}")
250
430
  end
251
431
 
432
+ # Get a keypair.
433
+ # @param id [Integer, Hash] The ID of the keypair. This parameter may also
434
+ # be a Hash, in which case it must have an "id" key.
435
+ # @return [Hash, nil] The keypair as a Hash, or nil in case the keypair
436
+ # does not exist.
252
437
  def get_keypair(id)
253
438
  if id.is_a? Hash then id = id['id'] end
254
439
  request(:GET, "/keypairs/#{id}")
255
440
  end
256
441
 
442
+ # Get a list of all keypairs.
443
+ # @param filter [Hash] An optional filter. Only keypairs that have
444
+ # key/value pairs as specified in the filter will be returned.
445
+ # @return [Array<Hash>] A list of keypairs.
257
446
  def get_keypairs(filter={})
258
447
  result = request(:GET, '/keypairs')
259
448
  result.select! do |kp| match_filter(kp, filter) end if !filter.empty?
260
449
  result
261
450
  end
262
451
 
452
+ # Create a new keypair.
453
+ # @param keypair [Hash] The keypair to created.
454
+ # @return [Hash] The keypair that was created.
263
455
  def create_keypair(keypair)
264
456
  request(:POST, '/keypairs', keypair)
265
457
  end
266
458
 
459
+ # Update a keypair.
460
+ # @param keypair [Hash] The keypair to update.
461
+ # @return [Hash] The updated keypair.
267
462
  def update_keypair(keypair)
268
463
  request(:PUT, "/keypairs/#{keypair['id']}", keypair)
269
464
  end
270
465
 
466
+ # Delete a keypair.
467
+ # @param id The ID of the keypair to delete. This parameter may also be a
468
+ # Hash, in which case it must have an "id" key.
469
+ # @return nil
271
470
  def delete_keypair(id)
272
471
  if id.is_a? Hash then id = id['id'] end
273
472
  request(:DELETE, "/keypairs/#{id}")
274
473
  end
275
474
 
475
+ # Generate a new keyapair.
476
+ # @return [Hash] The newly generated keypair.
276
477
  def generate_keypair
277
478
  request(:POST, '/keypairs/generate')
278
479
  end
@@ -284,7 +485,7 @@ module Ravello
284
485
  private
285
486
 
286
487
  def set_url(url)
287
- raise 'cannot change URL when connected' if !closed?
488
+ raise 'cannot change URL when connected' if connected?
288
489
  parsed = URI.parse(url.chomp '/')
289
490
  raise ArgumentError, 'https is required' if parsed.scheme != "https"
290
491
  @url = parsed
@@ -409,19 +610,23 @@ module Ravello
409
610
  response
410
611
  end
411
612
 
412
- def match_filter(obj, filter)
413
- filter.each_pair do |fkey,fvalue|
414
- obval = obj[fkey.to_s]
415
- if obval.nil?
416
- return false
417
- elsif fvalue.is_a? Hash
418
- return false if !obval.is_a? Hash || !match_filter(obval, fvalue)
419
- elsif fvalue != obval
420
- return false
613
+ def match_filter(obj, filter)
614
+ filter.each_pair do |fkey,fvalue|
615
+ obval = obj[fkey.to_s]
616
+ if obval.nil?
617
+ return false
618
+ elsif fvalue.is_a? Hash
619
+ return false if !obval.is_a? Hash || !match_filter(obval, fvalue)
620
+ elsif fvalue != obval
621
+ return false
622
+ end
421
623
  end
624
+ true
422
625
  end
423
- true
424
626
  end
425
627
 
426
- end
628
+ private
629
+
630
+ @@prng = Random.new
631
+
427
632
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ravello-sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.9'
4
+ version: 0.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Geert Jansen
@@ -42,3 +42,4 @@ signing_key:
42
42
  specification_version: 4
43
43
  summary: A Ruby SDK for the Ravello Systems API
44
44
  test_files: []
45
+ has_rdoc: