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.
@@ -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
- banner "knife xapi guest create NAME [NETWORKS] (options)"
30
+ deps do
31
+ require 'chef/knife/bootstrap'
32
+ Chef::Knife::Bootstrap.load_deps
33
+ end
30
34
 
31
- option :bootstrap,
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 "Clenaing up work and exiting"
82
- xapi.VM.destroy(vm_ref)
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: #{h.color( provisioned, :cyan)} "
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
- # TODO: bootstrap
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
@@ -1,3 +1,3 @@
1
1
  module KnifeXenserver
2
- VERSION = "0.1.5"
2
+ VERSION = "0.2.1"
3
3
  end
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.5
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-04-23 00:00:00.000000000 Z
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.19
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