chef-metal-vsphere 0.2.0 → 0.3.0.beta.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.
- checksums.yaml +4 -4
- data/.gitignore +27 -0
- data/CHANGELOG.md +6 -0
- data/Gemfile +3 -0
- data/{LICENSE → LICENSE.txt} +4 -2
- data/README.md +29 -42
- data/Rakefile +31 -22
- data/TODO.md +41 -0
- data/chef-metal-vpshere.gemspec +34 -0
- data/lib/chef_metal/driver_init/vsphere.rb +3 -0
- data/lib/chef_metal_vsphere.rb +1 -11
- data/lib/chef_metal_vsphere/version.rb +1 -1
- data/lib/chef_metal_vsphere/vsphere_driver.rb +325 -0
- data/spec/driver_spec.rb +21 -0
- data/spec/spec_helper.rb +2 -0
- data/test/.chef/knife.rb +4 -0
- data/test/cookbooks/test/libraries/vmonkey_helper.rb +34 -0
- data/test/cookbooks/test/recipes/destroy_all.rb +8 -0
- data/test/cookbooks/test/recipes/simple.rb +11 -0
- data/test/cookbooks/test/recipes/vagrant.rb +11 -0
- data/test/cookbooks/test/recipes/vsphere.rb +18 -0
- metadata +111 -34
- data/lib/chef_metal/provisioner_init/vsphere_init.rb +0 -3
- data/lib/chef_metal_vsphere/vsphere_helpers.rb +0 -168
- data/lib/chef_metal_vsphere/vsphere_provisioner.rb +0 -310
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1ab4cff7dadfa8f7cd8fdd608877d2fa4157542f
|
4
|
+
data.tar.gz: 48ec7ddcc3607dd222c59ab81dd7574255d60701
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cb74dc9033f4309fbab2f639a44cf82bb48d4182d5344356a2577e27495a35d81ae8d38f687e606987566a357e44bd7566b41c8a5a4e48da4a628ad7574846e5
|
7
|
+
data.tar.gz: 5894d2d4ee3ff44ac50971e4f78c462d55c5cc3e5dce0ba344d01e59d6f7d8622afc10b4f3ce0d950c50a357ea8dd5b24561d210fac261fa92b3e61c84b92e9c
|
data/.gitignore
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
23
|
+
.vagrant
|
24
|
+
**/local-mode-cache
|
25
|
+
test/nodes
|
26
|
+
test/clients
|
27
|
+
.vmonkey
|
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/{LICENSE → LICENSE.txt}
RENAMED
@@ -1,4 +1,6 @@
|
|
1
|
-
Copyright (c) 2014
|
1
|
+
Copyright (c) 2014 Brian Dupras
|
2
|
+
|
3
|
+
MIT License
|
2
4
|
|
3
5
|
Permission is hereby granted, free of charge, to any person obtaining
|
4
6
|
a copy of this software and associated documentation files (the
|
@@ -17,4 +19,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
19
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
20
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
21
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
@@ -17,51 +17,40 @@ Create or obtain a unix/linux VM template. The VM template must:
|
|
17
17
|
- provide access via ssh
|
18
18
|
- provide a user account with NOPASSWD sudo
|
19
19
|
|
20
|
+
|
21
|
+
### vSphere Credentials
|
22
|
+
Create a file called $HOME/.vmonkey. chef-metal-vsphere uses [vmonkey](https://github.com/vmtricks/vmonkey) to connect to vSphere.
|
23
|
+
|
24
|
+
host: vcenter_host_name
|
25
|
+
user: vcenter_user_name
|
26
|
+
password: your_mothers_maiden_name
|
27
|
+
datacenter: datacenter_name
|
28
|
+
cluster: cluster_name
|
29
|
+
insecure: true
|
30
|
+
ssl: true
|
31
|
+
|
32
|
+
|
20
33
|
### Example recipe
|
21
34
|
require 'chef_metal_vsphere'
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
datastore: 'datastore_name',
|
33
|
-
template_name: 'name_of_template_vm', # may be a VM or a VM Template
|
34
|
-
template_folder: 'folder_containing_template_vm',
|
35
|
-
vm_folder: 'folder_to_clone_vms_into',
|
36
|
-
customization_spec: 'standard-config', # optional
|
37
|
-
|
38
|
-
ssh: { # net-ssh start() options
|
39
|
-
user: 'username_on_vm', # must have nopasswd sudo
|
40
|
-
password: 'name_of_your_first_pet', # consider using a chef-vault
|
35
|
+
with_driver 'vsphere'
|
36
|
+
|
37
|
+
with_machine_options({
|
38
|
+
bootstrap_options: {
|
39
|
+
template: '/path/to/a/vm/template', # vCenter "VMs and Templates" path to a VM Template
|
40
|
+
folder: '/path/to/a/folder/to/place/new/vms' # vCenter "VMs and Templates" path to a Folder. New VMs are created in this folder.
|
41
|
+
},
|
42
|
+
ssh_options: {
|
43
|
+
user: 'root', # root or a user with ssh access and NOPASSWD sudo on a VM cloned from the template
|
44
|
+
password: 'your_first_pet', # consisder using chef-vault
|
41
45
|
port: 22,
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
keys_only: false
|
47
|
-
}
|
48
|
-
})
|
46
|
+
user_known_hosts_file: '/dev/null', # don't do this in production
|
47
|
+
paranoid: false # don't do this in production, either
|
48
|
+
}
|
49
|
+
})
|
49
50
|
|
50
51
|
1.upto 2 do |n|
|
51
52
|
machine "metal_#{n}" do
|
52
53
|
action [:create]
|
53
|
-
|
54
|
-
## optionally add options per-machine customizations
|
55
|
-
add_provisioner_options('bootstrap_options' => {
|
56
|
-
num_cpus: n,
|
57
|
-
memory_mb: 1024 * n,
|
58
|
-
annotation: "metal_#{n} created by chef-metal-vsphere"
|
59
|
-
})
|
60
|
-
end
|
61
|
-
|
62
|
-
machine_file "/tmp/metal_#{n}.txt" do
|
63
|
-
machine "metal_#{n}"
|
64
|
-
content "Hello machine #{n}!"
|
65
54
|
end
|
66
55
|
|
67
56
|
machine "metal_#{n}" do
|
@@ -75,7 +64,7 @@ Create or obtain a unix/linux VM template. The VM template must:
|
|
75
64
|
|
76
65
|
end
|
77
66
|
|
78
|
-
This will clone your VM template to create two VMware Virtual Machines, "metal_1" and "metal_2", in the vSphere Folder specified by
|
67
|
+
This will clone your VM template to create two VMware Virtual Machines, "metal_1" and "metal_2", in the vSphere Folder specified by bootstrap_options => folder, bootstrapped to an empty runlist. It will then stop (guest shutdown) and delete the VMs.
|
79
68
|
|
80
69
|
Roadmap
|
81
70
|
-------
|
@@ -85,6 +74,4 @@ Check out [TODO.md](TODO.md)
|
|
85
74
|
Bugs and Contact
|
86
75
|
----------------
|
87
76
|
|
88
|
-
Please submit bugs at [chef-metal-vpshere](https://github.com/
|
89
|
-
|
90
|
-
*Warning* if you get an rbvmomi error regarding VMODL::AnyType, add `gem 'nokogiri', '1.5.5'` to your dependencies.
|
77
|
+
Please submit bugs at [chef-metal-vpshere](https://github.com/vmtricks/chef-metal-vsphere), contact Brian Dupras on Twitter at @briandupras, email at brian@duprasville.com.
|
data/Rakefile
CHANGED
@@ -1,30 +1,39 @@
|
|
1
|
-
require 'bundler'
|
2
1
|
require 'bundler/gem_tasks'
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
require 'mixlib/shellout'
|
3
4
|
|
4
|
-
def
|
5
|
-
|
5
|
+
def shell_out(*command_args)
|
6
|
+
cmd = Mixlib::ShellOut.new(command_args)
|
7
|
+
cmd.live_stream = STDOUT
|
8
|
+
cmd.run_command
|
9
|
+
cmd
|
6
10
|
end
|
7
11
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
end
|
14
|
-
|
15
|
-
def rubygem_push(path)
|
16
|
-
print "Username: "
|
17
|
-
username = STDIN.gets.chomp
|
18
|
-
print "Password: "
|
19
|
-
password = STDIN.gets.chomp
|
12
|
+
def shell_out!(*command_args)
|
13
|
+
cmd = shell_out(*command_args)
|
14
|
+
cmd.error!
|
15
|
+
cmd
|
16
|
+
end
|
20
17
|
|
21
|
-
|
22
|
-
|
18
|
+
namespace :test do
|
19
|
+
desc 'Run test cookbook in vSphere'
|
20
|
+
task :vsphere do |t|
|
21
|
+
shell_out! %Q{(cd test && chef-client -z -o test::vsphere,test::simple)}
|
22
|
+
end
|
23
23
|
|
24
|
-
|
25
|
-
|
24
|
+
desc 'Run test cookbook in vagrant'
|
25
|
+
task :vagrant do |t|
|
26
|
+
shell_out! %Q{(cd test && chef-client -z -o test::vagrant,test::simple)}
|
27
|
+
end
|
26
28
|
|
27
|
-
|
28
|
-
|
29
|
+
desc 'Destroy test machines'
|
30
|
+
task :clean do |t|
|
31
|
+
shell_out! %Q{(cd test && chef-client -z -o test::vsphere,test::destroy_all)}
|
29
32
|
end
|
30
|
-
end
|
33
|
+
end
|
34
|
+
|
35
|
+
desc 'Run RSpec specs'
|
36
|
+
RSpec::Core::RakeTask.new(:spec)
|
37
|
+
|
38
|
+
desc 'Run all tests using vSphere'
|
39
|
+
task test: [:spec, 'test:vsphere']
|
data/TODO.md
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
To Do Items
|
2
|
+
===========
|
3
|
+
|
4
|
+
Clone features
|
5
|
+
--------------
|
6
|
+
|
7
|
+
:bootstrap_options
|
8
|
+
:num_cpus
|
9
|
+
:memory_mb
|
10
|
+
:annotation
|
11
|
+
|
12
|
+
:datastore_cluster
|
13
|
+
:linked_clone
|
14
|
+
|
15
|
+
:customizations
|
16
|
+
:vlan
|
17
|
+
:ips
|
18
|
+
:dns_ips
|
19
|
+
:dns_suffixes
|
20
|
+
:gw
|
21
|
+
:hostname
|
22
|
+
:domain
|
23
|
+
:tz
|
24
|
+
|
25
|
+
vApp features
|
26
|
+
-------------
|
27
|
+
|
28
|
+
- create/start/stop/delete vApps in recipes
|
29
|
+
- clone from existing vApp
|
30
|
+
- replace machines in vApp
|
31
|
+
|
32
|
+
VM features
|
33
|
+
-----------
|
34
|
+
|
35
|
+
- markAsTemplate
|
36
|
+
|
37
|
+
Usability
|
38
|
+
---------
|
39
|
+
|
40
|
+
- create example cookbooks
|
41
|
+
- with test-kitchen tests
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'chef_metal_vsphere/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'chef-metal-vsphere'
|
8
|
+
spec.version = ChefMetalVsphere::VERSION
|
9
|
+
spec.platform = Gem::Platform::RUBY
|
10
|
+
spec.authors = ['Brian Dupras']
|
11
|
+
spec.email = ['brian@duprasville.com']
|
12
|
+
spec.summary = %q{chef-metal vSphere driver}
|
13
|
+
spec.description = spec.summary
|
14
|
+
spec.homepage = 'https://github.com/vmtricks/chef-metal-vsphere'
|
15
|
+
spec.license = 'MIT'
|
16
|
+
|
17
|
+
spec.require_path = 'lib'
|
18
|
+
spec.files = `git ls-files -z`.split("\x0")
|
19
|
+
spec.extra_rdoc_files = ['README.md', 'LICENSE.txt' ]
|
20
|
+
spec.bindir = 'bin'
|
21
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
22
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
23
|
+
spec.require_paths = ['lib']
|
24
|
+
|
25
|
+
spec.add_dependency 'chef', '~> 11.12'
|
26
|
+
spec.add_dependency 'chef-metal', '~> 0.11'
|
27
|
+
spec.add_dependency 'vmonkey', '~> 0.4'
|
28
|
+
|
29
|
+
spec.add_development_dependency 'bundler', '~> 1.6'
|
30
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
31
|
+
spec.add_development_dependency 'rake', '~> 10.3'
|
32
|
+
spec.add_development_dependency 'mixlib-shellout', '~> 1.4.0'
|
33
|
+
spec.add_development_dependency 'chef-metal-vagrant', '~> 0.4'
|
34
|
+
end
|
data/lib/chef_metal_vsphere.rb
CHANGED
@@ -1,12 +1,2 @@
|
|
1
1
|
require 'chef_metal'
|
2
|
-
require 'chef_metal_vsphere/
|
3
|
-
|
4
|
-
class Chef
|
5
|
-
module DSL
|
6
|
-
module Recipe
|
7
|
-
def with_vsphere_provisioner(options = {}, &block)
|
8
|
-
run_context.chef_metal.with_provisioner(ChefMetalVsphere::VsphereProvisioner.new(options), &block)
|
9
|
-
end
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
2
|
+
require 'chef_metal_vsphere/vsphere_driver'
|
@@ -0,0 +1,325 @@
|
|
1
|
+
require 'chef_metal/driver'
|
2
|
+
require 'chef_metal/machine/windows_machine'
|
3
|
+
require 'chef_metal/machine/unix_machine'
|
4
|
+
require 'chef_metal/machine_spec'
|
5
|
+
require 'chef_metal/convergence_strategy/install_msi'
|
6
|
+
require 'chef_metal/convergence_strategy/install_sh'
|
7
|
+
require 'chef_metal/convergence_strategy/install_cached'
|
8
|
+
require 'chef_metal/convergence_strategy/no_converge'
|
9
|
+
require 'chef_metal/transport/ssh'
|
10
|
+
require 'chef_metal/transport/winrm'
|
11
|
+
require 'chef_metal_vsphere/version'
|
12
|
+
require 'vmonkey'
|
13
|
+
|
14
|
+
module ChefMetalVsphere
|
15
|
+
# Provisions machines in VMware vSphere.
|
16
|
+
class VsphereDriver < ChefMetal::Driver
|
17
|
+
DEFAULT_OPTIONS = {
|
18
|
+
:create_timeout => 300,
|
19
|
+
:start_timeout => 180,
|
20
|
+
:ssh_timeout => 20
|
21
|
+
}
|
22
|
+
|
23
|
+
def self.from_url(url, config)
|
24
|
+
VsphereDriver.new(url, config)
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.canonicalize_url(driver_url, config)
|
28
|
+
_, host, datacenter, cluster = driver_url.split(':', 4)
|
29
|
+
vmonkey_opts = VMonkey.default_opts
|
30
|
+
|
31
|
+
host ||= vmonkey_opts[:host]
|
32
|
+
datacenter ||= vmonkey_opts[:datacenter]
|
33
|
+
cluster ||= vmonkey_opts[:cluster]
|
34
|
+
|
35
|
+
[ "vsphere:#{host}:#{datacenter}:#{cluster}", config ]
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize(url, config)
|
39
|
+
super(url, config)
|
40
|
+
end
|
41
|
+
|
42
|
+
def allocate_machine(action_handler, machine_spec, machine_options)
|
43
|
+
create_vm(action_handler, machine_spec, machine_options)
|
44
|
+
end
|
45
|
+
|
46
|
+
def ready_machine(action_handler, machine_spec, machine_options)
|
47
|
+
vm = vm_for(machine_spec)
|
48
|
+
|
49
|
+
if vm.nil?
|
50
|
+
raise "Machine #{machine_spec.name} does not have a vm associated with it, or vm does not exist."
|
51
|
+
end
|
52
|
+
|
53
|
+
# Start the vm if needed, and wait for it to start
|
54
|
+
start_vm(action_handler, machine_spec, vm)
|
55
|
+
wait_until_ready(action_handler, machine_spec, machine_options, vm)
|
56
|
+
wait_for_transport(action_handler, machine_spec, machine_options, vm)
|
57
|
+
|
58
|
+
machine_for(machine_spec, machine_options, vm)
|
59
|
+
end
|
60
|
+
|
61
|
+
def connect_to_machine(machine_spec, machine_options)
|
62
|
+
machine_for(machine_spec, machine_options)
|
63
|
+
end
|
64
|
+
|
65
|
+
def destroy_machine(action_handler, machine_spec, machine_options)
|
66
|
+
vm = vm_for(machine_spec)
|
67
|
+
if vm
|
68
|
+
action_handler.perform_action "destroy machine #{machine_spec.name} (#{machine_spec.location['instance_uuid']} at #{driver_url})" do
|
69
|
+
vm.destroy
|
70
|
+
machine_spec.location = nil
|
71
|
+
end
|
72
|
+
end
|
73
|
+
strategy = convergence_strategy_for(machine_spec, machine_options)
|
74
|
+
strategy.cleanup_convergence(action_handler, machine_spec)
|
75
|
+
end
|
76
|
+
|
77
|
+
def transport_for(machine_spec, machine_options, vm)
|
78
|
+
# TODO winrm
|
79
|
+
create_ssh_transport(machine_spec, machine_options, vm)
|
80
|
+
end
|
81
|
+
|
82
|
+
protected
|
83
|
+
def option_for(machine_options, key)
|
84
|
+
machine_options[key] || DEFAULT_OPTIONS[key]
|
85
|
+
end
|
86
|
+
|
87
|
+
def monkey
|
88
|
+
## TODO - test @vim and reconnect on error - idle sessions get silently timed-out by vSphere :P
|
89
|
+
@vim ||= VMonkey.connect
|
90
|
+
end
|
91
|
+
|
92
|
+
def create_vm(action_handler, machine_spec, machine_options)
|
93
|
+
if machine_spec.location
|
94
|
+
if machine_spec.location['driver_url'] != driver_url
|
95
|
+
raise "Switching a machine's driver from #{machine_spec.location['driver_url']} to #{driver_url} for is not currently supported! Use machine :destroy and then re-create the machine on the new driver."
|
96
|
+
end
|
97
|
+
|
98
|
+
vm = vm_for!(machine_spec)
|
99
|
+
if vm
|
100
|
+
return vm
|
101
|
+
else
|
102
|
+
Chef::Log.warn "Machine #{machine_spec.name} (#{machine_spec.location['instance_uuid']} on #{driver_url}) no longer exists. Recreating ..."
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
bootstrap_options = bootstrap_options_for(action_handler, machine_spec, machine_options)
|
107
|
+
|
108
|
+
description = [ "creating machine #{machine_spec.name} on #{driver_url}" ]
|
109
|
+
bootstrap_options.each_pair { |key,value| description << " #{key}: #{value.inspect}" }
|
110
|
+
action_handler.report_progress description
|
111
|
+
vm = nil
|
112
|
+
if action_handler.should_perform_actions
|
113
|
+
template = monkey.vm! bootstrap_options[:template]
|
114
|
+
vm_path = "#{bootstrap_options[:folder]}/#{machine_spec.name}"
|
115
|
+
vm = template.clone_to vm_path
|
116
|
+
|
117
|
+
machine_spec.location = {
|
118
|
+
'driver_url' => driver_url,
|
119
|
+
'driver_version' => ChefMetalVsphere::VERSION,
|
120
|
+
'cloned_from' => bootstrap_options[:template],
|
121
|
+
'path' => vm_path,
|
122
|
+
'instance_uuid' => vm.config.instanceUuid,
|
123
|
+
'allocated_at' => Time.now.to_i
|
124
|
+
}
|
125
|
+
machine_spec.location['key_name'] = bootstrap_options[:key_name] if bootstrap_options[:key_name]
|
126
|
+
%w(is_windows ssh_username sudo ssh_gateway).each do |key|
|
127
|
+
machine_spec.location[key] = machine_options[key.to_sym] if machine_options[key.to_sym]
|
128
|
+
end
|
129
|
+
|
130
|
+
action_handler.performed_action "machine #{machine_spec.name} created as #{vm.config.instanceUuid} on #{driver_url}"
|
131
|
+
end
|
132
|
+
vm
|
133
|
+
end
|
134
|
+
|
135
|
+
def start_vm(action_handler, machine_spec, vm)
|
136
|
+
unless vm.started?
|
137
|
+
action_handler.perform_action "start machine #{machine_spec.name} (#{vm.config.instanceUuid} on #{driver_url})" do
|
138
|
+
vm.start
|
139
|
+
machine_spec.location['started_at'] = Time.now.to_i
|
140
|
+
end
|
141
|
+
machine_spec.save(action_handler)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def remaining_wait_time(machine_spec, machine_options)
|
146
|
+
if machine_spec.location['started_at']
|
147
|
+
timeout = option_for(machine_options, :start_timeout) - (Time.now.utc - parse_time(machine_spec.location['started_at']))
|
148
|
+
else
|
149
|
+
timeout = option_for(machine_options, :create_timeout) - (Time.now.utc - parse_time(machine_spec.location['allocated_at']))
|
150
|
+
end
|
151
|
+
timeout > 0 ? timeout : 0.01
|
152
|
+
end
|
153
|
+
|
154
|
+
def parse_time(value)
|
155
|
+
if value.is_a?(String)
|
156
|
+
Time.parse(value)
|
157
|
+
else
|
158
|
+
Time.at(value)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def wait_until_ready(action_handler, machine_spec, machine_options, vm)
|
163
|
+
unless vm.ready?
|
164
|
+
if action_handler.should_perform_actions
|
165
|
+
action_handler.report_progress "waiting for #{machine_spec.name} (#{vm.config.instanceUuid} on #{driver_url}) to be ready ..."
|
166
|
+
vm.wait_for(remaining_wait_time(machine_spec, machine_options)) { vm.ready? }
|
167
|
+
action_handler.report_progress "#{machine_spec.name} is now ready"
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def wait_for_transport(action_handler, machine_spec, machine_options, vm)
|
173
|
+
transport = transport_for(machine_spec, machine_options, vm)
|
174
|
+
if !transport.available?
|
175
|
+
if action_handler.should_perform_actions
|
176
|
+
action_handler.report_progress "waiting for #{machine_spec.name} (#{vm.config.instanceUuid} on #{driver_url}) to be connectable (transport up and running) ..."
|
177
|
+
|
178
|
+
_self = self
|
179
|
+
|
180
|
+
vm.wait_for(remaining_wait_time(machine_spec, machine_options)) do
|
181
|
+
transport.available?
|
182
|
+
end
|
183
|
+
action_handler.report_progress "#{machine_spec.name} is now connectable"
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def symbolize_keys(options)
|
189
|
+
options.inject({}) do |result,(key,value)|
|
190
|
+
result[key.to_sym] = value
|
191
|
+
result
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def vm_for(machine_spec)
|
196
|
+
if machine_spec.location
|
197
|
+
monkey.vm_by_instance_uuid machine_spec.location['instance_uuid']
|
198
|
+
else
|
199
|
+
nil
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def vm_for!(machine_spec)
|
204
|
+
monkey.vm_by_instance_uuid! machine_spec.location['instance_uuid']
|
205
|
+
end
|
206
|
+
|
207
|
+
def bootstrap_options_for(action_handler, machine_spec, machine_options)
|
208
|
+
bootstrap_options = symbolize_keys(machine_options[:bootstrap_options] || {})
|
209
|
+
|
210
|
+
bootstrap_options[:tags] = default_tags(machine_spec, bootstrap_options[:tags] || {})
|
211
|
+
|
212
|
+
bootstrap_options[:name] ||= machine_spec.name
|
213
|
+
|
214
|
+
bootstrap_options
|
215
|
+
end
|
216
|
+
|
217
|
+
def default_tags(machine_spec, bootstrap_tags = {})
|
218
|
+
tags = {
|
219
|
+
'Name' => machine_spec.name,
|
220
|
+
'BootstrapId' => machine_spec.id,
|
221
|
+
'BootstrapHost' => Socket.gethostname,
|
222
|
+
'BootstrapUser' => Etc.getlogin
|
223
|
+
}
|
224
|
+
# User-defined tags override the ones we set
|
225
|
+
tags.merge(bootstrap_tags)
|
226
|
+
end
|
227
|
+
|
228
|
+
def machine_for(machine_spec, machine_options, vm = nil)
|
229
|
+
vm ||= vm_for(machine_spec)
|
230
|
+
if !vm
|
231
|
+
raise "VM for node #{machine_spec.name} has not been created!"
|
232
|
+
end
|
233
|
+
|
234
|
+
if machine_spec.location['is_windows']
|
235
|
+
ChefMetal::Machine::WindowsMachine.new(machine_spec, transport_for(machine_spec, machine_options, vm), convergence_strategy_for(machine_spec, machine_options))
|
236
|
+
else
|
237
|
+
ChefMetal::Machine::UnixMachine.new(machine_spec, transport_for(machine_spec, machine_options, vm), convergence_strategy_for(machine_spec, machine_options))
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
def convergence_strategy_for(machine_spec, machine_options)
|
242
|
+
# Defaults
|
243
|
+
if !machine_spec.location
|
244
|
+
return ChefMetal::ConvergenceStrategy::NoConverge.new(machine_options[:convergence_options], config)
|
245
|
+
end
|
246
|
+
|
247
|
+
if machine_spec.location['is_windows']
|
248
|
+
ChefMetal::ConvergenceStrategy::InstallMsi.new(machine_options[:convergence_options], config)
|
249
|
+
elsif machine_options[:cached_installer] == true
|
250
|
+
ChefMetal::ConvergenceStrategy::InstallCached.new(machine_options[:convergence_options], config)
|
251
|
+
else
|
252
|
+
ChefMetal::ConvergenceStrategy::InstallSh.new(machine_options[:convergence_options], config)
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
def ssh_options_for(machine_spec, machine_options, vm)
|
257
|
+
result = {
|
258
|
+
:auth_methods => [ 'publickey' ],
|
259
|
+
:keys_only => true,
|
260
|
+
:host_key_alias => vm.config.instanceUuid
|
261
|
+
}.merge(machine_options[:ssh_options] || {})
|
262
|
+
if vm.respond_to?(:private_key) && vm.private_key
|
263
|
+
result[:key_data] = [ vm.private_key ]
|
264
|
+
elsif vm.respond_to?(:key_name) && vm.key_name
|
265
|
+
key = get_private_key(vm.key_name)
|
266
|
+
if !key
|
267
|
+
raise "VM has key name '#{vm.key_name}', but the corresponding private key was not found locally. Check if the key is in Chef::Config.private_key_paths: #{Chef::Config.private_key_paths.join(', ')}"
|
268
|
+
end
|
269
|
+
result[:key_data] = [ key ]
|
270
|
+
elsif machine_spec.location['key_name']
|
271
|
+
key = get_private_key(machine_spec.location['key_name'])
|
272
|
+
if !key
|
273
|
+
raise "VM was created with key name '#{machine_spec.location['key_name']}', but the corresponding private key was not found locally. Check if the key is in Chef::Config.private_key_paths: #{Chef::Config.private_key_paths.join(', ')}"
|
274
|
+
end
|
275
|
+
result[:key_data] = [ key ]
|
276
|
+
elsif machine_options[:bootstrap_options] && machine_options[:bootstrap_options][:key_path]
|
277
|
+
result[:key_data] = [ IO.read(machine_options[:bootstrap_options][:key_path]) ]
|
278
|
+
elsif machine_options[:bootstrap_options] && machine_options[:bootstrap_options][:key_name]
|
279
|
+
result[:key_data] = [ get_private_key(machine_options[:bootstrap_options][:key_name]) ]
|
280
|
+
elsif machine_options[:ssh_options] && machine_options[:ssh_options][:password]
|
281
|
+
result[:password] = machine_options[:ssh_options][:password]
|
282
|
+
result[:auth_methods] = [ 'password' ]
|
283
|
+
result[:keys] = [ ]
|
284
|
+
result[:keys_only] = false
|
285
|
+
else
|
286
|
+
# TODO make a way to suggest other keys to try ...
|
287
|
+
raise "No key or password found to connect to #{machine_spec.name} (#{machine_spec.location.inspect})! machine_options #{machine_options.inspect}"
|
288
|
+
end
|
289
|
+
result
|
290
|
+
end
|
291
|
+
|
292
|
+
def default_ssh_username(machine_options)
|
293
|
+
if machine_options[:ssh_options] && machine_options[:ssh_options][:user]
|
294
|
+
machine_options[:ssh_options][:user]
|
295
|
+
else
|
296
|
+
'root'
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
def create_ssh_transport(machine_spec, machine_options, vm)
|
301
|
+
ssh_options = ssh_options_for(machine_spec, machine_options, vm)
|
302
|
+
username = machine_spec.location['ssh_username'] || default_ssh_username(machine_options)
|
303
|
+
if machine_options.has_key?(:ssh_username) && machine_options[:ssh_username] != machine_spec.location['ssh_username']
|
304
|
+
Chef::Log.warn("VM #{machine_spec.name} was created with SSH username #{machine_spec.location['ssh_username']} and machine_options specifies username #{machine_options[:ssh_username]}. Using #{machine_spec.location['ssh_username']}. Please edit the node and change the metal.location.ssh_username attribute if you want to change it.")
|
305
|
+
end
|
306
|
+
options = {}
|
307
|
+
if machine_spec.location[:sudo] || (!machine_spec.location.has_key?(:sudo) && username != 'root')
|
308
|
+
options[:prefix] = 'sudo '
|
309
|
+
end
|
310
|
+
|
311
|
+
if vm.guest_ip.nil?
|
312
|
+
raise "VM #{machine_spec.name} (#{vm.config.instanceUuid} has no IP address!"
|
313
|
+
end
|
314
|
+
|
315
|
+
#Enable pty by default
|
316
|
+
options[:ssh_pty_enable] = true
|
317
|
+
options[:ssh_gateway] = machine_spec.location['ssh_gateway'] if machine_spec.location.has_key?('ssh_gateway')
|
318
|
+
|
319
|
+
ChefMetal::Transport::SSH.new(vm.guest_ip, username, ssh_options, options, config)
|
320
|
+
end
|
321
|
+
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
|