class ChefProvisioningVsphere::CloneSpecBuilder

Creates a cspec for VMware

Attributes

action_handler[R]
vsphere_helper[R]

Public Class Methods

new(vsphere_helper, action_handler) click to toggle source
# File lib/chef/provisioning/vsphere_driver/clone_spec_builder.rb, line 7
def initialize(vsphere_helper, action_handler)
  @vsphere_helper = vsphere_helper
  @action_handler = action_handler
end

Public Instance Methods

build(vm_template, vm_name, options) click to toggle source

The main build method.

@param [String] options Options set from Chef-Provisioning. @param [String] vm_template The VM template to clone from. @param [String] vm_name The VM name to create.

# File lib/chef/provisioning/vsphere_driver/clone_spec_builder.rb, line 20
def build(vm_template, vm_name, options)
  clone_spec = RbVmomi::VIM.VirtualMachineCloneSpec(
    location: relocate_spec_for(vm_template, options),
    powerOn: false,
    template: false,
    config: RbVmomi::VIM.VirtualMachineConfigSpec(
      cpuHotAddEnabled: true,
      memoryHotAddEnabled: true,
      cpuHotRemoveEnabled: true,
      deviceChange: []
    )
  )

  unless options[:annotation].to_s.nil?
    clone_spec.config.annotation = options[:annotation]
  end

  unless options[:num_cpus].to_s.nil?
    clone_spec.config.numCPUs = options[:num_cpus]
  end

  unless options[:memory_mb].to_s.nil?
    clone_spec.config.memoryMB = options[:memory_mb]
  end

  unless options[:network_name].nil?
    deviceAdditions, changes = vsphere_helper.network_device_changes( # rubocop:disable Naming/VariableName, Lint/UselessAssignment
      action_handler,
      vm_template,
      options
    )
    clone_spec.config.deviceChange = changes
  end

  clone_spec.customization = customization_options_from(
    vm_template,
    vm_name,
    options
  )

  clone_spec
end
customization_options_from(vm_template, vm_name, options) click to toggle source

Verify and create all the options needed for Customization Specs

@param [String] options Options set from Chef-Provisioning. @param [String] vm_name The VM name that is set. @param [String] vm_template The VM template to clone from.

# File lib/chef/provisioning/vsphere_driver/clone_spec_builder.rb, line 105
def customization_options_from(vm_template, vm_name, options)
  if options.key?(:customization_spec)
    if options[:customization_spec].is_a?(Hash) ||
        options[:customization_spec].is_a?(Cheffish::MergedConfig)
      cust_options = options[:customization_spec]
      ip_settings = cust_options[:ipsettings]
      cust_domain = cust_options[:domain]

      raise ArgumentError, "domain is required" unless cust_domain
      cust_ip_settings = nil
      if ip_settings && ip_settings.key?(:ip)
        unless cust_options[:ipsettings].key?(:subnetMask)
          raise ArgumentError, "subnetMask is required for static ip"
        end
        cust_ip_settings = RbVmomi::VIM::CustomizationIPSettings.new(
          ip_settings
        )
        action_handler.report_progress "customizing #{vm_name} \
          with static IP #{ip_settings[:ip]}"
        cust_ip_settings.ip = RbVmomi::VIM::CustomizationFixedIp(
          ipAddress: ip_settings[:ip]
        )
      end
      if cust_ip_settings.nil?
        cust_ip_settings = RbVmomi::VIM::CustomizationIPSettings.new(
          ip: RbVmomi::VIM::CustomizationDhcpIpGenerator.new
        )
      end

      if ip_settings && ip_settings.key?(:dnsServerList)
        cust_ip_settings.dnsServerList = ip_settings[:dnsServerList]
        action_handler.report_progress "customizing #{vm_name} with /
          dynamic IP and DNS: #{ip_settings[:dnsServerList]}"
      end

      cust_ip_settings.dnsDomain = cust_domain
      global_ip_settings = RbVmomi::VIM::CustomizationGlobalIPSettings.new
      global_ip_settings.dnsServerList = cust_ip_settings.dnsServerList
      global_ip_settings.dnsSuffixList = [cust_domain]
      cust_hostname = hostname_from(cust_options, vm_name)
      cust_hwclockutc = cust_options[:hw_clock_utc]
      cust_timezone = cust_options[:time_zone]

      cust_prep = if vm_template.config.guestId.start_with?("win")
                    windows_prep_for(options, vm_name)
                  else
                    RbVmomi::VIM::CustomizationLinuxPrep.new(
                      domain: cust_domain,
                      hostName: cust_hostname,
                      hwClockUTC: cust_hwclockutc,
                      timeZone: cust_timezone
                    )
                  end
      cust_adapter_mapping = [
        RbVmomi::VIM::CustomizationAdapterMapping.new(
          adapter: cust_ip_settings
        ),
      ]
      RbVmomi::VIM::CustomizationSpec.new(
        identity: cust_prep,
        globalIPSettings: global_ip_settings,
        nicSettingMap: cust_adapter_mapping
      )
    else
      vsphere_helper.find_customization_spec(options[:customization_spec])
    end
  end
