Rachit Shrivastava | b9809da | 2017-07-12 11:09:10 -0400 | [diff] [blame] | 1 | from tests.itests.voltha.rest_base import RestBase |
| 2 | |
Rachit Shrivastava | 5a2d569 | 2017-07-17 10:29:20 -0400 | [diff] [blame] | 3 | from google.protobuf.json_format import MessageToDict, ParseDict |
Rachit Shrivastava | b9809da | 2017-07-12 11:09:10 -0400 | [diff] [blame] | 4 | import unittest |
| 5 | |
| 6 | from voltha.protos import bbf_fiber_base_pb2 as fb |
Rachit Shrivastava | b9809da | 2017-07-12 11:09:10 -0400 | [diff] [blame] | 7 | from voltha.protos.device_pb2 import Device |
Nikolay Titov | 176f1db | 2017-08-10 12:38:43 -0400 | [diff] [blame] | 8 | from voltha.protos import bbf_fiber_gemport_body_pb2 as gemport |
| 9 | from voltha.protos import bbf_fiber_tcont_body_pb2 as tcont |
| 10 | from voltha.protos import bbf_fiber_traffic_descriptor_profile_body_pb2 as tdp |
Rachit Shrivastava | b9809da | 2017-07-12 11:09:10 -0400 | [diff] [blame] | 11 | from common.utils.consulhelpers import get_endpoint_from_consul |
| 12 | |
Rachit Shrivastava | 5a2d569 | 2017-07-17 10:29:20 -0400 | [diff] [blame] | 13 | ''' |
Rachit Shrivastava | a182e91 | 2017-07-28 15:18:34 -0400 | [diff] [blame] | 14 | These tests use the Ponsim OLT to verify create, update, and delete |
| 15 | functionalities of ChannelgroupConfig, ChannelpartitionConfig, |
| 16 | ChannelpairConfig, ChannelterminationConfig, VOntAni, OntAni, and VEnets |
| 17 | for xPON |
Rachit Shrivastava | 5a2d569 | 2017-07-17 10:29:20 -0400 | [diff] [blame] | 18 | The prerequisite for this test are: |
| 19 | 1. voltha ensemble is running |
| 20 | docker-compose -f compose/docker-compose-system-test.yml up -d |
| 21 | 2. ponsim olt is running with PONSIM-OLT |
| 22 | sudo -s |
| 23 | . ./env.sh |
| 24 | ./ponsim/main.py -v |
| 25 | ''' |
| 26 | |
Rachit Shrivastava | b9809da | 2017-07-12 11:09:10 -0400 | [diff] [blame] | 27 | device_type = 'ponsim_olt' |
| 28 | host_and_port = '172.17.0.1:50060' |
| 29 | scenario = [ |
Rachit Shrivastava | a182e91 | 2017-07-28 15:18:34 -0400 | [diff] [blame] | 30 | {'cg-add': { |
| 31 | 'pb2': fb.ChannelgroupConfig(), |
| 32 | 'rpc': { |
| 33 | "interface": { |
| 34 | "enabled": True, |
| 35 | "name": "Manhattan", |
| 36 | "description": "Channel Group for Manhattan.." |
| 37 | }, |
| 38 | "data": { |
| 39 | "polling_period": 100, |
| 40 | "system_id": "000000", |
| 41 | "raman_mitigation": "RAMAN_NONE" |
| 42 | }, |
| 43 | "name": "Manhattan" |
Rachit Shrivastava | 8f4f9bf | 2017-07-20 11:59:30 -0400 | [diff] [blame] | 44 | } |
Rachit Shrivastava | 8f4f9bf | 2017-07-20 11:59:30 -0400 | [diff] [blame] | 45 | } |
Rachit Shrivastava | a182e91 | 2017-07-28 15:18:34 -0400 | [diff] [blame] | 46 | }, |
| 47 | {'cpart-add': { |
| 48 | 'pb2': fb.ChannelpartitionConfig(), |
| 49 | 'rpc': { |
| 50 | "interface": { |
| 51 | "enabled": True, |
| 52 | "name": "Freedom Tower", |
| 53 | "description":"Channel Partition for Freedom Tower in Manhattan" |
| 54 | }, |
| 55 | "data": { |
| 56 | "differential_fiber_distance": 20, |
| 57 | "closest_ont_distance": 0, |
| 58 | "fec_downstream": False, |
| 59 | "multicast_aes_indicator": False, |
| 60 | "authentication_method": "SERIAL_NUMBER", |
| 61 | "channelgroup_ref": "Manhattan" |
| 62 | }, |
| 63 | "name": "Freedom Tower" |
| 64 | } |
| 65 | } |
| 66 | }, |
| 67 | {'cpair-add': { |
| 68 | 'pb2': fb.ChannelpairConfig(), |
| 69 | 'rpc': { |
| 70 | "interface": { |
| 71 | "enabled": True, |
| 72 | "name": "PON port", |
| 73 | "description": "Channel Pair for Freedom Tower" |
| 74 | }, |
| 75 | "data": { |
| 76 | "channelpair_linerate": "down_10_up_10", |
| 77 | "channelpair_type": "channelpair", |
| 78 | "channelgroup_ref": "Manhattan", |
| 79 | "gpon_ponid_interval": 0, |
| 80 | "channelpartition_ref": "Freedom Tower", |
| 81 | "gpon_ponid_odn_class": "CLASS_A" |
| 82 | }, |
| 83 | "name": "PON port" |
| 84 | } |
| 85 | } |
| 86 | }, |
| 87 | {'cterm-add': { |
| 88 | 'pb2': fb.ChannelterminationConfig(), |
| 89 | 'rpc': { |
| 90 | "interface": { |
| 91 | "enabled": True, |
| 92 | "name": "PON port", |
| 93 | "description": "Channel Termination for Freedom Tower" |
| 94 | }, |
| 95 | "data": { |
| 96 | "channelpair_ref": "PON port", |
| 97 | "location": "Freedom Tower OLT" |
| 98 | }, |
| 99 | "name": "PON port" |
| 100 | } |
| 101 | } |
| 102 | }, |
| 103 | {'vontani-add': { |
| 104 | 'pb2': fb.VOntaniConfig(), |
| 105 | 'rpc': { |
| 106 | "interface": { |
| 107 | "enabled": True, |
| 108 | "name": "Golden User", |
| 109 | "description": "Golden User in Freedom Tower" |
| 110 | }, |
| 111 | "data": { |
| 112 | "preferred_chanpair": "PON port", |
| 113 | "expected_serial_number": "PSMO00000001", |
| 114 | "parent_ref": "Freedom Tower", |
| 115 | "onu_id": 1 |
| 116 | }, |
| 117 | "name": "Golden User" |
| 118 | } |
| 119 | } |
| 120 | }, |
| 121 | {'ontani-add': { |
| 122 | 'pb2': fb.OntaniConfig(), |
| 123 | 'rpc': { |
| 124 | "interface": { |
| 125 | "enabled": True, |
| 126 | "name": "Golden User", |
| 127 | "description": "Golden User in Freedom Tower" |
| 128 | }, |
| 129 | "data": { |
| 130 | "upstream_fec_indicator": True, |
| 131 | "mgnt_gemport_aes_indicator": False |
| 132 | }, |
| 133 | "name": "Golden User" |
| 134 | } |
| 135 | } |
| 136 | }, |
| 137 | {'venet-add': { |
| 138 | 'pb2': fb.VEnetConfig(), |
| 139 | 'rpc': { |
| 140 | "interface": { |
| 141 | "enabled": True, |
| 142 | "name": "Enet UNI 1", |
| 143 | "description": "Ethernet port - 1" |
| 144 | }, |
| 145 | "data": { |
| 146 | "v_ontani_ref": "Golden User" |
| 147 | }, |
| 148 | "name": "Enet UNI 1" |
| 149 | } |
| 150 | } |
| 151 | }, |
Nikolay Titov | 176f1db | 2017-08-10 12:38:43 -0400 | [diff] [blame] | 152 | {'tdp-add': { |
| 153 | 'pb2': tdp.TrafficDescriptorProfileData(), |
| 154 | 'rpc': { |
| 155 | "name": "TDP 1", |
| 156 | "assured_bandwidth": "500000", |
| 157 | "additional_bw_eligibility_indicator": \ |
| 158 | "ADDITIONAL_BW_ELIGIBILITY_INDICATOR_NONE", |
| 159 | "fixed_bandwidth": "100000", |
| 160 | "maximum_bandwidth": "1000000", |
| 161 | } |
| 162 | } |
| 163 | }, |
| 164 | {'tcont-add': { |
| 165 | 'pb2': tcont.TcontsConfigData(), |
| 166 | 'rpc': { |
| 167 | "interface_reference": "Golden User", |
| 168 | "traffic_descriptor_profile_ref": "TDP 1", |
| 169 | "name": "TCont 1" |
| 170 | } |
| 171 | } |
| 172 | }, |
| 173 | {'gemport-add': { |
| 174 | 'pb2': gemport.GemportsConfigData(), |
| 175 | 'rpc': { |
| 176 | "aes_indicator": True, |
| 177 | "name": "GEMPORT 1", |
| 178 | "traffic_class": 0, |
| 179 | "itf_ref": "Enet UNI 1", |
| 180 | "tcont_ref": "TCont 1", |
| 181 | } |
| 182 | } |
Rachit Shrivastava | a182e91 | 2017-07-28 15:18:34 -0400 | [diff] [blame] | 183 | } |
| 184 | ] |
Rachit Shrivastava | 5a2d569 | 2017-07-17 10:29:20 -0400 | [diff] [blame] | 185 | |
Rachit Shrivastava | 8f4f9bf | 2017-07-20 11:59:30 -0400 | [diff] [blame] | 186 | #for ordering the test cases |
| 187 | id = 3 |
Rachit Shrivastava | b9809da | 2017-07-12 11:09:10 -0400 | [diff] [blame] | 188 | LOCAL_CONSUL = "localhost:8500" |
| 189 | # Retrieve details of the REST entry point |
| 190 | rest_endpoint = get_endpoint_from_consul(LOCAL_CONSUL, 'chameleon-rest') |
| 191 | # Construct the base_url |
| 192 | base_url = 'https://' + rest_endpoint |
| 193 | |
| 194 | class GlobalPreChecks(RestBase): |
| 195 | def test_000_get_root(self): |
| 196 | res = self.get('/#!/', expected_content_type='text/html') |
| 197 | self.assertGreaterEqual(res.find('swagger'), 0) |
| 198 | |
| 199 | def test_001_get_health(self): |
| 200 | res = self.get('/health') |
| 201 | self.assertEqual(res['state'], 'HEALTHY') |
| 202 | |
| 203 | class TestXPon(RestBase): |
Rachit Shrivastava | b9809da | 2017-07-12 11:09:10 -0400 | [diff] [blame] | 204 | def test_002_setup_device(self): |
| 205 | global device |
| 206 | device = self.add_device() |
| 207 | self.verify_device_preprovisioned_state(device['id']) |
| 208 | self.activate_device(device['id']) |
| 209 | |
Nikolay Titov | 7253ff2 | 2017-08-14 18:24:17 -0400 | [diff] [blame] | 210 | def _remove_device(self): |
Rachit Shrivastava | b9809da | 2017-07-12 11:09:10 -0400 | [diff] [blame] | 211 | self.deactivate_device(device['id']) |
| 212 | self.delete_device(device['id']) |
| 213 | |
| 214 | #~~~~~~~~~~~~~~~~~~~~~~ Helper Functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 215 | # Create a new simulated device |
| 216 | def add_device(self): |
Rachit Shrivastava | a182e91 | 2017-07-28 15:18:34 -0400 | [diff] [blame] | 217 | return self.post('/api/v1/devices', |
Rachit Shrivastava | b9809da | 2017-07-12 11:09:10 -0400 | [diff] [blame] | 218 | MessageToDict(Device( |
| 219 | type=device_type, |
| 220 | host_and_port=host_and_port |
| 221 | )), |
| 222 | expected_code=200) |
| 223 | |
| 224 | def verify_device_preprovisioned_state(self, olt_id): |
| 225 | # we also check that so far what we read back is same as what we get |
| 226 | # back on create |
Rachit Shrivastava | a182e91 | 2017-07-28 15:18:34 -0400 | [diff] [blame] | 227 | device = self.get('/api/v1/devices/{}'.format(olt_id)) |
Rachit Shrivastava | b9809da | 2017-07-12 11:09:10 -0400 | [diff] [blame] | 228 | self.assertNotEqual(device['id'], '') |
| 229 | self.assertEqual(device['adapter'], 'ponsim_olt') |
| 230 | self.assertEqual(device['admin_state'], 'PREPROVISIONED') |
| 231 | self.assertEqual(device['oper_status'], 'UNKNOWN') |
| 232 | |
| 233 | # Active the simulated device. |
| 234 | # This will trigger the simulation of random alarms |
| 235 | def activate_device(self, device_id): |
Rachit Shrivastava | a182e91 | 2017-07-28 15:18:34 -0400 | [diff] [blame] | 236 | path = '/api/v1/devices/{}'.format(device_id) |
Rachit Shrivastava | b9809da | 2017-07-12 11:09:10 -0400 | [diff] [blame] | 237 | self.post(path + '/enable', expected_code=200) |
| 238 | device = self.get(path) |
| 239 | self.assertEqual(device['admin_state'], 'ENABLED') |
| 240 | |
| 241 | def deactivate_device(self, device_id): |
Rachit Shrivastava | a182e91 | 2017-07-28 15:18:34 -0400 | [diff] [blame] | 242 | path = '/api/v1/devices/{}'.format(device_id) |
Rachit Shrivastava | b9809da | 2017-07-12 11:09:10 -0400 | [diff] [blame] | 243 | self.post(path + '/disable', expected_code=200) |
| 244 | device = self.get(path) |
| 245 | self.assertEqual(device['admin_state'], 'DISABLED') |
| 246 | |
| 247 | def delete_device(self, device_id): |
Rachit Shrivastava | a182e91 | 2017-07-28 15:18:34 -0400 | [diff] [blame] | 248 | path = '/api/v1/devices/{}'.format(device_id) |
Rachit Shrivastava | b9809da | 2017-07-12 11:09:10 -0400 | [diff] [blame] | 249 | self.delete(path + '/delete', expected_code=200) |
| 250 | device = self.get(path, expected_code=404) |
| 251 | self.assertIsNone(device) |
| 252 | |
| 253 | # Add cg, cpair, cpart |
| 254 | def add(self, type, config, req, name): |
| 255 | res = self.verify(type) |
| 256 | prev_len = len(res[config]) |
| 257 | self.post(self.get_path(type, name, ''), |
| 258 | MessageToDict(req, preserving_proto_field_name = True), |
| 259 | expected_code = 200) |
| 260 | return self.verify(type), prev_len |
| 261 | |
| 262 | # Modify the existing cg, cpair, cpart |
| 263 | def modify(self, type, req, name): |
| 264 | self.post(self.get_path(type, name, '/modify'), |
| 265 | MessageToDict(req, preserving_proto_field_name = True), |
| 266 | expected_code = 200) |
| 267 | return self.verify(type) |
| 268 | |
| 269 | # Delete cg, cpair, cpart |
| 270 | def remove(self, type, config, name): |
| 271 | res = self.verify(type) |
| 272 | prev_len = len(res[config]) |
| 273 | self.delete(self.get_path(type, name, '/delete'), |
| 274 | expected_code = 200) |
| 275 | return self.verify(type), prev_len |
| 276 | |
| 277 | # Retrieve the desired item upon Post message |
| 278 | def verify(self, type): |
| 279 | if(type == 'channel_terminations'): |
Rachit Shrivastava | 8f4f9bf | 2017-07-20 11:59:30 -0400 | [diff] [blame] | 280 | return self.get('/api/v1/devices/{}/{}'.format(device['id'], type)) |
Rachit Shrivastava | a182e91 | 2017-07-28 15:18:34 -0400 | [diff] [blame] | 281 | return self.get('/api/v1/{}'.format(type)) |
Rachit Shrivastava | b9809da | 2017-07-12 11:09:10 -0400 | [diff] [blame] | 282 | |
| 283 | def get_path(self, type, name, operation): |
| 284 | if(type == 'channel_terminations'): |
Rachit Shrivastava | a182e91 | 2017-07-28 15:18:34 -0400 | [diff] [blame] | 285 | return '/api/v1/devices/{}/{}/{}{}'.format(device['id'], |
Rachit Shrivastava | 8f4f9bf | 2017-07-20 11:59:30 -0400 | [diff] [blame] | 286 | type, name, operation) |
Rachit Shrivastava | a182e91 | 2017-07-28 15:18:34 -0400 | [diff] [blame] | 287 | return '/api/v1/{}/{}{}'.format(type, name, operation) |
Rachit Shrivastava | b9809da | 2017-07-12 11:09:10 -0400 | [diff] [blame] | 288 | |
| 289 | # Method to check if the result is same as the change requested |
| 290 | def search(self, req, result): |
Nikolay Titov | 176f1db | 2017-08-10 12:38:43 -0400 | [diff] [blame] | 291 | dict1 = MessageToDict(req, |
| 292 | including_default_value_fields = True, |
| 293 | preserving_proto_field_name = True) |
Nikolay Titov | 7253ff2 | 2017-08-14 18:24:17 -0400 | [diff] [blame] | 294 | #skip comparison of READ-ONLY fields |
Nikolay Titov | 176f1db | 2017-08-10 12:38:43 -0400 | [diff] [blame] | 295 | result['id'] = '' |
Nikolay Titov | 7253ff2 | 2017-08-14 18:24:17 -0400 | [diff] [blame] | 296 | if isinstance(req, fb.ChannelgroupConfig): |
| 297 | result['cg_index'] = 0 |
| 298 | elif isinstance(req, tcont.TcontsConfigData): |
| 299 | result['alloc_id'] = 0 |
| 300 | elif isinstance(req, gemport.GemportsConfigData): |
| 301 | result['gemport_id'] = 0 |
Nikolay Titov | 176f1db | 2017-08-10 12:38:43 -0400 | [diff] [blame] | 302 | return dict1 == result |
| 303 | |
Rachit Shrivastava | b9809da | 2017-07-12 11:09:10 -0400 | [diff] [blame] | 304 | |
Rachit Shrivastava | b9809da | 2017-07-12 11:09:10 -0400 | [diff] [blame] | 305 | #~~~~~~~~~~~~~~ Function to create test cases on the fly ~~~~~~~~~~~~~~~~ |
| 306 | def create_dynamic_method(key, value): |
| 307 | obj_type_config = { |
Rachit Shrivastava | a182e91 | 2017-07-28 15:18:34 -0400 | [diff] [blame] | 308 | 'cg': {'type':'channel_groups', |
| 309 | 'config':'channelgroup_config'}, |
| 310 | 'cpart': {'type':'channel_partitions', |
| 311 | 'config':'channelpartition_config'}, |
| 312 | 'cpair': {'type':'channel_pairs', |
| 313 | 'config':'channelpair_config'}, |
| 314 | 'cterm': {'type':'channel_terminations', |
| 315 | 'config':'channeltermination_config'}, |
| 316 | 'vontani':{'type':'v_ont_anis', |
| 317 | 'config':'v_ontani_config'}, |
| 318 | 'ontani': {'type':'ont_anis', |
| 319 | 'config':'ontani_config'}, |
| 320 | 'venet': {'type':'v_enets', |
Nikolay Titov | 176f1db | 2017-08-10 12:38:43 -0400 | [diff] [blame] | 321 | 'config':'v_enet_config'}, |
| 322 | 'gemport':{'type':'gemports', |
| 323 | 'config':'gemports_config'}, |
| 324 | 'tcont': {'type':'tconts', |
| 325 | 'config':'tconts_config'}, |
| 326 | 'tdp': {'type':'traffic_descriptor_profiles', |
| 327 | 'config':'traffic_descriptor_profiles'} |
Rachit Shrivastava | 8f4f9bf | 2017-07-20 11:59:30 -0400 | [diff] [blame] | 328 | } |
Rachit Shrivastava | b9809da | 2017-07-12 11:09:10 -0400 | [diff] [blame] | 329 | |
| 330 | def _add(self, type, config, req, name): |
| 331 | result, prev_len = self.add(type, config, req, name) |
| 332 | self.assertEqual(result[config][prev_len]['name'], name) |
| 333 | self.assertEqual(len(result[config]), prev_len+1) |
Nikolay Titov | 176f1db | 2017-08-10 12:38:43 -0400 | [diff] [blame] | 334 | self.assertEqual(self.search(req, result[config][0]), True) |
Rachit Shrivastava | b9809da | 2017-07-12 11:09:10 -0400 | [diff] [blame] | 335 | |
| 336 | def _mod(self, type, config, req, name): |
| 337 | result = self.modify(type, req, name) |
Nikolay Titov | 176f1db | 2017-08-10 12:38:43 -0400 | [diff] [blame] | 338 | self.assertEqual(self.search(req, result[config][0]), True) |
Rachit Shrivastava | b9809da | 2017-07-12 11:09:10 -0400 | [diff] [blame] | 339 | |
| 340 | def _del(self, type, config, req, name): |
| 341 | result, prev_len = self.remove(type, config, name) |
| 342 | self.assertEqual(len(result[config]), prev_len-1) |
| 343 | |
| 344 | def _operate(self, obj_action, type_config, req, name): |
| 345 | if obj_action == 'add': |
| 346 | _add(self, type_config['type'], type_config['config'], req, name) |
| 347 | elif obj_action == 'mod': |
| 348 | _mod(self, type_config['type'], type_config['config'], req, name) |
| 349 | elif obj_action == 'del': |
| 350 | _del(self, type_config['type'], type_config['config'], req, name) |
Rachit Shrivastava | 5a2d569 | 2017-07-17 10:29:20 -0400 | [diff] [blame] | 351 | |
| 352 | def _config(self): |
| 353 | ParseDict(value['rpc'], value['pb2']) |
| 354 | return value['pb2'] |
Rachit Shrivastava | b9809da | 2017-07-12 11:09:10 -0400 | [diff] [blame] | 355 | |
| 356 | def dynamic_test_method(self): |
Rachit Shrivastava | 5a2d569 | 2017-07-17 10:29:20 -0400 | [diff] [blame] | 357 | _obj_action = [val for val in key.split('-')] |
| 358 | _type_config = obj_type_config[_obj_action[0]] |
| 359 | _req = _config(self) |
| 360 | _operate(self, _obj_action[1], _type_config, _req, value['rpc']['name']) |
Rachit Shrivastava | b9809da | 2017-07-12 11:09:10 -0400 | [diff] [blame] | 361 | |
| 362 | return dynamic_test_method |
| 363 | |
Rachit Shrivastava | a182e91 | 2017-07-28 15:18:34 -0400 | [diff] [blame] | 364 | #read the set instructions for tests |
| 365 | #dynamically create test cases in desired sequence |
Rachit Shrivastava | b9809da | 2017-07-12 11:09:10 -0400 | [diff] [blame] | 366 | for item in scenario: |
| 367 | id = id + 1 |
| 368 | if(isinstance(item, dict)): |
| 369 | for k,v in item.items(): |
| 370 | dynamic_method = create_dynamic_method(k, v) |
Rachit Shrivastava | a182e91 | 2017-07-28 15:18:34 -0400 | [diff] [blame] | 371 | dynamic_method.__name__ = 'test_{:3d}_{}'.format(id, k).replace( |
| 372 | ' ', '0') |
Rachit Shrivastava | b9809da | 2017-07-12 11:09:10 -0400 | [diff] [blame] | 373 | setattr(TestXPon, dynamic_method.__name__, dynamic_method) |
| 374 | del dynamic_method |
| 375 | |
| 376 | if __name__ == '__main__': |
Rachit Shrivastava | 5a2d569 | 2017-07-17 10:29:20 -0400 | [diff] [blame] | 377 | unittest.main() |