annet 0.13.4__tar.gz → 3.22.0__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.
- {annet-0.13.4/annet.egg-info → annet-3.22.0}/PKG-INFO +15 -7
- annet-3.22.0/README.md +89 -0
- {annet-0.13.4 → annet-3.22.0}/annet/__init__.py +7 -11
- annet-3.22.0/annet/adapters/fetchers/stub/fetcher.py +25 -0
- annet-3.22.0/annet/adapters/file/provider.py +280 -0
- annet-3.22.0/annet/adapters/netbox/common/adapter.py +87 -0
- {annet-0.13.4 → annet-3.22.0}/annet/adapters/netbox/common/client.py +3 -1
- annet-3.22.0/annet/adapters/netbox/common/manufacturer.py +44 -0
- annet-3.22.0/annet/adapters/netbox/common/models.py +352 -0
- annet-3.22.0/annet/adapters/netbox/common/query.py +68 -0
- {annet-0.13.4 → annet-3.22.0}/annet/adapters/netbox/common/status_client.py +8 -1
- annet-3.22.0/annet/adapters/netbox/common/storage_base.py +290 -0
- annet-3.22.0/annet/adapters/netbox/common/storage_opts.py +72 -0
- annet-3.22.0/annet/adapters/netbox/provider.py +73 -0
- annet-3.22.0/annet/adapters/netbox/v24/models.py +296 -0
- {annet-0.13.4 → annet-3.22.0}/annet/adapters/netbox/v24/storage.py +14 -5
- annet-3.22.0/annet/adapters/netbox/v37/models.py +77 -0
- annet-3.22.0/annet/adapters/netbox/v37/storage.py +129 -0
- annet-3.22.0/annet/adapters/netbox/v41/models.py +85 -0
- annet-3.22.0/annet/adapters/netbox/v41/storage.py +132 -0
- annet-3.22.0/annet/adapters/netbox/v42/models.py +72 -0
- annet-3.22.0/annet/adapters/netbox/v42/storage.py +157 -0
- {annet-0.13.4 → annet-3.22.0}/annet/annet.py +10 -2
- {annet-0.13.4 → annet-3.22.0}/annet/annlib/command.py +6 -2
- {annet-0.13.4 → annet-3.22.0}/annet/annlib/diff.py +2 -5
- {annet-0.13.4 → annet-3.22.0}/annet/annlib/filter_acl.py +8 -3
- annet-3.22.0/annet/annlib/jsontools.py +252 -0
- {annet-0.13.4 → annet-3.22.0}/annet/annlib/lib.py +1 -15
- {annet-0.13.4 → annet-3.22.0}/annet/annlib/netdev/devdb/__init__.py +4 -2
- annet-3.22.0/annet/annlib/netdev/devdb/data/devdb.json +201 -0
- {annet-0.13.4 → annet-3.22.0}/annet/annlib/netdev/views/dump.py +8 -0
- {annet-0.13.4 → annet-3.22.0}/annet/annlib/netdev/views/hardware.py +41 -49
- {annet-0.13.4 → annet-3.22.0}/annet/annlib/output.py +8 -6
- annet-3.22.0/annet/annlib/patching.py +900 -0
- {annet-0.13.4 → annet-3.22.0}/annet/annlib/rbparser/acl.py +3 -2
- annet-3.22.0/annet/annlib/rbparser/ordering.py +96 -0
- annet-3.22.0/annet/annlib/rbparser/platform.py +1 -0
- {annet-0.13.4 → annet-3.22.0}/annet/annlib/rbparser/syntax.py +51 -29
- {annet-0.13.4 → annet-3.22.0}/annet/annlib/rulebook/common.py +30 -89
- {annet-0.13.4 → annet-3.22.0}/annet/api/__init__.py +276 -212
- {annet-0.13.4 → annet-3.22.0}/annet/argparse.py +8 -2
- annet-3.22.0/annet/bgp_models.py +449 -0
- {annet-0.13.4 → annet-3.22.0}/annet/cli.py +135 -39
- {annet-0.13.4 → annet-3.22.0}/annet/cli_args.py +88 -68
- annet-3.22.0/annet/configs/context.yml +16 -0
- {annet-0.13.4 → annet-3.22.0}/annet/configs/logging.yaml +1 -0
- annet-3.22.0/annet/connectors.py +144 -0
- annet-3.22.0/annet/deploy.py +233 -0
- annet-3.22.0/annet/deploy_ui.py +809 -0
- annet-3.22.0/annet/diff.py +246 -0
- {annet-0.13.4 → annet-3.22.0}/annet/filtering.py +1 -0
- {annet-0.13.4 → annet-3.22.0}/annet/gen.py +36 -98
- {annet-0.13.4 → annet-3.22.0}/annet/generators/__init__.py +99 -76
- annet-3.22.0/annet/generators/annotate.py +37 -0
- {annet-0.13.4 → annet-3.22.0}/annet/generators/base.py +13 -3
- {annet-0.13.4 → annet-3.22.0}/annet/generators/entire.py +20 -31
- {annet-0.13.4 → annet-3.22.0}/annet/generators/jsonfragment.py +31 -37
- {annet-0.13.4 → annet-3.22.0}/annet/generators/partial.py +32 -52
- {annet-0.13.4 → annet-3.22.0}/annet/generators/perf.py +9 -18
- {annet-0.13.4 → annet-3.22.0}/annet/generators/result.py +33 -30
- annet-3.22.0/annet/hardware.py +42 -0
- {annet-0.13.4 → annet-3.22.0}/annet/implicit.py +75 -11
- {annet-0.13.4 → annet-3.22.0}/annet/lib.py +43 -8
- annet-3.22.0/annet/mesh/__init__.py +23 -0
- annet-3.22.0/annet/mesh/basemodel.py +249 -0
- annet-3.22.0/annet/mesh/device_models.py +114 -0
- annet-3.22.0/annet/mesh/executor.py +421 -0
- annet-3.22.0/annet/mesh/match_args.py +165 -0
- annet-3.22.0/annet/mesh/models_converter.py +104 -0
- annet-3.22.0/annet/mesh/peer_models.py +135 -0
- annet-3.22.0/annet/mesh/port_processor.py +18 -0
- annet-3.22.0/annet/mesh/registry.py +299 -0
- {annet-0.13.4 → annet-3.22.0}/annet/output.py +37 -8
- {annet-0.13.4 → annet-3.22.0}/annet/parallel.py +28 -15
- annet-3.22.0/annet/rpl/__init__.py +28 -0
- annet-3.22.0/annet/rpl/action.py +51 -0
- annet-3.22.0/annet/rpl/condition.py +94 -0
- annet-3.22.0/annet/rpl/match_builder.py +136 -0
- annet-3.22.0/annet/rpl/policy.py +22 -0
- annet-3.22.0/annet/rpl/result.py +8 -0
- annet-3.22.0/annet/rpl/routemap.py +78 -0
- annet-3.22.0/annet/rpl/statement_builder.py +306 -0
- annet-3.22.0/annet/rpl_generators/__init__.py +26 -0
- annet-3.22.0/annet/rpl_generators/aspath.py +95 -0
- annet-3.22.0/annet/rpl_generators/community.py +348 -0
- annet-3.22.0/annet/rpl_generators/cumulus_frr.py +489 -0
- annet-3.22.0/annet/rpl_generators/entities.py +184 -0
- annet-3.22.0/annet/rpl_generators/execute.py +12 -0
- annet-3.22.0/annet/rpl_generators/policy.py +1486 -0
- annet-3.22.0/annet/rpl_generators/prefix_lists.py +238 -0
- annet-3.22.0/annet/rpl_generators/rd.py +71 -0
- {annet-0.13.4 → annet-3.22.0}/annet/rulebook/__init__.py +6 -4
- annet-3.22.0/annet/rulebook/arista/__init__.py +0 -0
- annet-3.22.0/annet/rulebook/arista/aaa.py +63 -0
- {annet-0.13.4 → annet-3.22.0}/annet/rulebook/aruba/ap_env.py +1 -6
- annet-3.22.0/annet/rulebook/b4com/__init__.py +0 -0
- annet-3.22.0/annet/rulebook/b4com/bgp.py +60 -0
- annet-3.22.0/annet/rulebook/b4com/file.py +5 -0
- annet-3.22.0/annet/rulebook/b4com/iface.py +119 -0
- annet-3.22.0/annet/rulebook/cisco/__init__.py +0 -0
- {annet-0.13.4 → annet-3.22.0}/annet/rulebook/cisco/iface.py +1 -1
- {annet-0.13.4 → annet-3.22.0}/annet/rulebook/cisco/misc.py +8 -6
- {annet-0.13.4 → annet-3.22.0}/annet/rulebook/deploying.py +3 -2
- annet-3.22.0/annet/rulebook/generic/__init__.py +0 -0
- annet-3.22.0/annet/rulebook/generic/misc.py +14 -0
- annet-3.22.0/annet/rulebook/h3c/__init__.py +0 -0
- annet-3.22.0/annet/rulebook/h3c/aaa.py +28 -0
- annet-3.22.0/annet/rulebook/h3c/bgp.py +84 -0
- annet-3.22.0/annet/rulebook/h3c/iface.py +22 -0
- annet-3.22.0/annet/rulebook/h3c/misc.py +306 -0
- annet-3.22.0/annet/rulebook/h3c/vlandb.py +118 -0
- annet-3.22.0/annet/rulebook/huawei/__init__.py +0 -0
- {annet-0.13.4 → annet-3.22.0}/annet/rulebook/huawei/aaa.py +12 -1
- annet-3.22.0/annet/rulebook/juniper/__init__.py +156 -0
- annet-3.22.0/annet/rulebook/juniper/iface.py +39 -0
- annet-3.22.0/annet/rulebook/nexus/__init__.py +0 -0
- {annet-0.13.4 → annet-3.22.0}/annet/rulebook/patching.py +8 -5
- annet-3.22.0/annet/rulebook/routeros/__init__.py +0 -0
- annet-3.22.0/annet/rulebook/routeros/file.py +5 -0
- {annet-0.13.4 → annet-3.22.0}/annet/rulebook/texts/arista.order +14 -13
- annet-3.22.0/annet/rulebook/texts/arista.rul +82 -0
- annet-3.22.0/annet/rulebook/texts/b4com.deploy +18 -0
- annet-3.22.0/annet/rulebook/texts/b4com.order +38 -0
- annet-3.22.0/annet/rulebook/texts/b4com.rul +47 -0
- {annet-0.13.4 → annet-3.22.0}/annet/rulebook/texts/cisco.deploy +1 -1
- annet-3.22.0/annet/rulebook/texts/cisco.order +86 -0
- annet-3.22.0/annet/rulebook/texts/cisco.rul +115 -0
- annet-0.13.4/annet/rulebook/texts/huawei.deploy → annet-3.22.0/annet/rulebook/texts/h3c.deploy +9 -1
- annet-3.22.0/annet/rulebook/texts/h3c.order +392 -0
- annet-3.22.0/annet/rulebook/texts/h3c.rul +432 -0
- annet-3.22.0/annet/rulebook/texts/huawei.deploy +201 -0
- {annet-0.13.4 → annet-3.22.0}/annet/rulebook/texts/huawei.order +24 -3
- {annet-0.13.4 → annet-3.22.0}/annet/rulebook/texts/huawei.rul +39 -7
- annet-3.22.0/annet/rulebook/texts/iosxr.deploy +27 -0
- annet-0.13.4/annet/rulebook/texts/cisco.order → annet-3.22.0/annet/rulebook/texts/iosxr.order +22 -2
- annet-0.13.4/annet/rulebook/texts/cisco.rul → annet-3.22.0/annet/rulebook/texts/iosxr.rul +11 -6
- annet-3.22.0/annet/rulebook/texts/juniper.order +12 -0
- {annet-0.13.4 → annet-3.22.0}/annet/rulebook/texts/juniper.rul +13 -2
- {annet-0.13.4 → annet-3.22.0}/annet/rulebook/texts/nexus.rul +5 -1
- annet-3.22.0/annet/rulebook/texts/optixtrans.deploy +18 -0
- annet-3.22.0/annet/rulebook/texts/optixtrans.order +5 -0
- annet-3.22.0/annet/rulebook/texts/optixtrans.rul +13 -0
- annet-3.22.0/annet/rulebook/texts/pc.deploy +0 -0
- {annet-0.13.4 → annet-3.22.0}/annet/rulebook/texts/routeros.order +14 -1
- annet-3.22.0/annet/storage.py +178 -0
- {annet-0.13.4 → annet-3.22.0}/annet/text_term_format.py +1 -1
- {annet-0.13.4 → annet-3.22.0}/annet/tracing.py +56 -8
- annet-3.22.0/annet/vendors/__init__.py +33 -0
- annet-3.22.0/annet/vendors/base.py +44 -0
- annet-3.22.0/annet/vendors/library/__init__.py +0 -0
- annet-3.22.0/annet/vendors/library/arista.py +44 -0
- annet-3.22.0/annet/vendors/library/aruba.py +43 -0
- annet-3.22.0/annet/vendors/library/b4com.py +49 -0
- annet-3.22.0/annet/vendors/library/cisco.py +41 -0
- annet-3.22.0/annet/vendors/library/h3c.py +40 -0
- annet-3.22.0/annet/vendors/library/huawei.py +43 -0
- annet-3.22.0/annet/vendors/library/iosxr.py +41 -0
- annet-3.22.0/annet/vendors/library/juniper.py +44 -0
- annet-3.22.0/annet/vendors/library/nexus.py +38 -0
- annet-3.22.0/annet/vendors/library/nokia.py +40 -0
- annet-3.22.0/annet/vendors/library/optixtrans.py +23 -0
- annet-3.22.0/annet/vendors/library/pc.py +39 -0
- annet-3.22.0/annet/vendors/library/ribbon.py +41 -0
- annet-3.22.0/annet/vendors/library/routeros.py +40 -0
- annet-3.22.0/annet/vendors/registry.py +93 -0
- annet-3.22.0/annet/vendors/tabparser.py +991 -0
- {annet-0.13.4 → annet-3.22.0/annet.egg-info}/PKG-INFO +15 -7
- {annet-0.13.4 → annet-3.22.0}/annet.egg-info/SOURCES.txt +103 -5
- {annet-0.13.4 → annet-3.22.0}/annet.egg-info/entry_points.txt +3 -0
- {annet-0.13.4 → annet-3.22.0}/annet.egg-info/requires.txt +6 -4
- annet-3.22.0/annet_generators/__init__.py +0 -0
- annet-3.22.0/annet_generators/example/__init__.py +15 -0
- annet-3.22.0/annet_generators/example/hostname.py +113 -0
- {annet-0.13.4 → annet-3.22.0}/annet_generators/example/lldp.py +28 -0
- {annet-0.13.4/annet_generators/example → annet-3.22.0/annet_generators/mesh_example}/__init__.py +2 -5
- annet-3.22.0/annet_generators/mesh_example/bgp.py +44 -0
- annet-3.22.0/annet_generators/mesh_example/mesh_logic.py +41 -0
- annet-3.22.0/annet_generators/rpl_example/__init__.py +7 -0
- annet-3.22.0/annet_generators/rpl_example/generator.py +130 -0
- annet-3.22.0/annet_generators/rpl_example/items.py +29 -0
- annet-3.22.0/annet_generators/rpl_example/mesh.py +9 -0
- annet-3.22.0/annet_generators/rpl_example/route_policy.py +67 -0
- {annet-0.13.4 → annet-3.22.0}/requirements.txt +2 -4
- {annet-0.13.4 → annet-3.22.0}/setup.py +10 -1
- annet-0.13.4/README.md +0 -111
- annet-0.13.4/annet/adapters/netbox/common/manufacturer.py +0 -62
- annet-0.13.4/annet/adapters/netbox/common/models.py +0 -136
- annet-0.13.4/annet/adapters/netbox/common/query.py +0 -23
- annet-0.13.4/annet/adapters/netbox/common/storage_opts.py +0 -14
- annet-0.13.4/annet/adapters/netbox/provider.py +0 -34
- annet-0.13.4/annet/adapters/netbox/v37/storage.py +0 -216
- annet-0.13.4/annet/annlib/jsontools.py +0 -116
- annet-0.13.4/annet/annlib/netdev/devdb/data/devdb.json +0 -145
- annet-0.13.4/annet/annlib/patching.py +0 -533
- annet-0.13.4/annet/annlib/rbparser/ordering.py +0 -52
- annet-0.13.4/annet/annlib/rbparser/platform.py +0 -51
- annet-0.13.4/annet/annlib/tabparser.py +0 -648
- annet-0.13.4/annet/configs/context.yml +0 -18
- annet-0.13.4/annet/connectors.py +0 -77
- annet-0.13.4/annet/deploy.py +0 -536
- annet-0.13.4/annet/diff.py +0 -84
- annet-0.13.4/annet/executor.py +0 -556
- annet-0.13.4/annet/hardware.py +0 -45
- annet-0.13.4/annet/rulebook/juniper/__init__.py +0 -107
- annet-0.13.4/annet/rulebook/ribbon/__init__.py +0 -12
- annet-0.13.4/annet/rulebook/texts/arista.rul +0 -59
- annet-0.13.4/annet/storage.py +0 -125
- annet-0.13.4/annet/tabparser.py +0 -36
- {annet-0.13.4 → annet-3.22.0}/AUTHORS +0 -0
- {annet-0.13.4 → annet-3.22.0}/LICENSE +0 -0
- {annet-0.13.4 → annet-3.22.0}/MANIFEST.in +0 -0
- {annet-0.13.4 → annet-3.22.0}/annet/adapters/__init__.py +0 -0
- {annet-0.13.4/annet/adapters/netbox → annet-3.22.0/annet/adapters/fetchers}/__init__.py +0 -0
- {annet-0.13.4/annet/adapters/netbox/common → annet-3.22.0/annet/adapters/fetchers/stub}/__init__.py +0 -0
- {annet-0.13.4/annet/adapters/netbox/v24 → annet-3.22.0/annet/adapters/file}/__init__.py +0 -0
- {annet-0.13.4/annet/adapters/netbox/v37 → annet-3.22.0/annet/adapters/netbox}/__init__.py +0 -0
- {annet-0.13.4/annet/annlib/netdev → annet-3.22.0/annet/adapters/netbox/common}/__init__.py +0 -0
- {annet-0.13.4/annet/annlib/netdev/views → annet-3.22.0/annet/adapters/netbox/v24}/__init__.py +0 -0
- {annet-0.13.4/annet/annlib/rbparser → annet-3.22.0/annet/adapters/netbox/v37}/__init__.py +0 -0
- {annet-0.13.4/annet/annlib/rulebook → annet-3.22.0/annet/adapters/netbox/v41}/__init__.py +0 -0
- {annet-0.13.4/annet/generators/common → annet-3.22.0/annet/adapters/netbox/v42}/__init__.py +0 -0
- {annet-0.13.4 → annet-3.22.0}/annet/annlib/__init__.py +0 -0
- {annet-0.13.4 → annet-3.22.0}/annet/annlib/errors.py +0 -0
- {annet-0.13.4/annet/rulebook/arista → annet-3.22.0/annet/annlib/netdev}/__init__.py +0 -0
- {annet-0.13.4 → annet-3.22.0}/annet/annlib/netdev/db.py +0 -0
- {annet-0.13.4/annet/rulebook/cisco → annet-3.22.0/annet/annlib/netdev/views}/__init__.py +0 -0
- {annet-0.13.4/annet/rulebook/huawei → annet-3.22.0/annet/annlib/rbparser}/__init__.py +0 -0
- {annet-0.13.4 → annet-3.22.0}/annet/annlib/rbparser/deploying.py +0 -0
- {annet-0.13.4/annet/rulebook/nexus → annet-3.22.0/annet/annlib/rulebook}/__init__.py +0 -0
- {annet-0.13.4 → annet-3.22.0}/annet/annlib/types.py +0 -0
- {annet-0.13.4/annet_generators → annet-3.22.0/annet/generators/common}/__init__.py +0 -0
- {annet-0.13.4 → annet-3.22.0}/annet/generators/common/initial.py +0 -0
- {annet-0.13.4 → annet-3.22.0}/annet/generators/exceptions.py +0 -0
- {annet-0.13.4 → annet-3.22.0}/annet/generators/ref.py +0 -0
- {annet-0.13.4 → annet-3.22.0}/annet/patching.py +0 -0
- {annet-0.13.4 → annet-3.22.0}/annet/reference.py +0 -0
- {annet-0.13.4 → annet-3.22.0}/annet/rulebook/arista/iface.py +0 -0
- {annet-0.13.4 → annet-3.22.0}/annet/rulebook/aruba/__init__.py +0 -0
- {annet-0.13.4 → annet-3.22.0}/annet/rulebook/aruba/misc.py +0 -0
- {annet-0.13.4 → annet-3.22.0}/annet/rulebook/cisco/vlandb.py +0 -0
- {annet-0.13.4 → annet-3.22.0}/annet/rulebook/common.py +0 -0
- {annet-0.13.4 → annet-3.22.0}/annet/rulebook/huawei/bgp.py +0 -0
- {annet-0.13.4 → annet-3.22.0}/annet/rulebook/huawei/iface.py +0 -0
- {annet-0.13.4 → annet-3.22.0}/annet/rulebook/huawei/misc.py +0 -0
- {annet-0.13.4 → annet-3.22.0}/annet/rulebook/huawei/vlandb.py +0 -0
- {annet-0.13.4 → annet-3.22.0}/annet/rulebook/nexus/iface.py +0 -0
- {annet-0.13.4 → annet-3.22.0}/annet/rulebook/texts/arista.deploy +0 -0
- {annet-0.13.4 → annet-3.22.0}/annet/rulebook/texts/aruba.deploy +0 -0
- {annet-0.13.4 → annet-3.22.0}/annet/rulebook/texts/aruba.order +0 -0
- {annet-0.13.4 → annet-3.22.0}/annet/rulebook/texts/aruba.rul +0 -0
- {annet-0.13.4 → annet-3.22.0}/annet/rulebook/texts/nexus.deploy +0 -0
- {annet-0.13.4 → annet-3.22.0}/annet/rulebook/texts/nexus.order +0 -0
- {annet-0.13.4 → annet-3.22.0}/annet/rulebook/texts/nokia.rul +0 -0
- {annet-0.13.4 → annet-3.22.0}/annet/rulebook/texts/pc.order +0 -0
- {annet-0.13.4 → annet-3.22.0}/annet/rulebook/texts/pc.rul +0 -0
- {annet-0.13.4 → annet-3.22.0}/annet/rulebook/texts/ribbon.deploy +0 -0
- {annet-0.13.4 → annet-3.22.0}/annet/rulebook/texts/ribbon.rul +0 -0
- {annet-0.13.4 → annet-3.22.0}/annet/rulebook/texts/routeros.rul +0 -0
- {annet-0.13.4 → annet-3.22.0}/annet/types.py +0 -0
- {annet-0.13.4 → annet-3.22.0}/annet.egg-info/dependency_links.txt +0 -0
- {annet-0.13.4 → annet-3.22.0}/annet.egg-info/top_level.txt +0 -0
- {annet-0.13.4 → annet-3.22.0}/setup.cfg +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: annet
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.22.0
|
|
4
4
|
Summary: annet
|
|
5
5
|
Home-page: https://github.com/annetutil/annet
|
|
6
6
|
License: MIT
|
|
7
|
-
Requires-Python: >=3.
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
8
|
License-File: LICENSE
|
|
9
9
|
License-File: AUTHORS
|
|
10
10
|
Requires-Dist: colorama>=0.4.6
|
|
@@ -14,12 +14,20 @@ Requires-Dist: jsonpointer>=2.4
|
|
|
14
14
|
Requires-Dist: PyYAML>=6.0.1
|
|
15
15
|
Requires-Dist: Pygments>=2.14.0
|
|
16
16
|
Requires-Dist: Mako>=1.2.4
|
|
17
|
-
Requires-Dist: Jinja2>=3.1.2
|
|
18
|
-
Requires-Dist: psutil>=5.8.0
|
|
19
17
|
Requires-Dist: packaging>=23.2
|
|
20
18
|
Requires-Dist: contextlog>=1.1
|
|
21
19
|
Requires-Dist: valkit>=0.1.4
|
|
22
|
-
Requires-Dist: aiohttp>=3.8.4
|
|
23
20
|
Requires-Dist: yarl>=1.8.2
|
|
24
|
-
Requires-Dist: adaptix==3.0.
|
|
21
|
+
Requires-Dist: adaptix==3.0.0b7
|
|
25
22
|
Requires-Dist: dataclass-rest==0.4
|
|
23
|
+
Requires-Dist: ordered-set>=4.1.0
|
|
24
|
+
Provides-Extra: netbox
|
|
25
|
+
Requires-Dist: annetbox[sync]>=0.9.0; extra == "netbox"
|
|
26
|
+
Requires-Dist: requests-cache>=1.2.1; extra == "netbox"
|
|
27
|
+
Dynamic: home-page
|
|
28
|
+
Dynamic: license
|
|
29
|
+
Dynamic: license-file
|
|
30
|
+
Dynamic: provides-extra
|
|
31
|
+
Dynamic: requires-dist
|
|
32
|
+
Dynamic: requires-python
|
|
33
|
+
Dynamic: summary
|
annet-3.22.0/README.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Annet - configuration generation and deploying utility for network equipment
|
|
2
|
+
|
|
3
|
+
[](https://t.me/annet_sup)
|
|
4
|
+
[](https://pypi.python.org/pypi/annet)
|
|
5
|
+
[](https://pypistats.org/packages/annet)
|
|
6
|
+
[](https://github.com/annetutil/annet/blob/master/LICENSE)
|
|
7
|
+
[](https://github.com/annetutil/annet/actions)
|
|
8
|
+
[](https://annetutil.github.io/annet)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
Annet is a configuration generator that can translate differences between old and new configurations into sequence of commands. This feature is vital for CLI-based devices, such as Huawei, Cisco IOS, Cisco NX-OS, Juniper. Devices configured via separate config files, Linux, FreeBSD and Cumulus are also supported.
|
|
12
|
+
|
|
13
|
+
It works this way. Annet `gen`erates configuration for a device by running Python code, which usually goes to the Network Source of Truth, like NetBox. Annet then gets the `diff`erence by getting the configuration from the device and comparing it. Finally, Annet translates the difference into a sequence of commands, called a `patch`. After `deploy`ing these commands, the diff will be empty.
|
|
14
|
+
|
|
15
|
+
Annet has a number of modes (subcommands):
|
|
16
|
+
|
|
17
|
+
- ```annet gen``` - generates the entire config for the specified devices or specified parts of it
|
|
18
|
+
- ```annet diff``` - first does gen and then builds diff with current config version
|
|
19
|
+
- ```annet patch``` - first does diff and then generates a list of commands to apply diff on the device
|
|
20
|
+
- ```annet deploy``` - first does patch and then deploys it to the device
|
|
21
|
+
|
|
22
|
+
Usage help can be obtained by calling ```annet -h``` or for a specific command, such as ```annet gen -h```.
|
|
23
|
+
|
|
24
|
+
<img src="https://github.com/annetutil/annet/blob/main/docs/_static/annet_demo.gif?raw=true" width="800" />
|
|
25
|
+
|
|
26
|
+
## Configuration
|
|
27
|
+
|
|
28
|
+
The path to the configuration file is searched in following order:
|
|
29
|
+
- `ANN_CONTEXT_CONFIG_PATH` env.
|
|
30
|
+
- `~/.annet/context.yml`.
|
|
31
|
+
- `annet/configs/context.yml`.
|
|
32
|
+
|
|
33
|
+
Config example:
|
|
34
|
+
|
|
35
|
+
```yaml
|
|
36
|
+
generators:
|
|
37
|
+
default:
|
|
38
|
+
- my_annet_generators.example
|
|
39
|
+
|
|
40
|
+
storage:
|
|
41
|
+
default:
|
|
42
|
+
adapter: annet.adapters.file.provider
|
|
43
|
+
params:
|
|
44
|
+
path: /path/to/file
|
|
45
|
+
|
|
46
|
+
context:
|
|
47
|
+
default:
|
|
48
|
+
generators: default
|
|
49
|
+
storage: default
|
|
50
|
+
|
|
51
|
+
selected_context: default
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Environment variable `ANN_SELECTED_CONTEXT` can be used to override `selected_context` parameter.
|
|
55
|
+
|
|
56
|
+
## Installation
|
|
57
|
+
|
|
58
|
+
Install from PyPI:
|
|
59
|
+
```shell
|
|
60
|
+
pip install annet
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Or install from source:
|
|
64
|
+
```shell
|
|
65
|
+
git clone https://github.com/annetutil/annet
|
|
66
|
+
cd annet
|
|
67
|
+
pip install -e .
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Building doc
|
|
71
|
+
1. Install dependencies:
|
|
72
|
+
```shell
|
|
73
|
+
pip install -r requirements-doc.txt
|
|
74
|
+
```
|
|
75
|
+
2. Build
|
|
76
|
+
```shell
|
|
77
|
+
sphinx-build -M html docs docs-build
|
|
78
|
+
```
|
|
79
|
+
3. Open rendered html in browser
|
|
80
|
+
```shell
|
|
81
|
+
open docs-build/html/index.html # macOS
|
|
82
|
+
xdg-open docs-build/html/index.html # Linux
|
|
83
|
+
start docs-build/html/index.html # Windows
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Links
|
|
87
|
+
|
|
88
|
+
* [Online Documentation](https://annetutil.github.io/annet/)
|
|
89
|
+
* [Tutorial](https://annetutil.github.io/annet/main/usage/tutorial.html)
|
|
@@ -7,14 +7,15 @@ from argparse import SUPPRESS, Namespace
|
|
|
7
7
|
|
|
8
8
|
import colorama
|
|
9
9
|
import yaml
|
|
10
|
-
from annet.annlib.errors import ( # pylint: disable=wrong-import-position
|
|
11
|
-
DeployCancelled,
|
|
12
|
-
ExecError,
|
|
13
|
-
)
|
|
14
10
|
from contextlog import patch_logging, patch_threading
|
|
15
11
|
from valkit.python import valid_logging_level
|
|
16
12
|
|
|
17
13
|
import annet.argparse
|
|
14
|
+
from annet.vendors import tabparser # pylint: disable=unused-import
|
|
15
|
+
from annet.annlib.errors import ( # pylint: disable=wrong-import-position
|
|
16
|
+
DeployCancelled,
|
|
17
|
+
ExecError,
|
|
18
|
+
)
|
|
18
19
|
|
|
19
20
|
|
|
20
21
|
__all__ = ("DeployCancelled", "ExecError")
|
|
@@ -49,13 +50,8 @@ def init(options: Namespace):
|
|
|
49
50
|
colorama.init = lambda *_, **__: None
|
|
50
51
|
colorama.init()
|
|
51
52
|
|
|
52
|
-
# Workaround for Python 3.8.0: https://bugs.python.org/issue38529
|
|
53
|
-
import asyncio.streams
|
|
54
|
-
if hasattr(asyncio.streams.StreamReaderProtocol, "_on_reader_gc"):
|
|
55
|
-
asyncio.streams.StreamReaderProtocol._on_reader_gc = lambda *args, **kwargs: None # pylint: disable=protected-access
|
|
56
|
-
|
|
57
53
|
|
|
58
54
|
def assert_python_version():
|
|
59
|
-
if sys.version_info < (3,
|
|
60
|
-
sys.stderr.write("Error: you need python 3.
|
|
55
|
+
if sys.version_info < (3, 10, 0):
|
|
56
|
+
sys.stderr.write("Error: you need python 3.10.0 or higher\n")
|
|
61
57
|
sys.exit(1)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from annet.deploy import Fetcher
|
|
2
|
+
from annet.connectors import AdapterWithConfig
|
|
3
|
+
from typing import Dict, List, Any
|
|
4
|
+
from annet.storage import Device
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class StubFetcher(Fetcher, AdapterWithConfig):
|
|
8
|
+
@classmethod
|
|
9
|
+
def with_config(cls, **kwargs: Dict[str, Any]) -> Fetcher:
|
|
10
|
+
return cls(**kwargs)
|
|
11
|
+
|
|
12
|
+
async def fetch_packages(self,
|
|
13
|
+
devices: list[Device],
|
|
14
|
+
processes: int = 1,
|
|
15
|
+
max_slots: int = 0,
|
|
16
|
+
) -> tuple[dict[Device, str], dict[Device, Any]]:
|
|
17
|
+
raise NotImplementedError()
|
|
18
|
+
|
|
19
|
+
async def fetch(self,
|
|
20
|
+
devices: list[Device],
|
|
21
|
+
files_to_download: dict[str, list[str]] | None = None,
|
|
22
|
+
processes: int = 1,
|
|
23
|
+
max_slots: int = 0,
|
|
24
|
+
):
|
|
25
|
+
raise NotImplementedError()
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
from dataclasses import dataclass, fields
|
|
3
|
+
from typing import Any, Iterable, List, Optional, Sequence
|
|
4
|
+
|
|
5
|
+
import yaml
|
|
6
|
+
|
|
7
|
+
from annet.annlib.netdev.views.dump import DumpableView
|
|
8
|
+
from annet.annlib.netdev.views.hardware import HardwareView
|
|
9
|
+
from annet.connectors import AdapterWithName
|
|
10
|
+
from annet.hardware import hardware_connector
|
|
11
|
+
from annet.storage import Device as DeviceProtocol
|
|
12
|
+
from annet.storage import Query, Storage, StorageProvider
|
|
13
|
+
from annet.adapters.netbox.common.manufacturer import get_breed
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class Interface(DumpableView):
|
|
18
|
+
name: str
|
|
19
|
+
description: str
|
|
20
|
+
enabled: bool = True
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def _dump__list_key(self):
|
|
24
|
+
return self.name
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass
|
|
28
|
+
class DeviceStorage:
|
|
29
|
+
fqdn: str
|
|
30
|
+
|
|
31
|
+
vendor: Optional[str] = None
|
|
32
|
+
hw_model: Optional[str] = None
|
|
33
|
+
sw_version: Optional[str] = None
|
|
34
|
+
breed: Optional[str] = None
|
|
35
|
+
|
|
36
|
+
hostname: Optional[str] = None
|
|
37
|
+
serial: Optional[str] = None
|
|
38
|
+
id: Optional[str] = None
|
|
39
|
+
interfaces: Optional[list[Interface]] = None
|
|
40
|
+
storage: Optional[Storage] = None
|
|
41
|
+
|
|
42
|
+
hw: HardwareView = dataclasses.field(init=False)
|
|
43
|
+
|
|
44
|
+
def __post_init__(self):
|
|
45
|
+
if not self.id:
|
|
46
|
+
self.id = self.fqdn
|
|
47
|
+
if not self.hostname:
|
|
48
|
+
self.hostname = self.fqdn.split(".")[0]
|
|
49
|
+
|
|
50
|
+
if self.hw_model:
|
|
51
|
+
hw = HardwareView(self.hw_model, self.sw_version)
|
|
52
|
+
if self.vendor and self.vendor != hw.vendor:
|
|
53
|
+
raise Exception(f"Vendor {self.vendor} is not vendor from hw model ({hw.vendor})")
|
|
54
|
+
else:
|
|
55
|
+
self.vendor = hw.vendor
|
|
56
|
+
else:
|
|
57
|
+
hw_provider = hardware_connector.get()
|
|
58
|
+
hw: HardwareView = hw_provider.vendor_to_hw(self.vendor)
|
|
59
|
+
if not hw:
|
|
60
|
+
raise Exception("unknown vendor")
|
|
61
|
+
self.hw_model = hw.model
|
|
62
|
+
self.hw = hw
|
|
63
|
+
|
|
64
|
+
if not self.breed:
|
|
65
|
+
if self.hw.model:
|
|
66
|
+
parts = self.hw.model.split(maxsplit=1)
|
|
67
|
+
if len(parts) >= 2:
|
|
68
|
+
self.breed = get_breed(parts[0], parts[1])
|
|
69
|
+
if not self.breed:
|
|
70
|
+
self.breed = self.hw.vendor
|
|
71
|
+
|
|
72
|
+
if isinstance(self.interfaces, list):
|
|
73
|
+
interfaces = []
|
|
74
|
+
for iface in self.interfaces:
|
|
75
|
+
try:
|
|
76
|
+
interfaces.append(Interface(**iface))
|
|
77
|
+
except Exception as e:
|
|
78
|
+
raise Exception("unable to parse %s as Interface: %s" % (iface, e))
|
|
79
|
+
self.interfaces = interfaces
|
|
80
|
+
|
|
81
|
+
def set_storage(self, storage: Storage):
|
|
82
|
+
self.storage = storage
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@dataclass
|
|
86
|
+
class Device(DeviceProtocol, DumpableView):
|
|
87
|
+
dev: DeviceStorage
|
|
88
|
+
|
|
89
|
+
def __hash__(self):
|
|
90
|
+
return hash((self.id, type(self)))
|
|
91
|
+
|
|
92
|
+
def __eq__(self, other):
|
|
93
|
+
return type(self) is type(other) and self.fqdn == other.fqdn and self.vendor == other.vendor
|
|
94
|
+
|
|
95
|
+
def is_pc(self) -> bool:
|
|
96
|
+
return False
|
|
97
|
+
|
|
98
|
+
@property
|
|
99
|
+
def hostname(self) -> str:
|
|
100
|
+
return self.dev.hostname
|
|
101
|
+
|
|
102
|
+
@property
|
|
103
|
+
def fqdn(self) -> str:
|
|
104
|
+
return self.dev.fqdn
|
|
105
|
+
|
|
106
|
+
@property
|
|
107
|
+
def id(self):
|
|
108
|
+
return self.dev.id
|
|
109
|
+
|
|
110
|
+
@property
|
|
111
|
+
def storage(self) -> Storage:
|
|
112
|
+
return self.dev.storage
|
|
113
|
+
|
|
114
|
+
@property
|
|
115
|
+
def hw(self) -> HardwareView:
|
|
116
|
+
return self.dev.hw
|
|
117
|
+
|
|
118
|
+
@property
|
|
119
|
+
def breed(self) -> str:
|
|
120
|
+
return self.dev.breed
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
def neighbours_ids(self):
|
|
124
|
+
return []
|
|
125
|
+
|
|
126
|
+
@property
|
|
127
|
+
def neighbours_fqdns(self) -> list[str]:
|
|
128
|
+
return []
|
|
129
|
+
|
|
130
|
+
def make_lag(self, lag: int, ports: Sequence[str], lag_min_links: Optional[int]) -> Interface:
|
|
131
|
+
raise NotImplementedError
|
|
132
|
+
|
|
133
|
+
def add_svi(self, svi: int) -> Interface:
|
|
134
|
+
raise NotImplementedError
|
|
135
|
+
|
|
136
|
+
def add_subif(self, interface: str, subif: int) -> Interface:
|
|
137
|
+
raise NotImplementedError
|
|
138
|
+
|
|
139
|
+
def find_interface(self, name: str) -> Optional[Interface]:
|
|
140
|
+
raise NotImplementedError
|
|
141
|
+
|
|
142
|
+
def flush_perf(self):
|
|
143
|
+
pass
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
@dataclass
|
|
147
|
+
class Devices:
|
|
148
|
+
devices: list[Device]
|
|
149
|
+
|
|
150
|
+
def __post_init__(self):
|
|
151
|
+
if isinstance(self.devices, list):
|
|
152
|
+
devices = []
|
|
153
|
+
for dev in self.devices:
|
|
154
|
+
try:
|
|
155
|
+
devices.append(Device(dev=DeviceStorage(**dev)))
|
|
156
|
+
except Exception as e:
|
|
157
|
+
raise Exception("unable to parse %s as Device: %s" % (dev, e))
|
|
158
|
+
self.devices = devices
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
class Provider(StorageProvider, AdapterWithName):
|
|
162
|
+
def storage(self):
|
|
163
|
+
return storage_factory
|
|
164
|
+
|
|
165
|
+
def opts(self):
|
|
166
|
+
return StorageOpts
|
|
167
|
+
|
|
168
|
+
def query(self):
|
|
169
|
+
return Query
|
|
170
|
+
|
|
171
|
+
@classmethod
|
|
172
|
+
def name(cls) -> str:
|
|
173
|
+
return "file"
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
@dataclass
|
|
177
|
+
class Query(Query):
|
|
178
|
+
query: List[str]
|
|
179
|
+
|
|
180
|
+
@classmethod
|
|
181
|
+
def new(cls, query: str | Iterable[str], hosts_range: Optional[slice] = None) -> "Query":
|
|
182
|
+
if hosts_range is not None:
|
|
183
|
+
raise ValueError("host_range is not supported")
|
|
184
|
+
return cls(query=list(query))
|
|
185
|
+
|
|
186
|
+
@property
|
|
187
|
+
def globs(self):
|
|
188
|
+
return self.query
|
|
189
|
+
|
|
190
|
+
def is_empty(self) -> bool:
|
|
191
|
+
return len(self.query) == 0
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
class StorageOpts:
|
|
195
|
+
def __init__(self, path: str):
|
|
196
|
+
self.path = path
|
|
197
|
+
|
|
198
|
+
@classmethod
|
|
199
|
+
def parse_params(cls, conf_params: Optional[dict[str, str]], cli_opts: Any):
|
|
200
|
+
path = conf_params.get("path")
|
|
201
|
+
if not path:
|
|
202
|
+
raise Exception("empty path")
|
|
203
|
+
return cls(path=path)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def storage_factory(opts: StorageOpts) -> Storage:
|
|
207
|
+
return FS(opts)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
class FS(Storage):
|
|
211
|
+
def __init__(self, opts: StorageOpts):
|
|
212
|
+
self.opts = opts
|
|
213
|
+
self.inventory: Devices = read_inventory(opts.path, self)
|
|
214
|
+
|
|
215
|
+
def __enter__(self):
|
|
216
|
+
return self
|
|
217
|
+
|
|
218
|
+
def __exit__(self, _, __, ___):
|
|
219
|
+
pass
|
|
220
|
+
|
|
221
|
+
def resolve_object_ids_by_query(self, query: Query) -> list[str]:
|
|
222
|
+
result = filter_query(self.inventory.devices, query)
|
|
223
|
+
return [dev.fqdn for dev in result]
|
|
224
|
+
|
|
225
|
+
def resolve_fdnds_by_query(self, query: Query) -> list[str]:
|
|
226
|
+
result = filter_query(self.inventory.devices, query)
|
|
227
|
+
return [dev.fqdn for dev in result]
|
|
228
|
+
|
|
229
|
+
def make_devices(
|
|
230
|
+
self,
|
|
231
|
+
query: Query | list,
|
|
232
|
+
preload_neighbors=False,
|
|
233
|
+
use_mesh=None,
|
|
234
|
+
preload_extra_fields=False,
|
|
235
|
+
**kwargs,
|
|
236
|
+
) -> list[Device]:
|
|
237
|
+
if isinstance(query, list):
|
|
238
|
+
query = Query.new(query)
|
|
239
|
+
result = filter_query(self.inventory.devices, query)
|
|
240
|
+
return result
|
|
241
|
+
|
|
242
|
+
def get_device(self, obj_id: str, preload_neighbors=False, use_mesh=None, **kwargs) -> Device:
|
|
243
|
+
result = filter_query(self.inventory.devices, Query.new(obj_id))
|
|
244
|
+
if not result:
|
|
245
|
+
raise Exception("not found")
|
|
246
|
+
return result[0]
|
|
247
|
+
|
|
248
|
+
def flush_perf(self):
|
|
249
|
+
pass
|
|
250
|
+
|
|
251
|
+
def resolve_all_fdnds(self) -> list[str]:
|
|
252
|
+
return [d.fqdn for d in self.inventory.devices]
|
|
253
|
+
|
|
254
|
+
def search_connections(self, device: "Device", neighbor: "Device") -> list[tuple["Interface", "Interface"]]:
|
|
255
|
+
return []
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def filter_query(devices: list[Device], query: Query) -> list[Device]:
|
|
259
|
+
result: list[Device] = []
|
|
260
|
+
for dev in devices:
|
|
261
|
+
if dev.fqdn in query.query:
|
|
262
|
+
result.append(dev)
|
|
263
|
+
return result
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def read_inventory(path: str, storage: Storage) -> Devices:
|
|
267
|
+
with open(path, "r") as f:
|
|
268
|
+
file_data = yaml.load(f, Loader=yaml.SafeLoader)
|
|
269
|
+
res = dataclass_from_dict(Devices, file_data)
|
|
270
|
+
for dev in res.devices:
|
|
271
|
+
dev.dev.set_storage(storage)
|
|
272
|
+
return res
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def dataclass_from_dict(klass: type, d: dict[str, Any]):
|
|
276
|
+
try:
|
|
277
|
+
fieldtypes = {f.name: f.type for f in fields(klass)}
|
|
278
|
+
except TypeError:
|
|
279
|
+
return d
|
|
280
|
+
return klass(**{f: dataclass_from_dict(fieldtypes[f], d[f]) for f in d})
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
from abc import abstractmethod, ABC
|
|
2
|
+
from typing import Generic, TypeVar
|
|
3
|
+
|
|
4
|
+
from annet.annlib.netdev.views.hardware import HardwareView
|
|
5
|
+
from .manufacturer import get_breed, get_hw
|
|
6
|
+
from .models import NetboxDevice, Interface, IpAddress, Prefix, \
|
|
7
|
+
FHRPGroupAssignment, FHRPGroup
|
|
8
|
+
|
|
9
|
+
NetboxDeviceT = TypeVar("NetboxDeviceT", bound=NetboxDevice)
|
|
10
|
+
InterfaceT = TypeVar("InterfaceT", bound=Interface)
|
|
11
|
+
IpAddressT = TypeVar("IpAddressT", bound=IpAddress)
|
|
12
|
+
PrefixT = TypeVar("PrefixT", bound=Prefix)
|
|
13
|
+
FHRPGroupT = TypeVar("FHRPGroupT", bound=FHRPGroup)
|
|
14
|
+
FHRPGroupAssignmentT = TypeVar(
|
|
15
|
+
"FHRPGroupAssignmentT", bound=FHRPGroupAssignment,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def get_device_breed(device: NetboxDeviceT) -> str:
|
|
20
|
+
if device.device_type and device.device_type.manufacturer:
|
|
21
|
+
return get_breed(
|
|
22
|
+
device.device_type.manufacturer.name,
|
|
23
|
+
device.device_type.model,
|
|
24
|
+
)
|
|
25
|
+
return ""
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def get_device_hw(device: NetboxDeviceT) -> HardwareView:
|
|
29
|
+
if device.device_type and device.device_type.manufacturer:
|
|
30
|
+
return get_hw(
|
|
31
|
+
device.device_type.manufacturer.name,
|
|
32
|
+
device.device_type.model,
|
|
33
|
+
device.platform.name if device.platform else "",
|
|
34
|
+
)
|
|
35
|
+
return HardwareView("", "")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class NetboxAdapter(
|
|
39
|
+
ABC,
|
|
40
|
+
Generic[
|
|
41
|
+
NetboxDeviceT,
|
|
42
|
+
InterfaceT,
|
|
43
|
+
IpAddressT,
|
|
44
|
+
PrefixT,
|
|
45
|
+
FHRPGroupT,
|
|
46
|
+
FHRPGroupAssignmentT,
|
|
47
|
+
],
|
|
48
|
+
):
|
|
49
|
+
@abstractmethod
|
|
50
|
+
def list_fqdns(self, query: dict[str, list[str]] | None = None) -> list[str]:
|
|
51
|
+
raise NotImplementedError()
|
|
52
|
+
|
|
53
|
+
@abstractmethod
|
|
54
|
+
def list_devices(self, query: dict[str, list[str]]) -> list[NetboxDeviceT]:
|
|
55
|
+
raise NotImplementedError()
|
|
56
|
+
|
|
57
|
+
@abstractmethod
|
|
58
|
+
def get_device(self, device_id: int) -> NetboxDeviceT:
|
|
59
|
+
raise NotImplementedError()
|
|
60
|
+
|
|
61
|
+
@abstractmethod
|
|
62
|
+
def list_interfaces_by_devices(self, device_ids: list[int]) -> list[InterfaceT]:
|
|
63
|
+
raise NotImplementedError()
|
|
64
|
+
|
|
65
|
+
@abstractmethod
|
|
66
|
+
def list_interfaces(self, ids: list[int]) -> list[InterfaceT]:
|
|
67
|
+
raise NotImplementedError()
|
|
68
|
+
|
|
69
|
+
@abstractmethod
|
|
70
|
+
def list_ipaddr_by_ifaces(self, iface_ids: list[int]) -> list[IpAddressT]:
|
|
71
|
+
raise NotImplementedError()
|
|
72
|
+
|
|
73
|
+
@abstractmethod
|
|
74
|
+
def list_ipprefixes(self, prefixes: list[str]) -> list[PrefixT]:
|
|
75
|
+
raise NotImplementedError()
|
|
76
|
+
|
|
77
|
+
@abstractmethod
|
|
78
|
+
def list_fhrp_group_assignments(
|
|
79
|
+
self, iface_ids: list[int],
|
|
80
|
+
) -> list[FHRPGroupAssignmentT]:
|
|
81
|
+
raise NotImplementedError()
|
|
82
|
+
|
|
83
|
+
@abstractmethod
|
|
84
|
+
def list_fhrp_groups(
|
|
85
|
+
self, ids: list[int],
|
|
86
|
+
) -> list[FHRPGroupT]:
|
|
87
|
+
raise NotImplementedError()
|
|
@@ -79,9 +79,11 @@ def collect(func: Func, field: str = "", batch_size: int = 100) -> Func:
|
|
|
79
79
|
|
|
80
80
|
|
|
81
81
|
class BaseNetboxClient(RequestsClient):
|
|
82
|
-
def __init__(self, url: str, token: str):
|
|
82
|
+
def __init__(self, url: str, token: str, insecure: bool = False):
|
|
83
83
|
url = url.rstrip("/") + "/api/"
|
|
84
84
|
session = Session()
|
|
85
|
+
if insecure:
|
|
86
|
+
session.verify = False
|
|
85
87
|
if token:
|
|
86
88
|
session.headers["Authorization"] = f"Token {token}"
|
|
87
89
|
super().__init__(url, session)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from logging import getLogger
|
|
2
|
+
|
|
3
|
+
from annet.annlib.netdev.views.hardware import HardwareView
|
|
4
|
+
|
|
5
|
+
logger = getLogger(__name__)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_hw(manufacturer: str, model: str, platform_name: str):
|
|
9
|
+
# By some reason Netbox calls Mellanox SN as MSN, so we fix them here
|
|
10
|
+
if manufacturer == "Mellanox" and model.startswith("MSN"):
|
|
11
|
+
model = model.replace("MSN", "SN", 1)
|
|
12
|
+
|
|
13
|
+
return HardwareView(manufacturer + " " + model, platform_name)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def get_breed(manufacturer: str, model: str):
|
|
17
|
+
hw = get_hw(manufacturer, model, "")
|
|
18
|
+
if hw.Huawei.CE:
|
|
19
|
+
return "vrp85"
|
|
20
|
+
elif hw.Huawei.NE:
|
|
21
|
+
return "vrp85"
|
|
22
|
+
elif hw.Huawei:
|
|
23
|
+
return "vrp55"
|
|
24
|
+
elif hw.H3C:
|
|
25
|
+
return "h3c"
|
|
26
|
+
elif hw.PC.NVIDIA or hw.PC.Mellanox:
|
|
27
|
+
return "cuml2"
|
|
28
|
+
elif hw.Juniper:
|
|
29
|
+
return "jun10"
|
|
30
|
+
elif hw.Cisco.Nexus:
|
|
31
|
+
return "nxos"
|
|
32
|
+
elif hw.Cisco:
|
|
33
|
+
return "ios12"
|
|
34
|
+
elif hw.Arista:
|
|
35
|
+
return "eos4"
|
|
36
|
+
elif hw.B4com:
|
|
37
|
+
return "bcom-os"
|
|
38
|
+
elif hw.RouterOS:
|
|
39
|
+
return "routeros"
|
|
40
|
+
elif hw.PC.Moxa or hw.PC.Nebius:
|
|
41
|
+
return "moxa"
|
|
42
|
+
elif hw.PC:
|
|
43
|
+
return "pc"
|
|
44
|
+
return ""
|