class Vagrant::LXC::Driver

Attributes

container_name[R]

Default root folder where container configs are stored

customizations[R]

Default root folder where container configs are stored

Public Class Methods

new(container_name, sudo_wrapper = nil, cli = nil, privileged: true) click to toggle source
# File lib/vagrant-lxc/driver.rb, line 23
def initialize(container_name, sudo_wrapper = nil, cli = nil, privileged: true)
  @container_name = container_name
  @sudo_wrapper   = sudo_wrapper || SudoWrapper.new(privileged: privileged)
  @cli            = cli || CLI.new(@sudo_wrapper, container_name)
  @logger         = Log4r::Logger.new("vagrant::provider::lxc::driver")
  @customizations = []
end

Public Instance Methods

all_containers() click to toggle source
# File lib/vagrant-lxc/driver.rb, line 40
def all_containers
  @cli.list
end
attach(*command) click to toggle source
# File lib/vagrant-lxc/driver.rb, line 129
def attach(*command)
  @cli.attach(*command)
end
base_path() click to toggle source
# File lib/vagrant-lxc/driver.rb, line 44
def base_path
  Pathname.new("#{containers_path}/#{@container_name}")
end
bridge_exists?(bridge_name) click to toggle source
# File lib/vagrant-lxc/driver.rb, line 188
def bridge_exists?(bridge_name)
  @logger.info "Checking whether bridge #{bridge_name} exists"
  brctl_output = `ip link | egrep -q " #{bridge_name}:"`
  $?.to_i == 0
end
bridge_has_an_ip?(bridge_name) click to toggle source
# File lib/vagrant-lxc/driver.rb, line 183
def bridge_has_an_ip?(bridge_name)
  @logger.info "Checking whether the bridge #{bridge_name} has an IP"
  `ip -4 addr show scope global #{bridge_name}` =~ /^\s+inet ([0-9.]+)\/[0-9]+\s+/
end
bridge_is_in_use?(bridge_name) click to toggle source
# File lib/vagrant-lxc/driver.rb, line 194
def bridge_is_in_use?(bridge_name)
  # REFACTOR: This method is **VERY** hacky
  @logger.info "Checking if bridge #{bridge_name} is in use"
  brctl_output = `brctl show #{bridge_name} 2>/dev/null | tail -n +2 | grep -q veth`
  $?.to_i == 0
end
compress_rootfs() click to toggle source

TODO: This needs to be reviewed and specs needs to be written

# File lib/vagrant-lxc/driver.rb, line 223
def compress_rootfs
  # TODO: Pass in tmpdir so we can clean up from outside
  target_path    = "#{Dir.mktmpdir}/rootfs.tar.gz"

  @logger.info "Compressing '#{rootfs_path}' rootfs to #{target_path}"
  @sudo_wrapper.run('tar', '--numeric-owner', '-cvzf', target_path, '-C',
    rootfs_path.parent.to_s, "./#{rootfs_path.basename.to_s}")

  @logger.info "Changing rootfs tarball owner"
  user_details = Etc.getpwnam(Etc.getlogin)
  @sudo_wrapper.run('chown', "#{user_details.uid}:#{user_details.gid}", target_path)

  target_path
end
config_path() click to toggle source
# File lib/vagrant-lxc/driver.rb, line 48
def config_path
  base_path.join('config').to_s
end
config_string() click to toggle source
# File lib/vagrant-lxc/driver.rb, line 78
def config_string
  @sudo_wrapper.run('cat', config_path)
end
configure_private_network(bridge_name, bridge_ip, container_name, address_type, ip) click to toggle source
# File lib/vagrant-lxc/driver.rb, line 137
def configure_private_network(bridge_name, bridge_ip, container_name, address_type, ip)
  @logger.info "Configuring network interface for #{container_name} using #{ip} and bridge #{bridge_name}"
  if ip
    ip += '/24'
  end

  if ! bridge_exists?(bridge_name)
    if not bridge_ip
      raise "Bridge is missing and no IP was specified!"
    end

    @logger.info "Creating the bridge #{bridge_name}"
    cmd = [
      'brctl',
      'addbr',
      bridge_name
    ]
    @sudo_wrapper.run(*cmd)
  end

  if ! bridge_has_an_ip?(bridge_name)
    if not bridge_ip
      raise "Bridge has no IP and none was specified!"
    end
    @logger.info "Adding #{bridge_ip} to the bridge #{bridge_name}"
    cmd = [
      'ip',
      'addr',
      'add',
      "#{bridge_ip}/24",
      'dev',
      bridge_name
    ]
    @sudo_wrapper.run(*cmd)
    @sudo_wrapper.run('ip', 'link', 'set', bridge_name, 'up')
  end

  cmd = [
    Vagrant::LXC.source_root.join('scripts/pipework').to_s,
    bridge_name,
    container_name,
    ip ||= "dhcp"
  ]
  @sudo_wrapper.run(*cmd)
end
containers_path() click to toggle source

Root folder where container configs are stored

# File lib/vagrant-lxc/driver.rb, line 36
def containers_path
  @containers_path ||= @cli.config('lxc.lxcpath')
end
create(name, backingstore, backingstore_options, template_path, config_file, template_options = {}) click to toggle source
# File lib/vagrant-lxc/driver.rb, line 82
def create(name, backingstore, backingstore_options, template_path, config_file, template_options = {})
  @cli.name = @container_name = name

  @logger.debug "Creating container..."

  @cli.create template_path, backingstore, backingstore_options, config_file, template_options
end
destroy() click to toggle source
# File lib/vagrant-lxc/driver.rb, line 121
def destroy
  @cli.destroy
