rvc 1.7.0 → 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,103 @@
1
+ # Copyright (c) 2013 VMware, Inc. All Rights Reserved.
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require 'rvc/vim'
22
+
23
+ begin
24
+ require 'net/ssh'
25
+ rescue LoadError
26
+ end
27
+
28
+ opts :config_syslog do
29
+ summary "Configure Syslog"
30
+ arg :entity, nil, :lookup => [VIM, VIM::HostSystem, VIM::ComputeResource, VIM::ClusterComputeResource]
31
+ arg :ip, nil, :type => :string
32
+ opt :vc_root_pwd, "VC root password for SSH access", :default => "vmware"
33
+ end
34
+
35
+ def config_syslog entity, ip, opts
36
+ if entity.is_a?(VIM)
37
+ puts "#{Time.now}: Finding all Hosts inside VC"
38
+ $shell.fs.marks['vcrvc'] = entity
39
+ hosts = []
40
+ hosts += $shell.fs.lookup("~vcrvc/*/computers/*/host")
41
+ hosts += $shell.fs.lookup("~vcrvc/*/computers/*/hosts/*")
42
+ elsif entity.is_a?(VIM::ComputeResource)
43
+ hosts = entity.host
44
+ else
45
+ hosts = [entity]
46
+ end
47
+ if hosts.length == 0
48
+ err "No hosts found"
49
+ end
50
+ conn = hosts.first._connection
51
+ pc = conn.propertyCollector
52
+
53
+ lock = Mutex.new
54
+ hosts_props = pc.collectMultiple(hosts,
55
+ 'name',
56
+ 'runtime.connectionState',
57
+ )
58
+ connected_hosts = hosts_props.select do |k,v|
59
+ v['runtime.connectionState'] == 'connected'
60
+ end.keys
61
+ host = connected_hosts.first
62
+ if !connected_hosts.first
63
+ err "Couldn't find any connected hosts"
64
+ end
65
+
66
+ puts "#{Time.now}: Configuring all ESX hosts ..."
67
+ loghost = "udp://#{ip}:514"
68
+ hosts.map do |host|
69
+ Thread.new do
70
+ begin
71
+ c1 = conn.spawn_additional_connection
72
+ host = host.dup_on_conn(c1)
73
+ hostName = host.name
74
+ lock.synchronize do
75
+ puts "#{Time.now}: Configuring syslog on #{hostName}"
76
+ end
77
+ syslog = host.esxcli.system.syslog
78
+ syslog.config.set(:loghost => loghost)
79
+ syslog.reload
80
+ rescue Exception => ex
81
+ puts "#{Time.now}: #{host.name}: Got exception: #{ex.class}: #{ex.message}"
82
+ end
83
+ end
84
+ end.each{|t| t.join}
85
+ puts "#{Time.now}: Done configuring syslog on all hosts"
86
+
87
+ local = "#{File.dirname(__FILE__)}/configurevCloudSuiteSyslog.sh"
88
+ osType = conn.serviceContent.about.osType
89
+ if File.exists?(local) && osType == "linux-x64"
90
+ puts "#{Time.now}: Configuring VCVA ..."
91
+ Net::SSH.start(conn.host, "root", :password => opts[:vc_root_pwd],
92
+ :paranoid => false) do |ssh|
93
+ ssh.scp.upload!(local, "/tmp/configurevCloudSuiteSyslog.sh")
94
+ cmd = "sh /tmp/configurevCloudSuiteSyslog.sh vcsa #{ip}"
95
+ puts "#{Time.now}: Running '#{cmd}' on VCVA"
96
+ puts ssh.exec!(cmd)
97
+ end
98
+ puts "#{Time.now}: Done with VC"
99
+ else
100
+ puts "#{Time.now}: VC isn't Linux, skipping ..."
101
+ end
102
+ puts "#{Time.now}: Done"
103
+ end
@@ -636,10 +636,10 @@ end
636
636
  opts :create_vmknic do
637
637
  summary "Create a vmknic on vDS on one or more hosts. Always uses DHCP"
638
638
  arg :portgroup, nil, :lookup => VIM::Network
639
- arg :host, nil, :lookup => VIM::HostSystem, :multi => true
639
+ arg :hosts, nil, :lookup => VIM::HostSystem, :multi => true
640
640
  end
641
641
 
