Chip Boling | 67b674a | 2019-02-08 11:42:18 -0600 | [diff] [blame^] | 1 | # |
| 2 | # Copyright 2018 the original author or authors. |
| 3 | # |
| 4 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | # you may not use this file except in compliance with the License. |
| 6 | # You may obtain a copy of the License at |
| 7 | # |
| 8 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | # |
| 10 | # Unless required by applicable law or agreed to in writing, software |
| 11 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | # See the License for the specific language governing permissions and |
| 14 | # limitations under the License. |
| 15 | # |
| 16 | from unittest import TestCase, main |
| 17 | from nose.tools import assert_raises |
| 18 | from nose.twistedtools import deferred |
| 19 | from copy import deepcopy |
| 20 | from mock.mock_adapter_agent import MockAdapterAgent, MockCore |
| 21 | from mock.mock_onu_handler import MockOnuHandler |
| 22 | from mock.mock_olt_handler import MockOltHandler |
| 23 | from mock.mock_onu import MockOnu |
| 24 | from pyvoltha.adapters.extensions.omci.openomci_agent import OpenOMCIAgent, OpenOmciAgentDefaults |
| 25 | from pyvoltha.adapters.extensions.omci.omci_defs import * |
| 26 | from pyvoltha.common.utils.asleep import asleep |
| 27 | from pyvoltha.adapters.extensions.omci.database.mib_db_api import DEVICE_ID_KEY, CLASS_ID_KEY, CREATED_KEY, \ |
| 28 | MODIFIED_KEY, MDS_KEY, LAST_SYNC_KEY, VERSION_KEY, DatabaseStateError |
| 29 | from pyvoltha.adapters.extensions.omci.database.mib_db_dict import MibDbVolatileDict |
| 30 | |
| 31 | |
| 32 | DEFAULT_OLT_DEVICE_ID = 'default_olt_mock' |
| 33 | DEFAULT_ONU_DEVICE_ID = 'default_onu_mock' |
| 34 | DEFAULT_PON_ID = 0 |
| 35 | DEFAULT_ONU_ID = 0 |
| 36 | DEFAULT_ONU_SN = 'TEST00000001' |
| 37 | |
| 38 | OP = EntityOperations |
| 39 | RC = ReasonCodes |
| 40 | |
| 41 | |
| 42 | def chunk(indexable, chunk_size): |
| 43 | for i in range(0, len(indexable), chunk_size): |
| 44 | yield indexable[i:i + chunk_size] |
| 45 | |
| 46 | |
| 47 | def hex2raw(hex_string): |
| 48 | return ''.join(chr(int(byte, 16)) for byte in chunk(hex_string, 2)) |
| 49 | |
| 50 | |
| 51 | class TestOnuDeviceEntry(TestCase): |
| 52 | """ |
| 53 | Test the ONU Device Entry methods |
| 54 | """ |
| 55 | def setUp(self): |
| 56 | self.adapter_agent = MockAdapterAgent() |
| 57 | |
| 58 | custom = deepcopy(OpenOmciAgentDefaults) |
| 59 | custom['mib-synchronizer']['database'] = MibDbVolatileDict |
| 60 | |
| 61 | self.agent = OpenOMCIAgent(MockCore, support_classes=custom) |
| 62 | self.agent.start() |
| 63 | |
| 64 | def tearDown(self): |
| 65 | if self.agent is not None: |
| 66 | self.agent.stop() |
| 67 | |
| 68 | if self.adapter_agent is not None: |
| 69 | self.adapter_agent.tearDown() |
| 70 | |
| 71 | def setup_mock_olt(self, device_id=DEFAULT_OLT_DEVICE_ID): |
| 72 | handler = MockOltHandler(self.adapter_agent, device_id) |
| 73 | self.adapter_agent.add_device(handler.device) |
| 74 | return handler |
| 75 | |
| 76 | def setup_mock_onu(self, parent_id=DEFAULT_OLT_DEVICE_ID, |
| 77 | device_id=DEFAULT_ONU_DEVICE_ID, |
| 78 | pon_id=DEFAULT_PON_ID, |
| 79 | onu_id=DEFAULT_ONU_ID, |
| 80 | serial_no=DEFAULT_ONU_SN): |
| 81 | handler = MockOnuHandler(self.adapter_agent, parent_id, device_id, pon_id, onu_id) |
| 82 | handler.serial_number = serial_no |
| 83 | onu = MockOnu(serial_no, self.adapter_agent, handler.device_id) \ |
| 84 | if serial_no is not None else None |
| 85 | handler.onu_mock = onu |
| 86 | return handler |
| 87 | |
| 88 | def setup_one_of_each(self): |
| 89 | # Most tests will use at lease one or more OLT and ONU |
| 90 | self.olt_handler = self.setup_mock_olt() |
| 91 | self.onu_handler = self.setup_mock_onu(parent_id=self.olt_handler.device_id) |
| 92 | self.onu_device = self.onu_handler.onu_mock |
| 93 | |
| 94 | self.adapter_agent.add_child_device(self.olt_handler.device, |
| 95 | self.onu_handler.device) |
| 96 | |
| 97 | def test_add_remove_device(self): |
| 98 | self.setup_one_of_each() |
| 99 | self.assertEqual(len(self.agent.device_ids()), 0) |
| 100 | |
| 101 | onu_device = self.agent.add_device(DEFAULT_ONU_DEVICE_ID, |
| 102 | self.adapter_agent) |
| 103 | self.assertIsNotNone(onu_device) |
| 104 | self.assertEqual(len(self.agent.device_ids()), 1) |
| 105 | self.assertEqual(self.agent.get_device(DEFAULT_ONU_DEVICE_ID), onu_device) |
| 106 | |
| 107 | # No MIB if not started |
| 108 | assert_raises(KeyError, onu_device.query_mib) |
| 109 | |
| 110 | self.agent.remove_device(DEFAULT_ONU_DEVICE_ID) |
| 111 | self.assertEqual(len(self.agent.device_ids()), 1) |
| 112 | |
| 113 | def test_delete_device(self): |
| 114 | self.setup_one_of_each() |
| 115 | self.assertEqual(len(self.agent.device_ids()), 0) |
| 116 | |
| 117 | onu_device = self.agent.add_device(DEFAULT_ONU_DEVICE_ID, |
| 118 | self.adapter_agent) |
| 119 | self.assertIsNotNone(onu_device) |
| 120 | self.assertEqual(len(self.agent.device_ids()), 1) |
| 121 | self.assertEqual(self.agent.get_device(DEFAULT_ONU_DEVICE_ID), onu_device) |
| 122 | # Can delete if it was not started |
| 123 | onu_device.delete() |
| 124 | self.assertEqual(len(self.agent.device_ids()), 0) |
| 125 | |
| 126 | ########################################## |
| 127 | # Delete of ONU device okay if it is started |
| 128 | onu_device = self.agent.add_device(DEFAULT_ONU_DEVICE_ID, |
| 129 | self.adapter_agent) |
| 130 | self.assertIsNotNone(onu_device) |
| 131 | self.assertEqual(len(self.agent.device_ids()), 1) |
| 132 | self.assertEqual(self.agent.get_device(DEFAULT_ONU_DEVICE_ID), onu_device) |
| 133 | |
| 134 | # Start it and then delete it |
| 135 | onu_device.start() |
| 136 | onu_device.delete() |
| 137 | self.assertEqual(len(self.agent.device_ids()), 0) |
| 138 | |
| 139 | @deferred(timeout=5) |
| 140 | def test_mib_query_fails_if_dev_not_started(self): |
| 141 | self.setup_one_of_each() |
| 142 | |
| 143 | onu_device = self.agent.add_device(DEFAULT_ONU_DEVICE_ID, |
| 144 | self.adapter_agent) |
| 145 | self.assertIsNotNone(onu_device) |
| 146 | self.assertEqual(len(self.agent.device_ids()), 1) |
| 147 | self.assertEqual(self.agent.get_device(DEFAULT_ONU_DEVICE_ID), onu_device) |
| 148 | |
| 149 | def not_called(_reason): |
| 150 | assert False, 'Should never be called' |
| 151 | |
| 152 | def check_status(_results): |
| 153 | # Device not yet started. Query should fail with KeyError since |
| 154 | # ONU is not in database yet |
| 155 | assert_raises(KeyError, onu_device.query_mib) |
| 156 | |
| 157 | # Yield context so that MIB Database callLater runs. This is a waiting |
| 158 | # Async task from when the OpenOMCIAgent was started. |
| 159 | d = asleep(0.2) |
| 160 | d.addCallbacks(check_status, not_called) |
| 161 | |
| 162 | return d |
| 163 | |
| 164 | @deferred(timeout=5) |
| 165 | def test_mib_query_ok_if_dev_started(self): |
| 166 | self.setup_one_of_each() |
| 167 | |
| 168 | onu_device = self.agent.add_device(DEFAULT_ONU_DEVICE_ID, |
| 169 | self.adapter_agent) |
| 170 | self.assertIsNotNone(onu_device) |
| 171 | self.assertEqual(len(self.agent.device_ids()), 1) |
| 172 | self.assertEqual(self.agent.get_device(DEFAULT_ONU_DEVICE_ID), onu_device) |
| 173 | |
| 174 | def not_called(_reason): |
| 175 | onu_device.stop() |
| 176 | assert False, 'Should never be called' |
| 177 | |
| 178 | def check_status(_results): |
| 179 | # Device started. Query will succeed but nothing should be populated |
| 180 | # but the most basic items |
| 181 | |
| 182 | results = onu_device.query_mib() |
| 183 | self.assertTrue(isinstance(results, dict)) |
| 184 | self.assertEqual(results.get(DEVICE_ID_KEY), DEFAULT_ONU_DEVICE_ID) |
| 185 | |
| 186 | self.assertIsNotNone(results.get(VERSION_KEY)) |
| 187 | self.assertIsNotNone(results.get(CREATED_KEY)) |
| 188 | self.assertIsNone(results.get(MODIFIED_KEY)) # Created! but not yet modified |
| 189 | |
| 190 | self.assertEqual(results.get(MDS_KEY), 0) |
| 191 | self.assertIsNone(results.get(LAST_SYNC_KEY)) |
| 192 | |
| 193 | self.assertIsNone(results.get(CLASS_ID_KEY)) |
| 194 | |
| 195 | # Stopping still allows a query. Note you just delete a device |
| 196 | # to clean up any associated databases |
| 197 | onu_device.stop() |
| 198 | results = onu_device.query_mib() |
| 199 | self.assertTrue(isinstance(results, dict)) |
| 200 | |
| 201 | # Yield context so that MIB Database callLater runs. This is a waiting |
| 202 | # Async task from when the OpenOMCIAgent was started. But also start the |
| 203 | # device so that it's queued async state machines can run as well |
| 204 | onu_device.start() |
| 205 | d = asleep(0.2) |
| 206 | d.addCallbacks(check_status, not_called) |
| 207 | |
| 208 | return d |
| 209 | |
| 210 | @deferred(timeout=5) |
| 211 | def test_delete_scrubs_mib(self): |
| 212 | self.setup_one_of_each() |
| 213 | |
| 214 | onu_device = self.agent.add_device(DEFAULT_ONU_DEVICE_ID, |
| 215 | self.adapter_agent) |
| 216 | self.assertIsNotNone(onu_device) |
| 217 | self.assertEqual(len(self.agent.device_ids()), 1) |
| 218 | self.assertEqual(self.agent.get_device(DEFAULT_ONU_DEVICE_ID), onu_device) |
| 219 | |
| 220 | def not_called(_reason): |
| 221 | onu_device.stop() |
| 222 | assert False, 'Should never be called' |
| 223 | |
| 224 | def check_status(_results): |
| 225 | # Device started. Query will succeed but nothing should be populated |
| 226 | # but the most basic items |
| 227 | |
| 228 | results = onu_device.query_mib() |
| 229 | self.assertTrue(isinstance(results, dict)) |
| 230 | self.assertEqual(results.get(DEVICE_ID_KEY), DEFAULT_ONU_DEVICE_ID) |
| 231 | |
| 232 | # Delete should wipe out any MIB data. Note that a delete of a started |
| 233 | # or stopped ONU device is allowed. In this case we are deleting a |
| 234 | # started ONU Device |
| 235 | |
| 236 | onu_device.delete() |
| 237 | assert_raises(Exception, onu_device.query_mib) |
| 238 | # TODO: When capabilities are supported, make sure capabilities get cleared as well |
| 239 | |
| 240 | # Yield context so that MIB Database callLater runs. This is a waiting |
| 241 | # Async task from when the OpenOMCIAgent was started. But also start the |
| 242 | # device so that it's queued async state machines can run as well |
| 243 | onu_device.start() |
| 244 | d = asleep(0.2) |
| 245 | d.addCallbacks(check_status, not_called) |
| 246 | |
| 247 | return d |
| 248 | |
| 249 | # TODO: Test pub/sub interface if possible |
| 250 | # TODO: Test custom/vendor-specific ME support |
| 251 | # TODO: Test override of various state machines or OMCI tasks if possible |
| 252 | |
| 253 | |
| 254 | if __name__ == '__main__': |
| 255 | main() |
| 256 | |