Signed-off-by: Chuck Short <chuck.short@canonical.com>
This commit is contained in:
Chuck Short 2015-06-12 11:29:12 -04:00
parent e3eb861caa
commit 5e1ab7f58d
18 changed files with 1146 additions and 1578 deletions

View File

@ -20,8 +20,8 @@ function configure_lxd {
install_package python-software-properties
apt_get update
sudo apt-add-repository -y ppa:ubuntu-lxc/lxc-git-master
sudo apt-add-repository -y ppa:ubuntu-lxc/lxd-git-master
# sudo apt-add-repository -y ppa:ubuntu-lxc/lxc-git-master
# sudo apt-add-repository -y ppa:ubuntu-lxc/lxd-git-master
apt_get update
install_package lxd lxc-dev lxd
}

View File

@ -1,346 +0,0 @@
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# Copyright (c) 2010 Citrix Systems, Inc.
# Copyright 2011 Justin Santa Barbara
# Copyright 2015 Canonical Ltd
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import os
import pwd
from oslo_config import cfg
from oslo_log import log as logging
from oslo_utils import excutils
from oslo_utils import units
from nova.i18n import _, _LE, _LI, _LW
from nova.compute import power_state
from nova import exception
from nova import utils
import image
import profile
import vif
import container_utils
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
LXD_POWER_STATES = {
'RUNNING': power_state.RUNNING,
'STOPPED': power_state.SHUTDOWN,
'STARTING': power_state.NOSTATE,
'STOPPING': power_state.SHUTDOWN,
'ABORTING': power_state.CRASHED,
'FREEZING': power_state.PAUSED,
'FROZEN': power_state.SUSPENDED,
'THAWED': power_state.PAUSED,
'PENDING': power_state.NOSTATE,
'UNKNOWN': power_state.NOSTATE
}
class Container(object):
def __init__(self, lxd, virtapi, firewall):
self.lxd = lxd
self.virtapi = virtapi
self.firewall_driver = firewall
self.image_driver = image.load_driver(CONF.lxd.lxd_image_type,
self.lxd)
self.profile = profile.LXDProfile(self.lxd)
self.vif_driver = vif.LXDGenericDriver()
def container_rebuild(self, context, instance, image_meta, injected_files,
admin_password, bdms, detach_block_devices,
attach_block_devices, network_info, recreate,
block_device_info,
preserve_ephemeral):
raise NotImplemented()
def container_start(self, context, instance, image_meta, injected_files,
admin_password, network_info, block_device_info):
try:
LOG.info(_LI('Starting container'), instance=instance)
if self.lxd.container_defined(instance.uuid):
raise exception.InstanceExists(name=instance.uuid)
self.image_driver.setup_container(context, instance, image_meta)
self.profile.profile_create(instance, network_info)
self._setup_container(instance)
self._start_container(instance, network_info)
except Exception:
with excutils.save_and_reraise_exception():
self.container_destroy(context, instance, network_info,
block_device_info, destroy_disks=None,
migrate_data=None)
def container_destroy(self, context, instance, network_info,
block_device_info, destroy_disks, migrate_data):
LOG.info(_LI('Destroying container'))
try:
if not self.lxd.container_defined(instance.uuid):
return
self.lxd.container_destroy(instance.uuid)
self.container_cleanup(context, instance, network_info,
block_device_info, destroy_disks=None,
migrate_data=None)
except Exception as ex:
with excutils.save_and_reraise_exception():
LOG.exception(_LE('Unable to destroy instance: %s ') % ex)
def container_reboot(self, context, instance, network_info, reboot_type,
block_device_info=None, bad_volumes_callback=None):
try:
if not self.lxd.container_defined(instance.uuid):
msg = _('Container does not exist')
raise exception.NovaException(msg)
return self.lxd.container_reboot(instance.uuid, 20)
except Exception as ex:
with excutils.save_and_reraise_exception():
LOG.exception(_LE('Unable to destroy instance: %s ') % ex)
def get_console_output(self, context, instance):
try:
if not self.lxd.container_defined(instance.uuid):
msg = _('Container does not exist')
raise exception.NovaException(msg)
console_log = container_utils.get_console_path(instance)
uid = pwd.getpwuid(os.getuid()).pw_uid
utils.execute('chown', '%s:%s' % (uid, uid),
console_log, run_as_root=True)
utils.execute('chmod', '755',
container_utils.get_container_dir(instance),
run_as_root=True)
with open(console_log , 'rb') as fp:
log_data, remaning = utils.last_bytes(fp,
MAX_CONSOLE_BYTES)
return log_data
except Exception as ex:
LOG.exception(_LE('Failed container: %s') % ex)
return ""
def container_cleanup(self, context, instance, network_info,
block_device_info, destroy_disks, migrate_data,
destroy_vifs=True):
LOG.info(_LI('Cleaning up container'))
try:
self.profile.profile_delete(instance)
self.unplug_vifs(instance, network_info)
except Exception as ex:
with excutils.save_and_reraise_exception():
LOG.exception(_LE('Unable to clean up instance: %s') % ex)
def container_state(self, instance):
try:
container_state = self.lxd.container_state(instance.uuid)
state = LXD_POWER_STATES[container_state]
except Exception:
state = power_state.NOSTATE
return state
def container_pause(self, instance):
raise NotImplementedError()
def container_unpause(self, instance):
raise NotImplementedError()
def container_suspend(self, context, instance):
try:
if not self.lxd.container_defined(instance.uuid):
msg = _("Container is not defined")
raise exception.NovaException(msg)
self.lxd.container_suspend(instance.uuid, 20)
except Exception:
with excutils.save_and_reraise_exception():
LOG.exception(_LE("Unable to suspend container"))
def container_resume(self, context, instance, network_info,
block_device_info=None):
try:
if not self.lxd.container_defined(instance.uuid):
msg = _('Container does not exist.')
raise exception.NovaException(msg)
self.lxd.container_resume(instance.uuid, 20)
except Exception:
with excutils.save_and_reraise_exception():
LOG.exception(_LE("Unable to resume container"))
def container_rescue(self, context, instance, network_info, image_meta,
rescue_password):
raise NotImplementedError()
def container_unrescue(self, instance, network_info):
raise NotImplementedError()
def container_power_off(self, instance, timeout=0, retry_interval=0):
try:
if not self.lxd.container_defined(instance.uuid):
msg = _('Container is not defined')
raise exception.NovaException(msg)
self.lxd.container_stop(instance.uuid, 20)
except Exception:
with excutils.save_and_reraise_exception():
LOG.execption(_LE("Unable to power off container"))
raise NotImplementedError()
def container_power_on(self, context, instance, network_info,
block_device_info):
try:
if not self.lxd.container_defined(instance.uuid):
msg = _('Container is not defined')
raise exception.NovaException(msg)
self.lxd.container_start(instance.uuid, 20)
except Exception:
with excutils.save_and_reraise_exception():
LOG.exception(_LE("Unable to power on conatainer"))
def container_soft_delete(self, instance):
raise NotImplementedError()
def container_restore(self, instance):
raise NotImplementedError()
def container_get_resource(self, nodename):
raise NotImplementedError()
def container_inject_file(self, instance, b64_path, b64_contents):
raise NotImplementedError()
def container_inject_network_info(self, instance, nw_info):
pass
def container_poll_rebooting_instances(self, timeout, instances):
raise NotImplementedError()
def container_attach_interface(self, instance, image_meta, vif):
raise NotImplementedError()
def container_detach_interface(self, instance, vif):
raise NotImplementedError()
def container_snapshot(self, context, instance, image_id,
update_task_state):
raise NotImplementedError()
def post_interrupted_snapshot_cleanup(self, context, instance):
pass
def container_quiesce(self, context, instance, image_meta):
raise NotImplementedError()
def container_unquiesce(self, context, instance, image_meta):
raise NotImplementedError()
def _setup_container(self, instance):
LOG.debug('Setting up container')
if not os.path.exists(
container_utils.get_container_image(instance)):
msg = _('Container image doesnt exist.')
raise exception.NovaException(msg)
if instance.uuid:
container = {}
container['name'] = instance.uuid
container['profiles'] = ['%s' % instance.uuid]
container['source'] = {
'type': 'image',
'alias': instance.image_ref
}
(state, data) = self.lxd.container_init(container)
self._wait_for_container(data.get('operation').split('/')[3])
def _start_container(self, instance, network_info):
timeout = CONF.vif_plugging_timeout
# check to see if neutron is ready before
# doing anything else
if (not self.lxd.container_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.plug_vifs(instance, network_info)
except exception.VirtualInterfaceCreateException:
LOG.info(_LW('Failed to connect networking to instance'))
(state, data) = self.lxd.container_start(instance.uuid, 20)
self._wait_for_container(data.get('operation').split('/')[3])
def _destroy_container(self, context, instance, network_info,
block_device_info,
destroy_disks, migrate_data):
if self.lxd.container_defined(instance.uuid):
msg = _('Unable to find container')
raise exception.NovaException(msg)
self.lxd.container_destroy(instance.uuid)
def plug_vifs(self, instance, network_info):
for _vif in network_info:
self.vif_driver.plug(instance, _vif)
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 unplug_vifs(self, instance, network_info):
for _vif in network_info:
self.vif_driver.unplug(instance, _vif)
self.firewall_driver.unfilter_instance(instance, network_info)
def _wait_for_container(self, oid):
if not oid:
msg = _('Unable to determine container operation')
raise exception.NovaException(msg)
if not self.lxd.wait_container_operation(oid, 200, 20):
msg = _('Container creation timed out')
raise exception.NovaException(msg)
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()

View File

@ -0,0 +1,31 @@
import sys
from oslo_config import cfg
from oslo_log import log as logging
from pylxd import api
from nova.i18n import _, _LE, _LI
from nova import exception
from nova import utils
from nova.virt import driver
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
class LXDOperations(object):
def __init__(self, virtapi):
self.virtapi = virtapi
self.lxd = api.API()
def container_init_host(self, hostname):
""" Make sure that the LXD daemon is starting
before trying to run a container
"""
try:
self.lxd.host_ping()
except Exception as ex:
msg = _('Unable to connect to LXD host')
raise exception.NovaException(msg)

View File

@ -1,63 +1,13 @@
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# Copyright (c) 2010 Citrix Systems, Inc.
# Copyright 2011 Justin Santa Barbara
# Copyright 2015 Canonical Ltd
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import os
import sys
from oslo_config import cfg
from oslo_log import log as logging
from nova.i18n import _LE
from nova.virt import images
from nova.i18n import _, _LE, _LI
from nova import exception
from nova import utils
from nova.virt import driver
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
def get_base_dir():
return os.path.join(CONF.instances_path,
CONF.image_cache_subdirectory_name)
def get_container_image(instance):
base_dir = get_base_dir()
return os.path.join(base_dir,
'%s.tar.gz' % instance.image_ref)
def fetch_image(context, image, instance, max_size=0):
try:
images.fetch(context, instance.image_ref, image,
instance.user_id, instance.project_id,
max_size=max_size)
except Exception:
LOG.exception(_LE("Image %(image_id)s doesn't exist anymore on"),
{'image_id': instance.image_ref})
def get_console_path(instance):
return os.path.join(CONF.lxd.lxd_root_dir,
'lxc',
instance.uuid,
'console.log')
def get_container_dir(instance):
return os.path.join(CONF.lxd.lxd_root_dir,
'lxc',
instance.uuid)
LOG = logging.getLogger(__name__)

File diff suppressed because it is too large Load Diff

View File

@ -1,158 +0,0 @@
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# Copyright (c) 2010 Citrix Systems, Inc.
# Copyright 2011 Justin Santa Barbara
# Copyright 2015 Canonical Ltd
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import os
import platform
from oslo_config import cfg
from oslo_log import log as logging
from oslo_serialization import jsonutils
from oslo_utils import units
from nova.compute import arch
from nova.compute import hv_type
from nova.compute import utils as compute_utils
from nova.compute import vm_mode
from nova.i18n import _LW
from nova import utils
from cpuinfo import cpuinfo
import psutil
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
class Host(object):
def __init__(self, lxd):
self.lxd = lxd
self.host_cpu_info = cpuinfo.get_cpu_info()
def get_available_resource(self, nodename):
local_cpu_info = self._get_cpu_info()
cpu_topology = local_cpu_info['topology']
vcpus = (cpu_topology['cores'] *
cpu_topology['sockets'] *
cpu_topology['threads'])
local_memory_info = self._get_memory_mb_usage()
local_disk_info = self._get_fs_info(CONF.lxd.lxd_root_dir)
data = {
'vcpus': vcpus,
'memory_mb': local_memory_info['total'] / units.Mi,
'memory_mb_used': local_memory_info['used'] / units.Mi,
'local_gb': local_disk_info['total'] / units.Gi,
'local_gb_used': local_disk_info['used'] / units.Gi,
'vcpus_used': 0,
'hypervisor_type': 'lxd',
'hypervisor_version': 1,
'hypervisor_hostname': platform.node(),
'supported_instances': jsonutils.dumps(
[(arch.I686, hv_type.LXC, vm_mode.EXE),
(arch.X86_64, hv_type.LXC, vm_mode.EXE)]),
'numa_topology': None,
}
return data
def get_host_ip_addr(self):
ips = compute_utils.get_machine_ips()
if CONF.my_ip not in ips:
LOG.warn(_LW('my_ip address (%(my_ip)s) was not found on '
'any of the interfaces: %(ifaces)s'),
{'my_ip': CONF.my_ip, 'ifaces': ", ".join(ips)})
return CONF.my_ip
def get_host_uptime(self):
out, err = utils.execute('env', 'LANG=C', 'uptime')
return out
def _get_fs_info(self, path):
"""get free/used/total space info for a filesystem
:param path: Any dirent on the filesystem
:returns: A dict containing
:free: How much space is free (in bytes)
:used: How much space is used (in bytes)
:total: How big the filesytem is (in bytes)
"""
hddinfo = os.statvfs(path)
total = hddinfo.f_blocks * hddinfo.f_bsize
available = hddinfo.f_bavail * hddinfo.f_bsize
used = total - available
return {'total': total,
'available': available,
'used': used}
def _get_memory_mb_usage(self):
"""Get the used memory size(MB) of the host.
"returns: the total usage of memory(MB)
"""
with open('/proc/meminfo') as fp:
m = fp.read().split()
idx1 = m.index('MemTotal:')
idx2 = m.index('MemFree:')
idx3 = m.index('Buffers:')
idx4 = m.index('Cached:')
total = int(m[idx1 + 1])
avail = int(m[idx2 + 1]) + int(m[idx3 + 1]) + int(m[idx4 + 1])
return {
'total': total * 1024,
'used': (total - avail) * 1024
}
def _get_cpu_info(self):
cpu_info = dict()
cpu_info['arch'] = platform.uname()[5]
cpu_info['model'] = self.host_cpu_info['brand']
cpu_info['vendor'] = self.host_cpu_info['vendor_id']
topology = dict()
topology['sockets'] = self._get_cpu_sockets()
topology['cores'] = self._get_cpu_cores()
topology['threads'] = 1 # fixme
cpu_info['topology'] = topology
cpu_info['features'] = self.host_cpu_info['flags']
return cpu_info
def _get_cpu_cores(self):
try:
return psutil.cpu_count()
except Exception:
return psutil.NUM_CPUS
def _get_cpu_sockets(self):
try:
return psutil.cpu_count(Logical=False)
except Exception:
return psutil.NUM_CPUS
def get_host_cpu_stats(self):
return {
'kernel': long(psutil.cpu_times()[2]),
'idle': long(psutil.cpu_times()[3]),
'user': long(psutil.cpu_times()[0]),
'iowait': long(psutil.cpu_times()[4]),
'frequency': self.host_cpu_info['hz_advertised']
}

View File

@ -1,56 +0,0 @@
# Copyright (c) 2015 Canonical Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import os
def get_fs_info(path):
"""get free/used/total space info for a filesystem
:param path: Any dirent on the filesystem
:returns: A dict containing
:free: How much space is free (in bytes)
:used: How much space is used (in bytes)
:total: How big the filesytem is (in bytes)
"""
hddinfo = os.statvfs(path)
total = hddinfo.f_blocks * hddinfo.f_bsize
available = hddinfo.f_bavail * hddinfo.f_bsize
used = total - available
return {'total': total,
'available': available,
'used': used}
def get_memory_mb_usage():
"""Get the used memory size(MB) of the host.
"returns: the total usage of memory(MB)
"""
with open('/proc/meminfo') as fp:
m = fp.read().split()
idx1 = m.index('MemTotal:')
idx2 = m.index('MemFree:')
idx3 = m.index('Buffers:')
idx4 = m.index('Cached:')
total = int(m[idx1 + 1])
avail = int(m[idx2 + 1]) + int(m[idx3 + 1]) + int(m[idx4 + 1])
return {
'total': total * 1024,
'used': (total - avail) * 1024
}

View File

@ -1,142 +0,0 @@
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# Copyright (c) 2010 Citrix Systems, Inc.
# Copyright 2011 Justin Santa Barbara
# Copyright 2015 Canonical Ltd
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import hashlib
import os
from oslo_config import cfg
from oslo_log import log as logging
from oslo_utils import excutils
from oslo_utils import importutils
from nova.i18n import _, _LE
from nova import exception
from nova.openstack.common import fileutils
from nova import utils
import container_utils
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
def load_driver(default, *args, **kwargs):
image_class = importutils.import_class(CONF.lxd.lxd_image_type)
return image_class(*args, **kwargs)
def fetch_image(client, context, image, instance):
try:
if image not in client.image_list():
if not os.path.exists(container_utils.get_base_dir()):
fileutils.ensure_tree(container_utils.get_base_dir())
container_image = container_utils.get_container_image(
instance)
container_utils.fetch_image(context, container_image, instance)
except Exception:
with excutils.save_and_reraise_exception():
LOG.error(_LE('Error downloading image: %(instance)'
' %(image)s'),
{'instance': instance.uuid,
'image': instance.image_ref})
class BaseContainerImage(object):
def __init__(self, lxd):
self.lxd = lxd
def setup_container(self, context, instance, image_meta):
pass
def destory_contianer(self, instance, image_meta):
pass
class DefaultContainerImage(object):
def __init__(self, lxd):
self.lxd = lxd
def setup_container(self, context, instance, image_meta):
LOG.debug("Setting up Container")
container_image = container_utils.get_container_image(instance)
try:
if instance.image_ref in self.lxd.image_list():
return
if os.path.exists(container_image):
return
fetch_image(self.lxd, context,
instance.image_ref, instance)
self._upload_image(container_image, instance, image_meta)
except Exception as ex:
with excutils.save_and_reraise_exception():
LOG.exception(_LE('Failed to setup container: %s = %s'),
(instance.uuid, ex))
self.destroy_contianer(instance, image_meta)
raise
def _upload_image(self, container_image, instance, image_meta):
if not self._check_image_file(container_image, image_meta):
msg = _('md5checksum mismtach')
raise exception.NovaException(msg)
if not self.lxd.image_upload(container_image,
container_image.split('/')[-1]):
msg = _('Image upload failed')
raise exception.NovaException(msg)
config = {'target': self._get_lxd_md5sum(container_image),
'name': instance.image_ref}
if not self.lxd.alias_create(config):
msg = _('Alias creation failed')
raise exception.NovaException(msg)
def _check_image_file(self, container_image, image_meta):
md5sum = self._get_glance_md5sum(container_image)
if image_meta.get('checksum') == md5sum:
return True
else:
return False
def _get_glance_md5sum(self, container_image):
out, err = utils.execute('md5sum', container_image)
return out.split(' ')[0]
def _get_lxd_md5sum(self, container_image):
with open(container_image, 'rb') as fd:
return hashlib.sha256(fd.read()).hexdigest()
def _image_rollback(self, container_image):
if os.path.exists(container_image):
os.unlink(container_image)
def destroy_container(self, instance, image_meta):
LOG.debug('Destroying container')
container_image = container_utils.get_container_image(instance)
if instance.image_ref in self.lxd.alias_list():
self.lxd.alias_delete(instance.image_ref)
fingerprint = self._get_lxd_md5sum(container_image)
if fingerprint in self.lxd.image_list():
self.lxd.image_delete(fingerprint)
if os.path.exists(container_image):
os.unlink(container_image)

View File

@ -1,92 +0,0 @@
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# Copyright (c) 2010 Citrix Systems, Inc.
# Copyright 2011 Justin Santa Barbara
# Copyright 2015 Canonical Ltd
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from nova.i18n import _
class Migration(object):
def __init__(self):
pass
def migrate_disk_and_power_off(self, context, instance, dest,
flavor, network_info,
block_device_info=None,
timeout=0, retry_interval=0):
raise NotImplementedError()
def finish_migration(self, context, migration, instance, disk_info,
network_info, image_meta, resize_instance,
block_device_info=None, power_on=True):
raise NotImplementedError()
def confirm_migration(self, migration, instance, network_info):
"""Confirms a resize, destroying the source VM.
:param instance: nova.objects.instance.Instance
"""
# TODO(Vek): Need to pass context in for access to auth_token
raise NotImplementedError()
def finish_revert_migration(self, context, instance, network_info,
block_device_info=None, power_on=True):
raise NotImplementedError()
def pre_live_migration(self, context, instance, block_device_info,
network_info, disk_info, migrate_data=None):
raise NotImplementedError()
def live_migration(self, context, instance, dest,
post_method, recover_method, block_migration=False,
migrate_data=None):
raise NotImplementedError()
def rollback_live_migration_at_destination(self, context, instance,
network_info,
block_device_info,
destroy_disks=True,
migrate_data=None):
raise NotImplementedError()
def post_live_migration(self, context, instance, block_device_info,
migrate_data=None):
pass
def post_live_migration_at_source(self, context, instance, network_info):
raise NotImplementedError(_("Hypervisor driver does not support "
"post_live_migration_at_source method"))
def post_live_migration_at_destination(self, context, instance,
network_info,
block_migration=False,
block_device_info=None):
raise NotImplementedError()
def check_can_live_migrate_destination(self, context, instance,
src_compute_info, dst_compute_info,
block_migration=False,
disk_over_commit=False):
raise NotImplementedError()
def check_can_live_migrate_destination_cleanup(self, context,
dest_check_data):
raise NotImplementedError()
def check_can_live_migrate_source(self, context, instance,
dest_check_data, block_device_info=None):
raise NotImplementedError()

View File

@ -1,85 +0,0 @@
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# Copyright (c) 2010 Citrix Systems, Inc.
# Copyright 2011 Justin Santa Barbara
# Copyright 2015 Canonical Ltd
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_config import cfg
from oslo_log import log as logging
from nova import exception
from nova.i18n import _
import container_utils
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
class LXDProfile(object):
def __init__(self, lxd):
self.lxd = lxd
''' Prefetch information that we need about the host.'''
self.host = self.lxd.host_info()
def profile_create(self, instance, network_info):
LOG.debug('Creating host profile')
profile = {'name': instance.uuid,
'config': {'raw.lxc':
'lxc.console.logfile = %s\n'
% container_utils.get_console_path(instance)}
}
if network_info:
profile['devices'] = self._get_network_devices(network_info)
if instance:
profile = self._get_container_limits(instance, profile)
if not self.lxd.profile_create(profile):
msg = _('Failed to create profile')
raise exception.NovaException(msg)
def profile_delete(self, instance):
if not self.lxd.profile_delete(instance.uuid):
msg = _('Unable to delete profile')
raise exception.NovaException(msg)
def _get_container_limits(self, instance, profile):
LOG.debug("Setting container limits")
if instance.vcpus >= 1:
profile['config'].update({'limits.cpus': '%s'
% instance.vcpus})
if instance.memory_mb >= 0:
profile['config'].update({'limits.memory': instance.memory_mb})
return profile
def _get_network_devices(self, network_info):
for vif in network_info:
vif_id = vif['id'][:11]
vif_type = vif['type']
bridge = vif['network']['bridge']
mac = vif['address']
if vif_type == 'ovs':
bridge = 'qbr%s' % vif_id
return {'eth0': {'nictype': 'bridged',
'hwaddr': mac,
'parent': bridge,
'type': 'nic'}}

View File

@ -1,76 +0,0 @@
# Copyright (c) 2015 Canonical Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import os
import pwd
import grp
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)

