c7ac488a5f
now all tests pass with tox -epy34 Depends-On: I5894485e55c04a8ca69825128798227714550c9d Change-Id: I719a6cddcbe7f2b7a15bcd35375075affc2513b8
733 lines
33 KiB
Python
733 lines
33 KiB
Python
# Copyright 2013 VMware, Inc.
|
|
# 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 sys
|
|
import time
|
|
|
|
import mock
|
|
from neutron_lib import constants
|
|
from neutron_lib import exceptions as n_exc
|
|
from oslo_config import cfg
|
|
from oslo_log import log
|
|
from oslo_serialization import jsonutils
|
|
|
|
from neutron import context
|
|
from neutron.extensions import l3
|
|
from neutron.tests import base
|
|
from neutron.tests.unit.api.v2 import test_base
|
|
from neutron.tests.unit import testlib_api
|
|
|
|
from vmware_nsx.api_client import client
|
|
from vmware_nsx.api_client import exception as api_exc
|
|
from vmware_nsx.api_client import version
|
|
from vmware_nsx.common import sync
|
|
from vmware_nsx.db import db
|
|
from vmware_nsx import nsx_cluster as cluster
|
|
from vmware_nsx.nsxlib import mh as nsxlib
|
|
from vmware_nsx import plugin
|
|
from vmware_nsx.tests import unit as vmware
|
|
from vmware_nsx.tests.unit.nsx_mh.apiclient import fake
|
|
|
|
LOG = log.getLogger(__name__)
|
|
|
|
_uuid = test_base._uuid
|
|
LSWITCHES = [{'uuid': _uuid(), 'name': 'ls-1'},
|
|
{'uuid': _uuid(), 'name': 'ls-2'}]
|
|
LSWITCHPORTS = [{'uuid': _uuid(), 'name': 'lp-1'},
|
|
{'uuid': _uuid(), 'name': 'lp-2'}]
|
|
LROUTERS = [{'uuid': _uuid(), 'name': 'lr-1'},
|
|
{'uuid': _uuid(), 'name': 'lr-2'}]
|
|
|
|
|
|
class CacheTestCase(base.BaseTestCase):
|
|
"""Test suite providing coverage for the Cache class."""
|
|
|
|
def setUp(self):
|
|
self.nsx_cache = sync.NsxCache()
|
|
for lswitch in LSWITCHES:
|
|
self.nsx_cache._uuid_dict_mappings[lswitch['uuid']] = (
|
|
self.nsx_cache._lswitches)
|
|
self.nsx_cache._lswitches[lswitch['uuid']] = (
|
|
{'data': lswitch,
|
|
'hash': hash(jsonutils.dumps(lswitch))})
|
|
for lswitchport in LSWITCHPORTS:
|
|
self.nsx_cache._uuid_dict_mappings[lswitchport['uuid']] = (
|
|
self.nsx_cache._lswitchports)
|
|
self.nsx_cache._lswitchports[lswitchport['uuid']] = (
|
|
{'data': lswitchport,
|
|
'hash': hash(jsonutils.dumps(lswitchport))})
|
|
for lrouter in LROUTERS:
|
|
self.nsx_cache._uuid_dict_mappings[lrouter['uuid']] = (
|
|
self.nsx_cache._lrouters)
|
|
self.nsx_cache._lrouters[lrouter['uuid']] = (
|
|
{'data': lrouter,
|
|
'hash': hash(jsonutils.dumps(lrouter))})
|
|
super(CacheTestCase, self).setUp()
|
|
|
|
def test_get_lswitches(self):
|
|
ls_uuids = self.nsx_cache.get_lswitches()
|
|
self.assertEqual(set(ls_uuids),
|
|
set([ls['uuid'] for ls in LSWITCHES]))
|
|
|
|
def test_get_lswitchports(self):
|
|
lp_uuids = self.nsx_cache.get_lswitchports()
|
|
self.assertEqual(set(lp_uuids),
|
|
set([lp['uuid'] for lp in LSWITCHPORTS]))
|
|
|
|
def test_get_lrouters(self):
|
|
lr_uuids = self.nsx_cache.get_lrouters()
|
|
self.assertEqual(set(lr_uuids),
|
|
set([lr['uuid'] for lr in LROUTERS]))
|
|
|
|
def test_get_lswitches_changed_only(self):
|
|
ls_uuids = self.nsx_cache.get_lswitches(changed_only=True)
|
|
self.assertEqual(0, len(ls_uuids))
|
|
|
|
def test_get_lswitchports_changed_only(self):
|
|
lp_uuids = self.nsx_cache.get_lswitchports(changed_only=True)
|
|
self.assertEqual(0, len(lp_uuids))
|
|
|
|
def test_get_lrouters_changed_only(self):
|
|
lr_uuids = self.nsx_cache.get_lrouters(changed_only=True)
|
|
self.assertEqual(0, len(lr_uuids))
|
|
|
|
def _verify_update(self, new_resource, changed=True, hit=True):
|
|
cached_resource = self.nsx_cache[new_resource['uuid']]
|
|
self.assertEqual(new_resource, cached_resource['data'])
|
|
self.assertEqual(hit, cached_resource.get('hit', False))
|
|
self.assertEqual(changed,
|
|
cached_resource.get('changed', False))
|
|
|
|
def test_update_lswitch_new_item(self):
|
|
new_switch_uuid = _uuid()
|
|
new_switch = {'uuid': new_switch_uuid, 'name': 'new_switch'}
|
|
self.nsx_cache.update_lswitch(new_switch)
|
|
self.assertIn(new_switch_uuid, self.nsx_cache._lswitches.keys())
|
|
self._verify_update(new_switch)
|
|
|
|
def test_update_lswitch_existing_item(self):
|
|
switch = LSWITCHES[0]
|
|
switch['name'] = 'new_name'
|
|
self.nsx_cache.update_lswitch(switch)
|
|
self.assertIn(switch['uuid'], self.nsx_cache._lswitches.keys())
|
|
self._verify_update(switch)
|
|
|
|
def test_update_lswitchport_new_item(self):
|
|
new_switchport_uuid = _uuid()
|
|
new_switchport = {'uuid': new_switchport_uuid,
|
|
'name': 'new_switchport'}
|
|
self.nsx_cache.update_lswitchport(new_switchport)
|
|
self.assertIn(new_switchport_uuid,
|
|
self.nsx_cache._lswitchports.keys())
|
|
self._verify_update(new_switchport)
|
|
|
|
def test_update_lswitchport_existing_item(self):
|
|
switchport = LSWITCHPORTS[0]
|
|
switchport['name'] = 'new_name'
|
|
self.nsx_cache.update_lswitchport(switchport)
|
|
self.assertIn(switchport['uuid'],
|
|
self.nsx_cache._lswitchports.keys())
|
|
self._verify_update(switchport)
|
|
|
|
def test_update_lrouter_new_item(self):
|
|
new_router_uuid = _uuid()
|
|
new_router = {'uuid': new_router_uuid,
|
|
'name': 'new_router'}
|
|
self.nsx_cache.update_lrouter(new_router)
|
|
self.assertIn(new_router_uuid,
|
|
self.nsx_cache._lrouters.keys())
|
|
self._verify_update(new_router)
|
|
|
|
def test_update_lrouter_existing_item(self):
|
|
router = LROUTERS[0]
|
|
router['name'] = 'new_name'
|
|
self.nsx_cache.update_lrouter(router)
|
|
self.assertIn(router['uuid'],
|
|
self.nsx_cache._lrouters.keys())
|
|
self._verify_update(router)
|
|
|
|
def test_process_updates_initial(self):
|
|
# Clear cache content to simulate first-time filling
|
|
self.nsx_cache._lswitches.clear()
|
|
self.nsx_cache._lswitchports.clear()
|
|
self.nsx_cache._lrouters.clear()
|
|
self.nsx_cache.process_updates(LSWITCHES, LROUTERS, LSWITCHPORTS)
|
|
for resource in LSWITCHES + LROUTERS + LSWITCHPORTS:
|
|
self._verify_update(resource)
|
|
|
|
def test_process_updates_no_change(self):
|
|
self.nsx_cache.process_updates(LSWITCHES, LROUTERS, LSWITCHPORTS)
|
|
for resource in LSWITCHES + LROUTERS + LSWITCHPORTS:
|
|
self._verify_update(resource, changed=False)
|
|
|
|
def test_process_updates_with_changes(self):
|
|
LSWITCHES[0]['name'] = 'altered'
|
|
self.nsx_cache.process_updates(LSWITCHES, LROUTERS, LSWITCHPORTS)
|
|
for resource in LSWITCHES + LROUTERS + LSWITCHPORTS:
|
|
changed = (True if resource['uuid'] == LSWITCHES[0]['uuid']
|
|
else False)
|
|
self._verify_update(resource, changed=changed)
|
|
|
|
def _test_process_updates_with_removals(self):
|
|
lswitches = LSWITCHES[:]
|
|
lswitch = lswitches.pop()
|
|
self.nsx_cache.process_updates(lswitches, LROUTERS, LSWITCHPORTS)
|
|
for resource in LSWITCHES + LROUTERS + LSWITCHPORTS:
|
|
hit = (False if resource['uuid'] == lswitch['uuid']
|
|
else True)
|
|
self._verify_update(resource, changed=False, hit=hit)
|
|
return (lswitch, lswitches)
|
|
|
|
def test_process_updates_with_removals(self):
|
|
self._test_process_updates_with_removals()
|
|
|
|
def test_process_updates_cleanup_after_delete(self):
|
|
deleted_lswitch, lswitches = self._test_process_updates_with_removals()
|
|
self.nsx_cache.process_deletes()
|
|
self.nsx_cache.process_updates(lswitches, LROUTERS, LSWITCHPORTS)
|
|
self.assertNotIn(deleted_lswitch['uuid'], self.nsx_cache._lswitches)
|
|
|
|
def test_update_resource_does_not_cleanup_deleted_resources(self):
|
|
deleted_lswitch, lswitches = self._test_process_updates_with_removals()
|
|
self.nsx_cache.process_deletes()
|
|
self.nsx_cache.update_lswitch(deleted_lswitch)
|
|
self.assertIn(deleted_lswitch['uuid'], self.nsx_cache._lswitches)
|
|
|
|
def _verify_delete(self, resource, deleted=True, hit=True):
|
|
cached_resource = self.nsx_cache[resource['uuid']]
|
|
data_field = 'data_bk' if deleted else 'data'
|
|
self.assertEqual(resource, cached_resource[data_field])
|
|
self.assertEqual(hit, cached_resource.get('hit', False))
|
|
self.assertEqual(deleted,
|
|
cached_resource.get('changed', False))
|
|
|
|
def _set_hit(self, resources, uuid_to_delete=None):
|
|
for resource in resources:
|
|
if resource['data']['uuid'] != uuid_to_delete:
|
|
resource['hit'] = True
|
|
|
|
def test_process_deletes_no_change(self):
|
|
# Mark all resources as hit
|
|
self._set_hit(self.nsx_cache._lswitches.values())
|
|
self._set_hit(self.nsx_cache._lswitchports.values())
|
|
self._set_hit(self.nsx_cache._lrouters.values())
|
|
self.nsx_cache.process_deletes()
|
|
for resource in LSWITCHES + LROUTERS + LSWITCHPORTS:
|
|
self._verify_delete(resource, hit=False, deleted=False)
|
|
|
|
def test_process_deletes_with_removals(self):
|
|
# Mark all resources but one as hit
|
|
uuid_to_delete = LSWITCHPORTS[0]['uuid']
|
|
self._set_hit(self.nsx_cache._lswitches.values(),
|
|
uuid_to_delete)
|
|
self._set_hit(self.nsx_cache._lswitchports.values(),
|
|
uuid_to_delete)
|
|
self._set_hit(self.nsx_cache._lrouters.values(),
|
|
uuid_to_delete)
|
|
self.nsx_cache.process_deletes()
|
|
for resource in LSWITCHES + LROUTERS + LSWITCHPORTS:
|
|
deleted = resource['uuid'] == uuid_to_delete
|
|
self._verify_delete(resource, hit=False, deleted=deleted)
|
|
|
|
|
|
class SyncLoopingCallTestCase(base.BaseTestCase):
|
|
|
|
def test_looping_calls(self):
|
|
# Avoid runs of the synchronization process - just start
|
|
# the looping call
|
|
with mock.patch.object(
|
|
sync.NsxSynchronizer, '_synchronize_state', return_value=0.01,):
|
|
synchronizer = sync.NsxSynchronizer(mock.ANY, mock.ANY,
|
|
100, 0, 0,
|
|
initial_delay=0)
|
|
time.sleep(0.03)
|
|
# stop looping call before asserting
|
|
synchronizer._sync_looping_call.stop()
|
|
# Just verify the looping call has been called, trying
|
|
# to assess the exact number of calls would be unreliable
|
|
self.assertTrue(synchronizer._synchronize_state.call_count)
|
|
|
|
|
|
class SyncTestCase(testlib_api.SqlTestCase):
|
|
|
|
def setUp(self):
|
|
# mock api client
|
|
self.fc = fake.FakeClient(vmware.STUBS_PATH)
|
|
mock_api = mock.patch(vmware.NSXAPI_NAME, autospec=True)
|
|
# Avoid runs of the synchronizer looping call
|
|
# These unit tests will excplicitly invoke synchronization
|
|
patch_sync = mock.patch.object(sync, '_start_loopingcall')
|
|
self.mock_api = mock_api.start()
|
|
patch_sync.start()
|
|
self.mock_api.return_value.login.return_value = "the_cookie"
|
|
# Emulate tests against NSX 3.x
|
|
self.mock_api.return_value.get_version.return_value = (
|
|
version.Version("3.1"))
|
|
|
|
self.mock_api.return_value.request.side_effect = self.fc.fake_request
|
|
self.fake_cluster = cluster.NSXCluster(
|
|
name='fake-cluster', nsx_controllers=['1.1.1.1:999'],
|
|
default_tz_uuid=_uuid(), nsx_user='foo', nsx_password='bar')
|
|
self.fake_cluster.api_client = client.NsxApiClient(
|
|
('1.1.1.1', '999', True),
|
|
self.fake_cluster.nsx_user, self.fake_cluster.nsx_password,
|
|
http_timeout=self.fake_cluster.http_timeout,
|
|
retries=self.fake_cluster.retries,
|
|
redirects=self.fake_cluster.redirects)
|
|
# Instantiate Neutron plugin
|
|
# and setup needed config variables
|
|
args = ['--config-file', vmware.get_fake_conf('neutron.conf.test'),
|
|
'--config-file', vmware.get_fake_conf('nsx.ini.test')]
|
|
self.config_parse(args=args)
|
|
cfg.CONF.set_override('allow_overlapping_ips', True)
|
|
self._plugin = plugin.NsxPlugin()
|
|
# Mock neutron manager plugin load functions to speed up tests
|
|
mock_nm_get_plugin = mock.patch('neutron.manager.NeutronManager.'
|
|
'get_plugin')
|
|
mock_nm_get_service_plugins = mock.patch(
|
|
'neutron.manager.NeutronManager.get_service_plugins')
|
|
self.mock_nm_get_plugin = mock_nm_get_plugin.start()
|
|
self.mock_nm_get_plugin.return_value = self._plugin
|
|
mock_nm_get_service_plugins.start()
|
|
super(SyncTestCase, self).setUp()
|
|
self.addCleanup(self.fc.reset_all)
|
|
|
|
@contextlib.contextmanager
|
|
def _populate_data(self, ctx, net_size=2, port_size=2, router_size=2):
|
|
|
|
def network(idx):
|
|
return {'network': {'name': 'net-%s' % idx,
|
|
'admin_state_up': True,
|
|
'shared': False,
|
|
'port_security_enabled': True,
|
|
'tenant_id': 'foo'}}
|
|
|
|
def subnet(idx, net_id):
|
|
return {'subnet':
|
|
{'cidr': '10.10.%s.0/24' % idx,
|
|
'name': 'sub-%s' % idx,
|
|
'gateway_ip': constants.ATTR_NOT_SPECIFIED,
|
|
'allocation_pools': constants.ATTR_NOT_SPECIFIED,
|
|
'ip_version': 4,
|
|
'dns_nameservers': constants.ATTR_NOT_SPECIFIED,
|
|
'host_routes': constants.ATTR_NOT_SPECIFIED,
|
|
'enable_dhcp': True,
|
|
'network_id': net_id,
|
|
'tenant_id': 'foo'}}
|
|
|
|
def port(idx, net_id):
|
|
return {'port': {'network_id': net_id,
|
|
'name': 'port-%s' % idx,
|
|
'admin_state_up': True,
|
|
'device_id': 'miao',
|
|
'device_owner': 'bau',
|
|
'fixed_ips': constants.ATTR_NOT_SPECIFIED,
|
|
'mac_address': constants.ATTR_NOT_SPECIFIED,
|
|
'tenant_id': 'foo'}}
|
|
|
|
def router(idx):
|
|
# Use random uuids as names
|
|
return {'router': {'name': 'rtr-%s' % idx,
|
|
'admin_state_up': True,
|
|
'tenant_id': 'foo'}}
|
|
|
|
networks = []
|
|
ports = []
|
|
routers = []
|
|
for i in range(net_size):
|
|
net = self._plugin.create_network(ctx, network(i))
|
|
networks.append(net)
|
|
self._plugin.create_subnet(ctx, subnet(i, net['id']))
|
|
for j in range(port_size):
|
|
ports.append(self._plugin.create_port(
|
|
ctx, port("%s-%s" % (i, j), net['id'])))
|
|
for i in range(router_size):
|
|
routers.append(self._plugin.create_router(ctx, router(i)))
|
|
# Do not return anything as the user does need the actual
|
|
# data created
|
|
yield
|
|
|
|
# Remove everything
|
|
for router in routers:
|
|
self._plugin.delete_router(ctx, router['id'])
|
|
for port in ports:
|
|
self._plugin.delete_port(ctx, port['id'])
|
|
# This will remove networks and subnets
|
|
for network in networks:
|
|
self._plugin.delete_network(ctx, network['id'])
|
|
|
|
def _get_tag_dict(self, tags):
|
|
return dict((tag['scope'], tag['tag']) for tag in tags)
|
|
|
|
def _test_sync(self, exp_net_status,
|
|
exp_port_status, exp_router_status,
|
|
action_callback=None, sp=None):
|
|
ls_uuid = list(self.fc._fake_lswitch_dict)[0]
|
|
neutron_net_id = self._get_tag_dict(
|
|
self.fc._fake_lswitch_dict[ls_uuid]['tags'])['quantum_net_id']
|
|
lp_uuid = list(self.fc._fake_lswitch_lport_dict)[0]
|
|
neutron_port_id = self._get_tag_dict(
|
|
self.fc._fake_lswitch_lport_dict[lp_uuid]['tags'])['q_port_id']
|
|
lr_uuid = list(self.fc._fake_lrouter_dict)[0]
|
|
neutron_rtr_id = self._get_tag_dict(
|
|
self.fc._fake_lrouter_dict[lr_uuid]['tags'])['q_router_id']
|
|
if action_callback:
|
|
action_callback(ls_uuid, lp_uuid, lr_uuid)
|
|
# Make chunk big enough to read everything
|
|
if not sp:
|
|
sp = sync.SyncParameters(100)
|
|
self._plugin._synchronizer._synchronize_state(sp)
|
|
# Verify element is in expected status
|
|
# TODO(salv-orlando): Verify status for all elements
|
|
ctx = context.get_admin_context()
|
|
neutron_net = self._plugin.get_network(ctx, neutron_net_id)
|
|
neutron_port = self._plugin.get_port(ctx, neutron_port_id)
|
|
neutron_rtr = self._plugin.get_router(ctx, neutron_rtr_id)
|
|
self.assertEqual(exp_net_status, neutron_net['status'])
|
|
self.assertEqual(exp_port_status, neutron_port['status'])
|
|
self.assertEqual(exp_router_status, neutron_rtr['status'])
|
|
|
|
def _action_callback_status_down(self, ls_uuid, lp_uuid, lr_uuid):
|
|
self.fc._fake_lswitch_dict[ls_uuid]['status'] = 'false'
|
|
self.fc._fake_lswitch_lport_dict[lp_uuid]['status'] = 'false'
|
|
self.fc._fake_lrouter_dict[lr_uuid]['status'] = 'false'
|
|
|
|
def test_initial_sync(self):
|
|
ctx = context.get_admin_context()
|
|
with self._populate_data(ctx):
|
|
self._test_sync(
|
|
constants.NET_STATUS_ACTIVE,
|
|
constants.PORT_STATUS_ACTIVE,
|
|
constants.NET_STATUS_ACTIVE)
|
|
|
|
def test_initial_sync_with_resources_down(self):
|
|
ctx = context.get_admin_context()
|
|
with self._populate_data(ctx):
|
|
self._test_sync(
|
|
constants.NET_STATUS_DOWN, constants.PORT_STATUS_DOWN,
|
|
constants.NET_STATUS_DOWN, self._action_callback_status_down)
|
|
|
|
def test_resync_with_resources_down(self):
|
|
if sys.version_info >= (3, 0):
|
|
# FIXME(arosen): this does not fail with an error...
|
|
self.skipTest('not supported')
|
|
|
|
ctx = context.get_admin_context()
|
|
with self._populate_data(ctx):
|
|
sp = sync.SyncParameters(100)
|
|
self._plugin._synchronizer._synchronize_state(sp)
|
|
# Ensure the synchronizer performs a resync
|
|
sp.init_sync_performed = True
|
|
self._test_sync(
|
|
constants.NET_STATUS_DOWN, constants.PORT_STATUS_DOWN,
|
|
constants.NET_STATUS_DOWN, self._action_callback_status_down,
|
|
sp=sp)
|
|
|
|
def _action_callback_del_resource(self, ls_uuid, lp_uuid, lr_uuid):
|
|
del self.fc._fake_lswitch_dict[ls_uuid]
|
|
del self.fc._fake_lswitch_lport_dict[lp_uuid]
|
|
del self.fc._fake_lrouter_dict[lr_uuid]
|
|
|
|
def test_initial_sync_with_resources_removed(self):
|
|
if sys.version_info >= (3, 0):
|
|
# FIXME(arosen): this does not fail with an error...
|
|
self.skipTest('not supported')
|
|
|
|
ctx = context.get_admin_context()
|
|
with self._populate_data(ctx):
|
|
self._test_sync(
|
|
constants.NET_STATUS_ERROR, constants.PORT_STATUS_ERROR,
|
|
constants.NET_STATUS_ERROR, self._action_callback_del_resource)
|
|
|
|
def test_resync_with_resources_removed(self):
|
|
if sys.version_info >= (3, 0):
|
|
# FIXME(arosen): this does not fail with an error...
|
|
self.skipTest('not supported')
|
|
|
|
ctx = context.get_admin_context()
|
|
with self._populate_data(ctx):
|
|
sp = sync.SyncParameters(100)
|
|
self._plugin._synchronizer._synchronize_state(sp)
|
|
# Ensure the synchronizer performs a resync
|
|
sp.init_sync_performed = True
|
|
self._test_sync(
|
|
constants.NET_STATUS_ERROR, constants.PORT_STATUS_ERROR,
|
|
constants.NET_STATUS_ERROR, self._action_callback_del_resource,
|
|
sp=sp)
|
|
|
|
def _test_sync_with_chunk_larger_maxpagesize(
|
|
self, net_size, port_size, router_size, chunk_size, exp_calls):
|
|
ctx = context.get_admin_context()
|
|
real_func = nsxlib.get_single_query_page
|
|
sp = sync.SyncParameters(chunk_size)
|
|
with self._populate_data(ctx, net_size=net_size,
|
|
port_size=port_size,
|
|
router_size=router_size):
|
|
with mock.patch.object(sync, 'MAX_PAGE_SIZE', 15):
|
|
# The following mock is just for counting calls,
|
|
# but we will still run the actual function
|
|
with mock.patch.object(
|
|
nsxlib, 'get_single_query_page',
|
|
side_effect=real_func) as mock_get_page:
|
|
self._test_sync(
|
|
constants.NET_STATUS_ACTIVE,
|
|
constants.PORT_STATUS_ACTIVE,
|
|
constants.NET_STATUS_ACTIVE,
|
|
sp=sp)
|
|
# As each resource type does not exceed the maximum page size,
|
|
# the method should be called once for each resource type
|
|
self.assertEqual(exp_calls, mock_get_page.call_count)
|
|
|
|
def test_sync_chunk_larger_maxpagesize_no_multiple_requests(self):
|
|
# total resource size = 20
|
|
# total size for each resource does not exceed max page size (15)
|
|
self._test_sync_with_chunk_larger_maxpagesize(
|
|
net_size=5, port_size=2, router_size=5,
|
|
chunk_size=20, exp_calls=3)
|
|
|
|
def test_sync_chunk_larger_maxpagesize_triggers_multiple_requests(self):
|
|
# total resource size = 48
|
|
# total size for each resource does exceed max page size (15)
|
|
self._test_sync_with_chunk_larger_maxpagesize(
|
|
net_size=16, port_size=1, router_size=16,
|
|
chunk_size=48, exp_calls=6)
|
|
|
|
def test_sync_multi_chunk(self):
|
|
# The fake NSX API client cannot be used for this test
|
|
ctx = context.get_admin_context()
|
|
# Generate 4 networks, 1 port per network, and 4 routers
|
|
with self._populate_data(ctx, net_size=4, port_size=1, router_size=4):
|
|
fake_lswitches = jsonutils.loads(
|
|
self.fc.handle_get('/ws.v1/lswitch'))['results']
|
|
fake_lrouters = jsonutils.loads(
|
|
self.fc.handle_get('/ws.v1/lrouter'))['results']
|
|
fake_lswitchports = jsonutils.loads(
|
|
self.fc.handle_get('/ws.v1/lswitch/*/lport'))['results']
|
|
return_values = [
|
|
# Chunk 0 - lswitches
|
|
(fake_lswitches, None, 4),
|
|
# Chunk 0 - lrouters
|
|
(fake_lrouters[:2], 'xxx', 4),
|
|
# Chunk 0 - lports (size only)
|
|
([], 'start', 4),
|
|
# Chunk 1 - lrouters (2 more) (lswitches are skipped)
|
|
(fake_lrouters[2:], None, None),
|
|
# Chunk 1 - lports
|
|
(fake_lswitchports, None, 4)]
|
|
|
|
def fake_fetch_data(*args, **kwargs):
|
|
return return_values.pop(0)
|
|
|
|
# 2 Chunks, with 6 resources each.
|
|
# 1st chunk lswitches and lrouters
|
|
# 2nd chunk lrouters and lports
|
|
# Mock _fetch_data
|
|
with mock.patch.object(
|
|
self._plugin._synchronizer, '_fetch_data',
|
|
side_effect=fake_fetch_data):
|
|
sp = sync.SyncParameters(6)
|
|
|
|
def do_chunk(chunk_idx, ls_cursor, lr_cursor, lp_cursor):
|
|
self._plugin._synchronizer._synchronize_state(sp)
|
|
self.assertEqual(chunk_idx, sp.current_chunk)
|
|
self.assertEqual(ls_cursor, sp.ls_cursor)
|
|
self.assertEqual(lr_cursor, sp.lr_cursor)
|
|
self.assertEqual(lp_cursor, sp.lp_cursor)
|
|
|
|
# check 1st chunk
|
|
do_chunk(1, None, 'xxx', 'start')
|
|
# check 2nd chunk
|
|
do_chunk(0, None, None, None)
|
|
# Chunk size should have stayed the same
|
|
self.assertEqual(sp.chunk_size, 6)
|
|
|
|
def test_synchronize_network(self):
|
|
ctx = context.get_admin_context()
|
|
with self._populate_data(ctx):
|
|
# Put a network down to verify synchronization
|
|
ls_uuid = list(self.fc._fake_lswitch_dict)[0]
|
|
q_net_id = self._get_tag_dict(
|
|
self.fc._fake_lswitch_dict[ls_uuid]['tags'])['quantum_net_id']
|
|
self.fc._fake_lswitch_dict[ls_uuid]['status'] = 'false'
|
|
q_net_data = self._plugin._get_network(ctx, q_net_id)
|
|
self._plugin._synchronizer.synchronize_network(ctx, q_net_data)
|
|
# Reload from db
|
|
q_nets = self._plugin.get_networks(ctx)
|
|
for q_net in q_nets:
|
|
if q_net['id'] == q_net_id:
|
|
exp_status = constants.NET_STATUS_DOWN
|
|
else:
|
|
exp_status = constants.NET_STATUS_ACTIVE
|
|
self.assertEqual(exp_status, q_net['status'])
|
|
|
|
def test_synchronize_network_not_found_in_db_no_raise(self):
|
|
ctx = context.get_admin_context()
|
|
with self._populate_data(ctx):
|
|
# Put a network down to verify synchronization
|
|
ls_uuid = list(self.fc._fake_lswitch_dict)[0]
|
|
q_net_id = self._get_tag_dict(
|
|
self.fc._fake_lswitch_dict[ls_uuid]['tags'])['quantum_net_id']
|
|
self.fc._fake_lswitch_dict[ls_uuid]['status'] = 'false'
|
|
q_net_data = self._plugin._get_network(ctx, q_net_id)
|
|
with mock.patch.object(self._plugin,
|
|
'_get_network') as _get_network:
|
|
_get_network.side_effect = n_exc.NetworkNotFound(
|
|
net_id=q_net_data['id'])
|
|
self._plugin._synchronizer.synchronize_network(ctx, q_net_data)
|
|
|
|
def test_synchronize_network_on_get(self):
|
|
cfg.CONF.set_override('always_read_status', True, 'NSX_SYNC')
|
|
ctx = context.get_admin_context()
|
|
with self._populate_data(ctx):
|
|
# Put a network down to verify punctual synchronization
|
|
ls_uuid = list(self.fc._fake_lswitch_dict)[0]
|
|
q_net_id = self._get_tag_dict(
|
|
self.fc._fake_lswitch_dict[ls_uuid]['tags'])['quantum_net_id']
|
|
self.fc._fake_lswitch_dict[ls_uuid]['status'] = 'false'
|
|
q_net_data = self._plugin.get_network(ctx, q_net_id)
|
|
self.assertEqual(constants.NET_STATUS_DOWN, q_net_data['status'])
|
|
|
|
def test_synchronize_port_not_found_in_db_no_raise(self):
|
|
ctx = context.get_admin_context()
|
|
with self._populate_data(ctx):
|
|
# Put a port down to verify synchronization
|
|
lp_uuid = list(self.fc._fake_lswitch_lport_dict)[0]
|
|
lport = self.fc._fake_lswitch_lport_dict[lp_uuid]
|
|
q_port_id = self._get_tag_dict(lport['tags'])['q_port_id']
|
|
lport['status'] = 'true'
|
|
q_port_data = self._plugin._get_port(ctx, q_port_id)
|
|
with mock.patch.object(self._plugin,
|
|
'_get_port') as _get_port:
|
|
_get_port.side_effect = n_exc.PortNotFound(
|
|
port_id=q_port_data['id'])
|
|
self._plugin._synchronizer.synchronize_port(ctx, q_port_data)
|
|
|
|
def test_synchronize_port(self):
|
|
ctx = context.get_admin_context()
|
|
with self._populate_data(ctx):
|
|
# Put a port down to verify synchronization
|
|
lp_uuid = list(self.fc._fake_lswitch_lport_dict)[0]
|
|
lport = self.fc._fake_lswitch_lport_dict[lp_uuid]
|
|
q_port_id = self._get_tag_dict(lport['tags'])['q_port_id']
|
|
lport['status'] = 'true'
|
|
q_port_data = self._plugin._get_port(ctx, q_port_id)
|
|
self._plugin._synchronizer.synchronize_port(ctx, q_port_data)
|
|
# Reload from db
|
|
q_ports = self._plugin.get_ports(ctx)
|
|
for q_port in q_ports:
|
|
if q_port['id'] == q_port_id:
|
|
exp_status = constants.PORT_STATUS_ACTIVE
|
|
else:
|
|
exp_status = constants.PORT_STATUS_DOWN
|
|
self.assertEqual(exp_status, q_port['status'])
|
|
|
|
def test_synchronize_port_on_get(self):
|
|
cfg.CONF.set_override('always_read_status', True, 'NSX_SYNC')
|
|
ctx = context.get_admin_context()
|
|
with self._populate_data(ctx):
|
|
# Put a port down to verify punctual synchronization
|
|
lp_uuid = list(self.fc._fake_lswitch_lport_dict)[0]
|
|
lport = self.fc._fake_lswitch_lport_dict[lp_uuid]
|
|
q_port_id = self._get_tag_dict(lport['tags'])['q_port_id']
|
|
lport['status'] = 'false'
|
|
q_port_data = self._plugin.get_port(ctx, q_port_id)
|
|
self.assertEqual(constants.PORT_STATUS_DOWN,
|
|
q_port_data['status'])
|
|
|
|
def test_synchronize_routernot_found_in_db_no_raise(self):
|
|
ctx = context.get_admin_context()
|
|
with self._populate_data(ctx):
|
|
# Put a router down to verify synchronization
|
|
lr_uuid = list(self.fc._fake_lrouter_dict)[0]
|
|
q_rtr_id = self._get_tag_dict(
|
|
self.fc._fake_lrouter_dict[lr_uuid]['tags'])['q_router_id']
|
|
self.fc._fake_lrouter_dict[lr_uuid]['status'] = 'false'
|
|
q_rtr_data = self._plugin._get_router(ctx, q_rtr_id)
|
|
with mock.patch.object(self._plugin,
|
|
'_get_router') as _get_router:
|
|
_get_router.side_effect = l3.RouterNotFound(
|
|
router_id=q_rtr_data['id'])
|
|
self._plugin._synchronizer.synchronize_router(ctx, q_rtr_data)
|
|
|
|
def test_synchronize_router(self):
|
|
ctx = context.get_admin_context()
|
|
with self._populate_data(ctx):
|
|
# Put a router down to verify synchronization
|
|
lr_uuid = list(self.fc._fake_lrouter_dict)[0]
|
|
q_rtr_id = self._get_tag_dict(
|
|
self.fc._fake_lrouter_dict[lr_uuid]['tags'])['q_router_id']
|
|
self.fc._fake_lrouter_dict[lr_uuid]['status'] = 'false'
|
|
q_rtr_data = self._plugin._get_router(ctx, q_rtr_id)
|
|
self._plugin._synchronizer.synchronize_router(ctx, q_rtr_data)
|
|
# Reload from db
|
|
q_routers = self._plugin.get_routers(ctx)
|
|
for q_rtr in q_routers:
|
|
if q_rtr['id'] == q_rtr_id:
|
|
exp_status = constants.NET_STATUS_DOWN
|
|
else:
|
|
exp_status = constants.NET_STATUS_ACTIVE
|
|
self.assertEqual(exp_status, q_rtr['status'])
|
|
|
|
def test_synchronize_router_nsx_mapping_not_found(self):
|
|
ctx = context.get_admin_context()
|
|
with self._populate_data(ctx):
|
|
# Put a router down to verify synchronization
|
|
lr_uuid = list(self.fc._fake_lrouter_dict)[0]
|
|
q_rtr_id = self._get_tag_dict(
|
|
self.fc._fake_lrouter_dict[lr_uuid]['tags'])['q_router_id']
|
|
self.fc._fake_lrouter_dict[lr_uuid]['status'] = 'false'
|
|
q_rtr_data = self._plugin._get_router(ctx, q_rtr_id)
|
|
|
|
# delete router mapping from db.
|
|
db.delete_neutron_nsx_router_mapping(ctx.session, q_rtr_id)
|
|
# pop router from fake nsx client
|
|
router_data = self.fc._fake_lrouter_dict.pop(lr_uuid)
|
|
|
|
self._plugin._synchronizer.synchronize_router(ctx, q_rtr_data)
|
|
# Reload from db
|
|
q_routers = self._plugin.get_routers(ctx)
|
|
for q_rtr in q_routers:
|
|
if q_rtr['id'] == q_rtr_id:
|
|
exp_status = constants.NET_STATUS_ERROR
|
|
else:
|
|
exp_status = constants.NET_STATUS_ACTIVE
|
|
self.assertEqual(exp_status, q_rtr['status'])
|
|
# put the router database since we don't handle missing
|
|
# router data in the fake nsx api_client
|
|
self.fc._fake_lrouter_dict[lr_uuid] = router_data
|
|
|
|
def test_synchronize_router_on_get(self):
|
|
cfg.CONF.set_override('always_read_status', True, 'NSX_SYNC')
|
|
ctx = context.get_admin_context()
|
|
with self._populate_data(ctx):
|
|
# Put a router down to verify punctual synchronization
|
|
lr_uuid = list(self.fc._fake_lrouter_dict)[0]
|
|
q_rtr_id = self._get_tag_dict(
|
|
self.fc._fake_lrouter_dict[lr_uuid]['tags'])['q_router_id']
|
|
self.fc._fake_lrouter_dict[lr_uuid]['status'] = 'false'
|
|
q_rtr_data = self._plugin.get_router(ctx, q_rtr_id)
|
|
self.assertEqual(constants.NET_STATUS_DOWN, q_rtr_data['status'])
|
|
|
|
def test_sync_nsx_failure_backoff(self):
|
|
self.mock_api.return_value.request.side_effect = api_exc.RequestTimeout
|
|
# chunk size won't matter here
|
|
sp = sync.SyncParameters(999)
|
|
for i in range(10):
|
|
self.assertEqual(
|
|
min(64, 2 ** i),
|
|
self._plugin._synchronizer._synchronize_state(sp))
|