642
- def create_vmknic portgroup, hosts, opts
642
+ def create_vmknic portgroup, hosts
643
643
  if !portgroup.is_a?(VIM::DistributedVirtualPortgroup)
644
644
  err "Legacy switches not supported yet"
645
645
  end
@@ -660,3 +660,60 @@ def create_vmknic portgroup, hosts, opts
660
660
  puts "Host #{host.name}: Added vmknic #{vmknic_name}"
661
661
  end
662
662
  end
663
+
664
+ opts :migrate_vmknic do
665
+ summary "Migrate a vmknic from a standard vSwitch to a vDS."
666
+ arg :host, nil, :lookup => VIM::HostSystem
667
+ arg :vmknic, nil
668
+ arg :portgroup, nil, :lookup => VIM::Network
669
+ end
670
+
671
+ def migrate_vmknic host, vmknic, portgroup
672
+ if !portgroup.is_a?(VIM::DistributedVirtualPortgroup)
673
+ err "Only migration from standard to a distributed vSwitch is supported for now."
674
+ end
675
+ ns = host.configManager.networkSystem
676
+
677
+ # Find the VMkernel interface.
678
+ vmk_dev = nil
679
+ ns.networkInfo.vnic.each do |vnic|
680
+ if vnic.device == vmknic
681
+ vmk_dev = vnic
682
+ break
683
+ end
684
+ end
685
+
686
+ # Find the source portgroup.
687
+ src_pg = nil
688
+ ns.networkInfo.portgroup.each do |pg|
689
+ if pg.spec.name == vmk_dev.portgroup
690
+ src_pg = pg
691
+ break
692
+ end
693
+ end
694
+
695
+ ns.UpdateNetworkConfig(
696
+ changeMode: 'modify',
697
+ config: VIM::HostNetworkConfig(
698
+ portgroup: [
699
+ VIM::HostPortGroupConfig(
700
+ changeOperation: 'remove',
701
+ spec: src_pg.spec
702
+ )
703
+ ],
704
+ vnic: [
705
+ VIM::HostVirtualNicConfig(
706
+ changeOperation: 'edit',
707
+ device: vmknic,
708
+ portgroup: src_pg.spec.name,
709
+ spec: VIM::HostVirtualNicSpec(
710
+ distributedVirtualPort: VIM::DistributedVirtualSwitchPortConnection(
711
+ portgroupKey: portgroup.key,
712
+ switchUuid: portgroup.config.distributedVirtualSwitch.uuid
713
+ )
714
+ )
715
+ )
716
+ ]
717
+ )
718
+ )
719
+ end
@@ -81,7 +81,7 @@ def connect uri, opts
81
81
  if env_rev && env_rev.to_f == 0
82
82
  vim.rev = env_rev
83
83
  else
84
- vim.rev = [rev, env_rev || '5.1'].min
84
+ vim.rev = [rev, env_rev || '5.5'].min
85
85
  end
86
86
  end
87
87
 
@@ -103,7 +103,25 @@ opts :shutdown_guest do
103
103
  end
104
104
 
105
105
  def shutdown_guest vms, opts
106
- vms.each(&:ShutdownGuest)
106
+ conn = vms.first._connection
107
+ pc = conn.propertyCollector
108
+ vmsProps = pc.collectMultiple(vms, 'runtime.powerState',
109
+ 'guest.toolsRunningStatus', 'name')
110
+ vms.each do |vm|
111
+ if vmsProps[vm]['runtime.powerState'] != 'poweredOn'
112
+ puts "VM #{vmsProps[vm]['name']} not powered on, skipping"
113
+ next
114
+ end
115
+ if vmsProps[vm]['guest.toolsRunningStatus'] != 'guestToolsRunning'
116
+ puts "VM #{vmsProps[vm]['name']} doesn't have tools running, skipping"
117
+ next
118
+ end
119
+ begin
120
+ vm.ShutdownGuest
121
+ rescue Exception => ex
122
+ puts "VM #{vmsProps[vm]['name']}: #{ex.class}: #{ex.message}"
123
+ end
124
+ end
107
125
  wait_for_shutdown vms, opts unless opts[:timeout].nil?
108
126
  end
109
127
 
@@ -274,7 +292,16 @@ rvc_alias :kill
274
292
  rvc_alias :kill, :k
275
293
 
276
294
  def kill vms