View File

@ -1,147 +0,0 @@
# Copyright (c) 2015 Canonical Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_concurrency import processutils
from oslo_config import cfg
from oslo_log import log as logging
from nova.i18n import _LE, _
from nova import exception
from nova.network import linux_net
from nova.network import model as network_model
from nova import utils
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
class LXDGenericDriver(object):
def _get_vif_driver(self, vif):
vif_type = vif['type']
if vif_type is None:
raise exception.NovaException(
_("vif_type parameter must be present "
"for this vif_driver implementation"))
elif vif_type == network_model.VIF_TYPE_OVS:
return LXDOpenVswitchDriver()
else:
return LXDNetworkBridgeDriver()
def plug(self, instance, vif):
vif_driver = self._get_vif_driver(vif)
vif_driver.plug(instance, vif)
def unplug(self, instance, vif):
vif_driver = self._get_vif_driver(vif)
vif_driver.unplug(instance, vif)
class LXDOpenVswitchDriver(object):
def plug(self, instance, vif, port='ovs'):
iface_id = self._get_ovs_interfaceid(vif)
br_name = self._get_br_name(vif['id'])
v1_name, v2_name = self._get_veth_pair_names(vif['id'])
if not linux_net.device_exists(br_name):
utils.execute('brctl', 'addbr', br_name, run_as_root=True)
utils.execute('brctl', 'setfd', br_name, 0, run_as_root=True)
utils.execute('brctl', 'stp', br_name, 'off', run_as_root=True)
utils.execute('tee',
('/sys/class/net/%s/bridge/multicast_snooping' %
br_name),
process_input='0',
run_as_root=True,
check_exit_code=[0, 1])
if not linux_net.device_exists(v2_name):
linux_net._create_veth_pair(v1_name, v2_name)
utils.execute('ip', 'link', 'set', br_name, 'up', run_as_root=True)
utils.execute('brctl', 'addif', br_name, v1_name, run_as_root=True)
if port == 'ovs':
linux_net.create_ovs_vif_port(self._get_bridge_name(vif),
v2_name, iface_id,
vif['address'], instance.uuid)
elif port == 'ivs':
linux_net.create_ivs_vif_port(v2_name, iface_id,
vif['address'], instance.uuid)
def unplug(self, instance, vif):
try:
br_name = self._get_br_name(vif['id'])
v1_name, v2_name = self._get_veth_pair_names(vif['id'])
if linux_net.device_exists(br_name):
utils.execute('brctl', 'delif', br_name, v1_name,
run_as_root=True)
utils.execute('ip', 'link', 'set', br_name, 'down',
run_as_root=True)
utils.execute('brctl', 'delbr', br_name,
run_as_root=True)
linux_net.delete_ovs_vif_port(self._get_bridge_name(vif),
v2_name)
if linux_net.device_exists(v2_name):
utils.execute('ip', 'link', 'set', v2_name, 'down',
run_as_root=True)
except processutils.ProcessExecutionError:
LOG.exception(_LE("Failed while unplugging vif"),
instance=instance)
def _get_bridge_name(self, vif):
return vif['network']['bridge']
def _get_ovs_interfaceid(self, vif):
return vif.get('ovs_interfaceid') or vif['id']
def _get_br_name(self, iface_id):
return ("qbr" + iface_id)[:network_model.NIC_NAME_LEN]
def _get_veth_pair_names(self, iface_id):
return (("qvb%s" % iface_id)[:network_model.NIC_NAME_LEN],
("qvo%s" % iface_id)[:network_model.NIC_NAME_LEN])
class LXDNetworkBridgeDriver(object):
def plug(self, instance, vif):
network = vif['network']
if (not network.get_meta('multi_host', False) and
network.get_meta('should_create_bridge', False)):
if network.get_meta('should_create_vlan', False):
iface = CONF.vlan_interface or \
network.get_meta('bridge_interface')
LOG.debug('Ensuring vlan %(vlan)s and bridge %(bridge)s',
{'vlan': network.get_meta('vlan'),
'bridge': vif['network']['bridge']},
instance=instance)
linux_net.LinuxBridgeInterfaceDriver.ensure_vlan_bridge(
network.get_meta('vlan'),
vif['network']['bridge'],
iface)
else:
iface = CONF.flat_interface or \
network.get_meta('bridge_interface')
LOG.debug("Ensuring bridge %s",
vif['network']['bridge'], instance=instance)
linux_net.LinuxBridgeInterfaceDriver.ensure_bridge(
vif['network']['bridge'],
iface)
def unplug(self, instance, vif):
pass

