knife-proxmox 0.0.12
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/CHANGELOG +155 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +14 -0
- data/LICENSE +201 -0
- data/README +127 -0
- data/TODO +7 -0
- data/knife-proxmox-0.0.11.gem +0 -0
- data/knife-proxmox.gemspec +22 -0
- data/lib/chef/knife/connection.rb +34 -0
- data/lib/chef/knife/proxmox_base.rb +251 -0
- data/lib/chef/knife/proxmox_server_create.rb +234 -0
- data/lib/chef/knife/proxmox_server_destroy.rb +63 -0
- data/lib/chef/knife/proxmox_server_list.rb +43 -0
- data/lib/chef/knife/proxmox_server_start.rb +27 -0
- data/lib/chef/knife/proxmox_server_stop.rb +28 -0
- data/lib/chef/knife/proxmox_template_available.rb +31 -0
- data/lib/chef/knife/proxmox_template_list.rb +36 -0
- data/lib/chef/knife/server.rb +0 -0
- data/lib/chef/knife/template.rb +0 -0
- data/lib/knife-proxmox/version.rb +6 -0
- metadata +124 -0
data/TODO
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
Description Resource Path Location Type
|
2
|
+
FIXME: storage = local debería ser tambien un parametro configurable proxmox_template_list.rb /knife-proxmox/lib/chef line 53 Studio Task
|
3
|
+
TODO: Create a good gem using this tutorial http://guides.rubygems.org/make-your-own-gem/#writing-tests
|
4
|
+
TODO: All inputs MUST be checked and errors MUST be catched.
|
5
|
+
TODO: Testing of everything
|
6
|
+
TODO: change ticket.gsub for CGI.escape(str) proxmox_server_create.rb /knife-proxmox/lib/chef line 110 Studio Task
|
7
|
+
TODO: parameters for openvz should be in other object proxmox_server_create.rb /knife-proxmox/lib/chef line 55 Studio Task
|
Binary file
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "knife-proxmox/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "knife-proxmox"
|
7
|
+
s.version = Knife::Proxmox::VERSION
|
8
|
+
s.has_rdoc = false
|
9
|
+
s.authors = ["Jorge Moratilla", "Sergio Galvan"]
|
10
|
+
s.email = ["jorge@moratilla.com","sergalma@gmail.com"]
|
11
|
+
s.homepage = "http://wiki.opscode.com/display/chef"
|
12
|
+
s.summary = "ProxmoxVE Support for Chef's Knife Command"
|
13
|
+
s.description = s.summary
|
14
|
+
s.extra_rdoc_files = ["README", "LICENSE","TODO","CHANGELOG" ]
|
15
|
+
|
16
|
+
s.files = Dir['lib/**/*.rb'] + Dir['./*']
|
17
|
+
s.add_dependency "chef", ">= 0.10.10"
|
18
|
+
s.add_dependency "rest-client", ">=1.6.7"
|
19
|
+
s.add_dependency "json", ">=1.5.4"
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# This class should be a singleton with all the logic to use proxmox
|
2
|
+
|
3
|
+
class Connection
|
4
|
+
|
5
|
+
@site = nil
|
6
|
+
@auth_params = nil
|
7
|
+
@servers = {}
|
8
|
+
@templates = {}
|
9
|
+
|
10
|
+
def initialize()
|
11
|
+
@site = RestClient::Resource.new(Chef::Config[:knife][:pve_cluster_url])
|
12
|
+
@auth_params ||= begin
|
13
|
+
ticket = nil
|
14
|
+
csrf_prevention_token = nil
|
15
|
+
@site['access/ticket'].post :username=>Chef::Config[:knife][:pve_user_name],
|
16
|
+
:realm=>Chef::Config[:knife][:pve_user_realm],
|
17
|
+
:password=>Chef::Config[:knife][:pve_user_password] do |response, request, result, &block|
|
18
|
+
if response.code == 200 then
|
19
|
+
data = JSON.parse(response.body)
|
20
|
+
ticket = data['data']['ticket']
|
21
|
+
csrf_prevention_token = data['data']['CSRFPreventionToken']
|
22
|
+
if !ticket.nil? then
|
23
|
+
token = 'PVEAuthCookie=' + ticket.gsub!(/:/,'%3A').gsub!(/=/,'%3D')
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
{:CSRFPreventionToken => csrf_prevention_token, :cookie => token}
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,251 @@
|
|
1
|
+
require 'chef/knife'
|
2
|
+
#TODO: Testing of everything
|
3
|
+
#TODO: All inputs MUST be checked and errors MUST be catched.
|
4
|
+
class Chef
|
5
|
+
class Knife
|
6
|
+
module ProxmoxBase
|
7
|
+
|
8
|
+
def self.included(includer)
|
9
|
+
includer.class_eval do
|
10
|
+
|
11
|
+
deps do
|
12
|
+
require 'rubygems'
|
13
|
+
require 'rest_client'
|
14
|
+
require 'json'
|
15
|
+
require 'chef/json_compat'
|
16
|
+
require 'cgi'
|
17
|
+
require 'chef/log'
|
18
|
+
require 'set'
|
19
|
+
require 'net/ssh/multi'
|
20
|
+
require 'chef/api_client'
|
21
|
+
require 'chef/node'
|
22
|
+
end
|
23
|
+
|
24
|
+
# options
|
25
|
+
option :pve_cluster_url,
|
26
|
+
:short => "-U URL",
|
27
|
+
:long => "--pve_cluster_url URL",
|
28
|
+
:description => "Your URL to access Proxmox VE server/cluster",
|
29
|
+
:proc => Proc.new {|url| Chef::Config[:knife][:pve_cluster_url] = url }
|
30
|
+
|
31
|
+
option :pve_user_name,
|
32
|
+
:short => "-u username",
|
33
|
+
:long => "--username username",
|
34
|
+
:description => "Your username in Proxmox VE",
|
35
|
+
:proc => Proc.new {|username| Chef::Config[:knife][:pve_user_name] = username }
|
36
|
+
|
37
|
+
option :pve_user_password,
|
38
|
+
:short => "-p password",
|
39
|
+
:long => "--password password",
|
40
|
+
:description => "Your password in Proxmox VE",
|
41
|
+
:proc => Proc.new {|password| Chef::Config[:knife][:pve_user_password] = password }
|
42
|
+
|
43
|
+
option :pve_user_realm,
|
44
|
+
:short => "-r realm",
|
45
|
+
:long => "--realm realm",
|
46
|
+
:description => "Your realm of Authentication in Proxmox VE",
|
47
|
+
:proc => Proc.new {|realm| Chef::Config[:knife][:pve_user_realm] = realm }
|
48
|
+
|
49
|
+
option :pve_node_name,
|
50
|
+
:short => "-n node",
|
51
|
+
:long => "--node nodename",
|
52
|
+
:description => "Proxmox VE server name where you will actuate",
|
53
|
+
:proc => Proc.new {|node| Chef::Config[:knife][:pve_node_name] = node }
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Checks that the parameter provided is defined in knife.rb
|
59
|
+
def check_global_parameter(value)
|
60
|
+
if (Chef::Config[:knife][value].nil? or Chef::Config[:knife][value].empty?) then
|
61
|
+
ui.error "knife[:#{value.to_s}] is empty, define a value for it and try again"
|
62
|
+
exit 1
|
63
|
+
end
|
64
|
+
Chef::Log.debug("knife[:#{value}] = " + Chef::Config[:knife][value])
|
65
|
+
end
|
66
|
+
|
67
|
+
def check_config_parameter(value)
|
68
|
+
if (config[value].nil? or config[value].empty?) then
|
69
|
+
ui.error "--#{value} is empty, define a value for it and try again"
|
70
|
+
exit 1
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Establishes the connection with proxmox server
|
75
|
+
def connection
|
76
|
+
# First, let's check we have all info needed to connect to pve
|
77
|
+
[:pve_cluster_url, :pve_node_name, :pve_user_name, :pve_user_password, :pve_user_realm].each do |value|
|
78
|
+
check_global_parameter(value)
|
79
|
+
end
|
80
|
+
|
81
|
+
@connection ||= RestClient::Resource.new(Chef::Config[:knife][:pve_cluster_url])
|
82
|
+
@auth_params ||= begin
|
83
|
+
token = nil
|
84
|
+
csrf_prevention_token = nil
|
85
|
+
@connection['access/ticket'].post :username=>Chef::Config[:knife][:pve_user_name],
|
86
|
+
:realm=>Chef::Config[:knife][:pve_user_realm],
|
87
|
+
:password=>Chef::Config[:knife][:pve_user_password] do |response, request, result, &block|
|
88
|
+
if response.code == 200 then
|
89
|
+
data = JSON.parse(response.body)
|
90
|
+
ticket = data['data']['ticket']
|
91
|
+
csrf_prevention_token = data['data']['CSRFPreventionToken']
|
92
|
+
if !ticket.nil? then
|
93
|
+
token = 'PVEAuthCookie=' + ticket.gsub!(/:/,'%3A').gsub!(/=/,'%3D')
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
{:CSRFPreventionToken => csrf_prevention_token, :cookie => token}
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# new_vmid: calculates a new vmid from the highest existing vmid
|
102
|
+
def new_vmid
|
103
|
+
vmid ||= @connection['cluster/resources?type=vm'].get @auth_params do |response, request, result, &block|
|
104
|
+
data = JSON.parse(response.body)['data']
|
105
|
+
vmids = Set[]
|
106
|
+
data.each {|entry|
|
107
|
+
vmids.add entry['vmid']
|
108
|
+
}
|
109
|
+
(vmids.max + 1).to_s
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# locate_config_value: find a value in arguments or default chef config properties
|
114
|
+
def locate_config_value(key)
|
115
|
+
key = key.to_sym
|
116
|
+
Chef::Config[:knife][key] || config[key]
|
117
|
+
end
|
118
|
+
|
119
|
+
# template_number_to_name: converts the id from the template list to the real name in the storage
|
120
|
+
# of the node
|
121
|
+
def template_number_to_name(number,storage)
|
122
|
+
template_list = []
|
123
|
+
#TODO: esta parte hay que sacarla a un modulo comun de acceso a templates
|
124
|
+
@connection["nodes/#{Chef::Config[:knife][:pve_node_name]}/storage/#{storage}/content"].get @auth_params do |response, request, result, &block|
|
125
|
+
JSON.parse(response.body)['data'].each { |entry|
|
126
|
+
if entry['content'] == 'vztmpl' then
|
127
|
+
template_list << entry['volid']
|
128
|
+
end
|
129
|
+
}
|
130
|
+
end
|
131
|
+
return CGI.escape(template_list[number.to_i])
|
132
|
+
end
|
133
|
+
|
134
|
+
# server_name_to_vmid: Use the name of the server to get the vmid
|
135
|
+
def server_name_to_vmid(name)
|
136
|
+
@connection['cluster/resources?type=vm'].get @auth_params do |response, request, result, &block|
|
137
|
+
data = JSON.parse(response.body)['data']
|
138
|
+
data.each {|entry|
|
139
|
+
return entry['vmid'] if entry['name'].to_s.match(name)
|
140
|
+
}
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# vmid_to_node: Specify the vmid and get the node in which is
|
145
|
+
def vmid_to_node(vmid)
|
146
|
+
@connection['cluster/resources?type=vm'].get @auth_params do |response, request, result, &block|
|
147
|
+
data = JSON.parse(response.body)['data']
|
148
|
+
data.each {|entry|
|
149
|
+
return entry['node'] if entry['vmid'].to_s.match(vmid.to_s)
|
150
|
+
}
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def action_response(action,response)
|
155
|
+
result = nil
|
156
|
+
taskid = nil
|
157
|
+
begin
|
158
|
+
if (response.code == 200) then
|
159
|
+
result = "OK"
|
160
|
+
else
|
161
|
+
result = "NOK: error code = " + response.code.to_s
|
162
|
+
end
|
163
|
+
taskid = JSON.parse(response.body)['data']
|
164
|
+
waitfor(taskid)
|
165
|
+
Chef::Log.debug("Action: #{action}, Result: #{result}\n")
|
166
|
+
rescue Exception => msg
|
167
|
+
result = "An exception ocurred. Use -VV to show it"
|
168
|
+
Chef::Log.debug("Action: #{action}, Result: #{msg}\n")
|
169
|
+
end
|
170
|
+
ui.msg(result)
|
171
|
+
end
|
172
|
+
|
173
|
+
# waitfor end of the task, need the taskid and the timeout
|
174
|
+
def waitfor(taskid, timeout=60)
|
175
|
+
taskstatus = nil
|
176
|
+
while taskstatus.nil? and timeout>= 0 do
|
177
|
+
print "."
|
178
|
+
@connection["nodes/#{Chef::Config[:knife][:pve_node_name]}/tasks/#{taskid}/status"].get @auth_params do |response, request, result, &block|
|
179
|
+
taskstatus = (JSON.parse(response.body)['data']['status'] == "stopped")?true:nil
|
180
|
+
end
|
181
|
+
timeout-=1
|
182
|
+
sleep(1)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def server_start(vmid)
|
187
|
+
node = vmid_to_node(vmid)
|
188
|
+
ui.msg("Starting VM #{vmid} on node #{node}....")
|
189
|
+
@connection["nodes/#{node}/openvz/#{vmid}/status/start"].post "", @auth_params do |response, request, result, &block|
|
190
|
+
# take the response and extract the taskid
|
191
|
+
action_response("server start",response)
|
192
|
+
end
|
193
|
+
|
194
|
+
end
|
195
|
+
|
196
|
+
# server_stop: Stops the server
|
197
|
+
def server_stop(vmid)
|
198
|
+
node = vmid_to_node(vmid)
|
199
|
+
ui.msg("Stopping VM #{vmid} on node #{node}...")
|
200
|
+
@connection["nodes/#{node}/openvz/#{vmid}/status/stop"].post "", @auth_params do |response, request, result, &block|
|
201
|
+
# take the response and extract the taskid
|
202
|
+
action_response("server stop",response)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
def server_create(vmid,vm_definition)
|
207
|
+
ui.msg("Creating VM #{vmid}...")
|
208
|
+
@connection["nodes/#{Chef::Config[:knife][:pve_node_name]}/openvz"].post "#{vm_definition}", @auth_params do |response, request, result, &block|
|
209
|
+
action_response("server create",response)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
# server_get_address: Returns the IP Address of the machine to chef
|
214
|
+
# field is a string, and if it doesn't exist, it will return nil
|
215
|
+
def server_get_data(vmid,field)
|
216
|
+
node = vmid_to_node(vmid)
|
217
|
+
@connection["nodes/#{node}/openvz/#{vmid}/status/current"].get @auth_params do |response, request, result, &block|
|
218
|
+
#action_response("server get data",response)
|
219
|
+
data = JSON.parse(response.body)['data'][field]
|
220
|
+
end
|
221
|
+
end
|
222
|
+
# server_destroy: Destroys the server
|
223
|
+
def server_destroy(vmid)
|
224
|
+
node = vmid_to_node(vmid)
|
225
|
+
ui.msg("Destroying VM #{vmid} on node #{node}...")
|
226
|
+
@connection["nodes/#{node}/openvz/#{vmid}"].delete @auth_params do |response, request, result, &block|
|
227
|
+
action_response("server destroy",response)
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
# Extracted from Chef::Knife.delete_object, because it has a
|
232
|
+
# confirmation step built in... By specifying the '--purge'
|
233
|
+
# flag (and also explicitly confirming the server destruction!)
|
234
|
+
# the user is already making their intent known. It is not
|
235
|
+
# necessary to make them confirm two more times.
|
236
|
+
def destroy_item(klass, name, type_name)
|
237
|
+
begin
|
238
|
+
object = klass.load(name)
|
239
|
+
object.destroy
|
240
|
+
ui.warn("Deleted #{type_name} #{name}")
|
241
|
+
rescue Net::HTTPServerException
|
242
|
+
ui.warn("Could not find a #{type_name} named #{name} to delete!")
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
|
247
|
+
|
248
|
+
|
249
|
+
end # module
|
250
|
+
end # class
|
251
|
+
end # class
|
@@ -0,0 +1,234 @@
|
|
1
|
+
require 'chef/knife/proxmox_base'
|
2
|
+
|
3
|
+
class Chef
|
4
|
+
class Knife
|
5
|
+
class ProxmoxServerCreate < Knife
|
6
|
+
|
7
|
+
include Knife::ProxmoxBase
|
8
|
+
|
9
|
+
deps do
|
10
|
+
require 'readline'
|
11
|
+
require 'chef/json_compat'
|
12
|
+
require 'chef/knife/bootstrap'
|
13
|
+
Chef::Knife::Bootstrap.load_deps
|
14
|
+
end
|
15
|
+
|
16
|
+
banner "knife proxmox server create (options)"
|
17
|
+
|
18
|
+
# TODO: parameters for openvz should be in other object
|
19
|
+
option :vm_hostname,
|
20
|
+
:short => "-H hostname",
|
21
|
+
:long => "--hostname hostname",
|
22
|
+
:description => "VM instance hostname"
|
23
|
+
|
24
|
+
option :vm_cpus,
|
25
|
+
:short => "-C CPUs",
|
26
|
+
:long => "--cpus number",
|
27
|
+
:description => "Number of cpus of the VM instance"
|
28
|
+
|
29
|
+
option :vm_memory,
|
30
|
+
:short => "-M MB",
|
31
|
+
:long => "--mem MB",
|
32
|
+
:description => "Memory in MB"
|
33
|
+
|
34
|
+
option :vm_swap,
|
35
|
+
:short => "-SW",
|
36
|
+
:long => "--swap MB",
|
37
|
+
:description => "Memory in MB for swap"
|
38
|
+
|
39
|
+
option :vm_vmid,
|
40
|
+
:short => "-I id",
|
41
|
+
:long => "--vmid id",
|
42
|
+
:description => "Id for the VM"
|
43
|
+
|
44
|
+
option :vm_disk,
|
45
|
+
:short => "-D disk",
|
46
|
+
:long => "--disk GB",
|
47
|
+
:description => "Disk space in GB"
|
48
|
+
|
49
|
+
option :vm_storage,
|
50
|
+
:short => "-ST name",
|
51
|
+
:long => "--storage name",
|
52
|
+
:description => "Name of the storage where to reserve space"
|
53
|
+
|
54
|
+
option :vm_password,
|
55
|
+
:short => "-P password",
|
56
|
+
:long => "--vm_pass password",
|
57
|
+
:description => "root password for VM (openvz only)",
|
58
|
+
:default => "proxmox"
|
59
|
+
|
60
|
+
option :vm_netif,
|
61
|
+
:short => "-N netif",
|
62
|
+
:long => "--netif netif_specification",
|
63
|
+
:description => "description of the network interface (experimental)"
|
64
|
+
|
65
|
+
option :vm_template,
|
66
|
+
:short => "-T number",
|
67
|
+
:long => "--template number",
|
68
|
+
:description => "id of the template"
|
69
|
+
|
70
|
+
option :vm_ipaddress,
|
71
|
+
:short => "-ip ipaddress",
|
72
|
+
:long => "--ipaddress IP Address",
|
73
|
+
:description => "force guest to use venet interface with this ip address"
|
74
|
+
|
75
|
+
option :bootstrap,
|
76
|
+
:long => "--[no-]bootstrap",
|
77
|
+
:description => "Bootstrap the server, enable by default",
|
78
|
+
:boolean => true,
|
79
|
+
:default => true
|
80
|
+
|
81
|
+
option :bootstrap_version,
|
82
|
+
:long => "--bootstrap-version VERSION",
|
83
|
+
:description => "The version of Chef to install",
|
84
|
+
:proc => Proc.new { |v| Chef::Config[:knife][:bootstrap_version] = v }
|
85
|
+
|
86
|
+
option :distro,
|
87
|
+
:short => "-d DISTRO",
|
88
|
+
:long => "--distro DISTRO",
|
89
|
+
:description => "Bootstrap a distro using a template; default is 'chef-full'",
|
90
|
+
:proc => Proc.new { |d| Chef::Config[:knife][:distro] = d },
|
91
|
+
:default => "chef-full"
|
92
|
+
|
93
|
+
option :template_file,
|
94
|
+
:long => "--template-file TEMPLATE",
|
95
|
+
:description => "Full path to location of template to use",
|
96
|
+
:proc => Proc.new { |t| Chef::Config[:knife][:template_file] = t },
|
97
|
+
:default => false
|
98
|
+
|
99
|
+
option :run_list,
|
100
|
+
:short => "-r RUN_LIST",
|
101
|
+
:long => "--run-list RUN_LIST",
|
102
|
+
:description => "Comma separated list of roles/recipes to apply",
|
103
|
+
:proc => lambda { |o| o.split(/[\s,]+/) },
|
104
|
+
:default => []
|
105
|
+
|
106
|
+
option :first_boot_attributes,
|
107
|
+
:short => "-j JSON_ATTRIBS",
|
108
|
+
:long => "--json-attributes",
|
109
|
+
:description => "A JSON string to be added to the first run of chef-client",
|
110
|
+
:proc => lambda { |o| JSON.parse(o) },
|
111
|
+
:default => {}
|
112
|
+
|
113
|
+
option :identity_file,
|
114
|
+
:short => "-i IDENTITY_FILE",
|
115
|
+
:long => "--identity-file IDENTITY_FILE",
|
116
|
+
:description => "The SSH identity file used for authentication"
|
117
|
+
|
118
|
+
option :host_key_verify,
|
119
|
+
:long => "--[no-]host-key-verify",
|
120
|
+
:description => "Verify host key, enabled by default",
|
121
|
+
:boolean => true,
|
122
|
+
:default => true
|
123
|
+
|
124
|
+
option :environment,
|
125
|
+
:short=> "-e environment",
|
126
|
+
:long => "--environment environment",
|
127
|
+
:description => "Chef environment",
|
128
|
+
:proc => Proc.new {|env| Chef::Config[:knife][:environment] = env },
|
129
|
+
:default => '_default'
|
130
|
+
|
131
|
+
def run
|
132
|
+
# Needed
|
133
|
+
connection
|
134
|
+
|
135
|
+
vm_id = config[:vm_vmid] || new_vmid
|
136
|
+
vm_hostname = config[:vm_hostname] || 'proxmox'
|
137
|
+
vm_storage = config[:vm_storage] || 'local'
|
138
|
+
vm_password = config[:vm_password] || 'pve123'
|
139
|
+
vm_cpus = config[:vm_cpus] || 1
|
140
|
+
vm_memory = config[:vm_memory] || 512
|
141
|
+
vm_disk = config[:vm_disk] || 4
|
142
|
+
vm_swap = config[:vm_swap] || 512
|
143
|
+
vm_ipaddress= config[:vm_ipaddress]|| nil
|
144
|
+
vm_netif = config[:vm_netif] || 'ifname%3Deth0%2Cbridge%3Dvmbr0'
|
145
|
+
vm_template = template_number_to_name(config[:vm_template],vm_storage) || 'local%3Avztmpl%2Fubuntu-11.10-x86_64-jorge2-.tar.gz'
|
146
|
+
|
147
|
+
vm_definition = "vmid=#{vm_id}&hostname=#{vm_hostname}&storage=#{vm_storage}&password=#{vm_password}&ostemplate=#{vm_template}&memory=#{vm_memory}&swap=#{vm_swap}&disk=#{vm_disk}&cpus=#{vm_cpus}"
|
148
|
+
|
149
|
+
# Add ip_address parameter to vm_definition if it's provided by CLI
|
150
|
+
if (config[:vm_ipaddress]) then
|
151
|
+
vm_definition += "&ip_address=" + vm_ipaddress
|
152
|
+
elsif (config[:vm_netif] || vm_netif) then
|
153
|
+
vm_definition += "&netif=" + vm_netif
|
154
|
+
end
|
155
|
+
|
156
|
+
Chef::Log.debug(vm_definition)
|
157
|
+
|
158
|
+
server_create(vm_id,vm_definition)
|
159
|
+
ui.msg("Preparing the server to start")
|
160
|
+
sleep(5)
|
161
|
+
server_start(vm_id)
|
162
|
+
sleep(5)
|
163
|
+
|
164
|
+
# which IP address to bootstrap
|
165
|
+
bootstrap_ip_address = server_get_data(vm_id,'ip')
|
166
|
+
ui.msg("New Server #{vm_id} has IP Address: #{server_get_data(vm_id,'ip')}")
|
167
|
+
|
168
|
+
if bootstrap_ip_address.nil?
|
169
|
+
ui.error("No IP address available for bootstrapping.")
|
170
|
+
exit 1
|
171
|
+
end
|
172
|
+
|
173
|
+
print(".") until tcp_test_ssh(bootstrap_ip_address) {
|
174
|
+
sleep @initial_sleep_delay ||= 10
|
175
|
+
puts("done")
|
176
|
+
}
|
177
|
+
|
178
|
+
# bootstrapping the node
|
179
|
+
if config[:bootstrap]
|
180
|
+
bootstrap_for_node(bootstrap_ip_address).run
|
181
|
+
else
|
182
|
+
ui.msg("Skipping bootstrap of the server because --no-bootstrap used as argument.")
|
183
|
+
end
|
184
|
+
|
185
|
+
end
|
186
|
+
|
187
|
+
def tcp_test_ssh(hostname)
|
188
|
+
tcp_socket = TCPSocket.new(hostname, 22)
|
189
|
+
readable = IO.select([tcp_socket], nil, nil, 5)
|
190
|
+
if readable
|
191
|
+
Chef::Log.debug("sshd accepting connections on #{hostname}, banner is #{tcp_socket.gets}")
|
192
|
+
yield
|
193
|
+
true
|
194
|
+
else
|
195
|
+
false
|
196
|
+
end
|
197
|
+
rescue Errno::ETIMEDOUT
|
198
|
+
false
|
199
|
+
rescue Errno::EPERM
|
200
|
+
false
|
201
|
+
rescue Errno::ECONNREFUSED
|
202
|
+
sleep 2
|
203
|
+
false
|
204
|
+
rescue Errno::EHOSTUNREACH
|
205
|
+
sleep 2
|
206
|
+
false
|
207
|
+
ensure
|
208
|
+
tcp_socket && tcp_socket.close
|
209
|
+
end
|
210
|
+
|
211
|
+
def bootstrap_for_node(bootstrap_ip_address)
|
212
|
+
bootstrap = Chef::Knife::Bootstrap.new
|
213
|
+
bootstrap.name_args = [bootstrap_ip_address]
|
214
|
+
bootstrap.config[:run_list] = config[:run_list]
|
215
|
+
bootstrap.config[:environment] = locate_config_value(:environment)
|
216
|
+
# bootstrap.config[:first_boot_attributes] = config[:first_boot_attributes]
|
217
|
+
bootstrap.config[:ssh_user] = "root"
|
218
|
+
bootstrap.config[:ssh_password] = config[:vm_password]
|
219
|
+
# bootstrap.config[:identity_file] = config[:identity_file]
|
220
|
+
# bootstrap.config[:host_key_verify] = config[:host_key_verify]
|
221
|
+
bootstrap.config[:chef_node_name] = config[:vm_hostname]
|
222
|
+
# bootstrap.config[:prerelease] = config[:prerelease]
|
223
|
+
bootstrap.config[:bootstrap_version] = locate_config_value(:bootstrap_version)
|
224
|
+
bootstrap.config[:distro] = locate_config_value(:distro)
|
225
|
+
# bootstrap will run as root...sudo (by default) also messes up Ohai on CentOS boxes
|
226
|
+
# bootstrap.config[:use_sudo] = false
|
227
|
+
bootstrap.config[:template_file] = locate_config_value(:template_file)
|
228
|
+
|
229
|
+
pp bootstrap.config
|
230
|
+
bootstrap
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|