occi-cli 4.2.0.beta.3 → 4.2.0.beta.6

Sign up to get free protection for your applications and to get access to all the features.
data/bin/occi CHANGED
@@ -17,6 +17,7 @@
17
17
  require 'rubygems'
18
18
  require 'pp'
19
19
  require 'openssl'
20
+ require 'highline/import'
20
21
 
21
22
  require 'occi-cli'
22
23
 
@@ -40,7 +41,7 @@ if options.auth[:password].nil? || options.auth[:user_cert_password].nil?
40
41
  Occi::Log.debug "Password is not set, asking for it now ..."
41
42
 
42
43
  options.auth[:user_cert_password] = ask("Enter a password: ") {
43
- |q| q.echo = false
44
+ |q| q.echo = '*'
44
45
  } unless options.auth[:type] == "none" || (options.auth[:voms] && options.auth[:type] == "x509")
45
46
 
46
47
  options.auth[:password] = options.auth[:user_cert_password]
@@ -139,14 +140,16 @@ begin
139
140
  helper_describe options, output
140
141
  when :create
141
142
  helper_create options, output
142
- when :delete
143
+ when :link
144
+ helper_link options, output
145
+ when :delete, :unlink
143
146
  helper_delete options, output
144
147
  when :trigger
145
148
  helper_trigger options, output
146
149
  when :refresh
147
150
  refresh
148
- when :skip
149
- Occi::Log.info "Skipping this action, probably not implemented yet!"
151
+ when :skip, :test, :dry_run
152
+ Occi::Log.info "Just a connection test ..."
150
153
  else
151
154
  raise "Unknown action [#{options.action}]!"
152
155
  end
@@ -9,4 +9,5 @@ extend Occi::Cli::Helpers::ListHelper
9
9
  extend Occi::Cli::Helpers::DescribeHelper
10
10
  extend Occi::Cli::Helpers::CreateHelper
11
11
  extend Occi::Cli::Helpers::DeleteHelper
12
- extend Occi::Cli::Helpers::TriggerHelper
12
+ extend Occi::Cli::Helpers::TriggerHelper
13
+ extend Occi::Cli::Helpers::LinkHelper
@@ -18,19 +18,16 @@ module Occi::Cli::Helpers::CreateHelper
18
18
  def helper_create_resource(options)
19
19
  Occi::Log.debug "#{options.resource.inspect} is a resource type."
20
20
 
21
- # TODO: implement the rest
22
- raise "Not yet implemented!" unless options.resource.include? "compute"
23
-
24
21
  res = resource(options.resource)
25
22
 
26
23
  Occi::Log.debug "Creating #{options.resource.inspect}: #{res.inspect}"
27
24
 
28
- helper_attach_mixins(options, res)
25
+ helper_create_attach_mixins(options, res)
29
26
 
30
27
  if res.kind_of? Occi::Infrastructure::Compute
31
- helper_attach_links(options, res)
28
+ helper_create_attach_links(options, res)
32
29
  # TODO: context vars are only attributes!
33
- helper_attach_context_vars(options, res)
30
+ helper_create_attach_context_vars(options, res)
34
31
  end
35
32
 
36
33
  options.attributes.names.each_pair do |attribute, value|
@@ -50,7 +47,7 @@ module Occi::Cli::Helpers::CreateHelper
50
47
  create res
51
48
  end
52
49
 
53
- def helper_attach_links(options, res)
50
+ def helper_create_attach_links(options, res)
54
51
  return unless options.links
55
52
  Occi::Log.debug "with links: #{options.links.inspect}"
56
53
 
@@ -71,7 +68,7 @@ module Occi::Cli::Helpers::CreateHelper
71
68
  end
72
69
  end
73
70
 
74
- def helper_attach_mixins(options, res)
71
+ def helper_create_attach_mixins(options, res)
75
72
  return unless options.mixins
76
73
  Occi::Log.debug "with mixins: #{options.mixins.inspect}"
77
74
 
@@ -86,7 +83,7 @@ module Occi::Cli::Helpers::CreateHelper
86
83
  end
87
84
  end
88
85
 
89
- def helper_attach_context_vars(options, res)
86
+ def helper_create_attach_context_vars(options, res)
90
87
  # TODO: find a better/universal way to do contextualization
91
88
  return unless options.context_vars
