steamcannon-deltacloud-client 0.0.9.7.1-java

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 (42) hide show
  1. data/COPYING +176 -0
  2. data/Rakefile +61 -0
  3. data/bin/deltacloudc +158 -0
  4. data/init.rb +20 -0
  5. data/lib/deltacloud.rb +586 -0
  6. data/lib/documentation.rb +98 -0
  7. data/lib/plain_formatter.rb +86 -0
  8. data/specs/data/images/img1.yml +4 -0
  9. data/specs/data/images/img2.yml +4 -0
  10. data/specs/data/images/img3.yml +4 -0
  11. data/specs/data/instances/inst0.yml +16 -0
  12. data/specs/data/instances/inst1.yml +9 -0
  13. data/specs/data/instances/inst2.yml +9 -0
  14. data/specs/data/storage_snapshots/snap1.yml +4 -0
  15. data/specs/data/storage_snapshots/snap2.yml +4 -0
  16. data/specs/data/storage_snapshots/snap3.yml +4 -0
  17. data/specs/data/storage_volumes/vol1.yml +6 -0
  18. data/specs/data/storage_volumes/vol2.yml +6 -0
  19. data/specs/data/storage_volumes/vol3.yml +6 -0
  20. data/specs/fixtures/images/img1.yml +4 -0
  21. data/specs/fixtures/images/img2.yml +4 -0
  22. data/specs/fixtures/images/img3.yml +4 -0
  23. data/specs/fixtures/instances/inst0.yml +16 -0
  24. data/specs/fixtures/instances/inst1.yml +9 -0
  25. data/specs/fixtures/instances/inst2.yml +9 -0
  26. data/specs/fixtures/storage_snapshots/snap1.yml +4 -0
  27. data/specs/fixtures/storage_snapshots/snap2.yml +4 -0
  28. data/specs/fixtures/storage_snapshots/snap3.yml +4 -0
  29. data/specs/fixtures/storage_volumes/vol1.yml +6 -0
  30. data/specs/fixtures/storage_volumes/vol2.yml +6 -0
  31. data/specs/fixtures/storage_volumes/vol3.yml +6 -0
  32. data/specs/hardware_profiles_spec.rb +78 -0
  33. data/specs/images_spec.rb +105 -0
  34. data/specs/initialization_spec.rb +60 -0
  35. data/specs/instance_states_spec.rb +78 -0
  36. data/specs/instances_spec.rb +191 -0
  37. data/specs/realms_spec.rb +64 -0
  38. data/specs/shared/resources.rb +30 -0
  39. data/specs/spec_helper.rb +52 -0
  40. data/specs/storage_snapshot_spec.rb +77 -0
  41. data/specs/storage_volume_spec.rb +89 -0
  42. metadata +191 -0