View File

@ -1,32 +0,0 @@
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# Copyright (c) 2010 Citrix Systems, Inc.
# Copyright 2011 Justin Santa Barbara
# Copyright 2015 Canonical Ltd
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
class Volume(object):
def __init__(object):
pass
def container_attach_volume(self, context, connection_info, instance,
mountpoint, disk_bus=None, device_type=None,
encryption=None):
raise NotImplementedError()
def container_detach_volume(self, connection_info, instance, mountpoint,
encryption=None):
raise NotImplementedError()

View File

@ -0,0 +1,54 @@
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# Copyright 2015 Canonical Ltd
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import contextlib
import mock
from oslo_config import cfg
import pylxd
from nova import context
from nova import test
from nova.virt import fake
from nova.tests.unit import fake_network
from nova.tests.unit import fake_instance
from nclxd.nova.virt.lxd import driver
from nova import exception
from nova import utils
from nclxd.nova.virt.lxd import container_ops
CONF = cfg.CONF
class LXDTestDriver(test.NoDBTestCase):
def setUp(self):
super(LXDTestDriver, self).setUp()
self.container = container_ops.LXDOperations(fake.FakeVirtAPI())
@mock.patch.object(pylxd.api.API, 'host_ping')
def tst_init_host_fail(self, mock_ping):
mock_ping.side_affect = True
self.assertTrue(self.container.container_init_host("fakehost"))
@mock.patch.object(pylxd.api.API, 'host_ping')
def test_init_host_fail(self, mock_ping):
mock_ping.side_affect = False
self.assertFalse(self.container.container_init_host("fakehost"))

