chef-provisioning-opennebula 0.3.4 → 0.4.3
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.
- 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
|