Class: Vagrant::LXC::Driver

Inherits:
Object
  • Object
show all
Defined in:
lib/vagrant-lxc/driver.rb,
lib/vagrant-lxc/driver/cli.rb

Defined Under Namespace

Classes: CLI, ContainerNotFound

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(container_name, sudo_wrapper = nil, cli = nil, privileged: true) ⇒ Driver

Returns a new instance of Driver.



23
24
25
26
27
28
29
# 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

Instance Attribute Details

#container_nameObject (readonly)

Default root folder where container configs are stored



20
21
22
# File 'lib/vagrant-lxc/driver.rb', line 20

def container_name
  @container_name
end

#customizationsObject (readonly)

Default root folder where container configs are stored



20
21
22
# File 'lib/vagrant-lxc/driver.rb', line 20

def customizations
  @customizations
end

Instance Method Details

#all_containersObject



40
41
42
# File 'lib/vagrant-lxc/driver.rb', line 40

def all_containers
  @cli.list
end

#attach(*command) ⇒ Object



128
129
130
# File 'lib/vagrant-lxc/driver.rb', line 128

def attach(*command)
  @cli.attach(*command)
end

#base_pathObject



44
45
46
# File 'lib/vagrant-lxc/driver.rb', line 44

def base_path
  Pathname.new("#{containers_path}/#{@container_name}")
end

#bridge_exists?(bridge_name) ⇒ Boolean

Returns:

  • (Boolean)


187
188
189
190
191
# File 'lib/vagrant-lxc/driver.rb', line 187

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) ⇒ Boolean

Returns:

  • (Boolean)


182
183
184
185
# File 'lib/vagrant-lxc/driver.rb', line 182

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) ⇒ Boolean

Returns:

  • (Boolean)


193
194
195
196
197
198
# File 'lib/vagrant-lxc/driver.rb', line 193

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_rootfsObject

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



218
219
220
221
222
223
224
225
226
227
228
229
230
231
# File 'lib/vagrant-lxc/driver.rb', line 218

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_pathObject



48
49
50
# File 'lib/vagrant-lxc/driver.rb', line 48

def config_path
  base_path.join('config').to_s
end

#config_stringObject



78
79
80
# 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) ⇒ Object



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/vagrant-lxc/driver.rb', line 136

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_pathObject

Root folder where container configs are stored



36
37
38
# 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 = {}) ⇒ Object



82
83
84
85
86
87
# 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

#destroyObject



120
121
122
# File 'lib/vagrant-lxc/driver.rb', line 120

def destroy
  @cli.destroy
end

#forced_haltObject



115
116
117
118
# File 'lib/vagrant-lxc/driver.rb', line 115

def forced_halt
  @logger.info('Shutting down container...')
  @cli.transition_to(:stopped) { |c| c.stop }
end

#info(*command) ⇒ Object



132
133
134
# File 'lib/vagrant-lxc/driver.rb', line 132

def info(*command)
  @cli.info(*command)
end

#mac_addressObject



70
71
72
73
74
75
76
# 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_customizationsObject



239
240
241
242
243
244
245
# File 'lib/vagrant-lxc/driver.rb', line 239

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) ⇒ Object



200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/vagrant-lxc/driver.rb', line 200

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_pathObject



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# 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) ⇒ Object



95
96
97
98
99
100
# File 'lib/vagrant-lxc/driver.rb', line 95

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) ⇒ Object



89
90
91
92
93
# File 'lib/vagrant-lxc/driver.rb', line 89

def share_folders(folders)
  folders.each do |f|
    share_folder(f[:hostpath], f[:guestpath], f.fetch(:mount_options, nil))
  end
end

#start(customizations) ⇒ Object



102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/vagrant-lxc/driver.rb', line 102

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

#stateObject



233
234
235
236
237
# File 'lib/vagrant-lxc/driver.rb', line 233

def state
  if @container_name
    @cli.state
  end
end

#supports_attach?Boolean

Returns:

  • (Boolean)


124
125
126
# File 'lib/vagrant-lxc/driver.rb', line 124

def supports_attach?
  @cli.supports_attach?
end

#update_config_keysObject



247
248
249
250
251
# File 'lib/vagrant-lxc/driver.rb', line 247

def update_config_keys
  @cli.update_config(config_path)
rescue Errors::ExecuteError
  # not on LXC 2.1+. Doesn't matter, ignore.
end

#validate!Object

Raises:



31
32
33
# File 'lib/vagrant-lxc/driver.rb', line 31

def validate!
  raise ContainerNotFound if @container_name && ! @cli.list.include?(@container_name)
end

#versionObject



213
214
215
# File 'lib/vagrant-lxc/driver.rb', line 213

def version
  @version ||= @cli.version
end