View File

@ -0,0 +1,58 @@
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# Copyright 2015 Canonical Ltd
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import contextlib
import platform
import mock
from oslo_config import cfg
from nova import context
from nova import test
from nova.virt import fake
from nova.tests.unit import fake_network
from nova.tests.unit import fake_instance
from nclxd.nova.virt.lxd import driver
from nova import exception
from nova import utils
from nclxd.nova.virt.lxd import container_ops
CONF = cfg.CONF
class LXDTestDriver(test.NoDBTestCase):
def setUp(self):
super(LXDTestDriver, self).setUp()
self.connection = driver.LXDDriver(fake.FakeVirtAPI())
def test_capabilities(self):
self.assertFalse(self.connection.capabilities['has_imagecache'])
self.assertFalse(self.connection.capabilities['supports_recreate'])
self.assertFalse(self.connection.capabilities[
'supports_migrate_to_same_host'])
@mock.patch.object(container_ops.LXDOperations, 'container_init_host')
def test_init_host(self, mock_container_init):
mock_container_init.side_affect = True
self.assertTrue(self.connection.init_host("fakehost"))
@mock.patch.object(container_ops.LXDOperations, 'container_init_host')
def test_init_host_fail(self, mock_container_init):
mock_container_init.side_affect = False
self.assertFalse(self.connection.init_host("fakehost"))