277
- on_vms = vms.select { |x| x.summary.runtime.powerState == 'poweredOn' }
295
+ on_vms = []
296
+ vms.each do |vm|
297
+ begin
298
+ if vm.summary.runtime.powerState == 'poweredOn'
299
+ on_vms.push(vm)
300
+ end
301
+ rescue Exception => ex
302
+ puts "vm.rb:kill: Skipping current object #{vm} due to Exception: #{ex.message}"
303
+ end
304
+ end
278
305
  off on_vms unless on_vms.empty?
279
306
  shell.cmds.basic.destroy vms unless vms.empty?
280
307
  end
@@ -516,6 +543,7 @@ opts :clone do
516
543
  opt :host, "Host", :short => 'o', :type => :string, :lookup => VIM::HostSystem
517
544
  opt :template, "Create a template", :short => 't'
518
545
  opt :linked, "Create a linked clone", :short => 'l'
546
+ opt :customizationspecname, "Customization spec", :type => :string, :short => 's'
519
547
  opt :power_on, "Power on VM after clone"
520
548
  end
521
549
 
@@ -528,9 +556,19 @@ def clone src, dst, opts
528
556
  diskMoveType = :moveChildMostDiskBacking
529
557
  end
530
558
 
559
+ if opts[:customizationspecname]
560
+ begin
561
+ spec = src._connection.serviceContent.customizationSpecManager.GetCustomizationSpec(:name => opts[:customizationspecname]).spec
562
+ rescue
563
+ print "Customization Spec '#{opts[:customizationspecname]}' not found\n"
564
+ return
565
+ end
566
+ end
567
+
531
568
  task = src.CloneVM_Task(:folder => folder,
532
569
  :name => name,
533
570
  :spec => {
571
+ :customization => spec,
534
572
  :location => {
535
573
  :diskMoveType => diskMoveType,
536
574
  :host => opts[:host],
@@ -574,15 +612,26 @@ def annotate vm, str
574
612
  vm.ReconfigVM_Task(:spec => { :annotation => str }).wait_for_completion
575
613
  end
576
614
 
577
-
578
615
  opts :modify_cpu do
579
616
  summary "Change CPU configuration"
580
617
  arg :vm, nil, :lookup => VIM::VirtualMachine
581
618
  opt :num, "New number of CPUs", :type => :int, :required => true
619
+ opt :reservation, "Reservation in Mhz", :type => :int
620
+ opt :limit, "Limit in Mhz. -1 means no limit", :type => :int
582
621
  end
583
622
 
584
623
  def modify_cpu vm, opts
585
- spec = { :numCPUs => opts[:num] }
624
+ alloc, = vm.collect 'config.cpuAllocation'
625
+ if opts[:reservation]
626
+ alloc.reservation = opts[:reservation]
627
+ end
628
+ if opts[:limit]
629
+ alloc.limit = opts[:limit]
630
+ end
631
+ spec = {
632
+ :numCPUs => opts[:num],
633
+ :cpuAllocation => alloc,
634
+ }
586
635
  tasks [vm], :ReconfigVM, :spec => spec
587
636
  end
588
637
 
@@ -591,12 +640,26 @@ opts :modify_memory do
591
640
  summary "Change memory configuration"
592
641
  arg :vm, nil, :lookup => VIM::VirtualMachine
593
642
  opt :size, "New memory size in MB", :type => :int, :required => true
643
+ opt :reservation, "Reservation in MB", :type => :int
644
+ opt :limit, "Limit in MB. -1 means no limit", :type => :int
594
645
  end
595
646
 
596
647
  def modify_memory vm, opts
597
- err "VM needs to be off" unless vm.summary.runtime.powerState == 'poweredOff'
598
648
  err "memory must be a multiple of 4MB" unless ( opts[:size] % 4 ) == 0
599
- spec = { :memoryMB => opts[:size] }
649
+
650
+ #alloc, = vm.collect 'config.memoryAllocation'
651
+ alloc = VIM::ResourceAllocationInfo()
652
+ if opts[:reservation]
653
+ alloc.reservation = opts[:reservation]
654
+ end
655
+ if opts[:limit]
656
+ alloc.limit = opts[:limit]
657
+ end
658
+
659
+ spec = {
660
+ :memoryMB => opts[:size],
661
+ :memoryAllocation => alloc,
662
+ }
600
663
  tasks [vm], :ReconfigVM, :spec => spec
601
664
  end
602
665
 
@@ -630,7 +693,7 @@ def vm_ip vm
630
693
 
631
694
  ip = if summary.guest.ipAddress and summary.guest.ipAddress != '127.0.0.1'
632
695
  summary.guest.ipAddress
633
- elsif note = YAML.load(summary.config.annotation) and note.is_a? Hash and note.member? 'ip'
696
+ elsif note = YAML.load(summary.config.annotation) and note.is_a? Hash and note['ip']
634
697
  note['ip']
635
698
  else
636
699
  err "no IP known for this VM"
@@ -1,30 +1,54 @@
1
+ # Copyright (c) 2013 VMware, Inc. All Rights Reserved.
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require 'rvc/vim'
22
+
1
23
  opts :authenticate do
2
24
  summary "Authenticate within guest"
3
- arg :vm, nil, :lookup => VIM::VirtualMachine
25
+ arg :vm, nil, :lookup => VIM::VirtualMachine, :multi => true
4
26
  opt :interactive_session, "Allow command to interact with desktop", :default => false, :type => :bool
5
27
  opt :password, "Password in guest", :type => :string
6
28
  opt :username, "Username in guest", :default => "root", :type => :string
7
29
  end
8
30
 
9
- def authenticate vm, opts
10
- auth = ((@auths ||= {})[vm] ||= {})[opts[:username]]
11
-
12
- if opts[:password].nil? or opts[:password].empty?
13
- opts[:password] = ask("password: ") { |q| q.echo = false }
14
- end
15
-
16
- auth = VIM.NamePasswordAuthentication(
17
- :username => opts[:username],
18
- :password => opts[:password],
19
- :interactiveSession => opts[:interactive_session]
20
- )
21
-
22
- @auths[vm][opts[:username]] = auth
23
- begin
24
- check_auth vm, opts
25
- rescue
26
- clear_auth vm, opts
27
- err "Could not authenticate: #{$!}"
31
+ def authenticate vms, opts
32
+ vms.each do |vm|
33
+ auth = ((@auths ||= {})[vm] ||= {})[opts[:username]]
34
+
35
+ if opts[:password].nil? or opts[:password].empty?
36
+ opts[:password] = ask("password: ") { |q| q.echo = false }
37
+ end
38
+
39
+ auth = VIM.NamePasswordAuthentication(
40
+ :username => opts[:username],
41
+ :password => opts[:password],
42
+ :interactiveSession => opts[:interactive_session]
43
+ )
44
+
45
+ @auths[vm][opts[:username]] = auth
46
+ begin
47
+ check_auth vm, opts
48
+ rescue
49
+ clear_auth vm, opts
50
+ err "Could not authenticate: #{$!}"
51
+ end
28
52
  end
29
53
  end
30
54
 
@@ -240,6 +264,67 @@ def rmfile vm, opts
240
264
  )
241
265
  end
242
266
 
267
+ def generic_http_download uri, local_path
268
+ http = Net::HTTP.new(uri.host, uri.port)
269
+ http.use_ssl = true
270
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
271
+ #http.set_debug_output $stderr
272
+ http.start
273
+
274
+ # headers = { 'cookie' => connection.cookie }
275
+ headers = {}
276
+ http_path = "#{uri.path}?#{uri.query}"
277
+ http.request_get(http_path, headers) do |res|
278
+ case res
279
+ when Net::HTTPOK
280
+ len = res.content_length
281
+ count = 0
282
+ File.open(local_path, 'wb') do |io|
283
+ res.read_body do |segment|
284
+ count += segment.length
285
+ io.write segment
286
+ $stdout.write "\e[0G\e[Kdownloading #{count}/#{len} bytes (#{(count*100)/len}%)"
287
+ $stdout.flush
288
+ end
289
+ end
290
+ $stdout.puts
291
+ else
292
+ err "download failed: #{res.message}"
293
+ end
294
+ end
295
+ end
296
+
297
+ def generic_http_upload local_path, uri, size = nil
298
+ http = Net::HTTP.new(uri.host, uri.port)
299
+ http.use_ssl = true
300
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
301
+ #http.set_debug_output $stderr
302
+ http.start
303
+
304
+
305
+ open(local_path, 'rb') do |io|
306
+ stream = ProgressStream.new(io, size || io.stat.size) do |s|
307
+ $stdout.write "\e[0G\e[Kuploading #{s.count}/#{s.len} bytes (#{(s.count*100)/s.len}%)"
308
+ $stdout.flush
309
+ end
310
+
311
+ headers = {
312
+ 'content-length' => (size || io.stat.size).to_s,
313
+ 'Content-Type' => 'application/octet-stream',
314
+ }
315
+ http_path = "#{uri.path}?#{uri.query}"
316
+
317
+ request = Net::HTTP::Put.new http_path, headers
318
+ request.body_stream = stream
319
+ res = http.request(request)
320
+ $stdout.puts
321
+ case res
322
+ when Net::HTTPOK
323
+ else
324
+ err "upload failed: #{res.message}"
325
+ end
326
+ end
327
+ end
243
328
 
244
329
  opts :download_file do
245
330
  summary "Download file from guest"
@@ -265,14 +350,14 @@ def download_file vm, opts
265
350
 
266
351
  download_uri = URI.parse(download_url.gsub /http(s?):\/\/\*:[0-9]*/, "")
267
352
  download_path = "#{download_uri.path}?#{download_uri.query}"
268
-
269
- http_download vm._connection, download_path, opts[:local_path]
353
+
354
+ generic_http_download download_uri, opts[:local_path]
270
355
  end
271
356
 
272
357
 
273
358
  opts :upload_file do
274
359
  summary "Upload file to guest"
275
- arg :vm, nil, :lookup => VIM::VirtualMachine
360
+ arg :vm, nil, :lookup => VIM::VirtualMachine, :multi => true
276
361
  opt :group_id, "Group ID of file", :type => :int
277
362
  opt :guest_path, "Path in guest to upload to", :required => true, :type => :string
278
363
  opt :local_path, "Local file to upload", :required => true, :type => :string
@@ -282,35 +367,45 @@ opts :upload_file do
282
367
  opt :username, "Username in guest", :default => "root", :type => :string
283
368
  end
284
369
 
285
- def upload_file vm, opts
286
- guestOperationsManager = vm._connection.serviceContent.guestOperationsManager
287
- err "This command requires vSphere 5 or greater" unless guestOperationsManager.respond_to? :fileManager
288
- fileManager = guestOperationsManager.fileManager
289
-
290
- opts[:permissions] = opts[:permissions].to_i(8) if opts[:permissions]
291
-
292
- auth = get_auth vm, opts
293
-
294
- file = File.new(opts[:local_path], 'rb')
295
-
296
- upload_url = fileManager.
297
- InitiateFileTransferToGuest(
298
- :vm => vm,
299
- :auth => auth,
300
- :guestFilePath => opts[:guest_path],
301
- :fileAttributes => VIM.GuestPosixFileAttributes(
302
- :groupId => opts[:group_id],
303
- :ownerId => opts[:owner_id],
304
- :permissions => opts[:permissions]
305
- ),
306
- :fileSize => file.size,
307
- :overwrite => opts[:overwrite]
308
- )
309
-
310
- upload_uri = URI.parse(upload_url.gsub /http(s?):\/\/\*:[0-9]*/, "")
311
- upload_path = "#{upload_uri.path}?#{upload_uri.query}"
312
-
313
- http_upload vm._connection, opts[:local_path], upload_path
370
+ def upload_file vms, opts
371
+ vms.each do |vm|
372
+ guestOperationsManager = vm._connection.serviceContent.guestOperationsManager
373
+ err "This command requires vSphere 5 or greater" unless guestOperationsManager.respond_to? :fileManager
374
+ fileManager = guestOperationsManager.fileManager
375
+
376
+ opts[:permissions] = opts[:permissions].to_i(8) if opts[:permissions]
377
+
378
+ auth = get_auth vm, opts
379
+
380
+ file_size = nil
381
+ if opts[:local_path] =~ /^http:/
382
+ # XXX: Not acceptable for big files
383
+ file_size = open(opts[:local_path], 'rb').read.length
384
+ else
385
+ err "local file does not exist" unless File.exists? local_path
386
+ file = File.new(opts[:local_path], 'rb')
387
+ file_size = file.size
388
+ end
389
+
390
+ upload_url = fileManager.
391
+ InitiateFileTransferToGuest(
392
+ :vm => vm,
393
+ :auth => auth,
394
+ :guestFilePath => opts[:guest_path],
395
+ :fileAttributes => VIM.GuestPosixFileAttributes(
396
+ :groupId => opts[:group_id],
397
+ :ownerId => opts[:owner_id],
398
+ :permissions => opts[:permissions]
399
+ ),
400
+ :fileSize => file_size,
401
+ :overwrite => opts[:overwrite]
402
+ )
403
+
404
+ upload_uri = URI.parse(upload_url.gsub /http(s?):\/\/\*:[0-9]*/, "")
405
+ upload_path = "#{upload_uri.path}?#{upload_uri.query}"
406
+
407
+ generic_http_upload opts[:local_path], upload_uri, file_size
408
+ end
314
409
  end
315
410
 
316
411
 
@@ -431,7 +526,7 @@ end
431
526
  # Process commands
432
527
  opts :start_program do
433
528
  summary "Run program in guest"
434
- arg :vm, nil, :lookup => VIM::VirtualMachine
529
+ arg :vm, nil, :lookup => VIM::VirtualMachine, :multi => true
435
530
  opt :arguments, "Arguments of command", :default => "", :type => :string
436
531
  opt :background, "Don't wait for process to finish", :default => false, :type => :bool
437
532
  opt :delay, "Interval in seconds", :type => :float, :default => 5.0
@@ -444,47 +539,51 @@ opts :start_program do
444
539
  conflicts :background, :delay
445
540
  end
446
541
 
447
- def start_program vm, opts
448
- guestOperationsManager = vm._connection.serviceContent.guestOperationsManager
449
- err "This command requires vSphere 5 or greater" unless guestOperationsManager.respond_to? :processManager
450
- processManager = guestOperationsManager.processManager
451
-
452
- auth = get_auth vm, opts
453
-
454
- pid = processManager.
455
- StartProgramInGuest(
456
- :vm => vm,
457
- :auth => auth,
458
- :spec => VIM.GuestProgramSpec(
459
- :arguments => opts[:arguments],
460
- :programPath => opts[:program_path],
461
- :envVariables => opts[:env],
462
- :workingDirectory => opts[:working_directory]
463
- )
464
- )
465
-
466
- Timeout.timeout opts[:timeout] do
467
- while true
468
- processes = processManager.
469
- ListProcessesInGuest(
470
- :vm => vm,
471
- :auth => auth,
472
- :pids => [pid]
542
+ def start_program vms, opts
543
+ vms.each do |vm|
544
+ guestOperationsManager = vm._connection.serviceContent.guestOperationsManager
545
+ err "This command requires vSphere 5 or greater" unless guestOperationsManager.respond_to? :processManager
546
+ processManager = guestOperationsManager.processManager
547
+
548
+ auth = get_auth vm, opts
549
+
550
+ pid = processManager.
551
+ StartProgramInGuest(
552
+ :vm => vm,
553
+ :auth => auth,
554
+ :spec => VIM.GuestProgramSpec(
555
+ :arguments => opts[:arguments],
556
+ :programPath => opts[:program_path],
557
+ :envVariables => opts[:env],
558
+ :workingDirectory => opts[:working_directory]
473
559
  )
474
- process = processes.first
475
-
476
- if !process.endTime.nil?
477
- if process.exitCode != 0
478
- err "Process failed with exit code #{process.exitCode}"
560
+ )
561
+
562
+ begin
563
+ Timeout.timeout opts[:timeout] do
564
+ while true
565
+ processes = processManager.
566
+ ListProcessesInGuest(
567
+ :vm => vm,
568
+ :auth => auth,
569
+ :pids => [pid]
570
+ )
571
+ process = processes.first
572
+
573
+ if !process.endTime.nil?
574
+ if process.exitCode != 0
575
+ err "Process failed with exit code #{process.exitCode}"
576
+ end
577
+ break
578
+ elsif opts[:background]
579
+ break
580
+ end
581
+
582
+ sleep opts[:delay]
479
583
  end
480
- break
481
- elsif opts[:background]
482
- break
483
584
  end
484
-
485
- sleep opts[:delay]
585
+ rescue Timeout::Error
586
+ err "Timed out waiting for process to finish."
486
587
  end
487
588
  end
488
- rescue Timeout::Error
489
- err "Timed out waiting for process to finish."
490
589
  end