Commit 3db67436 authored by Antoine Catton's avatar Antoine Catton

Merge branch 'lxc'

parents 3ab6bba4 a8b082be
......@@ -74,6 +74,7 @@ setup(name=name,
'pubsubnotifier = slapos.pubsub.notifier:main',
'slaprunnertest = slapos.runner.testRunner:run',
'zodbpack = slapos.zodbpack:run [zodbpack]',
'slapcontainer = slapos.container:main',
]
},
)
# -*- coding: utf-8 -*-
import ConfigParser
import argparse
import gdbm
import sys
import os
import logging
from . import process
def main():
parser = argparse.ArgumentParser(description="Slapcontainer binary")
parser.add_argument('configuration_file', type=str,
help="SlapOS configuration file.")
parser.add_argument('database', type=str,
help='slapcontainer database')
log_lvls = [lvl for lvl in logging._levelNames.keys()
if isinstance(lvl, basestring)]
parser.add_argument('--log', nargs=1, default=['INFO'],
choices=log_lvls,
metavar='lvl', help='Log level')
parser.add_argument('--pid', nargs=1, help='pid file path')
args = parser.parse_args()
if args.pid is not None:
pid_filename = args.pid[0]
if os.path.exists(pid_filename):
print >> sys.stderr, "Already running"
return 127
with open(pid_filename, 'w') as pid_file:
pid_file.write(str(os.getpid()))
try:
run(args)
finally:
os.remove(pid_filename)
else:
run(args)
def run(args):
slapos_conf = ConfigParser.ConfigParser()
slapos_conf.read(args.configuration_file)
current_binary = os.path.join(os.getcwd(), sys.argv[0])
binary_directory = os.path.dirname(current_binary)
sr_directory = os.path.realpath(os.path.join(binary_directory, '..'))
partition_amount = slapos_conf.getint('slapformat', 'partition_amount')
partition_base_name = slapos_conf.get('slapformat', 'partition_base_name')
bridge_name = slapos_conf.get('slapformat', 'interface_name')
instance_root = slapos_conf.get('slapos', 'instance_root')
partition_base_path = os.path.join(instance_root, partition_base_name)
partition_list = ['%s%d' % (partition_base_path, i)
for i in range(partition_amount)]
logging.basicConfig(level=logging.getLevelName(args.log[0]))
database = gdbm.open(args.database, 'c', 0600)
try:
process.main(sr_directory, partition_list, database, bridge_name)
finally:
database.sync()
database.close()
# -*- coding: utf-8 -*-
import os
import subprocess
import logging
import signal
class SlapContainerError(Exception):
"""This exception is thrown, if there is
any failure during slapcontainer preparation,
starting or stopping process"""
def main(sr_directory, partition_list, database, bridge_name):
logger = logging.getLogger('process')
######################
# Process partitions #
######################
start_requested = set()
logger.debug('Processing partitions...')
for partition_path in partition_list:
partition_logger = logger.getChild(
os.path.basename(partition_path)
)
partition_logger.debug('Processing...')
# XXX: Hardcoded path
slapcontainer_filename = os.path.join(partition_path,
'.slapcontainername')
if os.path.isfile(slapcontainer_filename):
partition_logger.debug('Container found...')
with open(slapcontainer_filename, 'r') as slapcontainer_file:
name = slapcontainer_file.read().strip()
# XXX: Hardcoded path
lxc_conf_path = os.path.join(partition_path,
'etc/lxc.conf')
# XXX: Avoid hacking slapos.core
##########################################################
magic_string = '!!BRIDGE_NAME!!'
with open(lxc_conf_path, 'r') as lxc_conf_file:
lxc_conf_content = lxc_conf_file.read()
if magic_string in lxc_conf_content:
with open(lxc_conf_path, 'w') as lxc_conf_file:
lxc_conf_file.write(
lxc_conf_content.replace(magic_string, bridge_name)
)
##########################################################
with open(lxc_conf_path, 'r') as lxc_conf_file:
requested_status = lxc_conf_file.readline().strip(' \n\r\t#')
if requested_status == 'started':
start_requested.add(name)
process_partition(requested_status=requested_status,
sr_directory=sr_directory,
partition_path=partition_path,
name=name,
database=database,
logger=partition_logger,
lxc_conf_filename=lxc_conf_path)
if start_requested:
logger.debug('Container which start was requested : %s.',
', '.join(start_requested))
####################################
# Stop unwanted running containers #
####################################
try:
active_containers = set((container.strip() for container in call(
[os.path.join(sr_directory, 'parts/lxc/bin/lxc-ls'),
'--active']
).split('\n') if container.strip()))
logger.debug('Active containers are %s.', ', '.join(active_containers))
except SlapContainerError:
active_containers = set()
### Stop containers
to_stop = active_containers - start_requested
if to_stop:
logger.debug('Stopping containers %s.', ', '.join(to_stop))
else:
logger.debug('No extra containers to stop.')
for container in to_stop:
try:
logger.info('Stopping container %s.', container)
call(
[os.path.join(sr_directory, 'parts/lxc/bin/lxc-stop'),
'-n', container
]
)
except SlapContainerError:
logger.fatal('Impossible to stop %s.', container)
### Stop shellinaboxes
active_shellinabox = set(database.keys())
to_stop = active_shellinabox - start_requested
if to_stop:
logger.debug('Stopping shellinaboxes %s.', ', '.join(to_stop))
else:
logger.debug('No extra shellinabox to stop.')
for shellinabox in to_stop:
pid = int(database[shellinabox])
try:
os.kill(pid, signal.SIGTERM)
except OSError:
# Shellinabox's already stopped
del database[shellinabox]
else:
# Stopping shellinabox
if not is_pid_running(pid):
del database[shellinabox]
def process_partition(requested_status,
sr_directory,
partition_path,
name,
database,
logger,
lxc_conf_filename):
if requested_status == 'started':
##############################
# Stateless container launch #
##############################
logger.debug('Check status.')
lxc_info = call([os.path.join(sr_directory, 'parts/lxc/bin/lxc-info'),
'-n', name])
### Check if container is launched
if 'RUNNING' in lxc_info:
current_status = 'started'
else:
current_status = 'stopped'
### If container is not launch, launch it
if requested_status != current_status:
logger.debug('Start lxc.')
lxc_start = os.path.join(sr_directory,
'parts/lxc/bin/lxc-start')
call([lxc_start, '-f', lxc_conf_filename,
'-n', name,
'-d'])
################################
# Stateless shellinabox launch #
################################
current_status = 'stopped'
# Check if shellinabox is started
if name in database:
pid = int(database[name])
if is_pid_running(pid):
current_status = 'started'
if current_status == 'stopped':
logger.debug('Start shellinabox.')
shellinabox_pid = call_daemonize([os.path.join(partition_path,
'bin/shellinaboxd')])
database[name] = str(shellinabox_pid)
def is_pid_running(pid):
logger = logging.getLogger('pid')
logger.debug('Check if pid %d is running.', pid)
# XXX: Magic number 0 for no special signal
try:
os.kill(pid, 0)
logger.debug('%d is running.', pid)
return True
except OSError:
# Process doesn't exists
logger.debug('%d is not running.', pid)
return False
def call(command_line, override_environ={}):
logger = logging.getLogger('commandline')
logger.debug('Call %s', ' '.join(command_line))
environ = dict(os.environ)
environ.update(override_environ)
process = subprocess.Popen(command_line, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, env=environ)
process.stdin.flush()
process.stdin.close()
if process.wait() != 0:
logger.debug('Failed')
raise SlapContainerError("Subprocess call failed")
out = process.stdout.read()
logger.debug('Output : %s.', out)
return out
def call_daemonize(command_line, override_environ={}):
logger = logging.getLogger('daemon')
environ = dict(os.environ)
environ.update(override_environ)
daemon = subprocess.Popen(command_line, env=environ)
logger.debug('Daemonize as pid %d : %s', daemon.pid,
' '.join(command_line))
return daemon.pid
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment