knife-xapi 0.1.5 → 0.2.1
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/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
|