annet 0.16.30__tar.gz → 0.16.32__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of annet might be problematic. Click here for more details.
- {annet-0.16.30/annet.egg-info → annet-0.16.32}/PKG-INFO +1 -1
- {annet-0.16.30 → annet-0.16.32}/annet/adapters/netbox/common/manufacturer.py +2 -0
- {annet-0.16.30 → annet-0.16.32}/annet/adapters/netbox/common/models.py +1 -1
- {annet-0.16.30 → annet-0.16.32}/annet/api/__init__.py +24 -15
- {annet-0.16.30 → annet-0.16.32}/annet/bgp_models.py +8 -2
- {annet-0.16.30 → annet-0.16.32}/annet/mesh/basemodel.py +3 -0
- {annet-0.16.30 → annet-0.16.32}/annet/mesh/device_models.py +4 -0
- {annet-0.16.30 → annet-0.16.32}/annet/mesh/executor.py +20 -1
- {annet-0.16.30 → annet-0.16.32}/annet/mesh/peer_models.py +3 -3
- {annet-0.16.30 → annet-0.16.32}/annet/mesh/registry.py +15 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rpl_generators/cumulus_frr.py +31 -17
- {annet-0.16.30 → annet-0.16.32}/annet/rpl_generators/entities.py +25 -12
- {annet-0.16.30 → annet-0.16.32}/annet/rpl_generators/policy.py +26 -15
- {annet-0.16.30 → annet-0.16.32}/annet/rpl_generators/prefix_lists.py +28 -18
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/texts/cisco.deploy +1 -1
- {annet-0.16.30 → annet-0.16.32/annet.egg-info}/PKG-INFO +1 -1
- {annet-0.16.30 → annet-0.16.32}/AUTHORS +0 -0
- {annet-0.16.30 → annet-0.16.32}/LICENSE +0 -0
- {annet-0.16.30 → annet-0.16.32}/MANIFEST.in +0 -0
- {annet-0.16.30 → annet-0.16.32}/README.md +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/__init__.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/adapters/__init__.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/adapters/fetchers/__init__.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/adapters/fetchers/stub/__init__.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/adapters/fetchers/stub/fetcher.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/adapters/file/__init__.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/adapters/file/provider.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/adapters/netbox/__init__.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/adapters/netbox/common/__init__.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/adapters/netbox/common/client.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/adapters/netbox/common/query.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/adapters/netbox/common/status_client.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/adapters/netbox/common/storage_opts.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/adapters/netbox/provider.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/adapters/netbox/v24/__init__.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/adapters/netbox/v24/storage.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/adapters/netbox/v37/__init__.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/adapters/netbox/v37/storage.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/annet.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/annlib/__init__.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/annlib/command.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/annlib/diff.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/annlib/errors.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/annlib/filter_acl.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/annlib/jsontools.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/annlib/lib.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/annlib/netdev/__init__.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/annlib/netdev/db.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/annlib/netdev/devdb/__init__.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/annlib/netdev/devdb/data/devdb.json +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/annlib/netdev/views/__init__.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/annlib/netdev/views/dump.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/annlib/netdev/views/hardware.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/annlib/output.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/annlib/patching.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/annlib/rbparser/__init__.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/annlib/rbparser/acl.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/annlib/rbparser/deploying.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/annlib/rbparser/ordering.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/annlib/rbparser/platform.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/annlib/rbparser/syntax.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/annlib/rulebook/__init__.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/annlib/rulebook/common.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/annlib/tabparser.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/annlib/types.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/argparse.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/cli.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/cli_args.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/configs/context.yml +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/configs/logging.yaml +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/connectors.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/deploy.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/diff.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/executor.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/filtering.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/gen.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/generators/__init__.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/generators/base.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/generators/common/__init__.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/generators/common/initial.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/generators/entire.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/generators/exceptions.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/generators/jsonfragment.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/generators/partial.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/generators/perf.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/generators/ref.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/generators/result.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/hardware.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/implicit.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/lib.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/mesh/__init__.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/mesh/match_args.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/mesh/models_converter.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/mesh/port_processor.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/output.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/parallel.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/patching.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/reference.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rpl/__init__.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rpl/action.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rpl/condition.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rpl/match_builder.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rpl/policy.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rpl/result.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rpl/routemap.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rpl/statement_builder.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rpl_generators/__init__.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rpl_generators/aspath.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rpl_generators/community.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rpl_generators/execute.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rpl_generators/rd.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/__init__.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/arista/__init__.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/arista/iface.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/aruba/__init__.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/aruba/ap_env.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/aruba/misc.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/b4com/__init__.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/b4com/file.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/cisco/__init__.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/cisco/iface.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/cisco/misc.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/cisco/vlandb.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/common.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/deploying.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/huawei/__init__.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/huawei/aaa.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/huawei/bgp.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/huawei/iface.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/huawei/misc.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/huawei/vlandb.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/juniper/__init__.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/nexus/__init__.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/nexus/iface.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/patching.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/ribbon/__init__.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/routeros/__init__.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/routeros/file.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/texts/arista.deploy +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/texts/arista.order +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/texts/arista.rul +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/texts/aruba.deploy +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/texts/aruba.order +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/texts/aruba.rul +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/texts/b4com.deploy +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/texts/b4com.order +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/texts/b4com.rul +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/texts/cisco.order +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/texts/cisco.rul +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/texts/huawei.deploy +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/texts/huawei.order +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/texts/huawei.rul +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/texts/juniper.rul +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/texts/nexus.deploy +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/texts/nexus.order +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/texts/nexus.rul +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/texts/nokia.rul +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/texts/optixtrans.deploy +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/texts/optixtrans.order +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/texts/optixtrans.rul +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/texts/pc.deploy +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/texts/pc.order +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/texts/pc.rul +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/texts/ribbon.deploy +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/texts/ribbon.rul +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/texts/routeros.order +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/rulebook/texts/routeros.rul +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/storage.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/tabparser.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/text_term_format.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/tracing.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet/types.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet.egg-info/SOURCES.txt +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet.egg-info/dependency_links.txt +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet.egg-info/entry_points.txt +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet.egg-info/requires.txt +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet.egg-info/top_level.txt +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet_generators/__init__.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet_generators/example/__init__.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet_generators/example/lldp.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet_generators/mesh_example/__init__.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet_generators/mesh_example/bgp.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet_generators/mesh_example/mesh_logic.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet_generators/rpl_example/__init__.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet_generators/rpl_example/generator.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet_generators/rpl_example/items.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet_generators/rpl_example/mesh.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/annet_generators/rpl_example/route_policy.py +0 -0
- {annet-0.16.30 → annet-0.16.32}/requirements.txt +0 -0
- {annet-0.16.30 → annet-0.16.32}/setup.cfg +0 -0
- {annet-0.16.30 → annet-0.16.32}/setup.py +0 -0
|
@@ -214,7 +214,7 @@ class NetboxDevice(Entity):
|
|
|
214
214
|
return type(self) is type(other) and self.url == other.url
|
|
215
215
|
|
|
216
216
|
def is_pc(self) -> bool:
|
|
217
|
-
return self.device_type.manufacturer.name
|
|
217
|
+
return self.device_type.manufacturer.name in ("Mellanox", "NVIDIA", "Moxa") or self.breed == "pc"
|
|
218
218
|
|
|
219
219
|
def _make_interface(self, name: str, type: InterfaceType) -> Interface:
|
|
220
220
|
return Interface(
|
|
@@ -274,12 +274,14 @@ def patch(args: cli_args.ShowPatchOptions, loader: ann_gen.Loader):
|
|
|
274
274
|
|
|
275
275
|
def _patch_worker(device_id, args: cli_args.ShowPatchOptions, stdin, loader: ann_gen.Loader, filterer: filtering.Filterer):
|
|
276
276
|
for res, _, patch_tree in res_diff_patch(device_id, args, stdin, loader, filterer):
|
|
277
|
+
old_files = res.old_files
|
|
277
278
|
new_files = res.get_new_files(args.acl_safe)
|
|
278
279
|
new_json_fragment_files = res.get_new_file_fragments(args.acl_safe)
|
|
279
280
|
if new_files:
|
|
280
281
|
for path, (cfg_text, _cmds) in new_files.items():
|
|
281
282
|
label = res.device.hostname + os.sep + path
|
|
282
|
-
|
|
283
|
+
if old_files.get(path) != cfg_text:
|
|
284
|
+
yield label, cfg_text, False
|
|
283
285
|
elif res.old_json_fragment_files or new_json_fragment_files:
|
|
284
286
|
for path, (new_json_cfg, _cmds) in new_json_fragment_files.items():
|
|
285
287
|
label = res.device.hostname + os.sep + path
|
|
@@ -468,6 +470,9 @@ class PCDeployerJob(DeployerJob):
|
|
|
468
470
|
elif not new_files and not new_json_fragment_files:
|
|
469
471
|
return
|
|
470
472
|
|
|
473
|
+
enable_reload = self.args.entire_reload is not cli_args.EntireReloadFlag.no
|
|
474
|
+
force_reload = self.args.entire_reload is cli_args.EntireReloadFlag.force
|
|
475
|
+
|
|
471
476
|
upload_files: Dict[str, bytes] = {}
|
|
472
477
|
reload_cmds: Dict[str, bytes] = {}
|
|
473
478
|
generator_types: Dict[str, GeneratorType] = {}
|
|
@@ -483,18 +488,23 @@ class PCDeployerJob(DeployerJob):
|
|
|
483
488
|
old_text = jsontools.format_json(old_json_cfg)
|
|
484
489
|
new_text = jsontools.format_json(file_content_or_json_cfg)
|
|
485
490
|
diff_content = "\n".join(_diff_file(old_text, new_text))
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
491
|
+
|
|
492
|
+
if diff_content or force_reload:
|
|
493
|
+
self._has_diff |= True
|
|
494
|
+
|
|
495
|
+
upload_files[file] = file_content.encode()
|
|
496
|
+
generator_types[file] = generator_type
|
|
497
|
+
self.cmd_lines.append("= %s/%s " % (device.hostname, file))
|
|
498
|
+
self.cmd_lines.extend([file_content, ""])
|
|
499
|
+
self.diff_lines.append("= %s/%s " % (device.hostname, file))
|
|
500
|
+
self.diff_lines.extend([diff_content, ""])
|
|
501
|
+
|
|
502
|
+
if enable_reload:
|
|
503
|
+
reload_cmds[file] = cmds.encode()
|
|
504
|
+
self.cmd_lines.append("= Deploy cmds %s/%s " % (device.hostname, file))
|
|
505
|
+
self.cmd_lines.extend([cmds, ""])
|
|
506
|
+
|
|
507
|
+
if self._has_diff:
|
|
498
508
|
self.deploy_cmds[device] = {
|
|
499
509
|
"files": upload_files,
|
|
500
510
|
"cmds": reload_cmds,
|
|
@@ -534,13 +544,12 @@ class Deployer:
|
|
|
534
544
|
self._filterer = filtering.filterer_connector.get()
|
|
535
545
|
|
|
536
546
|
def parse_result(self, job: DeployerJob, result: ann_gen.OldNewResult):
|
|
537
|
-
entire_reload = self.args.entire_reload
|
|
538
547
|
logger = get_logger(job.device.hostname)
|
|
539
548
|
|
|
540
549
|
job.parse_result(result)
|
|
541
550
|
self.failed_configs.update(job.failed_configs)
|
|
542
551
|
|
|
543
|
-
if job.has_diff()
|
|
552
|
+
if job.has_diff():
|
|
544
553
|
self.cmd_lines.extend(job.cmd_lines)
|
|
545
554
|
self.deploy_cmds.update(job.deploy_cmds)
|
|
546
555
|
self.diffs.update(job.diffs)
|
|
@@ -68,7 +68,7 @@ class BFDTimers:
|
|
|
68
68
|
multiplier: int = 4
|
|
69
69
|
|
|
70
70
|
|
|
71
|
-
Family = Literal["ipv4_unicast", "ipv6_unicast", "ipv4_labeled_unicast", "ipv6_labeled_unicast"]
|
|
71
|
+
Family = Literal["ipv4_unicast", "ipv6_unicast", "ipv4_labeled_unicast", "ipv6_labeled_unicast", "l2vpn_evpn"]
|
|
72
72
|
|
|
73
73
|
|
|
74
74
|
@dataclass(frozen=True)
|
|
@@ -207,7 +207,7 @@ class PeerGroup:
|
|
|
207
207
|
auth_key: bool = False
|
|
208
208
|
add_path: bool = False
|
|
209
209
|
multipath: bool = False
|
|
210
|
-
multihop:
|
|
210
|
+
multihop: Optional[int] = None
|
|
211
211
|
multihop_no_nexthop_change: bool = False
|
|
212
212
|
af_no_install: bool = False
|
|
213
213
|
bfd: bool = False
|
|
@@ -243,8 +243,10 @@ class VrfOptions:
|
|
|
243
243
|
ipv6_unicast: FamilyOptions
|
|
244
244
|
ipv4_labeled_unicast: FamilyOptions
|
|
245
245
|
ipv6_labeled_unicast: FamilyOptions
|
|
246
|
+
l2vpn_evpn: FamilyOptions
|
|
246
247
|
|
|
247
248
|
vrf_name_global: Optional[str] = None
|
|
249
|
+
l3vni: Optional[int] = None
|
|
248
250
|
as_path_relax: bool = False
|
|
249
251
|
rt_import: list[str] = field(default_factory=list)
|
|
250
252
|
rt_export: list[str] = field(default_factory=list)
|
|
@@ -262,6 +264,7 @@ class GlobalOptions:
|
|
|
262
264
|
ipv6_unicast: FamilyOptions
|
|
263
265
|
ipv4_labeled_unicast: FamilyOptions
|
|
264
266
|
ipv6_labeled_unicast: FamilyOptions
|
|
267
|
+
l2vpn_evpn: FamilyOptions
|
|
265
268
|
|
|
266
269
|
as_path_relax: bool = False
|
|
267
270
|
local_as: ASN = ASN(None)
|
|
@@ -299,6 +302,9 @@ def _used_redistribute_policies(opts: Union[GlobalOptions, VrfOptions]) -> Itera
|
|
|
299
302
|
for red in opts.ipv6_labeled_unicast.redistributes:
|
|
300
303
|
if red.policy:
|
|
301
304
|
yield red.policy
|
|
305
|
+
for red in opts.l2vpn_evpn.redistributes:
|
|
306
|
+
if red.policy:
|
|
307
|
+
yield red.policy
|
|
302
308
|
|
|
303
309
|
|
|
304
310
|
def extract_policies(config: BgpConfig) -> Sequence[str]:
|
|
@@ -133,6 +133,9 @@ class BaseMeshModel:
|
|
|
133
133
|
raise AttributeError(f"{self.__class__.__name__} has no field {key}")
|
|
134
134
|
super().__setattr__(key, value)
|
|
135
135
|
|
|
136
|
+
def is_empty(self):
|
|
137
|
+
return not self.__dict__
|
|
138
|
+
|
|
136
139
|
|
|
137
140
|
ModelT = TypeVar("ModelT", bound=BaseMeshModel)
|
|
138
141
|
|
|
@@ -43,11 +43,13 @@ class _FamiliesMixin:
|
|
|
43
43
|
kwargs.setdefault("ipv6_unicast", FamilyOptions(family="ipv6_unicast"))
|
|
44
44
|
kwargs.setdefault("ipv4_labeled_unicast", FamilyOptions(family="ipv4_labeled_unicast"))
|
|
45
45
|
kwargs.setdefault("ipv6_labeled_unicast", FamilyOptions(family="ipv6_labeled_unicast"))
|
|
46
|
+
kwargs.setdefault("l2vpn_evpn", FamilyOptions(family="l2vpn_evpn"))
|
|
46
47
|
super().__init__(**kwargs)
|
|
47
48
|
ipv4_unicast: Annotated[FamilyOptions, Merge()]
|
|
48
49
|
ipv6_unicast: Annotated[FamilyOptions, Merge()]
|
|
49
50
|
ipv4_labeled_unicast: Annotated[FamilyOptions, Merge()]
|
|
50
51
|
ipv6_labeled_unicast: Annotated[FamilyOptions, Merge()]
|
|
52
|
+
l2vpn_evpn: Annotated[FamilyOptions, Merge()]
|
|
51
53
|
|
|
52
54
|
|
|
53
55
|
class VrfOptions(_FamiliesMixin, BaseMeshModel):
|
|
@@ -56,6 +58,7 @@ class VrfOptions(_FamiliesMixin, BaseMeshModel):
|
|
|
56
58
|
kwargs.setdefault("ipv6_unicast", FamilyOptions(family="ipv6_unicast", vrf_name=vrf_name))
|
|
57
59
|
kwargs.setdefault("ipv4_labeled_unicast", FamilyOptions(family="ipv4_labeled_unicast", vrf_name=vrf_name))
|
|
58
60
|
kwargs.setdefault("ipv6_labeled_unicast", FamilyOptions(family="ipv6_labeled_unicast", vrf_name=vrf_name))
|
|
61
|
+
kwargs.setdefault("l2vpn_evpn", FamilyOptions(family="l2vpn_evpn", vrf_name=vrf_name))
|
|
59
62
|
kwargs.setdefault("groups", KeyDefaultDict(lambda x: MeshPeerGroup(name=x)))
|
|
60
63
|
super().__init__(vrf_name=vrf_name, **kwargs)
|
|
61
64
|
|
|
@@ -64,6 +67,7 @@ class VrfOptions(_FamiliesMixin, BaseMeshModel):
|
|
|
64
67
|
as_path_relax: bool
|
|
65
68
|
import_policy: Optional[str]
|
|
66
69
|
export_policy: Optional[str]
|
|
70
|
+
l3vni: Optional[int]
|
|
67
71
|
rt_import: Annotated[tuple[str, ...], Concat()]
|
|
68
72
|
rt_export: Annotated[tuple[str, ...], Concat()]
|
|
69
73
|
rt_import_v4: Annotated[tuple[str, ...], Concat()]
|
|
@@ -57,6 +57,11 @@ class MeshExecutor:
|
|
|
57
57
|
rule_global_opts = MeshGlobalOptions(rule.match, device)
|
|
58
58
|
logger.debug("Running device handler: %s", handler_name)
|
|
59
59
|
rule.handler(rule_global_opts)
|
|
60
|
+
|
|
61
|
+
if rule_global_opts.is_empty():
|
|
62
|
+
# nothing was set
|
|
63
|
+
continue
|
|
64
|
+
|
|
60
65
|
try:
|
|
61
66
|
global_opts = merge(global_opts, rule_global_opts)
|
|
62
67
|
except MergeForbiddenError as e:
|
|
@@ -79,7 +84,7 @@ class MeshExecutor:
|
|
|
79
84
|
rule: MatchedDirectPair,
|
|
80
85
|
ports: list[tuple[str, str]],
|
|
81
86
|
all_connected_ports: list[tuple[str, str]],
|
|
82
|
-
) -> Pair:
|
|
87
|
+
) -> Optional[Pair]:
|
|
83
88
|
session = MeshSession()
|
|
84
89
|
handler_name = self._handler_name(rule.handler)
|
|
85
90
|
logger.debug("Running direct handler: %s", handler_name)
|
|
@@ -102,6 +107,10 @@ class MeshExecutor:
|
|
|
102
107
|
else:
|
|
103
108
|
rule.handler(peer_neighbor, peer_device, session)
|
|
104
109
|
|
|
110
|
+
if peer_neighbor.is_empty() and peer_device.is_empty() and session.is_empty():
|
|
111
|
+
# nothing was set
|
|
112
|
+
return None
|
|
113
|
+
|
|
105
114
|
try:
|
|
106
115
|
neighbor_dto = merge(DirectPeerDTO(), peer_neighbor, session)
|
|
107
116
|
except MergeForbiddenError as e:
|
|
@@ -144,6 +153,9 @@ class MeshExecutor:
|
|
|
144
153
|
]
|
|
145
154
|
for ports in rule.port_processor(all_connected_ports):
|
|
146
155
|
pair = self._execute_direct_pair(device, neighbor_device, rule, ports, all_connected_ports)
|
|
156
|
+
if pair is None:
|
|
157
|
+
# nothing was set
|
|
158
|
+
continue
|
|
147
159
|
addr = getattr(pair.connected, "addr", None)
|
|
148
160
|
if addr is None:
|
|
149
161
|
raise ValueError(f"Handler `{handler_name}` returned no peer addr")
|
|
@@ -179,6 +191,9 @@ class MeshExecutor:
|
|
|
179
191
|
peer_virtual = VirtualPeer(num=order_number)
|
|
180
192
|
|
|
181
193
|
rule.handler(peer_device, peer_virtual, session)
|
|
194
|
+
if peer_virtual.is_empty() and peer_device.is_empty() and session.is_empty():
|
|
195
|
+
# nothing was set
|
|
196
|
+
continue
|
|
182
197
|
|
|
183
198
|
try:
|
|
184
199
|
virtual_dto = merge(VirtualPeerDTO(), peer_virtual, session)
|
|
@@ -232,6 +247,10 @@ class MeshExecutor:
|
|
|
232
247
|
peer_device = IndirectPeer(rule.match_right, device)
|
|
233
248
|
rule.handler(peer_connected, peer_device, session)
|
|
234
249
|
|
|
250
|
+
if peer_connected.is_empty() and peer_device.is_empty() and session.is_empty():
|
|
251
|
+
# nothing was set
|
|
252
|
+
continue
|
|
253
|
+
|
|
235
254
|
try:
|
|
236
255
|
connected_dto = merge(IndirectPeerDTO(), peer_connected, session)
|
|
237
256
|
except MergeForbiddenError as e:
|
|
@@ -3,7 +3,7 @@ from typing import Literal, Annotated, Union, Optional
|
|
|
3
3
|
from .basemodel import BaseMeshModel, Concat, Unite
|
|
4
4
|
from ..bgp_models import BFDTimers
|
|
5
5
|
|
|
6
|
-
FamilyName = Literal["ipv4_unicast", "ipv6_unicast", "ipv4_labeled_unicast", "ipv6_labeled_unicast"]
|
|
6
|
+
FamilyName = Literal["ipv4_unicast", "ipv6_unicast", "ipv4_labeled_unicast", "ipv6_labeled_unicast", "l2vpn_evpn"]
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class _SharedOptionsDTO(BaseMeshModel):
|
|
@@ -53,7 +53,7 @@ class _OptionsDTO(_SharedOptionsDTO):
|
|
|
53
53
|
advertise_bgp_static: bool
|
|
54
54
|
allowas_in: bool
|
|
55
55
|
auth_key: bool
|
|
56
|
-
multihop:
|
|
56
|
+
multihop: Optional[int]
|
|
57
57
|
multihop_no_nexthop_change: bool
|
|
58
58
|
af_no_install: bool
|
|
59
59
|
rib: bool
|
|
@@ -101,7 +101,7 @@ class IndirectPeerDTO(MeshSession, _OptionsDTO):
|
|
|
101
101
|
|
|
102
102
|
|
|
103
103
|
class VirtualLocalDTO(_OptionsDTO):
|
|
104
|
-
asnum: int
|
|
104
|
+
asnum: Union[int, str]
|
|
105
105
|
pod: int
|
|
106
106
|
addr: str
|
|
107
107
|
description: str
|
|
@@ -21,6 +21,9 @@ class DirectPeer(DirectPeerDTO):
|
|
|
21
21
|
self.ports = ports
|
|
22
22
|
self.all_connected_ports = all_connected_ports
|
|
23
23
|
|
|
24
|
+
def is_empty(self):
|
|
25
|
+
return self.__dict__.keys() == {"match", "device", "ports", "all_connected_ports"}
|
|
26
|
+
|
|
24
27
|
|
|
25
28
|
class IndirectPeer(IndirectPeerDTO):
|
|
26
29
|
match: MatchedArgs
|
|
@@ -31,6 +34,9 @@ class IndirectPeer(IndirectPeerDTO):
|
|
|
31
34
|
self.match = match
|
|
32
35
|
self.device = device
|
|
33
36
|
|
|
37
|
+
def is_empty(self):
|
|
38
|
+
return self.__dict__.keys() == {"match", "device"}
|
|
39
|
+
|
|
34
40
|
|
|
35
41
|
class VirtualLocal(VirtualLocalDTO):
|
|
36
42
|
match: MatchedArgs
|
|
@@ -41,10 +47,16 @@ class VirtualLocal(VirtualLocalDTO):
|
|
|
41
47
|
self.match = match
|
|
42
48
|
self.device = device
|
|
43
49
|
|
|
50
|
+
def is_empty(self):
|
|
51
|
+
return self.__dict__.keys() == {"match", "device"}
|
|
52
|
+
|
|
44
53
|
|
|
45
54
|
class VirtualPeer(VirtualPeerDTO):
|
|
46
55
|
num: int
|
|
47
56
|
|
|
57
|
+
def is_empty(self):
|
|
58
|
+
return self.__dict__.keys() == {"num"}
|
|
59
|
+
|
|
48
60
|
|
|
49
61
|
class GlobalOptions(GlobalOptionsDTO):
|
|
50
62
|
match: MatchedArgs
|
|
@@ -55,6 +67,9 @@ class GlobalOptions(GlobalOptionsDTO):
|
|
|
55
67
|
self.match = match
|
|
56
68
|
self.device = device
|
|
57
69
|
|
|
70
|
+
def is_empty(self):
|
|
71
|
+
return self.__dict__.keys() == {"match", "device"}
|
|
72
|
+
|
|
58
73
|
|
|
59
74
|
GlobalHandler = Callable[[GlobalOptions], None]
|
|
60
75
|
|
|
@@ -11,10 +11,10 @@ from annet.rpl.statement_builder import NextHopActionValue, AsPathActionValue, C
|
|
|
11
11
|
from .aspath import get_used_as_path_filters
|
|
12
12
|
from .community import get_used_united_community_lists
|
|
13
13
|
from .entities import (
|
|
14
|
-
AsPathFilter, IpPrefixList,
|
|
15
|
-
mangle_united_community_list_name,
|
|
14
|
+
AsPathFilter, IpPrefixList, CommunityList, CommunityLogic, CommunityType,
|
|
15
|
+
mangle_united_community_list_name, PrefixListNameGenerator,
|
|
16
16
|
)
|
|
17
|
-
from .prefix_lists import get_used_prefix_lists
|
|
17
|
+
from .prefix_lists import get_used_prefix_lists, new_prefix_list_name_generator
|
|
18
18
|
|
|
19
19
|
FRR_RESULT_MAP = {
|
|
20
20
|
ResultType.ALLOW: "permit",
|
|
@@ -51,6 +51,12 @@ class CumulusPolicyGenerator(ABC):
|
|
|
51
51
|
def get_prefix_lists(self, device: Any) -> Sequence[IpPrefixList]:
|
|
52
52
|
raise NotImplementedError()
|
|
53
53
|
|
|
54
|
+
def get_used_prefix_lists(self, device: Any, name_generator: PrefixListNameGenerator) -> Sequence[IpPrefixList]:
|
|
55
|
+
return get_used_prefix_lists(
|
|
56
|
+
prefix_lists=self.get_prefix_lists(device),
|
|
57
|
+
name_generator=name_generator,
|
|
58
|
+
)
|
|
59
|
+
|
|
54
60
|
@abstractmethod
|
|
55
61
|
def get_community_lists(self, device: Any) -> list[CommunityList]:
|
|
56
62
|
raise NotImplementedError()
|
|
@@ -61,11 +67,13 @@ class CumulusPolicyGenerator(ABC):
|
|
|
61
67
|
|
|
62
68
|
def generate_cumulus_rpl(self, device: Any) -> Iterator[Sequence[str]]:
|
|
63
69
|
policies = self.get_policies(device)
|
|
70
|
+
prefix_list_name_generator = new_prefix_list_name_generator(policies)
|
|
71
|
+
|
|
64
72
|
communities = {c.name: c for c in self.get_community_lists(device)}
|
|
65
73
|
yield from self._cumulus_as_path_filters(device, policies)
|
|
66
74
|
yield from self._cumulus_communities(device, communities, policies)
|
|
67
|
-
yield from self._cumulus_prefix_lists(device, policies)
|
|
68
|
-
yield from self._cumulus_policy_config(device, communities, policies)
|
|
75
|
+
yield from self._cumulus_prefix_lists(device, policies, prefix_list_name_generator)
|
|
76
|
+
yield from self._cumulus_policy_config(device, communities, policies, prefix_list_name_generator)
|
|
69
77
|
|
|
70
78
|
def _cumulus_as_path_filters(
|
|
71
79
|
self,
|
|
@@ -100,11 +108,12 @@ class CumulusPolicyGenerator(ABC):
|
|
|
100
108
|
("le", str(match.less_equal)) if match.less_equal is not None else ()
|
|
101
109
|
)
|
|
102
110
|
|
|
103
|
-
def _cumulus_prefix_lists(
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
111
|
+
def _cumulus_prefix_lists(
|
|
112
|
+
self, device: Any,
|
|
113
|
+
policies: list[RoutingPolicy],
|
|
114
|
+
prefix_list_name_generator: PrefixListNameGenerator,
|
|
115
|
+
) -> Iterable[Sequence[str]]:
|
|
116
|
+
plists = {p.name: p for p in self.get_used_prefix_lists(device, prefix_list_name_generator)}
|
|
108
117
|
if not plists.values():
|
|
109
118
|
return
|
|
110
119
|
|
|
@@ -114,7 +123,7 @@ class CumulusPolicyGenerator(ABC):
|
|
|
114
123
|
cond: SingleCondition[PrefixMatchValue]
|
|
115
124
|
for cond in statement.match.find_all(MatchField.ip_prefix):
|
|
116
125
|
for name in cond.value.names:
|
|
117
|
-
mangled_name =
|
|
126
|
+
mangled_name = prefix_list_name_generator.get_prefix_name(
|
|
118
127
|
name=name,
|
|
119
128
|
greater_equal=cond.value.greater_equal,
|
|
120
129
|
less_equal=cond.value.less_equal,
|
|
@@ -125,7 +134,7 @@ class CumulusPolicyGenerator(ABC):
|
|
|
125
134
|
precessed_names.add(mangled_name)
|
|
126
135
|
for cond in statement.match.find_all(MatchField.ipv6_prefix):
|
|
127
136
|
for name in cond.value.names:
|
|
128
|
-
mangled_name =
|
|
137
|
+
mangled_name = prefix_list_name_generator.get_prefix_name(
|
|
129
138
|
name=name,
|
|
130
139
|
greater_equal=cond.value.greater_equal,
|
|
131
140
|
less_equal=cond.value.less_equal,
|
|
@@ -217,6 +226,7 @@ class CumulusPolicyGenerator(ABC):
|
|
|
217
226
|
self,
|
|
218
227
|
device: Any,
|
|
219
228
|
condition: SingleCondition[Any],
|
|
229
|
+
prefix_list_name_generator: PrefixListNameGenerator,
|
|
220
230
|
) -> Iterator[Sequence[str]]:
|
|
221
231
|
if condition.field == MatchField.community:
|
|
222
232
|
for comm_name in self._get_match_community_names(condition):
|
|
@@ -236,7 +246,7 @@ class CumulusPolicyGenerator(ABC):
|
|
|
236
246
|
return
|
|
237
247
|
if condition.field == MatchField.ip_prefix:
|
|
238
248
|
for name in condition.value.names:
|
|
239
|
-
mangled_name =
|
|
249
|
+
mangled_name = prefix_list_name_generator.get_prefix_name(
|
|
240
250
|
name=name,
|
|
241
251
|
greater_equal=condition.value.greater_equal,
|
|
242
252
|
less_equal=condition.value.less_equal,
|
|
@@ -245,7 +255,7 @@ class CumulusPolicyGenerator(ABC):
|
|
|
245
255
|
return
|
|
246
256
|
if condition.field == MatchField.ipv6_prefix:
|
|
247
257
|
for name in condition.value.names:
|
|
248
|
-
mangled_name =
|
|
258
|
+
mangled_name = prefix_list_name_generator.get_prefix_name(
|
|
249
259
|
name=name,
|
|
250
260
|
greater_equal=condition.value.greater_equal,
|
|
251
261
|
less_equal=condition.value.less_equal,
|
|
@@ -268,7 +278,7 @@ class CumulusPolicyGenerator(ABC):
|
|
|
268
278
|
action: SingleAction[CommunityActionValue],
|
|
269
279
|
) -> Iterator[Sequence[str]]:
|
|
270
280
|
if action.value.replaced is not None:
|
|
271
|
-
if action.value.added or action.value.
|
|
281
|
+
if action.value.added or action.value.removed:
|
|
272
282
|
raise NotImplementedError(
|
|
273
283
|
"Cannot set community together with add/replace on cumulus",
|
|
274
284
|
)
|
|
@@ -421,11 +431,12 @@ class CumulusPolicyGenerator(ABC):
|
|
|
421
431
|
device: Any,
|
|
422
432
|
policy: RoutingPolicy,
|
|
423
433
|
statement: RoutingPolicyStatement,
|
|
434
|
+
prefix_list_name_generator: PrefixListNameGenerator,
|
|
424
435
|
) -> Iterable[Sequence[str]]:
|
|
425
436
|
yield "route-map", policy.name, FRR_RESULT_MAP[statement.result], str(statement.number)
|
|
426
437
|
|
|
427
438
|
for condition in statement.match:
|
|
428
|
-
for row in self._cumulus_policy_match(device, condition):
|
|
439
|
+
for row in self._cumulus_policy_match(device, condition, prefix_list_name_generator):
|
|
429
440
|
yield FRR_INDENT, *row
|
|
430
441
|
for action in statement.then:
|
|
431
442
|
for row in self._cumulus_policy_then(communities, device, action):
|
|
@@ -439,6 +450,7 @@ class CumulusPolicyGenerator(ABC):
|
|
|
439
450
|
device: Any,
|
|
440
451
|
communities: dict[str, CommunityList],
|
|
441
452
|
policies: list[RoutingPolicy],
|
|
453
|
+
prefix_list_name_generator: PrefixListNameGenerator,
|
|
442
454
|
) -> Iterable[Sequence[str]]:
|
|
443
455
|
""" Route maps configuration """
|
|
444
456
|
|
|
@@ -454,5 +466,7 @@ class CumulusPolicyGenerator(ABC):
|
|
|
454
466
|
raise RuntimeError(
|
|
455
467
|
f"Multiple statements have same number {statement.number} for policy `{policy.name}`: "
|
|
456
468
|
f"`{statement.name}` and `{applied_stmts[statement.number]}`")
|
|
457
|
-
yield from self._cumulus_policy_statement(
|
|
469
|
+
yield from self._cumulus_policy_statement(
|
|
470
|
+
communities, device, policy, statement, prefix_list_name_generator,
|
|
471
|
+
)
|
|
458
472
|
applied_stmts[statement.number] = statement.name
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from collections import defaultdict
|
|
1
2
|
from collections.abc import Sequence
|
|
2
3
|
from dataclasses import dataclass
|
|
3
4
|
from enum import Enum
|
|
@@ -56,15 +57,27 @@ def mangle_united_community_list_name(values: Sequence[str]) -> str:
|
|
|
56
57
|
return "_OR_".join(values)
|
|
57
58
|
|
|
58
59
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
60
|
+
class PrefixListNameGenerator:
|
|
61
|
+
def __init__(self):
|
|
62
|
+
self._prefix_lists = defaultdict(set)
|
|
63
|
+
|
|
64
|
+
def add_prefix(self, name: str, greater_equal: Optional[int], less_equal: Optional[int]) -> None:
|
|
65
|
+
self._prefix_lists[name].add((greater_equal, less_equal))
|
|
66
|
+
|
|
67
|
+
def is_used(self, name: str):
|
|
68
|
+
return name in self._prefix_lists
|
|
69
|
+
|
|
70
|
+
def get_prefix_name(self, name: str, greater_equal: Optional[int], less_equal: Optional[int]) -> str:
|
|
71
|
+
if len(self._prefix_lists[name]) == 1:
|
|
72
|
+
return name
|
|
73
|
+
if greater_equal is less_equal is None:
|
|
74
|
+
return name
|
|
75
|
+
if greater_equal is None:
|
|
76
|
+
ge_str = "unset"
|
|
77
|
+
else:
|
|
78
|
+
ge_str = str(greater_equal)
|
|
79
|
+
if less_equal is None:
|
|
80
|
+
le_str = "unset"
|
|
81
|
+
else:
|
|
82
|
+
le_str = str(less_equal)
|
|
83
|
+
return f"{name}_{ge_str}_{le_str}"
|