knife-vsphere 1.2.14 → 1.2.16
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/chef/knife/base_vsphere_command.rb +7 -2
- data/lib/chef/knife/vsphere_vm_clone.rb +145 -142
- data/lib/chef/knife/vsphere_vm_find.rb +118 -125
- data/lib/chef/knife/vsphere_vm_state.rb +6 -10
- data/lib/knife-vsphere/version.rb +1 -1
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ffde4a9240d703c519b66c4d7bddf1a26b5fdc2d
|
4
|
+
data.tar.gz: df28bd7b23377431778c93d79a6d04ec358dccbc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bf661f541572092f3a3746cc130ac5d04c7341cffee903e96118aa3ae813f5be37654d6effb654bb39de782ebb64e9a98ba2a59542dd8bf8912b026a2f385680
|
7
|
+
data.tar.gz: 7e641d48b040865eace2e255c29f149b2ebc66dcbf1d3af9509a63c230ca202a73bd933dcd5bd5561591eeaf428e7757341b01f608482181e43bc6b864ff2e34
|
@@ -9,6 +9,10 @@ require 'rbvmomi'
|
|
9
9
|
require 'base64'
|
10
10
|
require 'filesize'
|
11
11
|
|
12
|
+
PS_ON ||= 'poweredOn'.freeze
|
13
|
+
PS_OFF ||= 'poweredOff'.freeze
|
14
|
+
PS_SUSPENDED ||= 'suspended'.freeze
|
15
|
+
|
12
16
|
# Base class for vsphere knife commands
|
13
17
|
class Chef
|
14
18
|
class Knife
|
@@ -110,7 +114,7 @@ class Chef
|
|
110
114
|
end
|
111
115
|
|
112
116
|
def vim_connection
|
113
|
-
config[:vim]
|
117
|
+
config[:vim] ||= RbVmomi::VIM.connect conn_opts
|
114
118
|
end
|
115
119
|
|
116
120
|
def get_password_from_stdin
|
@@ -183,7 +187,7 @@ class Chef
|
|
183
187
|
|
184
188
|
def datacenter
|
185
189
|
dcname = get_config(:vsphere_dc)
|
186
|
-
traverse_folders_for_dc(
|
190
|
+
traverse_folders_for_dc(vim_connection.rootFolder, dcname) || abort('datacenter not found')
|
187
191
|
end
|
188
192
|
|
189
193
|
def find_folder(folderName)
|
@@ -373,6 +377,7 @@ class Chef
|
|
373
377
|
def linux?(config)
|
374
378
|
gid = config.guestId.downcase
|
375
379
|
# This makes the assumption that if it isn't mac or windows it's linux
|
380
|
+
# See https://www.vmware.com/support/developer/vc-sdk/visdk25pubs/ReferenceGuide/vim.vm.GuestOsDescriptor.GuestOsIdentifier.html for values
|
376
381
|
is_linux_bool = !gid.include?('windows') && !gid.include?('darwin')
|
377
382
|
Chef::Log.debug('Identified os as linux.') if is_linux_bool
|
378
383
|
is_linux_bool
|
@@ -16,14 +16,12 @@ require 'chef/knife/winrm_base'
|
|
16
16
|
require 'chef/knife/customization_helper'
|
17
17
|
require 'ipaddr'
|
18
18
|
|
19
|
-
# Clone an existing template into a new VM, optionally applying a customization specification.
|
20
|
-
# usage:
|
21
|
-
# knife vsphere vm clone NewNode UbuntuTemplate --cspec StaticSpec \
|
22
|
-
# --cips 192.168.0.99/24,192.168.1.99/24 \
|
23
|
-
# --chostname NODENAME --cdomain NODEDOMAIN
|
24
19
|
class Chef::Knife::VsphereVmClone < Chef::Knife::BaseVsphereCommand
|
25
20
|
banner 'knife vsphere vm clone VMNAME (options)'
|
26
21
|
|
22
|
+
AUTO_MAC = 'auto'
|
23
|
+
NO_IPS = ''
|
24
|
+
|
27
25
|
include Chef::Knife::WinrmBase
|
28
26
|
include CustomizationHelper
|
29
27
|
deps do
|
@@ -91,11 +89,12 @@ class Chef::Knife::VsphereVmClone < Chef::Knife::BaseVsphereCommand
|
|
91
89
|
option :customization_macs,
|
92
90
|
long: '--cmacs CUST_MACS',
|
93
91
|
description: 'Comma-delimited list of MAC addresses for network adapters',
|
94
|
-
default:
|
92
|
+
default: AUTO_MAC
|
95
93
|
|
96
94
|
option :customization_ips,
|
97
95
|
long: '--cips CUST_IPS',
|
98
|
-
description: 'Comma-delimited list of CIDR IPs for customization'
|
96
|
+
description: 'Comma-delimited list of CIDR IPs for customization',
|
97
|
+
default: NO_IPS
|
99
98
|
|
100
99
|
option :customization_dns_ips,
|
101
100
|
long: '--cdnsips CUST_DNS_IPS',
|
@@ -147,6 +146,10 @@ class Chef::Knife::VsphereVmClone < Chef::Knife::BaseVsphereCommand
|
|
147
146
|
long: '--fqdn SERVER_FQDN',
|
148
147
|
description: 'Fully qualified hostname for bootstrapping'
|
149
148
|
|
149
|
+
option :bootstrap_msi_url,
|
150
|
+
long: '--bootstrap-msi-url URL',
|
151
|
+
description: 'Location of the Chef Client MSI. The default templates will prefer to download from this location.'
|
152
|
+
|
150
153
|
option :bootstrap_protocol,
|
151
154
|
long: '--bootstrap-protocol protocol',
|
152
155
|
description: 'Protocol to bootstrap windows servers. options: winrm/ssh',
|
@@ -311,6 +314,16 @@ class Chef::Knife::VsphereVmClone < Chef::Knife::BaseVsphereCommand
|
|
311
314
|
fatal_exit('You must specify a virtual machine name OR use --random-vmname')
|
312
315
|
end
|
313
316
|
|
317
|
+
abort '--template or knife[:source_vm] must be specified' unless config[:source_vm]
|
318
|
+
|
319
|
+
if get_config(:datastore) && get_config(:datastorecluster)
|
320
|
+
abort 'Please select either datastore or datastorecluster'
|
321
|
+
end
|
322
|
+
|
323
|
+
if get_config(:customization_macs) != AUTO_MAC && get_config(:customization_ips) == NO_IPS
|
324
|
+
abort('Must specify IP numbers with --cips when specifying MAC addresses with --cmacs, can use "dhcp" as placeholder')
|
325
|
+
end
|
326
|
+
|
314
327
|
config[:chef_node_name] = vmname unless get_config(:chef_node_name)
|
315
328
|
config[:vmname] = vmname
|
316
329
|
|
@@ -321,8 +334,6 @@ class Chef::Knife::VsphereVmClone < Chef::Knife::BaseVsphereCommand
|
|
321
334
|
|
322
335
|
src_folder = find_folder(get_config(:folder)) || dc.vmFolder
|
323
336
|
|
324
|
-
abort '--template or knife[:source_vm] must be specified' unless config[:source_vm]
|
325
|
-
|
326
337
|
src_vm = find_in_folder(src_folder, RbVmomi::VIM::VirtualMachine, config[:source_vm]) ||
|
327
338
|
abort('VM/Template not found')
|
328
339
|
|
@@ -395,19 +406,18 @@ class Chef::Knife::VsphereVmClone < Chef::Knife::BaseVsphereCommand
|
|
395
406
|
puts 'Waiting for network interfaces to become available...'
|
396
407
|
sleep 2 while vm.guest.net.empty? || !vm.guest.ipAddress
|
397
408
|
ui.info "Found address #{vm.guest.ipAddress}" if log_verbose?
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
end
|
409
|
+
config[:fqdn] = if config[:bootstrap_ipv4]
|
410
|
+
ipv4_address(vm)
|
411
|
+
elsif config[:fqdn]
|
412
|
+
get_config(:fqdn)
|
413
|
+
else
|
414
|
+
# Use the first IP which is not a link-local address.
|
415
|
+
# This is the closest thing to vm.guest.ipAddress but
|
416
|
+
# allows specifying a NIC.
|
417
|
+
vm.guest.net[bootstrap_nic_index].ipConfig.ipAddress.detect do |addr|
|
418
|
+
addr.origin != 'linklayer'
|
419
|
+
end.ipAddress
|
420
|
+
end
|
411
421
|
end
|
412
422
|
|
413
423
|
def wait_for_access(connect_host, connect_port, protocol)
|
@@ -417,7 +427,7 @@ class Chef::Knife::VsphereVmClone < Chef::Knife::BaseVsphereCommand
|
|
417
427
|
config[:winrm_port] = '5986'
|
418
428
|
end
|
419
429
|
connect_port = get_config(:winrm_port)
|
420
|
-
print "\n#{ui.color("Waiting for winrm access to become available on #{connect_host}:#{connect_port}"
|
430
|
+
print "\n#{ui.color("Waiting for winrm access to become available on #{connect_host}:#{connect_port}", :magenta)}"
|
421
431
|
print('.') until tcp_test_winrm(connect_host, connect_port) do
|
422
432
|
sleep 10
|
423
433
|
puts('done')
|
@@ -456,51 +466,47 @@ class Chef::Knife::VsphereVmClone < Chef::Knife::BaseVsphereCommand
|
|
456
466
|
end
|
457
467
|
end
|
458
468
|
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
hosts
|
467
|
-
|
468
|
-
hosts.reject!(&:nil?)
|
469
|
-
hosts.reject! { |host| host.host.all? { |h| h.runtime.inMaintenanceMode } }
|
470
|
-
fatal_exit 'All hosts in maintenance mode!' if hosts.empty?
|
471
|
-
|
472
|
-
if get_config(:datastore)
|
473
|
-
hosts.reject! { |host| !host.datastore.include?(find_datastore(get_config(:datastore))) }
|
474
|
-
end
|
469
|
+
def find_available_hosts
|
470
|
+
hosts = traverse_folders_for_computeresources(datacenter.hostFolder)
|
471
|
+
fatal_exit('No ComputeResource found - Use --resource-pool to specify a resource pool or a cluster') if hosts.empty?
|
472
|
+
hosts.reject!(&:nil?)
|
473
|
+
hosts.reject! { |host| host.host.all? { |h| h.runtime.inMaintenanceMode } }
|
474
|
+
fatal_exit 'All hosts in maintenance mode!' if hosts.empty?
|
475
|
+
if get_config(:datastore)
|
476
|
+
hosts.reject! { |host| !host.datastore.include?(find_datastore(get_config(:datastore))) }
|
477
|
+
end
|
475
478
|
|
476
|
-
|
479
|
+
fatal_exit "No hosts have the requested Datastore available! #{get_config(:datastore)}" if hosts.empty?
|
477
480
|
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
+
if get_config(:datastorecluster)
|
482
|
+
hosts.reject! { |host| !host.datastore.include?(find_datastorecluster(get_config(:datastorecluster))) }
|
483
|
+
end
|
481
484
|
|
482
|
-
|
485
|
+
fatal_exit "No hosts have the requested DatastoreCluster available! #{get_config(:datastorecluster)}" if hosts.empty?
|
483
486
|
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
end
|
487
|
+
if get_config(:customization_vlan)
|
488
|
+
vlan_list = get_config(:customization_vlan).split(',')
|
489
|
+
vlan_list.each do |network|
|
490
|
+
hosts.reject! { |host| !host.network.include?(find_network(network)) }
|
489
491
|
end
|
492
|
+
end
|
490
493
|
|
491
|
-
|
494
|
+
fatal_exit "No hosts have the requested Network available! #{get_config(:customization_vlan)}" if hosts.empty?
|
495
|
+
hosts
|
496
|
+
end
|
492
497
|
|
493
|
-
|
494
|
-
|
495
|
-
|
498
|
+
# Builds a CloneSpec
|
499
|
+
def generate_clone_spec(src_config)
|
500
|
+
rspec = RbVmomi::VIM.VirtualMachineRelocateSpec
|
496
501
|
|
497
|
-
if get_config(:
|
498
|
-
|
499
|
-
|
502
|
+
rspec.pool = if get_config(:resource_pool)
|
503
|
+
find_pool(get_config(:resource_pool))
|
504
|
+
else
|
505
|
+
hosts = find_available_hosts
|
506
|
+
hosts.first.resourcePool
|
507
|
+
end
|
500
508
|
|
501
|
-
|
502
|
-
abort 'Please select either datastore or datastorecluster'
|
503
|
-
end
|
509
|
+
rspec.diskMoveType = :moveChildMostDiskBacking if get_config(:linked_clone)
|
504
510
|
|
505
511
|
if get_config(:datastore)
|
506
512
|
rspec.datastore = find_datastore(get_config(:datastore))
|
@@ -516,9 +522,7 @@ class Chef::Knife::VsphereVmClone < Chef::Knife::BaseVsphereCommand
|
|
516
522
|
end
|
517
523
|
end
|
518
524
|
|
519
|
-
if get_config(:thin_provision)
|
520
|
-
rspec = RbVmomi::VIM.VirtualMachineRelocateSpec(transform: :sparse, pool: find_pool(get_config(:resource_pool)))
|
521
|
-
end
|
525
|
+
rspec.transform = :sparse if get_config(:thin_provision)
|
522
526
|
|
523
527
|
is_template = !get_config(:mark_as_template).nil?
|
524
528
|
clone_spec = RbVmomi::VIM.VirtualMachineCloneSpec(location: rspec, powerOn: false, template: is_template)
|
@@ -537,12 +541,8 @@ class Chef::Knife::VsphereVmClone < Chef::Knife::BaseVsphereCommand
|
|
537
541
|
clone_spec.config.memoryMB = Integer(get_config(:customization_memory)) * 1024
|
538
542
|
end
|
539
543
|
|
540
|
-
if get_config(:customization_macs)
|
541
|
-
|
542
|
-
end
|
543
|
-
|
544
|
-
mac_list = if get_config(:customization_macs) == 'auto'
|
545
|
-
['auto'] * get_config(:customization_ips).split(',').length
|
544
|
+
mac_list = if get_config(:customization_macs) == AUTO_MAC
|
545
|
+
[AUTO_MAC] * get_config(:customization_ips).split(',').length
|
546
546
|
else
|
547
547
|
get_config(:customization_macs).split(',')
|
548
548
|
end
|
@@ -581,21 +581,26 @@ class Chef::Knife::VsphereVmClone < Chef::Knife::BaseVsphereCommand
|
|
581
581
|
# not connected to a distibuted switch?
|
582
582
|
card.backing = RbVmomi::VIM::VirtualEthernetCardNetworkBackingInfo(network: network, deviceName: network.name)
|
583
583
|
end
|
584
|
-
card.macAddress = mac_list[index] if get_config(:customization_macs) && mac_list[index] !=
|
584
|
+
card.macAddress = mac_list[index] if get_config(:customization_macs) && mac_list[index] != AUTO_MAC
|
585
585
|
dev_spec = RbVmomi::VIM.VirtualDeviceConfigSpec(device: card, operation: 'edit')
|
586
586
|
clone_spec.config.deviceChange.push dev_spec
|
587
587
|
end
|
588
588
|
end
|
589
589
|
|
590
|
-
if get_config(:customization_spec)
|
591
|
-
|
592
|
-
|
590
|
+
cust_spec = if get_config(:customization_spec)
|
591
|
+
csi = find_customization(get_config(:customization_spec)) ||
|
592
|
+
fatal_exit("failed to find customization specification named #{get_config(:customization_spec)}")
|
593
593
|
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
594
|
+
csi.spec
|
595
|
+
else
|
596
|
+
global_ipset = RbVmomi::VIM.CustomizationGlobalIPSettings
|
597
|
+
identity_settings = RbVmomi::VIM.CustomizationIdentitySettings
|
598
|
+
RbVmomi::VIM.CustomizationSpec(globalIPSettings: global_ipset, identity: identity_settings)
|
599
|
+
end
|
600
|
+
|
601
|
+
if get_config(:disable_customization)
|
602
|
+
clone_spec.customization = get_config(:customization_spec) ? cust_spec : nil
|
603
|
+
return clone_spec
|
599
604
|
end
|
600
605
|
|
601
606
|
if get_config(:customization_dns_ips)
|
@@ -612,71 +617,67 @@ class Chef::Knife::VsphereVmClone < Chef::Knife::BaseVsphereCommand
|
|
612
617
|
}
|
613
618
|
end
|
614
619
|
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
else
|
671
|
-
ui.error('Customization only supports Linux and Windows currently.')
|
672
|
-
exit 1
|
673
|
-
end
|
620
|
+
# TODO: why does the domain matter?
|
621
|
+
use_ident = config[:customization_hostname] || get_config(:customization_domain) || cust_spec.identity.props.empty?
|
622
|
+
|
623
|
+
# TODO: How could we not take this? Only if the identity were empty, but that's statically defined as empty above
|
624
|
+
if use_ident
|
625
|
+
hostname = config[:customization_hostname] || config[:vmname]
|
626
|
+
|
627
|
+
if windows?(src_config)
|
628
|
+
# TODO: This is just copying itself onto itself, isn't it?
|
629
|
+
identification = RbVmomi::VIM.CustomizationIdentification(
|
630
|
+
joinWorkgroup: cust_spec.identity.identification.joinWorkgroup
|
631
|
+
)
|
632
|
+
license_file_print_data = RbVmomi::VIM.CustomizationLicenseFilePrintData(
|
633
|
+
autoMode: cust_spec.identity.licenseFilePrintData.autoMode
|
634
|
+
)
|
635
|
+
|
636
|
+
user_data = RbVmomi::VIM.CustomizationUserData(
|
637
|
+
fullName: cust_spec.identity.userData.fullName,
|
638
|
+
orgName: cust_spec.identity.userData.orgName,
|
639
|
+
productId: cust_spec.identity.userData.productId,
|
640
|
+
computerName: RbVmomi::VIM.CustomizationFixedName(name: hostname)
|
641
|
+
)
|
642
|
+
|
643
|
+
gui_unattended = RbVmomi::VIM.CustomizationGuiUnattended(
|
644
|
+
autoLogon: cust_spec.identity.guiUnattended.autoLogon,
|
645
|
+
autoLogonCount: cust_spec.identity.guiUnattended.autoLogonCount,
|
646
|
+
password: RbVmomi::VIM.CustomizationPassword(
|
647
|
+
plainText: cust_spec.identity.guiUnattended.password.plainText,
|
648
|
+
value: cust_spec.identity.guiUnattended.password.value
|
649
|
+
),
|
650
|
+
timeZone: cust_spec.identity.guiUnattended.timeZone
|
651
|
+
)
|
652
|
+
runonce = RbVmomi::VIM.CustomizationGuiRunOnce(
|
653
|
+
commandList: ['cust_spec.identity.guiUnattended.commandList']
|
654
|
+
)
|
655
|
+
ident = RbVmomi::VIM.CustomizationSysprep
|
656
|
+
ident.guiRunOnce = runonce
|
657
|
+
ident.guiUnattended = gui_unattended
|
658
|
+
ident.identification = identification
|
659
|
+
ident.licenseFilePrintData = license_file_print_data
|
660
|
+
ident.userData = user_data
|
661
|
+
cust_spec.identity = ident
|
662
|
+
elsif linux?(src_config)
|
663
|
+
ident = RbVmomi::VIM.CustomizationLinuxPrep
|
664
|
+
ident.hostName = RbVmomi::VIM.CustomizationFixedName(name: hostname)
|
665
|
+
|
666
|
+
ident.domain = if get_config(:customization_domain)
|
667
|
+
get_config(:customization_domain)
|
668
|
+
else
|
669
|
+
''
|
670
|
+
end
|
671
|
+
cust_spec.identity = ident
|
672
|
+
else
|
673
|
+
ui.error('Customization only supports Linux and Windows currently.')
|
674
|
+
exit 1
|
674
675
|
end
|
675
|
-
|
676
|
+
end
|
677
|
+
clone_spec.customization = cust_spec
|
676
678
|
|
677
|
-
|
678
|
-
|
679
|
-
end
|
679
|
+
if customization_plugin && customization_plugin.respond_to?(:customize_clone_spec)
|
680
|
+
clone_spec = customization_plugin.customize_clone_spec(src_config, clone_spec)
|
680
681
|
end
|
681
682
|
clone_spec
|
682
683
|
end
|
@@ -717,7 +718,7 @@ class Chef::Knife::VsphereVmClone < Chef::Knife::BaseVsphereCommand
|
|
717
718
|
# @param name [String] name of customization
|
718
719
|
# @return [RbVmomi::VIM::CustomizationSpecItem]
|
719
720
|
def find_customization(name)
|
720
|
-
csm =
|
721
|
+
csm = vim_connection.serviceContent.customizationSpecManager
|
721
722
|
csm.GetCustomizationSpec(name: name)
|
722
723
|
end
|
723
724
|
|
@@ -748,7 +749,7 @@ class Chef::Knife::VsphereVmClone < Chef::Knife::BaseVsphereCommand
|
|
748
749
|
end
|
749
750
|
|
750
751
|
adapter_map = RbVmomi::VIM.CustomizationAdapterMapping
|
751
|
-
adapter_map.macAddress = mac if !mac.nil? && (mac !=
|
752
|
+
adapter_map.macAddress = mac if !mac.nil? && (mac != AUTO_MAC)
|
752
753
|
adapter_map.adapter = settings
|
753
754
|
adapter_map
|
754
755
|
end
|
@@ -789,6 +790,7 @@ class Chef::Knife::VsphereVmClone < Chef::Knife::BaseVsphereCommand
|
|
789
790
|
ui.error('Unsupported Bootstrapping Protocol. Supports : winrm, ssh')
|
790
791
|
exit 1
|
791
792
|
end
|
793
|
+
bootstrap.config[:msi_url] = get_config(:bootstrap_msi_url)
|
792
794
|
bootstrap_common_params(bootstrap)
|
793
795
|
end
|
794
796
|
|
@@ -802,6 +804,7 @@ class Chef::Knife::VsphereVmClone < Chef::Knife::BaseVsphereCommand
|
|
802
804
|
bootstrap.config[:ssh_port] = get_config(:ssh_port)
|
803
805
|
bootstrap.config[:identity_file] = get_config(:identity_file)
|
804
806
|
bootstrap.config[:use_sudo] = true unless get_config(:ssh_user) == 'root'
|
807
|
+
bootstrap.config[:use_sudo_password] = true unless get_config(:ssh_user) == 'root'
|
805
808
|
bootstrap.config[:log_level] = get_config(:log_level)
|
806
809
|
bootstrap_common_params(bootstrap)
|
807
810
|
end
|
@@ -8,10 +8,6 @@ require 'chef/knife/base_vsphere_command'
|
|
8
8
|
require 'rbvmomi'
|
9
9
|
require 'netaddr'
|
10
10
|
|
11
|
-
PsOn = 'poweredOn'
|
12
|
-
PsOff = 'poweredOff'
|
13
|
-
PsSuspended = 'suspended'
|
14
|
-
|
15
11
|
# find vms belonging to pool that match criteria, display specified fields
|
16
12
|
class Chef::Knife::VsphereVmFind < Chef::Knife::BaseVsphereCommand
|
17
13
|
banner 'knife vsphere vm find'
|
@@ -95,22 +91,20 @@ class Chef::Knife::VsphereVmFind < Chef::Knife::BaseVsphereCommand
|
|
95
91
|
long: '--full-path',
|
96
92
|
description: 'Show full path'
|
97
93
|
|
98
|
-
$stdout.sync = true
|
94
|
+
$stdout.sync = true # smoother output from print
|
99
95
|
|
100
96
|
def traverse_folders_for_pool_clustercompute(folder, poolname)
|
101
|
-
# children = folder.children.find_all
|
102
97
|
children = find_all_in_folder(folder, RbVmomi::VIM::ManagedObject)
|
103
98
|
children.each do |child|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
end
|
110
|
-
if pool then return pool end
|
99
|
+
next unless child.class == RbVmomi::VIM::ClusterComputeResource || child.class == RbVmomi::VIM::ComputeResource || child.class == RbVmomi::VIM::ResourcePool
|
100
|
+
if child.name == poolname
|
101
|
+
return child
|
102
|
+
elsif child.class == RbVmomi::VIM::Folder || child.class == RbVmomi::VIM::ComputeResource || child.class == RbVmomi::VIM::ClusterComputeResource || child.class == RbVmomi::VIM::ResourcePool
|
103
|
+
pool = traverse_folders_for_pool_clustercompute(child, poolname)
|
111
104
|
end
|
105
|
+
return pool if pool
|
112
106
|
end
|
113
|
-
|
107
|
+
false
|
114
108
|
end
|
115
109
|
|
116
110
|
def run
|
@@ -120,125 +114,124 @@ class Chef::Knife::VsphereVmFind < Chef::Knife::BaseVsphereCommand
|
|
120
114
|
fatal_exit('You must specify a resource pool or cluster name (see knife vsphere pool list)')
|
121
115
|
end
|
122
116
|
|
123
|
-
|
117
|
+
vim_connection
|
124
118
|
dc = datacenter
|
125
119
|
folder = dc.hostFolder
|
126
120
|
|
127
|
-
pool = traverse_folders_for_pool_clustercompute(folder, poolname)
|
121
|
+
pool = traverse_folders_for_pool_clustercompute(folder, poolname) || abort("Pool #{poolname} not found")
|
128
122
|
|
129
|
-
if pool.class == RbVmomi::VIM::ResourcePool
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
end
|
134
|
-
|
135
|
-
unless vm.nil?
|
136
|
-
vm.each do |vmc|
|
137
|
-
state = case vmc.runtime.powerState
|
138
|
-
when PsOn
|
139
|
-
ui.color('on', :green)
|
140
|
-
when PsOff
|
141
|
-
ui.color('off', :red)
|
142
|
-
when PsSuspended
|
143
|
-
ui.color('suspended', :yellow)
|
144
|
-
end
|
145
|
-
|
146
|
-
if get_config(:matchname)
|
147
|
-
next unless vmc.name.include? config[:matchname]
|
148
|
-
end
|
149
|
-
|
150
|
-
if get_config(:matchtools)
|
151
|
-
next unless vmc.guest.toolsStatus == config[:matchtools]
|
152
|
-
end
|
153
|
-
|
154
|
-
next if get_config(:soff) && (vmc.runtime.powerState == PsOn)
|
155
|
-
|
156
|
-
next if get_config(:son) && (vmc.runtime.powerState == PsOff)
|
157
|
-
|
158
|
-
if get_config(:matchip)
|
159
|
-
if (!vmc.guest.ipAddress.nil? && vmc.guest.ipAddress != '')
|
160
|
-
next unless vmc.guest.ipAddress.include? config[:matchip]
|
161
|
-
else
|
162
|
-
next
|
123
|
+
vm = if pool.class == RbVmomi::VIM::ResourcePool
|
124
|
+
pool.vm
|
125
|
+
else
|
126
|
+
pool.resourcePool.vm
|
163
127
|
end
|
164
|
-
end
|
165
128
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
if get_config(:full_path)
|
178
|
-
actualname = ''
|
179
|
-
vmcp = vmc
|
180
|
-
while vmcp.parent != nil && vmcp.parent.name != 'vm'
|
181
|
-
actualname.concat("#{vmcp.parent.name}/")
|
182
|
-
vmcp = vmcp.parent
|
183
|
-
end
|
184
|
-
print "#{ui.color("Folder:", :cyan)}"
|
185
|
-
print "\""
|
186
|
-
print actualname.split('/').reverse().join('/')
|
187
|
-
print "\"\t"
|
188
|
-
|
189
|
-
else
|
190
|
-
print "#{ui.color("Folder", :cyan)}: #{vmc.parent.name}\t"
|
191
|
-
end
|
192
|
-
|
193
|
-
if get_config(:ip)
|
194
|
-
print "#{ui.color("IP:", :cyan)} #{vmc.guest.ipAddress}\t"
|
195
|
-
end
|
196
|
-
if get_config(:ips)
|
197
|
-
ipregex = /^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/
|
198
|
-
networks = vmc.guest.net.map { |net| "#{net.network}:" + net.ipConfig.ipAddress.select { |i| i.ipAddress[ipregex] }[0].ipAddress }
|
199
|
-
print "#{ui.color("IPS:", :cyan)} #{networks.join(",")}\t"
|
200
|
-
end
|
201
|
-
if get_config(:os)
|
202
|
-
print "#{ui.color("OS:", :cyan)} #{vmc.guest.guestFullName}\t"
|
203
|
-
end
|
204
|
-
if get_config(:ram)
|
205
|
-
print "#{ui.color("RAM:", :cyan)} #{vmc.summary.config.memorySizeMB}\t"
|
206
|
-
end
|
207
|
-
if get_config(:cpu)
|
208
|
-
print "#{ui.color("CPU:", :cyan)} #{vmc.summary.config.numCpu}\t"
|
209
|
-
end
|
210
|
-
if get_config(:alarms)
|
211
|
-
print "#{ui.color("Alarms:", :cyan)} #{vmc.summary.overallStatus}\t"
|
212
|
-
end
|
213
|
-
print "#{ui.color("State:", :cyan)} #{state}\t"
|
214
|
-
if get_config(:tools)
|
215
|
-
print "#{ui.color("Tools:", :cyan)} #{vmc.guest.toolsStatus}\t"
|
216
|
-
end
|
217
|
-
|
218
|
-
if get_config(:os_disk)
|
219
|
-
print "#{ui.color("OS Disks:", :cyan)}"
|
220
|
-
vmc.guest.disk.each do |disc|
|
221
|
-
print "#{disc.diskPath} #{disc.capacity / 1024 / 1024}MB Free:#{disc.freeSpace / 1024 / 1024}MB |"
|
222
|
-
end
|
223
|
-
end
|
129
|
+
return if vm.nil?
|
130
|
+
vm.each do |vmc|
|
131
|
+
state = case vmc.runtime.powerState
|
132
|
+
when PS_ON
|
133
|
+
ui.color('on', :green)
|
134
|
+
when PS_OFF
|
135
|
+
ui.color('off', :red)
|
136
|
+
when PS_SUSPENDED
|
137
|
+
ui.color('suspended', :yellow)
|
138
|
+
end
|
224
139
|
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
140
|
+
if get_config(:matchname)
|
141
|
+
next unless vmc.name.include? config[:matchname]
|
142
|
+
end
|
143
|
+
|
144
|
+
if get_config(:matchtools)
|
145
|
+
next unless vmc.guest.toolsStatus == config[:matchtools]
|
146
|
+
end
|
147
|
+
|
148
|
+
next if get_config(:soff) && (vmc.runtime.powerState == PS_ON)
|
149
|
+
|
150
|
+
next if get_config(:son) && (vmc.runtime.powerState == PS_OFF)
|
151
|
+
|
152
|
+
if get_config(:matchip)
|
153
|
+
if !vmc.guest.ipAddress.nil? && vmc.guest.ipAddress != ''
|
154
|
+
next unless vmc.guest.ipAddress.include? config[:matchip]
|
155
|
+
else
|
156
|
+
next
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
unless vmc.guest.guestFullName.nil?
|
161
|
+
if get_config(:matchos)
|
162
|
+
next unless vmc.guest.guestFullName.include? config[:matchos]
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
print "#{ui.color('VM Name:', :cyan)} #{vmc.name}\t"
|
167
|
+
if get_config(:hostname)
|
168
|
+
print "#{ui.color('Hostname:', :cyan)} #{vmc.guest.hostName}\t"
|
169
|
+
end
|
170
|
+
|
171
|
+
if get_config(:full_path)
|
172
|
+
actualname = ''
|
173
|
+
vmcp = vmc
|
174
|
+
while !vmcp.parent.nil? && vmcp.parent.name != 'vm'
|
175
|
+
actualname.concat("#{vmcp.parent.name}/")
|
176
|
+
vmcp = vmcp.parent
|
177
|
+
end
|
178
|
+
print ui.color('Folder:', :cyan)
|
179
|
+
print '"'
|
180
|
+
print actualname.split('/').reverse.join('/')
|
181
|
+
print "\"\t"
|
182
|
+
|
183
|
+
else
|
184
|
+
print "#{ui.color('Folder', :cyan)}: #{vmc.parent.name}\t"
|
185
|
+
end
|
186
|
+
|
187
|
+
if get_config(:ip)
|
188
|
+
print "#{ui.color('IP:', :cyan)} #{vmc.guest.ipAddress}\t"
|
189
|
+
end
|
190
|
+
if get_config(:ips)
|
191
|
+
ipregex = /^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/
|
192
|
+
networks = vmc.guest.net.map { |net| "#{net.network}:" + net.ipConfig.ipAddress.select { |i| i.ipAddress[ipregex] }[0].ipAddress }
|
193
|
+
print "#{ui.color('IPS:', :cyan)} #{networks.join(',')}\t"
|
194
|
+
end
|
195
|
+
if get_config(:os)
|
196
|
+
print "#{ui.color('OS:', :cyan)} #{vmc.guest.guestFullName}\t"
|
197
|
+
end
|
198
|
+
if get_config(:ram)
|
199
|
+
print "#{ui.color('RAM:', :cyan)} #{vmc.summary.config.memorySizeMB}\t"
|
200
|
+
end
|
201
|
+
if get_config(:cpu)
|
202
|
+
print "#{ui.color('CPU:', :cyan)} #{vmc.summary.config.numCpu}\t"
|
203
|
+
end
|
204
|
+
if get_config(:alarms)
|
205
|
+
print "#{ui.color('Alarms:', :cyan)} #{vmc.summary.overallStatus}\t"
|
206
|
+
end
|
207
|
+
print "#{ui.color('State:', :cyan)} #{state}\t"
|
208
|
+
if get_config(:tools)
|
209
|
+
print "#{ui.color('Tools:', :cyan)} #{vmc.guest.toolsStatus}\t"
|
210
|
+
end
|
211
|
+
|
212
|
+
if get_config(:os_disk)
|
213
|
+
print ui.color('OS Disks:', :cyan)
|
214
|
+
vmc.guest.disk.each do |disc|
|
215
|
+
print "#{disc.diskPath} #{disc.capacity / 1024 / 1024}MB Free:#{disc.freeSpace / 1024 / 1024}MB |"
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
if get_config(:esx_disk)
|
220
|
+
print ui.color('ESX Disks:', :cyan)
|
221
|
+
vmc.layout.disk.each do |dsc|
|
222
|
+
print "#{dsc.diskFile} | "
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
if get_config(:snapshots)
|
227
|
+
unless vmc.snapshot.nil?
|
228
|
+
print ui.color('Snapshots:', :cyan)
|
229
|
+
vmc.snapshot.rootSnapshotList.each do |snap|
|
230
|
+
print " #{snap.name}"
|
231
|
+
end
|
232
|
+
end
|
241
233
|
end
|
234
|
+
puts
|
242
235
|
end
|
243
236
|
end
|
244
237
|
end
|
@@ -8,18 +8,14 @@ require 'chef/knife/base_vsphere_command'
|
|
8
8
|
require 'rbvmomi'
|
9
9
|
require 'netaddr'
|
10
10
|
|
11
|
-
PS_ON = 'poweredOn'
|
12
|
-
PS_OFF = 'poweredOff'
|
13
|
-
PS_SUSPENDED = 'suspended'
|
14
|
-
|
15
|
-
POWER_STATES = {
|
16
|
-
PS_ON => 'powered on',
|
17
|
-
PS_OFF => 'powered off',
|
18
|
-
PS_SUSPENDED => 'suspended'
|
19
|
-
}
|
20
|
-
|
21
11
|
# Manage power state of a virtual machine
|
22
12
|
class Chef::Knife::VsphereVmState < Chef::Knife::BaseVsphereCommand
|
13
|
+
POWER_STATES = {
|
14
|
+
PS_ON => 'powered on',
|
15
|
+
PS_OFF => 'powered off',
|
16
|
+
PS_SUSPENDED => 'suspended'
|
17
|
+
}
|
18
|
+
|
23
19
|
banner 'knife vsphere vm state VMNAME (options)'
|
24
20
|
|
25
21
|
common_options
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: knife-vsphere
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2.
|
4
|
+
version: 1.2.16
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ezra Pagel
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-09-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: filesize
|
@@ -108,6 +108,20 @@ dependencies:
|
|
108
108
|
- - ">="
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: byebug
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
111
125
|
description: VMware vSphere Support for Chef's Knife Command
|
112
126
|
email: ezra@cpan.org
|
113
127
|
executables: []
|