nclxd ng
Signed-off-by: Chuck Short <chuck.short@canonical.com>
This commit is contained in:
parent
e3eb861caa
commit
5e1ab7f58d
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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()
|
|
@ -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)
|
||||
|
|
@ -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
|
@ -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']
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
|
@ -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()
|
|
@ -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'}}
|
|
@ -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)
|
|
@ -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
|
|
@ -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()
|
|
@ -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"))
|
||||
|
|
@ -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"))
|
|
@ -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')])
|
|
@ -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)
|
|
@ -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)
|
Loading…
Reference in New Issue