92
89
  Occi::Log.debug "with context variables: #{options.context_vars.inspect}"
@@ -7,7 +7,7 @@ module Occi::Cli::Helpers::DescribeHelper
7
7
  found = Occi::Core::Resources.new
8
8
  found.merge describe(options.resource)
9
9
  elsif mixin_types.include?(options.resource) || mixin_type_identifiers.include?(options.resource)
10
- Occi::Log.debug "#{options.resourcre.inspect} is a mixin type or type identifier."
10
+ Occi::Log.debug "#{options.resource.inspect} is a mixin type or type identifier."
11
11
 
12
12
  found = Occi::Core::Mixins.new
13
13
  found.merge mixins(options.resource)
@@ -0,0 +1,76 @@
1
+ module Occi::Cli::Helpers::LinkHelper
2
+
3
+ def helper_link(options, output = nil)
4
+ location = nil
5
+
6
+ unless options.resource.start_with?(options.endpoint) || options.resource.start_with?('/')
7
+ raise "Given resource is not a valid instance URL! #{options.resource.inspect}"
8
+ end
9
+
10
+ unless options.links.length == 1
11
+ raise "You can assign only one link at a time!"
12
+ end
13
+
14
+ link = sanitize_instance_link(options.links.first)
15
+ unless link.start_with?('/')
16
+ raise "Given link is not a valid instance URL! #{link.inspect}"
17
+ end
18
+
19
+ link_kind = helper_link_kind(options, link)
20
+ location = helper_link_create_link(options, link_kind, link)
21
+
22
+ return location if output.nil?
23
+
24
+ puts location
25
+ end
26
+
27
+ def helper_link_kind(options, link)
28
+ raise "No valid links given!" if link.blank?
29
+
30
+ case link
31
+ when /\/network\//
32
+ link_kind = model.get_by_id("http://schemas.ogf.org/occi/infrastructure#networkinterface")
33
+ raise "#{options.endpoint.inspect} does not support networkinterface links!" unless link_kind
34
+ when /\/storage\//
35
+ link_kind = model.get_by_id("http://schemas.ogf.org/occi/infrastructure#storagelink")
36
+ raise "#{options.endpoint.inspect} does not support storagelink links!" unless link_kind
37
+ else
38
+ raise "Unknown link target #{link.inspect}! Only network and storage targets are supported!"
39
+ end
40
+
41
+ link_kind
42
+ end
43
+
44
+ def helper_link_create_link(options, link_kind, link)
45
+ Occi::Log.debug "Linking #{link.inspect} to #{options.resource.inspect}"
46
+
47
+ link_instance = Occi::Core::Link.new(link_kind)
48
+ link_instance.source = sanitize_instance_link(options.resource)
49
+ link_instance.target = link
50
+
51
+ helper_link_attach_mixins(options.mixins, link_instance)
52
+
53
+ options.attributes.names.each_pair do |attribute, value|
54
+ link_instance.attributes[attribute.to_s] = value
55
+ end
56
+
57
+ create link_instance
58
+ end
59
+
60
+ def helper_link_attach_mixins(mixins, link)
61
+ return if mixins.blank?
62
+
63
+ Occi::Log.debug "with mixins: #{mixins.inspect}"
64
+
65
+ mixins.to_a.each do |mxn|
66
+ Occi::Log.debug "Adding mixin #{mxn.inspect} to #{link.inspect}"
67
+
68
+ mxn = model.get_by_id(mxn.type_identifier)
69
+ raise Occi::Cli::Errors::MixinLookupError,
70
+ "The specified mixin is not declared in the model! #{mxn.type_identifier.inspect}" if mxn.blank?
71
+
72
+ link.mixins << mxn
73
+ end
74
+ end
75
+
76
+ end
@@ -1,7 +1,9 @@
1
- require 'ostruct'
2
1
  require 'optparse'
3
2
  require 'uri'
4
- require 'base64'
3
+ require 'erb'
4
+
5
+ # load all parts of OcciOpts
6
+ Dir[File.join(File.dirname(__FILE__), 'occi_opts', '*.rb')].each { |file| require file.gsub('.rb', '') }
5
7
 
6
8
  module Occi::Cli
7
9
 
@@ -9,64 +11,21 @@ module Occi::Cli
9
11
 
