class Chef::Provisioning::OneViewDriver

Provisioning driver for HPE OneView

Public Class Methods

canonicalize_url(url, config) click to toggle source
# File lib/chef/provisioning/oneview_driver.rb, line 19
def self.canonicalize_url(url, config)
  _scheme, oneview_url = url.split(':', 2)
  oneview_url ||= config[:driver_options][:oneview][:url] rescue nil
  oneview_url ||= config[:knife][:oneview_url] rescue nil
  oneview_url ||= ENV['ONEVIEWSDK_URL']
  raise 'Must set the oneview driver url! See docs for more info' if oneview_url.nil? || oneview_url.empty?
  "oneview:#{oneview_url}"
end
from_url(url, config) click to toggle source
# File lib/chef/provisioning/oneview_driver.rb, line 28
def self.from_url(url, config)
  OneViewDriver.new(url, config)
end
new(url, config) click to toggle source
Calls superclass method
# File lib/chef/provisioning/oneview_driver.rb, line 32
def initialize(url, config)
  _scheme, oneview_url = url.split(':', 2)
  super(url, config)

  ov_config = config[:driver_options][:oneview] || {} rescue {}
  ov_config = Hash[ov_config.map { |k, v| [k.to_sym, v] }] # Convert string keys to symbols

  ov_config[:url] = oneview_url
  ov_config[:user] ||= config[:knife][:oneview_username] || ENV['ONEVIEWSDK_USER']
  ov_config[:password] ||= config[:knife][:oneview_password] || ENV['ONEVIEWSDK_PASSWORD']
  ov_config[:token] ||= config[:knife][:oneview_token] || ENV['ONEVIEWSDK_TOKEN']
  ov_config[:ssl_enabled] = false if ov_config[:ssl_enabled] == false || (ov_config[:ssl_enabled].nil? && config[:knife][:oneview_ignore_ssl])
  ov_config[:timeout] ||= config[:knife][:oneview_timeout] if config[:knife][:oneview_timeout]
  ov_config[:print_wait_dots] = true if ov_config[:print_wait_dots].nil?
  ov_config[:logger] = Chef::Log
  @ov = OneviewSDK::Client.new(ov_config) # OneView client object

  icsp_config = config[:driver_options][:icsp] || {} rescue {}

  @icsp_base_url       = icsp_config[:url] || config[:knife][:icsp_url]
  @icsp_username       = icsp_config[:user] || config[:knife][:icsp_username]
  @icsp_password       = icsp_config[:password] || config[:knife][:icsp_password]
  @icsp_disable_ssl    = false
  @icsp_disable_ssl    = true if icsp_config[:ssl_enabled] == false || config[:knife][:icsp_ignore_ssl] == true
  @icsp_api_version    = 102 # Use this version for all calls that don't override it
  @icsp_timeout        = icsp_config[:timeout] || config[:knife][:icsp_timeout]

  Chef::Log.warn 'ICSP url not set! ICSP will be ignored'      if @icsp_base_url.nil?
  Chef::Log.warn 'ICSP user not set! ICSP will be ignored'     if @icsp_username.nil?
  Chef::Log.warn 'ICSP password not set! ICSP will be ignored' if @icsp_password.nil?

  @icsp_ignore = @icsp_base_url.nil? || @icsp_username.nil? || @icsp_password.nil?
  # If the config is not specified, skip ICSP
  return if @icsp_ignore
  Chef::Log.debug("Logging into ICSP at #{@icsp_base_url}")
  @current_icsp_api_version = get_icsp_api_version
  @icsp_key = login_to_icsp
end

Public Instance Methods

allocate_machine(action_handler, machine_spec, machine_options) click to toggle source
# File lib/chef/provisioning/oneview_driver.rb, line 72
def allocate_machine(action_handler, machine_spec, machine_options)
  raise 'Machine definition missing :driver_options parameter!' unless machine_options[:driver_options]
  if machine_spec.reference
    profile = OneviewSDK::ServerProfile.find_by(@ov, serialNumber: machine_spec.reference['serial_number']).first
    if profile # Update reference data
      machine_spec.reference['driver_url'] = driver_url
      machine_spec.reference['driver_version'] = ONEVIEW_DRIVER_VERSION
      # TODO: Update profile if differs from Chef and/or Template config
      machine_spec.reference['profile_name'] = profile['name']
    else # It doesn't really exist
      action_handler.report_progress "Machine #{machine_spec.name} does not really exist.  Recreating ..."
      machine_spec.reference = nil
    end
  end
  unless machine_spec.reference
    action_handler.perform_action "Allocate server #{machine_spec.name}" do
      profile = create_machine(action_handler, machine_spec.name, machine_options)
      machine_spec.reference = {
        'driver_url' => driver_url,
        'driver_version' => ONEVIEW_DRIVER_VERSION,
        'serial_number' => profile['serialNumber'],
        'profile_name' => profile['name']
      }
    end
  end
end
allocate_machines(action_handler, specs_and_options, _parallelizer) click to toggle source
# File lib/chef/provisioning/oneview_driver.rb, line 100
def allocate_machines(action_handler, specs_and_options, _parallelizer)
  specs_and_options.each do |machine_spec, machine_options|
    allocate_machine(action_handler, machine_spec, machine_options)
  end
end
connect_to_machine(machine_spec, machine_options) click to toggle source
# File lib/chef/provisioning/oneview_driver.rb, line 185
def connect_to_machine(machine_spec, machine_options)
  machine_for(machine_spec, machine_options)
