deltacloud-client 0.1.1 → 0.3.0

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.
data/DISCLAIMER ADDED
@@ -0,0 +1,8 @@
1
+ Apache Deltacloud is an effort undergoing incubation at The Apache Software
2
+ Foundation (ASF), sponsored by the Incubator PMC. Incubation is required of
3
+ all newly accepted projects until a further review indicates that the
4
+ infrastructure, communications, and decision making process have stabilized
5
+ in a manner consistent with other successful ASF projects. While
6
+ incubation status is not necessarily a reflection of the completeness or
7
+ stability of the code, it does indicate that the project has yet to be
8
+ fully endorsed by the ASF.
File without changes
data/NOTICE ADDED
@@ -0,0 +1,8 @@
1
+ Apache Deltacloud
2
+ Copyright 2010 The Apache Software Foundation
3
+
4
+ This product includes software developed at The Apache Software Foundation
5
+ (http://www.apache.org/).
6
+
7
+ This product includes software developed by Red Hat,
8
+ Inc. (http://www.redhat.com/)
data/Rakefile CHANGED
@@ -17,6 +17,7 @@
17
17
  # under the License.
18
18
 
19
19
  require 'rake/gempackagetask'
20
+ require 'rake/testtask'
20
21
 
21
22
  load 'deltacloud-client.gemspec'
22
23
 
@@ -25,6 +26,7 @@ task 'documentation' do
25
26
  load 'lib/documentation.rb'
26
27
  end
27
28
 
29
+
28
30
  spec = Gem::Specification.load('deltacloud-client.gemspec')
29
31
  Rake::GemPackageTask.new(spec) do |pkg|
30
32
  pkg.need_tar = true
@@ -44,6 +46,14 @@ task 'fixtures' do
44
46
  FileUtils.cp_r( File.dirname( __FILE__ ) + '/specs/fixtures', File.dirname( __FILE__ ) + '/specs/data' )
45
47
  end
46
48
 
49
+ namespace :test do
50
+ Rake::TestTask.new(:cmd) do |t|
51
+ t.libs << "tests"
52
+ t.test_files = FileList['tests/cmd.rb']
53
+ t.verbose = true
54
+ end
55
+ end
56
+
47
57
  desc "Clean Fixtures"
48
58
  task 'fixtures:clean' do
49
59
  FileUtils.rm_rf( File.dirname( __FILE__ ) + '/specs/data' )
data/bin/deltacloudc CHANGED
@@ -38,31 +38,66 @@ deltacloudc collection operation [options]
38
38
  URL format:
39
39
  API_URL=http://[user]:[password]@[api_url][port][/uri]
40
40
 
41
+ Examples:
42
+
43
+ 1. To list collections for deltacloud api on port 3333 of server deltacloud.foo
44
+
45
+ deltacloudc -l -u http://user:password@deltacloud.foo:3333/api
46
+
47
+ 2. To list the operations for the 'images' collection:
48
+
49
+ deltacloudc images -l -u http://user:password@deltacloud.foo:3333/api
50
+
51
+ 3. To list all images (i.e. call the 'index' operation of 'images'):
52
+
53
+ deltacloudc images index -u http://user:password@deltacloud.foo:3333/api
54
+
55
+ 4. To get the details of image '5':
56
+
57
+ deltacloudc images show -i 5 -u http://user:password@deltacloud.foo:3333/api
58
+
41
59
  Options:
42
60
  BANNER
43
61
  opts.on( '-i', '--id ID', 'ID for operation') { |id| options[:id] = id }
44
62
  opts.on( '-d', '--image-id ID', 'Image ID') { |id| options[:image_id] = id }
63
+ opts.on( '-b', '--bucket-id ID', 'Bucket ID') {|id| options[:bucket_id] = id }
45
64
  opts.on( '-a', '--arch ARCH', 'Architecture (x86, x86_64)') { |id| options[:architecture] = id }
46
65
  opts.on( '-p', '--hardware-profile HARDWARE_PROFILE', 'Hardware Profile') { |id| options[:hwp_id] = id }
47
66
  opts.on( '-n', '--name NAME', 'Name (for instance eg.)') { |name| options[:name] = name }
48
67
  opts.on( '-s', '--state STATE', 'Instance state (RUNNING, STOPPED)') { |state| options[:state] = state }
49
68
  opts.on( '-u', '--url URL', 'API url ($API_URL variable)') { |url| options[:api_url] = url }
50
69
  opts.on( '-l', '--list', 'List collections/operations') { |id| options[:list] = true }
51
- opts.on( '-h', '--help', 'Display this screen' ) { puts opts ; exit }
70
+ opts.on( '-h', '--help', 'Display this screen' ) { puts @optparse; Kernel.exit! }
52
71
  opts.on( '-v', '--version', 'Display API version' ) { options[:version]=true }
53
72
  opts.on( '-V', '--verbose', 'Print verbose messages' ) { options[:verbose]=true }
73
+ opts.on( '-f', '--file-path PATH', 'local path for new blob data') {|path| options[:file_path]=path }
74
+ opts.on( '-m', '--blob-metadata k1=v1, k2=v2', 'Comma seperated k=v pairs for blob metadata (for create operation)') do |meta|
75
+ blob_meta = {}
76
+ meta.gsub!(/ /,"")
77
+ meta.scan(/(\w+)=(\w+)/).map {|k,v| blob_meta[k] = v }
78
+ options[:blob_metadata] = blob_meta
79
+ end
54
80
  end
55
81
 
56
82
  def invalid_usage(error_msg='')
57
- puts "ERROR: #{error_msg}"
83
+ puts "\n ERROR: #{error_msg} \n\n"
84
+ puts @optparse
58
85
  exit(1)
59
86
  end
60
87
 
61
- @optparse.parse!
88
+ begin
89
+ @optparse.parse!
90
+ rescue Exception => e
91
+ invalid_usage(e.message)
92
+ end
62
93
 
63
94
  # First try to get API_URL from environment
64
95
  options[:api_url] = ENV['API_URL'] if options[:api_url].nil?
65
96
 
97
+ if(options[:api_url].nil?)
98
+ invalid_usage("You must supply the url to the deltacloud api; either use '-u' flag or set the 'API_URL' environment variable")
99
+ end
100
+
66
101
  url = URI.parse(options[:api_url])
67
102
  api_url = "http://#{url.host}#{url.port ? ":#{url.port}" : ''}#{url.path}"
68
103
 
@@ -75,6 +110,8 @@ collections = client.entry_points.keys
75
110
 
76
111
  # Exclude collection which don't have methods in client library yet
77
112
  collections.delete(:instance_states)
113
+ #add blob collection if buckets is present
114
+ collections << :blob if collections.include?(:buckets)
78
115
 
79
116
  # If list parameter passed print out available collection
80
117
  # with API documentation
@@ -88,6 +125,14 @@ end
88
125
  # If collection parameter is present and user requested list
89
126
  # print all operation defined for collection with API documentation
90
127
  if options[:list] and options[:collection]
128
+ #deal with blobs again - bypass 'normal' docs procedure
129
+ if options[:collection] =~ /blob/i
130
+ puts "create \t\t: Create a new blob in a specified bucket (POST /api/buckets/:bucket)"
131
+ puts "destroy \t: Delete a specified blob in a specified bucket (DELETE /api/buckets/:bucket/:blob)"
132
+ puts "show \t\t: Get details of a specified blob in a specified bucket (GET /api/buckets/:bucket/:blob)"
133
+ puts "data \t\t: Get the contents of a specified blob - the blob data content itself"
134
+ exit(0)
135
+ end
91
136
  doc = client.documentation(options[:collection])
92
137
  doc.operations.each do |c|
93
138
  puts sprintf("%-20s: %s", c.operation, c.description)
@@ -104,6 +149,8 @@ end
104
149
  # Do same if 'index' operation is set
105
150
  if options[:collection] and ( options[:operation].nil? or options[:operation].eql?('index') )
106
151
  invalid_usage("Unknown collection: #{options[:collection]}") unless collections.include?(options[:collection].to_sym)
152
+ #cannot list blobs - can only show a specific blob
153
+ invalid_usage("You must specify a particular blob with -i and a particular bucket with -b") if options[:collection] =~ (/blob/i)
107
154
  params = {}
108
155
  params.merge!(:architecture => options[:architecture]) if options[:architecture]
109
156
  params.merge!(:id => options[:id]) if options[:id]
@@ -123,6 +170,27 @@ if options[:collection] and options[:operation]
123
170
 
124
171
  # If collection is set and requested operation is 'show' just 'singularize'
125
172
  # collection name and print item with specified id (-i parameter)
173
+ #Blobs are a special case so deal with first -
174
+ if options[:collection] =~ (/blob/i)
175
+ invalid_usage("Please specify the bucket for this blob using the -b option") unless options[:bucket_id]
176
+ invalid_usage("Missing blob ID, please specify with -i option") unless options[:id]
177
+ params = {}
178
+ params.merge!(:id => options[:id], 'bucket' => options[:bucket_id])
179
+ params.merge!('metadata'=>options[:blob_metadata]) unless options[:blob_metadata].nil?
180
+ case options[:operation]
181
+ when 'show' then puts format(client.send( options[:collection], params))
182
+ when 'data' then puts (client.blob_data(params))
183
+ when 'create' then
184
+ invalid_usage("Specify the location of the new blob data (full local path) using -f option") unless options[:file_path]
185
+ params.merge!('file_path'=>options[:file_path])
186
+ blob = client.create_blob(params)
187
+ puts format(blob)
188
+ when 'destroy' then client.destroy_blob(params)
189
+ else invalid_usage("Please specify a valid operation for the blob collection - try -l to see available operations")
190
+ end
191
+ exit(0)
192
+ end
193
+
126
194
  if options[:operation].eql?('show')
127
195
  invalid_usage("Missing ID, must be provided with --id") unless options[:id]
128
196
  puts format(client.send(options[:collection].gsub(/s$/, ''), options[:id]))
@@ -145,6 +213,21 @@ if options[:collection] and options[:operation]
145
213
  exit(0)
146
214
  end
147
215
 
216
+ #Create and Destroy a bucket - require the bucket id:
217
+ if options[:collection].eql?('buckets')
218
+ if options[:operation].eql?('create')
219
+ invalid_usage("Please specify an id for the new bucket with -i") unless options[:id]
220
+ bucket = client.create_bucket('id'=>options[:id])
221
+ puts format(bucket)
222
+ elsif options[:operation].eql?('destroy')
223
+ invalid_usage("Please specify the bucket you wish to destroy with -i") unless options[:id]
224
+ client.destroy_bucket('id'=>options[:id])
225
+ else
226
+ invalid_usage("Please specify a valid operation on buckets - use -l to see valid operations")
227
+ end
228
+ exit(0)
229
+ end
230
+
148
231
  # All other operations above collections is done there:
149
232
  if options[:collection].eql?('instances')
150
233
  instance = client.instance(options[:id])
data/lib/base_object.rb CHANGED
@@ -100,6 +100,7 @@ module DeltaCloud
100
100
  when :text then return m[:value]
101
101
  when :property then return m[:property]
102
102
  when :collection then return m[:values]
103
+ when :list then return m[:value].join(", ")
103
104
  else raise NoHandlerForMethod
104
105
  end
105
106
  end
@@ -108,7 +109,6 @@ module DeltaCloud
108
109
  # First of all search throught array for method name
109
110
  m = search_for_method(method_name)
110
111
  if m.nil?
111
- warn "[WARNING] Method unsupported by API: '#{self.class}.#{method_name}(#{args.inspect})'"
112
112
  return nil
113
113
  else
114
114
  # Call appropriate handler for method
@@ -116,6 +116,23 @@ module DeltaCloud
116
116
  end
117
117
  end
118
118
 
119
+ # This method adds blobs to the blob_list property
120
+ # of a bucket
121
+ def add_blob!(blob_name)
122
+ if @blob_list.nil?
123
+ @blob_list = [blob_name]
124
+ @objects << {
125
+ :type => :list,
126
+ :method_name => "blob_list",
127
+ :value => @blob_list
128
+ }
129
+ else
130
+ @blob_list << blob_name
131
+ current = search_for_method('blob_list')
132
+ current[:value] = @blob_list
133
+ end
134
+ end
135
+
119
136
  private
120
137
 
121
138
  def search_for_method(name)
@@ -225,12 +242,21 @@ module DeltaCloud
225
242
  end
226
243
 
227
244
  def action_trigger(action)
228
- # Refresh object state after action
245
+ # Refresh object state after action unless the object was destroyed
246
+ return if action.to_s == "destroy"
229
247
  @new_state_object = @client.send(self.base_name, self.id)
230
248
  @state = @new_state_object.state
231
249
  self.update_actions!
232
250
  end
233
251
 
252
+ def add_run_action!(id, link)
253
+ @objects << {
254
+ :method_name => 'run',
255
+ :type => :run,
256
+ :url => link,
257
+ }
258
+ end
259
+
234
260
  alias :action_method_handler :method_handler
235
261
 
236
262
  def method_handler(m, args=[])
@@ -239,6 +265,7 @@ module DeltaCloud
239
265
  rescue NoHandlerForMethod
240
266
  case m[:type]
241
267
  when :state then evaluate_state(m[:state], @state)
268
+ when :run then run_command(m[:url][:href], args)
242
269
  else raise NoHandlerForMethod
243
270
  end
244
271
  end
@@ -246,6 +273,22 @@ module DeltaCloud
246
273
 
247
274
  # private
248
275
 
276
+ def run_command(instance_url, args)
277
+ credentials = args[1]
278
+ params = {
279
+ :cmd => args[0],
280
+ :private_key => credentials[:pem] ? File.read(credentials[:pem]) : nil,
281
+ }
282
+ params.merge!({
283
+ :username => credentials[:username],
284
+ :password => credentials[:password]
285
+ }) if credentials[:username] and credentials[:password]
286
+ @client.request(:post, instance_url, {}, params) do |response|
287
+ output = Nokogiri::XML(response)
288
+ (output/'/instance/output').first.text
289
+ end
290
+ end
291
+
249
292
  def evaluate_state(method_state, current_state)
250
293
  method_state.eql?(current_state)
251
294
  end
@@ -0,0 +1,54 @@
1
+ module ClientBucketMethods
2
+
3
+ def create_bucket(params)
4
+ obj = nil
5
+ request(:post, "#{api_uri.to_s}/buckets", {:name => params['id'] }) do |response|
6
+ handle_backend_error(response) if response.code!=201
7
+ obj = base_object(:bucket, response)
8
+ end
9
+ end
10
+
11
+ def destroy_bucket(params)
12
+ #actually response here is 204 - no content - so nothing returned to client?
13
+ request(:delete, "#{api_uri.to_s}/buckets/#{params['id']}") do |response|
14
+ handle_backend_error(response) if response.code!=204
15
+ response
16
+ end
17
+ end
18
+
19
+ def create_blob(params)
20
+ blob = nil
21
+ resource = RestClient::Resource.new("#{api_uri.to_s}/buckets/#{params['bucket']}", :open_timeout => 10, :timeout => 45)
22
+ headers = default_headers.merge(extended_headers)
23
+ unless params['metadata'].nil?
24
+ metadata_headers = {}
25
+ params['metadata'].each do |k,v|
26
+ metadata_headers["X-Deltacloud-Blobmeta-#{k}"] = v
27
+ end
28
+ headers = headers.merge(metadata_headers)
29
+ end
30
+ resource.send(:post, {:blob_data => File.new(params['file_path'], 'rb'), :blob_id => params[:id]}, headers) do |response, request, block|
31
+ handle_backend_error(response) if response.code.eql?(500)
32
+ blob = base_object(:blob, response)
33
+ yield blob if block_given?
34
+ end
35
+ return blob
36
+ end
37
+
38
+ def destroy_blob(params)
39
+ request(:delete, "#{api_uri.to_s}/buckets/#{params['bucket']}/#{params[:id]}") do |response|
40
+ handle_backend_error(response) if response.code!=204
41
+ response
42
+ end
43
+ end
44
+
45
+ #RestClient doesn't do streaming 'get' yet - we already opened a pull request on this see
46
+ #https://github.com/archiloque/rest-client/issues/closed#issue/62 - apparently its going to
47
+ #be in the next version - unknown when. For now get full response. FIXME
48
+ def blob_data(params)
49
+ request(:get, "#{api_uri.to_s}/buckets/#{params['bucket']}/#{params[:id]}/content") do |response|
50
+ response
51
+ end
52
+ end
53
+
54
+ end
data/lib/deltacloud.rb CHANGED
@@ -20,11 +20,11 @@ require 'nokogiri'
20
20
  require 'rest_client'
21
21
  require 'base64'
22
22
  require 'logger'
23
-
24
23
  require 'hwp_properties'
25
24
  require 'instance_state'
26
25
  require 'documentation'
27
26
  require 'base_object'
27
+ require 'client_bucket_methods'
28
28
 
29
29
  module DeltaCloud
30
30
 
@@ -32,11 +32,15 @@ module DeltaCloud
32
32
  #
33
33
  # @param [String, user_name] API user name
34
34
  # @param [String, password] API password
35
- # @param [String, user_name] API URL (eg. http://localhost:3001/api)
35
+ # @param [String, url] API URL (eg. http://localhost:3001/api)
36
36
  # @return [DeltaCloud::API]
37
- def self.new(user_name, password, api_url, opts={}, &block)
38
- opts ||= {}
39
- API.new(user_name, password, api_url, opts, &block)
37
+ #def self.new(user_name, password, api_url, opts={}, &block)
38
+ # opts ||= {}
39
+ # API.new(user_name, password, api_url, opts, &block)
40
+ #end
41
+
42
+ def self.new(user_name, password, api_url, &block)
43
+ API.new(user_name, password, api_url, &block)
40
44
  end
41
45
 
42
46
  # Check given credentials if their are valid against
@@ -46,8 +50,8 @@ module DeltaCloud
46
50
  # @param [String, password] API password
47
51
  # @param [String, user_name] API URL (eg. http://localhost:3001/api)
48
52
  # @return [true|false]
49
- def self.valid_credentials?(user_name, password, api_url)
50
- api=API.new(user_name, password, api_url)
53
+ def self.valid_credentials?(user_name, password, api_url, opts={})
54
+ api=API.new(user_name, password, api_url, opts)
51
55
  result = false
52
56
  api.request(:get, '', :force_auth => '1') do |response|
53
57
  result = true if response.code.eql?(200)
@@ -74,9 +78,44 @@ module DeltaCloud
74
78
  @features, @entry_points = {}, {}
75
79
  @verbose = opts[:verbose] || false
76
80
  discover_entry_points
81
+ if entry_points.include?(:buckets)
82
+ extend(ClientBucketMethods)
83
+ end
77
84
  yield self if block_given?
78
85
  end
79
86
 
87
+ # This method can be used to switch back-end cloud
88
+ # for API instance using HTTP headers.
89
+ # Options must include:
90
+ # {
91
+ # :driver => 'rhevm|ec2|gogrid|...',
92
+ # :username => 'API key for backend',
93
+ # :password => 'API secret key for backend',
94
+ # }
95
+ # Optionally you can pass also :provider option to change
96
+ # provider entry-point
97
+ #
98
+ # Example usage:
99
+ # client = Deltacloud::new('url', 'username', 'password')
100
+ # ...
101
+ # client.with_config(:driver => 'ec2', :username => '', :password => '') do |ec2|
102
+ # ec2.realms
103
+ # end
104
+ #
105
+ # Note: After this block finish client instance will be set back to default
106
+ # state
107
+ #
108
+ # @param [Hash, opts] New provider configuration
109
+ def with_config(opts, &block)
110
+ api_instance = self.dup
111
+ api_instance.use_driver(opts[:driver],
112
+ :username => opts[:username],
113
+ :password => opts[:password],
114
+ :provider => opts[:provider])
115
+ yield api_instance if block_given?
116
+ api_instance
117
+ end
118
+
80
119
  def connect(&block)
81
120
  yield self
82
121
  end
@@ -93,7 +132,6 @@ module DeltaCloud
93
132
  # Define methods based on 'rel' attribute in entry point
94
133
  # Two methods are declared: 'images' and 'image'
95
134
  def declare_entry_points_methods(entry_points)
96
-
97
135
  API.instance_eval do
98
136
  entry_points.keys.select {|k| [:instance_states].include?(k)==false }.each do |model|
99
137
 
@@ -114,8 +152,20 @@ module DeltaCloud
114
152
  self.send(model.to_s.singularize.to_sym, $1)
115
153
  end
116
154
 
155
+ end
156
+
157
+ #define methods for blobs:
158
+ if(entry_points.include?(:buckets))
159
+ define_method :"blob" do |*args|
160
+ bucket = args[0]["bucket"]
161
+ blob = args[0][:id]
162
+ request(:get, "#{entry_points[:buckets]}/#{bucket}/#{blob}") do |response|
163
+ base_object("blob", response)
164
+ end
117
165
  end
118
166
  end
167
+
168
+ end
119
169
  end
120
170
 
121
171
  def base_object_collection(model, response)
@@ -144,13 +194,11 @@ module DeltaCloud
144
194
  params.merge!({ :initial_state => (item/'state').text.sanitize }) if (item/'state').length > 0
145
195
 
146
196
  obj = base_object.new(params)
147
-
148
197
  # Traverse across XML document and deal with elements
149
198
  item.xpath('./*').each do |attribute|
150
-
151
199
  # Do a link for elements which are links to other REST models
152
200
  if self.entry_points.keys.include?(:"#{attribute.name}s")
153
- obj.add_link!(attribute.name, attribute['id']) && next
201
+ obj.add_link!(attribute.name, attribute['id']) && next unless (attribute.name == 'bucket' && item.name == 'blob')
154
202
  end
155
203
 
156
204
  # Do a HWP property for hardware profile properties
@@ -165,25 +213,40 @@ module DeltaCloud
165
213
  # If there are actions, add they to ActionObject/StateFullObject
166
214
  if attribute.name == 'actions'
167
215
  (attribute/'link').each do |link|
216
+ (obj.add_run_action!(item['id'], link) && next) if link[:rel] == 'run'
168
217
  obj.add_action_link!(item['id'], link)
169
218
  end && next
170
219
  end
171
220
 
172
221
  if attribute.name == 'mount'
173
- obj.add_link!("instance", (attribute/"./instance/@id").first)
222
+ obj.add_link!("instance", (attribute/"./instance/@id").first.value)
174
223
  obj.add_text!("device", (attribute/"./device/@name").first.value)
175
224
  next
176
225
  end
177
226
 
227
+ #deal with blob metadata
228
+ if(attribute.name == 'user_metadata')
229
+ meta = {}
230
+ attribute.children.select {|x| x.name=="entry" }.each do |element|
231
+ value = element.content.gsub!(/(\n) +/,'')
232
+ meta[element['key']] = value
233
+ end
234
+ obj.add_collection!(attribute.name, meta.inspect) && next
235
+ end
236
+
178
237
  # Deal with collections like public-addresses, private-addresses
179
238
  if (attribute/'./*').length > 0
180
239
  obj.add_collection!(attribute.name, (attribute/'*').collect { |value| value.text }) && next
181
240
  end
182
241
 
242
+ #deal with blobs for buckets
243
+ if(attribute.name == 'blob')
244
+ obj.add_blob!(attribute.attributes['id'].value) && next
245
+ end
246
+
183
247
  # Anything else is treaten as text object
184
248
  obj.add_text!(attribute.name, attribute.text.convert)
185
249
  end
186
-
187
250
  return obj
188
251
  end
189
252
 
@@ -237,6 +300,9 @@ module DeltaCloud
237
300
 
238
301
  request(:post, entry_points[:"#{$1}s"], {}, params) do |response|
239
302
  obj = base_object(:"#{$1}", response)
303
+ # All create calls must respond 201 HTTP code
304
+ # to indicate that resource was created.
305
+ handle_backend_error(response) if response.code!=201
240
306
  yield obj if block_given?
241
307
  end
242
308
  return obj
@@ -245,7 +311,7 @@ module DeltaCloud
245
311
  end
246
312
 
247
313
  def use_driver(driver, opts={})
248
- if opts[:driver]
314
+ if driver
249
315
  @api_driver = driver
250
316
  @driver_name = driver
251
317
  discover_entry_points
@@ -256,6 +322,11 @@ module DeltaCloud
256
322
  return self
257
323
  end
258
324
 
325
+ def use_config!(opts={})
326
+ @api_uri = URI.parse(opts[:url]) if opts[:url]
327
+ use_driver(opts[:driver], opts)
328
+ end
329
+
259
330
  def extended_headers
260
331
  headers = {}
261
332
  headers["X-Deltacloud-Driver"] = @api_driver.to_s if @api_driver
@@ -270,14 +341,17 @@ module DeltaCloud
270
341
  :method => (args[0] || 'get').to_sym,
271
342
  :path => (args[1]=~/^http/) ? args[1] : "#{api_uri.to_s}#{args[1]}",
272
343
  :query_args => args[2] || {},
273
- :form_data => args[3] || {}
344
+ :form_data => args[3] || {},
345
+ :timeout => args[4] || 45,
346
+ :open_timeout => args[5] || 10
274
347
  }
275
348
  if conf[:query_args] != {}
276
349
  conf[:path] += '?' + URI.escape(conf[:query_args].collect{ |key, value| "#{key}=#{value}" }.join('&')).to_s
277
350
  end
278
351
 
279
352
  if conf[:method].eql?(:post)
280
- RestClient.send(:post, conf[:path], conf[:form_data], default_headers.merge(extended_headers)) do |response, request, block|
353
+ resource = RestClient::Resource.new(conf[:path], :open_timeout => conf[:open_timeout], :timeout => conf[:timeout])
354
+ resource.send(:post, conf[:form_data], default_headers.merge(extended_headers)) do |response, request, block|
281
355
  handle_backend_error(response) if response.code.eql?(500)
282
356
  if response.respond_to?('body')
283
357
  yield response.body if block_given?
@@ -286,7 +360,8 @@ module DeltaCloud
286
360
  end
287
361
  end
288
362
  else
289
- RestClient.send(conf[:method], conf[:path], default_headers.merge(extended_headers)) do |response, request, block|
363
+ resource = RestClient::Resource.new(conf[:path], :open_timeout => conf[:open_timeout], :timeout => conf[:timeout])
364
+ resource.send(conf[:method], default_headers.merge(extended_headers)) do |response, request, block|
290
365
  handle_backend_error(response) if response.code.eql?(500)
291
366
  if conf[:method].eql?(:get) and [301, 302, 307].include? response.code
292
367
  response.follow_redirection(request) do |response, request, block|
@@ -309,7 +384,7 @@ module DeltaCloud
309
384
 
310
385
  # Re-raise backend errors as on exception in client with message from
311
386
  # backend
312
- class BackendError < Exception
387
+ class BackendError < StandardError
313
388
  def initialize(opts={})
314
389
  @message = opts[:message]
315
390
  end
@@ -1,3 +1,20 @@
1
+ # Copyright (C) 2009, 2010 Red Hat, Inc.
2
+ #
3
+ # Licensed to the Apache Software Foundation (ASF) under one or more
4
+ # contributor license agreements. See the NOTICE file distributed with
5
+ # this work for additional information regarding copyright ownership. The
6
+ # ASF licenses this file to you under the Apache License, Version 2.0 (the
7
+ # "License"); you may not use this file except in compliance with the
8
+ # License. You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15
+ # License for the specific language governing permissions and limitations
16
+ # under the License.
17
+
1
18
  module DeltaCloud
2
19
  module InstanceState
3
20
 
@@ -1,3 +1,20 @@
1
+ # Copyright (C) 2009, 2010 Red Hat, Inc.
2
+ #
3
+ # Licensed to the Apache Software Foundation (ASF) under one or more
4
+ # contributor license agreements. See the NOTICE file distributed with
5
+ # this work for additional information regarding copyright ownership. The
6
+ # ASF licenses this file to you under the Apache License, Version 2.0 (the
7
+ # "License"); you may not use this file except in compliance with the
8
+ # License. You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15
+ # License for the specific language governing permissions and limitations
16
+ # under the License.
17
+
1
18
  module DeltaCloud
2
19
  module PlainFormatter
3
20
  module FormatObject
@@ -33,8 +50,11 @@ module DeltaCloud
33
50
 
34
51
  class HardwareProfile < Base
35
52
  def format
53
+ architecture = @obj.architecture ? @obj.architecture.value[0,6] : 'opaque'
54
+ memory = @obj.memory ? @obj.memory.value.to_s[0,10] : 'opaque'
55
+ storage = @obj.storage ? @obj.storage.value.to_s[0,10] : 'opaque'
36
56
  sprintf("%-15s | %-6s | %10s | %10s ", @obj.id[0, 15],
37
- @obj.architecture.value[0,6], @obj.memory.value.to_s[0,10], @obj.storage.value.to_s[0,10])
57
+ architecture , memory, storage)
38
58
  end
