Class: ChefMetalVsphere::VsphereProvisioner

Inherits:
ChefMetal::Provisioner
  • Object
show all
Includes:
Chef::Mixin::ShellOut, Helpers
Defined in:
lib/chef_metal_vsphere/vsphere_provisioner.rb

Overview

Provisions machines in vSphere.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Helpers

#dc, #do_vm_clone, #find_customization_spec, #find_folder, #find_pool, #find_vm, #port_ready?, #start_vm, #stop_vm, #vim, #vm_started?, #vm_stopped?

Constructor Details

#initialize(connect_options) ⇒ VsphereProvisioner

Create a new Vsphere provisioner.

## Parameters connect_options - hash of options to be passed to RbVmomi::VIM.connect

:vsphere_host       - required - hostname of the vSphere API server
:vsphere_port       - optional - port on the vSphere API server (default: 443)
:vshere_path        - optional - path on the vSphere API server (default: /sdk)
:vsphere_ssl        - optional - true to use ssl in connection to vSphere API server (default: true)
:vsphere_insecure   - optional - true to ignore ssl certificate validation errors in connection to vSphere API server (default: false)
:vsphere_user       - required - user name to use in connection to vSphere API server
:vsphere_password   - required - password to use in connection to vSphere API server
:proxy_host         - optional - http proxy host to use in connection to vSphere API server (default: none)
:proxy_port         - optional - http proxy port to use in connection to vSphere API server (default: none)


37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/chef_metal_vsphere/vsphere_provisioner.rb', line 37

def initialize(connect_options)
  connect_options = stringify_keys(connect_options)
  default_connect_options = {
    'vsphere_port'     => 443,
    'vsphere_ssl'      => true,
    'vsphere_insecure' => false,
    'vsphere_path'     => '/sdk'
  }

  @connect_options = default_connect_options.merge(connect_options)

  required_options = %w( vsphere_host vsphere_user vsphere_password )
  missing_options = []
  required_options.each do |opt|
    missing_options << opt unless @connect_options.has_key?(opt)
  end
  unless missing_options.empty?
    raise "missing required options: #{missing_options.join(', ')}"
  end

  # test vim connection
  vim || raise("cannot connect to [#{provisioner_url}]")

  @connect_options
end

Instance Attribute Details

#connect_optionsObject (readonly)

Returns the value of attribute connect_options.



63
64
65
# File 'lib/chef_metal_vsphere/vsphere_provisioner.rb', line 63

def connect_options
  @connect_options
end

Class Method Details

.inflate(node) ⇒ Object



18
19
20
21
22
# File 'lib/chef_metal_vsphere/vsphere_provisioner.rb', line 18

def self.inflate(node)
  url = node['normal']['provisioner_output']['provisioner_url']
  scheme, provider, id = url.split(':', 3)
  VsphereProvisioner.new({ :provider => provider }, id)
end

Instance Method Details

#acquire_machine(action_handler, node) ⇒ Object

Acquire a machine, generally by provisioning it. Returns a Machine object pointing at the machine, allowing useful actions like setup, converge, execute, file and directory. The Machine object will have a “node” property which must be saved to the server (if it is any different from the original node object).

## Parameters action_handler - the action_handler object that is calling this method; this

is generally a action_handler, but could be anything that can support the
ChefMetal::ActionHandler interface (i.e., in the case of the test
kitchen metal driver for acquiring and destroying VMs; see the base
class for what needs providing).

node - node object (deserialized json) representing this machine. If

the node has a provisioner_options hash in it, these will be used
instead of options provided by the provisioner.  TODO compare and
fail if different?
node will have node['normal']['provisioner_options'] in it with any options.
It is a hash with this format:

   -- provisioner_url: vsphere://host:port?ssl=[true|false]&insecure=[true|false]
   -- bootstrap_options: hash of options to pass to RbVmomi::VIM::VirtualMachine::CloneTask()
        :datacenter
        :resource_pool
        :cluster
        :datastore
        :template_name
        :template_folder
        :vm_folder
        :winrm {...} (not yet implemented)
        :ssh {...}

