opennebula-cli 4.0.1 → 4.1.80.beta

Sign up to get free protection for your applications and to get access to all the features.
data/NOTICE CHANGED
@@ -21,6 +21,9 @@ The following people have contributed to the development of the technology
21
21
  - Daniel Molina Aranda (dmolina@opennebula.org)
22
22
  - Hector Sanjuan Redondo (hsanjuan@opennebula.org)
23
23
 
24
+ The new features for service elasticity (oneFlow) introduced in OpenNebula 4.2
25
+ were funded by Blackberry in the context of the Fund a Feature Program.
26
+
24
27
  OpenNebula Project also acknowledges the contributions of C12G Labs developers.
25
28
 
26
29
  LICENSE
@@ -43,5 +46,6 @@ OpenNebula distribution includes third-party software under fully compatible
43
46
  open-source licenses. See the following directories and the NOTICE files
44
47
  they contain for more information:
45
48
 
49
+ - share/vendor
46
50
  - src/sunstone/public/vendor
47
51
  - src/oca/java/lib
data/bin/onecluster CHANGED
@@ -185,10 +185,16 @@ cmd=CommandParser::CmdParser.new(ARGV) do
185
185
  be launched to modify the current content.
186
186
  EOT
187
187
 
188
- command :update, update_desc, :clusterid, [:file, nil] do
188
+ command :update, update_desc, :clusterid, [:file, nil],
189
+ :options=>OpenNebulaHelper::APPEND do
189
190
  helper.perform_action(args[0],options,"modified") do |obj|
190
- str = OpenNebulaHelper.update_template(args[0], obj, args[1])
191
- obj.update(str)
191
+ if options[:append]
192
+ str = OpenNebulaHelper.append_template(args[0], obj, args[1])
193
+ else
194
+ str = OpenNebulaHelper.update_template(args[0], obj, args[1])
195
+ end
196
+
197
+ obj.update(str, options[:append])
192
198
  end
193
199
  end
194
200
  end
data/bin/onedatastore CHANGED
@@ -162,10 +162,16 @@ cmd=CommandParser::CmdParser.new(ARGV) do
162
162
  be launched to modify the current content.
163
163
  EOT
164
164
 
165
- command :update, update_desc, :datastoreid, [:file, nil] do
165
+ command :update, update_desc, :datastoreid, [:file, nil],
166
+ :options=>OpenNebulaHelper::APPEND do
166
167
  helper.perform_action(args[0],options,"modified") do |obj|
167
- str = OpenNebulaHelper.update_template(args[0], obj, args[1])
168
- obj.update(str)
168
+ if options[:append]
169
+ str = OpenNebulaHelper.append_template(args[0], obj, args[1])
170
+ else
171
+ str = OpenNebulaHelper.update_template(args[0], obj, args[1])
172
+ end
173
+
174
+ obj.update(str, options[:append])
169
175
  end
170
176
  end
171
177
  end