10
12
  AUTH_METHODS = [:x509, :basic, :digest, :none].freeze
11
13
  MEDIA_TYPES = ["application/occi+json", "application/occi+xml", "text/plain,text/occi", "text/plain"].freeze
12
- ACTIONS = [:list, :describe, :create, :delete, :trigger].freeze
14
+ ACTIONS = [:list, :describe, :create, :delete, :trigger, :link, :unlink].freeze
13
15
  LOG_OUTPUTS = [:stdout, :stderr].freeze
14
- ALLOWED_CONTEXT_VARS = [:public_key, :user_data].freeze
15
16
  LOG_LEVELS = [:debug, :error, :fatal, :info, :unknown, :warn].freeze
16
17
 
17
- MIXIN_REGEXP = /^(https?:\/\/\S+?)#(\S+)$/
18
- CONTEXT_REGEXP = ATTR_REGEXP = /^(.+?)=(.+)$/
18
+ REQ_CREATE_ATTRS = ["occi.core.title"].freeze
19
19
 
20
20
  def self.parse(args, test_env = false)
21
21
 
22
22
  @@quiet = test_env
23
23
 
24
- options = OpenStruct.new
25
-
26
- options.debug = false
27
-
28
- options.log = {}
29
- options.log[:out] = STDERR
30
- options.log[:level] = Occi::Log::ERROR
31
-
32
- options.filter = nil
33
- options.dump_model = false
34
-
35
- options.endpoint = "https://localhost:3300/"
36
-
37
- options.auth = {}
38
- options.auth[:type] = "none"
39
- options.auth[:user_cert] = ENV['HOME'] + "/.globus/usercred.pem"
40
- options.auth[:ca_path] = "/etc/grid-security/certificates"
41
- options.auth[:username] = "anonymous"
42
- options.auth[:ca_file] = nil
43
- options.auth[:voms] = nil
44
-
45
- options.output_format = :plain
46
-
47
- options.mixins = Occi::Core::Mixins.new
48
- options.links = nil
49
- options.attributes = Occi::Core::Attributes.new
50
- options.context_vars = nil
51
-
52
- # TODO: change media type back to occi+json after the rOCCI-server update
53
- #options.media_type = "application/occi+json"
54
- options.media_type = "text/plain,text/occi"
24
+ options = Hashie::Mash.new
25
+ set_defaults(options)
55
26
 
56
27
  opts = OptionParser.new do |opts|
57
- opts.banner = %{Usage: occi [OPTIONS]
58
-
59
- Examples:
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 --attribute title="My rOCCI VM" --auth x509
68
-
69
- occi --endpoint https://localhost:3300/ --action delete --resource /compute/65sd4f654sf65g4-s5fg65sfg465sfg-sf65g46sf5g4sdfg --auth x509}
28
+ opts.banner = %{Usage: occi [OPTIONS]}
70
29
 
71
30
  opts.separator ""
72
31
  opts.separator "Options:"
@@ -81,33 +40,35 @@ occi --endpoint https://localhost:3300/ --action delete --resource /compute/65sd
81
40
  opts.on("-n",
82
41
  "--auth METHOD",
83
42
  AUTH_METHODS,
84
- "Authentication method, defaults to '#{options.auth[:type]}'") do |auth|
85
- options.auth[:type] = auth.to_s
43
+ "Authentication method, only: [#{AUTH_METHODS.join('|')}], defaults " \
44
+ "to '#{options.auth.type}'") do |auth|
45
+ options.auth.type = auth.to_s
86
46
  end
87
47
 
88
48
  opts.on("-u",
89
49
  "--username USER",
90
50
  String,
91
- "Username for basic or digest authentication, defaults to '#{options.auth[:username]}'") do |username|
92
- options.auth[:username] = username
51
+ "Username for basic or digest authentication, defaults to " \
52
+ "'#{options.auth.username}'") do |username|
53
+ options.auth.username = username
93
54
  end
94
55
 
95
56
  opts.on("-p",
96
57
  "--password PASSWORD",
97
58
  String,
98
59
  "Password for basic, digest and x509 authentication") do |password|
99
- options.auth[:password] = password
100
- options.auth[:user_cert_password] = password
60
+ options.auth.password = password
61
+ options.auth.user_cert_password = password
101
62
  end
102
63
 