Example bootstrap_options for vSphere:
  TODO: add other CloneTask params, e.g.: datastore, annotation, resource_pool, ...
  'bootstrap_options' => {
    'template_name' =>'centos6.small',
    'template_folder' =>'Templates',
    'vm_folder' => 'MyApp'
  }

node['normal']['provisioner_output'] will be populated with information
about the created machine.  For vSphere, it is a hash with this
format:

   -- provisioner_url: vsphere:host:port?ssl=[true|false]&insecure=[true|false]
   -- vm_folder: name of the vSphere folder containing the VM


111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/chef_metal_vsphere/vsphere_provisioner.rb', line 111

def acquire_machine(action_handler, node)
  # Set up the provisioner output
  provisioner_options = stringify_keys(node['normal']['provisioner_options'])

  vm_name = node['name']
  old_provisioner_output = node['normal']['provisioner_output']
  node['normal']['provisioner_output'] = provisioner_output = {
    'provisioner_url' => provisioner_url,
    'vm_name' => vm_name,
    'bootstrap_options' => provisioner_options['bootstrap_options']
  }

  bootstrap_options = node['normal']['provisioner_output']['bootstrap_options']
  vm_folder = bootstrap_options['vm_folder']

  if bootstrap_options['ssh']
    wait_on_port = bootstrap_options['ssh']['port']
    raise "Must specify bootstrap_options[:ssh][:port]" if wait_on_port.nil?
  else
    raise 'bootstrapping is currently supported for ssh only'
    # wait_on_port = bootstrap_options['winrm']['port']
  end

  # TODO compare new options to existing and fail if we cannot change it
  # over (perhaps introduce a boolean that will force a delete and recreate
  # in such a case)

  vm = vm_instance(action_handler, node)

  unless vm_started?(vm, wait_on_port)
    action_handler.perform_action "Start VM and wait for port #{wait_on_port}" do
      start_vm(vm, wait_on_port)
    end
  end

  machine = machine_for(node)

  machine
end

#connect_to_machine(node) ⇒ Object

Connect to machine without acquiring it



152
153
154
# File 'lib/chef_metal_vsphere/vsphere_provisioner.rb', line 152

def connect_to_machine(node)
  machine_for(node)
end

#delete_machine(action_handler, node) ⇒ Object



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/chef_metal_vsphere/vsphere_provisioner.rb', line 156

def delete_machine(action_handler, node)
  if node['normal'] && node['normal']['provisioner_output']
    provisioner_output = node['normal']['provisioner_output']
  else
    provisioner_output = {}
  end
  vm_name = provisioner_output['vm_name'] || node['name']
  vm_folder = provisioner_output['bootstrap_options']['vm_folder']
  vm = vm_for(node)

  unless vm.nil?
    action_handler.perform_action "Delete VM [#{vm_folder}/#{vm_name}]" do
      vm.PowerOffVM_Task.wait_for_completion unless vm.runtime.powerState == 'poweredOff'
      vm.Destroy_Task.wait_for_completion
    end
  end
end

#stop_machine(action_handler, node) ⇒ Object



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/chef_metal_vsphere/vsphere_provisioner.rb', line 174

def stop_machine(action_handler, node)
  if node['normal'] && node['normal']['provisioner_output']
    provisioner_output = node['normal']['provisioner_output']
  else
    provisioner_output = {}
  end
  vm_name = provisioner_output['vm_name'] || node['name']
  vm_folder = provisioner_output['bootstrap_options']['vm_folder']
  vm = vm_for(node)

  unless vm_stopped?(vm)
    action_handler.perform_action "Shutdown guest OS and power off VM [#{vm_folder}/#{vm_name}]" do
      stop_vm(vm)
    end
  end
end