annet 2.0.0__tar.gz → 2.0.1__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-2.0.0/annet.egg-info → annet-2.0.1}/PKG-INFO +1 -1
- {annet-2.0.0 → annet-2.0.1}/annet/adapters/netbox/provider.py +2 -1
- {annet-2.0.0 → annet-2.0.1}/annet/annlib/filter_acl.py +6 -2
- {annet-2.0.0 → annet-2.0.1}/annet/rpl/statement_builder.py +17 -2
- {annet-2.0.0 → annet-2.0.1}/annet/rpl_generators/cumulus_frr.py +41 -2
- {annet-2.0.0 → annet-2.0.1}/annet/rpl_generators/entities.py +11 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rpl_generators/policy.py +99 -1
- {annet-2.0.0 → annet-2.0.1/annet.egg-info}/PKG-INFO +1 -1
- {annet-2.0.0 → annet-2.0.1}/AUTHORS +0 -0
- {annet-2.0.0 → annet-2.0.1}/LICENSE +0 -0
- {annet-2.0.0 → annet-2.0.1}/MANIFEST.in +0 -0
- {annet-2.0.0 → annet-2.0.1}/README.md +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/__init__.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/adapters/__init__.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/adapters/fetchers/__init__.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/adapters/fetchers/stub/__init__.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/adapters/fetchers/stub/fetcher.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/adapters/file/__init__.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/adapters/file/provider.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/adapters/netbox/__init__.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/adapters/netbox/common/__init__.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/adapters/netbox/common/client.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/adapters/netbox/common/manufacturer.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/adapters/netbox/common/models.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/adapters/netbox/common/query.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/adapters/netbox/common/status_client.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/adapters/netbox/common/storage_opts.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/adapters/netbox/v24/__init__.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/adapters/netbox/v24/storage.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/adapters/netbox/v37/__init__.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/adapters/netbox/v37/storage.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/annet.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/annlib/__init__.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/annlib/command.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/annlib/diff.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/annlib/errors.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/annlib/jsontools.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/annlib/lib.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/annlib/netdev/__init__.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/annlib/netdev/db.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/annlib/netdev/devdb/__init__.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/annlib/netdev/devdb/data/devdb.json +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/annlib/netdev/views/__init__.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/annlib/netdev/views/dump.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/annlib/netdev/views/hardware.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/annlib/output.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/annlib/patching.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/annlib/rbparser/__init__.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/annlib/rbparser/acl.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/annlib/rbparser/deploying.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/annlib/rbparser/ordering.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/annlib/rbparser/platform.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/annlib/rbparser/syntax.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/annlib/rulebook/__init__.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/annlib/rulebook/common.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/annlib/tabparser.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/annlib/types.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/api/__init__.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/argparse.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/bgp_models.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/cli.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/cli_args.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/configs/context.yml +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/configs/logging.yaml +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/connectors.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/deploy.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/deploy_ui.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/diff.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/executor.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/filtering.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/gen.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/generators/__init__.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/generators/base.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/generators/common/__init__.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/generators/common/initial.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/generators/entire.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/generators/exceptions.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/generators/jsonfragment.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/generators/partial.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/generators/perf.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/generators/ref.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/generators/result.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/hardware.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/implicit.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/lib.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/mesh/__init__.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/mesh/basemodel.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/mesh/device_models.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/mesh/executor.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/mesh/match_args.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/mesh/models_converter.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/mesh/peer_models.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/mesh/port_processor.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/mesh/registry.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/output.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/parallel.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/patching.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/reference.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rpl/__init__.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rpl/action.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rpl/condition.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rpl/match_builder.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rpl/policy.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rpl/result.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rpl/routemap.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rpl_generators/__init__.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rpl_generators/aspath.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rpl_generators/community.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rpl_generators/execute.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rpl_generators/prefix_lists.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rpl_generators/rd.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/__init__.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/arista/__init__.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/arista/aaa.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/arista/iface.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/aruba/__init__.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/aruba/ap_env.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/aruba/misc.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/b4com/__init__.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/b4com/file.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/b4com/iface.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/cisco/__init__.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/cisco/iface.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/cisco/misc.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/cisco/vlandb.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/common.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/deploying.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/huawei/__init__.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/huawei/aaa.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/huawei/bgp.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/huawei/iface.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/huawei/misc.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/huawei/vlandb.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/juniper/__init__.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/nexus/__init__.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/nexus/iface.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/patching.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/routeros/__init__.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/routeros/file.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/texts/arista.deploy +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/texts/arista.order +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/texts/arista.rul +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/texts/aruba.deploy +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/texts/aruba.order +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/texts/aruba.rul +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/texts/b4com.deploy +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/texts/b4com.order +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/texts/b4com.rul +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/texts/cisco.deploy +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/texts/cisco.order +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/texts/cisco.rul +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/texts/huawei.deploy +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/texts/huawei.order +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/texts/huawei.rul +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/texts/juniper.order +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/texts/juniper.rul +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/texts/nexus.deploy +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/texts/nexus.order +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/texts/nexus.rul +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/texts/nokia.rul +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/texts/optixtrans.deploy +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/texts/optixtrans.order +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/texts/optixtrans.rul +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/texts/pc.deploy +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/texts/pc.order +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/texts/pc.rul +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/texts/ribbon.deploy +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/texts/ribbon.rul +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/texts/routeros.order +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/rulebook/texts/routeros.rul +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/storage.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/tabparser.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/text_term_format.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/tracing.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet/types.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet.egg-info/SOURCES.txt +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet.egg-info/dependency_links.txt +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet.egg-info/entry_points.txt +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet.egg-info/requires.txt +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet.egg-info/top_level.txt +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet_generators/__init__.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet_generators/example/__init__.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet_generators/example/lldp.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet_generators/mesh_example/__init__.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet_generators/mesh_example/bgp.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet_generators/mesh_example/mesh_logic.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet_generators/rpl_example/__init__.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet_generators/rpl_example/generator.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet_generators/rpl_example/items.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet_generators/rpl_example/mesh.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/annet_generators/rpl_example/route_policy.py +0 -0
- {annet-2.0.0 → annet-2.0.1}/requirements.txt +0 -0
- {annet-2.0.0 → annet-2.0.1}/setup.cfg +0 -0
- {annet-2.0.0 → annet-2.0.1}/setup.py +0 -0
|
@@ -28,11 +28,12 @@ def storage_factory(opts: NetboxStorageOpts) -> Storage:
|
|
|
28
28
|
|
|
29
29
|
class NetboxProvider(StorageProvider, AdapterWithName, AdapterWithConfig):
|
|
30
30
|
def __init__(self, url: Optional[str] = None, token: Optional[str] = None, insecure: bool = False,
|
|
31
|
-
exact_host_filter: bool = False):
|
|
31
|
+
exact_host_filter: bool = False, threads: int = 1):
|
|
32
32
|
self.url = url
|
|
33
33
|
self.token = token
|
|
34
34
|
self.insecure = insecure
|
|
35
35
|
self.exact_host_filter = exact_host_filter
|
|
36
|
+
self.threads = threads
|
|
36
37
|
|
|
37
38
|
@classmethod
|
|
38
39
|
def with_config(cls, **kwargs: Dict[str, Any]) -> T:
|
|
@@ -32,7 +32,6 @@ def filter_config(acl: Acl, fmtr: tabparser.CommonFormatter, input_config: Input
|
|
|
32
32
|
config = patching.apply_acl(config, acl, fatal_acl=False)
|
|
33
33
|
config = fmtr.join(config)
|
|
34
34
|
else:
|
|
35
|
-
config = typing.cast(input_config, FileConfigTree)
|
|
36
35
|
config = apply_acl_fileconfig(input_config, acl)
|
|
37
36
|
return config
|
|
38
37
|
|
|
@@ -47,7 +46,6 @@ def filter_diff(acl: Acl, fmtr: tabparser.CommonFormatter, input_config: InputCo
|
|
|
47
46
|
config = unshift_op(config)
|
|
48
47
|
config = config.rstrip()
|
|
49
48
|
else:
|
|
50
|
-
config = typing.cast(input_config, FileConfigTree)
|
|
51
49
|
config = apply_acl_fileconfig(input_config, acl)
|
|
52
50
|
return config
|
|
53
51
|
|
|
@@ -108,17 +106,23 @@ def get_op(line: str) -> typing.Tuple[str, str, str]:
|
|
|
108
106
|
indent = ""
|
|
109
107
|
opidx = -1
|
|
110
108
|
rowstart = 0
|
|
109
|
+
|
|
111
110
|
for rowstart in range(len(line)):
|
|
111
|
+
if line[rowstart] != " " and line[0:rowstart].strip():
|
|
112
|
+
break
|
|
112
113
|
if line[rowstart] not in diff_ops:
|
|
113
114
|
break
|
|
115
|
+
|
|
114
116
|
for opidx in range(rowstart):
|
|
115
117
|
if line[opidx] != " ":
|
|
116
118
|
break
|
|
119
|
+
|
|
117
120
|
if opidx >= 0:
|
|
118
121
|
op = line[opidx]
|
|
119
122
|
indent = line[:opidx] + line[opidx + 1:rowstart]
|
|
120
123
|
if op != " ":
|
|
121
124
|
indent = indent + " "
|
|
125
|
+
|
|
122
126
|
return op, indent, line[rowstart:]
|
|
123
127
|
|
|
124
128
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import warnings
|
|
1
2
|
from collections.abc import Callable
|
|
2
3
|
from dataclasses import dataclass
|
|
3
4
|
from dataclasses import field
|
|
@@ -14,6 +15,7 @@ class ThenField(str, Enum):
|
|
|
14
15
|
large_community = "large_community"
|
|
15
16
|
extcommunity_rt = "extcommunity_rt"
|
|
16
17
|
extcommunity_soo = "extcommunity_soo"
|
|
18
|
+
extcommunity = "extcommunity"
|
|
17
19
|
as_path = "as_path"
|
|
18
20
|
local_pref = "local_pref"
|
|
19
21
|
metric = "metric"
|
|
@@ -144,6 +146,7 @@ class StatementBuilder:
|
|
|
144
146
|
self._added_as_path: list[int] = []
|
|
145
147
|
self._community = CommunityActionValue()
|
|
146
148
|
self._large_community = CommunityActionValue()
|
|
149
|
+
self._extcommunity = CommunityActionValue()
|
|
147
150
|
self._extcommunity_rt = CommunityActionValue()
|
|
148
151
|
self._extcommunity_soo = CommunityActionValue()
|
|
149
152
|
self._as_path = AsPathActionValue()
|
|
@@ -165,12 +168,18 @@ class StatementBuilder:
|
|
|
165
168
|
def large_community(self) -> CommunityActionBuilder:
|
|
166
169
|
return CommunityActionBuilder(self._large_community)
|
|
167
170
|
|
|
171
|
+
@property
|
|
172
|
+
def extcommunity(self) -> CommunityActionBuilder:
|
|
173
|
+
return CommunityActionBuilder(self._extcommunity)
|
|
174
|
+
|
|
168
175
|
@property
|
|
169
176
|
def extcommunity_rt(self) -> CommunityActionBuilder:
|
|
177
|
+
warnings.warn("extcommunity_rt is deprecated, use extcommunity", DeprecationWarning, stacklevel=2)
|
|
170
178
|
return CommunityActionBuilder(self._extcommunity_rt)
|
|
171
179
|
|
|
172
180
|
@property
|
|
173
181
|
def extcommunity_soo(self) -> CommunityActionBuilder:
|
|
182
|
+
warnings.warn("extcommunity_soo is deprecated, use extcommunity", DeprecationWarning, stacklevel=2)
|
|
174
183
|
return CommunityActionBuilder(self._extcommunity_soo)
|
|
175
184
|
|
|
176
185
|
def _set(self, field: str, value: ValueT) -> None:
|
|
@@ -244,7 +253,13 @@ class StatementBuilder:
|
|
|
244
253
|
self._statement.then.append(SingleAction(
|
|
245
254
|
field=ThenField.large_community,
|
|
246
255
|
type=ActionType.CUSTOM,
|
|
247
|
-
value=self.
|
|
256
|
+
value=self._large_community,
|
|
257
|
+
))
|
|
258
|
+
if self._extcommunity:
|
|
259
|
+
self._statement.then.append(SingleAction(
|
|
260
|
+
field=ThenField.extcommunity,
|
|
261
|
+
type=ActionType.CUSTOM,
|
|
262
|
+
value=self._extcommunity,
|
|
248
263
|
))
|
|
249
264
|
if self._extcommunity_rt:
|
|
250
265
|
self._statement.then.append(SingleAction(
|
|
@@ -256,7 +271,7 @@ class StatementBuilder:
|
|
|
256
271
|
self._statement.then.append(SingleAction(
|
|
257
272
|
field=ThenField.extcommunity_soo,
|
|
258
273
|
type=ActionType.CUSTOM,
|
|
259
|
-
value=self.
|
|
274
|
+
value=self._extcommunity_soo,
|
|
260
275
|
))
|
|
261
276
|
if self._as_path:
|
|
262
277
|
self._statement.then.append(SingleAction(
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from abc import abstractmethod, ABC
|
|
2
|
+
from collections import defaultdict
|
|
2
3
|
from collections.abc import Sequence
|
|
3
4
|
from ipaddress import ip_interface
|
|
4
5
|
from typing import Any, Literal, Iterable, Iterator, Optional, cast
|
|
@@ -12,7 +13,7 @@ from .aspath import get_used_as_path_filters
|
|
|
12
13
|
from .community import get_used_united_community_lists
|
|
13
14
|
from .entities import (
|
|
14
15
|
AsPathFilter, IpPrefixList, CommunityList, CommunityLogic, CommunityType,
|
|
15
|
-
mangle_united_community_list_name, PrefixListNameGenerator,
|
|
16
|
+
mangle_united_community_list_name, PrefixListNameGenerator, group_community_members,
|
|
16
17
|
)
|
|
17
18
|
|
|
18
19
|
|
|
@@ -307,9 +308,44 @@ class CumulusPolicyGenerator(ABC):
|
|
|
307
308
|
raise NotImplementedError("Replacing SOO extcommunity is not supported for Cumulus")
|
|
308
309
|
for community_name in action.value.added:
|
|
309
310
|
yield "set", "extcommunity soo", community_name, "additive"
|
|
310
|
-
|
|
311
|
+
if action.value.removed:
|
|
311
312
|
raise NotImplementedError("SOO extcommunity remove is not supported for Cumulus")
|
|
312
313
|
|
|
314
|
+
def _cumulus_extcommunity_type_str(self, comm_type: CommunityType) -> str:
|
|
315
|
+
if comm_type is CommunityType.SOO:
|
|
316
|
+
return "soo"
|
|
317
|
+
elif comm_type is CommunityType.RT:
|
|
318
|
+
return "rt"
|
|
319
|
+
elif comm_type is CommunityType.LARGE:
|
|
320
|
+
raise ValueError("Large community is not subtype of extcommunity")
|
|
321
|
+
elif comm_type is CommunityType.BASIC:
|
|
322
|
+
raise ValueError("Basic community is not subtype of extcommunity")
|
|
323
|
+
else:
|
|
324
|
+
raise NotImplementedError(f"Community type {comm_type} is not supported on cumulus")
|
|
325
|
+
|
|
326
|
+
def _cumulus_then_extcommunity(
|
|
327
|
+
self,
|
|
328
|
+
communities: dict[str, CommunityList],
|
|
329
|
+
device: Any,
|
|
330
|
+
action: SingleAction[CommunityActionValue],
|
|
331
|
+
):
|
|
332
|
+
if action.value.replaced is not None:
|
|
333
|
+
if action.value.added or action.value.removed:
|
|
334
|
+
raise NotImplementedError(
|
|
335
|
+
"Cannot set extcommunity together with add/delete on cumulus",
|
|
336
|
+
)
|
|
337
|
+
if not action.value.replaced:
|
|
338
|
+
yield "set", "extcommunity", "none"
|
|
339
|
+
return
|
|
340
|
+
members = group_community_members(communities, action.value.replaced)
|
|
341
|
+
for community_type, replaced_members in members.items():
|
|
342
|
+
type_str = self._cumulus_extcommunity_type_str(community_type)
|
|
343
|
+
yield "set", "extcommunity", type_str, *replaced_members
|
|
344
|
+
if action.value.added:
|
|
345
|
+
raise NotImplementedError("extcommunity add is not supported for Cumulus")
|
|
346
|
+
if action.value.removed:
|
|
347
|
+
raise NotImplementedError("extcommunity remove is not supported for Cumulus")
|
|
348
|
+
|
|
313
349
|
def _cumulus_then_as_path(
|
|
314
350
|
self,
|
|
315
351
|
device: Any,
|
|
@@ -350,6 +386,9 @@ class CumulusPolicyGenerator(ABC):
|
|
|
350
386
|
cast(SingleAction[CommunityActionValue], action),
|
|
351
387
|
)
|
|
352
388
|
return
|
|
389
|
+
if action.field == ThenField.extcommunity:
|
|
390
|
+
yield from self._cumulus_then_extcommunity(communities, device, action)
|
|
391
|
+
return
|
|
353
392
|
if action.field == ThenField.extcommunity_rt:
|
|
354
393
|
yield from self._cumulus_then_rt_community(
|
|
355
394
|
communities,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from ipaddress import IPv4Network, IPv6Network, ip_network
|
|
2
|
+
from collections import defaultdict
|
|
2
3
|
from collections.abc import Sequence
|
|
3
4
|
from dataclasses import dataclass
|
|
4
5
|
from enum import Enum
|
|
@@ -124,3 +125,13 @@ class PrefixListNameGenerator:
|
|
|
124
125
|
for x in orig_prefix.members
|
|
125
126
|
],
|
|
126
127
|
)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def group_community_members(
|
|
131
|
+
all_communities: dict[str, CommunityList], communities: list[str],
|
|
132
|
+
) -> dict[CommunityType, list[str]]:
|
|
133
|
+
members: dict[CommunityType, list[str]] = defaultdict(list)
|
|
134
|
+
for community_name in communities:
|
|
135
|
+
community = all_communities[community_name]
|
|
136
|
+
members[community.type].extend(community.members)
|
|
137
|
+
return members
|
|
@@ -12,7 +12,7 @@ from annet.rpl.statement_builder import AsPathActionValue, NextHopActionValue, T
|
|
|
12
12
|
from annet.rpl_generators.entities import (
|
|
13
13
|
arista_well_known_community,
|
|
14
14
|
CommunityList, RDFilter, PrefixListNameGenerator, CommunityLogic, mangle_united_community_list_name,
|
|
15
|
-
IpPrefixList,
|
|
15
|
+
IpPrefixList, group_community_members, CommunityType,
|
|
16
16
|
)
|
|
17
17
|
|
|
18
18
|
|
|
@@ -245,6 +245,52 @@ class RoutingPolicyGenerator(PartialGenerator, ABC):
|
|
|
245
245
|
if action.value.removed:
|
|
246
246
|
raise NotImplementedError("Extcommunity_soo remove is not supported for huawei")
|
|
247
247
|
|
|
248
|
+
def _huawei_render_ext_community_members(
|
|
249
|
+
self, comm_type: CommunityType, members: list[str]
|
|
250
|
+
) -> Sequence[Sequence[str]]:
|
|
251
|
+
if comm_type is CommunityType.SOO:
|
|
252
|
+
return "soo", *members
|
|
253
|
+
if comm_type is CommunityType.RT:
|
|
254
|
+
return [f"rt {member}" for member in members]
|
|
255
|
+
elif comm_type is CommunityType.LARGE:
|
|
256
|
+
raise ValueError("Large community is not subtype of extcommunity")
|
|
257
|
+
elif comm_type is CommunityType.BASIC:
|
|
258
|
+
raise ValueError("Basic community is not subtype of extcommunity")
|
|
259
|
+
else:
|
|
260
|
+
raise NotImplementedError(f"Community type {comm_type} is not supported on huawei")
|
|
261
|
+
|
|
262
|
+
def _huawei_then_extcommunity(
|
|
263
|
+
self,
|
|
264
|
+
communities: dict[str, CommunityList],
|
|
265
|
+
device: Any,
|
|
266
|
+
action: SingleAction[CommunityActionValue],
|
|
267
|
+
):
|
|
268
|
+
if action.value.replaced is not None:
|
|
269
|
+
if action.value.added or action.value.removed:
|
|
270
|
+
raise NotImplementedError(
|
|
271
|
+
"Cannot set extcommunity together with add/delete on huawei",
|
|
272
|
+
)
|
|
273
|
+
if not action.value.replaced:
|
|
274
|
+
raise NotImplementedError(
|
|
275
|
+
"Cannot reset extcommunity on huawei",
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
members = group_community_members(communities, action.value.replaced)
|
|
279
|
+
for community_type, replaced_members in members.items():
|
|
280
|
+
if community_type is CommunityType.SOO:
|
|
281
|
+
raise NotImplementedError(
|
|
282
|
+
"Cannot set extcommunity soo on huawei",
|
|
283
|
+
)
|
|
284
|
+
rendered_memebers = self._huawei_render_ext_community_members(community_type, replaced_members)
|
|
285
|
+
yield "apply", "extcommunity", *rendered_memebers
|
|
286
|
+
if action.value.added:
|
|
287
|
+
members = group_community_members(communities, action.value.added)
|
|
288
|
+
for community_type, added_members in members.items():
|
|
289
|
+
rendered_memebers = self._huawei_render_ext_community_members(community_type, added_members)
|
|
290
|
+
yield "apply", "extcommunity", *rendered_memebers, "additive"
|
|
291
|
+
if action.value.removed:
|
|
292
|
+
raise NotImplementedError("Cannot remove extcommunity on huawei")
|
|
293
|
+
|
|
248
294
|
def _huawei_then_as_path(
|
|
249
295
|
self,
|
|
250
296
|
device: Any,
|
|
@@ -283,6 +329,10 @@ class RoutingPolicyGenerator(PartialGenerator, ABC):
|
|
|
283
329
|
yield from self._huawei_then_large_community(communities, device,
|
|
284
330
|
cast(SingleAction[CommunityActionValue], action))
|
|
285
331
|
return
|
|
332
|
+
if action.field == ThenField.extcommunity:
|
|
333
|
+
yield from self._huawei_then_extcommunity(communities, device,
|
|
334
|
+
cast(SingleAction[CommunityActionValue], action))
|
|
335
|
+
return
|
|
286
336
|
if action.field == ThenField.extcommunity_rt:
|
|
287
337
|
yield from self._huawei_then_extcommunity_rt(communities, device,
|
|
288
338
|
cast(SingleAction[CommunityActionValue], action))
|
|
@@ -564,6 +614,51 @@ class RoutingPolicyGenerator(PartialGenerator, ABC):
|
|
|
564
614
|
]
|
|
565
615
|
yield "set", "extcommunity", *members, "delete"
|
|
566
616
|
|
|
617
|
+
def _arista_extcommunity_type_str(self, comm_type: CommunityType) -> str:
|
|
618
|
+
if comm_type is CommunityType.SOO:
|
|
619
|
+
return "soo"
|
|
620
|
+
elif comm_type is CommunityType.RT:
|
|
621
|
+
return "rt"
|
|
622
|
+
elif comm_type is CommunityType.LARGE:
|
|
623
|
+
raise ValueError("Large community is not subtype of extcommunity")
|
|
624
|
+
elif comm_type is CommunityType.BASIC:
|
|
625
|
+
raise ValueError("Basic community is not subtype of extcommunity")
|
|
626
|
+
else:
|
|
627
|
+
raise NotImplementedError(f"Community type {comm_type} is not supported on arista")
|
|
628
|
+
|
|
629
|
+
def _arista_render_ext_community_members(
|
|
630
|
+
self, all_communities: dict[str, CommunityList], communities: list[str],
|
|
631
|
+
) -> Iterator[str]:
|
|
632
|
+
for community_name in communities:
|
|
633
|
+
community = all_communities[community_name]
|
|
634
|
+
comm_type = self._arista_extcommunity_type_str(community.type)
|
|
635
|
+
for member in community.members:
|
|
636
|
+
yield f"{comm_type} {member}"
|
|
637
|
+
|
|
638
|
+
def _arista_then_extcommunity(
|
|
639
|
+
self,
|
|
640
|
+
communities: dict[str, CommunityList],
|
|
641
|
+
device: Any,
|
|
642
|
+
action: SingleAction[CommunityActionValue],
|
|
643
|
+
):
|
|
644
|
+
if action.value.replaced is not None:
|
|
645
|
+
if action.value.added or action.value.removed:
|
|
646
|
+
raise NotImplementedError(
|
|
647
|
+
"Cannot set extcommunity together with add/delete on arista",
|
|
648
|
+
)
|
|
649
|
+
if not action.value.replaced:
|
|
650
|
+
yield "set", "extcommunity", "none"
|
|
651
|
+
return
|
|
652
|
+
members = list(self._arista_render_ext_community_members(communities, action.value.replaced))
|
|
653
|
+
yield "set extcommunity", *members
|
|
654
|
+
return
|
|
655
|
+
if action.value.added:
|
|
656
|
+
members = list(self._arista_render_ext_community_members(communities, action.value.added))
|
|
657
|
+
yield "set extcommunity", *members, "additive"
|
|
658
|
+
if action.value.removed:
|
|
659
|
+
members = list(self._arista_render_ext_community_members(communities, action.value.removed))
|
|
660
|
+
yield "set extcommunity", *members, "delete"
|
|
661
|
+
|
|
567
662
|
def _arista_then_as_path(
|
|
568
663
|
self,
|
|
569
664
|
device: Any,
|
|
@@ -610,6 +705,9 @@ class RoutingPolicyGenerator(PartialGenerator, ABC):
|
|
|
610
705
|
communities, device, cast(SingleAction[CommunityActionValue], action),
|
|
611
706
|
)
|
|
612
707
|
return
|
|
708
|
+
if action.field == ThenField.extcommunity:
|
|
709
|
+
yield from self._arista_then_extcommunity(communities, device, action)
|
|
710
|
+
return
|
|
613
711
|
if action.field == ThenField.extcommunity_rt:
|
|
614
712
|
yield from self._arista_then_extcommunity_rt(
|
|
615
713
|
communities, device, cast(SingleAction[CommunityActionValue], action),
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|