103
64
  opts.on("-c",
104
65
  "--ca-path PATH",
105
66
  String,
106
- "Path to CA certificates directory, defaults to '#{options.auth[:ca_path]}'") do |ca_path|
67
+ "Path to CA certificates directory, defaults to '#{options.auth.ca_path}'") do |ca_path|
107
68
  raise ArgumentError, "Path specified in --ca-path is not a directory!" unless File.directory? ca_path
108
69
  raise ArgumentError, "Path specified in --ca-path is not readable!" unless File.readable? ca_path
109
70
 
110
- options.auth[:ca_path] = ca_path
71
+ options.auth.ca_path = ca_path
111
72
  end
112
73
 
113
74
  opts.on("-f",
@@ -117,116 +78,98 @@ occi --endpoint https://localhost:3300/ --action delete --resource /compute/65sd
117
78
  raise ArgumentError, "File specified in --ca-file is not a file!" unless File.file? ca_file
118
79
  raise ArgumentError, "File specified in --ca-file is not readable!" unless File.readable? ca_file
119
80
 
120
- options.auth[:ca_file] = ca_file
81
+ options.auth.ca_file = ca_file
121
82
  end
122
83
 
123
84
  opts.on("-F",
124
85
  "--filter CATEGORY",
125
86
  String,
126
- "Category type identifier to filter categories from model, must be used together with the -m option") do |filter|
87
+ "Category type identifier to filter categories from model, must " \
88
+ "be used together with the -m option") do |filter|
127
89
  options.filter = filter
128
90
  end
129
91
 
130
92
  opts.on("-x",
131
93
  "--user-cred FILE",
132
94
  String,
133
- "Path to user's x509 credentials, defaults to '#{options.auth[:user_cert]}'") do |user_cred|
95
+ "Path to user's x509 credentials, defaults to '#{options.auth.user_cert}'") do |user_cred|
134
96
  raise ArgumentError, "File specified in --user-cred is not a file!" unless File.file? user_cred
135
97
  raise ArgumentError, "File specified in --user-cred is not readable!" unless File.readable? user_cred
136
98
 
137
- options.auth[:user_cert] = user_cred
99
+ options.auth.user_cert = user_cred
138
100
  end
139
101
 
140
102
  opts.on("-X",
141
103
  "--voms",
142
104
  "Using VOMS credentials; modifies behavior of the X509 authN module") do |voms|
143
105
 
144
- options.auth[:voms] = true
106
+ options.auth.voms = true
145
107
  end
146
108
 
147
109
  opts.on("-y",
148
110
  "--media-type MEDIA_TYPE",
149
111
  MEDIA_TYPES,
150
- "Media type for client <-> server communication, defaults to '#{options.media_type}'") do |media_type|
112
+ "Media type for client <-> server communication, only: [#{MEDIA_TYPES.join('|')}], " \
113
+ "defaults to '#{options.media_type}'") do |media_type|
151
114
  options.media_type = media_type
152
115
  end
153
116
 
154
117
  opts.on("-r",
155
118
  "--resource RESOURCE",
156
119
  String,
157
- "Resource to be queried (e.g. network, compute, storage etc.), required") do |resource|
120
+ "Term, identifier or URI of a resource to be queried, required") do |resource|
158
121
  options.resource = resource
159
122
  end
160
123
 
161
124
  opts.on("-t",
162
125
  "--attribute ATTRS",
163
126
  Array,
164
- "Comma separated attributes for new resources such as occi.core.title=\"Name\", required") do |attributes|
127
+ "Comma separated attributes for new resource instances, mandatory: " \
128
+ "[#{REQ_CREATE_ATTRS.join(', ')}]") do |attributes|
165
129
  options.attributes ||= Occi::Core::Attributes.new
166
130
 
167
131
  attributes.each do |attribute|
168
- ary = ATTR_REGEXP.match(attribute).to_a.drop 1
169
- raise ArgumentError, "Attribute must always contain ATTR=VALUE pairs!" unless ary.length == 2
170
-
171
- ary[0] = "occi.core.#{ary[0]}" unless ary[0].include?('.')
172
- options.attributes[ary[0]] = ary[1]
132
+ key, value = Occi::Cli::OcciOpts::Helper.parse_attribute(attribute)
133
+ options.attributes[key] = value
173
134
  end
