class HashiCorp::VagrantVMwareDesktop::Action::Network

This action sets up all the network adapters for the machine and also tells the guest to configure the networks.

Constants

DEFAULT_VMNET_NAT

Public Class Methods

new(app, env) click to toggle source
# File lib/vagrant-vmware-desktop/action/network.rb, line 24
def initialize(app, env)
  @app    = app
  @logger = Log4r::Logger.new("hashicorp::provider::vmware::network")
end

Public Instance Methods

assign_interface_numbers(networks, adapters) click to toggle source
# File lib/vagrant-vmware-desktop/action/network.rb, line 296
def assign_interface_numbers(networks, adapters)
  # First create a mapping of adapter slot to interface number
  # by reading over the existing network adapters.
  slots_in_use = []
  vm_adapters = @env[:machine].provider.driver.read_network_adapters
  vm_adapters.each do |adapter|
    slots_in_use << adapter[:slot].to_i
  end

  slot_to_interface = {}
  slots_in_use.sort.each_index do |i|
    slot_to_interface[slots_in_use[i]] = i
  end

  # Make a pass through the adapters to assign the :interface
  # key to each network configuration.
  adapters.each_index do |i|
    adapter = adapters[i]
    network = networks[i]

    # Figure out the interface number by simple lookup
    network[:interface] = slot_to_interface[adapter[:slot]]
  end
end
bridged_adapter(config) click to toggle source
# File lib/vagrant-vmware-desktop/action/network.rb, line 259
def bridged_adapter(config)
  mac_address = config[:mac]
  mac_address = vmware_mac_format(mac_address) if mac_address

  return {
    :type        => :bridged,
    :mac_address => mac_address
  }
end
bridged_config(options) click to toggle source
# File lib/vagrant-vmware-desktop/action/network.rb, line 251
def bridged_config(options)
  return {
    :auto_config => true,
    :mac         => nil,
    :type        => :dhcp
  }.merge(options || {})
end
bridged_network_config(config) click to toggle source
# File lib/vagrant-vmware-desktop/action/network.rb, line 269
def bridged_network_config(config)
  if config[:ip]
    options = {
      auto_config: true,
      mac:         nil,
      netmask:     "255.255.255.0",
    }.merge(config)
    options[:type] = :static
    return options
  end

  return {
    :type => :dhcp
  }
end
call(env) click to toggle source
# File lib/vagrant-vmware-desktop/action/network.rb, line 29
def call(env)
  # Set this to an ivar so that helper methods have access to it
  @env = env

  # Get the list of network adapters from the configuration
  network_adapters_config = env[:machine].provider_config.network_adapters.dup

  # Assign the adapter slot for each high-level network
  available_slots = Set.new(1..8)
  network_adapters_config.each do |slot, _data|
    available_slots.delete(slot)
  end

  @logger.debug("Available slots for high-level adapters: #{available_slots.inspect}")
  @logger.info("Determining network adapters required for high-level configuration...")
  available_slots = available_slots.to_a.sort
  env[:machine].config.vm.networks.each do |type, options|
    # We only handle private and public networks
    next if type != :private_network && type != :public_network

    scope_key = "vmware_#{PRODUCT_NAME}".to_sym
    options   = scoped_hash_override(options, scope_key)

    # Figure out the slot that this adapter will go into
    slot = options[:adapter]
    if !slot
      if available_slots.empty?
        raise Errors::NetworkingNoSlotsForHighLevel
      end

      slot = available_slots.shift
    end

    # Configure it
    data = nil
    if type == :private_network
      # private_network = hostonly
      data        = [:hostonly, options]
    elsif type == :public_network
      # public_network = bridged
      data        = [:bridged, options]
    end

    # Store it!
    @logger.info(" -- Slot #{slot}: #{data[0]}")
    network_adapters_config[slot] = data
  end

  @logger.info("Determining adapters and compiling network configuration...")
  adapters = []
  networks = []
  network_adapters_config.each do |slot, data|
    type    = data[0]
    options = data[1]

    if slot == 0 && env[:machine].provider_config.nat_device != DEFAULT_VMNET_NAT
      # TODO: what's the device name on windows?
      options[:device] = "/dev/#{env[:machine].provider_config.nat_device}"
    end

    @logger.info("Slot #{slot}. Type: #{type}")

    # Get normalized configuration so we can add/scrub values
    config = send("#{type}_config", options)
    @logger.debug("Normalized configuration: #{config.inspect}")

    # Get the adapter configuration for the driver
    adapter = send("#{type}_adapter", config)
    adapter[:slot] = slot
    adapters << adapter
    @logger.debug("Adapter configuration: #{adapter.inspect}")

    # Get the network configuration for the guest
    network = send("#{type}_network_config", config)
    network[:auto_config] = config[:auto_config]
    networks << network
    @logger.debug("Network configuration: #{network.inspect}")
  end

  if !adapters.empty?
    # Modify the VM metadata to add adapters
    @logger.info("Enabling #{adapters.length} adapters...")
    Helper::Lock.lock(env[:machine], "vmware-network") do
      env[:ui].info(I18n.t("hashicorp.vagrant_vmware_desktop.enabling_adapters"))
      env[:machine].provider.driver.setup_adapters(adapters, env[:machine].provider_config.allowlist_verified)
    end
  end

  @app.call(env)

  if !networks.empty?
    # Assign interface numbers to the networks
    assign_interface_numbers(networks, adapters)

    networks_to_configure = networks.select { |n| n[:auto_config] }
    env[:ui].info(I18n.t("hashicorp.vagrant_vmware_desktop.configuring_networks"))
    env[:machine].guest.capability(:configure_networks, networks_to_configure)
  end
