nova-lxd/nova/virt/lxd/migrate.py
Chuck Short fe1a6a7fc7 Remove profile after migration
Remove profile after live migration so that
the instance can be moved again if needed.

Change-Id: I47236828aebe9488ab0bf0954e889ad6f077f02d
Signed-off-by: Chuck Short <chuck.short@canonical.com>
2016-07-25 22:18:42 -04:00

283 lines
12 KiB
Python

# Copyright 2016 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 nova.conf
from nova import exception
from nova import i18n
from nova.objects import migrate_data as migrate_data_obj
from oslo_log import log as logging
from oslo_utils import excutils
from oslo_utils import fileutils
from nova.virt.lxd import utils as container_dir
from nova.virt.lxd import session
from nova.virt.lxd import vif as lxd_vif
from nova.virt import firewall
_ = i18n._
_LE = i18n._LE
_LI = i18n._LI
CONF = nova.conf.CONF
LOG = logging.getLogger(__name__)
class LXDContainerMigrate(object):
def __init__(self, driver):
self.driver = driver
self.vif_driver = lxd_vif.LXDGenericDriver()
self.firewall_driver = firewall.load_driver(
default='nova.virt.firewall.NoopFirewallDriver')
self.session = session.LXDAPISession()
#
# migrate/resize
#
def confirm_migration(self, migration, instance, network_info):
LOG.debug("confirm_migration called", instance=instance)
if not self.session.container_defined(instance.name, instance):
msg = _('Failed to find container %(instance)s') % \
{'instance': instance.name}
raise exception.NovaException(msg)
try:
self.session.profile_delete(instance)
self.session.container_destroy(instance.name,
instance)
self.unplug_vifs(instance, network_info)
except Exception as ex:
with excutils.save_and_reraise_exception():
LOG.exception(_LE('Confirm migration failed for %(instance)s: '
'%(ex)s'), {'instance': instance.name,
'ex': ex}, instance=instance)
def finish_migration(self, context, migration, instance, disk_info,
network_info, image_meta, resize_instance=False,
block_device_info=None, power_on=True):
LOG.debug("finish_migration called", instance=instance)
if self.session.container_defined(instance.name, instance):
return
try:
# Ensure that the instance directory exists
instance_dir = container_dir.get_instance_dir(instance.name)
if not os.path.exists(instance_dir):
fileutils.ensure_tree(instance_dir)
# Step 1 - Setup the profile on the dest host
self._copy_container_profile(instance, network_info)
# Step 2 - Open a websocket on the srct and and
# generate the container config
self._container_init(migration['source_compute'], instance)
# Step 3 - Start the network and container
self.plug_vifs(instance, network_info)
self.session.container_start(instance.name, instance)
except Exception as ex:
with excutils.save_and_reraise_exception():
LOG.exception(_LE('Migration failed for %(instance)s: '
'%(ex)s'),
{'instance': instance.name,
'ex': ex}, instance=instance)
def finish_revert_migration(self, context, instance, network_info,
block_device_info=None, power_on=True):
LOG.debug('finish_revert_migration called for instance',
instance=instance)
if self.session.container_defined(instance.name, instance):
self.session.container_start(instance.name, instance)
#
# live-migration
#
def pre_live_migration(self, context, instance, block_device_info,
network_info, disk_info, migrate_data=None):
LOG.debug('pre_live_migration called for instance', instance=instance)
try:
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)
self._copy_container_profile(instance, network_info)
except Exception as ex:
with excutils.save_and_reraise_exception():
LOG.error(_LE('pre_live_migration failed for %(instance)s: '
'%(reason)s'),
{'instance': instance.name, 'reason': ex},
instance=instance)
def live_migration(self, context, instance, dest,
post_method, recover_method, block_migration=False,
migrate_data=None):
LOG.debug('live_migration called for instance', instance=instance)
try:
self._container_init(dest, instance)
post_method(context, instance, dest, block_migration)
except Exception as ex:
with excutils.save_and_reraise_exception():
LOG.error(_LE('live_migration failed for %(instance)s: '
'%(reason)s'),
{'instance': instance.name, 'reason': ex},
instance=instance)
def post_live_migration(self, context, instance, block_device_info,
migrate_data=None):
LOG.debug('post_live_migration called for instance',
instance=instance)
try:
self.session.container_destroy(instance.name, instance)
except Exception as ex:
with excutils.save_and_reraise_exception():
LOG.error(_LE('post_live_migration failed for %(instance)s: '
'%(reason)s'),
{'instance': instance.name, 'reason': ex},
instance=instance)
def post_live_migration_at_destination(self, context, instance,
network_info,
block_migration=False,
block_device_info=None):
LOG.debug('post_live_migration_at_destinaation called for instance',
instance=instance)
return
def post_live_migration_at_source(self, context, instance, network_info):
LOG.debug('post_live_migration_at_source called for instance',
instance=instance)
try:
self.session.profile_delete(instance)
self.driver.cleanup(context, instance, network_info)
except Exception as ex:
with excutils.save_and_reraise_exception():
LOG.error(_LE('post_live_migration failed for %(instance)s: '
'%(reason)s'),
{'instance': instance.name, 'reason': ex},
instance=instance)
def check_can_live_migrate_destination(self, context, instance,
src_compute_info, dst_compute_info,
block_migration=False,
disk_over_commit=False):
LOG.debug('check_can_live_migration called for instance',
instance=instance)
if self.session.container_defined(instance.name, instance):
raise exception.InstanceExists(name=instance.name)
# XXX (zulcss) - June 14, 2016 - Workaround
# for LXD sending an empty migrate object
# that descirbes the host that the instance
# is being migrated to. Replace the
# HyperVLiveMigrateData object with an
# LXD named object.
return migrate_data_obj.HyperVLiveMigrateData()
def check_can_live_migrate_destination_cleanup(self, context,
dest_check_data):
LOG.debug('check_can_live_migrate_destination_cleanup')
return
def check_can_live_migrate_source(self, context, instance,
dest_check_data,
block_device_info=None):
LOG.debug('check_can_live_migrate_source called for instance',
instance=instance)
if not CONF.lxd.allow_live_migration:
msg = _('Live migration is not enabled.')
LOG.error(msg, instance=instance)
raise exception.MigrationPreCheckError(reason=msg)
return dest_check_data
def _copy_container_profile(self, instance, network_info):
LOG.debug('_copy_cotontainer_profile called for instnace',
instance=instance)
container_profile = self.driver.create_profile(instance,
network_info)
self.session.profile_create(container_profile, instance)
def _container_init(self, host, instance):
LOG.debug('_container_init called for instnace', instance=instance)
(state, data) = (self.session.container_migrate(instance.name,
CONF.my_ip,
instance))
container_config = {
'name': instance.name,
'profiles': [instance.name],
'source': self.driver.get_container_migrate(
data, host, instance)
}
self.session.container_init(container_config, instance, host)
def plug_vifs(self, instance, network_info):
"""Setup the container network on the host
:param instance: nova instance object
:param network_info: instance network configuration
"""
LOG.debug('plug_vifs called for instance', instance=instance)
try:
for viface in network_info:
self.vif_driver.plug(instance, viface)
self.start_firewall(instance, network_info)
except Exception as ex:
with excutils.save_and_reraise_exception():
LOG.error(_LE('Failed to configure container network'
' for %(instance)s: %(ex)s'),
{'instance': instance.name, 'ex': ex},
instance=instance)
def unplug_vifs(self, instance, network_info):
"""Unconfigure the LXD container network
:param instance: nova intance object
:param network_info: instance network confiugration
"""
try:
for viface in network_info:
self.vif_driver.unplug(instance, viface)
self.stop_firewall(instance, network_info)
except Exception as ex:
with excutils.save_and_reraise_exception():
LOG.error(_LE('Failed to remove container network'
' for %(instance)s: %(ex)s'),
{'instance': instance.name, 'ex': ex},
instance=instance)
def start_firewall(self, instance, network_info):
self.firewall_driver.setup_basic_filtering(instance, network_info)
self.firewall_driver.prepare_instance_filter(instance, network_info)
self.firewall_driver.apply_instance_filter(instance, network_info)
def stop_firewall(self, instance, network_info):
self.firewall_driver.unfilter_instance(instance, network_info)