2013-03-10 22:15:31 +00:00
|
|
|
require "vagrant/util/retryable"
|
|
|
|
require "vagrant/util/subprocess"
|
|
|
|
|
|
|
|
require "vagrant-lxc/errors"
|
|
|
|
|
|
|
|
module Vagrant
|
|
|
|
module LXC
|
2013-04-05 05:17:19 +00:00
|
|
|
class Driver
|
2013-03-10 22:15:31 +00:00
|
|
|
class CLI
|
2013-03-10 23:31:32 +00:00
|
|
|
attr_accessor :name
|
|
|
|
|
|
|
|
class TransitionBlockNotProvided < RuntimeError; end
|
2013-05-07 14:07:35 +00:00
|
|
|
class TargetStateNotReached < RuntimeError
|
|
|
|
def initialize(target_state, state)
|
|
|
|
msg = "Target state '#{target_state}' not reached, currently on '#{state}'"
|
|
|
|
super(msg)
|
|
|
|
end
|
|
|
|
end
|
2013-03-10 23:31:32 +00:00
|
|
|
|
2013-07-28 05:17:05 +00:00
|
|
|
def initialize(sudo_wrapper, name = nil)
|
|
|
|
@sudo_wrapper = sudo_wrapper
|
|
|
|
@name = name
|
|
|
|
@logger = Log4r::Logger.new("vagrant::provider::lxc::container::cli")
|
2013-03-10 22:15:31 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def list
|
2013-03-10 23:31:32 +00:00
|
|
|
run(:ls).split(/\s+/).uniq
|
|
|
|
end
|
|
|
|
|
2013-04-09 01:27:29 +00:00
|
|
|
def version
|
2014-09-23 12:07:03 +00:00
|
|
|
return @version if @version
|
2018-01-14 01:34:41 +00:00
|
|
|
@version = run(:create, '--version')
|
2014-09-23 12:07:03 +00:00
|
|
|
if @version =~ /(lxc version:\s+|)(.+)\s*$/
|
|
|
|
@version = $2.downcase
|
2013-04-09 01:27:29 +00:00
|
|
|
else
|
|
|
|
# TODO: Raise an user friendly error
|
|
|
|
raise 'Unable to parse lxc version!'
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-09-23 12:07:03 +00:00
|
|
|
def config(param)
|
2018-01-14 01:34:41 +00:00
|
|
|
run(:config, param).gsub("\n", '')
|
2014-09-23 12:07:03 +00:00
|
|
|
end
|
|
|
|
|
2018-01-14 02:03:34 +00:00
|
|
|
def update_config(path)
|
|
|
|
run('update-config', '-c', path)
|
|
|
|
end
|
|
|
|
|
2013-03-10 23:31:32 +00:00
|
|
|
def state
|
2013-12-24 09:35:19 +00:00
|
|
|
if @name && run(:info, '--name', @name, retryable: true) =~ /^state:[^A-Z]+([A-Z]+)$/i
|
2013-03-10 23:31:32 +00:00
|
|
|
$1.downcase.to_sym
|
|
|
|
elsif @name
|
|
|
|
:unknown
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-02-18 09:52:54 +00:00
|
|
|
def create(template, backingstore, backingstore_options, config_file, template_opts = {})
|
2013-06-06 03:04:59 +00:00
|
|
|
if config_file
|
|
|
|
config_opts = ['-f', config_file]
|
|
|
|
end
|
|
|
|
|
2018-04-30 02:46:12 +00:00
|
|
|
extra = template_opts.to_a.flatten.reject { |elem| elem.empty? }
|
2013-03-10 23:31:32 +00:00
|
|
|
extra.unshift '--' unless extra.empty?
|
|
|
|
|
|
|
|
run :create,
|
2014-02-18 09:52:54 +00:00
|
|
|
'-B', backingstore,
|
2013-03-10 23:31:32 +00:00
|
|
|
'--template', template,
|
|
|
|
'--name', @name,
|
2015-01-14 23:07:35 +00:00
|
|
|
*(backingstore_options.to_a.flatten),
|
2013-06-06 03:04:59 +00:00
|
|
|
*(config_opts),
|
2013-04-05 05:28:25 +00:00
|
|
|
*extra
|
2014-02-02 21:27:08 +00:00
|
|
|
rescue Errors::ExecuteError => e
|
|
|
|
if e.stderr =~ /already exists/i
|
|
|
|
raise Errors::ContainerAlreadyExists, name: @name
|
|
|
|
else
|
|
|
|
raise
|
|
|
|
end
|
2013-03-10 23:31:32 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def destroy
|
|
|
|
run :destroy, '--name', @name
|
|
|
|
end
|
|
|
|
|
2013-08-01 01:35:49 +00:00
|
|
|
def start(options = [])
|
|
|
|
run :start, '-d', '--name', @name, *Array(options)
|
2013-03-10 23:31:32 +00:00
|
|
|
end
|
|
|
|
|
2016-02-16 11:13:53 +00:00
|
|
|
## lxc-stop will exit 2 if machine was already stopped
|
|
|
|
# Man Page:
|
|
|
|
# 2 The specified container exists but was not running.
|
2013-05-07 14:07:35 +00:00
|
|
|
def stop
|
2016-02-16 11:13:53 +00:00
|
|
|
begin
|
|
|
|
run :stop, '--name', @name
|
|
|
|
rescue LXC::Errors::ExecuteError => e
|
|
|
|
if e.exitcode == 2
|
2018-07-24 15:15:20 +00:00
|
|
|
@logger.debug "Machine already stopped, lxc-stop returned 2"
|
2016-02-16 11:13:53 +00:00
|
|
|
else
|
2018-07-24 15:15:20 +00:00
|
|
|
raise e
|
2016-02-16 11:13:53 +00:00
|
|
|
end
|
|
|
|
end
|
2013-05-07 14:07:35 +00:00
|
|
|
end
|
|
|
|
|
2013-03-19 04:25:27 +00:00
|
|
|
def attach(*cmd)
|
|
|
|
cmd = ['--'] + cmd
|
|
|
|
|
|
|
|
if cmd.last.is_a?(Hash)
|
|
|
|
opts = cmd.pop
|
|
|
|
namespaces = Array(opts[:namespaces]).map(&:upcase).join('|')
|
2013-09-29 01:37:31 +00:00
|
|
|
|
2014-07-25 00:45:04 +00:00
|
|
|
# HACK: The wrapper script should be able to handle this
|
|
|
|
if @sudo_wrapper.wrapper_path
|
|
|
|
namespaces = "'#{namespaces}'"
|
|
|
|
end
|
|
|
|
|
2013-10-03 16:26:04 +00:00
|
|
|
if namespaces
|
2017-12-10 18:11:22 +00:00
|
|
|
extra = ['--namespaces', namespaces]
|
2013-09-29 01:37:31 +00:00
|
|
|
end
|
2013-03-19 04:25:27 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
run :attach, '--name', @name, *((extra || []) + cmd)
|
|
|
|
end
|
|
|
|
|
2017-12-10 18:11:22 +00:00
|
|
|
def info(*cmd)
|
|
|
|
run(:info, '--name', @name, *cmd)
|
|
|
|
end
|
|
|
|
|
2013-05-07 14:07:35 +00:00
|
|
|
def transition_to(target_state, tries = 30, timeout = 1, &block)
|
2013-03-10 23:31:32 +00:00
|
|
|
raise TransitionBlockNotProvided unless block_given?
|
|
|
|
|
|
|
|
yield self
|
|
|
|
|
2013-05-07 14:07:35 +00:00
|
|
|
while (last_state = self.state) != target_state && tries > 0
|
|
|
|
@logger.debug "Target state '#{target_state}' not reached, currently on '#{last_state}'"
|
|
|
|
sleep timeout
|
|
|
|
tries -= 1
|
|
|
|
end
|
|
|
|
|
|
|
|
unless last_state == target_state
|
2013-09-21 04:32:15 +00:00
|
|
|
# TODO: Raise an user friendly message
|
2013-05-07 14:07:35 +00:00
|
|
|
raise TargetStateNotReached.new target_state, last_state
|
|
|
|
end
|
2013-03-10 22:15:31 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
2013-03-10 23:31:32 +00:00
|
|
|
def run(command, *args)
|
2013-07-28 05:17:05 +00:00
|
|
|
@sudo_wrapper.run("lxc-#{command}", *args)
|
2013-03-10 22:15:31 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|