Class: ChefProvisioningVsphere::VsphereDriver
- Inherits:
-
Chef::Provisioning::Driver
- Object
- Chef::Provisioning::Driver
- ChefProvisioningVsphere::VsphereDriver
- Includes:
- Chef::Mixin::ShellOut, Helpers
- Defined in:
- lib/chef/provisioning/vsphere_driver/driver.rb
Overview
Provisions machines in vSphere.
Instance Attribute Summary collapse
-
#connect_options ⇒ Object
readonly
Returns the value of attribute connect_options.
Class Method Summary collapse
- .canonicalize_url(driver_url, config) ⇒ Object
- .from_url(driver_url, config) ⇒ Object
- .symbolize_keys(h) ⇒ Object
Instance Method Summary collapse
-
#allocate_machine(action_handler, machine_spec, machine_options) ⇒ Object
Acquire a machine, generally by provisioning it.
-
#connect_to_machine(machine_spec, machine_options) ⇒ Object
Connect to machine without acquiring it.
- #destroy_machine(action_handler, machine_spec, machine_options) ⇒ Object
-
#initialize(driver_url, config) ⇒ VsphereDriver
constructor
Create a new Vsphere provisioner.
- #ready_machine(action_handler, machine_spec, machine_options) ⇒ Object
- #restart_server(action_handler, machine_spec, vm) ⇒ Object
- #start_machine(action_handler, machine_spec, machine_options) ⇒ Object
- #stop_machine(action_handler, machine_spec, machine_options) ⇒ Object
Methods included from Helpers
#add_extra_nic, #backing_info_for, #create_delta_disk, #customization_options_from, #dc, #do_vm_clone, #find_customization_spec, #find_datastore, #find_ethernet_cards_for, #find_folder, #find_host, #find_pool, #find_vm, #find_vm_by_id, #hostname_from, #network_adapter_for, #network_device_changes, #network_id_for, #port_ready?, #relocate_spec_for, #start_vm, #stop_vm, #upload_file_to_vm, #vim, #virtual_disk_for, #vm_started?, #vm_stopped?, #windows_prep_for
Constructor Details
#initialize(driver_url, config) ⇒ VsphereDriver
Create a new Vsphere provisioner.
## Parameters connect_options - hash of options to be passed to RbVmomi::VIM.connect
:host - required - hostname of the vSphere API server
:port - optional - port on the vSphere API server (default: 443)
:path - optional - path on the vSphere API server (default: /sdk)
:use_ssl - optional - true to use ssl in connection to vSphere API server (default: true)
:insecure - optional - true to ignore ssl certificate validation errors in connection to vSphere API server (default: false)
:user - required - user name to use in connection to vSphere API server
: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)
89 90 91 92 |
# File 'lib/chef/provisioning/vsphere_driver/driver.rb', line 89 def initialize(driver_url, config) super(driver_url, config) @connect_options = config[:driver_options][:connect_options].to_hash end |
Instance Attribute Details
#connect_options ⇒ Object (readonly)
Returns the value of attribute connect_options.
94 95 96 |
# File 'lib/chef/provisioning/vsphere_driver/driver.rb', line 94 def @connect_options end |
Class Method Details
.canonicalize_url(driver_url, config) ⇒ Object
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 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 62 63 64 65 |
# File 'lib/chef/provisioning/vsphere_driver/driver.rb', line 20 def self.canonicalize_url(driver_url, config) config = symbolize_keys(config) new_defaults = { :driver_options => { :connect_options => { :port => 443, :use_ssl => true, :insecure => false, :path => '/sdk' } }, :machine_options => { :start_timeout => 600, :create_timeout => 600, :ready_timeout => 300, :bootstrap_options => { :ssh => { :port => 22, :user => 'root' }, :key_name => 'metal_default', :tags => {} } } } = {} [:provider] = 'vsphere' if !driver_url.nil? uri = URI(driver_url) [:host] = uri.host [:port] = uri.port if uri.path && uri.path.length > 0 [:path] = uri.path end [:use_ssl] = uri.use_ssl [:insecure] = uri.insecure end = .merge(config[:driver_options]) new_config = { :driver_options => { :connect_options => }} config = Cheffish::MergedConfig.new(new_config, config, new_defaults) = [:host, :user, :password] = [] .each do |opt| << opt unless config[:driver_options][:connect_options].has_key?(opt) end unless .empty? raise "missing required options: #{.join(', ')}" end url = URI::VsphereUrl.from_config(config[:driver_options][:connect_options]).to_s [ url, config ] end |
.from_url(driver_url, config) ⇒ Object
16 17 18 |
# File 'lib/chef/provisioning/vsphere_driver/driver.rb', line 16 def self.from_url(driver_url, config) VsphereDriver.new(driver_url, config) end |
.symbolize_keys(h) ⇒ Object
67 68 69 70 71 72 73 74 |
# File 'lib/chef/provisioning/vsphere_driver/driver.rb', line 67 def self.symbolize_keys(h) Hash === h ? Hash[ h.map do |k, v| [k.respond_to?(:to_sym) ? k.to_sym : k, symbolize_keys(v)] end ] : h end |
Instance Method Details
#allocate_machine(action_handler, machine_spec, machine_options) ⇒ 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
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
# File 'lib/chef/provisioning/vsphere_driver/driver.rb', line 142 def allocate_machine(action_handler, machine_spec, ) if machine_spec.location Chef::Log.warn "Checking to see if #{machine_spec.location} has been created..." vm = vm_for(machine_spec) if vm Chef::Log.warn "returning existing machine" return vm else Chef::Log.warn "Machine #{machine_spec.name} (#{machine_spec.location['server_id']} on #{driver_url}) no longer exists. Recreating ..." end end = (machine_spec, ) vm = nil if [:ssh] wait_on_port = [: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 description = [ "creating machine #{machine_spec.name} on #{driver_url}" ] .each_pair { |key,value| description << " #{key}: #{value.inspect}" } action_handler.report_progress description vm = find_vm([:datacenter], [:vm_folder], machine_spec.name) server_id = nil if vm Chef::Log.info "machine already created: #{[:vm_folder]}/#{machine_spec.name}" else vm = clone_vm(action_handler, ) end machine_spec.location = { 'driver_url' => driver_url, 'driver_version' => VERSION, 'server_id' => vm.config.instanceUuid, 'is_windows' => is_windows?(vm), 'allocated_at' => Time.now.utc.to_s, 'ipaddress' => vm.guest.ipAddress } machine_spec.location['key_name'] = [:key_name] if [:key_name] %w(ssh_username sudo use_private_ip_for_ssh ssh_gateway).each do |key| machine_spec.location[key] = [key.to_sym] if [key.to_sym] end action_handler.performed_action "machine #{machine_spec.name} created as #{machine_spec.location['server_id']} on #{driver_url}" vm end |
#connect_to_machine(machine_spec, machine_options) ⇒ Object
Connect to machine without acquiring it
281 282 283 |
# File 'lib/chef/provisioning/vsphere_driver/driver.rb', line 281 def connect_to_machine(machine_spec, ) machine_for(machine_spec, ) end |
#destroy_machine(action_handler, machine_spec, machine_options) ⇒ Object
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 |
# File 'lib/chef/provisioning/vsphere_driver/driver.rb', line 285 def destroy_machine(action_handler, machine_spec, ) vm = vm_for(machine_spec) if vm action_handler.perform_action "Delete VM [#{vm.parent.name}/#{vm.name}]" do begin vm.PowerOffVM_Task.wait_for_completion unless vm.runtime.powerState == 'poweredOff' vm.Destroy_Task.wait_for_completion rescue RbVmomi::Fault => fault raise fault unless fault.fault.class.wsdl_name == "ManagedObjectNotFound" ensure machine_spec.location = nil end end end strategy = convergence_strategy_for(machine_spec, ) strategy.cleanup_convergence(action_handler, machine_spec) end |
#ready_machine(action_handler, machine_spec, machine_options) ⇒ Object
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 |
# File 'lib/chef/provisioning/vsphere_driver/driver.rb', line 193 def ready_machine(action_handler, machine_spec, ) start_machine(action_handler, machine_spec, ) vm = vm_for(machine_spec) if vm.nil? raise "Machine #{machine_spec.name} does not have a server associated with it, or server does not exist." end wait_until_ready(action_handler, machine_spec, , vm) = (machine_spec, ) transport = nil vm_ip = ip_for(, vm) if !vm_ip.nil? transport = transport_for(machine_spec, , vm) end if transport.nil? || !transport.available? || !(vm.guest.net.map { |net| net.ipAddress}.flatten).include?(vm_ip) action_handler.report_progress "waiting up to #{[:ready_timeout]} seconds for customizations to complete and find #{vm_ip}" now = Time.now.utc until (Time.now.utc - now) > [:ready_timeout] || (vm.guest.net.map { |net| net.ipAddress}.flatten).include?(vm_ip) do action_handler.report_progress "IP addresses on #{machine_spec.name} are #{vm.guest.net.map { |net| net.ipAddress}.flatten}" vm_ip = ip_for(, vm) if vm_ip.nil? sleep 5 end if !(vm.guest.net.map { |net| net.ipAddress}.flatten).include?(vm_ip) action_handler.report_progress "rebooting..." if vm.guest.toolsRunningStatus != "guestToolsRunning" action_handler.report_progress "tools have stopped. current power state is #{vm.runtime.powerState} and tools state is #{vm.guest.toolsRunningStatus}. powering up server..." start_vm(vm) else restart_server(action_handler, machine_spec, vm) end now = Time.now.utc until (Time.now.utc - now) > 90 || (vm.guest.net.map { |net| net.ipAddress}.flatten).include?(vm_ip) do vm_ip = ip_for(, vm) if vm_ip.nil? print "-" sleep 5 end end machine_spec.location['ipaddress'] = vm.guest.ipAddress action_handler.report_progress "IP address obtained: #{machine_spec.location['ipaddress']}" end domain = [:customization_spec][:domain] if vm.config.guestId.start_with?('win') && domain != 'local' now = Time.now.utc trimmed_name = machine_spec.name.byteslice(0,15) expected_name="#{trimmed_name}.#{domain}" action_handler.report_progress "waiting to domain join and be named #{expected_name}" until (Time.now.utc - now) > 30 || (vm.guest.hostName == expected_name) do print "." sleep 5 end end begin wait_for_transport(action_handler, machine_spec, , vm) rescue Timeout::Error # Only ever reboot once, and only if it's been less than 10 minutes since we stopped waiting if machine_spec.location['started_at'] || remaining_wait_time(machine_spec, ) < -(10*60) raise else Chef::Log.warn "Machine #{machine_spec.name} (#{server.config.instanceUuid} on #{driver_url}) was started but SSH did not come up. Rebooting machine in an attempt to unstick it ..." restart_server(action_handler, machine_spec, vm) wait_until_ready(action_handler, machine_spec, , vm) wait_for_transport(action_handler, machine_spec, , vm) end end machine = machine_for(machine_spec, , vm) new_nics = add_extra_nic(action_handler, vm_template_for(), , vm) if is_windows?(vm) && !new_nics.nil? new_nics.each do |nic| machine.execute_always("Disable-Netadapter -Name '#{nic.device.deviceInfo.label}' -Confirm:$false") end end if has_static_ip() && !is_windows?(vm) setup_ubuntu_dns(machine, , machine_spec) end machine end |
#restart_server(action_handler, machine_spec, vm) ⇒ Object
322 323 324 325 326 327 328 |
# File 'lib/chef/provisioning/vsphere_driver/driver.rb', line 322 def restart_server(action_handler, machine_spec, vm) action_handler.perform_action "restart machine #{machine_spec.name} (#{vm.config.instanceUuid} on #{driver_url})" do stop_machine(action_handler, machine_spec, vm) start_vm(vm) machine_spec.location['started_at'] = Time.now.utc.to_s end end |
#start_machine(action_handler, machine_spec, machine_options) ⇒ Object
312 313 314 315 316 317 318 319 320 |
# File 'lib/chef/provisioning/vsphere_driver/driver.rb', line 312 def start_machine(action_handler, machine_spec, ) vm = vm_for(machine_spec) if vm action_handler.perform_action "Power on VM [#{vm.parent.name}/#{vm.name}]" do = (machine_spec, ) start_vm(vm, [:ssh][:port]) end end end |
#stop_machine(action_handler, machine_spec, machine_options) ⇒ Object
303 304 305 306 307 308 309 310 |
# File 'lib/chef/provisioning/vsphere_driver/driver.rb', line 303 def stop_machine(action_handler, machine_spec, ) vm = vm_for(machine_spec) if vm action_handler.perform_action "Shutdown guest OS and power off VM [#{vm.parent.name}/#{vm.name}]" do stop_vm(vm) end end end |