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 +8 -0
- data/{COPYING → LICENSE} +0 -0
- data/NOTICE +8 -0
- data/Rakefile +10 -0
- data/bin/deltacloudc +86 -3
- data/lib/base_object.rb +45 -2
- data/lib/client_bucket_methods.rb +54 -0
- data/lib/deltacloud.rb +93 -18
- data/lib/instance_state.rb +17 -0
- data/lib/plain_formatter.rb +54 -2
- data/specs/data/storage_volumes/vol1.yml +1 -0
- data/specs/data/storage_volumes/vol2.yml +1 -0
- data/specs/data/storage_volumes/vol3.yml +1 -0
- data/specs/fixtures/storage_volumes/vol1.yml +1 -0
- data/specs/fixtures/storage_volumes/vol2.yml +1 -0
- data/specs/fixtures/storage_volumes/vol3.yml +1 -0
- data/specs/instances_spec.rb +12 -0
- metadata +65 -60
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.
|
data/{COPYING → LICENSE}
RENAMED
File without changes
|
data/NOTICE
ADDED
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
|
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
|
-
|
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,
|
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
|
-
|
39
|
-
|
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
|
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.
|
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.
|
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 <
|
387
|
+
class BackendError < StandardError
|
313
388
|
def initialize(opts={})
|
314
389
|
@message = opts[:message]
|
315
390
|
end
|
data/lib/instance_state.rb
CHANGED
@@ -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
|
|
data/lib/plain_formatter.rb
CHANGED
@@ -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
|
-
|
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
|
data/specs/instances_spec.rb
CHANGED
@@ -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:
|
5
|
-
prerelease:
|
4
|
+
hash: 19
|
5
|
+
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
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:
|
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
|
-
-
|
76
|
+
- LICENSE
|
77
|
+
- NOTICE
|
78
|
+
- DISCLAIMER
|
77
79
|
files:
|
78
80
|
- Rakefile
|
79
|
-
- lib/
|
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
|
-
-
|
89
|
-
-
|
90
|
-
-
|
91
|
-
- specs/
|
92
|
-
- specs/
|
93
|
-
- specs/
|
94
|
-
- specs/
|
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/
|
109
|
+
- specs/fixtures/instances/inst0.yml
|
98
110
|
- specs/fixtures/instances/inst1.yml
|
99
111
|
- specs/fixtures/instances/inst2.yml
|
100
|
-
- specs/fixtures/
|
101
|
-
- specs/
|
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.
|
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/
|
159
|
-
- specs/
|
160
|
-
- specs/
|
161
|
-
- specs/
|
162
|
-
- specs/
|
163
|
-
- specs/
|
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/
|
178
|
+
- specs/fixtures/instances/inst0.yml
|
167
179
|
- specs/fixtures/instances/inst1.yml
|
168
180
|
- specs/fixtures/instances/inst2.yml
|
169
|
-
- specs/fixtures/
|
170
|
-
- specs/
|
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
|