occi-cli 4.0.0.alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +15 -0
- data/.rspec +1 -0
- data/.travis.yml +43 -0
- data/.yardopts +1 -0
- data/AUTHORS +9 -0
- data/Gemfile +12 -0
- data/LICENSE +13 -0
- data/README.md +211 -0
- data/Rakefile +29 -0
- data/bin/occi +351 -0
- data/config/warble.rb +151 -0
- data/doc/macosx.md +27 -0
- data/ext/mkrf_conf.rb +34 -0
- data/lib/occi/cli/helpers.rb +149 -0
- data/lib/occi/cli/occi_opts.rb +388 -0
- data/lib/occi/cli/resource_output_factory.rb +95 -0
- data/lib/occi/cli/templates/compute.erb +18 -0
- data/lib/occi/cli/templates/network.erb +9 -0
- data/lib/occi/cli/templates/os_tpl.erb +7 -0
- data/lib/occi/cli/templates/resource_tpl.erb +7 -0
- data/lib/occi/cli/templates/storage.erb +8 -0
- data/lib/occi/cli/version.rb +5 -0
- data/lib/occi-cli.rb +9 -0
- data/occi-cli.gemspec +35 -0
- data/spec/occi/cli/helpers_spec.rb +9 -0
- data/spec/occi/cli/occi_opts_spec.rb +57 -0
- data/spec/occi/cli/resource_output_factory_spec.rb +9 -0
- data/spec/spec_helper.rb +13 -0
- metadata +240 -0
@@ -0,0 +1,388 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'optparse'
|
3
|
+
require 'uri'
|
4
|
+
|
5
|
+
require 'occi/cli/resource_output_factory'
|
6
|
+
|
7
|
+
module Occi::Cli
|
8
|
+
|
9
|
+
class OcciOpts
|
10
|
+
|
11
|
+
AUTH_METHODS = [:x509, :basic, :digest, :none].freeze
|
12
|
+
MEDIA_TYPES = ["application/occi+json", "application/occi+xml", "text/plain,text/occi", "text/plain"].freeze
|
13
|
+
ACTIONS = [:list, :describe, :create, :delete, :trigger].freeze
|
14
|
+
LOG_OUTPUTS = [:stdout, :stderr].freeze
|
15
|
+
|
16
|
+
def self.parse(args, test_env = false)
|
17
|
+
|
18
|
+
@@quiet = test_env
|
19
|
+
|
20
|
+
options = OpenStruct.new
|
21
|
+
|
22
|
+
options.debug = false
|
23
|
+
options.verbose = false
|
24
|
+
|
25
|
+
options.log = {}
|
26
|
+
options.log[:out] = STDERR
|
27
|
+
options.log[:level] = Occi::Log::WARN
|
28
|
+
|
29
|
+
options.filter = nil
|
30
|
+
options.dump_model = false
|
31
|
+
|
32
|
+
options.interactive = false
|
33
|
+
|
34
|
+
options.endpoint = "https://localhost:3300/"
|
35
|
+
|
36
|
+
options.auth = {}
|
37
|
+
options.auth[:type] = "none"
|
38
|
+
options.auth[:user_cert] = ENV['HOME'] + "/.globus/usercred.pem"
|
39
|
+
options.auth[:ca_path] = "/etc/grid-security/certificates"
|
40
|
+
options.auth[:username] = "anonymous"
|
41
|
+
options.auth[:ca_file] = nil
|
42
|
+
options.auth[:voms] = nil
|
43
|
+
|
44
|
+
options.output_format = :plain
|
45
|
+
|
46
|
+
options.mixins = nil
|
47
|
+
options.links = nil
|
48
|
+
options.attributes = nil
|
49
|
+
options.context_vars = nil
|
50
|
+
|
51
|
+
# TODO: change media type back to occi+json after the rOCCI-server update
|
52
|
+
#options.media_type = "application/occi+json"
|
53
|
+
options.media_type = "text/plain,text/occi"
|
54
|
+
|
55
|
+
opts = OptionParser.new do |opts|
|
56
|
+
opts.banner = %{Usage: occi [OPTIONS]
|
57
|
+
|
58
|
+
Examples:
|
59
|
+
occi --interactive --endpoint https://localhost:3300/ --auth x509
|
60
|
+
|
61
|
+
occi --endpoint https://localhost:3300/ --action list --resource os_tpl --auth x509
|
62
|
+
|
63
|
+
occi --endpoint https://localhost:3300/ --action list --resource resource_tpl --auth x509
|
64
|
+
|
65
|
+
occi --endpoint https://localhost:3300/ --action describe --resource os_tpl#debian6 --auth x509
|
66
|
+
|
67
|
+
occi --endpoint https://localhost:3300/ --action create --resource compute --mixin os_tpl#debian6 --mixin resource_tpl#small --attributes title="My rOCCI VM" --auth x509
|
68
|
+
|
69
|
+
occi --endpoint https://localhost:3300/ --action delete --resource /compute/65sd4f654sf65g4-s5fg65sfg465sfg-sf65g46sf5g4sdfg --auth x509}
|
70
|
+
|
71
|
+
opts.separator ""
|
72
|
+
opts.separator "Options:"
|
73
|
+
|
74
|
+
opts.on("-i",
|
75
|
+
"--interactive",
|
76
|
+
"Run as an interactive client without additional arguments") do |interactive|
|
77
|
+
options.interactive = interactive
|
78
|
+
end
|
79
|
+
|
80
|
+
opts.on("-e",
|
81
|
+
"--endpoint URI",
|
82
|
+
String,
|
83
|
+
"OCCI server URI, defaults to '#{options.endpoint}'") do |endpoint|
|
84
|
+
options.endpoint = URI(endpoint).to_s
|
85
|
+
end
|
86
|
+
|
87
|
+
opts.on("-n",
|
88
|
+
"--auth METHOD",
|
89
|
+
AUTH_METHODS,
|
90
|
+
"Authentication method, defaults to '#{options.auth[:type]}'") do |auth|
|
91
|
+
options.auth[:type] = auth.to_s
|
92
|
+
end
|
93
|
+
|
94
|
+
opts.on("-u",
|
95
|
+
"--username USER",
|
96
|
+
String,
|
97
|
+
"Username for basic or digest authentication, defaults to '#{options.auth[:username]}'") do |username|
|
98
|
+
options.auth[:username] = username
|
99
|
+
end
|
100
|
+
|
101
|
+
opts.on("-p",
|
102
|
+
"--password PASSWORD",
|
103
|
+
String,
|
104
|
+
"Password for basic, digest and x509 authentication") do |password|
|
105
|
+
options.auth[:password] = password
|
106
|
+
options.auth[:user_cert_password] = password
|
107
|
+
end
|
108
|
+
|
109
|
+
opts.on("-c",
|
110
|
+
"--ca-path PATH",
|
111
|
+
String,
|
112
|
+
"Path to CA certificates directory, defaults to '#{options.auth[:ca_path]}'") do |ca_path|
|
113
|
+
raise ArgumentError, "Path specified in --ca-path is not a directory!" unless File.directory? ca_path
|
114
|
+
raise ArgumentError, "Path specified in --ca-path is not readable!" unless File.readable? ca_path
|
115
|
+
|
116
|
+
options.auth[:ca_path] = ca_path
|
117
|
+
end
|
118
|
+
|
119
|
+
opts.on("-f",
|
120
|
+
"--ca-file PATH",
|
121
|
+
String,
|
122
|
+
"Path to CA certificates in a file") do |ca_file|
|
123
|
+
raise ArgumentError, "File specified in --ca-file is not a file!" unless File.file? ca_file
|
124
|
+
raise ArgumentError, "File specified in --ca-file is not readable!" unless File.readable? ca_file
|
125
|
+
|
126
|
+
options.auth[:ca_file] = ca_file
|
127
|
+
end
|
128
|
+
|
129
|
+
opts.on("-F",
|
130
|
+
"--filter CATEGORY",
|
131
|
+
String,
|
132
|
+
"Category type identifier to filter categories from model, must be used together with the -m option") do |filter|
|
133
|
+
options.filter = filter
|
134
|
+
end
|
135
|
+
|
136
|
+
opts.on("-x",
|
137
|
+
"--user-cred FILE",
|
138
|
+
String,
|
139
|
+
"Path to user's x509 credentials, defaults to '#{options.auth[:user_cert]}'") do |user_cred|
|
140
|
+
raise ArgumentError, "File specified in --user-cred is not a file!" unless File.file? user_cred
|
141
|
+
raise ArgumentError, "File specified in --user-cred is not readable!" unless File.readable? user_cred
|
142
|
+
|
143
|
+
options.auth[:user_cert] = user_cred
|
144
|
+
end
|
145
|
+
|
146
|
+
opts.on("-X",
|
147
|
+
"--voms",
|
148
|
+
"Using VOMS credentials; modifies behavior of the X509 authN module") do |voms|
|
149
|
+
|
150
|
+
options.auth[:voms] = true
|
151
|
+
end
|
152
|
+
|
153
|
+
opts.on("-y",
|
154
|
+
"--media-type MEDIA_TYPE",
|
155
|
+
MEDIA_TYPES,
|
156
|
+
"Media type for client <-> server communication, defaults to '#{options.media_type}'") do |media_type|
|
157
|
+
options.media_type = media_type
|
158
|
+
end
|
159
|
+
|
160
|
+
opts.on("-r",
|
161
|
+
"--resource RESOURCE",
|
162
|
+
String,
|
163
|
+
"Resource to be queried (e.g. network, compute, storage etc.), required") do |resource|
|
164
|
+
options.resource = resource
|
165
|
+
end
|
166
|
+
|
167
|
+
opts.on("-t",
|
168
|
+
"--attributes ATTRS",
|
169
|
+
Array,
|
170
|
+
"Comma separated attributes for new resources such as title=\"Name\", required") do |attributes|
|
171
|
+
options.attributes = {}
|
172
|
+
|
173
|
+
attributes.each do |attribute|
|
174
|
+
ary = /^(.+?)=(.+)$/.match(attribute).to_a.drop 1
|
175
|
+
raise ArgumentError, "Attributes must always contain ATTR=VALUE pairs!" if ary.length != 2
|
176
|
+
|
177
|
+
options.attributes[ary[0].to_sym] = ary[1]
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
opts.on("-T",
|
182
|
+
"--context CTX_VARS",
|
183
|
+
Array,
|
184
|
+
"Comma separated context variables for new compute resources such as SSH_KEY=\"ssh-rsa dfsdf...adfdf== user@localhost\"") do |context|
|
185
|
+
options.context_vars = {}
|
186
|
+
|
187
|
+
context.each do |ctx|
|
188
|
+
ary = /^(.+?)=(.+)$/.match(ctx).to_a.drop 1
|
189
|
+
raise ArgumentError, "Context variables must always contain ATTR=VALUE pairs!" if ary.length != 2
|
190
|
+
|
191
|
+
options.context_vars[ary[0].to_sym] = ary[1]
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
opts.on("-a",
|
196
|
+
"--action ACTION",
|
197
|
+
ACTIONS,
|
198
|
+
"Action to be performed on the resource, required") do |action|
|
199
|
+
options.action = action
|
200
|
+
end
|
201
|
+
|
202
|
+
opts.on("-M",
|
203
|
+
"--mixin NAME",
|
204
|
+
String,
|
205
|
+
"Type and name of the mixin as TYPE#NAME (e.g. os_tpl#monitoring, resource_tpl#medium)") do |mixin|
|
206
|
+
parts = mixin.split("#")
|
207
|
+
|
208
|
+
raise "Unknown mixin format! Use TYPE#NAME!" unless parts.length == 2
|
209
|
+
|
210
|
+
options.mixins = {} if options.mixins.nil?
|
211
|
+
options.mixins[parts[0]] = [] if options.mixins[parts[0]].nil?
|
212
|
+
options.mixins[parts[0]] << parts[1]
|
213
|
+
end
|
214
|
+
|
215
|
+
opts.on("-j",
|
216
|
+
"--link URI",
|
217
|
+
String,
|
218
|
+
"Link specified resource to the resource being created, only for action CREATE and resource COMPUTE") do |link|
|
219
|
+
link_relative_path = URI(link).path
|
220
|
+
|
221
|
+
raise ArgumentError, "Specified link URI is not valid!" unless link_relative_path.start_with? '/'
|
222
|
+
|
223
|
+
options.links = [] if options.links.nil?
|
224
|
+
options.links << link_relative_path
|
225
|
+
end
|
226
|
+
|
227
|
+
opts.on("-g",
|
228
|
+
"--trigger-action TRIGGER",
|
229
|
+
String,
|
230
|
+
"Action to be triggered on the resource") do |trigger_action|
|
231
|
+
options.trigger_action = trigger_action
|
232
|
+
end
|
233
|
+
|
234
|
+
opts.on("-l",
|
235
|
+
"--log-to OUTPUT",
|
236
|
+
LOG_OUTPUTS,
|
237
|
+
"Log to the specified device, defaults to 'STDERR'") do |log_to|
|
238
|
+
options.log[:out] = STDOUT if log_to == :stdout or log_to == :STDOUT
|
239
|
+
end
|
240
|
+
|
241
|
+
opts.on("-o",
|
242
|
+
"--output-format FORMAT",
|
243
|
+
Occi::Cli::ResourceOutputFactory.allowed_formats,
|
244
|
+
"Output format, defaults to human-readable 'plain'") do |output_format|
|
245
|
+
options.output_format = output_format
|
246
|
+
end
|
247
|
+
|
248
|
+
opts.on_tail("-m",
|
249
|
+
"--dump-model",
|
250
|
+
"Contact the endpoint and dump its model, cannot be used with the interactive mode") do |dump_model|
|
251
|
+
options.dump_model = dump_model
|
252
|
+
end
|
253
|
+
|
254
|
+
opts.on_tail("-d",
|
255
|
+
"--debug",
|
256
|
+
"Enable debugging messages") do |debug|
|
257
|
+
options.debug = debug
|
258
|
+
options.log[:level] = Occi::Log::DEBUG
|
259
|
+
end
|
260
|
+
|
261
|
+
opts.on_tail("-b",
|
262
|
+
"--verbose",
|
263
|
+
"Be more verbose, less intrusive than debug mode") do |verbose|
|
264
|
+
options.verbose = verbose
|
265
|
+
options.log[:level] = Occi::Log::INFO unless options.log[:level] == Occi::Log::DEBUG
|
266
|
+
end
|
267
|
+
|
268
|
+
opts.on_tail("-h",
|
269
|
+
"--help",
|
270
|
+
"Show this message") do
|
271
|
+
if @@quiet
|
272
|
+
exit true
|
273
|
+
else
|
274
|
+
puts opts
|
275
|
+
exit! true
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
opts.on_tail("-v",
|
280
|
+
"--version",
|
281
|
+
"Show version") do
|
282
|
+
if @@quiet
|
283
|
+
exit true
|
284
|
+
else
|
285
|
+
puts Occi::Cli::VERSION
|
286
|
+
exit! true
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
begin
|
292
|
+
opts.parse!(args)
|
293
|
+
rescue Exception => ex
|
294
|
+
if @@quiet
|
295
|
+
exit false
|
296
|
+
else
|
297
|
+
puts ex.message.capitalize
|
298
|
+
puts opts
|
299
|
+
exit!
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
check_restrictions options, opts
|
304
|
+
|
305
|
+
options
|
306
|
+
end
|
307
|
+
|
308
|
+
private
|
309
|
+
|
310
|
+
def self.check_restrictions(options, opts)
|
311
|
+
if options.interactive && options.dump_model
|
312
|
+
if @@quiet
|
313
|
+
exit false
|
314
|
+
else
|
315
|
+
puts "You cannot use '--dump-model' and '--interactive' at the same time!"
|
316
|
+
puts opts
|
317
|
+
exit!
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
if !options.dump_model && options.filter
|
322
|
+
if @@quiet
|
323
|
+
exit false
|
324
|
+
else
|
325
|
+
puts "You cannot use '--filter' without '--dump-model'!"
|
326
|
+
puts opts
|
327
|
+
exit!
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
if options.voms && options.auth[:type] != "x509"
|
332
|
+
if @@quiet
|
333
|
+
exit false
|
334
|
+
else
|
335
|
+
puts "You cannot use '--voms' without '--auth x509'!"
|
336
|
+
puts opts
|
337
|
+
exit!
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
return if options.interactive || options.dump_model
|
342
|
+
|
343
|
+
mandatory = []
|
344
|
+
|
345
|
+
if options.action == :trigger
|
346
|
+
mandatory << :trigger_action
|
347
|
+
end
|
348
|
+
|
349
|
+
if options.action == :create
|
350
|
+
if !options.links.nil?
|
351
|
+
mandatory << :links
|
352
|
+
else
|
353
|
+
mandatory << :mixins
|
354
|
+
end
|
355
|
+
|
356
|
+
mandatory << :attributes
|
357
|
+
check_attrs = true
|
358
|
+
end
|
359
|
+
|
360
|
+
mandatory.concat [:resource, :action]
|
361
|
+
|
362
|
+
check_hash options, mandatory, opts
|
363
|
+
|
364
|
+
if check_attrs
|
365
|
+
mandatory = [:title]
|
366
|
+
check_hash options.attributes, mandatory, opts
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
def self.check_hash(hash, mandatory, opts)
|
371
|
+
if !hash.is_a? Hash
|
372
|
+
hash = hash.marshal_dump
|
373
|
+
end
|
374
|
+
|
375
|
+
missing = mandatory.select{ |param| hash[param].nil? }
|
376
|
+
if !missing.empty?
|
377
|
+
if @@quiet
|
378
|
+
exit false
|
379
|
+
else
|
380
|
+
puts "Missing required arguments: #{missing.join(', ')}"
|
381
|
+
puts opts
|
382
|
+
exit!
|
383
|
+
end
|
384
|
+
end
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'erb'
|
3
|
+
|
4
|
+
module Occi::Cli
|
5
|
+
|
6
|
+
class ResourceOutputFactory
|
7
|
+
|
8
|
+
@@allowed_formats = [:json, :plain].freeze
|
9
|
+
@@allowed_resource_types = [:compute, :storage, :network, :os_tpl, :resource_tpl].freeze
|
10
|
+
@@allowed_data_types = [:locations, :resources].freeze
|
11
|
+
|
12
|
+
attr_reader :output_format
|
13
|
+
|
14
|
+
def initialize(output_format = :plain)
|
15
|
+
raise "Unsupported output format!" unless @@allowed_formats.include? output_format
|
16
|
+
@output_format = output_format
|
17
|
+
end
|
18
|
+
|
19
|
+
def format(data, data_type, resource_type)
|
20
|
+
raise "Data has to be in an array!" unless data.is_a? Array
|
21
|
+
raise "Unsupported resource type! Got: #{resource_type.to_s}" unless @@allowed_resource_types.include? resource_type
|
22
|
+
raise "Unsupported data type! Got: #{data_type.to_s}" unless @@allowed_data_types.include? data_type
|
23
|
+
|
24
|
+
# validate incoming data
|
25
|
+
if data_type == :resources
|
26
|
+
data.each do |occi_collection|
|
27
|
+
raise "I need the resources to be in a Collection!" unless occi_collection.respond_to? :as_json
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# construct method name from data type and output format
|
32
|
+
method = (data_type.to_s + "_to_" + output_format.to_s).to_sym
|
33
|
+
|
34
|
+
ResourceOutputFactory.send method, data, resource_type
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.allowed_formats
|
38
|
+
@@allowed_formats
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.allowed_resource_types
|
42
|
+
@@allowed_resource_types
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.allowed_data_types
|
46
|
+
@@allowed_data_types
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.resources_to_json(occi_resources, resource_type)
|
50
|
+
# generate JSON document from an array of JSON objects
|
51
|
+
JSON.generate occi_resources
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.resources_to_plain(occi_resources, resource_type)
|
55
|
+
# using ERB templates for known resource and mixin types
|
56
|
+
file = File.expand_path("..", __FILE__) + '/templates/' + resource_type.to_s + ".erb"
|
57
|
+
template = ERB.new File.new(file).read
|
58
|
+
|
59
|
+
formatted_output = ""
|
60
|
+
|
61
|
+
occi_resources.each do |occi_resource|
|
62
|
+
json_resource = occi_resource.as_json
|
63
|
+
formatted_output << template.result(binding) unless json_resource.nil? || json_resource.empty?
|
64
|
+
end
|
65
|
+
|
66
|
+
formatted_output
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.locations_to_json(url_locations, resource_type)
|
70
|
+
# give all locatios a common key and convert to JSON
|
71
|
+
locations = { resource_type => [] }
|
72
|
+
|
73
|
+
url_locations.each do |location|
|
74
|
+
location = location.split("/").last if [:os_tpl, :resource_tpl].include? resource_type
|
75
|
+
locations[resource_type] << location
|
76
|
+
end
|
77
|
+
|
78
|
+
locations.to_json
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.locations_to_plain(url_locations, resource_type)
|
82
|
+
# just an attempt to make the array more readable
|
83
|
+
output = "\n" + resource_type.to_s.capitalize + " locations:\n"
|
84
|
+
|
85
|
+
url_locations.each do |location|
|
86
|
+
location = location.split("/").last if [:os_tpl, :resource_tpl].include? resource_type
|
87
|
+
output << "\t" << location << "\n"
|
88
|
+
end
|
89
|
+
|
90
|
+
output
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<%# We always get a JSON %>
|
2
|
+
COMPUTE:
|
3
|
+
ID: <%= json_resource.resources.first.attributes.occi.core.id %>
|
4
|
+
TITLE: <%= json_resource.resources.first.attributes.occi.core.title %>
|
5
|
+
STATE: <%= json_resource.resources.first.attributes.occi.compute.state %>
|
6
|
+
LINKS:
|
7
|
+
<% if json_resource.links %><% json_resource.links.each do |link| %>
|
8
|
+
LINK "<%= link.kind %>":
|
9
|
+
ID: <%= link.attributes.occi.core.id %>
|
10
|
+
TITLE: <%= link.attributes.occi.core.title %>
|
11
|
+
TARGET: <%= link.target %>
|
12
|
+
<% if link.attributes.occi.networkinterface %>
|
13
|
+
IP ADDRESS: <%= link.attributes.occi.networkinterface.address %>
|
14
|
+
MAC ADDRESS: <%= link.attributes.occi.networkinterface.mac %>
|
15
|
+
<% elsif link.attributes.occi.storagelink %>
|
16
|
+
MOUNT POINT: <%= link.attributes.occi.storagelink.deviceid %>
|
17
|
+
<% end %>
|
18
|
+
<% end %><% end %>
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<%# We always get a JSON %>
|
2
|
+
NETWORK:
|
3
|
+
ID: <%= json_resource.resources.first.attributes.occi.core.id %>
|
4
|
+
TITLE: <%= json_resource.resources.first.attributes.occi.core.title %>
|
5
|
+
STATE: <%= json_resource.resources.first.attributes.occi.network.state %>
|
6
|
+
ALLOCATION: <%= json_resource.resources.first.attributes.occi.network.allocation %>
|
7
|
+
ADDRESS: <%= json_resource.resources.first.attributes.occi.network.address %>
|
8
|
+
|
9
|
+
|
@@ -0,0 +1,8 @@
|
|
1
|
+
<%# We always get a JSON %>
|
2
|
+
STORAGE:
|
3
|
+
ID: <%= json_resource.resources.first.attributes.occi.core.id %>
|
4
|
+
TITLE: <%= json_resource.resources.first.attributes.occi.core.title %>
|
5
|
+
STATE: <%= json_resource.resources.first.attributes.occi.storage.state%>
|
6
|
+
DESCRIPTION: <%= json_resource.resources.first.attributes.occi.core.summary %>
|
7
|
+
|
8
|
+
|
data/lib/occi-cli.rb
ADDED
data/occi-cli.gemspec
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib/', __FILE__)
|
3
|
+
$:.unshift lib unless $:.include?(lib)
|
4
|
+
|
5
|
+
require 'occi/cli/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |gem|
|
8
|
+
gem.name = "occi-cli"
|
9
|
+
gem.version = Occi::Cli::VERSION
|
10
|
+
gem.authors = ["Florian Feldhaus","Piotr Kasprzak", "Boris Parak"]
|
11
|
+
gem.email = ["florian.feldhaus@gwdg.de", "piotr.kasprzak@gwdg.de", "xparak@mail.muni.cz"]
|
12
|
+
gem.description = %q{OCCI is a collection of classes to simplify the implementation of the Open Cloud Computing API in Ruby}
|
13
|
+
gem.summary = %q{OCCI toolkit}
|
14
|
+
gem.homepage = 'https://github.com/gwdg/rOCCI-cli'
|
15
|
+
|
16
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
|
+
gem.files = `git ls-files`.split("\n")
|
18
|
+
gem.test_files = `git ls-files -- {test,spec}/*`.split("\n")
|
19
|
+
gem.require_paths = ["lib"]
|
20
|
+
gem.extensions = 'ext/mkrf_conf.rb'
|
21
|
+
|
22
|
+
gem.add_dependency 'occi-api'
|
23
|
+
gem.add_dependency 'highline'
|
24
|
+
|
25
|
+
gem.add_development_dependency "rspec"
|
26
|
+
gem.add_development_dependency "rake"
|
27
|
+
gem.add_development_dependency "builder"
|
28
|
+
gem.add_development_dependency "simplecov"
|
29
|
+
gem.add_development_dependency "yard"
|
30
|
+
gem.add_development_dependency "yard-sinatra"
|
31
|
+
gem.add_development_dependency "yard-rspec"
|
32
|
+
gem.add_development_dependency "yard-cucumber"
|
33
|
+
|
34
|
+
gem.required_ruby_version = ">= 1.8.7"
|
35
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Occi
|
2
|
+
module Cli
|
3
|
+
describe OcciOpts do
|
4
|
+
|
5
|
+
it "terminates without arguments" do
|
6
|
+
expect{Occi::Cli::OcciOpts.parse [], true}.to raise_error()
|
7
|
+
end
|
8
|
+
|
9
|
+
it "terminates when it encounters an unknown argument" do
|
10
|
+
expect{Occi::Cli::OcciOpts.parse ["--non-existent", "fake"], true}.to raise_error()
|
11
|
+
end
|
12
|
+
|
13
|
+
it "parses minimal number of required arguments without errors" do
|
14
|
+
expect{Occi::Cli::OcciOpts.parse ["--resource", "compute", "--action", "list"], true}.not_to raise_error()
|
15
|
+
end
|
16
|
+
|
17
|
+
it "parses resource and action arguments without errors" do
|
18
|
+
begin
|
19
|
+
parsed = Occi::Cli::OcciOpts.parse ["--resource", "compute", "--action", "list"], true
|
20
|
+
rescue SystemExit
|
21
|
+
fail
|
22
|
+
end
|
23
|
+
|
24
|
+
parsed.should_not be_nil
|
25
|
+
|
26
|
+
parsed.resource.should_not be_nil
|
27
|
+
parsed.action.should_not be_nil
|
28
|
+
|
29
|
+
parsed.resource.should eq("compute")
|
30
|
+
parsed.action.should eq(:list)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "shows version"
|
34
|
+
|
35
|
+
it "shows help"
|
36
|
+
|
37
|
+
it "doesn't accept unsupported authN methods"
|
38
|
+
|
39
|
+
it "doesn't accept an invalid URI as an endpoint"
|
40
|
+
|
41
|
+
it "doesn't accept an invalid URI as a resource"
|
42
|
+
|
43
|
+
it "doesn't accept unsupported actions"
|
44
|
+
|
45
|
+
it "doesn't allow create actions without attributes present"
|
46
|
+
|
47
|
+
it "doesn't allow create actions without either link(s) or mixin(s)"
|
48
|
+
|
49
|
+
it "doesn't allow create actions without attribute 'title' present"
|
50
|
+
|
51
|
+
it "doesn't accept malformed mixins"
|
52
|
+
|
53
|
+
it "doesn't accept malformed attributes"
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'occi-cli'
|
3
|
+
|
4
|
+
# enable coverage reports
|
5
|
+
if ENV['COVERAGE']
|
6
|
+
require 'simplecov'
|
7
|
+
SimpleCov.start
|
8
|
+
end
|
9
|
+
|
10
|
+
RSpec.configure do |c|
|
11
|
+
# in RSpec 3 this will no longer be necessary.
|
12
|
+
c.treat_symbols_as_metadata_keys_with_true_values = true
|
13
|
+
end
|