data/bin/oneflow ADDED
@@ -0,0 +1,673 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # -------------------------------------------------------------------------- #
4
+ # Copyright 2010-2013, C12G Labs S.L. #
5
+ # #
6
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may #
7
+ # not use this file except in compliance with the License. You may obtain #
8
+ # 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, #
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
15
+ # See the License for the specific language governing permissions and #
16
+ # limitations under the License. #
17
+ #--------------------------------------------------------------------------- #
18
+
19
+ ONE_LOCATION=ENV["ONE_LOCATION"]
20
+
21
+ if !ONE_LOCATION
22
+ RUBY_LIB_LOCATION="/usr/lib/one/ruby"
23
+ else
24
+ RUBY_LIB_LOCATION=ONE_LOCATION+"/lib/ruby"
25
+ end
26
+
27
+ $: << RUBY_LIB_LOCATION
28
+ $: << RUBY_LIB_LOCATION+'/cli'
29
+
30
+ require 'command_parser'
31
+ require 'opennebula/oneflow_client'
32
+
33
+ require 'cli_helper'
34
+ require 'one_helper/onevm_helper'
35
+
36
+ require 'json'
37
+
38
+ USER_AGENT = "CLI"
39
+
40
+ # Base Path representing the resource to be used in the requests
41
+ RESOURCE_PATH = "/service"
42
+
43
+ #
44
+ # Table
45
+ #
46
+
47
+ SERVICE_TABLE = CLIHelper::ShowTable.new(nil, self) do
48
+ column :ID, "ID", :size=>10 do |d|
49
+ d["ID"]
50
+ end
51
+
52
+ column :USER, "Username", :left, :size=>15 do |d|
53
+ d["UNAME"]
54
+ end
55
+
56
+ column :GROUP, "Group", :left, :size=>15 do |d|
57
+ d["GNAME"]
58
+ end
59
+
60
+ column :NAME, "Name", :size=>25, :left=>true do |d|
61
+ d["NAME"]
62
+ end
63
+
64
+ column :STATE, "State", :size=>11, :left=>true do |d|
65
+ Service.state_str(d["TEMPLATE"]["BODY"]['state'])
66
+ end
67
+
68
+ default :ID, :USER, :GROUP, :NAME, :STATE
69
+ end
70
+
71
+ NODE_TABLE = CLIHelper::ShowTable.new(nil, self) do
72
+ column :VM_ID, "ONE identifier for Virtual Machine", :size=>6 do |d|
73
+ st = ""
74
+ if d['scale_up']
75
+ st << "\u2191 "
76
+ elsif d['disposed']
77
+ st << "\u2193 "
78
+ end
79
+
80
+ if d['vm_info'].nil?
81
+ st << d['deploy_id'].to_s
82
+ else
83
+ st << d['vm_info']['VM']["ID"]
84
+ end
85
+
86
+ st
87
+ end
88
+
89
+ column :NAME, "Name of the Virtual Machine", :left,
90
+ :size=>23 do |d|
91
+ if !d['vm_info'].nil?
92
+ if d['vm_info']['VM']["RESCHED"] == "1"
93
+ "*#{d["NAME"]}"
94
+ else
95
+ d['vm_info']['VM']["NAME"]
96
+ end
97
+ else
98
+ ""
99
+ end
100
+ end
101
+
102
+ column :USER, "Username of the Virtual Machine owner", :left,
103
+ :size=>8 do |d|
104
+ if !d['vm_info'].nil?
105
+ d['vm_info']['VM']["UNAME"]
106
+ else
107
+ ""
108
+ end
109
+ end
110
+
111
+ column :GROUP, "Group of the Virtual Machine", :left,
112
+ :size=>8 do |d|
113
+ if !d['vm_info'].nil?
114
+ d['vm_info']['VM']["GNAME"]
115
+ else
116
+ ""
117
+ end
118
+ end
119
+
120
+ column :STAT, "Actual status", :size=>4 do |d,e|
121
+ if !d['vm_info'].nil?
122
+ OneVMHelper.state_to_str(d['vm_info']['VM']["STATE"], d['vm_info']['VM']["LCM_STATE"])
123
+ else
124
+ ""
125
+ end
126
+ end
127
+
128
+ column :UCPU, "CPU percentage used by the VM", :size=>4 do |d|
129
+ if !d['vm_info'].nil?
130
+ d['vm_info']['VM']["CPU"]
131
+ else
132
+ ""
133
+ end
134
+ end
135
+
136
+ column :UMEM, "Memory used by the VM", :size=>7 do |d|
137
+ if !d['vm_info'].nil?
138
+ OpenNebulaHelper.unit_to_str(d['vm_info']['VM']["MEMORY"].to_i, {})
139
+ else
140
+ ""
141
+ end
142
+ end
143
+
144
+ column :HOST, "Host where the VM is running", :left, :size=>20 do |d|
145
+ if !d['vm_info'].nil?
146
+ if d['vm_info']['VM']['HISTORY_RECORDS'] && d['vm_info']['VM']['HISTORY_RECORDS']['HISTORY']
147
+ state_str = VirtualMachine::VM_STATE[d['vm_info']['VM']['STATE'].to_i]
148
+ if %w{ACTIVE SUSPENDED}.include? state_str
149
+ history = if d['vm_info']['VM']['HISTORY_RECORDS']['HISTORY'].instance_of?(Array)
150
+ d['vm_info']['VM']['HISTORY_RECORDS']['HISTORY'].last
151
+ else
152
+ d['vm_info']['VM']['HISTORY_RECORDS']['HISTORY']
153
+ end
154
+
155
+ history['HOSTNAME']
156
+ end
157
+ end
158
+ else
159
+ ""
160
+ end
161
+ end
162
+
163
+ column :TIME, "Time since the VM was submitted", :size=>10 do |d|
164
+ if !d['vm_info'].nil?
165
+ stime = d['vm_info']['VM']["STIME"].to_i
166
+ etime = d['vm_info']['VM']["ETIME"]=="0" ? Time.now.to_i : d['vm_info']['VM']["ETIME"].to_i
167
+ dtime = etime-stime
168
+ OpenNebulaHelper.period_to_str(dtime, false)
169
+ else
170
+ ""
171
+ end
172
+ end
173
+
174
+ default :VM_ID, :NAME, :STAT, :UCPU, :UMEM, :HOST, :TIME
175
+ end
176
+
177
+ # List the services. This method is used in top and list commands
178
+ # @param [Service::Client] client
179
+ # @param [Array] args
180
+ # @param [Hash] options
181
+ # @return [[Integer, String], Integer] Returns the exit_code and optionally
182
+ # a String to be printed
183
+ def list_services(client, args, options)
184
+ response = client.get(RESOURCE_PATH)
185
+
186
+ if CloudClient::is_error?(response)
187
+ [response.code.to_i, response.to_s]
188
+ else
189
+ #[0,response.body]
190
+ if options[:json]
191
+ [0,response.body]
192
+ else
193
+ array_list = JSON.parse(response.body)
194
+ SERVICE_TABLE.show(array_list['DOCUMENT_POOL']['DOCUMENT'])
195
+ 0
196
+ end
197
+ end
198
+ end
199
+
200
+ # Show the service information. This method is used in top and show commands
201
+ # @param [Service::Client] client
202
+ # @param [Array] args
203
+ # @param [Hash] options
204
+ # @return [[Integer, String], Integer] Returns the exit_code and optionally
205
+ # a String to be printed
206
+ def show_service(client, args, options)
207
+ response = client.get("#{RESOURCE_PATH}/#{args[0]}")
208
+
209
+ if CloudClient::is_error?(response)
210
+ [response.code.to_i, response.to_s]
211
+ else
212
+ #[0,response.body]
213
+ if options[:json]
214
+ [0,response.body]
215
+ else
216
+ str="%-20s: %-20s"
217
+ str_h1="%-80s"
218
+
219
+ document_hash = JSON.parse(response.body)
220
+ template = document_hash['DOCUMENT']['TEMPLATE']['BODY']
221
+
222
+ CLIHelper.print_header(str_h1 % "SERVICE #{document_hash['DOCUMENT']['ID']} INFORMATION")
223
+
224
+ puts str % ["ID", document_hash['DOCUMENT']['ID']]
225
+ puts str % ["NAME", document_hash['DOCUMENT']['NAME']]
226
+ puts str % ["USER", document_hash['DOCUMENT']['UNAME']]
227
+ puts str % ["GROUP",document_hash['DOCUMENT']['GNAME']]
228
+
229
+ puts str % ["STRATEGY", template['deployment']]
230
+ puts str % ["SERVICE STATE", Service.state_str(template['state'])]
231
+ puts str % ["SHUTDOWN", template['shutdown_action']] if template['shutdown_action']
232
+
233
+ puts
234
+
235
+ CLIHelper.print_header(str_h1 % "PERMISSIONS",false)
236
+
237
+ ["OWNER", "GROUP", "OTHER"].each { |e|
238
+ mask = "---"
239
+ mask[0] = "u" if document_hash['DOCUMENT']['PERMISSIONS']["#{e}_U"] == "1"
240
+ mask[1] = "m" if document_hash['DOCUMENT']['PERMISSIONS']["#{e}_M"] == "1"
241
+ mask[2] = "a" if document_hash['DOCUMENT']['PERMISSIONS']["#{e}_A"] == "1"
242
+
243
+ puts str % [e, mask]
244
+ }
245
+
246
+ puts
247
+
248
+ template['roles'].each {|role|
249
+ CLIHelper.print_header("ROLE #{role['name']}", false)
250
+
251
+ puts str % ["ROLE STATE", Role.state_str(role['state'])]
252
+ puts str % ["PARENTS", role['parents'].join(', ')] if role['parents']
253
+ puts str % ["VM TEMPLATE", role['vm_template']]
254
+ puts str % ["CARNIDALITY", role['cardinality']]
255
+ puts str % ["MIN VMS", role['min_vms']] if role['min_vms']
256
+ puts str % ["MAX VMS", role['max_vms']] if role['max_vms']
257
+ puts str % ["SHUTDOWN", role['shutdown_action']] if role['shutdown_action']
258
+
259
+ puts "NODES INFORMATION"
260
+ NODE_TABLE.show(role['nodes'])
261
+
262
+ if !role['elasticity_policies'].nil? || !role['scheduled_policies'].nil?
263
+ puts
264
+ puts "ELASTICITY RULES"
265
+ puts str % ["COOLDOWN", "#{role['cooldown']}s"] if role['cooldown']
266
+
267
+ if role['elasticity_policies']
268
+ puts
269
+ # puts "ELASTICITY POLICIES"
270
+ CLIHelper::ShowTable.new(nil, self) do
271
+ column :ADJUST, "", :left, :size=>12 do |d|
272
+ adjust_str(d)
273
+ end
274
+
275
+ column :EXPRESSION, "", :left, :size=>48 do |d|
276
+ if !d['expression_evaluated'].nil?
277
+ d['expression_evaluated']
278
+ else
279
+ d['expression']
280
+ end
281
+ end
282
+
283
+ column :'EVALS', "", :left, :size=>5 do |d|
284
+ "#{d['true_evals'].to_i} / #{d['period_number']}"
285
+ end
286
+
287
+ column :PERIOD, "", :size=>6 do |d|
288
+ "#{d['period']}s"
289
+ end
290
+
291
+ column :COOL, "", :size=>5 do |d|
292
+ d['cooldown'] ? "#{d['cooldown']}s" : '-'
293
+ end
294
+
295
+ default :ADJUST, :EXPRESSION, :EVALS, :PERIOD, :COOL
296
+ end.show([role['elasticity_policies']].flatten, {})
297
+ end
298
+
299
+ if role['scheduled_policies']
300
+ puts
301
+ # puts "SCHEDULED POLICIES"
302
+ CLIHelper::ShowTable.new(nil, self) do
303
+ column :ADJUST, "", :left, :size=>12 do |d|
304
+ adjust_str(d)
305
+ end
306
+
307
+ column :TIME, "", :left, :size=>67 do |d|
308
+ if d['start_time']
309
+ Time.parse(d['start_time']).to_s
310
+ else
311
+ d['recurrence']
312
+ end
313
+ end
314
+
315
+ default :ADJUST, :TIME
316
+ end.show([role['scheduled_policies']].flatten, {})
317
+ end
318
+ end
319
+
320
+ puts
321
+ }
322
+
323
+ puts
324
+
325
+ CLIHelper.print_header(str_h1 % "LOG MESSAGES",false)
326
+
327
+ if template['log']
328
+ template['log'].each { |log|
329
+ t = Time.at(log['timestamp']).strftime("%m/%d/%y %H:%M")
330
+ puts "#{t} [#{log['severity']}] #{log['message']}"
331
+ }
332
+ end
333
+
334
+ 0
335
+ end
336
+ end
337
+ end
338
+
339
+ def adjust_str(policy)
340
+ sign = policy['adjust'].to_i >= 0 ? "+" : "-"
341
+ adjust = policy['adjust'].to_i.abs
342
+
343
+ case policy['type']
344
+ when 'CARDINALITY'
345
+ "= #{adjust}"
346
+ when 'PERCENTAGE_CHANGE'
347
+ st = "#{sign} #{adjust} %"
348
+ if policy['min_adjust_step']
349
+ st << " (#{policy['min_adjust_step']})"
350
+ end
351
+
352
+ st
353
+ else
354
+ "#{sign} #{adjust}"
355
+ end
356
+ end
357
+
358
+ #
359
+ # Commands
360
+ #
361
+
362
+ cmd=CommandParser::CmdParser.new(ARGV) do
363
+ usage "`oneflow` <command> [<args>] [<options>]"
364
+ version OpenNebulaHelper::ONE_VERSION
365
+
366
+ set :option, Service::DEFAULT_OPTIONS
367
+ set :option, CommandParser::VERSION
368
+ set :option, CommandParser::HELP
369
+
370
+ #
371
+ # Formatters for arguments
372
+ #
373
+ set :format, :groupid, OpenNebulaHelper.rname_to_id_desc("GROUP") do |arg|
374
+ OpenNebulaHelper.rname_to_id(arg, "GROUP")
375
+ end
376
+
377
+ set :format, :userid, OpenNebulaHelper.rname_to_id_desc("USER") do |arg|
378
+ OpenNebulaHelper.rname_to_id(arg, "USER")
379
+ end
380
+
381
+ set :format, :service_id, Service.rname_to_id_desc("SERVICE") do |arg|
382
+ Service.rname_to_id(arg, "SERVICE")
383
+ end
384
+
385
+ set :format, :service_id_list, Service.list_to_id_desc("SERVICE") do |arg|
386
+ Service.list_to_id(arg, "SERVICE")
387
+ end
388
+
389
+ set :format, :vm_action, "Actions supported: #{Role::SCHEDULE_ACTIONS.join(', ')}" do |arg|
390
+ if Role::SCHEDULE_ACTIONS.include?(arg)
391
+ [0, arg]
392
+ else
393
+ [-1, "Action #{arg} is not supported. Actions supported: #{Role::SCHEDULE_ACTIONS.join(', ')}"]
394
+ end
395
+ end
396
+
397
+ #
398
+ # List
399
+ #
400
+
401
+ list_desc = <<-EOT.unindent
402
+ List the available services
403
+ EOT
404
+
405
+ command :list, list_desc, :options => Service::JSON_FORMAT do
406
+ client = Service::Client.new(
407
+ :username => options[:username],
408
+ :password => options[:password],
409
+ :url => options[:server],
410
+ :user_agent => USER_AGENT)
411
+
412
+ list_services(client, args, options)
413
+ end
414
+
415
+ #
416
+ # Show
417
+ #
418
+
419
+ show_desc = <<-EOT.unindent
420
+ Show detailed information of a given service
421
+ EOT
422
+
423
+ command :show, show_desc, :service_id, :options => Service::JSON_FORMAT do
424
+ client = Service::Client.new(
425
+ :username => options[:username],
426
+ :password => options[:password],
427
+ :url => options[:server],
428
+ :user_agent => USER_AGENT)
429
+
430
+ show_service(client, args, options)
431
+ end
432
+
433
+ #
434
+ # Top
435
+ #
436
+
437
+ top_desc = <<-EOT.unindent
438
+ Top the services or the extended information of the target service if a
439
+ id is specified
440
+ EOT
441
+
442
+ command :top, top_desc, [:service_id, nil],
443
+ :options => [Service::JSON_FORMAT, Service::TOP, CLIHelper::DELAY] do
444
+ client = Service::Client.new(
445
+ :username => options[:username],
446
+ :password => options[:password],
447
+ :url => options[:server],
448
+ :user_agent => USER_AGENT)
449
+
450
+ delay=options[:delay] ? options[:delay] : 3
451
+
452
+ begin
453
+ while true
454
+ CLIHelper.scr_cls
455
+ CLIHelper.scr_move(0,0)
456
+
457
+ if args[0]
458
+ rc, message = show_service(client, args, options)
459
+
460
+ if rc != 0
461
+ raise message
462
+ end
463
+ else
464
+ rc, message = list_services(client, args, options)
465
+
466
+ if rc != 0
467
+ raise message
468
+ end
469
+ end
470
+
471
+ sleep delay
472
+ end
473
+ rescue Exception => e
474
+ puts e.message
475
+ -1
476
+ end
477
+ end
478
+
479
+ #
480
+ # Delete
481
+ #
482
+
483
+ delete_desc = <<-EOT.unindent
484
+ Delete a given service
485
+ EOT
486
+
487
+ command :delete, delete_desc, [:range, :service_id_list] do
488
+ client = Service::Client.new(
489
+ :username => options[:username],
490
+ :password => options[:password],
491
+ :url => options[:server],
492
+ :user_agent => USER_AGENT)
493
+
494
+ Service.perform_actions(args[0]) { |service_id|
495
+ client.delete("#{RESOURCE_PATH}/#{service_id}")
496
+ }
497
+ end
498
+
499
+ #
500
+ # Shutdown
501
+ #
502
+
503
+ shutdown_desc = <<-EOT.unindent
504
+ Shutdown a service.
505
+ From RUNNING or WARNING shuts down the Service
506
+ EOT
507
+
508
+ command :shutdown, shutdown_desc, [:range, :service_id_list] do
509
+ client = Service::Client.new(
510
+ :username => options[:username],
511
+ :password => options[:password],
512
+ :url => options[:server],
513
+ :user_agent => USER_AGENT)
514
+
515
+ Service.perform_actions(args[0]) { |service_id|
516
+ json_action = Service.build_json_action('shutdown')
517
+
518
+ client.post("#{RESOURCE_PATH}/#{service_id}/action", json_action)
519
+ }
520
+ end
521
+
522
+ #
523
+ # Recover
524
+ #
525
+
526
+ recover_desc = <<-EOT.unindent
527
+ Recover a failed service, cleaning the failed VMs.
528
+ From FAILED_DEPLOYING continues deploying the Service
529
+ From FAILED_SCALING continues scaling the Service
530
+ From FAILED_UNDEPLOYING continues shutting down the Service
531
+ From COOLDOWN the Service is set to running ignoring the cooldown duration
532
+ From WARNING failed VMs are deleted, and new VMs are instantiated
533
+ EOT
534
+
535
+ command :recover, recover_desc, [:range, :service_id_list] do
536
+ client = Service::Client.new(
537
+ :username => options[:username],
538
+ :password => options[:password],
539
+ :url => options[:server],
540
+ :user_agent => USER_AGENT)
541
+
542
+ Service.perform_actions(args[0]) { |service_id|
543
+ json_action = Service.build_json_action('recover')
544
+
545
+ client.post("#{RESOURCE_PATH}/#{service_id}/action", json_action)
546
+ }
547
+ end
548
+
549
+ #
550
+ # Scale
551
+ #
552
+
553
+ scale_desc = <<-EOT.unindent
554
+ Scale a role to the given cardinality
555
+ EOT
556
+
557
+ command :'scale', scale_desc, :service_id, :role_name,
558
+ :cardinality, :options => [Service::FORCE] do
559
+ client = Service::Client.new(
560
+ :username => options[:username],
561
+ :password => options[:password],
562
+ :url => options[:server],
563
+ :user_agent => USER_AGENT)
564
+
565
+ if !(args[2] =~ /^\d+$/)
566
+ puts "Cardinality must be an integer number"
567
+ exit -1
568
+ end
569
+
570
+ exit_code = 0
571
+
572
+ json = "{ \"cardinality\" : #{args[2]},\n"<<
573
+ " \"force\" : #{options[:force] == true} }"
574
+
575
+ response = client.put("#{RESOURCE_PATH}/#{args[0]}/role/#{args[1]}", json)
576
+
577
+ if CloudClient::is_error?(response)
578
+ puts response.to_s
579
+ exit_code = response.code.to_i
580
+ end
581
+
582
+ exit_code
583
+ end
584
+
585
+ chgrp_desc = <<-EOT.unindent
586
+ Changes the service group
587
+ EOT
588
+
589
+ command :chgrp, chgrp_desc, [:range, :service_id_list], :groupid do
590
+ client = Service::Client.new(
591
+ :username => options[:username],
592
+ :password => options[:password],
593
+ :url => options[:server],
594
+ :user_agent => USER_AGENT)
595
+
596
+ Service.perform_actions(args[0]) { |service_id|
597
+ params = Hash.new
598
+ params['group_id'] = args[1].to_i
599
+
600
+ json_action = Service.build_json_action('chgrp', params)
601
+
602
+ client.post("#{RESOURCE_PATH}/#{service_id}/action", json_action)
603
+ }
604
+ end
605
+
606
+ chown_desc = <<-EOT.unindent
607
+ Changes the service owner and group
608
+ EOT
609
+
610
+ command :chown, chown_desc, [:range, :service_id_list], :userid, [:groupid, nil] do
611
+ client = Service::Client.new(
612
+ :username => options[:username],
613
+ :password => options[:password],
614
+ :url => options[:server],
615
+ :user_agent => USER_AGENT)
616
+
617
+ Service.perform_actions(args[0]) { |service_id|
618
+ params = Hash.new
619
+ params['owner_id'] = args[1]
620
+ params['group_id'] = args[2] if args[2]
621
+
622
+ json_action = Service.build_json_action('chown', params)
623
+
624
+ client.post("#{RESOURCE_PATH}/#{service_id}/action", json_action)
625
+ }
626
+ end
627
+
628
+ chmod_desc = <<-EOT.unindent
629
+ Changes the service permissions
630
+ EOT
631
+
632
+ command :chmod, chmod_desc, [:range, :service_id_list], :octet do
633
+ client = Service::Client.new(
634
+ :username => options[:username],
635
+ :password => options[:password],
636
+ :url => options[:server],
637
+ :user_agent => USER_AGENT)
638
+
639
+ Service.perform_actions(args[0]) { |service_id|
640
+ params = Hash.new
641
+ params['octet'] = args[1]
642
+
643
+ json_action = Service.build_json_action('chmod', params)
644
+
645
+ client.post("#{RESOURCE_PATH}/#{service_id}/action", json_action)
646
+ }
647
+ end
648
+
649
+ action_desc = <<-EOT.unindent
650
+ Perform an action on all the Virtual Machines of a given role.
651
+ Actions supported: #{Role::SCHEDULE_ACTIONS.join(",")}
652
+ EOT
653
+
654
+ command :"action", action_desc, :service_id, :role_name, :vm_action,
655
+ :options => [Service::PERIOD, Service::NUMBER] do
656
+
657
+ client = Service::Client.new(
658
+ :username => options[:username],
659
+ :password => options[:password],
660
+ :url => options[:server],
661
+ :user_agent => USER_AGENT)
662
+
663
+ Service.perform_actions([args[0]]) { |service_id|
664
+ params = Hash.new
665
+ params[:period] = options[:period].to_i if options[:period]
666
+ params[:number] = options[:number].to_i if options[:number]
667
+
668
+ json_action = Service.build_json_action(args[2], params)
669
+
670
+ client.post("#{RESOURCE_PATH}/#{service_id}/role/#{args[1]}/action", json_action)
671
+ }
672
+ end
673
+ end