244 lines
8.9 KiB
Python
244 lines
8.9 KiB
Python
import os
|
|
import grp
|
|
import pwd
|
|
import uuid
|
|
|
|
import lxc
|
|
import tarfile
|
|
|
|
from oslo.config import cfg
|
|
from oslo.utils import importutils, units
|
|
|
|
from nova.i18n import _, _LW, _LE, _LI
|
|
from nova.openstack.common import fileutils
|
|
from nova.openstack.common import log as logging
|
|
from nova import utils
|
|
from nova.virt import images
|
|
from nova import objects
|
|
from nova import exception
|
|
|
|
from . import config
|
|
from . import vif
|
|
|
|
CONF = cfg.CONF
|
|
CONF.import_opt('vif_plugging_timeout', 'nova.virt.driver')
|
|
CONF.import_opt('vif_plugging_is_fatal', 'nova.virt.driver')
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
MAX_CONSOLE_BYTES = 100 * units.Ki
|
|
|
|
def get_container_dir(instance):
|
|
return os.path.join(CONF.lxd.lxd_root_dir, instance, 'rootfs')
|
|
|
|
class Container(object):
|
|
def __init__(self, client, virtapi, firewall):
|
|
self.client = client
|
|
self.virtapi = virtapi
|
|
self.firewall_driver = firewall
|
|
|
|
self.container = None
|
|
self.idmap = LXCUserIdMap()
|
|
self.vif_driver = vif.LXDGenericDriver()
|
|
|
|
self.base_dir = os.path.join(CONF.instances_path,
|
|
CONF.image_cache_subdirectory_name)
|
|
|
|
def init_container(self):
|
|
lxc_cgroup = uuid.uuid4()
|
|
utils.execute('cgm', 'create', 'all', lxc_cgroup,
|
|
run_as_root=True)
|
|
utils.execute('cgm', 'chown', 'all', lxc_cgroup,
|
|
pwd.getpwuid(os.getuid()).pw_uid,
|
|
pwd.getpwuid(os.getuid()).pw_gid,
|
|
run_as_root=True)
|
|
utils.execute('cgm', 'movepid', 'all', lxc_cgroup, os.getpid())
|
|
|
|
def get_console_log(self, instance):
|
|
console_log = os.path.join(CONF.lxd.lxd_root_dir,
|
|
instance['uuid'],
|
|
'console.log')
|
|
with open(console_log, 'rb') as fp:
|
|
log_data, remaining = utils.last_bytes(fp, MAX_CONSOLE_BYTES)
|
|
if remaining > 0:
|
|
LOG.info(_LI('Truncated console log returned, '
|
|
'%d bytes ignored'),
|
|
remaining, instance=instance)
|
|
return log_data
|
|
|
|
def start_container(self, context, instance, image_meta, injected_files,
|
|
admin_password, network_info, block_device_info, flavor):
|
|
LOG.info(_LI('Starting new instance'), instance=instance)
|
|
|
|
instance_name = instance['uuid']
|
|
self.container = lxc.Container(instance['uuid'])
|
|
self.container.set_config_path(CONF.lxd.lxd_root_dir)
|
|
|
|
''' Create the instance directories '''
|
|
self._create_container(instance_name)
|
|
|
|
''' Fetch the image from glance '''
|
|
self._fetch_image(context, instance)
|
|
|
|
''' Start the contianer '''
|
|
self._start_container(context, instance, network_info, image_meta)
|
|
|
|
def _create_container(self, instance):
|
|
if not os.path.exists(get_container_dir(instance)):
|
|
fileutils.ensure_tree(get_container_dir(instance))
|
|
if not os.path.exists(self.base_dir):
|
|
fileutils.ensure_tree(self.base_dir)
|
|
|
|
def _fetch_image(self, context, instance):
|
|
(user, group) = self.idmap.get_user()
|
|
image = os.path.join(self.base_dir, '%s.tar.gz' % instance['image_ref'])
|
|
if not os.path.exists(image):
|
|
images.fetch_to_raw(context, instance['image_ref'], image,
|
|
instance['user_id'], instance['project_id'])
|
|
if not tarfile.is_tarfile(image):
|
|
raise exception.NovaException(_('Not an valid image'))
|
|
|
|
utils.execute('tar', '--directory', get_container_dir(instance['uuid']),
|
|
'--anchored', '--numeric-owner', '-xpzf', image,
|
|
run_as_root=True, check_exit_code=[0, 2])
|
|
utils.execute('chown', '-R', '%s:%s' % (user, group),
|
|
get_container_dir(instance['uuid']), run_as_root=True)
|
|
|
|
def _start_container(self, context, instance, network_info, image_meta):
|
|
with utils.temporary_mutation(context, read_deleted="yes"):
|
|
flavor = objects.Flavor.get_by_id(context,
|
|
instance['instance_type_id'])
|
|
timeout = CONF.vif_plugging_timeout
|
|
# check to see if neutron is ready before
|
|
# doing anything else
|
|
if (not self.client.running(instance['uuid']) and
|
|
utils.is_neutron() and timeout):
|
|
events = self._get_neutron_events(network_info)
|
|
else:
|
|
events = {}
|
|
|
|
try:
|
|
with self.virtapi.wait_for_instance_event(
|
|
instance, events, deadline=timeout,
|
|
error_callback=self._neutron_failed_callback):
|
|
self._write_config(instance, network_info, image_meta, flavor)
|
|
self._start_network(instance, network_info)
|
|
self._start_firewall(instance, network_info)
|
|
self.client.start(instance['uuid'])
|
|
except exception.VirtualInterfaceCreateException:
|
|
LOG.info(_LW('Failed'))
|
|
|
|
def _write_config(self, instance, network_info, image_meta, flavor):
|
|
template = config.LXDConfigTemplate(instance['uuid'], image_meta)
|
|
template.set_config()
|
|
|
|
self.container.load_config()
|
|
|
|
name = config.LXDConfigSetName(self.container, instance['uuid'])
|
|
name.set_config()
|
|
|
|
rootfs = config.LXDConfigSetRoot(self.container, instance['uuid'])
|
|
rootfs.set_config()
|
|
|
|
logpath = config.LXDConfigSetLog(self.container, instance['uuid'])
|
|
logpath.set_config()
|
|
|
|
console_log = config.LXDConfigConsole(self.container, instance['uuid'])
|
|
console_log.set_config()
|
|
|
|
idmap = config.LXDUserConfig(self.container, self.idmap)
|
|
idmap.set_config()
|
|
|
|
limit = config.LXDSetLimits(self.container, instance)
|
|
limit.set_config()
|
|
|
|
self.container.save_config()
|
|
|
|
def _start_network(self, instance, network_info):
|
|
for vif in network_info:
|
|
self.vif_driver.plug(instance, vif)
|
|
|
|
def teardown_network(self, instance, network_info):
|
|
for vif in network_info:
|
|
self.vif_driver.unplug(instancece, vif)
|
|
self._stop_firewall(instance, network_info)
|
|
|
|
def _start_firewall(self, instance, network_info):
|
|
self.firewall_driver.setup_basic_filtering(instance, network_info)
|
|
self.firewall_driver.prepare_instance_filter(instance, network_info)
|
|
self.firewall_driver.apply_instance_filter(instance, network_info)
|
|
|
|
def _stop_firewall(self, instnce, network_inf):
|
|
self.firewall_driver.unfilter_instance(instance, network_info)
|
|
|
|
def _get_neutron_events(self, network_info):
|
|
return [('network-vif-plugged', vif['id'])
|
|
for vif in network_info if vif.get('active', True) is False]
|
|
|
|
def _neutron_failed_callback(self, event_name, instance):
|
|
LOG.error(_LE('Neutron Reported failure on event '
|
|
'%(event)s for instance %(uuid)s'),
|
|
{'event': event_name, 'uuid': instance.uuid})
|
|
if CONF.vif_plugging_is_fatal:
|
|
raise exception.VirtualInterfaceCreateException()
|
|
|
|
|
|
class LXCIdMap(object):
|
|
|
|
def __init__(self, ustart, unum, gstart, gnum):
|
|
self.ustart = int(ustart)
|
|
self.unum = int(unum)
|
|
self.gstart = int(gstart)
|
|
self.gnum = int(gnum)
|
|
|
|
def usernsexec_margs(self, with_read=None):
|
|
if with_read:
|
|
if with_read == "user":
|
|
with_read = os.getuid()
|
|
unum = self.unum - 1
|
|
rflag = ['-m', 'u:%s:%s:1' % (self.ustart + self.unum, with_read)]
|
|
print(
|
|
"================ rflag: %s ==================" %
|
|
(str(rflag)))
|
|
else:
|
|
unum = self.unum
|
|
rflag = []
|
|
|
|
return ['-m', 'u:0:%s:%s' % (self.ustart, unum),
|
|
'-m', 'g:0:%s:%s' % (self.gstart, self.gnum)] + rflag
|
|
|
|
def lxc_conf_lines(self):
|
|
return (('lxc.id_map', 'u 0 %s %s' % (self.ustart, self.unum)),
|
|
('lxc.id_map', 'g 0 %s %s' % (self.gstart, self.gnum)))
|
|
|
|
def get_user(self):
|
|
return (self.ustart, self.gstart)
|
|
|
|
|
|
class LXCUserIdMap(LXCIdMap):
|
|
|
|
def __init__(self, user=None, group=None, subuid_f="/etc/subuid",
|
|
subgid_f="/etc/subgid"):
|
|
if user is None:
|
|
user = pwd.getpwuid(os.getuid())[0]
|
|
if group is None:
|
|
group = grp.getgrgid(os.getgid()).gr_name
|
|
|
|
def parse_sfile(fname, name):
|
|
line = None
|
|
with open(fname, "r") as fp:
|
|
for cline in fp:
|
|
if cline.startswith(name + ":"):
|
|
line = cline
|
|
break
|
|
if line is None:
|
|
raise ValueError("%s not found in %s" % (name, fname))
|
|
toks = line.split(":")
|
|
return (toks[1], toks[2])
|
|
|
|
ustart, unum = parse_sfile(subuid_f, user)
|
|
gstart, gnum = parse_sfile(subgid_f, group)
|
|
|
|
self.user = user
|
|
self.group = group
|
|
super(LXCUserIdMap, self).__init__(ustart, unum, gstart, gnum)
|