View File

@ -1,104 +0,0 @@
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# Copyright 2015 Canonical Ltd
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import contextlib
import platform
import mock
from oslo_config import cfg
from oslo_utils import units
from nova import test
from nova.virt import fake
from nclxd.nova.virt.lxd import driver
from nclxd.nova.virt.lxd import host
from nova import utils
CONF = cfg.CONF
class LXDTestHostCase(test.NoDBTestCase):
def setUp(self):
super(LXDTestHostCase, self).setUp()
self.connection = driver.LXDDriver(fake.FakeVirtAPI())
def test_get_available_resource(self):
memory = {
'total': 4 * units.Mi,
'used': 1 * units.Mi
}
disk = {
'total': 10 * units.Gi,
'available': 3 * units.Gi,
'used': 1 * units.Gi
}
cpu_info = {
'arch': 'x86_64',
'model': 'Intel(R) Pentium(R) CPU J2900 @ 2.41GHz',
'vendor': 'GenuineIntel',
'sockets': 1,
'cores': 4,
'threads': 1,
'topology': {'sockets': 1,
'cores': 4,
'threads': 1
},
'features': 'fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov'
'pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe '
'syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_'
'good nopl xtopology nonstop_tsc aperfmperf pni pclmul'
'qdq dtes64 monitor ds_cpl vmx est tm2 ssse3 cx16'
'xtpr pdcm sse4_1 sse4_2 movbe popcnt tsc_deadline_timer'
'rdrand lahf_lm 3dnowprefetch ida arat epb dtherm tpr_shadow'
' vnmi flexpriority ept vpid tsc_adjust smep erms'
}
with contextlib.nested(
mock.patch.object(host.Host, '_get_fs_info',
return_value=disk),
mock.patch.object(host.Host, '_get_memory_mb_usage',
return_value=memory),
mock.patch.object(host.Host, '_get_cpu_info',
return_value=cpu_info)
) as (
_get_fs_info,
_get_memory_mb_usage,
_get_cpu_info
):
stats = self.connection.get_available_resource("compute1")
self.assertEquals(stats['vcpus'], 4)
self.assertEquals(stats['memory_mb'], 4)
self.assertEquals(stats['memory_mb_used'], 1)
self.assertEquals(stats['local_gb'], 10)
self.assertEquals(stats['local_gb_used'], 1)
self.assertEquals(stats['vcpus_used'], 0)
self.assertEquals(stats['hypervisor_type'], 'lxd')
self.assertEquals(stats['hypervisor_version'], 1)
self.assertEquals(stats['hypervisor_hostname'], platform.node())
def test_get_host_ip_addr(self):
ip = self.connection.get_host_ip_addr()
self.assertEqual(ip, CONF.my_ip)
#@mock.patch('nova.utils.execute')
#def test_get_host_uptime(self, mock_execute):
# self.connection.get_host_uptime()
# mock_execute.assert_has_calls([
# mock.call('env', 'LANG=C', 'uptime')])

