211 lines
8.3 KiB
Python
211 lines
8.3 KiB
Python
# Copyright (c) 2015 OpenStack Foundation.
|
|
# 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 copy
|
|
|
|
import mock
|
|
from oslo_config import cfg
|
|
|
|
from dragonflow.controller import topology
|
|
from dragonflow.tests.common import utils
|
|
from dragonflow.tests.unit import test_app_base
|
|
|
|
|
|
def nb_api_get_all_func(*instances):
|
|
"""
|
|
Create an method that can be used to override the mock's nb_api's get_all
|
|
to return objects that should exist, e.g. instances that were created
|
|
with create (and verified with the relevant assert)
|
|
:param instances: An iterable of instances that should exist in nb_api
|
|
:type instances: iterable of instances
|
|
"""
|
|
alls = {}
|
|
for instance in instances:
|
|
try:
|
|
alls[instance.__class__].append(instance)
|
|
except KeyError:
|
|
alls[instance.__class__] = [instance]
|
|
|
|
def nb_api_get_all(inst, topic=None):
|
|
try:
|
|
if not topic:
|
|
return alls[inst]
|
|
return [obj for obj in alls[inst] if obj.topic == topic]
|
|
except KeyError:
|
|
return mock.MagicMock(name='NbApi.get_instance().get()')
|
|
return nb_api_get_all
|
|
|
|
|
|
class TestTopology(test_app_base.DFAppTestBase):
|
|
# This is to comply the current code, as the app_list can't be empty.
|
|
# But we don't need any app in this test, acutally.
|
|
apps_list = ["l2"]
|
|
|
|
def setUp(self):
|
|
cfg.CONF.set_override('enable_selective_topology_distribution',
|
|
True, group='df')
|
|
cfg.CONF.set_override('enable_neutron_notifier', False, group='df')
|
|
super(TestTopology, self).setUp(enable_selective_topo_dist=True)
|
|
# By default, return empty value for all resources, each case can
|
|
# customize the return value on their own.
|
|
self.nb_api.get_all.return_value = []
|
|
self.fake_invalid_ovs_port = copy.deepcopy(
|
|
test_app_base.fake_ovs_port1)
|
|
self.controller._register_models()
|
|
|
|
@utils.with_nb_objects(
|
|
test_app_base.fake_chassis1,
|
|
test_app_base.fake_local_port1,
|
|
test_app_base.fake_logic_switch1,
|
|
)
|
|
def test_vm_port_online_offline(self):
|
|
original_update = self.controller.update
|
|
self.controller.update = mock.Mock()
|
|
self.controller.update.side_effect = original_update
|
|
self.controller._sync._update_cb = self.controller.update
|
|
|
|
original_delete = self.controller.delete
|
|
self.controller.delete = mock.Mock()
|
|
self.controller.delete.side_effect = original_delete
|
|
self.controller._sync._delete_cb = self.controller.delete
|
|
|
|
original_delete_by_id = self.controller.delete_by_id
|
|
self.controller.delete_by_id = mock.Mock()
|
|
self.controller.delete_by_id.side_effect = original_delete_by_id
|
|
|
|
# Verify port online
|
|
self.topology.ovs_port_updated(test_app_base.fake_ovs_port1)
|
|
self.controller.update.assert_has_calls(
|
|
(mock.call(test_app_base.fake_logic_switch1),
|
|
mock.call(test_app_base.fake_local_port1)),
|
|
)
|
|
self.nb_api.subscriber.register_topic.assert_called_once_with(
|
|
test_app_base.fake_local_port1.topic)
|
|
|
|
# Verify port offline
|
|
self.controller.delete.reset_mock()
|
|
self.controller.update.reset_mock()
|
|
self.nb_api.get_all.return_value = []
|
|
self.topology.ovs_port_deleted(test_app_base.fake_ovs_port1)
|
|
self.controller.delete.assert_has_calls([
|
|
mock.call(test_app_base.fake_local_port1),
|
|
mock.call(test_app_base.fake_logic_switch1),
|
|
])
|
|
self.nb_api.subscriber.unregister_topic.assert_called_once_with(
|
|
test_app_base.fake_local_port1.topic)
|
|
|
|
def test_vm_online_after_topology_pulled(self):
|
|
self.nb_api.get_all.side_effect = nb_api_get_all_func(
|
|
test_app_base.fake_logic_switch1,
|
|
test_app_base.fake_local_port1,
|
|
test_app_base.fake_chassis1,
|
|
)
|
|
self.nb_api.get.return_value = test_app_base.fake_local_port1
|
|
# Pull topology by first ovs port online
|
|
self.topology.ovs_port_updated(test_app_base.fake_ovs_port1)
|
|
|
|
# Another port online
|
|
self.nb_api.get_all.side_effect = nb_api_get_all_func(
|
|
test_app_base.fake_logic_switch1,
|
|
test_app_base.fake_local_port1,
|
|
test_app_base.fake_local_port2,
|
|
test_app_base.fake_chassis1,
|
|
)
|
|
self.controller.update = mock.Mock()
|
|
self.nb_api.get.return_value = test_app_base.fake_local_port2
|
|
self.topology.ovs_port_updated(test_app_base.fake_ovs_port2)
|
|
self.controller.update.assert_called_once_with(
|
|
test_app_base.fake_local_port2)
|
|
self.nb_api.subscriber.register_topic.assert_called_once()
|
|
|
|
def test_multi_vm_port_online_restart_controller(self):
|
|
self.nb_api.get_all.side_effect = nb_api_get_all_func(
|
|
test_app_base.fake_logic_switch1,
|
|
test_app_base.fake_local_port1,
|
|
test_app_base.fake_local_port2,
|
|
test_app_base.fake_chassis1,
|
|
)
|
|
|
|
def _get_logical_port(lport):
|
|
lport_id = lport.id
|
|
if lport_id == test_app_base.fake_local_port1.id:
|
|
return test_app_base.fake_local_port1
|
|
if lport_id == test_app_base.fake_local_port2.id:
|
|
return test_app_base.fake_local_port2
|
|
|
|
self.nb_api.get.side_effect = _get_logical_port
|
|
original_update = self.controller.update
|
|
self.controller.update = mock.Mock()
|
|
self.controller.update.side_effect = original_update
|
|
self.controller._sync._update_cb = self.controller.update
|
|
|
|
# The vm ports are online one by one
|
|
self.topology.ovs_port_updated(test_app_base.fake_ovs_port1)
|
|
self.topology.ovs_port_updated(test_app_base.fake_ovs_port2)
|
|
|
|
calls = [mock.call(test_app_base.fake_chassis1),
|
|
mock.call(test_app_base.fake_logic_switch1),
|
|
mock.call(test_app_base.fake_local_port1),
|
|
mock.call(test_app_base.fake_local_port2)]
|
|
self.controller.update.assert_has_calls(
|
|
calls, any_order=True)
|
|
self.assertEqual(4, self.controller.update.call_count)
|
|
self.nb_api.subscriber.register_topic.assert_called_once()
|
|
|
|
@utils.with_local_objects(test_app_base.fake_ovs_port1)
|
|
def test_check_topology_info(self):
|
|
topic = 'fake_tenant1'
|
|
lport_id2 = '2'
|
|
ovs_port_id2 = 'ovs_port2'
|
|
lport_id3 = '3'
|
|
ovs_port_id3 = 'ovs_port3'
|
|
|
|
self.topology.ovs_to_lport_mapping = {
|
|
ovs_port_id2: topology.OvsLportMapping(
|
|
lport_id=lport_id2,
|
|
topic=topic
|
|
),
|
|
ovs_port_id3: topology.OvsLportMapping(
|
|
lport_id=lport_id3,
|
|
topic=topic
|
|
)
|
|
}
|
|
self.topology.topic_subscribed = {
|
|
topic: {lport_id2, lport_id3}
|
|
}
|
|
self.controller.db_store.update(test_app_base.fake_local_port1)
|
|
self.topology.check_topology_info()
|
|
self.assertEqual(1, len(self.topology.topic_subscribed[topic]))
|
|
|
|
def test_db_sync(self):
|
|
self.nb_api.get_all.side_effect = nb_api_get_all_func(
|
|
test_app_base.fake_logic_switch1,
|
|
test_app_base.fake_local_port1)
|
|
|
|
self.topology.topic_subscribed = {
|
|
'fake_tenant1': test_app_base.fake_local_port1}
|
|
update = mock.Mock()
|
|
self.controller._sync._topics = {'fake_tenant1'}
|
|
self.controller._sync._update_cb = update
|
|
|
|
# Verify the db sync will work for topology
|
|
self.controller.sync()
|
|
update_calls = [
|
|
mock.call(test_app_base.fake_logic_switch1),
|
|
mock.call(test_app_base.fake_local_port1)
|
|
]
|
|
update.assert_has_calls(update_calls)
|
|
self.assertEqual(2, update.call_count)
|