lxc_copy.py 5.38 KB
#!/usr/bin/python
#

# (c) 2014, Pavel Antonov <antonov@adwz.ru>
#
# This file is part of Ansible
#
# This module is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This software is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this software.  If not, see <http://www.gnu.org/licenses/>.

######################################################################

DOCUMENTATION = '''
---
module: lxc_copy
author: Georg Hopp
version_added: "1.9"
short_description: copy files in an lxc
description:
     - Very simple first start to copy files from host to lxc container
       even in non privileged containers
options:
  name:
    description:
       - Lxc container name to use
    required: true
    default: null
    aliases: []
  src:
    description:
       - Path to source file
    required: true
    default: null
    aliases: []
  dest:
    description:
       - Path to file in container
    required: true
    default: null
    aliases: []
requirements: [ "lxc", "create_user_cgroup" ]
'''

EXAMPLES = '''
Copy a file from the host in the container:

- hosts: localhost
  tasks:
  - name: copy host file to container
    lxc_copy:
      name: ansible_test
      src: nicefile
      dest: /tmp/verynicefile
'''

try:
  import lxc
  import sys
  import os
  import json
  import getpass
  import hashlib
  from contextlib import contextmanager
except ImportError, e:
  print "failed=True msg='failed to import python module: %s'" % e
  sys.exit(1)

class LxcManager:
  @contextmanager
  def lxc_started(self):
      started = self.container.running

      if not started and not self.container.running:
        if (os.getuid() != 0):
          uname = getpass.getuser()
          subprocess.call(["create_user_cgroup"]);
          for i in os.listdir('/sys/fs/cgroup'):
            if (i == 'openrc'):
              continue
            with open('/sys/fs/cgroup/'+i+'/'+uname+'/tasks', 'a') as f:
              f.write(str(os.getpid())+'\n');

          self.container.start();

      yield self

      if not started and self.container.running:
        self.container.shutdown(5)

  def __init__(self, module):
    self.module = module
    self.name = self.module.params.get('name')
    self.src  = self.module.params.get('src')
    self.dest = self.module.params.get('dest')
    self.container = lxc.Container(self.name)
    self.changed = False
    self.log = []
    self.error_msg = None
    self.buffer_size = 20480

  def get_log(self, as_string=True):
    return "".join(self.log) if as_string else self.log

  def _verify(self):
    def work(dest):
      digest = ''
      try:
        chk = hashlib.sha1()
        with open(dest, 'r') as dfile:
          data = dfile.read(self.buffer_size)
          while(self.buffer_size == len(data)):
            chk.update(data)
            data = dfile.read(self.buffer_size)
          chk.update(data)
          digest = chk.hexdigest()
      except:
        pass
      sys.stdout.write(digest)
      sys.stdout.flush()

    lxc_read, lxc_write = os.pipe()

    pid = self.container.attach(work, self.dest, stdout=lxc_write)
    os.close(lxc_write)

    chk = hashlib.sha1()
    with open(self.src, 'r') as dfile:
      data = dfile.read(self.buffer_size)
      while (self.buffer_size == len(data)):
        chk.update(data)
        data = dfile.read(self.buffer_size)
      chk.update(data)

    lxc_chk = os.read(lxc_read, 100) # read digest from container....
    os.close(lxc_read)

    self.files_equal   = lxc_chk == chk.hexdigest()
    self.local_sum     = chk.hexdigest()
    self.container_sum = lxc_chk

  def copy(self):
    with self.lxc_started():
      self._verify()
      if not self.files_equal:
        def work(dest):
          infno = sys.stdin.fileno()
          with open(dest, 'w') as outfile:
            data = os.read(infno, self.buffer_size)
            while (self.buffer_size == len(data)):
              outfile.write(data)
              data = os.read(infno, self.buffer_size)
            outfile.write(data)

        lxc_read, lxc_write = os.pipe()

        pid = self.container.attach(work, self.dest, stdin=lxc_read)
        os.close(lxc_read)

        with open(self.src, 'r') as infile:
          data = infile.read(self.buffer_size)
          while (self.buffer_size == len(data)):
            os.write(lxc_write, data)
            data = infile.read(self.buffer_size)
          os.write(lxc_write, data)

        os.close(lxc_write)
        os.waitpid(pid, 0)
        self.changed = True

  def has_changed(self):
    return self.changed


def main():
  module = AnsibleModule(
      argument_spec = dict(
        name = dict(required=True, default=None),
        src  = dict(required=True, default=None),
        dest = dict(required=True, default=None),
        )
      )

  manager = LxcManager(module)

  image_id = None
  msg      = ''
  do_build = False

  manager.copy()

  module.exit_json(failed=False, changed=manager.has_changed(), msg="File copied")

# import module snippets
from ansible.module_utils.basic import *
if __name__ == '__main__':
    main()