View File

@ -1,71 +0,0 @@
# Copyright 2015 Canonical Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import mock
import os
from oslo_config import cfg
from nova import test
from nova.tests.unit import fake_instance
from nclxd.nova.virt.lxd import container_utils
CONF = cfg.CONF
class LXDUitlsTestCase(test.NoDBTestCase):
def test_get_base_dir(self):
path = container_utils.get_base_dir()
expected_path = os.path.join(CONF.instances_path,
CONF.image_cache_subdirectory_name)
self.assertEqual(expected_path, path)
def test_get_container_image(self):
instance = fake_instance.fake_instance_obj(None, name='fake_inst',
uuid='fake_uuid')
path = container_utils.get_container_image(instance)
expected_path = os.path.join(CONF.instances_path,
CONF.image_cache_subdirectory_name,
'%s.tar.gz' % instance.image_ref)
self.assertEqual(expected_path, path)
def test_get_console_path(self):
instance = fake_instance.fake_instance_obj(None, name='fake_inst',
uuid='fake_uuid')
path = container_utils.get_console_path(instance)
expected_path = os.path.join(CONF.lxd.lxd_root_dir,
'lxc',
instance.uuid,
'console.log')
self.assertEqual(expected_path, path)
def test_get_container_dir(self):
instance = fake_instance.fake_instance_obj(None, name='fake_inst',
uuid='fake_uuid')
path = container_utils.get_container_dir(instance)
expected_path = os.path.join(CONF.lxd.lxd_root_dir,
'lxc',
instance.uuid)
self.assertEqual(expected_path, path)
@mock.patch('nova.virt.images.fetch')
def test_fetch_image(self, mock_images):
instance = fake_instance.fake_instance_obj(None, name='fake_inst',
uuid='fake_uuid')
context = 'opaque context'
target = '/tmp/targetfile'
container_utils.fetch_image(context, target, instance)
mock_images.assert_called_once_with(context, None, target,
instance.user_id, instance.project_id,
max_size=0)

