This commit is contained in:
Vyacheslav Anzhiganov 2016-05-12 03:07:53 +03:00
parent a17bd53183
commit 07de84e764

View file

@ -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 <name>
# :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 <name>
# :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)