174
135
  end
175
136
 
176
137
  opts.on("-T",
177
138
  "--context CTX_VARS",
178
139
  Array,
179
- "Comma separated context variables for new compute resources such as public_key=\"ssh-rsa dfsdf...adfdf== user@localhost\"") do |context|
140
+ "Comma separated context variables for new 'compute' resource instances, " \
141
+ "only: [#{Occi::Cli::OcciOpts::Helper::ALLOWED_CONTEXT_VARS.join(', ')}]") do |context|
180
142
  options.context_vars ||= {}
181
143
 
182
144
  context.each do |ctx|
183
- ary = CONTEXT_REGEXP.match(ctx).to_a.drop 1
184
- raise ArgumentError, "Context variables must always contain ATTR=VALUE pairs!" unless ary.length == 2
185
-
186
- symbol = ary[0].to_sym
187
- if ALLOWED_CONTEXT_VARS.include?(symbol)
188
- context_data = ary[1]
189
- if context_data.gsub!(/^file:\/\//,'')
190
- raise 'File does not exist! #{context_data}' unless File.exist? context_data
191
- context_data = File.read(context_data)
192
- end
193
-
194
- if symbol == :user_data
195
- context_data = Base64.encode64(context_data).gsub("\n", '')
196
- end
197
-
198
- options.context_vars[symbol] = context_data.strip
199
- else
200
- Occi::Log.warn "Only #{ALLOWED_CONTEXT_VARS.join(', ')} context variables are supported! Skipping #{ary[0].to_s} ..."
201
- end
145
+ key, value = Occi::Cli::OcciOpts::Helper.parse_context_variable(ctx)
146
+ options.context_vars[key] = value
202
147
  end
203
148
  end
204
149
 
205
150
  opts.on("-a",
206
151
  "--action ACTION",
207
152
  ACTIONS,
208
- "Action to be performed on the resource, required") do |action|
153
+ "Action to be performed on a resource instance, required") do |action|
209
154
  options.action = action
210
155
  end
211
156
 
212
157
  opts.on("-M",
213
- "--mixin NAME",
158
+ "--mixin IDENTIFIER",
214
159
  Array,
215
- "Type and name of the mixin as SCHEME#NAME (e.g. http://localhost/os_tpl#monitoring, http://localhost/resource_tpl#medium)") do |mixins|
160
+ "Identifier of a mixin, formatted as SCHEME#TERM or SHORT_SCHEME#TERM") do |mixins|
216
161
  options.mixins ||= Occi::Core::Mixins.new
217
162
 
218
163
  mixins.each do |mixin|
219
- parts = MIXIN_REGEXP.match(mixin).to_a.drop(1)
220
- raise "Unknown mixin format #{mixin.inspect}! Use SCHEME#NAME!" unless parts.length == 2
221
-
222
- options.mixins << Occi::Core::Mixin.new("#{parts[0]}#", parts[1])
164
+ options.mixins << Occi::Cli::OcciOpts::Helper.parse_mixin(mixin)
223
165
  end
224
166
  end
225
167
 
226
168
  opts.on("-j",
227
169
  "--link URI",
228
170
  Array,
229
- "Link specified resource to the resource being created, only for action CREATE and resource COMPUTE") do |links|
171
+ "Link this resource to the resource being created, only for action " \
172
+ "'create' and resource 'compute'") do |links|
230
173
  options.links ||= []
231
174
 
232
175
  links.each do |link|
@@ -237,32 +180,49 @@ occi --endpoint https://localhost:3300/ --action delete --resource /compute/65sd
237
180
  end
238
181
 
239
182
  opts.on("-g",
240
- "--trigger-action TRIGGER",
183
+ "--trigger-action TRIGGER_ACTION",
241
184
  String,
242
- "Action to be triggered on the resource") do |trigger_action|
243
- options.trigger_action = trigger_action
185
+ "Action to be triggered on the resource, formatted as SCHEME#TERM or SHORT_SCHEME#TERM") do |trigger_action|
186
+ options.trigger_action = Occi::Cli::OcciOpts::Helper.parse_action(trigger_action)
244
187
  end
245
188
 
