annet 1.0.3__tar.gz → 1.0.4__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-1.0.3/annet.egg-info → annet-1.0.4}/PKG-INFO +1 -1
- {annet-1.0.3 → annet-1.0.4}/README.md +5 -0
- {annet-1.0.3 → annet-1.0.4}/annet/annlib/netdev/devdb/data/devdb.json +3 -0
- {annet-1.0.3 → annet-1.0.4}/annet/bgp_models.py +83 -1
- {annet-1.0.3 → annet-1.0.4}/annet/mesh/device_models.py +12 -0
- {annet-1.0.3 → annet-1.0.4}/annet/mesh/models_converter.py +3 -1
- annet-1.0.4/annet/rulebook/arista/aaa.py +63 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/arista.rul +1 -1
- {annet-1.0.3 → annet-1.0.4/annet.egg-info}/PKG-INFO +1 -1
- {annet-1.0.3 → annet-1.0.4}/annet.egg-info/SOURCES.txt +1 -0
- {annet-1.0.3 → annet-1.0.4}/AUTHORS +0 -0
- {annet-1.0.3 → annet-1.0.4}/LICENSE +0 -0
- {annet-1.0.3 → annet-1.0.4}/MANIFEST.in +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/__init__.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/adapters/__init__.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/adapters/fetchers/__init__.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/adapters/fetchers/stub/__init__.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/adapters/fetchers/stub/fetcher.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/adapters/file/__init__.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/adapters/file/provider.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/adapters/netbox/__init__.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/adapters/netbox/common/__init__.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/adapters/netbox/common/client.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/adapters/netbox/common/manufacturer.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/adapters/netbox/common/models.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/adapters/netbox/common/query.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/adapters/netbox/common/status_client.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/adapters/netbox/common/storage_opts.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/adapters/netbox/provider.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/adapters/netbox/v24/__init__.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/adapters/netbox/v24/storage.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/adapters/netbox/v37/__init__.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/adapters/netbox/v37/storage.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/annet.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/annlib/__init__.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/annlib/command.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/annlib/diff.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/annlib/errors.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/annlib/filter_acl.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/annlib/jsontools.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/annlib/lib.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/annlib/netdev/__init__.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/annlib/netdev/db.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/annlib/netdev/devdb/__init__.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/annlib/netdev/views/__init__.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/annlib/netdev/views/dump.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/annlib/netdev/views/hardware.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/annlib/output.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/annlib/patching.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/annlib/rbparser/__init__.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/annlib/rbparser/acl.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/annlib/rbparser/deploying.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/annlib/rbparser/ordering.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/annlib/rbparser/platform.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/annlib/rbparser/syntax.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/annlib/rulebook/__init__.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/annlib/rulebook/common.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/annlib/tabparser.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/annlib/types.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/api/__init__.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/argparse.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/cli.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/cli_args.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/configs/context.yml +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/configs/logging.yaml +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/connectors.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/deploy.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/deploy_ui.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/diff.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/executor.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/filtering.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/gen.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/generators/__init__.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/generators/base.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/generators/common/__init__.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/generators/common/initial.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/generators/entire.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/generators/exceptions.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/generators/jsonfragment.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/generators/partial.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/generators/perf.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/generators/ref.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/generators/result.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/hardware.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/implicit.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/lib.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/mesh/__init__.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/mesh/basemodel.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/mesh/executor.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/mesh/match_args.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/mesh/peer_models.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/mesh/port_processor.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/mesh/registry.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/output.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/parallel.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/patching.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/reference.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rpl/__init__.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rpl/action.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rpl/condition.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rpl/match_builder.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rpl/policy.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rpl/result.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rpl/routemap.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rpl/statement_builder.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rpl_generators/__init__.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rpl_generators/aspath.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rpl_generators/community.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rpl_generators/cumulus_frr.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rpl_generators/entities.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rpl_generators/execute.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rpl_generators/policy.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rpl_generators/prefix_lists.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rpl_generators/rd.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/__init__.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/arista/__init__.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/arista/iface.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/aruba/__init__.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/aruba/ap_env.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/aruba/misc.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/b4com/__init__.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/b4com/file.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/b4com/iface.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/cisco/__init__.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/cisco/iface.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/cisco/misc.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/cisco/vlandb.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/common.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/deploying.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/huawei/__init__.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/huawei/aaa.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/huawei/bgp.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/huawei/iface.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/huawei/misc.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/huawei/vlandb.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/juniper/__init__.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/nexus/__init__.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/nexus/iface.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/patching.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/ribbon/__init__.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/routeros/__init__.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/routeros/file.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/arista.deploy +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/arista.order +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/aruba.deploy +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/aruba.order +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/aruba.rul +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/b4com.deploy +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/b4com.order +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/b4com.rul +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/cisco.deploy +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/cisco.order +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/cisco.rul +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/huawei.deploy +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/huawei.order +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/huawei.rul +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/juniper.rul +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/nexus.deploy +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/nexus.order +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/nexus.rul +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/nokia.rul +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/optixtrans.deploy +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/optixtrans.order +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/optixtrans.rul +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/pc.deploy +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/pc.order +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/pc.rul +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/ribbon.deploy +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/ribbon.rul +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/routeros.order +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/routeros.rul +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/storage.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/tabparser.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/text_term_format.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/tracing.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet/types.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet.egg-info/dependency_links.txt +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet.egg-info/entry_points.txt +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet.egg-info/requires.txt +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet.egg-info/top_level.txt +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet_generators/__init__.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet_generators/example/__init__.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet_generators/example/lldp.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet_generators/mesh_example/__init__.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet_generators/mesh_example/bgp.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet_generators/mesh_example/mesh_logic.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet_generators/rpl_example/__init__.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet_generators/rpl_example/generator.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet_generators/rpl_example/items.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet_generators/rpl_example/mesh.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/annet_generators/rpl_example/route_policy.py +0 -0
- {annet-1.0.3 → annet-1.0.4}/requirements.txt +0 -0
- {annet-1.0.3 → annet-1.0.4}/setup.cfg +0 -0
- {annet-1.0.3 → annet-1.0.4}/setup.py +0 -0
|
@@ -68,3 +68,8 @@ sphinx-build -M html docs docs-build
|
|
|
68
68
|
```
|
|
69
69
|
|
|
70
70
|
3. Open rendered html in browser docs-build/html/index.html
|
|
71
|
+
|
|
72
|
+
## Links
|
|
73
|
+
|
|
74
|
+
* [Online Documentation](https://annetutil.github.io/annet/)
|
|
75
|
+
* [Tutorial](https://annetutil.github.io/annet/main/usage/tutorial.html)
|
|
@@ -132,6 +132,9 @@
|
|
|
132
132
|
"PC.Whitebox.Ufispace.S": " S",
|
|
133
133
|
"PC.Whitebox.Ufispace.S.S9100": " S91\\d\\d",
|
|
134
134
|
"PC.Whitebox.Ufispace.S.S9100.S9110_32X": " S9110-32X",
|
|
135
|
+
"PC.Whitebox.Ufispace.S.S9300": " S93\\d\\d",
|
|
136
|
+
"PC.Whitebox.Ufispace.S.S9300.S9301_32DB": " S9301-32DB",
|
|
137
|
+
"PC.Whitebox.Ufispace.S.S9300.S9321_64EO": " S9321-64EO",
|
|
135
138
|
"PC.Nebius": "^Nebius",
|
|
136
139
|
"PC.Nebius.NB-E-BR-DCU-AST2600": "^Nebius NB-E-BR-DCU-AST2600",
|
|
137
140
|
|
|
@@ -3,6 +3,77 @@ from dataclasses import dataclass, field
|
|
|
3
3
|
from typing import Literal, Union, Optional
|
|
4
4
|
|
|
5
5
|
|
|
6
|
+
class VidRange:
|
|
7
|
+
def __init__(self, start: int, stop: int) -> None:
|
|
8
|
+
self.start = start
|
|
9
|
+
self.stop = stop
|
|
10
|
+
|
|
11
|
+
def is_single(self):
|
|
12
|
+
return self.start == self.stop
|
|
13
|
+
|
|
14
|
+
def __iter__(self):
|
|
15
|
+
return iter(range(self.start, self.stop + 1))
|
|
16
|
+
|
|
17
|
+
def __str__(self):
|
|
18
|
+
if self.is_single():
|
|
19
|
+
return str(self.start)
|
|
20
|
+
return f"{self.start}-{self.stop}"
|
|
21
|
+
|
|
22
|
+
def __repr__(self):
|
|
23
|
+
return f"VlanRange({self.start}, {self.stop})"
|
|
24
|
+
|
|
25
|
+
def __eq__(self, other: object) -> bool:
|
|
26
|
+
if type(other) is VidRange:
|
|
27
|
+
return self.start == other.start and self.stop == other.stop
|
|
28
|
+
return NotImplemented
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _parse_vlan_ranges(ranges: str) -> Iterable[VidRange]:
|
|
32
|
+
for range in ranges.split(","):
|
|
33
|
+
start, sep, stop = range.strip().partition("-")
|
|
34
|
+
try:
|
|
35
|
+
if not sep:
|
|
36
|
+
int_start = int(start)
|
|
37
|
+
yield VidRange(int_start, int_start)
|
|
38
|
+
elif not stop or not start:
|
|
39
|
+
raise ValueError(f"Cannot parse range {range!r}. Expected `start-stop`")
|
|
40
|
+
else:
|
|
41
|
+
yield VidRange(int(start), int(stop))
|
|
42
|
+
except ValueError:
|
|
43
|
+
raise ValueError(f"Cannot parse range {range!r}. Expected `vid1-vid2` or `vid`")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class VidCollection:
|
|
47
|
+
@staticmethod
|
|
48
|
+
def parse(ranges: int | str) -> "VidCollection":
|
|
49
|
+
if isinstance(ranges, int):
|
|
50
|
+
return VidCollection([VidRange(ranges, ranges)])
|
|
51
|
+
elif isinstance(ranges, str):
|
|
52
|
+
return VidCollection(list(_parse_vlan_ranges(ranges)))
|
|
53
|
+
elif isinstance(ranges, VidCollection):
|
|
54
|
+
return VidCollection(ranges.ranges)
|
|
55
|
+
else:
|
|
56
|
+
raise TypeError(f"Expected str or int, got {type(ranges)}")
|
|
57
|
+
|
|
58
|
+
def __init__(self, ranges: list[VidRange]) -> None:
|
|
59
|
+
self.ranges = ranges
|
|
60
|
+
|
|
61
|
+
def __str__(self):
|
|
62
|
+
return ",".join(map(str, self.ranges))
|
|
63
|
+
|
|
64
|
+
def __repr__(self):
|
|
65
|
+
return f"VlanCollection({str(self)!r})"
|
|
66
|
+
|
|
67
|
+
def __iter__(self):
|
|
68
|
+
for range in self.ranges:
|
|
69
|
+
yield from range
|
|
70
|
+
|
|
71
|
+
def __eq__(self, other: object) -> bool:
|
|
72
|
+
if type(other) is VidCollection:
|
|
73
|
+
return self.ranges == other.ranges
|
|
74
|
+
return False
|
|
75
|
+
|
|
76
|
+
|
|
6
77
|
class ASN(int):
|
|
7
78
|
"""
|
|
8
79
|
Stores ASN number and formats it as в AS1.AS2
|
|
@@ -235,6 +306,17 @@ class PeerGroup:
|
|
|
235
306
|
mtu: int = 0
|
|
236
307
|
|
|
237
308
|
|
|
309
|
+
@dataclass
|
|
310
|
+
class L2VpnOptions:
|
|
311
|
+
name: str
|
|
312
|
+
vid: VidCollection
|
|
313
|
+
l2vni: int # VNI, possible values are 1 to 2**24-1
|
|
314
|
+
route_distinguisher: str = "" # like in VrfOptions
|
|
315
|
+
rt_import: list[str] = field(default_factory=list) # like in VrfOptions
|
|
316
|
+
rt_export: list[str] = field(default_factory=list) # like in VrfOptions
|
|
317
|
+
advertise_host_routes: bool = True # advertise IP+MAC routes into L3VNI
|
|
318
|
+
|
|
319
|
+
|
|
238
320
|
@dataclass
|
|
239
321
|
class VrfOptions:
|
|
240
322
|
vrf_name: str
|
|
@@ -274,8 +356,8 @@ class GlobalOptions:
|
|
|
274
356
|
multipath: int = 0
|
|
275
357
|
router_id: str = ""
|
|
276
358
|
vrf: dict[str, VrfOptions] = field(default_factory=dict)
|
|
277
|
-
|
|
278
359
|
groups: list[PeerGroup] = field(default_factory=list)
|
|
360
|
+
l2vpn: dict[str, L2VpnOptions] = field(default_factory=dict)
|
|
279
361
|
|
|
280
362
|
|
|
281
363
|
@dataclass
|
|
@@ -76,10 +76,21 @@ class VrfOptions(_FamiliesMixin, BaseMeshModel):
|
|
|
76
76
|
groups: Annotated[dict[str, MeshPeerGroup], DictMerge(Merge())]
|
|
77
77
|
|
|
78
78
|
|
|
79
|
+
class L2VpnOptions(BaseMeshModel):
|
|
80
|
+
name: str
|
|
81
|
+
vid: str | int # VLAN ID, possible values are 1 to 4094, ranges can be set as strings
|
|
82
|
+
l2vni: int # VNI, possible values are 1 to 2**24-1
|
|
83
|
+
route_distinguisher: str # like in VrfOptions
|
|
84
|
+
rt_import: Annotated[tuple[str, ...], Concat()] # like in VrfOptions
|
|
85
|
+
rt_export: Annotated[tuple[str, ...], Concat()] # like in VrfOptions
|
|
86
|
+
advertise_host_routes: bool # advertise IP+MAC routes into L3VNI
|
|
87
|
+
|
|
88
|
+
|
|
79
89
|
class GlobalOptionsDTO(_FamiliesMixin, BaseMeshModel):
|
|
80
90
|
def __init__(self, **kwargs):
|
|
81
91
|
kwargs.setdefault("groups", KeyDefaultDict(lambda x: MeshPeerGroup(name=x)))
|
|
82
92
|
kwargs.setdefault("vrf", KeyDefaultDict(lambda x: VrfOptions(vrf_name=x)))
|
|
93
|
+
kwargs.setdefault("l2vpn", KeyDefaultDict(lambda x: L2VpnOptions(name=x)))
|
|
83
94
|
super().__init__(**kwargs)
|
|
84
95
|
|
|
85
96
|
as_path_relax: bool
|
|
@@ -89,3 +100,4 @@ class GlobalOptionsDTO(_FamiliesMixin, BaseMeshModel):
|
|
|
89
100
|
router_id: str
|
|
90
101
|
vrf: Annotated[dict[str, VrfOptions], DictMerge(Merge())]
|
|
91
102
|
groups: Annotated[dict[str, MeshPeerGroup], DictMerge(Merge())]
|
|
103
|
+
l2vpn: Annotated[dict[str, L2VpnOptions], DictMerge(Merge())]
|
|
@@ -7,7 +7,7 @@ from adaptix import Retort, loader, Chain, name_mapping, as_is_loader
|
|
|
7
7
|
from .peer_models import DirectPeerDTO, IndirectPeerDTO, VirtualPeerDTO, VirtualLocalDTO
|
|
8
8
|
from ..bgp_models import (
|
|
9
9
|
Aggregate, GlobalOptions, VrfOptions, FamilyOptions, Peer, PeerGroup, ASN, PeerOptions,
|
|
10
|
-
Redistribute, BFDTimers,
|
|
10
|
+
Redistribute, BFDTimers, L2VpnOptions, VidCollection,
|
|
11
11
|
)
|
|
12
12
|
|
|
13
13
|
|
|
@@ -49,8 +49,10 @@ retort = Retort(
|
|
|
49
49
|
recipe=[
|
|
50
50
|
loader(InterfaceChanges, ObjMapping, Chain.FIRST),
|
|
51
51
|
loader(ASN, ASN),
|
|
52
|
+
loader(VidCollection, VidCollection.parse),
|
|
52
53
|
loader(GlobalOptions, ObjMapping, Chain.FIRST),
|
|
53
54
|
loader(VrfOptions, ObjMapping, Chain.FIRST),
|
|
55
|
+
loader(L2VpnOptions, ObjMapping, Chain.FIRST),
|
|
54
56
|
loader(FamilyOptions, ObjMapping, Chain.FIRST),
|
|
55
57
|
loader(Aggregate, ObjMapping, Chain.FIRST),
|
|
56
58
|
loader(PeerOptions, ObjMapping, Chain.FIRST),
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from annet.annlib.types import Op
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass
|
|
7
|
+
class UserConfig:
|
|
8
|
+
name: str
|
|
9
|
+
privilege: int
|
|
10
|
+
role: str
|
|
11
|
+
secret_type: str
|
|
12
|
+
secret: str
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _parse_user_config(config_line):
|
|
16
|
+
"""Convert a user config line into a dataclass. Config example:
|
|
17
|
+
|
|
18
|
+
username someuser privilege 15 role network-admin secret sha512 $6$....
|
|
19
|
+
|
|
20
|
+
privilege could be omitted if equal to 1
|
|
21
|
+
role could be omitted
|
|
22
|
+
secret could be omitted, 'nopassword' is provided instead
|
|
23
|
+
"""
|
|
24
|
+
splstr = config_line.split()
|
|
25
|
+
name = splstr[1]
|
|
26
|
+
priv = 1
|
|
27
|
+
role = ""
|
|
28
|
+
secret_type = ""
|
|
29
|
+
secret = ""
|
|
30
|
+
if "privilege" in splstr:
|
|
31
|
+
pos = splstr.index("privilege")
|
|
32
|
+
priv = int(splstr[pos + 1])
|
|
33
|
+
if "role" in splstr:
|
|
34
|
+
pos = splstr.index("role")
|
|
35
|
+
role = splstr[pos + 1]
|
|
36
|
+
if "secret" in splstr:
|
|
37
|
+
pos = splstr.index("secret")
|
|
38
|
+
secret_type = splstr[pos + 1]
|
|
39
|
+
secret = splstr[pos + 2]
|
|
40
|
+
return UserConfig(name=name, privilege=priv, role=role, secret_type=secret_type, secret=secret)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def user(key, diff, **_):
|
|
44
|
+
if diff[Op.ADDED] and not diff[Op.REMOVED]:
|
|
45
|
+
for add in diff[Op.ADDED]:
|
|
46
|
+
yield (True, add["row"], None)
|
|
47
|
+
elif diff[Op.REMOVED] and not diff[Op.ADDED]:
|
|
48
|
+
for _ in diff[Op.REMOVED]:
|
|
49
|
+
yield (False, f"no username {key[0]}", None)
|
|
50
|
+
else:
|
|
51
|
+
for num, add in enumerate(diff[Op.ADDED]):
|
|
52
|
+
new_user = _parse_user_config(add["row"])
|
|
53
|
+
old_user = _parse_user_config(diff[Op.REMOVED][num]["row"])
|
|
54
|
+
if new_user.privilege != old_user.privilege:
|
|
55
|
+
yield (True, f"username {key[0]} privilege {new_user.privilege}", None)
|
|
56
|
+
if new_user.role != old_user.role and not new_user.role:
|
|
57
|
+
yield (True, f"no username {key[0]} role", None)
|
|
58
|
+
elif new_user.role != old_user.role:
|
|
59
|
+
yield (True, f"username {key[0]} role {new_user.role}", None)
|
|
60
|
+
if new_user.secret != old_user.secret and not new_user.secret:
|
|
61
|
+
yield (True, f"username {key[0]} nopassword", None)
|
|
62
|
+
elif new_user.secret != old_user.secret:
|
|
63
|
+
yield (True, f"username {key[0]} secret {new_user.secret_type} {new_user.secret}", None)
|
|
@@ -125,6 +125,7 @@ annet/rulebook/common.py
|
|
|
125
125
|
annet/rulebook/deploying.py
|
|
126
126
|
annet/rulebook/patching.py
|
|
127
127
|
annet/rulebook/arista/__init__.py
|
|
128
|
+
annet/rulebook/arista/aaa.py
|
|
128
129
|
annet/rulebook/arista/iface.py
|
|
129
130
|
annet/rulebook/aruba/__init__.py
|
|
130
131
|
annet/rulebook/aruba/ap_env.py
|
|
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
|