Merge "svm migration across physical networks with multiple port bindings"
This commit is contained in:
commit
1ffa0c1273
@ -1333,6 +1333,12 @@ def share_server_backend_details_set(context, share_server_id, server_details):
|
||||
server_details)
|
||||
|
||||
|
||||
def share_server_backend_details_get_item(context, share_server_id, meta_key):
|
||||
"""Get backend details."""
|
||||
return IMPL.share_server_backend_details_get_item(context, share_server_id,
|
||||
meta_key)
|
||||
|
||||
|
||||
def share_server_backend_details_delete(context, share_server_id):
|
||||
"""Delete backend details DB records for a share server."""
|
||||
return IMPL.share_server_backend_details_delete(context, share_server_id)
|
||||
|
@ -5617,6 +5617,17 @@ def share_server_backend_details_set(context, share_server_id, server_details):
|
||||
return server_details
|
||||
|
||||
|
||||
@require_context
|
||||
@context_manager.reader
|
||||
def share_server_backend_details_get_item(context, share_server_id, meta_key):
|
||||
try:
|
||||
meta_ref = _share_server_backend_details_get_item(
|
||||
context, share_server_id, meta_key)
|
||||
except exception.ShareServerBackendDetailsNotFound:
|
||||
return None
|
||||
return meta_ref.get('value')
|
||||
|
||||
|
||||
@require_context
|
||||
@context_manager.writer
|
||||
def share_server_backend_details_delete(context, share_server_id):
|
||||
@ -5784,7 +5795,7 @@ def network_allocations_get_for_share_server(
|
||||
share_server_id=share_server_id,
|
||||
)
|
||||
if label:
|
||||
if label != 'admin':
|
||||
if label == 'user':
|
||||
query = query.filter(or_(
|
||||
# NOTE(vponomaryov): we treat None as alias for 'user'.
|
||||
models.NetworkAllocation.label == None, # noqa
|
||||
|
@ -66,6 +66,19 @@ CONF = cfg.CONF
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class PortBindingAlreadyExistsClient(neutron_client_exc.Conflict):
|
||||
pass
|
||||
|
||||
|
||||
# We need to monkey-patch neutronclient.common.exceptions module, to make
|
||||
# neutron client to raise error specific exceptions. E.g. exception
|
||||
# PortBindingAlreadyExistsClient is raised for Neutron API error
|
||||
# PortBindingAlreadyExists. If not defined, a general exception of type
|
||||
# Conflict will be raised.
|
||||
neutron_client_exc.PortBindingAlreadyExistsClient = \
|
||||
PortBindingAlreadyExistsClient
|
||||
|
||||
|
||||
def list_opts():
|
||||
return client_auth.AuthClientLoader.list_opts(NEUTRON_GROUP)
|
||||
|
||||
@ -336,6 +349,32 @@ class API(object):
|
||||
raise exception.NetworkException(code=e.status_code,
|
||||
message=e.message)
|
||||
|
||||
def bind_port_to_host(self, port_id, host, vnic_type):
|
||||
"""Add an inactive binding to existing port."""
|
||||
|
||||
try:
|
||||
data = {"binding": {"host": host, "vnic_type": vnic_type}}
|
||||
return self.client.create_port_binding(port_id, data)['binding']
|
||||
except neutron_client_exc.PortBindingAlreadyExistsClient as e:
|
||||
LOG.warning('Port binding already exists: %s', e)
|
||||
except neutron_client_exc.NeutronClientException as e:
|
||||
raise exception.NetworkException(
|
||||
code=e.status_code, message=e.message)
|
||||
|
||||
def delete_port_binding(self, port_id, host):
|
||||
try:
|
||||
return self.client.delete_port_binding(port_id, host)
|
||||
except neutron_client_exc.NeutronClientException as e:
|
||||
raise exception.NetworkException(
|
||||
code=e.status_code, message=e.message)
|
||||
|
||||
def activate_port_binding(self, port_id, host):
|
||||
try:
|
||||
return self.client.activate_port_binding(port_id, host)
|
||||
except neutron_client_exc.NeutronClientException as e:
|
||||
raise exception.NetworkException(
|
||||
code=e.status_code, message=e.message)
|
||||
|
||||
def show_router(self, router_id):
|
||||
try:
|
||||
return self.client.show_router(router_id).get('router', {})
|
||||
|
@ -26,6 +26,7 @@ from manila.i18n import _
|
||||
from manila import network
|
||||
from manila.network.neutron import api as neutron_api
|
||||
from manila.network.neutron import constants as neutron_constants
|
||||
from manila.share import utils as share_utils
|
||||
from manila import utils
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
@ -718,6 +719,70 @@ class NeutronBindNetworkPlugin(NeutronNetworkPlugin):
|
||||
context, port['id'], port_info)
|
||||
return ports
|
||||
|
||||
@utils.retry(retry_param=exception.NetworkException, retries=20)
|
||||
def _wait_for_network_segment(self, share_server, host):
|
||||
network_id = share_server['share_network_subnet']['neutron_net_id']
|
||||
network = self.neutron_api.get_network(network_id)
|
||||
for segment in network['segments']:
|
||||
if segment['provider:physical_network'] == (
|
||||
self.config.neutron_physical_net_name):
|
||||
return segment['provider:segmentation_id']
|
||||
msg = _('Network segment not found on host %s') % host
|
||||
raise exception.NetworkException(msg)
|
||||
|
||||
def extend_network_allocations(self, context, share_server):
|
||||
"""Extend network to target host.
|
||||
|
||||
This will create port bindings on target host without activating them.
|
||||
If network segment does not exist on target host, it will be created.
|
||||
|
||||
:return: list of port bindings with new segmentation id on target host
|
||||
"""
|
||||
vnic_type = self.config.neutron_vnic_type
|
||||
host_id = self.config.neutron_host_id
|
||||
|
||||
active_port_bindings = (
|
||||
self.db.network_allocations_get_for_share_server(
|
||||
context, share_server['id'], label='user'))
|
||||
if len(active_port_bindings) == 0:
|
||||
raise exception.NetworkException(
|
||||
'Can not extend network with no active bindings')
|
||||
|
||||
# Create port binding on destination backend. It's safe to call neutron
|
||||
# api bind_port_to_host if the port is already bound to destination
|
||||
# host.
|
||||
for port in active_port_bindings:
|
||||
self.neutron_api.bind_port_to_host(port['id'], host_id,
|
||||
vnic_type)
|
||||
|
||||
# Wait for network segment to be created on destination host.
|
||||
vlan = self._wait_for_network_segment(share_server, host_id)
|
||||
for port in active_port_bindings:
|
||||
port['segmentation_id'] = vlan
|
||||
|
||||
return active_port_bindings
|
||||
|
||||
def delete_extended_allocations(self, context, share_server):
|
||||
host_id = self.config.neutron_host_id
|
||||
ports = self.db.network_allocations_get_for_share_server(
|
||||
context, share_server['id'], label='user')
|
||||
for port in ports:
|
||||
try:
|
||||
self.neutron_api.delete_port_binding(port['id'], host_id)
|
||||
except exception.NetworkException as e:
|
||||
msg = 'Failed to delete port binding on port %{port}s: %{err}s'
|
||||
LOG.warning(msg, {'port': port['id'], 'err': e})
|
||||
|
||||
def cutover_network_allocations(self, context, src_share_server):
|
||||
src_host = share_utils.extract_host(src_share_server['host'], 'host')
|
||||
dest_host = self.config.neutron_host_id
|
||||
ports = self.db.network_allocations_get_for_share_server(
|
||||
context, src_share_server['id'], label='user')
|
||||
for port in ports:
|
||||
self.neutron_api.activate_port_binding(port['id'], dest_host)
|
||||
self.neutron_api.delete_port_binding(port['id'], src_host)
|
||||
return ports
|
||||
|
||||
|
||||
class NeutronBindSingleNetworkPlugin(NeutronSingleNetworkPlugin,
|
||||
NeutronBindNetworkPlugin):
|
||||
|
@ -6291,6 +6291,7 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
|
||||
raise exception.NetAppException(message=e.message)
|
||||
msg = (_('Failed to check migration support. Reason: '
|
||||
'%s' % e.message))
|
||||
LOG.error(msg)
|
||||
raise exception.NetAppException(msg)
|
||||
|
||||
@na_utils.trace
|
||||
|
@ -113,6 +113,14 @@ share_manager_opts = [
|
||||
'the share manager will poll the driver to perform the '
|
||||
'next step of migration in the storage backend, for a '
|
||||
'migrating share server.'),
|
||||
cfg.BoolOpt('server_migration_extend_neutron_network',
|
||||
default=False,
|
||||
help='If set to True, neutron network are extended to '
|
||||
'destination host during share server migration. This '
|
||||
'option should only be enabled if using '
|
||||
'NeutronNetworkPlugin or its derivatives and when '
|
||||
'multiple bindings of Manila ports are supported by '
|
||||
'Neutron ML2 plugin.'),
|
||||
cfg.IntOpt('share_usage_size_update_interval',
|
||||
default=300,
|
||||
help='This value, specified in seconds, determines how often '
|
||||
@ -5716,8 +5724,20 @@ class ShareManager(manager.SchedulerDependentManager):
|
||||
availability_zone_id=service['availability_zone_id'],
|
||||
share_network_id=new_share_network_id))
|
||||
|
||||
extended_allocs = None
|
||||
dest_share_server = None
|
||||
try:
|
||||
# NOTE: Extend network allocations to destination host, i.e. create
|
||||
# inactive port bindings on the destination host. Refresh
|
||||
# network_allocations field in source_share_server with the new
|
||||
# bindings, so that correct segmentation id is used during
|
||||
# compatibility check and migration.
|
||||
if CONF.server_migration_extend_neutron_network:
|
||||
extended_allocs = (
|
||||
self.driver.network_api.extend_network_allocations(
|
||||
context, source_share_server))
|
||||
source_share_server['network_allocations'] = extended_allocs
|
||||
|
||||
compatibility = (
|
||||
self.driver.share_server_migration_check_compatibility(
|
||||
context, source_share_server, dest_host, old_share_network,
|
||||
@ -5797,6 +5817,10 @@ class ShareManager(manager.SchedulerDependentManager):
|
||||
|
||||
backend_details = (
|
||||
server_info.get('backend_details') if server_info else None)
|
||||
if extended_allocs:
|
||||
backend_details = backend_details or {}
|
||||
backend_details['segmentation_id'] = (
|
||||
extended_allocs[0]['segmentation_id'])
|
||||
if backend_details:
|
||||
self.db.share_server_backend_details_set(
|
||||
context, dest_share_server['id'], backend_details)
|
||||
@ -5816,6 +5840,10 @@ class ShareManager(manager.SchedulerDependentManager):
|
||||
context, constants.STATUS_AVAILABLE,
|
||||
share_instance_ids=share_instance_ids,
|
||||
snapshot_instance_ids=snapshot_instance_ids)
|
||||
# Rollback port bindings on destination host
|
||||
if extended_allocs:
|
||||
self.driver.network_api.delete_extended_allocations(
|
||||
context, source_share_server, dest_share_server)
|
||||
# Rollback read only access rules
|
||||
self._reset_read_only_access_rules_for_server(
|
||||
context, share_instances, source_share_server,
|
||||
@ -5879,6 +5907,22 @@ class ShareManager(manager.SchedulerDependentManager):
|
||||
service = self.db.service_get_by_args(
|
||||
context, service_host, 'manila-share')
|
||||
|
||||
# NOTE: Extend network allocations to destination host, i.e. create
|
||||
# inactive port bindings on destination host with the same ports in the
|
||||
# share network subnet. Refresh share_server with new network
|
||||
# allocations, so that correct segmentation id is used in the
|
||||
# compatibility check.
|
||||
if CONF.server_migration_extend_neutron_network:
|
||||
try:
|
||||
allocs = self.driver.network_api.extend_network_allocations(
|
||||
context, share_server)
|
||||
share_server['network_allocations'] = allocs
|
||||
except Exception:
|
||||
LOG.warning(
|
||||
'Failed to extend network allocations for '
|
||||
'share server %s.', share_server['id'])
|
||||
return result
|
||||
|
||||
# NOTE(dviroel): We'll build a list of request specs and send it to
|
||||
# the driver so vendors have a chance to validate if the destination
|
||||
# host meets the requirements before starting the migration.
|
||||
@ -5906,6 +5950,12 @@ class ShareManager(manager.SchedulerDependentManager):
|
||||
# the validations.
|
||||
driver_result['compatible'] = False
|
||||
|
||||
# NOTE: Delete port bindings on destination host after compatibility
|
||||
# check
|
||||
if CONF.server_migration_extend_neutron_network:
|
||||
self.driver.network_api.delete_extended_allocations(
|
||||
context, share_server)
|
||||
|
||||
result.update(driver_result)
|
||||
|
||||
return result
|
||||
@ -6148,6 +6198,29 @@ class ShareManager(manager.SchedulerDependentManager):
|
||||
dest_snss = self.db.share_network_subnet_get_all_by_share_server_id(
|
||||
context, dest_share_server['id'])
|
||||
|
||||
migration_extended_network_allocations = (
|
||||
CONF.server_migration_extend_neutron_network)
|
||||
|
||||
# NOTE: Network allocations are extended to the destination host on
|
||||
# previous (migration_start) step, i.e. port bindings are created on
|
||||
# destination host with existing ports. The network allocations will be
|
||||
# cut over on this (migration_complete) step, i.e. port bindings on
|
||||
# destination host will be activated and bindings on source host will
|
||||
# be deleted.
|
||||
if migration_extended_network_allocations:
|
||||
updated_allocations = (
|
||||
self.driver.network_api.cutover_network_allocations(
|
||||
context, source_share_server))
|
||||
segmentation_id = self.db.share_server_backend_details_get_item(
|
||||
context, dest_share_server['id'], 'segmentation_id')
|
||||
alloc_update = {
|
||||
'segmentation_id': segmentation_id,
|
||||
'share_server_id': dest_share_server['id']
|
||||
}
|
||||
subnet_update = {
|
||||
'segmentation_id': segmentation_id,
|
||||
}
|
||||
|
||||
migration_reused_network_allocations = (len(
|
||||
self.db.network_allocations_get_for_share_server(
|
||||
context, dest_share_server['id'])) == 0)
|
||||
@ -6164,7 +6237,14 @@ class ShareManager(manager.SchedulerDependentManager):
|
||||
context, source_share_server, dest_share_server, share_instances,
|
||||
snapshot_instances, new_network_allocations)
|
||||
|
||||
if not migration_reused_network_allocations:
|
||||
if migration_extended_network_allocations:
|
||||
for alloc in updated_allocations:
|
||||
self.db.network_allocation_update(context, alloc['id'],
|
||||
alloc_update)
|
||||
for subnet in dest_snss:
|
||||
self.db.share_network_subnet_update(context, subnet['id'],
|
||||
subnet_update)
|
||||
elif not migration_reused_network_allocations:
|
||||
network_allocations = []
|
||||
for net_allocation in new_network_allocations:
|
||||
network_allocations += net_allocation['network_allocations']
|
||||
@ -6310,6 +6390,10 @@ class ShareManager(manager.SchedulerDependentManager):
|
||||
context, share_server, dest_share_server,
|
||||
share_instances, snapshot_instances)
|
||||
|
||||
if CONF.server_migration_extend_neutron_network:
|
||||
self.driver.network_api.delete_extended_allocations(
|
||||
context, share_server)
|
||||
|
||||
# NOTE(dviroel): After cancelling the migration we should set the new
|
||||
# share server to INVALID since it may contain an invalid configuration
|
||||
# to be reused. We also cleanup the source_share_server_id to unblock
|
||||
|
@ -45,6 +45,15 @@ class FakeNeutronClient(object):
|
||||
def list_ports(self, **search_opts):
|
||||
pass
|
||||
|
||||
def create_port_binding(self, port_id, body):
|
||||
return body
|
||||
|
||||
def delete_port_binding(self, port_id, host_id):
|
||||
pass
|
||||
|
||||
def activate_port_binding(self, port_id, host_id):
|
||||
pass
|
||||
|
||||
def list_networks(self):
|
||||
pass
|
||||
|
||||
@ -536,6 +545,30 @@ class NeutronApiTest(test.TestCase):
|
||||
port_id, {'port': fixed_ips})
|
||||
self.assertTrue(clientv20.Client.called)
|
||||
|
||||
def test_bind_port_to_host(self):
|
||||
port_id = 'test_port'
|
||||
host = 'test_host'
|
||||
vnic_type = 'test_vnic_type'
|
||||
|
||||
port = self.neutron_api.bind_port_to_host(port_id, host, vnic_type)
|
||||
|
||||
self.assertEqual(host, port['host'])
|
||||
self.assertTrue(clientv20.Client.called)
|
||||
|
||||
def test_delete_port_binding(self):
|
||||
port_id = 'test_port'
|
||||
host = 'test_host'
|
||||
self.neutron_api.delete_port_binding(port_id, host)
|
||||
self.assertTrue(clientv20.Client.called)
|
||||
|
||||
def test_activate_port_binding(self):
|
||||
port_id = 'test_port'
|
||||
host = 'test_host'
|
||||
|
||||
self.neutron_api.activate_port_binding(port_id, host)
|
||||
|
||||
self.assertTrue(clientv20.Client.called)
|
||||
|
||||
def test_router_update_routes(self):
|
||||
# Set up test data
|
||||
router_id = 'test_router'
|
||||
|
@ -28,6 +28,7 @@ from manila import exception
|
||||
from manila.network.neutron import api as neutron_api
|
||||
from manila.network.neutron import constants as neutron_constants
|
||||
from manila.network.neutron import neutron_network_plugin as plugin
|
||||
from manila.share import utils as share_utils
|
||||
from manila import test
|
||||
from manila.tests import utils as test_utils
|
||||
|
||||
@ -132,6 +133,11 @@ fake_nw_info = {
|
||||
'provider:physical_network': 'net1',
|
||||
'provider:segmentation_id': 3926,
|
||||
},
|
||||
{
|
||||
'provider:network_type': 'vlan',
|
||||
'provider:physical_network': 'net2',
|
||||
'provider:segmentation_id': 1249,
|
||||
},
|
||||
{
|
||||
'provider:network_type': 'vxlan',
|
||||
'provider:physical_network': None,
|
||||
@ -197,6 +203,23 @@ fake_binding_profile = {
|
||||
'neutron_switch_info': 'fake switch info'
|
||||
}
|
||||
|
||||
fake_network_allocation_ext = {
|
||||
'id': 'fake port binding id',
|
||||
'share_server_id': fake_share_server['id'],
|
||||
'ip_address': fake_neutron_port['fixed_ips'][0]['ip_address'],
|
||||
'mac_address': fake_neutron_port['mac_address'],
|
||||
'status': constants.STATUS_ACTIVE,
|
||||
'label': fake_nw_info['segments'][1]['provider:physical_network'],
|
||||
'network_type': fake_share_network_subnet['network_type'],
|
||||
'segmentation_id': (
|
||||
fake_nw_info['segments'][1]['provider:segmentation_id']
|
||||
),
|
||||
'ip_version': fake_share_network_subnet['ip_version'],
|
||||
'cidr': fake_share_network_subnet['cidr'],
|
||||
'gateway': fake_share_network_subnet['gateway'],
|
||||
'mtu': 1509,
|
||||
}
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class NeutronNetworkPluginTest(test.TestCase):
|
||||
@ -1145,6 +1168,122 @@ class NeutronBindNetworkPluginTest(test.TestCase):
|
||||
fake_neutron_port['id'],
|
||||
network_allocation_update_data)
|
||||
|
||||
def test_extend_network_allocations(self):
|
||||
old_network_allocation = copy.deepcopy(fake_network_allocation)
|
||||
fake_network = copy.deepcopy(fake_neutron_network_multi)
|
||||
fake_ss = copy.deepcopy(fake_share_server)
|
||||
fake_ss["share_network_subnet"] = fake_share_network_subnet
|
||||
|
||||
fake_host_id = "fake_host_id"
|
||||
fake_physical_net = "net2"
|
||||
fake_port_id = old_network_allocation["id"]
|
||||
fake_vnic_type = "baremetal"
|
||||
config_data = {
|
||||
'DEFAULT': {
|
||||
"neutron_host_id": fake_host_id,
|
||||
"neutron_vnic_type": fake_vnic_type,
|
||||
'neutron_physical_net_name': fake_physical_net,
|
||||
}
|
||||
}
|
||||
self.bind_plugin = self._get_neutron_network_plugin_instance(
|
||||
config_data
|
||||
)
|
||||
self.mock_object(
|
||||
self.bind_plugin.neutron_api,
|
||||
"get_network",
|
||||
mock.Mock(return_value=fake_network),
|
||||
)
|
||||
self.mock_object(self.bind_plugin.neutron_api, "bind_port_to_host")
|
||||
self.mock_object(
|
||||
self.bind_plugin.db,
|
||||
"network_allocations_get_for_share_server",
|
||||
mock.Mock(return_value=[old_network_allocation]))
|
||||
|
||||
# calling the extend_network_allocations method
|
||||
self.bind_plugin.extend_network_allocations(self.fake_context, fake_ss)
|
||||
|
||||
# testing the calls, we expect the port to be bound to the current host
|
||||
# and the new network allocation to be created
|
||||
self.bind_plugin.neutron_api.bind_port_to_host.assert_called_once_with(
|
||||
fake_port_id, fake_host_id, fake_vnic_type
|
||||
)
|
||||
|
||||
def test_delete_extended_allocations(self):
|
||||
old_network_allocation = copy.deepcopy(fake_network_allocation)
|
||||
fake_ss = copy.deepcopy(fake_share_server)
|
||||
fake_host_id = "fake_host_id"
|
||||
fake_physical_net = "net2"
|
||||
fake_port_id = old_network_allocation["id"]
|
||||
fake_vnic_type = "baremetal"
|
||||
config_data = {
|
||||
"DEFAULT": {
|
||||
"neutron_host_id": fake_host_id,
|
||||
"neutron_vnic_type": fake_vnic_type,
|
||||
"neutron_physical_net_name": fake_physical_net,
|
||||
}
|
||||
}
|
||||
|
||||
self.bind_plugin = self._get_neutron_network_plugin_instance(
|
||||
config_data)
|
||||
self.mock_object(self.bind_plugin.neutron_api, "delete_port_binding")
|
||||
self.mock_object(self.bind_plugin.db, "network_allocation_delete")
|
||||
self.mock_object(self.bind_plugin.db,
|
||||
"network_allocations_get_for_share_server",
|
||||
mock.Mock(return_value=[old_network_allocation]))
|
||||
|
||||
self.bind_plugin.delete_extended_allocations(self.fake_context,
|
||||
fake_ss)
|
||||
neutron_api = self.bind_plugin.neutron_api
|
||||
neutron_api.delete_port_binding.assert_called_once_with(
|
||||
fake_port_id, fake_host_id)
|
||||
|
||||
@ddt.unpack
|
||||
def test_cutover_network_allocation(self):
|
||||
fake_alloc = copy.deepcopy(fake_network_allocation)
|
||||
fake_network = copy.deepcopy(fake_neutron_network_multi)
|
||||
fake_old_ss = copy.deepcopy(fake_share_server)
|
||||
fake_old_ss["share_network_subnet"] = fake_share_network_subnet
|
||||
fake_dest_ss = copy.deepcopy(fake_share_server)
|
||||
fake_dest_ss["host"] = "fake_host2@backend2#pool2"
|
||||
fake_old_host = share_utils.extract_host(fake_old_ss["host"], "host")
|
||||
|
||||
fake_host_id = "fake_host_id"
|
||||
fake_physical_net = "net2"
|
||||
fake_port_id = fake_alloc["id"]
|
||||
fake_vnic_type = "baremetal"
|
||||
config_data = {
|
||||
"DEFAULT": {
|
||||
"neutron_host_id": fake_host_id,
|
||||
"neutron_vnic_type": fake_vnic_type,
|
||||
"neutron_physical_net_name": fake_physical_net,
|
||||
}
|
||||
}
|
||||
self.bind_plugin = self._get_neutron_network_plugin_instance(
|
||||
config_data)
|
||||
self.mock_object(self.bind_plugin.neutron_api, "get_network",
|
||||
mock.Mock(return_value=fake_network))
|
||||
self.mock_object(self.bind_plugin.neutron_api, "bind_port_to_host")
|
||||
self.mock_object(self.bind_plugin.db, "network_allocation_create")
|
||||
|
||||
self.mock_object(self.bind_plugin.db,
|
||||
"network_allocations_get_for_share_server",
|
||||
mock.Mock(return_value=[fake_alloc]))
|
||||
|
||||
neutron_api = self.bind_plugin.neutron_api
|
||||
db_api = self.bind_plugin.db
|
||||
self.mock_object(neutron_api, "activate_port_binding")
|
||||
self.mock_object(neutron_api, "delete_port_binding")
|
||||
self.mock_object(db_api, "network_allocation_update")
|
||||
self.mock_object(db_api, "network_allocation_delete")
|
||||
self.mock_object(db_api, "share_network_subnet_update")
|
||||
|
||||
self.bind_plugin.cutover_network_allocations(
|
||||
self.fake_context, fake_old_ss)
|
||||
neutron_api.activate_port_binding.assert_called_once_with(
|
||||
fake_port_id, fake_host_id)
|
||||
neutron_api.delete_port_binding.assert_called_once_with(
|
||||
fake_port_id, fake_old_host)
|
||||
|
||||
@ddt.data({
|
||||
'neutron_binding_profiles': None,
|
||||
'binding_profiles': {}
|
||||
|
@ -0,0 +1,7 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Added support for share server migrations between different physical
|
||||
networks. It is achieved by creating an inactive port binding on the target
|
||||
host during share server migration, then cut it over to target host during
|
||||
migration-complete step.
|
Loading…
Reference in New Issue
Block a user