deltacloud-client 0.1.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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