X-Git-Url: https://osm.etsi.org/gitweb/?a=blobdiff_plain;f=osm_ro%2Fwim%2Ftests%2Ftest_actions.py;fp=osm_ro%2Fwim%2Ftests%2Ftest_actions.py;h=920182bddf5ce2bdb03bb612908c408d16284191;hb=0446cd5df24c38f95cea13b995c553e9b2403f21;hp=0000000000000000000000000000000000000000;hpb=63056c57eea17465ada68bcc076a0159d9c5f93f;p=osm%2FRO.git diff --git a/osm_ro/wim/tests/test_actions.py b/osm_ro/wim/tests/test_actions.py new file mode 100644 index 00000000..920182bd --- /dev/null +++ b/osm_ro/wim/tests/test_actions.py @@ -0,0 +1,366 @@ +# -*- coding: utf-8 -*- +## +# Copyright 2018 University of Bristol - High Performance Networks Research +# Group +# All Rights Reserved. +# +# Contributors: Anderson Bravalheri, Dimitrios Gkounis, Abubakar Siddique +# Muqaddas, Navdeep Uniyal, Reza Nejabati and Dimitra Simeonidou +# +# 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. +# +# For those usages not covered by the Apache License, Version 2.0 please +# contact with: +# +# Neither the name of the University of Bristol nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# This work has been performed in the context of DCMS UK 5G Testbeds +# & Trials Programme and in the framework of the Metro-Haul project - +# funded by the European Commission under Grant number 761727 through the +# Horizon 2020 and 5G-PPP programmes. +## +# pylint: disable=E1101 + +from __future__ import unicode_literals, print_function + +import json +import unittest +from time import time + +from mock import MagicMock, patch + +from . import fixtures as eg +from ...tests.db_helpers import ( + TestCaseWithDatabasePerTest, + disable_foreign_keys, + uuid, +) +from ..persistence import WimPersistence +from ..wan_link_actions import WanLinkCreate, WanLinkDelete +from ..wimconn import WimConnectorError + + +class TestActionsWithDb(TestCaseWithDatabasePerTest): + def setUp(self): + super(TestActionsWithDb, self).setUp() + self.persist = WimPersistence(self.db) + self.connector = MagicMock() + self.ovim = MagicMock() + + +class TestCreate(TestActionsWithDb): + @disable_foreign_keys + def test_process__instance_nets_on_build(self): + # Given we want 1 WAN link between 2 datacenters + # and the local network in each datacenter is still being built + wan_link = eg.instance_wim_nets() + instance_nets = eg.instance_nets(num_datacenters=2, num_links=1) + for net in instance_nets: + net['status'] = 'BUILD' + self.populate([{'instance_nets': instance_nets, + 'instance_wim_nets': wan_link}]) + + # When we try to process a CREATE action that refers to the same + # instance_scenario_id and sce_net_id + now = time() + action = WanLinkCreate(eg.wim_actions('CREATE')[0]) + action.instance_scenario_id = instance_nets[0]['instance_scenario_id'] + action.sce_net_id = instance_nets[0]['sce_net_id'] + # -- ensure it is in the database for updates --> # + action_record = action.as_record() + action_record['extra'] = json.dumps(action_record['extra']) + self.populate([{'vim_wim_actions': action_record}]) + # <-- # + action.process(self.connector, self.persist, self.ovim) + + # Then the action should be defered + assert action.is_scheduled + self.assertEqual(action.extra['attempts'], 1) + self.assertGreater(action.extra['last_attempted_at'], now) + + @disable_foreign_keys + def test_process__instance_nets_on_error(self): + # Given we want 1 WAN link between 2 datacenters + # and at least one local network is in a not good state (error, or + # being deleted) + instance_nets = eg.instance_nets(num_datacenters=2, num_links=1) + instance_nets[1]['status'] = 'SCHEDULED_DELETION' + wan_link = eg.instance_wim_nets() + self.populate([{'instance_nets': instance_nets, + 'instance_wim_nets': wan_link}]) + + # When we try to process a CREATE action that refers to the same + # instance_scenario_id and sce_net_id + action = WanLinkCreate(eg.wim_actions('CREATE')[0]) + action.instance_scenario_id = instance_nets[0]['instance_scenario_id'] + action.sce_net_id = instance_nets[0]['sce_net_id'] + # -- ensure it is in the database for updates --> # + action_record = action.as_record() + action_record['extra'] = json.dumps(action_record['extra']) + self.populate([{'vim_wim_actions': action_record}]) + # <-- # + action.process(self.connector, self.persist, self.ovim) + + # Then the action should fail + assert action.is_failed + self.assertIn('issue with the local networks', action.error_msg) + self.assertIn('SCHEDULED_DELETION', action.error_msg) + + def prepare_create__sdn(self): + db_state = [{'nfvo_tenants': eg.tenant()}] + eg.wim_set() + + instance_nets = eg.instance_nets(num_datacenters=2, num_links=1) + port_mappings = [ + eg.wim_port_mapping(0, 0), + eg.wim_port_mapping(0, 1) + ] + instance_action = eg.instance_action(action_id='ACTION-000') + for i, net in enumerate(instance_nets): + net['status'] = 'ACTIVE' + net['sdn_net_id'] = uuid('sdn-net%d' % i) + + db_state += [{'instance_nets': instance_nets}, + {'instance_wim_nets': eg.instance_wim_nets()}, + {'wim_port_mappings': port_mappings}, + {'instance_actions': instance_action}] + + action = WanLinkCreate( + eg.wim_actions('CREATE', action_id='ACTION-000')[0]) + # --> ensure it is in the database for updates --> # + action_record = action.as_record() + action_record['extra'] = json.dumps(action_record['extra']) + self.populate([{'vim_wim_actions': action_record}]) + + return db_state, action + + @disable_foreign_keys + def test_process__sdn(self): + # Given we want 1 WAN link between 2 datacenters + # and the local network in each datacenter is already created + db_state, action = self.prepare_create__sdn() + self.populate(db_state) + + instance_action = self.persist.get_by_uuid( + 'instance_actions', action.instance_action_id) + number_done = instance_action['number_done'] + number_failed = instance_action['number_failed'] + + connector_patch = patch.object( + self.connector, 'create_connectivity_service', + lambda *_, **__: (uuid('random-id'), None)) + + ovim_patch = patch.object( + self.ovim, 'get_ports', MagicMock(return_value=[{ + 'switch_dpid': 'AA:AA:AA:AA:AA:AA:AA:AA', + 'switch_port': 1, + }])) + + # If the connector works fine + with connector_patch, ovim_patch: + # When we try to process a CREATE action that refers to the same + # instance_scenario_id and sce_net_id + action.process(self.connector, self.persist, self.ovim) + + # Then the action should be succeeded + db_action = self.persist.query_one('vim_wim_actions', WHERE={ + 'instance_action_id': action.instance_action_id, + 'task_index': action.task_index}) + self.assertEqual(db_action['status'], 'DONE') + + instance_action = self.persist.get_by_uuid( + 'instance_actions', action.instance_action_id) + self.assertEqual(instance_action['number_done'], number_done + 1) + self.assertEqual(instance_action['number_failed'], number_failed) + + @disable_foreign_keys + def test_process__sdn_fail(self): + # Given we want 1 WAN link between 2 datacenters + # and the local network in each datacenter is already created + db_state, action = self.prepare_create__sdn() + self.populate(db_state) + + instance_action = self.persist.get_by_uuid( + 'instance_actions', action.instance_action_id) + number_done = instance_action['number_done'] + number_failed = instance_action['number_failed'] + + connector_patch = patch.object( + self.connector, 'create_connectivity_service', + MagicMock(side_effect=WimConnectorError('foobar'))) + + ovim_patch = patch.object( + self.ovim, 'get_ports', MagicMock(return_value=[{ + 'switch_dpid': 'AA:AA:AA:AA:AA:AA:AA:AA', + 'switch_port': 1, + }])) + + # If the connector throws an error + with connector_patch, ovim_patch: + # When we try to process a CREATE action that refers to the same + # instance_scenario_id and sce_net_id + action.process(self.connector, self.persist, self.ovim) + + # Then the action should be fail + db_action = self.persist.query_one('vim_wim_actions', WHERE={ + 'instance_action_id': action.instance_action_id, + 'task_index': action.task_index}) + self.assertEqual(db_action['status'], 'FAILED') + + instance_action = self.persist.get_by_uuid( + 'instance_actions', action.instance_action_id) + self.assertEqual(instance_action['number_done'], number_done) + self.assertEqual(instance_action['number_failed'], number_failed + 1) + + +class TestDelete(TestActionsWithDb): + @disable_foreign_keys + def test_process__no_internal_id(self): + # Given no WAN link was created yet, + # when we try to process a DELETE action, with no wim_internal_id + action = WanLinkDelete(eg.wim_actions('DELETE')[0]) + action.wim_internal_id = None + # -- ensure it is in the database for updates --> # + action_record = action.as_record() + action_record['extra'] = json.dumps(action_record['extra']) + self.populate([{'vim_wim_actions': action_record, + 'instance_wim_nets': eg.instance_wim_nets()}]) + # <-- # + action.process(self.connector, self.persist, self.ovim) + + # Then the action should succeed + assert action.is_done + + def prepare_delete(self): + db_state = [{'nfvo_tenants': eg.tenant()}] + eg.wim_set() + + instance_nets = eg.instance_nets(num_datacenters=2, num_links=1) + port_mappings = [ + eg.wim_port_mapping(0, 0), + eg.wim_port_mapping(0, 1) + ] + instance_action = eg.instance_action(action_id='ACTION-000') + for i, net in enumerate(instance_nets): + net['status'] = 'ACTIVE' + net['sdn_net_id'] = uuid('sdn-net%d' % i) + + db_state += [{'instance_nets': instance_nets}, + {'instance_wim_nets': eg.instance_wim_nets()}, + {'wim_port_mappings': port_mappings}, + {'instance_actions': instance_action}] + + action = WanLinkDelete( + eg.wim_actions('DELETE', action_id='ACTION-000')[0]) + # --> ensure it is in the database for updates --> # + action_record = action.as_record() + action_record['extra'] = json.dumps(action_record['extra']) + self.populate([{'vim_wim_actions': action_record}]) + + return db_state, action + + @disable_foreign_keys + def test_process(self): + # Given we want to delete 1 WAN link between 2 datacenters + db_state, action = self.prepare_delete() + self.populate(db_state) + + instance_action = self.persist.get_by_uuid( + 'instance_actions', action.instance_action_id) + number_done = instance_action['number_done'] + number_failed = instance_action['number_failed'] + + connector_patch = patch.object( + self.connector, 'delete_connectivity_service') + + # If the connector works fine + with connector_patch: + # When we try to process a DELETE action that refers to the same + # instance_scenario_id and sce_net_id + action.process(self.connector, self.persist, self.ovim) + + # Then the action should be succeeded + db_action = self.persist.query_one('vim_wim_actions', WHERE={ + 'instance_action_id': action.instance_action_id, + 'task_index': action.task_index}) + self.assertEqual(db_action['status'], 'DONE') + + instance_action = self.persist.get_by_uuid( + 'instance_actions', action.instance_action_id) + self.assertEqual(instance_action['number_done'], number_done + 1) + self.assertEqual(instance_action['number_failed'], number_failed) + + @disable_foreign_keys + def test_process__wan_link_error(self): + # Given we have a delete action that targets a wan link with an error + db_state, action = self.prepare_delete() + wan_link = [tables for tables in db_state + if tables.get('instance_wim_nets')][0]['instance_wim_nets'] + from pprint import pprint + pprint(wan_link) + wan_link[0]['status'] = 'ERROR' + self.populate(db_state) + + # When we try to process it + action.process(self.connector, self.persist, self.ovim) + + # Then it should fail + assert action.is_failed + + def create_action(self): + action = WanLinkCreate( + eg.wim_actions('CREATE', action_id='ACTION-000')[0]) + # --> ensure it is in the database for updates --> # + action_record = action.as_record() + action_record['extra'] = json.dumps(action_record['extra']) + self.populate([{'vim_wim_actions': action_record}]) + + return action + + @disable_foreign_keys + def test_create_and_delete(self): + # Given a CREATE action was well succeeded + db_state, delete_action = self.prepare_delete() + delete_action.save(self.persist, task_index=1) + self.populate(db_state) + create_action = self.create_action() + + connector_patch = patch.multiple( + self.connector, + delete_connectivity_service=MagicMock(), + create_connectivity_service=( + lambda *_, **__: (uuid('random-id'), None))) + + ovim_patch = patch.object( + self.ovim, 'get_ports', MagicMock(return_value=[{ + 'switch_dpid': 'AA:AA:AA:AA:AA:AA:AA:AA', + 'switch_port': 1, + }])) + + with connector_patch, ovim_patch: + create_action.process(self.connector, self.persist, self.ovim) + + # When we try to process a CREATE action that refers to the same + # instance_scenario_id and sce_net_id + with connector_patch: + delete_action.process(self.connector, self.persist, self.ovim) + + # Then the DELETE action should be successful + db_action = self.persist.query_one('vim_wim_actions', WHERE={ + 'instance_action_id': delete_action.instance_action_id, + 'task_index': delete_action.task_index}) + self.assertEqual(db_action['status'], 'DONE') + + +if __name__ == '__main__': + unittest.main()