end
forced_halt() click to toggle source
# File lib/vagrant-lxc/driver.rb, line 116
def forced_halt
  @logger.info('Shutting down container...')
  @cli.transition_to(:stopped) { |c| c.stop }
end
info(*command) click to toggle source
# File lib/vagrant-lxc/driver.rb, line 133
def info(*command)
  @cli.info(*command)
end
mac_address() click to toggle source
# File lib/vagrant-lxc/driver.rb, line 70
def mac_address
  return @mac_address if @mac_address

  if config_string =~ /^lxc\.network\.hwaddr\s*+=\s*+(.+)$/
    @mac_address = $1
  end
end
prune_customizations() click to toggle source
# File lib/vagrant-lxc/driver.rb, line 244
def prune_customizations
  # Use sed to just strip out the block of code which was inserted by Vagrant
  @logger.debug 'Prunning vagrant-lxc customizations'
  contents = config_string
  contents.gsub! /^# VAGRANT-BEGIN(.|\s)*# VAGRANT-END\n/, ''
  write_config(contents)
end
remove_bridge(bridge_name) click to toggle source
# File lib/vagrant-lxc/driver.rb, line 201
def remove_bridge(bridge_name)
  if ['lxcbr0', 'virbr0'].include? bridge_name
     @logger.info "Skipping removal of system bridge #{bridge_name}"
     return
  end

  return unless bridge_exists?(bridge_name)

  @logger.info "Removing bridge #{bridge_name}"
  @sudo_wrapper.run('ip', 'link', 'set', bridge_name, 'down')
  @sudo_wrapper.run('brctl', 'delbr', bridge_name)
end
rootfs_path() click to toggle source
# File lib/vagrant-lxc/driver.rb, line 52
def rootfs_path
  config_entry = config_string.match(/^lxc\.rootfs\s+=\s+(.+)$/)[1]
  case config_entry
  when /^overlayfs:/
    # Split on colon (:), ignoring any colon escaped by an escape character ( \ )
    # Pays attention to when the escape character is itself escaped.
    fs_type, master_path, overlay_path = config_entry.split(/(?<!\\)(?:\\\\)*:/)
    if overlay_path
      Pathname.new(overlay_path)
    else
      # Malformed: fall back to prior behaviour
      Pathname.new(config_entry)
    end
  else
    Pathname.new(config_entry)
  end
end
share_folder(host_path, guest_path, mount_options = nil) click to toggle source
# File lib/vagrant-lxc/driver.rb, line 96
def share_folder(host_path, guest_path, mount_options = nil)
  guest_path    = guest_path.gsub(/^\//, '').gsub(' ', '\\\040')
  mount_options = Array(mount_options || ['bind', 'create=dir'])
  host_path     = host_path.to_s.gsub(' ', '\\\040')
  @customizations << ['mount.entry', "#{host_path} #{guest_path} none #{mount_options.join(',')} 0 0"]
end
share_folders(folders) click to toggle source
# File lib/vagrant-lxc/driver.rb, line 90
def share_folders(folders)
  folders.each do |f|
    share_folder(f[:hostpath], f[:guestpath], f.fetch(:mount_options, nil))
  end
end
start(customizations) click to toggle source
# File lib/vagrant-lxc/driver.rb, line 103
def start(customizations)
  @logger.info('Starting container...')

  if ENV['LXC_START_LOG_FILE']
    extra = ['-o', ENV['LXC_START_LOG_FILE'], '-l', 'DEBUG']
  end

  prune_customizations
  write_customizations(customizations + @customizations)

  @cli.start(extra)
end
state() click to toggle source
# File lib/vagrant-lxc/driver.rb, line 238
def state
  if @container_name
    @cli.state
  end
end
supports_attach?() click to toggle source
# File lib/vagrant-lxc/driver.rb, line 125
def supports_attach?
  @cli.supports_attach?
end
supports_new_config_format() click to toggle source
# File lib/vagrant-lxc/driver.rb, line 218
def supports_new_config_format
  Gem::Version.new(version) >= Gem::Version.new('2.1.0')
end
update_config_keys(path = nil) click to toggle source
# File lib/vagrant-lxc/driver.rb, line 252
def update_config_keys(path = nil)
  path = path || config_path
  @cli.update_config(path)
rescue Errors::ExecuteError
  # not on LXC 2.1+. Doesn't matter, ignore.
end
validate!() click to toggle source
# File lib/vagrant-lxc/driver.rb, line 31
def validate!
  raise ContainerNotFound if @container_name && ! @cli.list.include?(@container_name)
end
version() click to toggle source
# File lib/vagrant-lxc/driver.rb, line 214
def version
  @version ||= @cli.version
end

Protected Instance Methods

write_config(contents) click to toggle source
# File lib/vagrant-lxc/driver.rb, line 274
def write_config(contents)
  confpath = base_path.join('config').to_s
  begin
    File.open(confpath, File::RDWR) do |file|
      file.write contents
    end
  rescue
    # We don't have permissions to write in the conf file. That's probably because it's a
    # privileged container. Work around that through sudo_wrapper.
    Tempfile.new('lxc-config').tap do |file|
      file.chmod 0644
      file.write contents
      file.close
      @sudo_wrapper.run 'cp', '-f', file.path, confpath
      @sudo_wrapper.run 'chown', 'root:root', confpath
    end
  end
end
write_customizations(customizations) click to toggle source
# File lib/vagrant-lxc/driver.rb, line 261
def write_customizations(customizations)
  customizations = customizations.map do |key, value|
    "lxc.#{key}=#{value}"
  end
  customizations.unshift '# VAGRANT-BEGIN'
  customizations      << "# VAGRANT-END\n"

  contents = config_string
  contents << customizations.join("\n")

  write_config(contents)
end