246
189
  opts.on("-l",
247
190
  "--log-to OUTPUT",
248
191
  LOG_OUTPUTS,
249
- "Log to the specified device, defaults to 'STDERR'") do |log_to|
250
- options.log[:out] = STDOUT if log_to == :stdout or log_to == :STDOUT
192
+ "Log to the specified device, only: [#{LOG_OUTPUTS.join('|')}], defaults to 'stderr'") do |log_to|
193
+ options.log.out = STDOUT if log_to.to_s == "stdout"
251
194
  end
252
195
 
253
196
  opts.on("-o",
254
197
  "--output-format FORMAT",
255
198
  Occi::Cli::ResourceOutputFactory.allowed_formats,
256
- "Output format, defaults to human-readable 'plain'") do |output_format|
199
+ "Output format, only: [#{Occi::Cli::ResourceOutputFactory.allowed_formats.join('|')}], " \
200
+ "defaults to '#{options.output_format}'") do |output_format|
257
201
  options.output_format = output_format
258
202
  end
259
203
 
260
204
  opts.on("-b",
261
205
  "--log-level LEVEL",
262
206
  LOG_LEVELS,
263
- "Set the specified logging level, less intrusive than debug mode") do |log_level|
264
- unless options.log[:level] == Occi::Log::DEBUG
265
- options.log[:level] = Occi::Log.const_get(log_level.to_s.upcase)
207
+ "Set the specified logging level, only: [#{LOG_LEVELS.join('|')}]") do |log_level|
208
+ unless options.log.level == Occi::Log::DEBUG
209
+ options.log.level = Occi::Log.const_get(log_level.to_s.upcase)
210
+ end
211
+ end
212
+
213
+ opts.on_tail("-z",
214
+ "--examples",
215
+ "Show usage examples") do |examples|
216
+ if examples
217
+ if @@quiet
218
+ exit true
219
+ else
220
+ file = "#{File.expand_path('..', __FILE__)}/occi_opts/cli_examples.erb"
221
+ template = ERB.new(File.new(file).read, nil, '-')
222
+
223
+ puts template.result(binding)
224
+ exit! true
225
+ end
266
226
  end
267
227
  end
268
228
 
@@ -276,7 +236,7 @@ occi --endpoint https://localhost:3300/ --action delete --resource /compute/65sd
276
236
  "--debug",
277
237
  "Enable debugging messages") do |debug|
278
238
  options.debug = debug
279
- options.log[:level] = Occi::Log::DEBUG
239
+ options.log.level = Occi::Log::DEBUG
280
240
  end
281
241
 