39
59
  end
40
60
 
@@ -74,10 +94,42 @@ module DeltaCloud
74
94
  end
75
95
  end
76
96
 
97
+ class Bucket < Base
98
+ def format
99
+ sprintf("%-s | %-s | %-s | %-s",
100
+ @obj.id,
101
+ @obj.name,
102
+ @obj.size ? @obj.size : "0",
103
+ @obj.blob_list ? @obj.blob_list : ""
104
+ )
105
+ end
106
+ end
107
+
108
+ class Blob < Base
109
+ def format
110
+ sprintf("%-s | %-s | %-d | %-s | %-s | %-s " ,
111
+ @obj.id,
112
+ @obj.bucket,
113
+ @obj.content_length,
114
+ @obj.content_type,
115
+ @obj.last_modified,
116
+ @obj.user_metadata
117
+ )
118
+ end
119
+ end
120
+
121
+ class Driver < Base
122
+ def format
123
+ sprintf("%-15s | %-15s | %-s",
124
+ @obj.id,
125
+ @obj.name,
126
+ @obj.url)
127
+ end
128
+ end
77
129
  end
78
130
 
79
131
  def format(obj)
80
- object_name = obj.class.name.classify.gsub(/^DeltaCloud::API::/, '')
132
+ object_name = obj.class.name.classify.gsub(/^DeltaCloud::API::(\w+)::/, '')
81
133
  format_class = DeltaCloud::PlainFormatter::FormatObject.const_get(object_name)