data/lib/deltacloud.rb ADDED
@@ -0,0 +1,586 @@
1
+ #
2
+ # Copyright (C) 2009 Red Hat, Inc.
3
+ #
4
+ # Licensed to the Apache Software Foundation (ASF) under one or more
5
+ # contributor license agreements. See the NOTICE file distributed with
6
+ # this work for additional information regarding copyright ownership. The
7
+ # ASF licenses this file to you under the Apache License, Version 2.0 (the
8
+ # "License"); you may not use this file except in compliance with the
9
+ # License. You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16
+ # License for the specific language governing permissions and limitations
17
+ # under the License.
18
+
19
+ require 'nokogiri'
20
+ require 'rest_client'
21
+ require 'base64'
22
+ require 'logger'
23
+
24
+ module DeltaCloud
25
+
26
+ # Get a new API client instance
27
+ #
28
+ # @param [String, user_name] API user name
29
+ # @param [String, password] API password
30
+ # @param [String, user_name] API URL (eg. http://localhost:3001/api)
31
+ # @return [DeltaCloud::API]
32
+ def self.new(user_name, password, api_url, &block)
33
+ API.new(user_name, password, api_url, &block)
34
+ end
35
+
36
+ # Check given credentials if their are valid against
37
+ # backend cloud provider
38
+ #
39
+ # @param [String, user_name] API user name
40
+ # @param [String, password] API password
41
+ # @param [String, user_name] API URL (eg. http://localhost:3001/api)
42
+ # @return [true|false]
43
+ def self.valid_credentials?(user_name, password, api_url)
44
+ api=API.new(user_name, password, api_url)
45
+ result = false
46
+ api.request(:get, '', :force_auth => '1') do |response|
47
+ result = true if response.code.eql?(200)
48
+ end
49
+ return result
50
+ end
51
+
52
+ # Return a API driver for specified URL
53
+ #
54
+ # @param [String, url] API URL (eg. http://localhost:3001/api)
55
+ def self.driver_name(url)
56
+ API.new(nil, nil, url).driver_name
57
+ end
58
+
59
+ def self.define_class(name)
60
+ @defined_classes ||= []
61
+ if @defined_classes.include?(name)
62
+ self.module_eval("API::#{name}")
63
+ else
64
+ @defined_classes << name unless @defined_classes.include?(name)
65
+ API.const_set(name, Class.new)
66
+ end
67
+ end
68
+
69
+ def self.classes
70
+ @defined_classes || []
71
+ end
72
+
73
+ class API
74
+ attr_accessor :logger
75
+ attr_reader :api_uri, :driver_name, :api_version, :features, :entry_points
76
+
77
+ def initialize(user_name, password, api_url, opts={}, &block)
78
+ opts[:version] = true
79
+ @logger = opts[:verbose] ? Logger.new(STDERR) : []
80
+ @username, @password = user_name, password
81
+ @api_uri = URI.parse(api_url)
82
+ @features, @entry_points = {}, {}
83
+ @verbose = opts[:verbose] || false
84
+ discover_entry_points
85
+ yield self if block_given?
86
+ end
87
+
88
+ def connect(&block)
89
+ yield self
90
+ end
91
+
92
+ # Return API hostname
93
+ def api_host; @api_uri.host ; end
94
+
95
+ # Return API port
96
+ def api_port; @api_uri.port ; end
97
+
98
+ # Return API path
99
+ def api_path; @api_uri.path ; end
100
+
101
+ # Define methods based on 'rel' attribute in entry point
102
+ # Two methods are declared: 'images' and 'image'
103
+ def declare_entry_points_methods(entry_points)
104
+ logger = @logger
105
+ API.instance_eval do
106
+ entry_points.keys.select {|k| [:instance_states].include?(k)==false }.each do |model|
107
+ define_method model do |*args|
108
+ request(:get, "/#{model}", args.first) do |response|
109
+ # Define a new class based on model name
110
+ c = DeltaCloud.define_class("#{model.to_s.classify}")
111
+ # Create collection from index operation
112
+ base_object_collection(c, model, response)
113
+ end
114
+ end
115
+ logger << "[API] Added method #{model}\n"
116
+ define_method :"#{model.to_s.singularize}" do |*args|
117
+ request(:get, "/#{model}/#{args[0]}") do |response|
118
+ # Define a new class based on model name
119
+ c = DeltaCloud.define_class("#{model.to_s.classify}")
120
+ # Build class for returned object
121
+ base_object(c, model, response)
122
+ end
123
+ end
124
+ logger << "[API] Added method #{model.to_s.singularize}\n"
125
+ define_method :"fetch_#{model.to_s.singularize}" do |url|
126
+ id = url.grep(/\/#{model}\/(.*)$/)
127
+ self.send(model.to_s.singularize.to_sym, $1)
128
+ end
129
+ end
130
+ end
131
+ end
132
+
133
+ def base_object_collection(c, model, response)
134
+ collection = []
135
+ Nokogiri::XML(response).xpath("#{model}/#{model.to_s.singularize}").each do |item|
136
+ c.instance_eval do
137
+ attr_accessor :id
138
+ attr_accessor :uri
139
+ end
140
+ collection << xml_to_class(c, item)
141
+ end
142
+ return collection
143
+ end
144
+
145
+ # Add default attributes [id and href] to class
146
+ def base_object(c, model, response)
147
+ obj = nil
148
+ Nokogiri::XML(response).xpath("#{model.to_s.singularize}").each do |item|
149
+ c.instance_eval do
150
+ attr_accessor :id
151
+ attr_accessor :uri
152
+ end
153
+ obj = xml_to_class(c, item)
154
+ end
155
+ return obj
156
+ end
157
+
158
+ # Convert XML response to defined Ruby Class
159
+ def xml_to_class(c, item)
160
+ obj = c.new
161
+ # Set default attributes
162
+ obj.id = item['id']
163
+ api = self
164
+ c.instance_eval do
165
+ define_method :client do
166
+ api
167
+ end
168
+ end
169
+ obj.uri = item['href']
170
+ logger = @logger
171
+ logger << "[DC] Creating class #{obj.class.name}\n"
172
+ obj.instance_eval do
173
+ # Declare methods for all attributes in object
174
+ item.xpath('./*').each do |attribute|
175
+ # If attribute is a link to another object then
176
+ # create a method which request this object from API
177
+ if api.entry_points.keys.include?(:"#{attribute.name}s")
178
+ c.instance_eval do
179
+ define_method :"#{attribute.name.sanitize}" do
180
+ client.send(:"#{attribute.name}", attribute['id'] )
181
+ end
182
+ logger << "[DC] Added #{attribute.name} to class #{obj.class.name}\n"
183
+ end
184
+ else
185
+ # Define methods for other attributes
186
+ c.instance_eval do
187
+ case attribute.name
188
+ # When response cointains 'link' block, declare
189
+ # methods to call links inside. This is used for instance
190
+ # to dynamicaly create .stop!, .start! methods
191
+ when "actions":
192
+ actions = []
193
+ attribute.xpath('link').each do |link|
194
+ actions << [link['rel'], link[:href]]
195
+ define_method :"#{link['rel'].sanitize}!" do |*params|
196
+ client.request(:"#{link['method']}", link['href'], {}, params.first || {})
197
+ @current_state = client.send(:"#{item.name}", item['id']).state
198
+ obj.instance_eval do |o|
199
+ def state
200
+ @current_state
201
+ end
202
+ end
203
+ end
204
+ end
205
+ define_method :actions do
206
+ actions.collect { |a| a.first }
207
+ end
208
+ define_method :actions_urls do
209
+ urls = {}
210
+ actions.each { |a| urls[a.first] = a.last }
211
+ urls
212
+ end
213
+ # Property attribute is handled differently
214
+ when "property":
215
+ attr_accessor :"#{attribute['name'].sanitize}"
216
+ if attribute['value'] =~ /^(\d+)$/
217
+ obj.send(:"#{attribute['name'].sanitize}=",
218
+ DeltaCloud::HWP::FloatProperty.new(attribute, attribute['name']))
219
+ else
220
+ obj.send(:"#{attribute['name'].sanitize}=",
221
+ DeltaCloud::HWP::Property.new(attribute, attribute['name']))
222
+ end
223
+ # Public and private addresses are returned as Array
224
+ when "public_addresses", "private_addresses":
225
+ attr_accessor :"#{attribute.name.sanitize}"
226
+ obj.send(:"#{attribute.name.sanitize}=",
227
+ attribute.xpath('address').collect { |address| address.text })
228
+ # Value for other attributes are just returned using
229
+ # method with same name as attribute (eg. .owner_id, .state)
230
+ else
231
+ attr_accessor :"#{attribute.name.sanitize}"
232
+ obj.send(:"#{attribute.name.sanitize}=", attribute.text.convert)
233
+ logger << "[DC] Added method #{attribute.name}[#{attribute.text}] to #{obj.class.name}\n"
234
+ end
235
+ end
236
+ end
237
+ end
238
+ end
239
+ return obj
240
+ end
241
+
242
+ # Get /api and parse entry points
243
+ def discover_entry_points
244
+ return if discovered?
245
+ request(:get, @api_uri.to_s) do |response|
246
+ api_xml = Nokogiri::XML(response)
247
+ @driver_name = api_xml.xpath('/api').first['driver']
248
+ @api_version = api_xml.xpath('/api').first['version']
249
+ logger << "[API] Version #{@api_version}\n"
250
+ logger << "[API] Driver #{@driver_name}\n"
251
+ api_xml.css("api > link").each do |entry_point|
252
+ rel, href = entry_point['rel'].to_sym, entry_point['href']
253
+ @entry_points.store(rel, href)
254
+ logger << "[API] Entry point '#{rel}' added\n"
255
+ entry_point.css("feature").each do |feature|
256
+ @features[rel] ||= []
257
+ @features[rel] << feature['name'].to_sym
258
+ logger << "[API] Feature #{feature['name']} added to #{rel}\n"
259
+ end
260
+ end
261
+ end
262
+ declare_entry_points_methods(@entry_points)
263
+ end
264
+
265
+ def create_key(opts={}, &block)
266
+ params = { :name => opts[:name] }
267
+ key = nil
268
+ request(:post, entry_points[:keys], {}, params) do |response|
269
+ c = DeltaCloud.define_class("Key")
270
+ key = base_object(c, :key, response)
271
+ yield key if block_given?
272
+ end
273
+ return key
274
+ end
275
+
276
+ # Create a new instance, using image +image_id+. Possible optiosn are
277
+ #
278
+ # name - a user-defined name for the instance
279
+ # realm - a specific realm for placement of the instance
280
+ # hardware_profile - either a string giving the name of the
281
+ # hardware profile or a hash. The hash must have an
282
+ # entry +id+, giving the id of the hardware profile,
283
+ # and may contain additional names of properties,
284
+ # e.g. 'storage', to override entries in the
285
+ # hardware profile
286
+ def create_instance(image_id, opts={}, &block)
287
+ name = opts[:name]
288
+ realm_id = opts[:realm]
289
+ user_data = opts[:user_data]
290
+ key_name = opts[:key_name]
291
+
292
+ params = opts.dup
293
+ ( params[:realm_id] = realm_id ) if realm_id
294
+ ( params[:name] = name ) if name
295
+ ( params[:user_data] = user_data ) if user_data
296
+ ( params[:keyname] = user_data ) if key_name
297
+
298
+ if opts[:hardware_profile].is_a?(String)
299
+ params[:hwp_id] = opts[:hardware_profile]
300
+ elsif opts[:hardware_profile].is_a?(Hash)
301
+ params.delete(:hardware_profile)
302
+ opts[:hardware_profile].each do |k,v|
303
+ params[:"hwp_#{k}"] = v
304
+ end
305
+ end
306
+
307
+ params[:image_id] = image_id
308
+ instance = nil
309
+
310
+ request(:post, entry_points[:instances], {}, params) do |response|
311
+ c = DeltaCloud.define_class("Instance")
312
+ instance = base_object(c, :instance, response)
313
+ yield instance if block_given?
314
+ end
315
+
316
+ return instance
317
+ end
318
+
319
+
320
+ def create_storage_volume(opts={}, &block)
321
+ params = opts.dup
322
+ params[:realm_id] ||= params.delete(:realm)
323
+ storage_volume = nil
324
+
325
+ request(:post, entry_points[:storage_volumes], {}, params) do |response|
326
+ c = DeltaCloud.define_class("StorageVolume")
327
+ storage_volume = base_object(c, :storage_volume, response)
328
+ yield storage_volume if block_given?
329
+ end
330
+
331
+ storage_volume
332
+ end
333
+
334
+ # Basic request method
335
+ #
336
+ def request(*args, &block)
337
+ conf = {
338
+ :method => (args[0] || 'get').to_sym,
339
+ :path => (args[1]=~/^http/) ? args[1] : "#{api_uri.to_s}#{args[1]}",
340
+ :query_args => args[2] || {},
341
+ :form_data => args[3] || {}
342
+ }
343
+ if conf[:query_args] != {}
344
+ conf[:path] += '?' + URI.escape(conf[:query_args].collect{ |key, value| "#{key}=#{value}" }.join('&')).to_s
345
+ end
346
+ logger << "[#{conf[:method].to_s.upcase}] #{conf[:path]}\n"
347
+ if conf[:method].eql?(:post)
348
+ RestClient.send(:post, conf[:path], conf[:form_data], default_headers) do |response, request, block|
349
+ if response.respond_to?('body')
350
+ yield response.body if block_given?
351
+ else
352
+ yield response.to_s if block_given?
353
+ end
354
+ end
355
+ else
356
+ RestClient.send(conf[:method], conf[:path], default_headers) do |response, request, block|
357
+ if response.respond_to?('body')
358
+ yield response.body if block_given?
359
+ else
360
+ yield response.to_s if block_given?
361
+ end
362
+ end
363
+ end
364
+ end
365
+
366
+ # Check if specified collection have wanted feature
367
+ def feature?(collection, name)
368
+ @features.has_key?(collection) && @features[collection].include?(name)
369
+ end
370
+
371
+ # List available instance states and transitions between them
372
+ def instance_states
373
+ states = []
374
+ request(:get, entry_points[:instance_states]) do |response|
375
+ Nokogiri::XML(response).xpath('states/state').each do |state_el|
376
+ state = DeltaCloud::InstanceState::State.new(state_el['name'])
377
+ state_el.xpath('transition').each do |transition_el|
378
+ state.transitions << DeltaCloud::InstanceState::Transition.new(
379
+ transition_el['to'],
380
+ transition_el['action']
381
+ )
382
+ end
383
+ states << state
384
+ end
385
+ end
386
+ states
387
+ end
388
+
389
+ # Select instance state specified by name
390
+ def instance_state(name)
391
+ instance_states.select { |s| s.name.to_s.eql?(name.to_s) }.first
392
+ end
393
+
394
+ # Skip parsing /api when we already got entry points
395
+ def discovered?
396
+ true if @entry_points!={}
397
+ end
398
+
399
+ def documentation(collection, operation=nil)
400
+ data = {}
401
+ request(:get, "/docs/#{collection}") do |body|
402
+ document = Nokogiri::XML(body)
403
+ if operation
404
+ data[:operation] = operation
405
+ data[:description] = document.xpath('/docs/collection/operations/operation[@name = "'+operation+'"]/description').first.text.strip
406
+ return false unless data[:description]
407
+ data[:params] = []
408
+ (document/"/docs/collection/operations/operation[@name='#{operation}']/parameter").each do |param|
409
+ data[:params] << {
410
+ :name => param['name'],
411
+ :required => param['type'] == 'optional',
412
+ :type => (param/'class').text
413
+ }
414
+ end
415
+ else
416
+ data[:description] = (document/'/docs/collection/description').text
417
+ data[:collection] = collection
418
+ data[:operations] = (document/"/docs/collection/operations/operation").collect{ |o| o['name'] }
419
+ end
420
+ end
421
+ return Documentation.new(self, data)
422
+ end
423
+
424
+ private
425
+
426
+ def default_headers
427
+ # The linebreaks inserted every 60 characters in the Base64
428
+ # encoded header cause problems under JRuby
429
+ auth_header = "Basic "+Base64.encode64("#{@username}:#{@password}")
430
+ auth_header.gsub!("\n", "")
431
+ {
432
+ :authorization => auth_header,
433
+ :accept => "application/xml"
434
+ }
435
+ end
436
+
437
+ end
438
+
439
+ class Documentation
440
+
441
+ attr_reader :api, :description, :params, :collection_operations
442
+ attr_reader :collection, :operation
443
+
444
+ def initialize(api, opts={})
445
+ @description, @api = opts[:description], api
446
+ @params = parse_parameters(opts[:params]) if opts[:params]
447
+ @collection_operations = opts[:operations] if opts[:operations]
448
+ @collection = opts[:collection]
449
+ @operation = opts[:operation]
450
+ self
451
+ end
452
+
453
+ def operations
454
+ @collection_operations.collect { |o| api.documentation(@collection, o) }
455
+ end
456
+
457
+ class OperationParameter
458
+ attr_reader :name
459
+ attr_reader :type
460
+ attr_reader :required
461
+ attr_reader :description
462
+
463
+ def initialize(data)
464
+ @name, @type, @required, @description = data[:name], data[:type], data[:required], data[:description]
465
+ end
466
+
467
+ def to_comment
468
+ " # @param [#{@type}, #{@name}] #{@description}"
469
+ end
470
+
471
+ end
472
+
473
+ private
474
+
475
+ def parse_parameters(params)
476
+ params.collect { |p| OperationParameter.new(p) }
477
+ end
478
+
479
+ end
480
+
481
+ module InstanceState
482
+
483
+ class State
484
+ attr_reader :name
485
+ attr_reader :transitions
486
+
487
+ def initialize(name)
488
+ @name, @transitions = name, []
489
+ end
490
+ end
491
+
492
+ class Transition
493
+ attr_reader :to
494
+ attr_reader :action
495
+
496
+ def initialize(to, action)
497
+ @to = to
498
+ @action = action
499
+ end
500
+
501
+ def auto?
502
+ @action.nil?
503
+ end
504
+ end
505
+ end
506
+
507
+ module HWP
508
+
509
+ class Property
510
+ attr_reader :name, :unit, :value, :kind
511
+
512
+ def initialize(xml, name)
513
+ @name, @kind, @value, @unit = xml['name'], xml['kind'].to_sym, xml['value'], xml['unit']
514
+ declare_ranges(xml)
515
+ self
516
+ end
517
+
518
+ def present?
519
+ ! @value.nil?
520
+ end
521
+
522
+ private
523
+
524
+ def declare_ranges(xml)
525
+ case xml['kind']
526
+ when 'range':
527
+ self.class.instance_eval do
528
+ attr_reader :range
529
+ end
530
+ @range = { :from => xml.xpath('range').first['first'], :to => xml.xpath('range').first['last'] }
531
+ when 'enum':
532
+ self.class.instance_eval do
533
+ attr_reader :options
534
+ end
535
+ @options = xml.xpath('enum/entry').collect { |e| e['value'] }
536
+ end
537
+ end
538
+
539
+ end
540
+
541
+ # FloatProperty is like Property but return value is Float instead of String.
542
+ class FloatProperty < Property
543
+ def initialize(xml, name)
544
+ super(xml, name)
545
+ @value = @value.to_f if @value
546
+ end
547
+ end
548
+ end
549
+
550
+ end
551
+
552
+ class String
553
+
554
+ unless method_defined?(:classify)
555
+ # Create a class name from string
556
+ def classify
557
+ self.singularize.camelize
558
+ end
559
+ end
560
+
561
+ unless method_defined?(:camelize)
562
+ # Camelize converts strings to UpperCamelCase
563
+ def camelize
564
+ self.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
565
+ end
566
+ end
567
+
568
+ unless method_defined?(:singularize)
569
+ # Strip 's' character from end of string
570
+ def singularize
571
+ self.gsub(/s$/, '')
572
+ end
573
+ end
574
+
575
+ # Convert string to float if string value seems like Float
576
+ def convert
577
+ return self.to_f if self.strip =~ /^([\d\.]+$)/
578
+ self
579
+ end
580
+
581
+ # Simply converts whitespaces and - symbols to '_' which is safe for Ruby
582
+ def sanitize
583
+ self.gsub(/(\W+)/, '_')
584
+ end
585
+
586
+ end
@@ -0,0 +1,98 @@
1
+ require 'lib/deltacloud'
2
+
3
+ skip_methods = [ "id=", "uri=" ]
4
+
5
+ begin
6
+ @dc=DeltaCloud.new('mockuser', 'mockpassword', 'http://localhost:3001/api')
7
+ rescue
8
+ puts "Please make sure that Deltacloud API is running with Mock driver"
9
+ exit(1)
10
+ end
11
+
12
+ @dc.entry_points.keys.each do |ep|
13
+ @dc.send(ep)
14
+ end
15
+
16
+ class_list = DeltaCloud::classes.collect { |c| DeltaCloud::module_eval("::DeltaCloud::API::#{c}")}
17
+
18
+ def read_method_description(c, method)
19
+ if method =~ /es$/
20
+ " # Read #{c.downcase} collection from Deltacloud API"
21
+ else
22
+ case method
23
+ when "uri"
24
+ " # Return URI to API for this object"
25
+ when "action_urls"
26
+ " # Return available actions API URL"
27
+ when "client"
28
+ " # Return instance of API client"
29
+ else
30
+ " # Get #{method} attribute value from #{c.downcase}"
31
+ end
32
+ end
33
+ end
34
+
35
+ def read_parameters(c, method)
36
+ out = []
37
+ if method =~ /es$/
38
+ out << " # @param [String, #id] Filter by ID"
39
+ end
40
+ out.join("\n")
41
+ end
42
+
43
+ def read_return_value(c, method)
44
+ if method =~ /es$/
45
+ rt = "Array"
46
+ else
47
+ rt = "String"
48
+ end
49
+ " # @return [String] Value of #{method}"
50
+ end
51
+
52
+ out = []
53
+
54
+ class_list.each do |c|
55
+ class_name = "#{c}".gsub(/^DeltaCloud::/, '')
56
+ out << "module DeltaCloud"
57
+ out << " class API"
58
+ @dc.entry_points.keys.each do |ep|
59
+ out << "# Return #{ep.to_s.classify} object with given id\n"
60
+ out << "# "
61
+ out << "# #{@dc.documentation(ep.to_s).description.split("\n").join("\n# ")}"
62
+ out << "# @return [#{ep.to_s.classify}]"
63
+ out << "def #{ep.to_s.gsub(/s$/, '')}"
64
+ out << "end"
65
+ out << "# Return collection of #{ep.to_s.classify} objects"
66
+ out << "# "
67
+ out << "# #{@dc.documentation(ep.to_s).description.split("\n").join("\n# ")}"
68
+ @dc.documentation(ep.to_s, 'index').params.each do |p|
69
+ out << p.to_comment
70
+ end
71
+ out << "# @return [Array] [#{ep.to_s.classify}]"
72
+ out << "def #{ep}(opts={})"
73
+ out << "end"
74
+ end
75
+ out << " end"
76
+ out << " class #{class_name}"
77
+ c.instance_methods(false).each do |method|
78
+ next if skip_methods.include?(method)
79
+ params = read_parameters(class_name, method)
80
+ retval = read_return_value(class_name, method)
81
+ out << read_method_description(class_name, method)
82
+ out << params if params
83
+ out << retval if retval
84
+ out << " def #{method}"
85
+ out << " # This method was generated dynamically from API"
86
+ out << " end\n"
87
+ end
88
+ out << " end"
89
+ out << "end"
90
+ end
91
+
92
+ FileUtils.rm_r('doc') rescue nil
93
+ FileUtils.mkdir_p('doc')
94
+ File.open('doc/deltacloud.rb', 'w') do |f|
95
+ f.puts(out.join("\n"))
96
+ end
97
+ system("yardoc -m markdown --readme README --title 'Deltacloud Client Library' 'lib/*.rb' 'doc/deltacloud.rb' --verbose")
98
+ FileUtils.rm('doc/deltacloud.rb')