rvc 1.7.0 → 1.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +2 -2
- data/VERSION +1 -1
- data/lib/rvc/completion.rb +1 -1
- data/lib/rvc/extensions/ClusterComputeResource.rb +11 -0
- data/lib/rvc/extensions/ComputeResource.rb +5 -1
- data/lib/rvc/extensions/Datacenter.rb +1 -1
- data/lib/rvc/extensions/HostSystem.rb +15 -0
- data/lib/rvc/extensions/VirtualMachine.rb +7 -2
- data/lib/rvc/fs.rb +2 -2
- data/lib/rvc/inventory.rb +9 -1
- data/lib/rvc/modules/basic.rb +4 -1
- data/lib/rvc/modules/cluster.rb +99 -38
- data/lib/rvc/modules/connection.rb +2 -0
- data/lib/rvc/modules/device.rb +37 -1
- data/lib/rvc/modules/diagnostics.rb +119 -11
- data/lib/rvc/modules/find.rb +1 -1
- data/lib/rvc/modules/host.rb +116 -4
- data/lib/rvc/modules/perf.rb +53 -7
- data/lib/rvc/modules/snapshot.rb +7 -1
- data/lib/rvc/modules/spbm.rb +728 -0
- data/lib/rvc/modules/syslog.rb +103 -0
- data/lib/rvc/modules/vds.rb +59 -2
- data/lib/rvc/modules/vim.rb +1 -1
- data/lib/rvc/modules/vm.rb +70 -7
- data/lib/rvc/modules/vm_guest.rb +190 -91
- data/lib/rvc/modules/vnc.rb +29 -5
- data/lib/rvc/modules/vsan.rb +4105 -0
- data/lib/rvc/util.rb +31 -11
- metadata +7 -3
@@ -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
|
data/lib/rvc/modules/vds.rb
CHANGED
@@ -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 :
|
639
|
+
arg :hosts, nil, :lookup => VIM::HostSystem, :multi => true
|
640
640
|
end
|
641
641
|
|
642
|
-
def create_vmknic portgroup, hosts
|
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
|
data/lib/rvc/modules/vim.rb
CHANGED
data/lib/rvc/modules/vm.rb
CHANGED
@@ -103,7 +103,25 @@ opts :shutdown_guest do
|
|
103
103
|
end
|
104
104
|
|
105
105
|
def shutdown_guest vms, opts
|
106
|
-
vms.
|
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 =
|
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
|
-
|
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
|
-
|
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
|
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"
|
data/lib/rvc/modules/vm_guest.rb
CHANGED
@@ -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
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
opts[:password]
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
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
|
-
|
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
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
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
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
:
|
460
|
-
|
461
|
-
|
462
|
-
|
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
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
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
|
-
|
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
|