chef-provisioning-opennebula 0.3.4 → 0.4.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +34 -35
- data/lib/chef/provider/one_image.rb +69 -48
- data/lib/chef/provider/one_template.rb +113 -35
- data/lib/chef/provider/one_user.rb +1 -1
- data/lib/chef/provider/one_vnet.rb +2 -1
- data/lib/chef/provider/one_vnet_lease.rb +2 -17
- data/lib/chef/provisioning/opennebula_driver/credentials.rb +6 -2
- data/lib/chef/provisioning/opennebula_driver/driver.rb +69 -51
- data/lib/chef/provisioning/opennebula_driver/one_lib.rb +192 -53
- data/lib/chef/provisioning/opennebula_driver/version.rb +1 -1
- data/lib/chef/resource/one_image.rb +3 -2
- data/lib/chef/resource/one_template.rb +7 -5
- data/lib/chef/resource/one_vnet.rb +1 -0
- metadata +6 -4
@@ -36,7 +36,7 @@ class Chef
|
|
36
36
|
|
37
37
|
def exists?(filter)
|
38
38
|
new_driver = driver
|
39
|
-
@current_user = new_driver.one.get_resource(
|
39
|
+
@current_user = new_driver.one.get_resource(:user, filter)
|
40
40
|
Chef::Log.debug("user '#{filter}' exists: #{!@current_user.nil?}")
|
41
41
|
!@current_user.nil?
|
42
42
|
end
|
@@ -36,7 +36,7 @@ class Chef
|
|
36
36
|
|
37
37
|
def exists?(filter)
|
38
38
|
new_driver = driver
|
39
|
-
@current_vnet = new_driver.one.get_resource(
|
39
|
+
@current_vnet = new_driver.one.get_resource(:virtualnetwork, filter)
|
40
40
|
Chef::Log.debug("VNET '#{filter}' exists: #{!@current_vnet.nil?}")
|
41
41
|
!@current_vnet.nil?
|
42
42
|
end
|
@@ -53,6 +53,7 @@ class Chef
|
|
53
53
|
vnet = new_driver.one.allocate_vnet(template_str, @new_resource.cluster_id)
|
54
54
|
Chef::Log.debug(template_str)
|
55
55
|
fail "failed to create vnet '#{@new_resource.name}': #{vnet.message}" if OpenNebula.is_error?(vnet)
|
56
|
+
new_driver.one.chmod_resource(vnet, new_resource.mode)
|
56
57
|
@new_resource.updated_by_last_action(true)
|
57
58
|
end
|
58
59
|
end
|
@@ -37,7 +37,7 @@ class Chef
|
|
37
37
|
def exists?
|
38
38
|
new_driver = driver
|
39
39
|
filter = { @new_resource.vnet.is_a?(Integer) ? :id : :name => @new_resource.vnet }
|
40
|
-
@current_vnet = new_driver.one.get_resource(
|
40
|
+
@current_vnet = new_driver.one.get_resource(:virtualnetwork, filter)
|
41
41
|
fail "vnet '#{@new_resource.vnet}' does not exist" if @current_vnet.nil?
|
42
42
|
@current_vnet.info!
|
43
43
|
hash = @current_vnet.to_hash
|
@@ -49,22 +49,7 @@ class Chef
|
|
49
49
|
ar_pool = get_ar_pool(ar_pool, @new_resource.ar_id.to_s)
|
50
50
|
fail "ar_id not found '#{@new_resource.ar_id}'" if ar_pool.nil?
|
51
51
|
end
|
52
|
-
|
53
|
-
fail "'#{name}' is already allocated to a VM (ID: #{vm})" unless available
|
54
|
-
available
|
55
|
-
# ar_pool.each do |a|
|
56
|
-
# if a['AR']['LEASES']['LEASE']
|
57
|
-
# [a['AR']['LEASES']['LEASE']].flatten.each do |l|
|
58
|
-
# if l[lookup] && l[lookup] == @new_resource.name
|
59
|
-
# exists = true
|
60
|
-
# vm = l['VM'].to_i
|
61
|
-
# break
|
62
|
-
# end
|
63
|
-
# end
|
64
|
-
# end
|
65
|
-
# end
|
66
|
-
# fail "'#{name}' is already allocated to a VM (ID: #{vm})" if exists && vm > -1
|
67
|
-
# (exists && vm == -1)
|
52
|
+
lease_available?(ar_pool, lookup)
|
68
53
|
end
|
69
54
|
|
70
55
|
action :hold do
|
@@ -69,8 +69,12 @@ class Chef
|
|
69
69
|
begin
|
70
70
|
content_hash = JSON.parse(File.read(file), :symbolize_names => true)
|
71
71
|
content_hash.each { |k, v| json[k.to_s] = v }
|
72
|
-
rescue
|
73
|
-
Chef::Log.warn("Failed to read
|
72
|
+
rescue StandardError => e_file
|
73
|
+
Chef::Log.warn("Failed to read config file #{file}: #{e_file.message}")
|
74
|
+
rescue JSON::ParserError => e_json
|
75
|
+
Chef::Log.warn("Failed to parse config file #{file}: #{e_json.message}")
|
76
|
+
rescue
|
77
|
+
Chef::Log.warn("Failed to read or parse config file #{file}: #{$!}")
|
74
78
|
end
|
75
79
|
@credentials.merge!(json)
|
76
80
|
end
|
@@ -21,6 +21,7 @@ require 'chef/provisioning/transport/ssh'
|
|
21
21
|
require 'chef/provisioning/opennebula_driver/version'
|
22
22
|
require 'chef/provisioning/opennebula_driver/one_lib'
|
23
23
|
require 'chef/provisioning/opennebula_driver/credentials'
|
24
|
+
require 'net/ssh/proxy/command'
|
24
25
|
|
25
26
|
class Chef
|
26
27
|
module Provisioning
|
@@ -84,14 +85,7 @@ class Chef
|
|
84
85
|
#
|
85
86
|
def initialize(driver_url, config)
|
86
87
|
super
|
87
|
-
|
88
|
-
endpoint = scan[2]
|
89
|
-
profile_name = scan[3]
|
90
|
-
fail "OpenNebula endpoint must be specified in 'driver_url': #{driver_url}" if endpoint.nil?
|
91
|
-
|
92
|
-
profile = profile_name ? one_credentials[profile_name] : one_credentials.default
|
93
|
-
Chef::Log.warn("':credentials' and ':secret_file' will be deprecated in next version in favour of 'opennebula:<endpoint>:<profile_name>'") if profile_name.nil?
|
94
|
-
@one = OneLib.new(profile[:credentials], endpoint, profile[:options] || {})
|
88
|
+
@one = OneLib.new(:driver_url => driver_url)
|
95
89
|
end
|
96
90
|
|
97
91
|
def self.from_url(driver_url, config)
|
@@ -127,27 +121,34 @@ class Chef
|
|
127
121
|
# saved back after allocate_machine completes.
|
128
122
|
#
|
129
123
|
def allocate_machine(action_handler, machine_spec, machine_options)
|
130
|
-
fqdn = begin
|
131
|
-
machine_options.bootstrap_options[:enforce_chef_fqdn]
|
132
|
-
rescue NoMethodError
|
133
|
-
false
|
134
|
-
end
|
135
|
-
fail "Machine 'name' must be a FQDN" if fqdn && machine_spec.name.scan(/([a-z0-9-]+\.)/i).length == 0
|
136
124
|
instance = instance_for(machine_spec)
|
125
|
+
return machine_spec unless instance.nil?
|
137
126
|
|
138
|
-
|
139
|
-
fail "'bootstrap_options' must be specified"
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
127
|
+
unless machine_options.bootstrap_options
|
128
|
+
fail "'bootstrap_options' must be specified"
|
129
|
+
end
|
130
|
+
check_unique_names(machine_options, machine_spec)
|
131
|
+
action_handler.perform_action "created vm '#{machine_spec.name}'" do
|
132
|
+
Chef::Log.debug(machine_options)
|
133
|
+
tpl = @one.get_template(machine_spec.name,
|
134
|
+
machine_options.bootstrap_options)
|
135
|
+
vm = @one.allocate_vm(tpl)
|
136
|
+
populate_node_object(machine_spec, machine_options, vm)
|
137
|
+
@one.chmod_resource(vm, machine_options.bootstrap_options[:mode])
|
138
|
+
|
139
|
+
# This option allows to manipulate how the machine shows up
|
140
|
+
# in the OpenNebula UI and CLI tools. We either set the VM
|
141
|
+
# name to the short hostname of the machine, rename it
|
142
|
+
# to the String passed to us, or leave it alone.
|
143
|
+
if machine_options[:vm_name] == :short
|
144
|
+
@one.rename_vm(vm, machine_spec.name.split('.').first)
|
145
|
+
elsif machine_options[:vm_name].is_a?(String)
|
146
|
+
@one.rename_vm(vm, machine_options[:vm_name])
|
147
|
+
# else use machine_spec.name for name in OpenNebula
|
146
148
|
end
|
147
|
-
Chef::Log.debug(machine_spec.reference)
|
148
|
-
else
|
149
|
-
Chef::Log.info("vm '#{machine_spec.name}' already exists - nothing to do")
|
150
149
|
end
|
150
|
+
Chef::Log.debug(machine_spec.reference)
|
151
|
+
|
151
152
|
machine_spec
|
152
153
|
end
|
153
154
|
|
@@ -178,8 +179,9 @@ class Chef
|
|
178
179
|
deployed = @one.wait_for_vm(instance.id)
|
179
180
|
machine_spec.reference['name'] = deployed.name
|
180
181
|
machine_spec.reference['state'] = deployed.state_str
|
181
|
-
|
182
|
-
|
182
|
+
if deployed.to_hash['VM']['TEMPLATE']['NIC']
|
183
|
+
ip = [deployed.to_hash['VM']['TEMPLATE']['NIC']].flatten.first['IP']
|
184
|
+
end
|
183
185
|
fail "Could not get IP from VM '#{deployed.name}'" if ip.nil? || ip.to_s.empty?
|
184
186
|
machine_spec.reference['ip'] = ip
|
185
187
|
machine = machine_for(machine_spec, machine_options)
|
@@ -241,8 +243,8 @@ class Chef
|
|
241
243
|
instance = instance_for(machine_spec)
|
242
244
|
if !instance.nil?
|
243
245
|
action_handler.perform_action "powered off machine #{machine_spec.name} (#{machine_spec.reference['instance_id']})" do
|
244
|
-
if machine_spec.reference[:is_shutdown] || machine_options
|
245
|
-
hard = machine_spec.reference[:shutdown_hard] || machine_options
|
246
|
+
if machine_spec.reference[:is_shutdown] || (machine_options[:bootstrap_options] && machine_options[:bootstrap_options][:is_shutdown])
|
247
|
+
hard = machine_spec.reference[:shutdown_hard] || machine_options[:bootstrap_options][:shutdown_hard] || false
|
246
248
|
instance.shutdown(hard)
|
247
249
|
else
|
248
250
|
instance.stop
|
@@ -264,19 +266,19 @@ class Chef
|
|
264
266
|
def allocate_image(action_handler, image_spec, image_options, machine_spec)
|
265
267
|
if image_spec.reference
|
266
268
|
# check if image already exists
|
267
|
-
image = @one.get_resource(
|
269
|
+
image = @one.get_resource(:image, :id => image_spec.reference['image_id'].to_i)
|
268
270
|
action_handler.report_progress "image #{image_spec.name} (ID: #{image_spec.reference['image_id']}) already exists" unless image.nil?
|
269
271
|
else
|
270
272
|
action_handler.perform_action "create image #{image_spec.name} from machine ID #{machine_spec.reference['instance_id']} with options #{image_options.inspect}" do
|
271
|
-
vm = @one.get_resource(
|
273
|
+
vm = @one.get_resource(:virtualmachine, :id => machine_spec.reference['instance_id'])
|
272
274
|
fail "allocate_image: VM does not exist" if vm.nil?
|
273
275
|
# set default disk ID
|
274
276
|
disk_id = 1
|
275
277
|
if image_options.disk_id
|
276
278
|
disk_id = image_options.disk_id.is_a?(Integer) ? image_options.disk_id : @one.get_disk_id(vm, new_resource.disk_id)
|
277
279
|
end
|
280
|
+
new_img = @one.version_ge_4_14 ? vm.disk_saveas(disk_id, image_spec.name) : vm.disk_snapshot(disk_id, image_spec.name, "", true)
|
278
281
|
|
279
|
-
new_img = vm.disk_snapshot(disk_id, image_spec.name, "", true)
|
280
282
|
fail "Failed to create snapshot '#{new_resource.name}': #{new_img.message}" if OpenNebula.is_error?(new_img)
|
281
283
|
populate_img_object(image_spec, new_image)
|
282
284
|
end
|
@@ -292,7 +294,7 @@ class Chef
|
|
292
294
|
# @param [Hash] image_options
|
293
295
|
# A set of options representing the desired state of the machine
|
294
296
|
def ready_image(action_handler, image_spec, _image_options)
|
295
|
-
img = @one.get_resource(
|
297
|
+
img = @one.get_resource(:image, :id => image_spec.reference['image_id'].to_i)
|
296
298
|
fail "Image #{image_spec.name} (#{image_spec.reference['image_id']}) does not exist" if img.nil?
|
297
299
|
action_handler.perform_action "image #{image_spec.name} is ready" do
|
298
300
|
deployed = @one.wait_for_img(img.name, img.id)
|
@@ -310,7 +312,7 @@ class Chef
|
|
310
312
|
# @param [Hash] image_options
|
311
313
|
# A set of options representing the desired state of the machine
|
312
314
|
def destroy_image(action_handler, image_spec, _image_options)
|
313
|
-
img = @one.get_resource(
|
315
|
+
img = @one.get_resource(:image, :id => image_spec.location['image_id'].to_i)
|
314
316
|
if img.nil?
|
315
317
|
action_handler.report_progress "image #{image_spec.name} (#{image_spec.location['image_id']}) does not exist - nothing to do"
|
316
318
|
else
|
@@ -441,8 +443,18 @@ class Chef
|
|
441
443
|
end
|
442
444
|
end
|
443
445
|
|
444
|
-
def check_unique_names(
|
445
|
-
|
446
|
+
def check_unique_names(machine_options, machine_spec)
|
447
|
+
return unless machine_options.bootstrap_options[:unique_names]
|
448
|
+
hostname = if machine_options[:vm_name] == :short
|
449
|
+
machine_spec.name.split('.').first
|
450
|
+
elsif machine_options[:vm_name].is_a?(String)
|
451
|
+
machine_options[:vm_name]
|
452
|
+
else
|
453
|
+
machine_spec.name
|
454
|
+
end
|
455
|
+
|
456
|
+
return if @one.get_resource(:virtualmachine, :name => hostname).nil?
|
457
|
+
fail "VM with name '#{hostname}' already exists"
|
446
458
|
end
|
447
459
|
|
448
460
|
def populate_node_object(machine_spec, machine_options, vm)
|
@@ -482,11 +494,11 @@ class Chef
|
|
482
494
|
if machine_spec.reference
|
483
495
|
fail "Switching a machine's driver from #{machine_spec.driver_url} to #{driver_url} is not supported!" \
|
484
496
|
" Use machine :destroy and then :create the machine on the new driver." if machine_spec.driver_url != driver_url
|
485
|
-
instance = @one.get_resource(
|
497
|
+
instance = @one.get_resource(:virtualmachine, :id => machine_spec.reference['instance_id'].to_i)
|
486
498
|
elsif machine_spec.location
|
487
499
|
fail "Switching a machine's driver from #{machine_spec.driver_url} to #{driver_url} is not supported!" \
|
488
500
|
" Use machine :destroy and then :create the machine on the new driver." if machine_spec.driver_url != driver_url
|
489
|
-
instance = @one.get_resource(
|
501
|
+
instance = @one.get_resource(:virtualmachine, :id => machine_spec.location['server_id'].to_i)
|
490
502
|
unless instance.nil?
|
491
503
|
# Convert from previous driver
|
492
504
|
machine_spec.reference = {
|
@@ -506,7 +518,9 @@ class Chef
|
|
506
518
|
instance = instance_for(machine_spec)
|
507
519
|
fail "#{machine_spec.name} (#{machine_spec.reference['instance_id']}) does not exist!" if instance.nil?
|
508
520
|
# TODO: Support Windoze VMs (see chef-provisioning-vagrant)
|
509
|
-
Chef::Provisioning::Machine::UnixMachine.new(machine_spec,
|
521
|
+
Chef::Provisioning::Machine::UnixMachine.new(machine_spec,
|
522
|
+
transport_for(machine_spec, machine_options, instance),
|
523
|
+
convergence_strategy_for(machine_spec, machine_options))
|
510
524
|
end
|
511
525
|
|
512
526
|
def get_ssh_user(machine_spec, machine_options)
|
@@ -516,33 +530,37 @@ class Chef
|
|
516
530
|
end
|
517
531
|
|
518
532
|
def transport_for(machine_spec, machine_options, _instance)
|
519
|
-
# TODO: Store ssh_options in machine_spec.reference ???
|
520
533
|
ssh_options = {
|
521
534
|
:keys_only => false,
|
522
535
|
:forward_agent => true,
|
523
536
|
:use_agent => true,
|
524
|
-
:user_known_hosts_file => '/dev/null'
|
537
|
+
:user_known_hosts_file => '/dev/null',
|
538
|
+
:timeout => 10
|
525
539
|
}.merge(machine_options[:ssh_options] || {})
|
540
|
+
ssh_options[:proxy] = Net::SSH::Proxy::Command.new(ssh_options[:proxy]) if ssh_options.key?(:proxy)
|
541
|
+
|
542
|
+
connection_timeout = machine_options[:connection_timeout] || 300
|
526
543
|
username = get_ssh_user(machine_spec, machine_options)
|
527
|
-
|
544
|
+
|
528
545
|
options = {}
|
529
546
|
if machine_spec.reference[:sudo] || (!machine_spec.reference.key?(:sudo) && username != 'root')
|
530
547
|
options[:prefix] = 'sudo '
|
531
548
|
end
|
549
|
+
options[:ssh_pty_enable] = machine_options[:ssh_pty_enable] || true
|
550
|
+
# User provided ssh_gateway takes precedence over machine_spec value
|
551
|
+
options[:ssh_gateway] = machine_options[:ssh_gateway] || machine_spec.reference['ssh_gateway']
|
532
552
|
|
533
|
-
|
534
|
-
options[:ssh_pty_enable] = true
|
535
|
-
options[:ssh_gateway] = machine_spec.reference['ssh_gateway'] if machine_spec.reference.key?('ssh_gateway')
|
536
|
-
|
537
|
-
transport = Chef::Provisioning::Transport::SSH.new(machine_spec.reference['ip'], username, ssh_options, options, conf)
|
553
|
+
transport = Chef::Provisioning::Transport::SSH.new(machine_spec.reference['ip'], username, ssh_options, options, config)
|
538
554
|
|
539
555
|
# wait up to 5 min to establish SSH connection
|
540
|
-
|
556
|
+
connect_sleep = 3
|
557
|
+
start = Time.now
|
558
|
+
loop do
|
541
559
|
break if transport.available?
|
542
|
-
|
543
|
-
Chef::Log.
|
560
|
+
fail "Failed to establish SSH connection to '#{machine_spec.name}'" if Time.now > start + connection_timeout.to_i
|
561
|
+
Chef::Log.info("Waiting for SSH server ...")
|
562
|
+
sleep connect_sleep
|
544
563
|
end
|
545
|
-
fail "Failed to establish SSH connection to '#{machine_spec.name}'" unless transport.available?
|
546
564
|
transport
|
547
565
|
end
|
548
566
|
|
@@ -33,11 +33,11 @@ end
|
|
33
33
|
#
|
34
34
|
class Chef
|
35
35
|
#
|
36
|
-
# Module
|
36
|
+
# Module extension.
|
37
37
|
#
|
38
38
|
module Provisioning
|
39
39
|
#
|
40
|
-
# Module
|
40
|
+
# Module extension.
|
41
41
|
#
|
42
42
|
module OpenNebulaDriver
|
43
43
|
#
|
@@ -50,17 +50,63 @@ class Chef
|
|
50
50
|
# Implementation.
|
51
51
|
#
|
52
52
|
class OneLib
|
53
|
-
attr_accessor :client
|
54
|
-
|
55
|
-
def initialize(
|
53
|
+
attr_accessor :client, :version_ge_4_14
|
54
|
+
|
55
|
+
def initialize(args)
|
56
|
+
credentials = args[:credentials]
|
57
|
+
endpoint = args[:endpoint]
|
58
|
+
options = args[:options] || {}
|
59
|
+
if args[:driver_url]
|
60
|
+
scan = args[:driver_url].match(%r/(opennebula):(https?:\/\/[^:\/]+ (?::[0-9]{2,5})? (?:\/[^:\s]+) ) :?([^:\s]+)?/x)
|
61
|
+
endpoint = scan[2]
|
62
|
+
profile = scan[3]
|
63
|
+
fail "'driver_url' option has invalid format: #{args[:driver_url]}" if endpoint.nil? || profile.nil?
|
64
|
+
one_profile = Chef::Provisioning::OpenNebulaDriver::Credentials.new[profile]
|
65
|
+
credentials = one_profile[:credentials]
|
66
|
+
options = one_profile[:options] || {}
|
67
|
+
end
|
56
68
|
@client = OpenNebula::Client.new(credentials, endpoint, options)
|
57
69
|
rc = @client.get_version
|
58
70
|
raise OpenNebulaException, rc.message if OpenNebula.is_error?(rc)
|
71
|
+
|
72
|
+
server_version = rc.split('.').map(&:to_i)
|
73
|
+
gem_version = Gem.loaded_specs["opennebula"].version.to_s.split('.').map(&:to_i)
|
74
|
+
@version_ge_4_14 = gem_version[0] > 4 || (gem_version[0] == 4 && gem_version[1] >= 14)
|
75
|
+
|
76
|
+
version_mismatch_warning(server_version, gem_version) if server_version != gem_version
|
59
77
|
end
|
60
78
|
|
61
|
-
#
|
79
|
+
# This function provides a more readable way to return a
|
80
|
+
# OpenNebula::*Pool back to a caller. The caller simply needs
|
81
|
+
# to pass the pool type in symbol form to us, and we send back
|
82
|
+
# the pool. The idea is that passing :template will give us
|
83
|
+
# back OpenNebula::TemplatePool, etc. for consistency with
|
84
|
+
# the OpenNebula API calls. Note that we are still supporting
|
85
|
+
# the old API, while logging a warning that the old format
|
86
|
+
# is deprecated. Users should expect the old format to disappear
|
87
|
+
# in a future release.
|
62
88
|
def get_pool(type)
|
63
89
|
fail "pool type must be specified" if type.nil?
|
90
|
+
key = type.capitalize
|
91
|
+
key = :SecurityGroup if key == :Securitygroup
|
92
|
+
key = :VirtualMachine if key == :Virtualmachine
|
93
|
+
key = :VirtualNetwork if key == :Virtualnetwork
|
94
|
+
if key == :Documentpooljson # Doesn't match the template below
|
95
|
+
return OpenNebula::DocumentPoolJSON.new(@client)
|
96
|
+
end
|
97
|
+
|
98
|
+
pool_class = Object.const_get("OpenNebula::#{key}Pool")
|
99
|
+
pool_class.new(@client)
|
100
|
+
rescue NameError
|
101
|
+
_get_pool(type.to_s) # This will raise an exception if invalid.
|
102
|
+
end
|
103
|
+
|
104
|
+
# TODO: add filtering to pool retrieval (type, start, end, user)
|
105
|
+
def _get_pool(type)
|
106
|
+
Chef::Log.warn("Use of deprecated pool type '#{type}' detected. " \
|
107
|
+
"Switch to symbol form; i.e. '[:#{type}]' to use the " \
|
108
|
+
"'OpenNebula::#{type}Pool'.")
|
109
|
+
|
64
110
|
case type
|
65
111
|
when 'acl'
|
66
112
|
OpenNebula::AclPool.new(@client)
|
@@ -76,11 +122,11 @@ class Chef
|
|
76
122
|
OpenNebula::GroupPool.new(@client)
|
77
123
|
when 'host'
|
78
124
|
OpenNebula::HostPool.new(@client)
|
79
|
-
when 'img'
|
125
|
+
when 'image', 'img'
|
80
126
|
OpenNebula::ImagePool.new(@client, -1)
|
81
127
|
when 'secgroup'
|
82
128
|
OpenNebula::SecurityGroupPool.new(@client)
|
83
|
-
when 'tpl'
|
129
|
+
when 'tpl', 'vmtemplate', 'template'
|
84
130
|
OpenNebula::TemplatePool.new(@client, -1)
|
85
131
|
when 'user'
|
86
132
|
OpenNebula::UserPool.new(@client)
|
@@ -91,30 +137,40 @@ class Chef
|
|
91
137
|
when 'vnet'
|
92
138
|
OpenNebula::VirtualNetworkPool.new(@client)
|
93
139
|
else
|
94
|
-
fail "Invalid pool type specified."
|
140
|
+
fail "Invalid pool type '#{type}' specified."
|
95
141
|
end
|
96
142
|
end
|
143
|
+
private :_get_pool
|
97
144
|
|
145
|
+
# TODO: Always return an array
|
98
146
|
def get_resource(resource_type, filter = {})
|
147
|
+
fail "resource_type must be specified" if resource_type.nil?
|
148
|
+
|
149
|
+
# Ensure the hash key is correct when searching
|
150
|
+
hash_key = resource_type.to_s.upcase
|
151
|
+
hash_key = 'VMTEMPLATE' if hash_key == 'TPL' || hash_key == 'TEMPLATE'
|
152
|
+
|
99
153
|
if filter.empty?
|
100
154
|
Chef::Log.warn("get_resource: 'name' or 'id' must be provided")
|
101
155
|
return nil
|
102
156
|
end
|
103
157
|
pool = get_pool(resource_type)
|
104
158
|
|
105
|
-
if resource_type != 'user' && filter[:id] && !filter[:id].nil?
|
159
|
+
if resource_type.to_s != 'user' && filter[:id] && !filter[:id].nil?
|
106
160
|
pool.info!(-2, filter[:id].to_i, filter[:id].to_i)
|
107
161
|
return pool.first
|
108
162
|
end
|
109
163
|
|
110
|
-
if resource_type == 'user'
|
164
|
+
if resource_type.to_s == 'user'
|
111
165
|
pool.info
|
112
166
|
else
|
113
|
-
pool.info!(-2, -1, -1)
|
167
|
+
pool.info!(-2, -1, -1)
|
114
168
|
end
|
115
169
|
resources = []
|
116
170
|
pool.each do |res|
|
117
|
-
|
171
|
+
next unless res.name == filter[:name]
|
172
|
+
next if filter[:uname] && res.to_hash[hash_key]['UNAME'] != filter[:uname]
|
173
|
+
resources << res
|
118
174
|
end
|
119
175
|
return nil if resources.size == 0
|
120
176
|
return resources[0] if resources.size == 1
|
@@ -133,7 +189,7 @@ class Chef
|
|
133
189
|
|
134
190
|
def wait_for_vm(id, end_state = nil)
|
135
191
|
end_state ||= 'RUNNING'
|
136
|
-
vm = get_resource(
|
192
|
+
vm = get_resource(:virtualmachine, :id => id)
|
137
193
|
fail "Did not find VM with ID: #{id}" unless vm
|
138
194
|
while vm.lcm_state_str != end_state.upcase
|
139
195
|
vm.info
|
@@ -144,53 +200,63 @@ class Chef
|
|
144
200
|
vm
|
145
201
|
end
|
146
202
|
|
147
|
-
def
|
203
|
+
def rename_vm(res, name)
|
204
|
+
rc = res.rename(name)
|
205
|
+
raise OpenNebulaException, rc.message if OpenNebula.is_error?(rc)
|
206
|
+
end
|
207
|
+
|
208
|
+
def upload_img(img_config)
|
148
209
|
template = <<-EOTPL
|
149
|
-
NAME = #{name}
|
150
|
-
PATH = \"#{path}\"
|
151
|
-
DRIVER = #{driver}
|
152
|
-
DESCRIPTION = \"#{description}\"
|
153
|
-
EOTPL
|
154
|
-
|
155
|
-
template << "TYPE = #{type}\n" unless type.nil?
|
156
|
-
template << "
|
157
|
-
template << "
|
158
|
-
template << "
|
159
|
-
template << "
|
160
|
-
template << "
|
161
|
-
template << "
|
162
|
-
template << "
|
163
|
-
template << "
|
210
|
+
NAME = #{img_config[:name]}
|
211
|
+
PATH = \"#{img_config[:path]}\"
|
212
|
+
DRIVER = #{img_config[:driver]}
|
213
|
+
DESCRIPTION = \"#{img_config[:description]}\"
|
214
|
+
EOTPL
|
215
|
+
|
216
|
+
template << "TYPE = #{img_config[:type]}\n" unless img_config[:type].nil?
|
217
|
+
template << "DEV_PREFIX = #{img_config[:prefix]}\n" unless img_config[:prefix].nil?
|
218
|
+
template << "TARGET = #{img_config[:target]}\n" unless img_config[:target].nil?
|
219
|
+
template << "DISK_STYPE = #{img_config[:disk_type]}\n" unless img_config[:disk_type].nil?
|
220
|
+
template << "SOURCE = #{img_config[:source]}\n" unless img_config[:source].nil?
|
221
|
+
template << "SIZE = #{img_config[:size]}\n" unless img_config[:size].nil?
|
222
|
+
template << "FSTYPE = #{img_config[:fs_type]}\n" unless img_config[:fs_type].nil?
|
223
|
+
template << "PUBLIC = #{img_config[:public] ? 'YES' : 'NO'}\n" unless img_config[:public].nil?
|
224
|
+
template << "PERSISTENT = #{img_config[:persistent] ? 'YES' : 'NO'}\n" unless img_config[:persistent].nil?
|
164
225
|
|
165
226
|
Chef::Log.debug("\n#{template}")
|
166
|
-
|
167
227
|
image = OpenNebula::Image.new(OpenNebula::Image.build_xml, @client)
|
168
228
|
raise OpenNebulaException, image.message if OpenNebula.is_error?(image)
|
169
|
-
rc = image.allocate(template,
|
229
|
+
rc = image.allocate(template, img_config[:datastore_id].to_i)
|
230
|
+
raise OpenNebulaException, rc.message if OpenNebula.is_error?(rc)
|
231
|
+
Chef::Log.debug("Waiting for image '#{img_config[:name]}' (#{image.id}) to be ready")
|
232
|
+
wait_for_img(img_config[:name], image.id)
|
233
|
+
chmod_resource(image, img_config[:mode])
|
234
|
+
end
|
235
|
+
|
236
|
+
def chmod_resource(res = nil, octet = nil)
|
237
|
+
rc = res.chmod_octet(octet) unless res.nil? || octet.nil?
|
170
238
|
raise OpenNebulaException, rc.message if OpenNebula.is_error?(rc)
|
171
|
-
Chef::Log.debug("Waiting for image '#{name}' (#{image.id}) to be ready")
|
172
|
-
wait_for_img(name, image.id)
|
173
239
|
end
|
174
240
|
|
175
|
-
def allocate_img(
|
241
|
+
def allocate_img(img_config)
|
176
242
|
template = <<-EOT
|
177
|
-
NAME = #{name}
|
178
|
-
TYPE = #{type}
|
179
|
-
FSTYPE = #{fstype}
|
180
|
-
SIZE = #{size}
|
181
|
-
PERSISTENT = #{persistent ? 'YES' : 'NO'}
|
243
|
+
NAME = #{img_config[:name]}
|
244
|
+
TYPE = #{img_config[:type]}
|
245
|
+
FSTYPE = #{img_config[:fstype]}
|
246
|
+
SIZE = #{img_config[:size]}
|
247
|
+
PERSISTENT = #{img_config[:persistent] ? 'YES' : 'NO'}
|
182
248
|
|
183
|
-
DRIVER = #{driver}
|
184
|
-
DEV_PREFIX = #{prefix}
|
185
|
-
EOT
|
249
|
+
DRIVER = #{img_config[:driver]}
|
250
|
+
DEV_PREFIX = #{img_config[:prefix]}
|
251
|
+
EOT
|
186
252
|
|
187
253
|
img = OpenNebula::Image.new(OpenNebula::Image.build_xml, @client)
|
188
254
|
raise OpenNebulaException, img.message if OpenNebula.is_error?(img)
|
189
255
|
|
190
|
-
rc = img.allocate(template,
|
256
|
+
rc = img.allocate(template, img_config[:datastore_id])
|
191
257
|
raise OpenNebulaException, rc.message if OpenNebula.is_error?(rc)
|
192
258
|
|
193
|
-
Chef::Log.debug("Allocated disk image #{name} (#{img.id})")
|
259
|
+
Chef::Log.debug("Allocated disk image #{img_config[:name]} (#{img.id})")
|
194
260
|
img
|
195
261
|
end
|
196
262
|
|
@@ -198,7 +264,7 @@ EOT
|
|
198
264
|
cur_state = nil
|
199
265
|
image = nil
|
200
266
|
state = 'INIT'
|
201
|
-
pool = get_pool(
|
267
|
+
pool = get_pool(:image)
|
202
268
|
while state == 'INIT' || state == 'LOCKED'
|
203
269
|
pool.info!(-2, img_id, img_id)
|
204
270
|
pool.each do |img|
|
@@ -230,6 +296,13 @@ EOT
|
|
230
296
|
vnet
|
231
297
|
end
|
232
298
|
|
299
|
+
def update_template(template_id, template_str)
|
300
|
+
template = OpenNebula::Template.new(OpenNebula::Template.build_xml(template_id), @client)
|
301
|
+
rc = template.update(template_str) unless OpenNebula.is_error?(template)
|
302
|
+
raise OpenNebulaException, rc.message if OpenNebula.is_error?(rc)
|
303
|
+
rc
|
304
|
+
end
|
305
|
+
|
233
306
|
def allocate_template(template_str)
|
234
307
|
tpl = OpenNebula::Template.new(OpenNebula::Template.build_xml, @client)
|
235
308
|
rc = tpl.allocate(template_str) unless OpenNebula.is_error?(tpl)
|
@@ -270,24 +343,39 @@ EOT
|
|
270
343
|
elsif !options[:template].nil?
|
271
344
|
t_hash = template_from_hash(options)
|
272
345
|
else
|
273
|
-
fail "To create a VM you must specify one of ':template',
|
346
|
+
fail "To create a VM you must specify one of ':template', " \
|
347
|
+
"':template_id', or ':template_name' option " \
|
348
|
+
"in ':bootstrap_options'"
|
274
349
|
end
|
275
350
|
fail "Inavlid VM template : #{t_hash}" if t_hash.nil? || t_hash.empty?
|
276
351
|
tpl_updates = options[:template_options] || {}
|
277
352
|
if options[:user_variables]
|
278
|
-
Chef::Log.warn("':user_variables' will be deprecated in next
|
353
|
+
Chef::Log.warn("':user_variables' will be deprecated in next " \
|
354
|
+
"version in favour of ':template_options'")
|
279
355
|
recursive_merge(tpl_updates, options[:user_variables])
|
280
356
|
end
|
281
357
|
recursive_merge(t_hash, tpl_updates) unless tpl_updates.empty?
|
282
|
-
|
358
|
+
if options[:enforce_chef_fqdn]
|
359
|
+
Chef::Log.warn(':enforce_chef_fqdn has been deprecated. VM name ' \
|
360
|
+
'will be set to the machine resource name.')
|
361
|
+
end
|
362
|
+
# FQDN is the machine resource name, unless overridden by e.g. cloud-init
|
363
|
+
t_hash['NAME'] = name
|
364
|
+
unless t_hash['CONTEXT']['SSH_PUBLIC_KEY']
|
365
|
+
t_hash['CONTEXT']['SSH_PUBLIC_KEY'] = "$USER[SSH_PUBLIC_KEY]"
|
366
|
+
end
|
367
|
+
unless t_hash['CONTEXT']['USER_DATA']
|
368
|
+
t_hash['CONTEXT']['USER_DATA'] = "#cloud-config\n" \
|
369
|
+
"manage_etc_hosts: true\n"
|
370
|
+
end
|
283
371
|
tpl = create_template(t_hash)
|
284
372
|
Chef::Log.debug(tpl)
|
285
373
|
tpl
|
286
374
|
end
|
287
375
|
|
288
376
|
def template_from_one(options)
|
289
|
-
t = get_resource(
|
290
|
-
t = get_resource(
|
377
|
+
t = get_resource(:template, :name => options[:template_name]) if options[:template_name]
|
378
|
+
t = get_resource(:template, :id => options[:template_id]) if options[:template_id]
|
291
379
|
fail "Template '#{options}' does not exist" if t.nil?
|
292
380
|
t.to_hash["VMTEMPLATE"]["TEMPLATE"]
|
293
381
|
end
|
@@ -306,7 +394,6 @@ EOT
|
|
306
394
|
end
|
307
395
|
|
308
396
|
def template_from_hash(options)
|
309
|
-
Chef::Log.debug("TEMPLATE_JSON: #{options[:template]}")
|
310
397
|
options[:template]
|
311
398
|
end
|
312
399
|
|
@@ -315,6 +402,49 @@ EOT
|
|
315
402
|
# in the 't' Hash. The hash must have equivalent structure as the
|
316
403
|
# VM template.
|
317
404
|
#
|
405
|
+
# We considered using OpenNebulaHelper::create_template for this,
|
406
|
+
# however it would require a backwards compatibility shim and/or
|
407
|
+
# making breaking changes to the API. In particular, our method is
|
408
|
+
# more attractive due to the nested nature of our Hash, versus specifying
|
409
|
+
# a long string for the :context attribute with embedded newlines.
|
410
|
+
# Our strategy provides a way to override specific values, while this is
|
411
|
+
# difficult to accomplish with OpenNebulaHelper::create_template.
|
412
|
+
#
|
413
|
+
# Current template hash:
|
414
|
+
# {
|
415
|
+
# "NAME" => "baz"
|
416
|
+
# "CPU" => "1",
|
417
|
+
# "VCPU" => "1",
|
418
|
+
# "MEMORY" => "512",
|
419
|
+
# "OS" => {
|
420
|
+
# "ARCH" => "x86_64"
|
421
|
+
# },
|
422
|
+
# "GRAPHICS" => {
|
423
|
+
# "LISTEN" => "0.0.0.0",
|
424
|
+
# "TYPE" => "vnc"
|
425
|
+
# },
|
426
|
+
# "CONTEXT" => {
|
427
|
+
# "FOO" => "BAR",
|
428
|
+
# "BAZ" => "QUX"
|
429
|
+
# "HOSTNAME" => "$NAME",
|
430
|
+
# "SSH_PUBLIC_KEY" => "$USER[SSH_PUBLIC_KEY]",
|
431
|
+
# "NETWORK" => "YES"
|
432
|
+
# }
|
433
|
+
# }
|
434
|
+
#
|
435
|
+
# Using OpenNebulaHelper::create_template:
|
436
|
+
# {
|
437
|
+
# :name => 'baz'
|
438
|
+
# :cpu => 1,
|
439
|
+
# :vcpu => 1,
|
440
|
+
# :memory => 512,
|
441
|
+
# :arch => 'x86_64',
|
442
|
+
# :vnc => true,
|
443
|
+
# :context => "FOO=\"BAR\"\nBAZ=\"QUX\"\nHOSTNAME=\"$NAME\"",
|
444
|
+
# :ssh => true,
|
445
|
+
# :net_context => true
|
446
|
+
# }
|
447
|
+
#
|
318
448
|
def create_template(t, level = 0)
|
319
449
|
tpl = ""
|
320
450
|
count = t.size
|
@@ -340,12 +470,21 @@ EOT
|
|
340
470
|
else
|
341
471
|
comma = (index < count) && level > 0
|
342
472
|
level.times { tpl << " " }
|
343
|
-
|
473
|
+
# Escape quotation marks to fix MAND-1394:
|
474
|
+
# template does not support embedded quotation marks
|
475
|
+
# Escaping of " only happens if " is not already escaped, preceded by \\
|
476
|
+
tpl << "#{k} = \"#{v.gsub(/(?<!\\)\"/, '\"')}\"" << (comma ? ",\n" : "\n")
|
344
477
|
index += 1
|
345
478
|
end
|
346
479
|
end
|
347
480
|
tpl
|
348
481
|
end
|
482
|
+
|
483
|
+
def version_mismatch_warning(server, gem)
|
484
|
+
Chef::Log.warn('GEM / SERVER VERSION MISMATCH')
|
485
|
+
Chef::Log.warn("Your gem version is #{gem.join('.')} and the server version is #{server.join('.')}")
|
486
|
+
Chef::Log.warn('Users may experience issues with this gem.')
|
487
|
+
end
|
349
488
|
end
|
350
489
|
end
|
351
490
|
end
|