for creole's zephir2 branch
This commit is contained in:
224
creole/containers.py
Normal file
224
creole/containers.py
Normal file
@ -0,0 +1,224 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
##########################################################################
|
||||
# creole.containers - management of LXC containers
|
||||
# Copyright © 2012,2013 Pôle de compétences EOLE <eole@ac-dijon.fr>
|
||||
#
|
||||
# License CeCILL:
|
||||
# * in french: http://www.cecill.info/licences/Licence_CeCILL_V2-fr.html
|
||||
# * in english http://www.cecill.info/licences/Licence_CeCILL_V2-en.html
|
||||
##########################################################################
|
||||
|
||||
"""Manage LXC containers
|
||||
|
||||
"""
|
||||
|
||||
from .client import CreoleClient, _CONTAINER_COMPONENTS
|
||||
from .config import VIRTENABLED_LOCKFILE, VIRTDISABLED_LOCKFILE
|
||||
from .error import VirtError
|
||||
from .config import templatedir, VIRTROOT
|
||||
from .template import CreoleTemplateEngine
|
||||
from pyeole.process import system_code, system_out, system_progress_out
|
||||
from pyeole.diagnose import test_tcp
|
||||
from .i18n import _
|
||||
|
||||
from distutils.spawn import find_executable
|
||||
from os.path import isdir
|
||||
from os.path import isfile, islink
|
||||
from os.path import ismount
|
||||
from os.path import join
|
||||
from os.path import dirname
|
||||
from os import access
|
||||
from os import F_OK
|
||||
from os import stat
|
||||
from os import symlink
|
||||
from os import makedirs
|
||||
from os import mknod
|
||||
from os import makedev
|
||||
from os import major
|
||||
from os import minor
|
||||
from os import unlink
|
||||
from stat import S_IFBLK
|
||||
from stat import S_ISBLK
|
||||
from hashlib import md5
|
||||
from glob import glob
|
||||
import cjson
|
||||
|
||||
import logging
|
||||
|
||||
client = CreoleClient()
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
_LXC_MD5 = '/etc/eole/lxc.md5'
|
||||
_LXC_LOG = '/var/log/isolation.log'
|
||||
|
||||
_NOT_REALLY_LXC_CONTAINERS = ['root', 'all']
|
||||
"""List of container names that are not to be generated.
|
||||
|
||||
"""
|
||||
|
||||
_LXC_TEMPLATE = {'config': "lxc.config",
|
||||
'fstab': "lxc.fstab",
|
||||
'rootfs/etc/network/interfaces' : "lxc.interfaces",
|
||||
}
|
||||
"""Creole templates for LXC containers.
|
||||
|
||||
"""
|
||||
|
||||
def is_lxc_locked():
|
||||
"""Check if the LXC virtualization is locked.
|
||||
|
||||
The virtualization is locked after first ``instance`` of the
|
||||
server to avoid switching between modes.
|
||||
|
||||
:return: ``enable`` if LXC is enabled, ``disable`` if LXC is
|
||||
disabled or ``None`` where there is no lockfile.
|
||||
|
||||
"""
|
||||
if isfile(VIRTENABLED_LOCKFILE) and isfile(VIRTDISABLED_LOCKFILE):
|
||||
raise VirtError(_(u"Invalid LXC lock files state: both are present."))
|
||||
elif isfile(VIRTENABLED_LOCKFILE):
|
||||
virtlocked = 'enable'
|
||||
elif isfile(VIRTDISABLED_LOCKFILE):
|
||||
virtlocked = 'disable'
|
||||
else:
|
||||
virtlocked = None
|
||||
return virtlocked
|
||||
|
||||
def is_lxc_enabled():
|
||||
"""Check if LXC controller is enabled
|
||||
|
||||
We do not accept to switch between enabled and disabled LXC, after
|
||||
first ``instance``, a lock file is set to check at each
|
||||
``reconfigure``.
|
||||
|
||||
:return: If the LXC container mode is enabled.
|
||||
:rtype: `bool`
|
||||
:raise VirtError: if state in inconsistent between configuration
|
||||
and lock files.
|
||||
|
||||
"""
|
||||
containers_enabled = client.get_creole('mode_conteneur_actif', 'non') == 'oui'
|
||||
if containers_enabled and not find_executable('lxc-info'):
|
||||
raise VirtError(_(u'LXC is enabled but LXC commands not found in PATH.'))
|
||||
|
||||
if containers_enabled and is_lxc_locked() == 'disable':
|
||||
raise VirtError(_(u"Server already instantiated in no containers mode, attempt to activate containers mode aborted."))
|
||||
elif not containers_enabled and is_lxc_locked() == 'enable':
|
||||
raise VirtError(_(u"Server already instantiated in containers mode, attempt to activate no containers mode aborted."))
|
||||
|
||||
return containers_enabled
|
||||
|
||||
def generate_lxc_container(name, logger=None):
|
||||
"""Run creation of a container.
|
||||
|
||||
Check if LXC is enabled and take care of ``root`` and ``all``
|
||||
containers.
|
||||
|
||||
:param name: name of the LXC container
|
||||
:type name: `str`
|
||||
|
||||
"""
|
||||
if name not in _NOT_REALLY_LXC_CONTAINERS:
|
||||
if not test_tcp('localhost', client.get_creole('apt_cacher_port')):
|
||||
raise Exception(_('cacher not available, please start check log in /var/log/apt-cacher-ng/ and restart it with "service apt-cacher-ng start" command'))
|
||||
if isfile(_LXC_LOG):
|
||||
unlink(_LXC_LOG)
|
||||
cmd = ['lxc-create', '-n', name, '-t', 'eole']
|
||||
log.debug('Run: {0}'.format(' '.join(cmd)))
|
||||
code, stdout, stderr = system_progress_out(cmd, _(u"Managing container {0}").format(name), logger)
|
||||
fh = open(_LXC_LOG, 'w')
|
||||
fh.write(stdout)
|
||||
fh.write(stderr)
|
||||
fh.close()
|
||||
if code != 0 and stdout.find(u"'{0}' already exists'".format(name)) >= 0:
|
||||
raise Exception(_('error during the process of container creation, more informations in {0}').format(_LXC_LOG))
|
||||
path_container = client.get_creole('container_path_{0}'.format(name))
|
||||
path_apt_eole_conf = join(path_container, 'etc', 'apt', 'apt-eole.conf')
|
||||
path_apt_eole = join(path_container, 'usr', 'sbin', 'apt-eole')
|
||||
if not isfile(path_apt_eole_conf) or not isfile(path_apt_eole):
|
||||
raise Exception(_('eole-common-pkg not installed in container, something goes wrong, more informations in {0}').format(_LXC_LOG))
|
||||
|
||||
|
||||
def is_lxc_running(container):
|
||||
"""Check if an LXC container is running.
|
||||
|
||||
This check at LXC level and check TCP on port SSH.
|
||||
|
||||
:param container: the container informations
|
||||
:type container: `dict`
|
||||
:return: if the container is running and reachable
|
||||
:rtype: `bool`
|
||||
|
||||
"""
|
||||
|
||||
return is_lxc_started(container) and test_tcp(container[u'ip'], 22)
|
||||
|
||||
|
||||
def is_lxc_started(container):
|
||||
"""Check if an LXC container is started.
|
||||
|
||||
This check at LXC level and check TCP on port SSH.
|
||||
|
||||
:param container: the container informations
|
||||
:type container: `dict`
|
||||
:return: if the container is started
|
||||
:rtype: `bool`
|
||||
|
||||
"""
|
||||
|
||||
if not is_lxc_enabled() or container.get(u'path', None) == '':
|
||||
return True
|
||||
|
||||
if container.get(u'name', None) is None:
|
||||
raise ValueError(_(u"Container has no name"))
|
||||
|
||||
if container.get(u'ip', None) is None:
|
||||
raise ValueError(_(u"Container {0} has no IP").format(container[u'name']))
|
||||
|
||||
cmd = ['lxc-info', '--state', '--name', container[u'name']]
|
||||
code, stdout, stderr = system_out(cmd)
|
||||
|
||||
return stdout.strip().endswith('RUNNING')
|
||||
|
||||
|
||||
def create_mount_point(group):
|
||||
"""Create mount points in LXC.
|
||||
|
||||
This is required for LXC to start.
|
||||
|
||||
"""
|
||||
if 'fstabs' not in group:
|
||||
return
|
||||
for fstab in group['fstabs']:
|
||||
mount_point = fstab.get('mount_point', fstab['name'])
|
||||
full_path = join(group['path'], mount_point.lstrip('/'))
|
||||
if not isdir(full_path):
|
||||
makedirs(full_path)
|
||||
|
||||
|
||||
def lxc_need_restart():
|
||||
def md5sum(file):
|
||||
return md5(open(file).read()).hexdigest()
|
||||
files = ['/etc/lxc/default.conf', '/etc/default/lxc-net']
|
||||
files += glob('/opt/lxc/*/config')
|
||||
files += glob('/opt/lxc/*/fstab')
|
||||
md5s = []
|
||||
for f in files:
|
||||
md5s.append(md5sum(f))
|
||||
if not isfile(_LXC_MD5):
|
||||
ret = True
|
||||
else:
|
||||
try:
|
||||
old_md5s = cjson.decode(open(_LXC_MD5, 'r').read())
|
||||
except cjson.DecodeError:
|
||||
ret = True
|
||||
else:
|
||||
ret = not old_md5s == md5s
|
||||
|
||||
if ret:
|
||||
fh = open(_LXC_MD5, 'w')
|
||||
fh.write(cjson.encode(md5s))
|
||||
fh.close()
|
||||
return ret
|
||||
|
Reference in New Issue
Block a user