vagrant-lxc-ng/lib/vagrant-lxc/driver/cli.rb

167 lines
4.6 KiB
Ruby

require "vagrant/util/retryable"
require "vagrant/util/subprocess"
require "vagrant-lxc/errors"
module Vagrant
module LXC
class Driver
class CLI
attr_accessor :name
class TransitionBlockNotProvided < RuntimeError; end
class TargetStateNotReached < RuntimeError
def initialize(target_state, state)
msg = "Target state '#{target_state}' not reached, currently on '#{state}'"
super(msg)
end
end
# Include this so we can use `Subprocess` more easily.
include Vagrant::Util::Retryable
def initialize(name = nil)
@name = name
@logger = Log4r::Logger.new("vagrant::provider::lxc::container::cli")
end
def list
run(:ls).split(/\s+/).uniq
end
def version
if run(:version) =~ /lxc version:\s+(.+)\s*$/
$1.downcase
else
# TODO: Raise an user friendly error
raise 'Unable to parse lxc version!'
end
end
def state
if @name && run(:info, '--name', @name) =~ /^state:[^A-Z]+([A-Z]+)$/
$1.downcase.to_sym
elsif @name
:unknown
end
end
def create(template, config_file, template_opts = {})
if config_file
config_opts = ['-f', config_file]
end
extra = template_opts.to_a.flatten
extra.unshift '--' unless extra.empty?
run :create,
'--template', template,
'--name', @name,
*(config_opts),
*extra
end
def destroy
run :destroy, '--name', @name
end
def start(overrides = [], extra_opts = [])
options = overrides.map { |key, value| ["-s", "lxc.#{key}=#{value}"] }.flatten
options += extra_opts if extra_opts
run :start, '-d', '--name', @name, *options
end
def stop
run :stop, '--name', @name
end
def shutdown
run :shutdown, '--name', @name
end
def attach(*cmd)
cmd = ['--'] + cmd
if cmd.last.is_a?(Hash)
opts = cmd.pop
namespaces = Array(opts[:namespaces]).map(&:upcase).join('|')
extra = ['--namespaces', namespaces] if namespaces
end
run :attach, '--name', @name, *((extra || []) + cmd)
end
def transition_to(target_state, tries = 30, timeout = 1, &block)
raise TransitionBlockNotProvided unless block_given?
yield self
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
raise TargetStateNotReached.new target_state, last_state
end
end
private
def run(command, *args)
execute('sudo', "lxc-#{command}", *args)
end
# TODO: Review code below this line, it was pretty much a copy and
# paste from VirtualBox base driver and has no tests
def execute(*command, &block)
# Get the options hash if it exists
opts = {}
opts = command.pop if command.last.is_a?(Hash)
tries = 0
tries = 3 if opts[:retryable]
sleep = opts.fetch(:sleep, 1)
# Variable to store our execution result
r = nil
retryable(:on => LXC::Errors::ExecuteError, :tries => tries, :sleep => sleep) do
# Execute the command
r = raw(*command, &block)
# If the command was a failure, then raise an exception that is
# nicely handled by Vagrant.
if r.exit_code != 0
if @interrupted
@logger.info("Exit code != 0, but interrupted. Ignoring.")
else
raise LXC::Errors::ExecuteError, :command => command.inspect
end
end
end
# Return the output, making sure to replace any Windows-style
# newlines with Unix-style.
r.stdout.gsub("\r\n", "\n")
end
def raw(*command, &block)
int_callback = lambda do
@interrupted = true
@logger.info("Interrupted.")
end
# Append in the options for subprocess
command << { :notify => [:stdout, :stderr] }
Vagrant::Util::Busy.busy(int_callback) do
Vagrant::Util::Subprocess.execute(*command, &block)
end
end
end
end
end
end