Loading src/tests/tools/simap_server/simap_client/ScenarioTopology.py 0 → 100644 +57 −0 Original line number Diff line number Diff line """Idempotent SIMAP topology mapping for the deployed MWC26 F5G-A scenario.""" from typing import Dict, Tuple SOURCE_LINKS = { 'L1' : ('ONT1', '500', 'OLT', '200'), 'L3' : ('OLT', '501', 'P-PE1', '200'), 'L5' : ('P-PE1', '500', 'P-P1', '500'), 'L9' : ('P-P1', '501', 'P-PE2', '500'), 'L13': ('P-PE2', '200', 'POP2', '500'), } def configure_scenario_topology(simap_client) -> Tuple[Dict, Dict]: """Upsert the real source and recursively aggregated scenario links. TFS SIMAP connectors create the named networks before the sampler starts. PATCH-based updates keep this operation idempotent and preserve unrelated nodes and links already published by those connectors. """ admin = simap_client.network('admin') for link_id, endpoints in SOURCE_LINKS.items(): admin.link(link_id).update(*endpoints) trans = simap_client.network('trans-pkt') trans.update(supporting_network_ids=['admin']) trans.node('site1').update(supporting_node_ids=[('admin', 'P-PE1')]) trans.node('site2').update(supporting_node_ids=[('admin', 'P-PE2')]) trans.link('Trans-L1').update( 'site1', '500', 'site2', '500', supporting_link_ids=[('admin', 'L5'), ('admin', 'L9')]) agg = simap_client.network('agg') agg.update(supporting_network_ids=['admin', 'trans-pkt']) agg.node('sdp1').update(supporting_node_ids=[('admin', 'OLT')]) agg.node('sdp2').update(supporting_node_ids=[('admin', 'POP2')]) agg.link('AggNet-L1').update( 'sdp1', '500', 'sdp2', '500', supporting_link_ids=[ ('admin', 'L3'), ('trans-pkt', 'Trans-L1'), ('admin', 'L13')]) e2e = simap_client.network('e2e') e2e.update(supporting_network_ids=['admin', 'agg']) e2e.node('sdp1').update(supporting_node_ids=[('admin', 'ONT1')]) e2e.node('sdp2').update(supporting_node_ids=[('admin', 'POP2')]) e2e.link('E2E-L1').update( 'sdp1', '500', 'sdp2', '500', supporting_link_ids=[('admin', 'L1'), ('agg', 'AggNet-L1')]) source_links = {link_id: admin.link(link_id) for link_id in SOURCE_LINKS} abstract_links = { 'Trans-L1': trans.link('Trans-L1'), 'AggNet-L1': agg.link('AggNet-L1'), 'E2E-L1': e2e.link('E2E-L1'), } return source_links, abstract_links src/tests/tools/simap_server/simap_client/scheduled_main.py +2 −21 Original line number Diff line number Diff line Loading @@ -13,10 +13,9 @@ from typing import Optional, Sequence from common.tools.rest_conf.client.RestConfClient import RestConfClient from .LoadSchedule import PROFILE_NAMES, build_profile, LoadSchedule from .ScenarioTopology import configure_scenario_topology from .SimapClient import SimapClient from .SimapMetricsGenerator import SimapMetricsGenerator from .Tools import (create_simap_aggnet, create_simap_e2enet, create_simap_te, create_simap_trans) logging.basicConfig(level=logging.INFO) logging.getLogger('RestConfClient').setLevel(logging.WARN) Loading Loading @@ -97,25 +96,7 @@ def main(argv: Optional[Sequence[str]] = None) -> None: generator = SimapMetricsGenerator(service_count=schedule.connection_count_at(0), seed=args.seed ^ 0x5A17A5EED) try: create_simap_te(simap_client) create_simap_trans(simap_client) create_simap_aggnet(simap_client) create_simap_e2enet(simap_client) except Exception as exc: error_msg = str(exc) if 'status_code=409' in error_msg or 'already exists' in error_msg.lower(): LOGGER.warning('SIMAP topology already exists; continuing with existing topology.') else: raise te_network = simap_client.network('te') te_links = {link_id: te_network.link(link_id) for link_id in ('L1', 'L3', 'L5', 'L9', 'L13')} abstract_links = { 'Trans-L1': simap_client.network('simap-trans').link('Trans-L1'), 'AggNet-L1': simap_client.network('simap-aggnet').link('AggNet-L1'), 'E2E-L1': simap_client.network('simap-e2e').link('E2E-L1'), } te_links, abstract_links = configure_scenario_topology(simap_client) sample_count = int(args.duration / args.sample_interval) if sample_count < 1: Loading src/tests/tools/simap_server/simap_client/tests/test_scenario_topology.py 0 → 100644 +52 −0 Original line number Diff line number Diff line from pathlib import Path import sys MODULE_DIR = Path(__file__).resolve().parents[1] if str(MODULE_DIR) not in sys.path: sys.path.insert(0, str(MODULE_DIR)) from ScenarioTopology import configure_scenario_topology class Entity: def __init__(self, calls, kind, network, identifier): self.calls = calls self.key = (kind, network, identifier) def update(self, *args, **kwargs): self.calls.append((self.key, args, kwargs)) class Network(Entity): def __init__(self, calls, identifier): super().__init__(calls, 'network', identifier, identifier) self.identifier = identifier def node(self, identifier): return Entity(self.calls, 'node', self.identifier, identifier) def link(self, identifier): return Entity(self.calls, 'link', self.identifier, identifier) class Client: def __init__(self): self.calls = [] def network(self, identifier): return Network(self.calls, identifier) def test_deployed_hierarchy_uses_connector_recognised_networks(): client = Client() source, abstract = configure_scenario_topology(client) assert sorted(source) == ['L1', 'L13', 'L3', 'L5', 'L9'] assert sorted(abstract) == ['AggNet-L1', 'E2E-L1', 'Trans-L1'] link_calls = {key: (args, kwargs) for key, args, kwargs in client.calls if key[0] == 'link'} assert link_calls[('link', 'trans-pkt', 'Trans-L1')][1]['supporting_link_ids'] == [ ('admin', 'L5'), ('admin', 'L9')] assert link_calls[('link', 'agg', 'AggNet-L1')][1]['supporting_link_ids'] == [ ('admin', 'L3'), ('trans-pkt', 'Trans-L1'), ('admin', 'L13')] assert link_calls[('link', 'e2e', 'E2E-L1')][1]['supporting_link_ids'] == [ ('admin', 'L1'), ('agg', 'AggNet-L1')] Loading
src/tests/tools/simap_server/simap_client/ScenarioTopology.py 0 → 100644 +57 −0 Original line number Diff line number Diff line """Idempotent SIMAP topology mapping for the deployed MWC26 F5G-A scenario.""" from typing import Dict, Tuple SOURCE_LINKS = { 'L1' : ('ONT1', '500', 'OLT', '200'), 'L3' : ('OLT', '501', 'P-PE1', '200'), 'L5' : ('P-PE1', '500', 'P-P1', '500'), 'L9' : ('P-P1', '501', 'P-PE2', '500'), 'L13': ('P-PE2', '200', 'POP2', '500'), } def configure_scenario_topology(simap_client) -> Tuple[Dict, Dict]: """Upsert the real source and recursively aggregated scenario links. TFS SIMAP connectors create the named networks before the sampler starts. PATCH-based updates keep this operation idempotent and preserve unrelated nodes and links already published by those connectors. """ admin = simap_client.network('admin') for link_id, endpoints in SOURCE_LINKS.items(): admin.link(link_id).update(*endpoints) trans = simap_client.network('trans-pkt') trans.update(supporting_network_ids=['admin']) trans.node('site1').update(supporting_node_ids=[('admin', 'P-PE1')]) trans.node('site2').update(supporting_node_ids=[('admin', 'P-PE2')]) trans.link('Trans-L1').update( 'site1', '500', 'site2', '500', supporting_link_ids=[('admin', 'L5'), ('admin', 'L9')]) agg = simap_client.network('agg') agg.update(supporting_network_ids=['admin', 'trans-pkt']) agg.node('sdp1').update(supporting_node_ids=[('admin', 'OLT')]) agg.node('sdp2').update(supporting_node_ids=[('admin', 'POP2')]) agg.link('AggNet-L1').update( 'sdp1', '500', 'sdp2', '500', supporting_link_ids=[ ('admin', 'L3'), ('trans-pkt', 'Trans-L1'), ('admin', 'L13')]) e2e = simap_client.network('e2e') e2e.update(supporting_network_ids=['admin', 'agg']) e2e.node('sdp1').update(supporting_node_ids=[('admin', 'ONT1')]) e2e.node('sdp2').update(supporting_node_ids=[('admin', 'POP2')]) e2e.link('E2E-L1').update( 'sdp1', '500', 'sdp2', '500', supporting_link_ids=[('admin', 'L1'), ('agg', 'AggNet-L1')]) source_links = {link_id: admin.link(link_id) for link_id in SOURCE_LINKS} abstract_links = { 'Trans-L1': trans.link('Trans-L1'), 'AggNet-L1': agg.link('AggNet-L1'), 'E2E-L1': e2e.link('E2E-L1'), } return source_links, abstract_links
src/tests/tools/simap_server/simap_client/scheduled_main.py +2 −21 Original line number Diff line number Diff line Loading @@ -13,10 +13,9 @@ from typing import Optional, Sequence from common.tools.rest_conf.client.RestConfClient import RestConfClient from .LoadSchedule import PROFILE_NAMES, build_profile, LoadSchedule from .ScenarioTopology import configure_scenario_topology from .SimapClient import SimapClient from .SimapMetricsGenerator import SimapMetricsGenerator from .Tools import (create_simap_aggnet, create_simap_e2enet, create_simap_te, create_simap_trans) logging.basicConfig(level=logging.INFO) logging.getLogger('RestConfClient').setLevel(logging.WARN) Loading Loading @@ -97,25 +96,7 @@ def main(argv: Optional[Sequence[str]] = None) -> None: generator = SimapMetricsGenerator(service_count=schedule.connection_count_at(0), seed=args.seed ^ 0x5A17A5EED) try: create_simap_te(simap_client) create_simap_trans(simap_client) create_simap_aggnet(simap_client) create_simap_e2enet(simap_client) except Exception as exc: error_msg = str(exc) if 'status_code=409' in error_msg or 'already exists' in error_msg.lower(): LOGGER.warning('SIMAP topology already exists; continuing with existing topology.') else: raise te_network = simap_client.network('te') te_links = {link_id: te_network.link(link_id) for link_id in ('L1', 'L3', 'L5', 'L9', 'L13')} abstract_links = { 'Trans-L1': simap_client.network('simap-trans').link('Trans-L1'), 'AggNet-L1': simap_client.network('simap-aggnet').link('AggNet-L1'), 'E2E-L1': simap_client.network('simap-e2e').link('E2E-L1'), } te_links, abstract_links = configure_scenario_topology(simap_client) sample_count = int(args.duration / args.sample_interval) if sample_count < 1: Loading
src/tests/tools/simap_server/simap_client/tests/test_scenario_topology.py 0 → 100644 +52 −0 Original line number Diff line number Diff line from pathlib import Path import sys MODULE_DIR = Path(__file__).resolve().parents[1] if str(MODULE_DIR) not in sys.path: sys.path.insert(0, str(MODULE_DIR)) from ScenarioTopology import configure_scenario_topology class Entity: def __init__(self, calls, kind, network, identifier): self.calls = calls self.key = (kind, network, identifier) def update(self, *args, **kwargs): self.calls.append((self.key, args, kwargs)) class Network(Entity): def __init__(self, calls, identifier): super().__init__(calls, 'network', identifier, identifier) self.identifier = identifier def node(self, identifier): return Entity(self.calls, 'node', self.identifier, identifier) def link(self, identifier): return Entity(self.calls, 'link', self.identifier, identifier) class Client: def __init__(self): self.calls = [] def network(self, identifier): return Network(self.calls, identifier) def test_deployed_hierarchy_uses_connector_recognised_networks(): client = Client() source, abstract = configure_scenario_topology(client) assert sorted(source) == ['L1', 'L13', 'L3', 'L5', 'L9'] assert sorted(abstract) == ['AggNet-L1', 'E2E-L1', 'Trans-L1'] link_calls = {key: (args, kwargs) for key, args, kwargs in client.calls if key[0] == 'link'} assert link_calls[('link', 'trans-pkt', 'Trans-L1')][1]['supporting_link_ids'] == [ ('admin', 'L5'), ('admin', 'L9')] assert link_calls[('link', 'agg', 'AggNet-L1')][1]['supporting_link_ids'] == [ ('admin', 'L3'), ('trans-pkt', 'Trans-L1'), ('admin', 'L13')] assert link_calls[('link', 'e2e', 'E2E-L1')][1]['supporting_link_ids'] == [ ('admin', 'L1'), ('agg', 'AggNet-L1')]