knife-xapi 0.1.5 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/chef/knife/xapi_base.rb +6 -0
- data/lib/chef/knife/xapi_guest_create.rb +185 -10
- data/lib/knife-xapi/version.rb +1 -1
- metadata +3 -3
data/lib/chef/knife/xapi_base.rb
CHANGED
@@ -28,6 +28,7 @@ end
|
|
28
28
|
|
29
29
|
require 'chef/knife'
|
30
30
|
require 'units/standard'
|
31
|
+
require 'xenapi'
|
31
32
|
|
32
33
|
class Chef::Knife
|
33
34
|
module XapiBase
|
@@ -84,6 +85,11 @@ class Chef::Knife
|
|
84
85
|
end
|
85
86
|
end
|
86
87
|
|
88
|
+
def locate_config_value(key)
|
89
|
+
key = key.to_sym
|
90
|
+
Chef::Config[:knife][key] || config[key]
|
91
|
+
end
|
92
|
+
|
87
93
|
# get template by name_label
|
88
94
|
def get_template(template)
|
89
95
|
xapi.VM.get_by_name_label(template).first
|
@@ -25,14 +25,14 @@ class Chef
|
|
25
25
|
class Knife
|
26
26
|
class XapiGuestCreate < Knife
|
27
27
|
include Chef::Knife::XapiBase
|
28
|
+
require 'timeout'
|
28
29
|
|
29
|
-
|
30
|
+
deps do
|
31
|
+
require 'chef/knife/bootstrap'
|
32
|
+
Chef::Knife::Bootstrap.load_deps
|
33
|
+
end
|
30
34
|
|
31
|
-
|
32
|
-
:short => "-B BOOTSTRAP",
|
33
|
-
:long => "--xapi-bootstrap BOOTSTRAP",
|
34
|
-
:description => "bootstrap template to push to the server",
|
35
|
-
:proc => Proc.new { |bootstrap| Chef::Config[:knife][:xapi_bootstrap] = bootstrap }
|
35
|
+
banner "knife xapi guest create NAME [NETWORKS] (options)"
|
36
36
|
|
37
37
|
option :vm_template,
|
38
38
|
:short => "-T Template Name Label",
|
@@ -40,6 +40,12 @@ class Chef
|
|
40
40
|
:description => "xapi template name to create from. accepts an string or regex",
|
41
41
|
:proc => Proc.new { |template| Chef::Config[:knife][:xapi_vm_template] = template }
|
42
42
|
|
43
|
+
option :domain,
|
44
|
+
:short => "-f Name",
|
45
|
+
:long => "--domain Name",
|
46
|
+
:description => "the domain name for the guest",
|
47
|
+
:proc => Proc.new { |domain| Chef::Config[:knife][:xapi_domainname] = domain }
|
48
|
+
|
43
49
|
option :install_repo,
|
44
50
|
:short => "-R If you're using a builtin template you will need to specify a repo url",
|
45
51
|
:long => "--xapi-install-repo",
|
@@ -76,13 +82,131 @@ class Chef
|
|
76
82
|
:description => "Ammount of memory the VM should have specify with m g etc 512m, 2g if no unit spcified it assumes gigabytes",
|
77
83
|
:proc => Proc.new {|mem| Chef::Config[:knife][:xapi_mem] = mem }
|
78
84
|
|
85
|
+
option :chef_node_name,
|
86
|
+
:short => "-N NAME",
|
87
|
+
:long => "--node-name NAME",
|
88
|
+
:description => "The Chef node name for your new node"
|
89
|
+
|
90
|
+
option :ssh_key_name,
|
91
|
+
:short => "-S KEY",
|
92
|
+
:long => "--ssh-key KEY",
|
93
|
+
:description => "The SSH key id",
|
94
|
+
:proc => Proc.new { |key| Chef::Config[:knife][:xapi_ssh_key_id] = key }
|
95
|
+
|
96
|
+
option :ssh_user,
|
97
|
+
:short => "-x USERNAME",
|
98
|
+
:long => "--ssh-user USERNAME",
|
99
|
+
:description => "The ssh username",
|
100
|
+
:default => "root"
|
101
|
+
|
102
|
+
option :ssh_password,
|
103
|
+
:short => "-P PASSWORD",
|
104
|
+
:long => "--ssh-password PASSWORD",
|
105
|
+
:description => "The ssh password"
|
106
|
+
|
107
|
+
option :ssh_port,
|
108
|
+
:short => "-p PORT",
|
109
|
+
:long => "--ssh-port PORT",
|
110
|
+
:description => "The ssh port",
|
111
|
+
:default => "22",
|
112
|
+
:proc => Proc.new { |key| Chef::Config[:knife][:ssh_port] = key }
|
113
|
+
|
114
|
+
option :bootstrap_version,
|
115
|
+
:long => "--bootstrap-version VERSION",
|
116
|
+
:description => "The version of Chef to install",
|
117
|
+
:proc => Proc.new { |v| Chef::Config[:knife][:bootstrap_version] = v }
|
118
|
+
|
119
|
+
option :distro,
|
120
|
+
:short => "-d DISTRO",
|
121
|
+
:long => "--distro DISTRO",
|
122
|
+
:description => "Bootstrap a distro using a template",
|
123
|
+
:proc => Proc.new { |d| Chef::Config[:knife][:distro] = d },
|
124
|
+
:default => "ubuntu10.04-gems"
|
125
|
+
|
126
|
+
option :template_file,
|
127
|
+
:short => "-F FILEPATH",
|
128
|
+
:long => "--template-file TEMPLATE",
|
129
|
+
:description => "Full path to location of template to use",
|
130
|
+
:proc => Proc.new { |t| Chef::Config[:knife][:template_file] = t },
|
131
|
+
:default => false
|
132
|
+
|
133
|
+
option :run_list,
|
134
|
+
:short => "-r RUN_LIST",
|
135
|
+
:long => "--run-list RUN_LIST",
|
136
|
+
:description => "Comma separated list of roles/recipes to apply",
|
137
|
+
:proc => lambda { |o| o.split(/[\s,]+/) },
|
138
|
+
:default => []
|
139
|
+
|
140
|
+
def tcp_test_ssh(hostname)
|
141
|
+
tcp_socket = TCPSocket.new(hostname, config[:ssh_port])
|
142
|
+
readable = IO.select([tcp_socket], nil, nil, 5)
|
143
|
+
if readable
|
144
|
+
Chef::Log.debug("sshd accepting connections on #{hostname}, banner is #{tcp_socket.gets}")
|
145
|
+
yield
|
146
|
+
true
|
147
|
+
else
|
148
|
+
false
|
149
|
+
end
|
150
|
+
rescue SocketError
|
151
|
+
sleep 2
|
152
|
+
false
|
153
|
+
rescue Errno::ETIMEDOUT
|
154
|
+
false
|
155
|
+
rescue Errno::EPERM
|
156
|
+
false
|
157
|
+
rescue Errno::ECONNREFUSED
|
158
|
+
sleep 2
|
159
|
+
false
|
160
|
+
# This happens on EC2 quite often
|
161
|
+
rescue Errno::EHOSTUNREACH
|
162
|
+
sleep 2
|
163
|
+
false
|
164
|
+
ensure
|
165
|
+
tcp_socket && tcp_socket.close
|
166
|
+
end
|
167
|
+
|
168
|
+
def get_guest_ip(vm_ref)
|
169
|
+
begin
|
170
|
+
timeout(480) do
|
171
|
+
ui.msg "Waiting for guest ip address"
|
172
|
+
guest_ip = ""
|
173
|
+
while guest_ip.empty?
|
174
|
+
print(".")
|
175
|
+
sleep @initial_sleep_delay ||= 10
|
176
|
+
vgm = xapi.VM.get_guest_metrics(vm_ref)
|
177
|
+
next if "OpaqueRef:NULL" == vgm
|
178
|
+
networks = xapi.VM_guest_metrics.get_networks(vgm)
|
179
|
+
if networks.has_key?("0/ip")
|
180
|
+
guest_ip = networks["0/ip"]
|
181
|
+
end
|
182
|
+
end
|
183
|
+
puts "\n"
|
184
|
+
return guest_ip
|
185
|
+
end
|
186
|
+
rescue Timeout::Error
|
187
|
+
ui.msg "Timeout waiting for XAPI to report IP address "
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
79
191
|
# destroy/remove VM refs and exit
|
80
192
|
def cleanup(vm_ref)
|
81
|
-
ui.warn "
|
82
|
-
|
193
|
+
ui.warn "Cleaning up work and exiting"
|
194
|
+
# shutdown and dest
|
195
|
+
unless xapi.VM.get_power_state(vm_ref) == "Halted"
|
196
|
+
print "Shutting down Guest"
|
197
|
+
task = xapi.Async.VM.hard_shutdown(vm_ref)
|
198
|
+
wait_on_task(task)
|
199
|
+
print " #{h.color "Done", :green} \n"
|
200
|
+
end
|
201
|
+
|
202
|
+
print "Destroying Guest"
|
203
|
+
task = xapi.Async.VM.destroy(vm_ref)
|
204
|
+
wait_on_task(task)
|
205
|
+
print " #{h.color "Done", :green} \n"
|
83
206
|
exit 1
|
84
207
|
end
|
85
208
|
|
209
|
+
|
86
210
|
def run
|
87
211
|
server_name = @name_args[0]
|
88
212
|
$stdout.sync = true
|
@@ -115,8 +239,13 @@ class Chef
|
|
115
239
|
# setup the Boot args
|
116
240
|
#
|
117
241
|
boot_args = Chef::Config[:knife][:xapi_kernel_params] || "graphical utf8"
|
242
|
+
domainname = Chef::Config[:knife][:xapi_domainname] || ""
|
243
|
+
|
118
244
|
# if no hostname param set hostname to given vm name
|
119
245
|
boot_args << " hostname=#{server_name}" unless boot_args.match(/hostname=.+\s?/)
|
246
|
+
# if domainname is supplied we put that in there as well
|
247
|
+
boot_args << " domainname=#{domainname}" unless boot_args.match(/domainname=.+\s?/)
|
248
|
+
|
120
249
|
ui.msg "Setting Boot Args: #{h.color boot_args, :cyan}"
|
121
250
|
xapi.VM.set_PV_args( vm_ref, boot_args )
|
122
251
|
|
@@ -156,12 +285,13 @@ class Chef
|
|
156
285
|
ui.msg "Provisioning new Guest: #{h.color(vm_ref, :bold, :cyan )}"
|
157
286
|
provisioned = xapi.VM.provision(vm_ref)
|
158
287
|
|
159
|
-
ui.msg "Starting new Guest
|
288
|
+
ui.msg "Starting new Guest #{h.color( provisioned, :cyan)} "
|
160
289
|
|
161
290
|
task = xapi.Async.VM.start(vm_ref, false, true)
|
162
291
|
wait_on_task(task)
|
163
292
|
ui.msg( "#{ h.color "Done!", :green}" )
|
164
293
|
|
294
|
+
exit 0 unless locate_config_value(:run_list)
|
165
295
|
rescue Exception => e
|
166
296
|
ui.msg "#{h.color 'ERROR:'} #{h.color( e.message, :red )}"
|
167
297
|
# have to use join here to pass a string to highline
|
@@ -170,7 +300,52 @@ class Chef
|
|
170
300
|
|
171
301
|
cleanup(vm_ref)
|
172
302
|
end
|
173
|
-
|
303
|
+
|
304
|
+
guest_addr = get_guest_ip(vm_ref)
|
305
|
+
if guest_addr.nil? or guest_addr.empty?
|
306
|
+
ui.msg("ip seems wrong using host+domain name instead")
|
307
|
+
guest_addr = "#{host_name}.#{domainname}"
|
308
|
+
end
|
309
|
+
ui.msg "Trying to connect to guest @ #{guest_addr} "
|
310
|
+
|
311
|
+
begin
|
312
|
+
timeout(480) do
|
313
|
+
print(".") until tcp_test_ssh(guest_addr) {
|
314
|
+
sleep @initial_sleep_delay ||= 10
|
315
|
+
puts("done")
|
316
|
+
}
|
317
|
+
end
|
318
|
+
rescue Timeout::Error
|
319
|
+
ui.msg "Timeout trying to login cleaning up"
|
320
|
+
cleanup(vm_ref)
|
321
|
+
end
|
322
|
+
|
323
|
+
|
324
|
+
begin
|
325
|
+
bootstrap = Chef::Knife::Bootstrap.new
|
326
|
+
bootstrap.name_args = [ guest_addr ]
|
327
|
+
bootstrap.config[:run_list] = config[:run_list]
|
328
|
+
bootstrap.config[:ssh_user] = config[:ssh_user]
|
329
|
+
bootstrap.config[:ssh_port] = config[:ssh_port]
|
330
|
+
bootstrap.config[:ssh_password] = config[:ssh_password]
|
331
|
+
bootstrap.config[:identity_file] = config[:identity_file]
|
332
|
+
bootstrap.config[:chef_node_name] = config[:chef_node_name] || server_name
|
333
|
+
bootstrap.config[:bootstrap_version] = locate_config_value(:bootstrap_version)
|
334
|
+
bootstrap.config[:distro] = locate_config_value(:distro)
|
335
|
+
bootstrap.config[:use_sudo] = true unless config[:ssh_user] == 'root'
|
336
|
+
bootstrap.config[:template_file] = locate_config_value(:template_file)
|
337
|
+
bootstrap.config[:environment] = config[:environment]
|
338
|
+
bootstrap.config[:host_key_verify] = false
|
339
|
+
bootstrap.config[:run_list] = config[:run_list]
|
340
|
+
|
341
|
+
bootstrap.run
|
342
|
+
rescue Exception => e
|
343
|
+
ui.msg "#{h.color 'ERROR:'} #{h.color( e.message, :red )}"
|
344
|
+
puts "Nested backtrace:"
|
345
|
+
ui.msg "#{h.color( e.backtrace.join("\n"), :yellow)}"
|
346
|
+
cleanup(vm_ref)
|
347
|
+
end
|
348
|
+
|
174
349
|
end
|
175
350
|
|
176
351
|
end
|
data/lib/knife-xapi/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: knife-xapi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 0.2.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-05-25 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: chef
|
@@ -110,7 +110,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
110
110
|
version: '0'
|
111
111
|
requirements: []
|
112
112
|
rubyforge_project:
|
113
|
-
rubygems_version: 1.8.
|
113
|
+
rubygems_version: 1.8.24
|
114
114
|
signing_key:
|
115
115
|
specification_version: 3
|
116
116
|
summary: Xen API Support for Chef's Knife Command
|