82
134
  format_class.new(obj).format
83
135
  end
@@ -2,5 +2,6 @@
2
2
  :created: Thu Jul 30 14:35:11 UTC 2009
3
3
  :state: AVAILABLE
4
4
  :capacity: 1
5
+ :realm_id: us
5
6
  :device:
6
7
  :instance_id:
@@ -3,4 +3,5 @@
3
3
  :state: AVAILABLE
4
4
  :capacity: 1
5
5
  :device:
6
+ :realm_id: us
6
7
  :instance_id:
@@ -2,5 +2,6 @@
2
2
  :created: Thu Jul 30 14:35:11 UTC 2009
3
3
  :state: IN-USE
4
4
  :capacity: 1
5
+ :realm_id: us
5
6
  :device: /dev/sda1
6
7
  :instance_id: inst1
@@ -2,5 +2,6 @@
2
2
  :created: Thu Jul 30 14:35:11 UTC 2009
3
3
  :state: AVAILABLE
4
4
  :capacity: 1
5
+ :realm_id: us
5
6
  :device:
6
7
  :instance_id:
@@ -3,4 +3,5 @@
3
3
  :state: AVAILABLE
4
4
  :capacity: 1
5
5
  :device:
6
+ :realm_id: us
6
7
  :instance_id:
@@ -2,5 +2,6 @@
2
2
  :created: Thu Jul 30 14:35:11 UTC 2009
