knife-esx 0.1.5 → 0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +81 -2
- data/knife-esx.gemspec +2 -0
- data/lib/chef/knife/esx_base.rb +1 -1
- data/lib/chef/knife/esx_vm_create.rb +168 -2
- data/lib/knife-esx/version.rb +1 -1
- metadata +30 -8
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,63 @@
|
|
1
|
-
#
|
1
|
+
# 0.2 - 2012/02/28
|
2
|
+
|
3
|
+
* Added --batch and --async options
|
4
|
+
|
5
|
+
Inspired by spiceweasel from Matt Ray (https://github.com/mattray/spiceweasel), I've added a batch mode where a YAML file describes the VMs you want to bootstrap.
|
6
|
+
|
7
|
+
knife esx vm create --batch batch.yml
|
8
|
+
|
9
|
+
Sample batch.yml file:
|
10
|
+
|
11
|
+
---
|
12
|
+
:test1:
|
13
|
+
'extra-args': --no-host-key-verify
|
14
|
+
'vm-memory': 128
|
15
|
+
'esx-host': esx-server-1
|
16
|
+
'esx-password': secret
|
17
|
+
'ssh-user': ubuntu
|
18
|
+
'ssh-password': ubuntu
|
19
|
+
'vm-disk': /home/rubiojr/mnt/mirror/virtual_appliances/ubuntu1110-x64-vmware-tools.vmdk
|
20
|
+
'datastore': datastore2
|
21
|
+
:test2:
|
22
|
+
'extra-args': --no-host-key-verify
|
23
|
+
'vm-memory': 128
|
24
|
+
'esx-host': esx-server-1
|
25
|
+
'esx-password': secret
|
26
|
+
'ssh-user': ubuntu
|
27
|
+
'ssh-password': ubuntu
|
28
|
+
'vm-disk': /home/rubiojr/mnt/mirror/virtual_appliances/ubuntu1110-x64-vmware-tools.vmdk
|
29
|
+
'datastore': datastore2
|
30
|
+
:test3:
|
31
|
+
'extra-args': --no-host-key-verify
|
32
|
+
'vm-memory': 256
|
33
|
+
'esx-host': esx-server-1
|
34
|
+
'esx-password': secret
|
35
|
+
'ssh-user': ubuntu
|
36
|
+
'ssh-password': ubuntu
|
37
|
+
'vm-disk': /home/rubiojr/mnt/mirror/virtual_appliances/ubuntu1110-x64-vmware-tools.vmdk
|
38
|
+
'datastore': datastore2
|
39
|
+
|
40
|
+
This will try to create three VMs (testvm1, testvm2 and testvm3) sequentially. VM definitions inside the batch file accept all the parameters that can be used with knife-esx.
|
41
|
+
|
42
|
+
If you want to bootstrap the VMs asynchronously, use the --async flag.
|
43
|
+
|
44
|
+
knife esx vm create --batch batch.yml --async
|
45
|
+
|
46
|
+
Standard output and error log is redirected to /tmp/knife_esx_vm_create_VMNAME.log, so if we use the deploy script from above, three log files will be created:
|
47
|
+
|
48
|
+
/tmp/knife_esx_vm_create_test1.log
|
49
|
+
/tmp/knife_esx_vm_create_test2.log
|
50
|
+
/tmp/knife_esx_vm_create_test3.log
|
51
|
+
|
52
|
+
* Added --skip-bootstrap flag. If the flag is used the VM will be created but
|
53
|
+
the bootstrap template/script won't be executed (it also means that Chef won't be installed).
|
54
|
+
|
55
|
+
* Fixed bug preventing knife-esx to create a VM when the hypervisor has an empty root password.
|
56
|
+
|
57
|
+
KNOWN ISSUES
|
58
|
+
* To use --batch without --skip-bootstrap, the ssh user (--ssh-user) needs to be able to sudo without asking for a password (i.e. adding something like 'ubuntu ALL=(ALL) NOPASSWD: ALL' to /etc/sudoers in the appliance template) otherwise the bootstraping process won't work if more than one VM is being deployed.
|
59
|
+
|
60
|
+
# 0.1.5 - 2012/02/25
|
2
61
|
|
3
62
|
* Patch from @pperezrubio adding multiple networks support
|
4
63
|
|
@@ -8,7 +67,27 @@
|
|
8
67
|
--ssh-password ubuntu \
|
9
68
|
--vm-network "VLAN-Integration,VLAN-Test"
|
10
69
|
|
11
|
-
|
12
70
|
This will create a VM with two NICs, attaching them to the VLAN-Integration and VLAN-Test networks respectively.
|
13
71
|
|
72
|
+
Fixed MAC addresses can also be assigned to each NIC using the --mac-address parameter:
|
73
|
+
|
74
|
+
knife esx vm create --vm-disk ubuntu-oneiric.vmdk \
|
75
|
+
--vm-name testvm --datastore datastore1 \
|
76
|
+
--esx-host 192.168.88.1 --ssh-user ubuntu \
|
77
|
+
--ssh-password ubuntu \
|
78
|
+
--vm-network "VLAN-Integration,VLAN-Test" \
|
79
|
+
--mac-address "00:01:02:03:04:05,00:01:02:03:04:06"
|
80
|
+
|
81
|
+
MAC address 00:01:02:03:04:05 will be assigned to VLAN-Integration NIC and 00:01:02:03:04:06 to the VLAN-Test NIC.
|
82
|
+
|
83
|
+
If a MAC address is omitted it will be dynamically generated:
|
84
|
+
|
85
|
+
knife esx vm create --vm-disk ubuntu-oneiric.vmdk \
|
86
|
+
--vm-name testvm --datastore datastore1 \
|
87
|
+
--esx-host 192.168.88.1 --ssh-user ubuntu \
|
88
|
+
--ssh-password ubuntu \
|
89
|
+
--vm-network "VLAN-Integration,VLAN-Test" \
|
90
|
+
--mac-address ",00:01:02:03:04:06"
|
91
|
+
|
92
|
+
Note that I did not specify the first MAC address, so VLAN-Integration NIC will get a random MAC.
|
14
93
|
|
data/knife-esx.gemspec
CHANGED
data/lib/chef/knife/esx_base.rb
CHANGED
@@ -61,7 +61,7 @@ class Chef
|
|
61
61
|
ui.info "#{ui.color("Connecting to ESX host #{config[:esx_host]}... ", :magenta)}"
|
62
62
|
@connection = ESX::Host.connect(Chef::Config[:knife][:esx_host],
|
63
63
|
Chef::Config[:knife][:esx_username],
|
64
|
-
Chef::Config[:knife][:esx_password])
|
64
|
+
Chef::Config[:knife][:esx_password] || '')
|
65
65
|
else
|
66
66
|
@connection
|
67
67
|
end
|
@@ -17,9 +17,121 @@
|
|
17
17
|
#
|
18
18
|
|
19
19
|
require 'chef/knife/esx_base'
|
20
|
+
require 'open4'
|
21
|
+
require 'celluloid'
|
22
|
+
require 'singleton'
|
23
|
+
|
24
|
+
module KnifeESX
|
25
|
+
class DeployScript
|
26
|
+
|
27
|
+
attr_reader :job_count
|
28
|
+
|
29
|
+
# Sample job
|
30
|
+
#---
|
31
|
+
#:test1:
|
32
|
+
# 'vm-memory':
|
33
|
+
# 'extra-args':
|
34
|
+
# 'esx-host':
|
35
|
+
# 'template-file':
|
36
|
+
# 'vm-disk':
|
37
|
+
# 'ssh-user':
|
38
|
+
# 'ssh-password':
|
39
|
+
# 'run-list':
|
40
|
+
# 'network-interface':
|
41
|
+
def initialize(batch_file)
|
42
|
+
@batch_file = batch_file
|
43
|
+
@jobs = []
|
44
|
+
@job_count = 0
|
45
|
+
(YAML.load_file batch_file).each do |i|
|
46
|
+
@jobs << DeployJob.new(i)
|
47
|
+
@job_count += 1
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def each_job(&block)
|
52
|
+
@jobs.each do |j|
|
53
|
+
yield j
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
class CLogger
|
60
|
+
include Celluloid
|
61
|
+
include Singleton
|
62
|
+
|
63
|
+
def info(msg)
|
64
|
+
puts "INFO: #{msg}"
|
65
|
+
end
|
66
|
+
|
67
|
+
def error(msg)
|
68
|
+
$stderr.puts "ERROR: #{msg}"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class DeployJob
|
73
|
+
|
74
|
+
include Celluloid
|
75
|
+
|
76
|
+
attr_reader :name
|
77
|
+
|
78
|
+
def initialize(options)
|
79
|
+
@name, @options = options
|
80
|
+
validate
|
81
|
+
end
|
82
|
+
|
83
|
+
def validate
|
84
|
+
if @name.nil? or @name.empty?
|
85
|
+
raise Exception.new("Invalid job name")
|
86
|
+
end
|
87
|
+
if not @options['vm-disk'] or !File.exist?(@options['vm-disk'])
|
88
|
+
raise Exception.new("Invalid VM disk for job #{@name}.")
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# returns [status, stdout, stderr]
|
93
|
+
def run
|
94
|
+
args = ""
|
95
|
+
extra_args = ""
|
96
|
+
@options.each do |k, v|
|
97
|
+
if k == 'extra-args'
|
98
|
+
extra_args << v
|
99
|
+
else
|
100
|
+
args << "--#{k} #{v} " unless k == 'extra-args'
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
@out = ""
|
105
|
+
@err = ""
|
106
|
+
optstring = []
|
107
|
+
@options.each do |k,v|
|
108
|
+
optstring << " - #{k}:".ljust(25) + "#{v}\n" unless k =~ /password/
|
109
|
+
end
|
110
|
+
log_file = "/tmp/knife_esx_vm_create_#{@name.to_s.strip.chomp.gsub(/\s/,'_')}.log"
|
111
|
+
CLogger.instance.info! "Bootstrapping VM #{@name} \n#{optstring.join}"
|
112
|
+
CLogger.instance.info! "VM #{@name} bootstrap log: #{log_file}"
|
113
|
+
@status = Open4.popen4("knife esx vm create --vm-name #{@name} #{args} #{extra_args} > #{log_file} 2>&1") do |pid, stdin, stdout, stderr|
|
114
|
+
@out << stdout.read.strip
|
115
|
+
@err << stderr.read.strip
|
116
|
+
end
|
117
|
+
if @status == 0
|
118
|
+
CLogger.instance.info! "[#{@name}] deployment finished OK"
|
119
|
+
else
|
120
|
+
CLogger.instance.error! "[#{@name}] deployment FAILED"
|
121
|
+
@err.each_line do |l|
|
122
|
+
CLogger.instance.error! "[#{@name}] #{l.chomp}"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
return @status, @out, @err
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
20
131
|
|
21
132
|
class Chef
|
22
133
|
class Knife
|
134
|
+
|
23
135
|
class EsxVmCreate < Knife
|
24
136
|
|
25
137
|
include Knife::ESXBase
|
@@ -125,7 +237,26 @@ class Chef
|
|
125
237
|
:long => "--mac-address",
|
126
238
|
:description => "Mac address list",
|
127
239
|
:default => nil
|
128
|
-
|
240
|
+
|
241
|
+
option :skip_bootstrap,
|
242
|
+
:long => "--skip-bootstrap",
|
243
|
+
:description => "Skip bootstrap process (Deploy only mode)",
|
244
|
+
:boolean => true,
|
245
|
+
:default => false,
|
246
|
+
:proc => Proc.new { true }
|
247
|
+
|
248
|
+
option :async,
|
249
|
+
:long => "--async",
|
250
|
+
:description => "Deploy the VMs asynchronously (Ignored unless combined with --batch)",
|
251
|
+
:boolean => true,
|
252
|
+
:default => false,
|
253
|
+
:proc => Proc.new { true }
|
254
|
+
|
255
|
+
option :batch,
|
256
|
+
:long => "--batch script.yml",
|
257
|
+
:description => "Use a batch file to deploy multiple VMs",
|
258
|
+
:default => nil
|
259
|
+
|
129
260
|
def tcp_test_ssh(hostname)
|
130
261
|
tcp_socket = TCPSocket.new(hostname, 22)
|
131
262
|
readable = IO.select([tcp_socket], nil, nil, 5)
|
@@ -147,6 +278,38 @@ class Chef
|
|
147
278
|
|
148
279
|
def run
|
149
280
|
$stdout.sync = true
|
281
|
+
|
282
|
+
if config[:batch]
|
283
|
+
CLogger.instance.info "Running in batch mode. Extra arguments will be ignored."
|
284
|
+
if not config[:async]
|
285
|
+
counter = 0
|
286
|
+
script = KnifeESX::DeployScript.new(config[:batch])
|
287
|
+
script.each_job do |job|
|
288
|
+
counter += 1
|
289
|
+
status, stdout, stderr = job.run
|
290
|
+
if status == 0
|
291
|
+
CLogger.instance.info 'Ok'
|
292
|
+
else
|
293
|
+
CLogger.instance.error 'Failed'
|
294
|
+
stderr.each_line do |l|
|
295
|
+
ui.error l
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
else
|
300
|
+
CLogger.instance.info! "Asynchronous boostrapping selected"
|
301
|
+
CLogger.instance.info! "Now do something productive while I finish my job ;)"
|
302
|
+
script = KnifeESX::DeployScript.new(config[:batch])
|
303
|
+
futures = []
|
304
|
+
script.each_job do |job|
|
305
|
+
futures << job.future(:run)
|
306
|
+
end
|
307
|
+
futures.each do |f|
|
308
|
+
f.value
|
309
|
+
end
|
310
|
+
end
|
311
|
+
return
|
312
|
+
end
|
150
313
|
|
151
314
|
unless config[:vm_disk]
|
152
315
|
ui.error("You have not provided a valid VMDK file. (--vm-disk)")
|
@@ -172,7 +335,7 @@ class Chef
|
|
172
335
|
destination_path = "/vmfs/volumes/#{datastore}/#{vm_name}"
|
173
336
|
|
174
337
|
connection.remote_command "mkdir #{destination_path}"
|
175
|
-
puts "#{ui.color("Creating VM... ", :magenta)}"
|
338
|
+
puts "#{ui.color("Creating VM #{vm_name}... ", :magenta)}"
|
176
339
|
puts "#{ui.color("Importing VM disk... ", :magenta)}"
|
177
340
|
|
178
341
|
connection.import_disk vm_disk, destination_path + "/#{vm_name}.vmdk"
|
@@ -186,6 +349,8 @@ class Chef
|
|
186
349
|
|
187
350
|
puts "#{ui.color("VM Name", :cyan)}: #{vm.name}"
|
188
351
|
puts "#{ui.color("VM Memory", :cyan)}: #{(vm.memory_size.to_f/1024/1024).round} MB"
|
352
|
+
|
353
|
+
return if config[:skip_bootstrap]
|
189
354
|
|
190
355
|
# wait for it to be ready to do stuff
|
191
356
|
print "\n#{ui.color("Waiting server... ", :magenta)}"
|
@@ -220,6 +385,7 @@ class Chef
|
|
220
385
|
def bootstrap_for_node(vm)
|
221
386
|
bootstrap = Chef::Knife::Bootstrap.new
|
222
387
|
bootstrap.name_args = [vm.ip_address]
|
388
|
+
bootstrap.config[:async] = config[:async]
|
223
389
|
bootstrap.config[:run_list] = config[:run_list]
|
224
390
|
bootstrap.config[:ssh_user] = config[:ssh_user]
|
225
391
|
bootstrap.config[:identity_file] = config[:identity_file]
|
data/lib/knife-esx/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: knife-esx
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: '0.2'
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-02-
|
12
|
+
date: 2012-02-28 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: esx
|
16
|
-
requirement: &
|
16
|
+
requirement: &12088940 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 0.3.2
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *12088940
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: terminal-table
|
27
|
-
requirement: &
|
27
|
+
requirement: &12088520 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *12088520
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: chef
|
38
|
-
requirement: &
|
38
|
+
requirement: &12087940 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,7 +43,29 @@ dependencies:
|
|
43
43
|
version: '0.10'
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *12087940
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: celluloid
|
49
|
+
requirement: &12087280 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.9'
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *12087280
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: open4
|
60
|
+
requirement: &12086840 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
type: :runtime
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *12086840
|
47
69
|
description: ESX Support for Chef's Knife Command
|
48
70
|
email:
|
49
71
|
- rubiojr@frameos.org
|