update lxc plugin

This commit is contained in:
Jörg Thalheim 2015-04-09 10:30:08 +00:00
parent 9bc1eb9898
commit 5f32464f71
2 changed files with 66 additions and 64 deletions

View File

@ -2,7 +2,6 @@ ansible-lxc
=========== ===========
Ansible Connection Plugin for lxc containers (https://linuxcontainers.org/) Ansible Connection Plugin for lxc containers (https://linuxcontainers.org/)
The plugin will use lxc-attach under the hood to connect to containers
INSTALL INSTALL
======= =======

View File

@ -1,11 +1,10 @@
from __future__ import absolute_import from __future__ import absolute_import
import distutils.spawn import os, sys
import os,sys
import subprocess import subprocess
import shutil import shutil
import traceback import traceback
import re import select
from ansible import errors from ansible import errors
from ansible.callbacks import vvv from ansible.callbacks import vvv
@ -14,38 +13,15 @@ import lxc as _lxc
class Connection(object): class Connection(object):
""" Local lxc based connections """ """ Local lxc based connections """
def _search_executable(self, executable):
cmd = distutils.spawn.find_executable(executable)
if not cmd:
raise errors.AnsibleError("%s command not found in PATH") % executable
return cmd
def _root_fs(self):
rootfs = self.container.get_running_config_item("lxc.rootfs")
# overlayfs use the scheme:
# overlayfs:/var/lib/lxc/LXC-Template-1404/rootfs:/var/lib/lxc/lxc-demo/delta0
match = re.match(r'^overlayfs:.+?rootfs:(.+)', rootfs)
if match:
rootfs = match.group(1)
if not rootfs:
raise errors.AnsibleError("rootfs not set in configuration for %s") % self.host
return rootfs
def __init__(self, runner, host, port, *args, **kwargs): def __init__(self, runner, host, port, *args, **kwargs):
self.has_pipelining = False self.has_pipelining = False
self.host = host self.host = host
# port is unused, since this is local
self.port = port
self.runner = runner self.runner = runner
self.lxc_attach = self._search_executable("lxc-attach")
self.container = _lxc.Container(host) self.container = _lxc.Container(host)
if self.container.state == "STOPPED": if self.container.state == "STOPPED":
raise errors.AnsibleError("%s is not running" % host) raise errors.AnsibleError("%s is not running" % host)
self.rootfs = self._root_fs()
def connect(self, port=None): def connect(self, port=None):
""" connect to the lxc; nothing to do here """ """ connect to the lxc; nothing to do here """
@ -53,59 +29,86 @@ class Connection(object):
return self return self
def _generate_cmd(self, executable, cmd): def exec_command(self, cmd, tmp_path, sudo_user=None, become_user=None, sudoable=False, executable="/bin/sh", in_data=None, su=None, su_user=None):
if executable:
return [self.lxc_attach, "--name", self.host, "--", executable, "-c", cmd]
else:
return "%s --name %s -- %s" % (self.lxc_attach, self.host, cmd)
def exec_command(self, cmd, tmp_path, sudo_user=None, sudoable=False, executable="/bin/sh", in_data=None, su=None, su_user=None):
""" run a command on the chroot """ """ run a command on the chroot """
# We enter lxc as root so sudo stuff can be ignored local_cmd = [cmd]
local_cmd = self._generate_cmd(executable, cmd) if executable:
local_cmd = [executable, "-c"] + local_cmd
if sudo_user:
local_cmd = ["sudo", "-u", sudo_user, "--"] + local_cmd
read_stdout, write_stdout = os.pipe()
read_stderr, write_stderr = os.pipe()
vvv("EXEC %s" % (local_cmd), host=self.host) vvv("EXEC %s" % (local_cmd), host=self.host)
p = subprocess.Popen(local_cmd, shell=isinstance(local_cmd, basestring),
cwd=self.runner.basedir,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
return (p.returncode, "", stdout, stderr)
def _normalize_path(self, path, prefix): pid = self.container.attach(_lxc.attach_run_command, local_cmd,
if not path.startswith(os.path.sep): stdout=write_stdout,
path = os.path.join(os.path.sep, path) stderr=write_stderr)
normpath = os.path.normpath(path) os.close(write_stdout)
return os.path.join(prefix, normpath[1:]) os.close(write_stderr)
fds = [read_stdout, read_stderr]
def _copy(self, in_path, out_path): buf = { read_stdout: [], read_stderr: [] }
if not os.path.exists(in_path): while len(fds) > 0:
raise errors.AnsibleFileNotFound("file or module does not exist: %s" % in_path) ready_fds, _, _ = select.select(fds, [], [])
try: for fd in ready_fds:
shutil.copyfile(in_path, out_path) data = os.read(fd, 32768)
except shutil.Error: if not data:
traceback.print_exc() fds.remove(fd)
raise errors.AnsibleError("failed to copy: %s and %s are the same" % (in_path, out_path)) os.close(fd)
except IOError: buf[fd].append(data)
traceback.print_exc()
raise errors.AnsibleError("failed to transfer file to %s" % out_path) (pid, returncode) = os.waitpid(pid, 0)
stdout = b"".join(buf[read_stdout])
stderr = b"".join(buf[read_stderr])
return (returncode, "", stdout, stderr)
def put_file(self, in_path, out_path): def put_file(self, in_path, out_path):
""" transfer a file from local to lxc """ """ transfer a file from local to lxc """
out_path = self._normalize_path(out_path, self.rootfs)
vvv("PUT %s TO %s" % (in_path, out_path), host=self.host) vvv("PUT %s TO %s" % (in_path, out_path), host=self.host)
self._copy(in_path, out_path) if not os.path.exists(in_path):
raise errors.AnsibleFileNotFound("file or module does not exist: %s" % in_path)
try:
src_file = open(in_path, "rb")
except IOError:
traceback.print_exc()
raise errors.AnsibleError("failed to open file to %s" % in_path)
def write_file(args):
dst_file = open(out_path, 'wb')
shutil.copyfileobj(src_file, dst_file)
try:
self.container.attach_wait(write_file, None)
except IOError:
traceback.print_exc()
raise errors.AnsibleError("failed to transfer file to %s" % out_path)
def fetch_file(self, in_path, out_path): def fetch_file(self, in_path, out_path):
""" fetch a file from lxc to local """ """ fetch a file from lxc to local """
in_path = self._normalize_path(in_path, self.rootfs)
vvv("FETCH %s TO %s" % (in_path, out_path), host=self.host) vvv("FETCH %s TO %s" % (in_path, out_path), host=self.host)
self._copy(in_path, out_path) if not os.path.exists(in_path):
raise errors.AnsibleFileNotFound("file or module does not exist: %s" % in_path)
try:
dst_file = open(out_path, "wb")
except IOError:
traceback.print_exc()
raise errors.AnsibleError("failed to write to file %s" % out_path)
def write_file(args):
src_file = open(in_path, 'rb')
shutil.copyfileobj(src_file, dst_file)
try:
self.container.attach_wait(write_file, None)
except IOError:
traceback.print_exc()
raise errors.AnsibleError("failed to transfer file from %s to %s" % (in_path, out_path))
def close(self): def close(self):
""" terminate the connection; nothing to do here """ """ terminate the connection; nothing to do here """