3
3
  :state: IN-USE
4
4
  :capacity: 1
5
+ :realm_id: us
5
6
  :device: /dev/sda1
6
7
  :instance_id: inst1
@@ -190,5 +190,17 @@ describe "instances" do
190
190
  instance.state.should eql( "RUNNING" )
191
191
  end
192
192
  end
193
+
194
+ it "should not throw exception when destroying an instance" do
195
+ DeltaCloud.new( API_NAME, API_PASSWORD, API_URL ) do |client|
196
+ instance = client.create_instance( 'img1',
197
+ :name=>'TestDestroyInstance',
198
+ :hardware_profile => 'm1-xlarge' )
199
+ instance.stop!
200
+ lambda {
201
+ instance.destroy!
202
+ }.should_not raise_error
203
+ end
204
+ end
193
205
  end
194
206
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: deltacloud-client
3
3
  version: !ruby/object:Gem::Version
4
- hash: 25
5
- prerelease: false
4
+ hash: 19
5
+ prerelease:
6
6
  segments:
7
7
  - 0
8
- - 1
9
- - 1
10
- version: 0.1.1
8
+ - 3
9
+ - 0
10
+ version: 0.3.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Red Hat, Inc.
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-12-09 00:00:00 +01:00
18
+ date: 2011-05-05 00:00:00 +02:00
19
19
  default_executable: deltacloudc
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -73,53 +73,58 @@ executables:
73
73
  extensions: []
