class Elevage::Provisioner

Provisioner is responsible for the actual execution of the commands to create the requested virtual machine.

Attributes

component[RW]
environment[RW]
instance[RW]
name[RW]
vcenter[RW]

Public Class Methods

new(name, component, instance, environment, options) click to toggle source

Create the Provisioner object for the requested virtual machine @param [String] name Name of the node to create @param [String] component Name of the platform component node is part of @param [String] instance Number representing which instance of `component` this node is @param [String] environment Name of the environnment we're provisioning to @param [String] options Thor `options` hash @return [Elevage::Provisioner]

# File lib/elevage/provisioner.rb, line 27
def initialize(name, component, instance, environment, options)
  @name = name
  @component = component
  @instance = instance
  @environment = environment
  @options = options
  @vcenter = @environment.vcenter
end

Public Instance Methods

build() click to toggle source

Build the the virtual machine

# File lib/elevage/provisioner.rb, line 50
def build
  knife_cmd = generate_knife_cmd

  # Modify behavior for dry-run
  # Echo command to stdout and logfile instead of executing command.
  if @options['dry-run']
    puts knife_cmd
    knife_cmd = "echo #{knife_cmd}"
  end

  # Open the logfile for writing
  logfile = File.new("#{@options[:logfiles]}/#{@name}.log", 'w')

  stamp = @options['dry-run'] ? '' : "#{Time.now} [#{$$}]: "
  puts "#{stamp}#{@name}: logging to #{logfile.path}"
  logfile.puts "#{stamp}#{@name}: Provisioning."

  # Execute the knife command, capturing stderr and stdout as they
  # produce anything, and push it all into a Queue object, which we then
  # write to the log file as things come available.
  status = Open4.popen4(knife_cmd) do |_pid, _stdin, stdout, stderr|
    sem = Mutex.new
    # Set and forget the thread for stderr...
    # err_thread = Thread.new do
    Thread.new do
      while (line = stderr.gets)
        sem.synchronize do
          logfile.puts line
          logfile.sync
        end
      end
    end
    out_thread = Thread.new do
      while (line = stdout.gets)
        sem.synchronize do
          logfile.puts line
          logfile.sync
        end
      end
    end
    out_thread.join
    # err_thread.exit
  end

  stamp = @options['dry-run'] ? '' : "#{Time.now} [#{$$}]: "
  logfile.puts "#{stamp}#{@name}: exit status: #{status.exitstatus}"
  logfile.close

  # Inform our master whether we succeeded or failed. Any non-zero
  # exit status is a failure, and the details will be in the logfile
  status.exitstatus == 0 ? true : false
end
to_s() click to toggle source

Stringify the Provisioner @return [String]

# File lib/elevage/provisioner.rb, line 38
def to_s
  puts "Name: #{@name}"
  puts "Instance: #{@instance}"
  puts "Component: #{@component}"
  puts @component.to_yaml
  puts 'Environment:'
  puts @environment.to_yaml
end

Private Instance Methods

generate_knife_cmd() click to toggle source

Private

Build the knife command that will do the provisioning. @return [String] knife command line string

# File lib/elevage/provisioner.rb, line 137
def generate_knife_cmd
  knife_cmd = 'knife vsphere vm clone --vsinsecure --start'

  # Authentication and host
  knife_cmd << " --vsuser #{@options[:vsuser]}"
  knife_cmd << " --vspass #{@options[:vspass]}"
  knife_cmd << " --vshost #{@vcenter['host']}"

  # VM Template (what we're cloning)
  knife_cmd << " --folder '#{@vcenter['imagefolder']}'"
  knife_cmd << " --template '#{@component['image']}'"

  # vSphere destination information (where the clone will end up)
  knife_cmd << " --vsdc '#{@vcenter['datacenter']}'"
  knife_cmd << " --dest-folder '#{@vcenter['destfolder']}"
  knife_cmd << "/#{@component['tier']}" if @vcenter['appendtier']
  knife_cmd << '\''
  knife_cmd << " --resource-pool '#{@vcenter['resourcepool']}'" if vcenter['resourcepool']
  knife_cmd << " --datastore '#{select_datastore}'"

  # VM Hardware
  knife_cmd << " --ccpu #{@component['compute']['cpu']}"
  knife_cmd << " --cram #{@component['compute']['ram']}"

  # VM Networking
  knife_cmd << " --cvlan '#{@component['network']['vlanid']}'"
  knife_cmd << " --cips #{@component['addresses'][@instance - 1]}/#{@component['network']['netmask']}"
  knife_cmd << " --cdnsips #{@vcenter['dnsips'].join(',')}"
  knife_cmd << " --cgw #{@component['network']['gateway']}"
  knife_cmd << " --chostname #{@name}"
  knife_cmd << " --ctz #{@vcenter['timezone']}"

  # AD Domain and DNS Suffix
  domain = @vcenter['domain']
  domain = domain[1, domain.length] if domain.start_with?('.')
  knife_cmd << " --cdomain #{domain}"
  knife_cmd << " --cdnssuffix #{domain}"

  # Knife Bootstrap options
  knife_cmd << ' --bootstrap'
  knife_cmd << " --template-file '#{@options['template-file']}'"

  # knife fqdn specifies how knife will connect to the target (in this case by IP)
  knife_cmd << " --fqdn #{@component['addresses'][@instance - 1]}"
  knife_cmd << " --ssh-user #{@options['ssh-user']}"
  knife_cmd << " --identity-file '#{@options['ssh-key']}'"

  # customization spec if it exists
  knife_cmd << " --cspec #{@component['cspec']}" if @component['cspec']

  # What the node should be identified as in Chef
  nodename = String.new(@name)
  nodename << @vcenter['domain'] if @vcenter['appenddomain']
  knife_cmd << " --node-name '#{nodename}'"

  # Assign the run_list
  knife_cmd << " --run-list '#{@component['runlist']}'"

  # Assign the Chef environment
  knife_cmd << " --environment '#{@environment.name}'"

  # What version of chef-client are we bootstrapping (not sure
  # this is necessary)
  knife_cmd << " --bootstrap-version #{@options['bootstrap-version']}"

  # Finally, the name of the VM as seen by vSphere.
  # Whereas nodename will optionally append the domain name, VM names
  # should *always* have the domain name appended - UNLESS it's windows
  vmname = String.new(@name)
  vmname << @vcenter['domain'] if @component['ostype'] != 'windows'
  knife_cmd << " #{vmname}"
end
select_datastore() click to toggle source

Private

Determine which datastore to use for this specific provisioning. @return [String] Name of datastore

# File lib/elevage/provisioner.rb, line 112
def select_datastore
  knife_cmd = 'knife vsphere datastore maxfree --vsinsecure'

  # Authentication and host
  knife_cmd << " --vsuser #{@options[:vsuser]}"
  knife_cmd << " --vspass #{@options[:vspass]}"
  knife_cmd << " --vshost #{@vcenter['host']}"

  # vSphere destination information (where the clone will end up)
  knife_cmd << " --vsdc '#{@vcenter['datacenter']}'"

  # datastore prefix
  knife_cmd << " --regex #{@vcenter['datastore']}"

  # get result and clean up
  @options['dry-run'] ? @vcenter['datastore'] : `#{knife_cmd}`.to_s.delete!("\n", '')
end