class VagrantPlugins::ProviderLibvirt::Action::ResolveDiskSettings

Public Class Methods

new(app, _env) click to toggle source
# File lib/vagrant-libvirt/action/resolve_disk_settings.rb, line 16
def initialize(app, _env)
  @logger = Log4r::Logger.new('vagrant_libvirt::action::resolve_disk_devices')
  @app = app
end

Public Instance Methods

call(env) click to toggle source
# File lib/vagrant-libvirt/action/resolve_disk_settings.rb, line 21
def call(env)
  # Get config.
  config = env[:machine].provider_config

  domain_name = env[:domain_name] # only set on create
  disk_bus = config.disk_bus
  disk_device = config.disk_device
  domain_volume_cache = config.volume_cache || 'default'

  # Storage
  storage_pool_name = config.storage_pool_name
  snapshot_pool_name = config.snapshot_pool_name
  domain_volumes = []
  disks = config.disks.dup

  resolver = ::VagrantPlugins::ProviderLibvirt::Util::DiskDeviceResolver.new(disk_device[0..1])

  # Get path to domain image from the storage pool selected if we have a box.
  if env[:machine].config.vm.box
    pool_name = if snapshot_pool_name == storage_pool_name
                  storage_pool_name
                else
                  snapshot_pool_name
                end

    if env[:box_volumes].nil?
      # domain must be already created, need to read domain volumes from domain XML
      libvirt_domain = env[:machine].provider.driver.connection.client.lookup_domain_by_uuid(
        env[:machine].id
      )
      domain_xml = libvirt_domain.xml_desc(1)
      xml_descr = REXML::Document.new(domain_xml)
      domain_name = xml_descr.elements['domain'].elements['name'].text
      disks_xml = REXML::XPath.match(xml_descr, '/domain/devices/disk[@device="disk"]')
      have_aliases = !REXML::XPath.match(disks_xml, './alias[@name="ua-box-volume-0"]').first.nil?
      env[:ui].warn(I18n.t('vagrant_libvirt.domain_xml.obsolete_method')) unless have_aliases

      if have_aliases
        REXML::XPath.match(disks_xml,
                           './alias[contains(@name, "ua-box-volume-")]').each_with_index do |alias_xml, idx|
          domain_volumes.push(volume_from_xml(alias_xml.parent, domain_name, idx))
        end
      else
        # fallback to try and infer which boxes are box images, as they are listed first
        # as soon as there is no match, can exit
        disks_xml.each_with_index do |box_disk_xml, idx|
          diskname = box_disk_xml.elements['source'].attributes['file'].rpartition('/').last

          break if volume_name(domain_name, idx) != diskname

          domain_volumes.push(volume_from_xml(box_disk_xml, domain_name, idx))
        end
      end
    else

      @logger.debug "Search for volumes in pool: #{pool_name}"
      env[:box_volumes].each_index do |index|
        domain_volume = env[:machine].provider.driver.connection.volumes.all(
          name: volume_name(domain_name, index)
        ).find { |x| x.pool_name == pool_name }
        raise Errors::NoDomainVolume if domain_volume.nil?

        domain_volumes.push(
          {
            name: volume_name(domain_name, index),
            device: env[:box_volumes][index][:device],
            cache: domain_volume_cache,
            bus: disk_bus,
            absolute_path: domain_volume.path,
            virtual_size: env[:box_volumes][index][:virtual_size],
            pool: pool_name,
          }
        )
      end
    end

    resolver.resolve!(domain_volumes)

    # If we have a box, take the path from the domain volume and set our storage_prefix.
    # If not, we dump the storage pool xml to get its defined path.
    # the default storage prefix is typically: /var/lib/libvirt/images/
    storage_prefix = "#{File.dirname(domain_volumes[0][:absolute_path])}/" # steal
  else
    if domain_name.nil?
      # Ensure domain name is set for subsequent steps if restarting a machine without a box
      libvirt_domain = env[:machine].provider.driver.connection.client.lookup_domain_by_uuid(
        env[:machine].id
      )
      domain_xml = libvirt_domain.xml_desc(1)
      xml_descr = REXML::Document.new(domain_xml)
      domain_name = xml_descr.elements['domain'].elements['name'].text
    end

    storage_prefix = get_disk_storage_prefix(env[:machine], storage_pool_name)
  end

  resolver.resolve!(disks)

  disks.each do |disk|
    disk[:path] ||= disk_name(domain_name, disk)

    # On volume creation, the <path> element inside <target>
    # is oddly ignored; instead the path is taken from the
    # <name> element:
    # http://www.redhat.com/archives/libvir-list/2008-August/msg00329.html
    disk[:name] = disk[:path]

    disk[:absolute_path] = storage_prefix + disk[:path]

    if disk[:pool].nil?
      disk[:pool] = storage_pool_name
    else
      @logger.debug "Overriding pool name with: #{disk[:pool]}"
      disk_storage_prefix = get_disk_storage_prefix(env[:machine], disk[:pool])
      disk[:absolute_path] = disk_storage_prefix + disk[:path]
      @logger.debug "Overriding disk path with: #{disk[:absolute_path]}"
    end
  end

  env[:domain_volumes] = domain_volumes
  env[:disks] = disks

  @app.call(env)
end

Private Instance Methods

disk_name(name, disk) click to toggle source
# File lib/vagrant-libvirt/action/resolve_disk_settings.rb, line 148
def disk_name(name, disk)
  "#{name}-#{disk[:device]}.#{disk[:type]}" # disk name
end
get_disk_storage_prefix(machine, disk_pool_name) click to toggle source
# File lib/vagrant-libvirt/action/resolve_disk_settings.rb, line 152
def get_disk_storage_prefix(machine, disk_pool_name)
  disk_storage_pool = machine.provider.driver.connection.client.lookup_storage_pool_by_name(disk_pool_name)
  raise Errors::NoStoragePool if disk_storage_pool.nil?

  xml = Nokogiri::XML(disk_storage_pool.xml_desc)
  "#{xml.xpath('/pool/target/path').inner_text}/"
end
volume_from_xml(device_xml, domain_name, index) click to toggle source
# File lib/vagrant-libvirt/action/resolve_disk_settings.rb, line 164
def volume_from_xml(device_xml, domain_name, index)
  driver = device_xml.elements['driver']
  source = device_xml.elements['source']
  target = device_xml.elements['target']

  {
    name: volume_name(domain_name, index),
    device: target.attributes['dev'],
    cache: driver.attributes['cache'],
    bus: target.attributes['bus'],
    absolute_path: source.attributes['file'],
  }
end
volume_name(domain_name, index) click to toggle source
# File lib/vagrant-libvirt/action/resolve_disk_settings.rb, line 160
def volume_name(domain_name, index)
  domain_name + (index.zero? ? '.img' : "_#{index}.img")
end