diff --git a/SWSCloudNode/lxc/__init__.py b/SWSCloudNode/lxc/__init__.py index 288fc29..2e186af 100644 --- a/SWSCloudNode/lxc/__init__.py +++ b/SWSCloudNode/lxc/__init__.py @@ -1,407 +1,407 @@ -# coding: utf-8 - -import subprocess -import logging -import threading -import select -import pty -import os -import signal - - -class ContainerAlreadyExists(Exception): - pass - - -class ContainerAlreadyRunning(Exception): - pass - - -class ContainerNotExists(Exception): - pass - - -_logger = logging.getLogger("pylxc") -_monitor = None - - -class _LXCMonitor(threading.Thread): - def __init__(self): - threading.Thread.__init__(self) - self._process = None - self._monitors = {} - - def run(self): - master, slave = pty.openpty() - cmd = ['/usr/bin/lxc-monitor', '-n', '.*'] - self._process = subprocess.Popen(cmd, stdout=slave, bufsize=1) - stdout = os.fdopen(master) - while self._process.poll() is None: - ready, _, _ = select.select([stdout], [], [], 0.1) - if ready: - logging.debug("Waiting for state change") - state = stdout.readline() - inf = state.strip().split() - container = inf[0].strip("'") - state = inf[-1].strip('[]') - if container in self._monitors: - logging.debug("State of container '%s' changed to '%s'", container, state) - self._monitors[container](state) - _logger.info("LXC Monitor stopped!") - - def add_monitor(self, name, callback): - self._monitors[name] = callback - - def rm_monitor(self, name): - self._monitors.pop(name) - - def is_monitored(self, name): - return name in self._monitors - - def kill(self): - try: - self._process.terminate() - self._process.wait() - except: - pass - self.join() - - -class lxc(): - def __init__(self): - logging.debug("") - - def list(self, status=None): - """ - :return: ['container_first', 'container_second'] - """ - if status in ['active', 'frozen', 'running', 'stopped', 'nesting']: - path = "--%s" % status - else: - path = "" - - cmd = ['/usr/bin/lxc-ls', path] - out = subprocess.check_output(cmd).splitlines() - # print out - return out - - def exists(self, name): - """ - checks if a given container is defined or not - """ - if name in self.list(): - return True - return False - - def start(self, name, config_file=None): - """ - starts a container in daemon mode - """ - if not self.exists(name): - raise ContainerNotExists("The container (%s) does not exist!" % name) - - if name in self.list("running"): - raise ContainerAlreadyRunning('The container %s is already started!' % name) - - cmd = ['lxc-start', '-n', name, '-d'] - if config_file: - cmd += ['-f', config_file] - - return subprocess.check_call(cmd) - - def stop(self, name): - """ - stops a container - """ - if not self.exists(name): - raise ContainerNotExists("The container (%s) does not exist!" % name) - - cmd = ['/usr/bin/lxc-stop', '-n', name] - - try: - result = subprocess.check_call(cmd) - return True - except Exception as e: - return False - - def destroy(self, name): - """ - removes a container [stops a container if it's running and] - raises ContainerNotExists exception if the specified name is not created - """ - if not self.exists(name): - raise ContainerNotExists("The container (%s) does not exist!" % name) - - # todo: check status. If status not STOPPED - run method self.stop(name) - # todo: add condition - self.stop(name) - - cmd = ['/usr/bin/lxc-destroy', '-f', '-n', name] - - return subprocess.check_call(cmd) - - def info(self, name): - """ - returns info dict about the specified container - """ - # - if not self.exists(name): - raise ContainerNotExists("The container (%s) does not exist!" % name) - # - cmd = ['/usr/bin/lxc-info', '-n', name, "-H"] - out = subprocess.check_output(cmd).splitlines() - clean = [] - info = {} - # - for line in out: - if line not in clean: - clean.append(line) - # - for line in clean: - key, value = line.split(":") - - # strip - key = key.lstrip() - value = value.lstrip() - # - key = key.replace(" ", "_") - - info[key.lower()] = value - - # get container size - info['size'] = self.__get_container_size(name) - return info - - def __get_container_size(self, name): - cmd = ['/usr/bin/du', '--total', '-s', '/var/lib/lxc/%s' % name] - out = subprocess.check_output(cmd).splitlines() - size = 0 - for l in out: - key, value = l.split('\t') - if value == 'total': - size = key - return int(key) - - def freeze(self, name): - """ - freezes the container - """ - if not self.exists(name): - raise ContainerNotExists("The container (%s) does not exist!" % name) - cmd = ['/usr/bin/lxc-freeze', '-n', name] - subprocess.check_call(cmd) - - def unfreeze(self, name): - """ - unfreezes the container - """ - if not self.exists(name): - raise ContainerNotExists("The container (%s) does not exist!" % name) - cmd = ['lxc-unfreeze', '-n', name] - subprocess.check_call(cmd) - - def notify(self, name, states, callback): - """ - executes the callback function with no parameters when the container reaches the specified state or states - states can be or-ed or and-ed - notify('test', 'STOPPED', letmeknow) - - notify('test', 'STOPPED|RUNNING', letmeknow) - """ - if not self.exists(name): - raise ContainerNotExists("The container (%s) does not exist!" % name) - - cmd = ['lxc-wait', '-n', name, '-s', states] - def th(): - subprocess.check_call(cmd) - callback() - _logger.info("Waiting on states %s for container %s", states, name) - threading.Thread(target=th).start() - - def checkconfig(self): - """ - returns the output of lxc-checkconfig - """ - cmd = ['lxc-checkconfig'] - return subprocess.check_output(cmd).replace('[1;32m', '').replace('[1;33m', '').replace('[0;39m', '').replace('[1;32m', '').replace(' ', '').split('\n') - - def create(self, name, config_file=None, template=None, backing_store=None, template_options=None): - """ - Create a new container - raises ContainerAlreadyExists exception if the container name is reserved already. - - :param template_options: Options passed to the specified template - :type template_options: list or None - """ - if self.exists(name): - raise ContainerAlreadyExists("The Container %s is already created!" % name) - - command = list() - command.append("lxc-create -n %s" % name) - - if config_file: - command.append(' -f %s' % config_file) - if template: - command.append(' -t %s' % template) - if backing_store: - command.append(' -B %s' % backing_store) - if template_options: - command.append(' -- %s' % template_options) - - print " ".join(command) - print - # create = subprocess.check_call(command, shell=True) - create = subprocess.check_call(" ".join(command), shell=True) - print - print create - print - - # if create == 0: - # if not self.exists(name): - # _logger.critical("The Container %s doesn't seem to be created! (options: %s)", name, command[3:]) - # raise ContainerNotExists("The container (%s) does not exist!" % name) - # - # _logger.info("Container %s has been created with options %s", name, command[3:]) - # return False - return True - - def reset_password(self, container_name, username, password): - call = [ - 'echo', - '"%s:${PASSWORD:-%s}"' % (username, password), - "|", - "chroot", - "/var/lib/lxc/%s/rootfs/ chpasswd" % container_name - ] - subprocess.check_call(call, shell=True) - # subprocess.call("echo \"ubuntu:${PASSWORD:-%(password)s}\" | chroot /var/lib/lxc/%(hostname)s/rootfs/ chpasswd" % task['parameters'], shell=True) - return True - -# def running(): -# ''' -# returns a list of the currently running containers -# ''' -# return all_as_dict()['Running'] - - -# def stopped(): -# ''' -# returns a list of the stopped containers -# ''' -# return all_as_dict()['Stopped'] - - -# def all_as_dict(): -# ''' -# returns a dict {'Running': ['cont1', 'cont2'], -# 'Stopped': ['cont3', 'cont4'] -# } -# -# ''' -# cmd = ['lxc-ls'] -# out = subprocess.check_output(cmd).splitlines() -# print out -# stopped = [] -# running = [] -# frozen = [] -# current = None -# for c in out: -# c = c.strip() -# if c == 'RUNNING': -# current = running -# continue -# if c == 'STOPPED': -# current = stopped -# continue -# if c == 'FROZEN': -# current = frozen -# continue -# if not len(c): -# continue -# current.append(c) -# return {'Running': running, -# 'Stopped': stopped, -# 'Frozen': frozen} - - -# def all_as_list(): -# ''' -# returns a list of all defined containers -# ''' -# as_dict = all_as_dict() -# containers = as_dict['Running'] + as_dict['Frozen'] + as_dict['Stopped'] -# containers_list = [] -# for i in containers: -# i = i.replace(' (auto)', '') -# containers_list.append(i) -# return containers_list - - -# def kill(name, signal): -# ''' -# sends a kill signal to process 1 of ths container -# :param signal: numeric signal -# ''' -# if not exists(name): -# raise ContainerNotExists("The container (%s) does not exist!" % name) -# cmd = ['lxc-kill', '--name=%s' % name, signal] -# subprocess.check_call(cmd) - - -# def shutdown(name, wait=False, reboot=False): -# ''' -# graceful shutdown sent to the container -# :param wait: should we wait for the shutdown to complete? -# :param reboot: reboot a container, ignores wait -# ''' -# if not exists(name): -# raise ContainerNotExists("The container (%s) does not exist!" % name) -# cmd = ['lxc-shutdown', '-n', name] -# if wait: -# cmd += ['-w'] -# if reboot: -# cmd += ['-r'] -# -# subprocess.check_call(cmd) - - -# def monitor(name, callback): -# ''' -# monitors actions on the specified container, -# callback is a function to be called on -# ''' -# global _monitor -# if not exists(name): -# raise ContainerNotExists("The container (%s) does not exist!" % name) -# if _monitor: -# if _monitor.is_monitored(name): -# raise Exception("You are already monitoring this container (%s)" % name) -# else: -# _monitor = _LXCMonitor() -# logging.info("Starting LXC Monitor") -# _monitor.start() -# def kill_handler(sg, fr): -# stop_monitor() -# signal.signal(signal.SIGTERM, kill_handler) -# signal.signal(signal.SIGINT, kill_handler) -# _monitor.add_monitor(name, callback) - - -# def unmonitor(name): -# if not exists(name): -# raise ContainerNotExists("The container (%s) does not exist!" % name) -# if not _monitor: -# raise Exception("LXC Monitor is not started!") -# if not _monitor.is_monitored(name): -# raise Exception("This container (%s) is not monitored!" % name) -# _monitor.rm_monitor(name) - - -# def stop_monitor(): -# global _monitor -# if _monitor: -# logging.info("Killing LXC Monitor") -# _monitor.kill() -# _monitor = None -# signal.signal(signal.SIGTERM, signal.SIG_DFL) -# signal.signal(signal.SIGINT, signal.SIG_DFL) +# coding: utf-8 + +import subprocess +import logging +import threading +import select +import pty +import os +import signal + + +class ContainerAlreadyExists(Exception): + pass + + +class ContainerAlreadyRunning(Exception): + pass + + +class ContainerNotExists(Exception): + pass + + +_logger = logging.getLogger("pylxc") +_monitor = None + + +class _LXCMonitor(threading.Thread): + def __init__(self): + threading.Thread.__init__(self) + self._process = None + self._monitors = {} + + def run(self): + master, slave = pty.openpty() + cmd = ['/usr/bin/lxc-monitor', '-n', '.*'] + self._process = subprocess.Popen(cmd, stdout=slave, bufsize=1) + stdout = os.fdopen(master) + while self._process.poll() is None: + ready, _, _ = select.select([stdout], [], [], 0.1) + if ready: + logging.debug("Waiting for state change") + state = stdout.readline() + inf = state.strip().split() + container = inf[0].strip("'") + state = inf[-1].strip('[]') + if container in self._monitors: + logging.debug("State of container '%s' changed to '%s'", container, state) + self._monitors[container](state) + _logger.info("LXC Monitor stopped!") + + def add_monitor(self, name, callback): + self._monitors[name] = callback + + def rm_monitor(self, name): + self._monitors.pop(name) + + def is_monitored(self, name): + return name in self._monitors + + def kill(self): + try: + self._process.terminate() + self._process.wait() + except: + pass + self.join() + + +class lxc(): + def __init__(self): + logging.debug("") + + def list(self, status=None): + """ + :return: ['container_first', 'container_second'] + """ + if status in ['active', 'frozen', 'running', 'stopped', 'nesting']: + path = "--%s" % status + else: + path = "" + + cmd = ['/usr/bin/lxc-ls', path] + out = subprocess.check_output(cmd).splitlines() + # print out + return out + + def exists(self, name): + """ + checks if a given container is defined or not + """ + if name in self.list(): + return True + return False + + def start(self, name, config_file=None): + """ + starts a container in daemon mode + """ + if not self.exists(name): + raise ContainerNotExists("The container (%s) does not exist!" % name) + + if name in self.list("running"): + raise ContainerAlreadyRunning('The container %s is already started!' % name) + + cmd = ['lxc-start', '-n', name, '-d'] + if config_file: + cmd += ['-f', config_file] + + return subprocess.check_call(cmd) + + def stop(self, name): + """ + stops a container + """ + if not self.exists(name): + raise ContainerNotExists("The container (%s) does not exist!" % name) + + cmd = ['/usr/bin/lxc-stop', '-n', name] + + try: + result = subprocess.check_call(cmd) + return True + except Exception as e: + return False + + def destroy(self, name): + """ + removes a container [stops a container if it's running and] + raises ContainerNotExists exception if the specified name is not created + """ + if not self.exists(name): + raise ContainerNotExists("The container (%s) does not exist!" % name) + + # todo: check status. If status not STOPPED - run method self.stop(name) + # todo: add condition + self.stop(name) + + cmd = ['/usr/bin/lxc-destroy', '-f', '-n', name] + + return subprocess.check_call(cmd) + + def info(self, name): + """ + returns info dict about the specified container + """ + # + if not self.exists(name): + raise ContainerNotExists("The container (%s) does not exist!" % name) + # + cmd = ['/usr/bin/lxc-info', '-n', name, "-H"] + out = subprocess.check_output(cmd).splitlines() + clean = [] + info = {} + # + for line in out: + if line not in clean: + clean.append(line) + # + for line in clean: + key, value = line.split(":") + + # strip + key = key.lstrip() + value = value.lstrip() + # + key = key.replace(" ", "_") + + info[key.lower()] = value + + # get container size + info['size'] = self.__get_container_size(name) + return info + + def __get_container_size(self, name): + cmd = ['/usr/bin/du', '--total', '-s', '/var/lib/lxc/%s' % name] + out = subprocess.check_output(cmd).splitlines() + size = 0 + for l in out: + key, value = l.split('\t') + if value == 'total': + size = key + return int(key) + + def freeze(self, name): + """ + freezes the container + """ + if not self.exists(name): + raise ContainerNotExists("The container (%s) does not exist!" % name) + cmd = ['/usr/bin/lxc-freeze', '-n', name] + subprocess.check_call(cmd) + + def unfreeze(self, name): + """ + unfreezes the container + """ + if not self.exists(name): + raise ContainerNotExists("The container (%s) does not exist!" % name) + cmd = ['lxc-unfreeze', '-n', name] + subprocess.check_call(cmd) + + def notify(self, name, states, callback): + """ + executes the callback function with no parameters when the container reaches the specified state or states + states can be or-ed or and-ed + notify('test', 'STOPPED', letmeknow) + + notify('test', 'STOPPED|RUNNING', letmeknow) + """ + if not self.exists(name): + raise ContainerNotExists("The container (%s) does not exist!" % name) + + cmd = ['lxc-wait', '-n', name, '-s', states] + def th(): + subprocess.check_call(cmd) + callback() + _logger.info("Waiting on states %s for container %s", states, name) + threading.Thread(target=th).start() + + def checkconfig(self): + """ + returns the output of lxc-checkconfig + """ + cmd = ['lxc-checkconfig'] + return subprocess.check_output(cmd).replace('[1;32m', '').replace('[1;33m', '').replace('[0;39m', '').replace('[1;32m', '').replace(' ', '').split('\n') + + def create(self, name, config_file=None, template=None, backing_store=None, template_options=None): + """ + Create a new container + raises ContainerAlreadyExists exception if the container name is reserved already. + + :param template_options: Options passed to the specified template + :type template_options: list or None + """ + if self.exists(name): + raise ContainerAlreadyExists("The Container %s is already created!" % name) + + command = list() + command.append("lxc-create -n %s" % name) + + if config_file: + command.append(' -f %s' % config_file) + if template: + command.append(' -t %s' % template) + if backing_store: + command.append(' -B %s' % backing_store) + if template_options: + command.append(' -- %s' % template_options) + + print " ".join(command) + print + # create = subprocess.check_call(command, shell=True) + create = subprocess.check_call(" ".join(command), shell=True) + print + print create + print + + # if create == 0: + # if not self.exists(name): + # _logger.critical("The Container %s doesn't seem to be created! (options: %s)", name, command[3:]) + # raise ContainerNotExists("The container (%s) does not exist!" % name) + # + # _logger.info("Container %s has been created with options %s", name, command[3:]) + # return False + return True + + def reset_password(self, container_name, username, password): + call = [ + 'echo', + '"%s:${PASSWORD:-%s}"' % (username, password), + "|", + "chroot", + "/var/lib/lxc/%s/rootfs/ chpasswd" % container_name + ] + subprocess.check_call(call, shell=True) + # subprocess.call("echo \"ubuntu:${PASSWORD:-%(password)s}\" | chroot /var/lib/lxc/%(hostname)s/rootfs/ chpasswd" % task['parameters'], shell=True) + return True + +# def running(): +# ''' +# returns a list of the currently running containers +# ''' +# return all_as_dict()['Running'] + + +# def stopped(): +# ''' +# returns a list of the stopped containers +# ''' +# return all_as_dict()['Stopped'] + + +# def all_as_dict(): +# ''' +# returns a dict {'Running': ['cont1', 'cont2'], +# 'Stopped': ['cont3', 'cont4'] +# } +# +# ''' +# cmd = ['lxc-ls'] +# out = subprocess.check_output(cmd).splitlines() +# print out +# stopped = [] +# running = [] +# frozen = [] +# current = None +# for c in out: +# c = c.strip() +# if c == 'RUNNING': +# current = running +# continue +# if c == 'STOPPED': +# current = stopped +# continue +# if c == 'FROZEN': +# current = frozen +# continue +# if not len(c): +# continue +# current.append(c) +# return {'Running': running, +# 'Stopped': stopped, +# 'Frozen': frozen} + + +# def all_as_list(): +# ''' +# returns a list of all defined containers +# ''' +# as_dict = all_as_dict() +# containers = as_dict['Running'] + as_dict['Frozen'] + as_dict['Stopped'] +# containers_list = [] +# for i in containers: +# i = i.replace(' (auto)', '') +# containers_list.append(i) +# return containers_list + + +# def kill(name, signal): +# ''' +# sends a kill signal to process 1 of ths container +# :param signal: numeric signal +# ''' +# if not exists(name): +# raise ContainerNotExists("The container (%s) does not exist!" % name) +# cmd = ['lxc-kill', '--name=%s' % name, signal] +# subprocess.check_call(cmd) + + +# def shutdown(name, wait=False, reboot=False): +# ''' +# graceful shutdown sent to the container +# :param wait: should we wait for the shutdown to complete? +# :param reboot: reboot a container, ignores wait +# ''' +# if not exists(name): +# raise ContainerNotExists("The container (%s) does not exist!" % name) +# cmd = ['lxc-shutdown', '-n', name] +# if wait: +# cmd += ['-w'] +# if reboot: +# cmd += ['-r'] +# +# subprocess.check_call(cmd) + + +# def monitor(name, callback): +# ''' +# monitors actions on the specified container, +# callback is a function to be called on +# ''' +# global _monitor +# if not exists(name): +# raise ContainerNotExists("The container (%s) does not exist!" % name) +# if _monitor: +# if _monitor.is_monitored(name): +# raise Exception("You are already monitoring this container (%s)" % name) +# else: +# _monitor = _LXCMonitor() +# logging.info("Starting LXC Monitor") +# _monitor.start() +# def kill_handler(sg, fr): +# stop_monitor() +# signal.signal(signal.SIGTERM, kill_handler) +# signal.signal(signal.SIGINT, kill_handler) +# _monitor.add_monitor(name, callback) + + +# def unmonitor(name): +# if not exists(name): +# raise ContainerNotExists("The container (%s) does not exist!" % name) +# if not _monitor: +# raise Exception("LXC Monitor is not started!") +# if not _monitor.is_monitored(name): +# raise Exception("This container (%s) is not monitored!" % name) +# _monitor.rm_monitor(name) + + +# def stop_monitor(): +# global _monitor +# if _monitor: +# logging.info("Killing LXC Monitor") +# _monitor.kill() +# _monitor = None +# signal.signal(signal.SIGTERM, signal.SIG_DFL) +# signal.signal(signal.SIGINT, signal.SIG_DFL)