end
destroy_machine(action_handler, machine_spec, machine_options) click to toggle source
# File lib/chef/provisioning/oneview_driver.rb, line 149
def destroy_machine(action_handler, machine_spec, machine_options)
  return unless machine_spec.reference
  profile = OneviewSDK::ServerProfile.find_by(@ov, serialNumber: machine_spec.reference['serial_number']).first
  if profile
    profile.get_server_hardware.power_off
    action_handler.perform_action "Delete server profile #{machine_spec.name}" do
      action_handler.report_progress "INFO: Deleting server profile #{machine_spec.name}"
      profile.delete
    end
  else
    action_handler.report_progress "INFO: #{machine_spec.name} is already deleted."
  end
  destroy_icsp_server(action_handler, machine_spec) unless @icsp_ignore # Delete os deployment server from ICSP

  name = machine_spec.name # Save for next steps

  # Remove entry from known_hosts file(s)
  ip_address = ip_from_machine(machine_spec, machine_options)
  return unless ip_address
  action_handler.perform_action "Delete entries for #{name} (#{ip_address}) from known_hosts file(s)" do
    files = [File.expand_path('~/.ssh/known_hosts'), File.expand_path('/etc/ssh/known_hosts')]
    files.each do |f|
      next unless File.exist?(f)
      begin
        text = File.read(f)
        text.gsub!(/#{ip_address} ssh-rsa.*(\n|\r\n)/, '')
        File.open(f, 'w') { |file| file.puts text } if text
      rescue Exception => e
        action_handler.report_progress "WARN: Failed to delete entries for #{name} (#{ip_address}) from known_hosts file: '#{f}'! "
        puts "Error: #{e.message}"
      end
    end
  end
end
machine_for(machine_spec, machine_options) click to toggle source
# File lib/chef/provisioning/oneview_driver.rb, line 120
def machine_for(machine_spec, machine_options)
  bootstrap_ip_address = ip_from_machine(machine_spec, machine_options)
  raise 'Server IP address not specified and could not be retrieved!' unless bootstrap_ip_address
  username = machine_options[:transport_options][:user] || 'root' rescue 'root'
  default_ssh_options = {
    # auth_methods: ['password', 'publickey'],
    # keys: ['~/.ssh/id_rsa'],
    password: Chef::Config.knife[:node_root_password]
  }
  ssh_options = machine_options[:transport_options][:ssh_options] || default_ssh_options rescue default_ssh_options
  default_options = {
    prefix: 'sudo ',
    ssh_pty_enable: true
  }
  options = machine_options[:transport_options][:options] || default_options rescue default_options
  transport = Chef::Provisioning::Transport::SSH.new(bootstrap_ip_address, username, ssh_options, options, config)
  convergence_strategy = Chef::Provisioning::ConvergenceStrategy::InstallSh.new(
    machine_options[:convergence_options], {}
  )
  Chef::Provisioning::Machine::UnixMachine.new(machine_spec, transport, convergence_strategy)
end
ready_machine(action_handler, machine_spec, machine_options) click to toggle source
# File lib/chef/provisioning/oneview_driver.rb, line 107
def ready_machine(action_handler, machine_spec, machine_options)
  profile = OneviewSDK::ServerProfile.find_by(@ov, serialNumber: machine_spec.reference['serial_number']).first
  raise "Failed to retrieve Server Profile for #{machine_spec.name}. Serial Number used to search: #{machine_spec.reference['serial_number']}" unless profile
  customize_machine(action_handler, machine_spec, machine_options, profile)
  if @icsp_ignore == true
    Chef::Log.warn("Skipping ICSP configuration for machine '#{machine_spec.name}'")
    Machine::BasicMachine.new(machine_spec, OneViewNilTransport.new, OneViewNilConvergence.new)
  else # Return a machine object that Chef can connect to (to install chef-client)
    machine_for(machine_spec, machine_options)
  end
end
stop_machine(_action_handler, machine_spec, _machine_options) click to toggle source
# File lib/chef/provisioning/oneview_driver.rb, line 142
def stop_machine(_action_handler, machine_spec, _machine_options)
  return unless machine_spec.reference
  profile = OneviewSDK::ServerProfile.find_by(@ov, serialNumber: machine_spec.reference['serial_number']).first
  profile.get_server_hardware.power_off
end

Private Instance Methods

ip_from_machine(machine_spec, machine_options) click to toggle source
# File lib/chef/provisioning/oneview_driver.rb, line 191
def ip_from_machine(machine_spec, machine_options)
  return machine_options[:driver_options][:ip_address] if machine_options[:driver_options][:ip_address]

  id, connection = machine_options[:driver_options][:connections].find { |_id, c| c[:bootstrap] == true }
  raise 'Must specify a connection to use to bootstrap!' unless id && connection # TODO: Try first connection anyways?
  return connection[:ip4Address] if connection[:ip4Address] # Return static IP if set
  # Look for dhcp address given to this connection
  if machine_spec.data['normal']['icsp'] && machine_spec.data['normal']['icsp']['interfaces']
    interface = machine_spec.data['normal']['icsp']['interfaces'].find { |i| i['oneViewId'] == id }
    if interface
      addr = interface['ipv4Addr'] || interface['ipv6Addr']
      return addr if addr
    end
  end
  profile = OneviewSDK::ServerProfile.find_by(@ov, serialNumber: machine_spec.reference['serial_number']).first
  my_server = get_icsp_server_by_sn(machine_spec.reference['serial_number'])
  mac = profile['connections'].find { |x| x['id'] == id }['mac']
  interface = my_server['interfaces'].find { |i| i['macAddr'] == mac }
  addr = interface['ipv4Addr'] || interface['ipv6Addr']
  addr ||= my_server['hostName'] # Fall back on hostName
  Chef::Log.warn "IP address for '#{machine_spec.name}' not specified and could not be retrieved!" unless addr
  addr
end