knife-vsphere 0.9.0 → 0.9.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/chef/knife/base_vsphere_command.rb +304 -0
- data/lib/chef/knife/vsphere_customization_list.rb +29 -29
- data/lib/chef/knife/vsphere_datastore_list.rb +59 -58
- data/lib/chef/knife/vsphere_pool_list.rb +45 -45
- data/lib/chef/knife/vsphere_template_list.rb +32 -32
- data/lib/chef/knife/vsphere_vlan_list.rb +38 -37
- data/lib/chef/knife/vsphere_vm_clone.rb +414 -414
- data/lib/chef/knife/vsphere_vm_config.rb +48 -0
- data/lib/chef/knife/vsphere_vm_delete.rb +39 -39
- data/lib/chef/knife/vsphere_vm_execute.rb +67 -68
- data/lib/chef/knife/vsphere_vm_list.rb +46 -46
- data/lib/chef/knife/vsphere_vm_query.rb +3 -4
- data/lib/chef/knife/vsphere_vm_snapshot.rb +56 -56
- data/lib/chef/knife/vsphere_vm_state.rb +46 -46
- data/lib/chef/knife/vsphere_vm_vmdk_add.rb +67 -67
- data/lib/knife-vsphere/version.rb +1 -1
- metadata +4 -3
- data/lib/chef/knife/BaseVsphereCommand.rb +0 -304
@@ -1,45 +1,45 @@
|
|
1
|
-
#
|
2
|
-
# Author:: Jesse Campbell (<hikeit@gmail.com>)
|
3
|
-
# License:: Apache License, Version 2.0
|
4
|
-
#
|
5
|
-
require 'chef/knife'
|
6
|
-
require 'chef/knife/
|
7
|
-
|
8
|
-
# Lists all known pools in the configured datacenter
|
9
|
-
class Chef::Knife::VspherePoolList < Chef::Knife::BaseVsphereCommand
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
end
|
1
|
+
#
|
2
|
+
# Author:: Jesse Campbell (<hikeit@gmail.com>)
|
3
|
+
# License:: Apache License, Version 2.0
|
4
|
+
#
|
5
|
+
require 'chef/knife'
|
6
|
+
require 'chef/knife/base_vsphere_command'
|
7
|
+
|
8
|
+
# Lists all known pools in the configured datacenter
|
9
|
+
class Chef::Knife::VspherePoolList < Chef::Knife::BaseVsphereCommand
|
10
|
+
|
11
|
+
banner "knife vsphere pool list"
|
12
|
+
|
13
|
+
get_common_options
|
14
|
+
|
15
|
+
def traverse_folders(folder)
|
16
|
+
puts "#{ui.color("#{folder.class}", :cyan)}: "+(folder.path[3..-1].map { |x| x[1] }.* '/')
|
17
|
+
folders = find_all_in_folder(folder, RbVmomi::VIM::ManagedObject)
|
18
|
+
unless folders.nil?
|
19
|
+
folders.each do |child|
|
20
|
+
traverse_folders(child)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def find_pool_folder(folderName)
|
26
|
+
dcname = get_config(:vsphere_dc)
|
27
|
+
dc = config[:vim].serviceInstance.find_datacenter(dcname) or abort "datacenter not found"
|
28
|
+
baseEntity = dc.hostFolder
|
29
|
+
entityArray = folderName.split('/')
|
30
|
+
entityArray.each do |entityArrItem|
|
31
|
+
if entityArrItem != ''
|
32
|
+
baseEntity = baseEntity.childEntity.grep(RbVmomi::VIM::ManagedObject).find { |f| f.name == entityArrItem } or
|
33
|
+
abort "no such folder #{folderName} while looking for #{entityArrItem}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
baseEntity
|
37
|
+
end
|
38
|
+
|
39
|
+
def run
|
40
|
+
$stdout.sync = true
|
41
|
+
vim = get_vim_connection
|
42
|
+
baseFolder = find_pool_folder(get_config(:folder));
|
43
|
+
traverse_folders(baseFolder)
|
44
|
+
end
|
45
|
+
end
|
@@ -1,32 +1,32 @@
|
|
1
|
-
#
|
2
|
-
# Author:: Ezra Pagel (<ezra@cpan.org>)
|
3
|
-
# License:: Apache License, Version 2.0
|
4
|
-
#
|
5
|
-
|
6
|
-
require 'chef/knife'
|
7
|
-
require 'chef/knife/
|
8
|
-
|
9
|
-
# Lists all known VM templates in the configured datacenter
|
10
|
-
class Chef::Knife::VsphereTemplateList < Chef::Knife::BaseVsphereCommand
|
11
|
-
|
12
|
-
banner "knife vsphere template list"
|
13
|
-
|
14
|
-
get_common_options
|
15
|
-
|
16
|
-
def run
|
17
|
-
|
18
|
-
$stdout.sync = true
|
19
|
-
$stderr.sync = true
|
20
|
-
|
21
|
-
vim = get_vim_connection
|
22
|
-
|
23
|
-
baseFolder = find_folder(get_config(:folder));
|
24
|
-
|
25
|
-
vms = find_all_in_folder(baseFolder, RbVmomi::VIM::VirtualMachine).
|
26
|
-
|
27
|
-
|
28
|
-
vms.each do |vm|
|
29
|
-
puts "#{ui.color("Template Name", :cyan)}: #{vm.name}"
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
1
|
+
#
|
2
|
+
# Author:: Ezra Pagel (<ezra@cpan.org>)
|
3
|
+
# License:: Apache License, Version 2.0
|
4
|
+
#
|
5
|
+
|
6
|
+
require 'chef/knife'
|
7
|
+
require 'chef/knife/base_vsphere_command'
|
8
|
+
|
9
|
+
# Lists all known VM templates in the configured datacenter
|
10
|
+
class Chef::Knife::VsphereTemplateList < Chef::Knife::BaseVsphereCommand
|
11
|
+
|
12
|
+
banner "knife vsphere template list"
|
13
|
+
|
14
|
+
get_common_options
|
15
|
+
|
16
|
+
def run
|
17
|
+
|
18
|
+
$stdout.sync = true
|
19
|
+
$stderr.sync = true
|
20
|
+
|
21
|
+
vim = get_vim_connection
|
22
|
+
|
23
|
+
baseFolder = find_folder(get_config(:folder));
|
24
|
+
|
25
|
+
vms = find_all_in_folder(baseFolder, RbVmomi::VIM::VirtualMachine).
|
26
|
+
select { |v| !v.config.nil? && v.config.template == true }
|
27
|
+
|
28
|
+
vms.each do |vm|
|
29
|
+
puts "#{ui.color("Template Name", :cyan)}: #{vm.name}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -1,37 +1,38 @@
|
|
1
|
-
# Author: Jesse Campbell
|
2
|
-
#
|
3
|
-
# Permission to use, copy, modify, and/or distribute this software for
|
4
|
-
# any purpose with or without fee is hereby granted, provided that the
|
5
|
-
# above copyright notice and this permission notice appear in all
|
6
|
-
# copies.
|
7
|
-
#
|
8
|
-
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
9
|
-
# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
10
|
-
# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
11
|
-
# AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
12
|
-
# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
|
13
|
-
# OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
14
|
-
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
15
|
-
# PERFORMANCE OF THIS SOFTWARE
|
16
|
-
|
17
|
-
require 'chef/knife'
|
18
|
-
require 'chef/knife/
|
19
|
-
|
20
|
-
# Lists all known data stores in datacenter with sizes
|
21
|
-
class Chef::Knife::VsphereVlanList < Chef::Knife::BaseVsphereCommand
|
22
|
-
|
23
|
-
banner "knife vsphere vlan list"
|
24
|
-
|
25
|
-
get_common_options
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
dc.
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
end
|
37
|
-
|
1
|
+
# Author: Jesse Campbell
|
2
|
+
#
|
3
|
+
# Permission to use, copy, modify, and/or distribute this software for
|
4
|
+
# any purpose with or without fee is hereby granted, provided that the
|
5
|
+
# above copyright notice and this permission notice appear in all
|
6
|
+
# copies.
|
7
|
+
#
|
8
|
+
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
9
|
+
# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
10
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
11
|
+
# AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
12
|
+
# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
|
13
|
+
# OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
14
|
+
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
15
|
+
# PERFORMANCE OF THIS SOFTWARE
|
16
|
+
|
17
|
+
require 'chef/knife'
|
18
|
+
require 'chef/knife/base_vsphere_command'
|
19
|
+
|
20
|
+
# Lists all known data stores in datacenter with sizes
|
21
|
+
class Chef::Knife::VsphereVlanList < Chef::Knife::BaseVsphereCommand
|
22
|
+
|
23
|
+
banner "knife vsphere vlan list"
|
24
|
+
|
25
|
+
get_common_options
|
26
|
+
|
27
|
+
def run
|
28
|
+
$stdout.sync = true
|
29
|
+
|
30
|
+
vim = get_vim_connection
|
31
|
+
dcname = get_config(:vsphere_dc)
|
32
|
+
dc = config[:vim].serviceInstance.find_datacenter(dcname) or abort "datacenter not found"
|
33
|
+
dc.network.each do |network|
|
34
|
+
puts "#{ui.color("VLAN", :cyan)}: #{network.name}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
@@ -7,7 +7,7 @@
|
|
7
7
|
#
|
8
8
|
|
9
9
|
require 'chef/knife'
|
10
|
-
require 'chef/knife/
|
10
|
+
require 'chef/knife/base_vsphere_command'
|
11
11
|
require 'rbvmomi'
|
12
12
|
require 'netaddr'
|
13
13
|
|
@@ -18,244 +18,244 @@ require 'netaddr'
|
|
18
18
|
# --chostname NODENAME --cdomain NODEDOMAIN
|
19
19
|
class Chef::Knife::VsphereVmClone < Chef::Knife::BaseVsphereCommand
|
20
20
|
|
21
|
-
|
21
|
+
banner "knife vsphere vm clone VMNAME (options)"
|
22
22
|
|
23
|
-
|
23
|
+
get_common_options
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
25
|
+
option :dest_folder,
|
26
|
+
:long => "--dest-folder FOLDER",
|
27
|
+
:description => "The folder into which to put the cloned VM"
|
28
28
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
29
|
+
option :datastore,
|
30
|
+
:long => "--datastore STORE",
|
31
|
+
:description => "The datastore into which to put the cloned VM"
|
32
|
+
|
33
|
+
option :resource_pool,
|
34
|
+
:long => "--resource-pool POOL",
|
35
|
+
:description => "The resource pool into which to put the cloned VM"
|
36
|
+
|
37
|
+
option :source_vm,
|
38
|
+
:long => "--template TEMPLATE",
|
39
|
+
:description => "The source VM / Template to clone from",
|
40
|
+
:required => true
|
41
|
+
|
42
|
+
option :annotation,
|
43
|
+
:long => "--annotation TEXT",
|
44
|
+
:description => "Add TEXT in Notes field from annotation"
|
45
|
+
|
46
|
+
option :customization_spec,
|
47
|
+
:long => "--cspec CUST_SPEC",
|
48
|
+
:description => "The name of any customization specification to apply"
|
49
|
+
|
50
|
+
option :customization_plugin,
|
51
|
+
:long => "--cplugin CUST_PLUGIN_PATH",
|
52
|
+
:description => "Path to plugin that implements KnifeVspherePlugin.customize_clone_spec and/or KnifeVspherePlugin.reconfig_vm"
|
53
|
+
|
54
|
+
option :customization_plugin_data,
|
55
|
+
:long => "--cplugin-data CUST_PLUGIN_DATA",
|
56
|
+
:description => "String of data to pass to the plugin. Use any format you wish."
|
57
|
+
|
58
|
+
option :customization_vlan,
|
59
|
+
:long => "--cvlan CUST_VLAN",
|
60
|
+
:description => "VLAN name for network adapter to join"
|
61
|
+
|
62
|
+
option :customization_ips,
|
63
|
+
:long => "--cips CUST_IPS",
|
64
|
+
:description => "Comma-delimited list of CIDR IPs for customization"
|
65
|
+
|
66
|
+
option :customization_dns_ips,
|
67
|
+
:long => "--cdnsips CUST_DNS_IPS",
|
68
|
+
:description => "Comma-delimited list of DNS IP addresses"
|
69
|
+
|
70
|
+
option :customization_dns_suffixes,
|
71
|
+
:long => "--cdnssuffix CUST_DNS_SUFFIXES",
|
72
|
+
:description => "Comma-delimited list of DNS search suffixes"
|
73
|
+
|
74
|
+
option :customization_gw,
|
75
|
+
:long => "--cgw CUST_GW",
|
76
|
+
:description => "CIDR IP of gateway for customization"
|
77
|
+
|
78
|
+
option :customization_hostname,
|
79
|
+
:long => "--chostname CUST_HOSTNAME",
|
80
|
+
:description => "Unqualified hostname for customization"
|
81
|
+
|
82
|
+
option :customization_domain,
|
83
|
+
:long => "--cdomain CUST_DOMAIN",
|
84
|
+
:description => "Domain name for customization"
|
85
|
+
|
86
|
+
option :customization_tz,
|
87
|
+
:long => "--ctz CUST_TIMEZONE",
|
88
|
+
:description => "Timezone invalid 'Area/Location' format"
|
89
|
+
|
90
|
+
option :customization_cpucount,
|
91
|
+
:long => "--ccpu CUST_CPU_COUNT",
|
92
|
+
:description => "Number of CPUs"
|
93
|
+
|
94
|
+
option :customization_memory,
|
95
|
+
:long => "--cram CUST_MEMORY_GB",
|
96
|
+
:description => "Gigabytes of RAM"
|
97
|
+
|
98
|
+
option :power,
|
99
|
+
:long => "--start",
|
100
|
+
:description => "Indicates whether to start the VM after a successful clone",
|
101
|
+
:boolean => false
|
102
|
+
|
103
|
+
option :bootstrap,
|
104
|
+
:long => "--bootstrap",
|
105
|
+
:description => "Indicates whether to bootstrap the VM",
|
106
|
+
:boolean => false
|
107
|
+
|
108
|
+
option :fqdn,
|
109
|
+
:long => "--fqdn SERVER_FQDN",
|
110
|
+
:description => "Fully qualified hostname for bootstrapping"
|
111
|
+
|
112
|
+
option :ssh_user,
|
113
|
+
:short => "-x USERNAME",
|
114
|
+
:long => "--ssh-user USERNAME",
|
115
|
+
:description => "The ssh username"
|
116
|
+
$default[:ssh_user] = "root"
|
117
|
+
|
118
|
+
option :ssh_password,
|
119
|
+
:short => "-P PASSWORD",
|
120
|
+
:long => "--ssh-password PASSWORD",
|
121
|
+
:description => "The ssh password"
|
122
|
+
|
123
|
+
option :ssh_port,
|
124
|
+
:short => "-p PORT",
|
125
|
+
:long => "--ssh-port PORT",
|
126
|
+
:description => "The ssh port"
|
127
|
+
$default[:ssh_port] = 22
|
128
|
+
|
129
|
+
option :identity_file,
|
130
|
+
:short => "-i IDENTITY_FILE",
|
131
|
+
:long => "--identity-file IDENTITY_FILE",
|
132
|
+
:description => "The SSH identity file used for authentication"
|
133
|
+
|
134
|
+
option :chef_node_name,
|
135
|
+
:short => "-N NAME",
|
136
|
+
:long => "--node-name NAME",
|
137
|
+
:description => "The Chef node name for your new node"
|
138
|
+
|
139
|
+
option :prerelease,
|
140
|
+
:long => "--prerelease",
|
141
|
+
:description => "Install the pre-release chef gems",
|
142
|
+
:boolean => false
|
143
|
+
|
144
|
+
option :bootstrap_version,
|
145
|
+
:long => "--bootstrap-version VERSION",
|
146
|
+
:description => "The version of Chef to install",
|
147
|
+
:proc => Proc.new { |v| Chef::Config[:knife][:bootstrap_version] = v }
|
148
|
+
|
149
|
+
option :bootstrap_proxy,
|
150
|
+
:long => "--bootstrap-proxy PROXY_URL",
|
151
|
+
:description => "The proxy server for the node being bootstrapped",
|
152
|
+
:proc => Proc.new { |p| Chef::Config[:knife][:bootstrap_proxy] = p }
|
153
|
+
|
154
|
+
option :distro,
|
155
|
+
:short => "-d DISTRO",
|
156
|
+
:long => "--distro DISTRO",
|
157
|
+
:description => "Bootstrap a distro using a template"
|
158
|
+
|
159
|
+
option :template_file,
|
160
|
+
:long => "--template-file TEMPLATE",
|
161
|
+
:description => "Full path to location of template to use"
|
162
162
|
|
163
163
|
option :run_list,
|
164
|
-
|
165
|
-
|
166
|
-
|
164
|
+
:short => "-r RUN_LIST",
|
165
|
+
:long => "--run-list RUN_LIST",
|
166
|
+
:description => "Comma separated list of roles/recipes to apply"
|
167
167
|
$default[:run_list] = ''
|
168
168
|
|
169
169
|
option :secret_file,
|
170
|
-
|
171
|
-
|
170
|
+
:long => "--secret-file SECRET_FILE",
|
171
|
+
:description => "A file containing the secret key to use to encrypt data bag item values"
|
172
172
|
$default[:secret_file] = ''
|
173
173
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
174
|
+
option :no_host_key_verify,
|
175
|
+
:long => "--no-host-key-verify",
|
176
|
+
:description => "Disable host key verification",
|
177
|
+
:boolean => true
|
178
178
|
|
179
179
|
option :first_boot_attributes,
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
180
|
+
:short => "-j JSON_ATTRIBS",
|
181
|
+
:long => "--json-attributes",
|
182
|
+
:description => "A JSON string to be added to the first run of chef-client",
|
183
|
+
:proc => lambda { |o| JSON.parse(o) },
|
184
|
+
:default => {}
|
185
|
+
|
186
|
+
option :disable_customization,
|
187
|
+
:long => "--disable-customization",
|
188
|
+
:description => "Disable default customization",
|
189
|
+
:boolean => true,
|
190
|
+
:default => false
|
191
191
|
|
192
192
|
option :log_level,
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
193
|
+
:short => "-l LEVEL",
|
194
|
+
:long => "--log_level",
|
195
|
+
:description => "Set the log level (debug, info, warn, error, fatal) for chef-client",
|
196
|
+
:proc => lambda { |l| l.to_sym }
|
197
|
+
|
198
|
+
def run
|
199
|
+
$stdout.sync = true
|
200
|
+
|
201
|
+
vmname = @name_args[0]
|
202
|
+
if vmname.nil?
|
203
|
+
show_usage
|
204
|
+
fatal_exit("You must specify a virtual machine name")
|
205
|
+
end
|
206
|
+
config[:chef_node_name] = vmname unless config[:chef_node_name]
|
207
|
+
config[:vmname] = vmname
|
208
208
|
|
209
|
-
|
210
|
-
|
211
|
-
|
209
|
+
if get_config(:bootstrap) && get_config(:distro) && !@@chef_config_dir
|
210
|
+
fatal_exit("Can't find .chef for bootstrap files. chdir to a location with a .chef directory and try again")
|
211
|
+
end
|
212
212
|
|
213
|
-
|
213
|
+
vim = get_vim_connection
|
214
214
|
|
215
215
|
dcname = get_config(:vsphere_dc)
|
216
216
|
dc = vim.serviceInstance.find_datacenter(dcname) or abort "datacenter not found"
|
217
217
|
|
218
|
-
|
218
|
+
src_folder = find_folder(get_config(:folder)) || dc.vmFolder
|
219
219
|
|
220
|
-
|
221
|
-
|
220
|
+
src_vm = find_in_folder(src_folder, RbVmomi::VIM::VirtualMachine, config[:source_vm]) or
|
221
|
+
abort "VM/Template not found"
|
222
222
|
|
223
|
-
|
223
|
+
clone_spec = generate_clone_spec(src_vm.config)
|
224
224
|
|
225
225
|
cust_folder = config[:dest_folder] || get_config(:folder)
|
226
226
|
|
227
|
-
|
227
|
+
dest_folder = cust_folder.nil? ? src_vm.vmFolder : find_folder(cust_folder)
|
228
228
|
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
229
|
+
task = src_vm.CloneVM_Task(:folder => dest_folder, :name => vmname, :spec => clone_spec)
|
230
|
+
puts "Cloning template #{config[:source_vm]} to new VM #{vmname}"
|
231
|
+
task.wait_for_completion
|
232
|
+
puts "Finished creating virtual machine #{vmname}"
|
233
233
|
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
234
|
+
if customization_plugin && customization_plugin.respond_to?(:reconfig_vm)
|
235
|
+
target_vm = find_in_folder(dest_folder, RbVmomi::VIM::VirtualMachine, vmname) or abort "VM could not be found in #{dest_folder}"
|
236
|
+
customization_plugin.reconfig_vm(target_vm)
|
237
|
+
end
|
238
238
|
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
239
|
+
if get_config(:power) || get_config(:bootstrap)
|
240
|
+
vm = find_in_folder(dest_folder, RbVmomi::VIM::VirtualMachine, vmname) or
|
241
|
+
fatal_exit("VM #{vmname} not found")
|
242
|
+
vm.PowerOnVM_Task.wait_for_completion
|
243
|
+
puts "Powered on virtual machine #{vmname}"
|
244
|
+
end
|
245
245
|
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
246
|
+
if get_config(:bootstrap)
|
247
|
+
sleep 2 until vm.guest.ipAddress
|
248
|
+
config[:fqdn] = vm.guest.ipAddress unless config[:fqdn]
|
249
|
+
print "Waiting for sshd..."
|
250
|
+
print "." until tcp_test_ssh(config[:fqdn])
|
251
|
+
puts "done"
|
252
252
|
|
253
|
-
|
254
|
-
|
255
|
-
|
253
|
+
bootstrap_for_node.run
|
254
|
+
end
|
255
|
+
end
|
256
256
|
|
257
|
-
|
258
|
-
|
257
|
+
# Builds a CloneSpec
|
258
|
+
def generate_clone_spec (src_config)
|
259
259
|
|
260
260
|
rspec = nil
|
261
261
|
if get_config(:resource_pool)
|
@@ -268,219 +268,219 @@ class Chef::Knife::VsphereVmClone < Chef::Knife::BaseVsphereCommand
|
|
268
268
|
rspec = RbVmomi::VIM.VirtualMachineRelocateSpec(:pool => rp)
|
269
269
|
end
|
270
270
|
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
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
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
271
|
+
if get_config(:datastore)
|
272
|
+
rspec.datastore = find_datastore(get_config(:datastore))
|
273
|
+
end
|
274
|
+
|
275
|
+
clone_spec = RbVmomi::VIM.VirtualMachineCloneSpec(:location => rspec,
|
276
|
+
:powerOn => false,
|
277
|
+
:template => false)
|
278
|
+
|
279
|
+
clone_spec.config = RbVmomi::VIM.VirtualMachineConfigSpec(:deviceChange => Array.new)
|
280
|
+
|
281
|
+
if get_config(:annotation)
|
282
|
+
clone_spec.config.annotation = get_config(:annotation)
|
283
|
+
end
|
284
|
+
|
285
|
+
if get_config(:customization_cpucount)
|
286
|
+
clone_spec.config.numCPUs = get_config(:customization_cpucount)
|
287
|
+
end
|
288
|
+
|
289
|
+
if get_config(:customization_memory)
|
290
|
+
clone_spec.config.memoryMB = Integer(get_config(:customization_memory)) * 1024
|
291
|
+
end
|
292
|
+
|
293
|
+
if get_config(:customization_vlan)
|
294
|
+
network = find_network(get_config(:customization_vlan))
|
295
|
+
card = src_config.hardware.device.find { |d| d.deviceInfo.label == "Network adapter 1" } or
|
296
|
+
abort "Can't find source network card to customize"
|
297
|
+
begin
|
298
|
+
switch_port = RbVmomi::VIM.DistributedVirtualSwitchPortConnection(:switchUuid => network.config.distributedVirtualSwitch.uuid, :portgroupKey => network.key)
|
299
|
+
card.backing.port = switch_port
|
300
|
+
rescue
|
301
|
+
# not connected to a distibuted switch?
|
302
|
+
card.backing.deviceName = network.name
|
303
|
+
end
|
304
|
+
dev_spec = RbVmomi::VIM.VirtualDeviceConfigSpec(:device => card, :operation => "edit")
|
305
|
+
clone_spec.config.deviceChange.push dev_spec
|
306
|
+
end
|
307
|
+
|
308
|
+
if get_config(:customization_spec)
|
309
|
+
csi = find_customization(get_config(:customization_spec)) or
|
310
|
+
fatal_exit("failed to find customization specification named #{get_config(:customization_spec)}")
|
311
|
+
|
312
|
+
if csi.info.type != "Linux"
|
313
|
+
fatal_exit("Only Linux customization specifications are currently supported")
|
314
|
+
end
|
315
|
+
cust_spec = csi.spec
|
316
|
+
else
|
317
|
+
global_ipset = RbVmomi::VIM.CustomizationGlobalIPSettings
|
318
|
+
cust_spec = RbVmomi::VIM.CustomizationSpec(:globalIPSettings => global_ipset)
|
319
|
+
end
|
320
|
+
|
321
|
+
if get_config(:customization_dns_ips)
|
322
|
+
cust_spec.globalIPSettings.dnsServerList = get_config(:customization_dns_ips).split(',')
|
323
|
+
end
|
324
|
+
|
325
|
+
if get_config(:customization_dns_suffixes)
|
326
|
+
cust_spec.globalIPSettings.dnsSuffixList = get_config(:customization_dns_suffixes).split(',')
|
327
|
+
end
|
328
|
+
|
329
|
+
if config[:customization_ips]
|
330
|
+
if get_config(:customization_gw)
|
331
|
+
cust_spec.nicSettingMap = config[:customization_ips].split(',').map { |i| generate_adapter_map(i, get_config(:customization_gw)) }
|
332
|
+
else
|
333
|
+
cust_spec.nicSettingMap = config[:customization_ips].split(',').map { |i| generate_adapter_map(i) }
|
334
|
+
end
|
335
|
+
end
|
336
336
|
|
337
337
|
unless get_config(:disable_customization)
|
338
338
|
use_ident = !config[:customization_hostname].nil? || !get_config(:customization_domain).nil? || cust_spec.identity.nil?
|
339
339
|
|
340
340
|
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
341
|
+
if use_ident
|
342
|
+
# TODO - verify that we're deploying a linux spec, at least warn
|
343
|
+
ident = RbVmomi::VIM.CustomizationLinuxPrep
|
344
|
+
|
345
|
+
ident.hostName = RbVmomi::VIM.CustomizationFixedName
|
346
|
+
if config[:customization_hostname]
|
347
|
+
ident.hostName.name = config[:customization_hostname]
|
348
|
+
else
|
349
|
+
ident.hostName.name = config[:vmname]
|
350
|
+
end
|
351
|
+
|
352
|
+
if get_config(:customization_domain)
|
353
|
+
ident.domain = get_config(:customization_domain)
|
354
|
+
else
|
355
|
+
ident.domain = ''
|
356
|
+
end
|
357
|
+
|
358
|
+
cust_spec.identity = ident
|
359
|
+
end
|
360
|
+
|
361
|
+
if customization_plugin && customization_plugin.respond_to?(:customize_clone_spec)
|
362
|
+
clone_spec = customization_plugin.customize_clone_spec(src_config, clone_spec)
|
363
|
+
end
|
364
|
+
|
365
|
+
clone_spec.customization = cust_spec
|
366
|
+
end
|
367
|
+
clone_spec
|
368
|
+
end
|
369
|
+
|
370
|
+
# Loads the customization plugin if one was specified
|
371
|
+
# @return [KnifeVspherePlugin] the loaded and initialized plugin or nil
|
372
|
+
def customization_plugin
|
373
|
+
if @customization_plugin.nil?
|
374
|
+
if cplugin_path = get_config(:customization_plugin)
|
375
|
+
if File.exists? cplugin_path
|
376
|
+
require cplugin_path
|
377
|
+
else
|
378
|
+
abort "Customization plugin could not be found at #{cplugin_path}"
|
379
|
+
end
|
380
|
+
|
381
|
+
if Object.const_defined? 'KnifeVspherePlugin'
|
382
|
+
@customization_plugin = Object.const_get('KnifeVspherePlugin').new
|
383
|
+
if cplugin_data = get_config(:customization_plugin_data)
|
384
|
+
if @customization_plugin.respond_to?(:data=)
|
385
|
+
@customization_plugin.data = cplugin_data
|
386
|
+
else
|
387
|
+
abort "Customization plugin has no :data= accessor to receive the --cplugin-data argument. Define both or neither."
|
388
|
+
end
|
389
|
+
end
|
390
|
+
else
|
391
|
+
abort "KnifeVspherePlugin class is not defined in #{cplugin_path}"
|
392
|
+
end
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
@customization_plugin
|
397
|
+
end
|
398
|
+
|
399
|
+
# Retrieves a CustomizationSpecItem that matches the supplied name
|
400
|
+
# @param vim [Connection] VI Connection to use
|
401
|
+
# @param name [String] name of customization
|
402
|
+
# @return [RbVmomi::VIM::CustomizationSpecItem]
|
403
|
+
def find_customization(name)
|
404
|
+
csm = config[:vim].serviceContent.customizationSpecManager
|
405
|
+
csm.GetCustomizationSpec(:name => name)
|
406
|
+
end
|
407
|
+
|
408
|
+
# Generates a CustomizationAdapterMapping (currently only single IPv4 address) object
|
409
|
+
# @param ip [String] Any static IP address to use, otherwise DHCP
|
410
|
+
# @param gw [String] If static, the gateway for the interface, otherwise network address + 1 will be used
|
411
|
+
# @return [RbVmomi::VIM::CustomizationIPSettings]
|
412
|
+
def generate_adapter_map (ip=nil, gw=nil, dns1=nil, dns2=nil, domain=nil)
|
413
|
+
|
414
|
+
settings = RbVmomi::VIM.CustomizationIPSettings
|
415
|
+
|
416
|
+
if ip.nil?
|
417
|
+
settings.ip = RbVmomi::VIM::CustomizationDhcpIpGenerator
|
418
|
+
else
|
419
|
+
cidr_ip = NetAddr::CIDR.create(ip)
|
420
|
+
settings.ip = RbVmomi::VIM::CustomizationFixedIp(:ipAddress => cidr_ip.ip)
|
421
|
+
settings.subnetMask = cidr_ip.netmask_ext
|
422
|
+
|
423
|
+
# TODO - want to confirm gw/ip are in same subnet?
|
424
|
+
# Only set gateway on first IP.
|
425
|
+
if config[:customization_ips].split(',').first == ip
|
426
|
+
if gw.nil?
|
427
|
+
settings.gateway = [cidr_ip.network(:Objectify => true).next_ip]
|
428
|
+
else
|
429
|
+
gw_cidr = NetAddr::CIDR.create(gw)
|
430
|
+
settings.gateway = [gw_cidr.ip]
|
431
|
+
end
|
432
|
+
end
|
433
|
+
end
|
434
|
+
|
435
|
+
adapter_map = RbVmomi::VIM.CustomizationAdapterMapping
|
436
|
+
adapter_map.adapter = settings
|
437
|
+
adapter_map
|
438
|
+
end
|
439
|
+
|
440
|
+
def bootstrap_for_node()
|
441
|
+
Chef::Knife::Bootstrap.load_deps
|
442
|
+
bootstrap = Chef::Knife::Bootstrap.new
|
443
|
+
bootstrap.name_args = [config[:fqdn]]
|
444
|
+
bootstrap.config[:run_list] = get_config(:run_list).split(/[\s,]+/)
|
445
445
|
bootstrap.config[:secret_file] = get_config(:secret_file)
|
446
446
|
bootstrap.config[:ssh_user] = get_config(:ssh_user)
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
447
|
+
bootstrap.config[:ssh_password] = get_config(:ssh_password)
|
448
|
+
bootstrap.config[:ssh_port] = get_config(:ssh_port)
|
449
|
+
bootstrap.config[:identity_file] = get_config(:identity_file)
|
450
|
+
bootstrap.config[:chef_node_name] = get_config(:chef_node_name)
|
451
|
+
bootstrap.config[:prerelease] = get_config(:prerelease)
|
452
|
+
bootstrap.config[:bootstrap_version] = get_config(:bootstrap_version)
|
453
|
+
bootstrap.config[:distro] = get_config(:distro)
|
454
|
+
bootstrap.config[:use_sudo] = true unless get_config(:ssh_user) == 'root'
|
455
|
+
bootstrap.config[:template_file] = get_config(:template_file)
|
456
|
+
bootstrap.config[:environment] = get_config(:environment)
|
457
457
|
bootstrap.config[:first_boot_attributes] = get_config(:first_boot_attributes)
|
458
458
|
bootstrap.config[:log_level] = get_config(:log_level)
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
459
|
+
# may be needed for vpc_mode
|
460
|
+
bootstrap.config[:no_host_key_verify] = get_config(:no_host_key_verify)
|
461
|
+
bootstrap
|
462
|
+
end
|
463
|
+
|
464
|
+
def tcp_test_ssh(hostname)
|
465
|
+
tcp_socket = TCPSocket.new(hostname, get_config(:ssh_port))
|
466
|
+
readable = IO.select([tcp_socket], nil, nil, 5)
|
467
|
+
if readable
|
468
|
+
Chef::Log.debug("sshd accepting connections on #{hostname}, banner is #{tcp_socket.gets}")
|
469
|
+
true
|
470
|
+
else
|
471
|
+
false
|
472
|
+
end
|
473
|
+
rescue Errno::ETIMEDOUT
|
474
|
+
false
|
475
|
+
rescue Errno::EPERM
|
476
|
+
false
|
477
|
+
rescue Errno::ECONNREFUSED
|
478
|
+
sleep 2
|
479
|
+
false
|
480
|
+
rescue Errno::EHOSTUNREACH, Errno::ENETUNREACH
|
481
|
+
sleep 2
|
482
|
+
false
|
483
|
+
ensure
|
484
|
+
tcp_socket && tcp_socket.close
|
485
|
+
end
|
486
486
|
end
|