end
hostonly_adapter(config) click to toggle source
# File lib/vagrant-vmware-desktop/action/network.rb, line 188
def hostonly_adapter(config)
  # If we're just doing normal DHCP, then we just connect to the
  # basic default adapter.
  if config[:type] == :dhcp
    return {
      :type => :hostonly
    }
  end

  # Otherwise we want a static IP. Start by trying to find
  # an existing network that matches our needs.
  vmnet = nil
  @env[:machine].provider.driver.read_vmnet_devices.each do |device|
    if device[:hostonly_subnet] == config[:subnet_ip]
      @logger.info("Found matching vmnet device: #{device[:name]}")
      vmnet = device
      break
    end
  end

  # Check for collisions by checking for if there is another device
  # that the IP would route to. The basic logic is: if there is
  # a device, and it is NOT the vmnet we care about, then it
  # is an error.
  @logger.info("Checking for hostonly network collisions...")
  device = routing_table.device_for_route(config[:ip])
  if device
    if !vmnet || device != vmnet[:name]
      # There is a collision with some other networking device.
      raise Errors::NetworkingHostOnlyCollision,
        :device => device,
        :ip     => config[:ip]
    end
  end

  if !vmnet
    @logger.info("No collisions detected, creating new vmnet device.")
    vmnet = @env[:machine].provider.driver.create_vmnet_device(
      :netmask => config[:netmask],
      :subnet_ip => config[:subnet_ip])
  end

  # Determine MAC address of the adapter
  mac_address = config[:mac]
  mac_address = vmware_mac_format(mac_address) if mac_address

  # Return a more complex configuration to describe what we need
  return {
    :type        => :custom,
    :mac_address => mac_address,
    :vnet        => vmnet[:name]
  }
end
hostonly_config(options) click to toggle source
# File lib/vagrant-vmware-desktop/action/network.rb, line 150
def hostonly_config(options)
  # Get the default configuration built up
  config = {
    :auto_config => true,
    :netmask     => "255.255.255.0",
    :type        => :dhcp
  }.merge(options || {})

  if options[:ip]
    # Check if we are using ipv6, which is not supported
    ip = IPAddr.new(options[:ip])
    if ip.ipv6?
      raise Errors::VMNetNoIPV6
    end

    # We are using static if we have an IP set
    config[:type] = :static

    # Get the static IP and use the static IP + subnet mask to
    # determine the subnet IP.
    static_ip = config[:ip]
    subnet_ip = network_address(static_ip, config[:netmask])
    config[:subnet_ip] = subnet_ip

    # Calculate the actual IP of the adapter itself, which is usually
    # just the network address "+ 1" in the last octet
    ip_parts = subnet_ip.split(".").map { |i| i.to_i }
    adapter_ip    = ip_parts.dup
    adapter_ip[3] += 1
    config[:adapter_ip] ||= adapter_ip.join(".")
  end

  # Make sure the type is a symbol
  config[:type] = config[:type].to_sym

  return config
end
hostonly_network_config(config) click to toggle source
# File lib/vagrant-vmware-desktop/action/network.rb, line 242
def hostonly_network_config(config)
  return {
    :type       => config[:type],
    :adapter_ip => config[:adapter_ip],
    :ip         => config[:ip],
    :netmask    => config[:netmask]
  }
end
nat_adapter(config) click to toggle source
# File lib/vagrant-vmware-desktop/action/network.rb, line 136
def nat_adapter(config)
  {
    type: :nat,
    mac_address: config[:mac_address],
    vnet: config[:device]
  }.compact
end
nat_config(options) click to toggle source
# File lib/vagrant-vmware-desktop/action/network.rb, line 129
def nat_config(options)
  return {
    :auto_config => true,
    :type        => :dhcp
  }.merge(options)
end
nat_network_config(config) click to toggle source
# File lib/vagrant-vmware-desktop/action/network.rb, line 144
def nat_network_config(config)
  return {
    :type => :dhcp
  }
end
routing_table() click to toggle source

This is a lazy loaded {Helper::RoutingTable}.

@return [Helper::RoutingTable]

# File lib/vagrant-vmware-desktop/action/network.rb, line 333
def routing_table
  @routing_table ||= Helper::RoutingTable.new
end
vmware_mac_format(mac) click to toggle source

This converts the Vagrant configured MAC address format to a typical MAC address format.

@param [String] mac @return [String]

# File lib/vagrant-vmware-desktop/action/network.rb, line 326
def vmware_mac_format(mac)
  mac.scan(/.{2}/).join(":")
end