View File

@ -1,48 +0,0 @@
import contextlib
import mock
from oslo_config import cfg
from nova import test
from nova.network import linux_net
from nova.network import model as network_model
from nclxd.nova.virt.lxd import driver as lxd_driver
from nova import exception
from nova import utils
cfg = cfg.CONF
class LXDVifTestCase(test.NoDBTestCase):
gateway_bridge_4 = network_model.IP(address='101.168.1.1', type='gateway')
dns_bridge_4 = network_model.IP(address='8.8.8.8', type=None)
ips_bridge_4 = [network_model.IP(address='101.168.1.9', type=None)]
subnet_bridge_4 = network_model.Subnet(cidr='101.168.1.0/24',
dns=[dns_bridge_4],
gateway=gateway_bridge_4,
routes=None,
dhcp_server='191.168.1.1')
gateway_bridge_6 = network_model.IP(address='101:1db9::1', type='gateway')
subnet_bridge_6 = network_model.Subnet(cidr='101:1db9::/64',
dns=None,
gateway=gateway_bridge_6,
ips=None,
routes=None)
network_bridge = network_model.Network(id='network-id-xxx-yyy-zzz',
bridge='br0',
label=None,
subnets=[subnet_bridge_4,
subnet_bridge_6],
bridge_interface='eth0',
vlan=99)
def setUp(self):
super(LXDVifTestCase(), self).setUp()
self.executes = []
def fake_execute(*cmd, **kwargs):
self.executes.append(cmd)
return None, None
self.stubs.Set(utils, 'execute', fake_execute)