end
hostname_from(options, vm_name) click to toggle source

Creates a hostname, and verifies that it fulfills the requirements

@param [String] options Options set from Chef-Provisioning. @param [String] vm_name The VM name that is set.

# File lib/chef/provisioning/vsphere_driver/clone_spec_builder.rb, line 178
def hostname_from(options, vm_name)
  hostname = options[:hostname] || vm_name
  test = /^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])$/
  unless hostname.match?(test)
    raise "Only letters, numbers or hyphens in hostnames allowed"
  end
  RbVmomi::VIM::CustomizationFixedName.new(name: hostname)
end
relocate_spec_for(vm_template, options) click to toggle source

Figure out or declare where you need to bootstrap the vm

@param [String] options Options set from Chef-Provisioning. @param [String] vm_template The VM template to clone from.

# File lib/chef/provisioning/vsphere_driver/clone_spec_builder.rb, line 67
def relocate_spec_for(vm_template, options)
  rspec = RbVmomi::VIM.VirtualMachineRelocateSpec
  host = nil

  if options.key?(:host)
    host = vsphere_helper.find_host(options[:host])
    rspec.host = host
  end

  if options[:resource_pool]
    rspec.pool = vsphere_helper.find_pool(options[:resource_pool])
  elsif vm_template.config.template && !host.nil?
    rspec.pool = host.parent.resourcePool # assign to the "invisible" pool root
  elsif vm_template.config.template
    raise "either :host or :resource_pool must be specified when cloning from a VM Template"
  end

  if options[:use_linked_clone]
    if vm_template.config.template
      Chef::Log.warn("Using a VM Template, ignoring use_linked_clone.")
    else
      vsphere_helper.create_delta_disk(vm_template)
      rspec.diskMoveType = :moveChildMostDiskBacking
    end
  end

  unless options[:datastore].to_s.empty?
    rspec.datastore = vsphere_helper.find_datastore(options[:datastore])
  end

  rspec
end
windows_prep_for(options, vm_name) click to toggle source

Preparation work for windows, sysprep and the like.

@param [String] options Options set from Chef-Provisioning. @param [String] vm_name The VM name that is set.

# File lib/chef/provisioning/vsphere_driver/clone_spec_builder.rb, line 191
def windows_prep_for(options, vm_name)
  cust_options = options[:customization_spec]
  unless cust_options[:run_once].nil?
    cust_runonce = RbVmomi::VIM::CustomizationGuiRunOnce.new(
      commandList: cust_options[:run_once]
    )
  end

  cust_login_password = RbVmomi::VIM::CustomizationPassword(
    plainText: true,
    value: options[:ssh][:password]
  )
  if cust_options.key?(:domain) && (cust_options[:domain] != "local")
    cust_domain_password = RbVmomi::VIM::CustomizationPassword(
      plainText: true,
      value: ENV["domainAdminPassword"] || cust_options[:domainAdminPassword]
    )
    cust_id = RbVmomi::VIM::CustomizationIdentification.new(
      joinDomain: cust_options[:domain],
      domainAdmin: cust_options[:domainAdmin],
      domainAdminPassword: cust_domain_password
    )
    action_handler.report_progress "joining domain #{cust_options[:domain]} /
      with user: #{cust_options[:domainAdmin]}"
  else
    cust_id = RbVmomi::VIM::CustomizationIdentification.new(
      joinWorkgroup: "WORKGROUP"
    )
  end
  cust_gui_unattended = RbVmomi::VIM::CustomizationGuiUnattended.new(
    autoLogon: true,
    autoLogonCount: 1,
    password: cust_login_password,
    timeZone: cust_options[:win_time_zone]
  )
  cust_userdata = RbVmomi::VIM::CustomizationUserData.new(
    computerName: hostname_from(cust_options, vm_name),
    fullName: cust_options[:org_name],
    orgName: cust_options[:org_name],
    productId: cust_options[:product_id]
  )
  RbVmomi::VIM::CustomizationSysprep.new(
    guiRunOnce: cust_runonce,
    identification: cust_id,
    guiUnattended: cust_gui_unattended,
    userData: cust_userdata
  )
end