class VagrantPlugins::ProviderLibvirt::Action::SetBootOrder

boot order useful for pxe in discovery workflow

Public Class Methods

new(app, env) click to toggle source
# File lib/vagrant-libvirt/action/set_boot_order.rb, line 11
def initialize(app, env)
  @app    = app
  @logger = Log4r::Logger.new('vagrant_libvirt::action::set_boot_order')
  config = env[:machine].provider_config
  @boot_order = config.boot_order
end

Public Instance Methods

call(env) click to toggle source
# File lib/vagrant-libvirt/action/set_boot_order.rb, line 18
def call(env)
  # Only execute specific boot ordering if this is defined
  # in the Vagrant file
  unless @boot_order.count >= 1
    return @app.call(env)
  end

  # Get domain first
  begin
    domain = env[:machine].provider
                          .driver
                          .connection
                          .client
                          .lookup_domain_by_uuid(
                            env[:machine].id.to_s
                          )
  rescue => e
    raise Errors::NoDomainError,
          error_message: e.message
  end

  # If a domain is initially defined with no box or disk or
  # with an explicit boot order, Libvirt adds <boot dev="foo">
  # This conflicts with an explicit boot_order configuration,
  # so we need to remove it from the domain xml and feed it back.
  # Also see https://bugzilla.redhat.com/show_bug.cgi?id=1248514
  # as to why we have to do this after all devices have been defined.
  xml = Nokogiri::XML(domain.xml_desc)
  xml.search('/domain/os/boot').each(&:remove)

  # Parse the XML and find each defined drive and network interfacee
  hd = xml.search("/domain/devices/disk[@device='disk']")
  cdrom = xml.search("/domain/devices/disk[@device='cdrom']")
  # implemented only for 1 network
  nets = @boot_order.flat_map do |x|
    x.class == Hash ? x : nil
  end.compact
  raise 'Defined only for 1 network for boot' if nets.size > 1
  network = search_network(nets, xml)

  # Generate an array per device group and a flattened
  # array from all of those
  devices = { 'hd' => hd,
              'cdrom' => cdrom,
              'network' => network }

  final_boot_order = final_boot_order(@boot_order, devices)
  # Loop over the entire defined boot order array and
  # create boot order entries in the domain XML
  final_boot_order.each_with_index do |node, index|
    next if node.nil?
    boot = "<boot order='#{index + 1}'/>"
    node.add_child(boot)
    logger_msg(node, index)
  end

  # Finally redefine the domain XML through Libvirt
  # to apply the boot ordering
  env[:machine].provider
                .driver
                .connection
                .client
                .define_domain_xml(xml.to_s)

  @app.call(env)
end
final_boot_order(boot_order, devices) click to toggle source
# File lib/vagrant-libvirt/action/set_boot_order.rb, line 85
def final_boot_order(boot_order, devices)
  boot_order.flat_map do |category|
    devices[category.class == Hash ? category.keys.first : category]
  end
end
logger_msg(node, index) click to toggle source
# File lib/vagrant-libvirt/action/set_boot_order.rb, line 106
def logger_msg(node, index)
  name = if node.name == 'disk'
           node['device']
         elsif node.name == 'interface'
           node.name
         end
  @logger.debug "Setting #{name} to boot index #{index + 1}"
end
search_network(nets, xml) click to toggle source
# File lib/vagrant-libvirt/action/set_boot_order.rb, line 91
def search_network(nets, xml)
  str = '/domain/devices/interface'
  str += "[(@type='network' or @type='udp' or @type='bridge' or @type='direct')"
  unless nets.empty?
    net = nets.first
    network = net['network']
    dev = net['dev']
    str += " and source[@network='#{network}']" if network
    str += " and source[@dev='#{dev}']" if dev
  end
  str += ']'
  @logger.debug(str)
  xml.search(str)
end