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



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

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)


188
189
190
191
192
# 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) ⇒ Boolean

Returns:

  • (Boolean)


183
184
185
186
# 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) ⇒ Boolean

Returns:

  • (Boolean)


194
195
196
197
198
199
# 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_rootfsObject

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



223
224
225
226
227
228
229
230
231
232
233
234
235
236
# 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_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



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
181
# 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_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
88
# 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



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

def destroy
  @cli.destroy
end

#forced_haltObject



116
117
118
119
# 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) ⇒ Object



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

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



244
245
246
247
248
249
250
# 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) ⇒ Object



201
202
203
204
205
206
207
208
209
210
211
212
# 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_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



96
97
98
99
100
101
# 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) ⇒ Object



90
91
92
93
94
# 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) ⇒ Object



103
104
105
106
107
108
109
110
111
112
113
114
# 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

#stateObject



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

def state
  if @container_name
    @cli.state
  end
end

#supports_attach?Boolean

Returns:

  • (Boolean)


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

def supports_attach?
  @cli.supports_attach?
end

#supports_new_config_formatObject



218
219
220
# 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) ⇒ Object



252
253
254
255
256
257
# 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!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



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

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