74
74
 
75
75
  extra_rdoc_files:
76
- - COPYING
76
+ - LICENSE
77
+ - NOTICE
78
+ - DISCLAIMER
77
79
  files:
78
80
  - Rakefile
79
- - lib/plain_formatter.rb
81
+ - lib/base_object.rb
82
+ - lib/client_bucket_methods.rb
83
+ - lib/deltacloud.rb
80
84
  - lib/documentation.rb
81
85
  - lib/hwp_properties.rb
82
86
  - lib/instance_state.rb
87
+ - lib/plain_formatter.rb
83
88
  - lib/string.rb
84
- - lib/base_object.rb
85
- - lib/deltacloud.rb
86
89
  - init.rb
87
90
  - bin/deltacloudc
88
- - COPYING
89
- - specs/fixtures/storage_volumes/vol1.yml
90
- - specs/fixtures/storage_volumes/vol3.yml
91
- - specs/fixtures/storage_volumes/vol2.yml
92
- - specs/fixtures/storage_snapshots/snap3.yml
93
- - specs/fixtures/storage_snapshots/snap1.yml
94
- - specs/fixtures/storage_snapshots/snap2.yml
91
+ - LICENSE
92
+ - NOTICE
93
+ - DISCLAIMER
94
+ - specs/data/images/img1.yml
95
+ - specs/data/images/img2.yml
96
+ - specs/data/images/img3.yml
97
+ - specs/data/instances/inst0.yml
98
+ - specs/data/instances/inst1.yml
99
+ - specs/data/instances/inst2.yml
100
+ - specs/data/storage_snapshots/snap1.yml
101
+ - specs/data/storage_snapshots/snap2.yml
102
+ - specs/data/storage_snapshots/snap3.yml
103
+ - specs/data/storage_volumes/vol1.yml
104
+ - specs/data/storage_volumes/vol2.yml
105
+ - specs/data/storage_volumes/vol3.yml
106
+ - specs/fixtures/images/img1.yml
95
107
  - specs/fixtures/images/img2.yml
