opennebula-cli 4.0.1 → 4.1.80.beta

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/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