282
242
  opts.on_tail("-h",
@@ -327,7 +287,52 @@ occi --endpoint https://localhost:3300/ --action delete --resource /compute/65sd
327
287
 
328
288
  private
329
289
 
290
+ def self.set_defaults(options)
291
+ options.debug = false
292
+
293
+ options.log = {}
294
+ options.log.out = STDERR
295
+ options.log.level = Occi::Log::ERROR
296
+
297
+ options.filter = nil
298
+ options.dump_model = false
299
+
300
+ options.endpoint = "http://localhost:3000"
301
+
302
+ options.auth = {}
303
+ options.auth.type = "none"
304
+ options.auth.user_cert = "#{ENV['HOME']}/.globus/usercred.pem"
305
+ options.auth.ca_path = "/etc/grid-security/certificates"
306
+ options.auth.username = "anonymous"
307
+ options.auth.ca_file = nil
308
+ options.auth.voms = nil
309
+
310
+ options.output_format = :plain
311
+
312
+ options.mixins = Occi::Core::Mixins.new
313
+ options.links = nil
314
+ options.attributes = Occi::Core::Attributes.new
315
+ options.context_vars = nil
316
+
317
+ # TODO: change media type back to occi+json after the rOCCI-server update
318
+ #options.media_type = "application/occi+json"
319
+ options.media_type = "text/plain,text/occi"
320
+
321
+ options
322
+ end
323
+
330
324
  def self.check_restrictions(options, opts)
325
+ check_incompatible_args(options, opts)
326
+
327
+ return if options.dump_model
328
+
329
+ mandatory = get_mandatory_args(options)
330
+ check_hash(options, mandatory, opts)
331
+
332
+ check_attributes(options.attributes, REQ_CREATE_ATTRS, opts) if options.action == :create
333
+ end
334
+
335
+ def self.check_incompatible_args(options, opts)
331
336
  if !options.dump_model && options.filter
332
337
  if @@quiet
333
338
  exit false
@@ -338,7 +343,7 @@ occi --endpoint https://localhost:3300/ --action delete --resource /compute/65sd
338
343
  end
339
344
  end
340
345
 
341
- if options.voms && options.auth[:type] != "x509"
346
+ if options.voms && options.auth.type != "x509"
342
347
  if @@quiet
343
348
  exit false
344
349
  else
@@ -347,9 +352,9 @@ occi --endpoint https://localhost:3300/ --action delete --resource /compute/65sd
347
352
  exit!
348
353
  end
349
354
  end
355
+ end
350
356
 
351
- return if options.dump_model
352
-
357
+ def self.get_mandatory_args(options)
353
358
  mandatory = []
354
359
 
355
360
  if options.action == :trigger
@@ -364,17 +369,15 @@ occi --endpoint https://localhost:3300/ --action delete --resource /compute/65sd
364
369
  end
365
370
 
366
371
  mandatory << :attributes
367
- check_attrs = true
368
372
  end
369
373
 
370
- mandatory.concat [:resource, :action]
374
+ if options.action == :link
375
+ mandatory << :links
376
+ end
371
377
 
372
- check_hash options, mandatory, opts
378
+ mandatory.concat [:resource, :action]
373
379
 
374
- if check_attrs
375
- mandatory = ["occi.core.title"]
376
- check_attributes options.attributes, mandatory, opts
377
- end
380
+ mandatory
378
381
  end
379
382
 
380
383
  def self.check_hash(hash, mandatory, opts)
@@ -0,0 +1,11 @@
1
+ Examples:
2
+
3
+ occi --endpoint https://localhost:3300/ --action list --resource os_tpl --auth x509
4
+
5
+ occi --endpoint https://localhost:3300/ --action list --resource resource_tpl --auth x509
6
+
7
+ occi --endpoint https://localhost:3300/ --action describe --resource os_tpl#debian6 --auth x509
8
+
9
+ occi --endpoint https://localhost:3300/ --action create --resource compute --mixin os_tpl#debian6 --mixin resource_tpl#small --attribute title="My rOCCI VM" --auth x509
10
+
11
+ occi --endpoint https://localhost:3300/ --action delete --resource /compute/65sd4f654sf65g4-s5fg65sfg465sfg-sf65g46sf5g4sdfg --auth x509
@@ -0,0 +1,61 @@
1
+ require 'base64'
2
+
3
+ module Occi::Cli
4
+ class OcciOpts
5
+ module Helper
6
+
7
+ ALLOWED_CONTEXT_VARS = [:public_key, :user_data].freeze
8
+
9
+ MIXIN_REGEXP = ACTION_REGEXP = /^(\S+?)#(\S+)$/
10
+ CONTEXT_REGEXP = ATTR_REGEXP = /^(\S+?)=(.+)$/
11
+
12
+ def self.parse_context_variable(cvar)
13
+ ary = CONTEXT_REGEXP.match(cvar).to_a.drop 1
14
+ raise ArgumentError, "Context variables must always contain ATTR=VALUE pairs!" unless ary.length == 2
15
+
16
+ symbol = ary[0].to_sym
17
+ unless ALLOWED_CONTEXT_VARS.include?(symbol)
18
+ raise ArgumentError,
19
+ "Only #{ALLOWED_CONTEXT_VARS.join(', ')} context " \
20
+ "variables are supported! #{symbol.to_s.inspect}"
21
+ end
22
+
23
+ context_data = ary[1]
24
+ if context_data.gsub!(/^file:\/\//,'')
25
+ raise 'File does not exist! #{context_data}' unless File.exist? context_data
26
+ context_data = File.read(context_data)
27
+ end
28
+
29
+ if symbol == :user_data
30
+ context_data = Base64.encode64(context_data).gsub("\n", '')
31
+ end
32
+
33
+ [symbol, context_data.strip]
34
+ end
35
+
36
+ def self.parse_attribute(attribute)
37
+ ary = ATTR_REGEXP.match(attribute).to_a.drop 1
38
+ raise ArgumentError, "Attribute must always contain ATTR=VALUE pairs!" unless ary.length == 2
39
+
40
+ ary[0] = "occi.core.#{ary[0]}" unless ary[0].include?('.')
41
+
42
+ ary
43
+ end
44
+
45
+ def self.parse_mixin(mixin)
46
+ parts = MIXIN_REGEXP.match(mixin).to_a.drop(1)
47
+ raise "Unknown mixin format '#{mixin.inspect}'! Use SCHEME#TERM or SHORT_SCHEME#TERM!" unless parts.length == 2
48
+
49
+ Occi::Core::Mixin.new("#{parts[0]}#", parts[1])
50
+ end
51
+
52
+ def self.parse_action(action)
53
+ parts = ACTION_REGEXP.match(action).to_a.drop(1)
54
+ raise "Unknown action format '#{action.inspect}'! Use SCHEME#TERM or SHORT_SCHEME#TERM!" unless parts.length == 2
55
+
56
+ Occi::Core::Action.new("#{parts[0]}#", parts[1])
57
+ end
58
+
59
+ end
60
+ end
61
+ end
@@ -1,5 +1,5 @@
1
1
  module Occi
2
2
  module Cli
3
- VERSION = "4.2.0.beta.3" unless defined?(::Occi::Cli::VERSION)
3
+ VERSION = "4.2.0.beta.6" unless defined?(::Occi::Cli::VERSION)
4
4
  end
5
5
  end
data/occi-cli.gemspec CHANGED
@@ -19,8 +19,9 @@ Gem::Specification.new do |gem|
19
19
  gem.test_files = `git ls-files -- {test,spec}/*`.split("\n")
20
20
  gem.require_paths = ["lib"]
21
21
 
22
- gem.add_dependency 'occi-api', '= 4.2.0.beta.2'
22
+ gem.add_dependency 'occi-api', '= 4.2.0.beta.6'
23
23
  gem.add_dependency 'json'
24
+ gem.add_dependency 'highline'
24
25
 
25
26
  gem.required_ruby_version = ">= 1.9.3"
26
27
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: occi-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.2.0.beta.3
4
+ version: 4.2.0.beta.6
5
5
  prerelease: 6
6
6
  platform: ruby
7
7
  authors:
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2013-12-06 00:00:00.000000000 Z
14
+ date: 2014-01-06 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: occi-api
@@ -20,7 +20,7 @@ dependencies:
20
20
  requirements:
21
21
  - - '='
22
22
  - !ruby/object:Gem::Version
23
- version: 4.2.0.beta.2
23
+ version: 4.2.0.beta.6
24
24
  type: :runtime
25
25
  prerelease: false
26
26
  version_requirements: !ruby/object:Gem::Requirement
@@ -28,7 +28,7 @@ dependencies:
28
28
  requirements:
29
29
  - - '='
30
30
  - !ruby/object:Gem::Version
31
- version: 4.2.0.beta.2
31
+ version: 4.2.0.beta.6
32
32
  - !ruby/object:Gem::Dependency
33
33
  name: json
34
34
  requirement: !ruby/object:Gem::Requirement
@@ -45,6 +45,22 @@ dependencies:
45
45
  - - ! '>='
46
46
  - !ruby/object:Gem::Version
47
47
  version: '0'
48
+ - !ruby/object:Gem::Dependency
49
+ name: highline
50
+ requirement: !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ type: :runtime
57
+ prerelease: false
58
+ version_requirements: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ! '>='
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
48
64
  description: This gem is a client implementation of the Open Cloud Computing Interface
49
65
  in Ruby
50
66
  email:
@@ -78,9 +94,12 @@ files:
78
94
  - lib/occi/cli/helpers/create_helper.rb
79
95
  - lib/occi/cli/helpers/delete_helper.rb
80
96
  - lib/occi/cli/helpers/describe_helper.rb
97
+ - lib/occi/cli/helpers/link_helper.rb
81
98
  - lib/occi/cli/helpers/list_helper.rb
82
99
  - lib/occi/cli/helpers/trigger_helper.rb
83
100
  - lib/occi/cli/occi_opts.rb
101
+ - lib/occi/cli/occi_opts/cli_examples.erb
102
+ - lib/occi/cli/occi_opts/occi_opts_helper.rb
84
103
  - lib/occi/cli/resource_output_factory.rb
85
104
  - lib/occi/cli/templates/mixins.erb
86
105
  - lib/occi/cli/templates/resources.erb