96
108
  - specs/fixtures/images/img3.yml
97
- - specs/fixtures/images/img1.yml
109
+ - specs/fixtures/instances/inst0.yml
98
110
  - specs/fixtures/instances/inst1.yml
99
111
  - specs/fixtures/instances/inst2.yml
100
- - specs/fixtures/instances/inst0.yml
101
- - specs/shared/resources.rb
112
+ - specs/fixtures/storage_snapshots/snap1.yml
113
+ - specs/fixtures/storage_snapshots/snap2.yml
114
+ - specs/fixtures/storage_snapshots/snap3.yml
115
+ - specs/fixtures/storage_volumes/vol1.yml
116
+ - specs/fixtures/storage_volumes/vol2.yml
117
+ - specs/fixtures/storage_volumes/vol3.yml
118
+ - specs/hardware_profiles_spec.rb
102
119
  - specs/images_spec.rb
103
120
  - specs/initialization_spec.rb
104
121
  - specs/instance_states_spec.rb
122
+ - specs/instances_spec.rb
105
123
  - specs/realms_spec.rb
124
+ - specs/shared/resources.rb
125
+ - specs/spec_helper.rb
106
126
  - specs/storage_snapshot_spec.rb
107
- - specs/hardware_profiles_spec.rb
108
127
  - specs/storage_volume_spec.rb
