knife-vsphere 1.2.14 → 1.2.16
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.
- 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: []
|