109
- - specs/spec_helper.rb
110
- - specs/instances_spec.rb
111
- - specs/data/storage_volumes/vol1.yml
112
- - specs/data/storage_volumes/vol3.yml
113
- - specs/data/storage_volumes/vol2.yml
114
- - specs/data/storage_snapshots/snap3.yml
115
- - specs/data/storage_snapshots/snap1.yml
116
- - specs/data/storage_snapshots/snap2.yml
117
- - specs/data/images/img2.yml
118
- - specs/data/images/img3.yml
119
- - specs/data/images/img1.yml
120
- - specs/data/instances/inst1.yml
121
- - specs/data/instances/inst2.yml
122
- - specs/data/instances/inst0.yml
123
128
  has_rdoc: true
124
129
  homepage: http://www.deltacloud.org
125
130
  licenses: []
@@ -150,42 +155,42 @@ required_rubygems_version: !ruby/object:Gem::Requirement
150
155
  requirements: []
151
156
 
152
157
  rubyforge_project:
153
- rubygems_version: 1.3.7
158
+ rubygems_version: 1.6.1
154
159
  signing_key:
155
160
  specification_version: 3
156
161
  summary: Deltacloud REST Client
157
162
  test_files:
158
- - specs/fixtures/storage_volumes/vol1.yml
159
- - specs/fixtures/storage_volumes/vol3.yml
160
- - specs/fixtures/storage_volumes/vol2.yml
161
- - specs/fixtures/storage_snapshots/snap3.yml
162
- - specs/fixtures/storage_snapshots/snap1.yml
163
- - specs/fixtures/storage_snapshots/snap2.yml
163
+ - specs/data/images/img1.yml
164
+ - specs/data/images/img2.yml
165
+ - specs/data/images/img3.yml
166
+ - specs/data/instances/inst0.yml
167
+ - specs/data/instances/inst1.yml
168
+ - specs/data/instances/inst2.yml
169
+ - specs/data/storage_snapshots/snap1.yml
170
+ - specs/data/storage_snapshots/snap2.yml
171
+ - specs/data/storage_snapshots/snap3.yml
172
+ - specs/data/storage_volumes/vol1.yml
173
+ - specs/data/storage_volumes/vol2.yml
174
+ - specs/data/storage_volumes/vol3.yml
175
+ - specs/fixtures/images/img1.yml
164
176
  - specs/fixtures/images/img2.yml
165
177
  - specs/fixtures/images/img3.yml
166
- - specs/fixtures/images/img1.yml
178
+ - specs/fixtures/instances/inst0.yml
167
179
  - specs/fixtures/instances/inst1.yml
168
180
  - specs/fixtures/instances/inst2.yml
169
- - specs/fixtures/instances/inst0.yml
170
- - specs/shared/resources.rb
181
+ - specs/fixtures/storage_snapshots/snap1.yml
182
+ - specs/fixtures/storage_snapshots/snap2.yml
183
+ - specs/fixtures/storage_snapshots/snap3.yml
184
+ - specs/fixtures/storage_volumes/vol1.yml
185
+ - specs/fixtures/storage_volumes/vol2.yml
186
+ - specs/fixtures/storage_volumes/vol3.yml
187
+ - specs/hardware_profiles_spec.rb
171
188
  - specs/images_spec.rb
172
189
  - specs/initialization_spec.rb
173
190
  - specs/instance_states_spec.rb
191
+ - specs/instances_spec.rb
174
192
  - specs/realms_spec.rb
193
+ - specs/shared/resources.rb
194
+ - specs/spec_helper.rb
175
195
  - specs/storage_snapshot_spec.rb
176
- - specs/hardware_profiles_spec.rb
177
196
  - specs/storage_volume_spec.rb
178
- - specs/spec_helper.rb
179
- - specs/instances_spec.rb
180
- - specs/data/storage_volumes/vol1.yml
181
- - specs/data/storage_volumes/vol3.yml
182
- - specs/data/storage_volumes/vol2.yml
183
- - specs/data/storage_snapshots/snap3.yml
184
- - specs/data/storage_snapshots/snap1.yml
185
- - specs/data/storage_snapshots/snap2.yml
186
- - specs/data/images/img2.yml
187
- - specs/data/images/img3.yml
188
- - specs/data/images/img1.yml
189
- - specs/data/instances/inst1.yml
190
- - specs/data/instances/inst2.yml
191
- - specs/data/instances/inst0.yml