annet 3.15.0__tar.gz → 3.16.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.
Potentially problematic release.
This version of annet might be problematic. Click here for more details.
- {annet-3.15.0/annet.egg-info → annet-3.16.0}/PKG-INFO +1 -1
- {annet-3.15.0 → annet-3.16.0}/annet/adapters/file/provider.py +11 -1
- {annet-3.15.0 → annet-3.16.0}/annet/annlib/netdev/devdb/data/devdb.json +19 -18
- {annet-3.15.0 → annet-3.16.0}/annet/annlib/netdev/views/hardware.py +7 -2
- {annet-3.15.0 → annet-3.16.0}/annet/annlib/output.py +8 -6
- annet-3.16.0/annet/annlib/rbparser/platform.py +1 -0
- annet-3.16.0/annet/rulebook/h3c/aaa.py +28 -0
- annet-3.16.0/annet/rulebook/h3c/bgp.py +84 -0
- annet-3.16.0/annet/rulebook/h3c/iface.py +22 -0
- annet-3.16.0/annet/rulebook/h3c/misc.py +280 -0
- annet-3.16.0/annet/rulebook/h3c/vlandb.py +118 -0
- annet-3.16.0/annet/rulebook/texts/h3c.deploy +196 -0
- annet-3.16.0/annet/rulebook/texts/h3c.order +390 -0
- annet-3.16.0/annet/rulebook/texts/h3c.rul +432 -0
- {annet-3.15.0 → annet-3.16.0}/annet/vendors/library/h3c.py +3 -0
- {annet-3.15.0 → annet-3.16.0/annet.egg-info}/PKG-INFO +1 -1
- {annet-3.15.0 → annet-3.16.0}/annet.egg-info/SOURCES.txt +9 -0
- annet-3.16.0/annet_generators/__init__.py +0 -0
- annet-3.15.0/annet/annlib/rbparser/platform.py +0 -3
- {annet-3.15.0 → annet-3.16.0}/AUTHORS +0 -0
- {annet-3.15.0 → annet-3.16.0}/LICENSE +0 -0
- {annet-3.15.0 → annet-3.16.0}/MANIFEST.in +0 -0
- {annet-3.15.0 → annet-3.16.0}/README.md +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/__init__.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/adapters/__init__.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/adapters/fetchers/__init__.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/adapters/fetchers/stub/__init__.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/adapters/fetchers/stub/fetcher.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/adapters/file/__init__.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/adapters/netbox/__init__.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/adapters/netbox/common/__init__.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/adapters/netbox/common/adapter.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/adapters/netbox/common/client.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/adapters/netbox/common/manufacturer.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/adapters/netbox/common/models.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/adapters/netbox/common/query.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/adapters/netbox/common/status_client.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/adapters/netbox/common/storage_base.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/adapters/netbox/common/storage_opts.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/adapters/netbox/provider.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/adapters/netbox/v24/__init__.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/adapters/netbox/v24/models.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/adapters/netbox/v24/storage.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/adapters/netbox/v37/__init__.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/adapters/netbox/v37/models.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/adapters/netbox/v37/storage.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/adapters/netbox/v41/__init__.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/adapters/netbox/v41/models.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/adapters/netbox/v41/storage.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/adapters/netbox/v42/__init__.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/adapters/netbox/v42/models.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/adapters/netbox/v42/storage.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/annet.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/annlib/__init__.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/annlib/command.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/annlib/diff.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/annlib/errors.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/annlib/filter_acl.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/annlib/jsontools.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/annlib/lib.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/annlib/netdev/__init__.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/annlib/netdev/db.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/annlib/netdev/devdb/__init__.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/annlib/netdev/views/__init__.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/annlib/netdev/views/dump.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/annlib/patching.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/annlib/rbparser/__init__.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/annlib/rbparser/acl.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/annlib/rbparser/deploying.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/annlib/rbparser/ordering.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/annlib/rbparser/syntax.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/annlib/rulebook/__init__.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/annlib/rulebook/common.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/annlib/types.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/api/__init__.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/argparse.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/bgp_models.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/cli.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/cli_args.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/configs/context.yml +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/configs/logging.yaml +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/connectors.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/deploy.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/deploy_ui.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/diff.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/filtering.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/gen.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/generators/__init__.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/generators/base.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/generators/common/__init__.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/generators/common/initial.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/generators/entire.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/generators/exceptions.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/generators/jsonfragment.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/generators/partial.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/generators/perf.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/generators/ref.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/generators/result.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/hardware.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/implicit.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/lib.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/mesh/__init__.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/mesh/basemodel.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/mesh/device_models.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/mesh/executor.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/mesh/match_args.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/mesh/models_converter.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/mesh/peer_models.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/mesh/port_processor.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/mesh/registry.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/output.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/parallel.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/patching.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/reference.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rpl/__init__.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rpl/action.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rpl/condition.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rpl/match_builder.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rpl/policy.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rpl/result.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rpl/routemap.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rpl/statement_builder.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rpl_generators/__init__.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rpl_generators/aspath.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rpl_generators/community.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rpl_generators/cumulus_frr.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rpl_generators/entities.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rpl_generators/execute.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rpl_generators/policy.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rpl_generators/prefix_lists.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rpl_generators/rd.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/__init__.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/arista/__init__.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/arista/aaa.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/arista/iface.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/aruba/__init__.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/aruba/ap_env.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/aruba/misc.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/b4com/__init__.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/b4com/file.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/b4com/iface.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/cisco/__init__.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/cisco/iface.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/cisco/misc.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/cisco/vlandb.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/common.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/deploying.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/generic/__init__.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/generic/misc.py +0 -0
- {annet-3.15.0/annet/rulebook/huawei → annet-3.16.0/annet/rulebook/h3c}/__init__.py +0 -0
- {annet-3.15.0/annet/rulebook/nexus → annet-3.16.0/annet/rulebook/huawei}/__init__.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/huawei/aaa.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/huawei/bgp.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/huawei/iface.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/huawei/misc.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/huawei/vlandb.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/juniper/__init__.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/juniper/iface.py +0 -0
- {annet-3.15.0/annet/rulebook/routeros → annet-3.16.0/annet/rulebook/nexus}/__init__.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/nexus/iface.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/patching.py +0 -0
- {annet-3.15.0/annet/vendors/library → annet-3.16.0/annet/rulebook/routeros}/__init__.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/routeros/file.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/texts/arista.deploy +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/texts/arista.order +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/texts/arista.rul +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/texts/aruba.deploy +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/texts/aruba.order +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/texts/aruba.rul +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/texts/b4com.deploy +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/texts/b4com.order +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/texts/b4com.rul +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/texts/cisco.deploy +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/texts/cisco.order +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/texts/cisco.rul +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/texts/huawei.deploy +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/texts/huawei.order +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/texts/huawei.rul +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/texts/iosxr.deploy +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/texts/iosxr.order +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/texts/iosxr.rul +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/texts/juniper.order +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/texts/juniper.rul +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/texts/nexus.deploy +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/texts/nexus.order +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/texts/nexus.rul +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/texts/nokia.rul +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/texts/optixtrans.deploy +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/texts/optixtrans.order +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/texts/optixtrans.rul +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/texts/pc.deploy +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/texts/pc.order +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/texts/pc.rul +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/texts/ribbon.deploy +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/texts/ribbon.rul +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/texts/routeros.order +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/rulebook/texts/routeros.rul +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/storage.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/text_term_format.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/tracing.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/types.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/vendors/__init__.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/vendors/base.py +0 -0
- {annet-3.15.0/annet_generators → annet-3.16.0/annet/vendors/library}/__init__.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/vendors/library/arista.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/vendors/library/aruba.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/vendors/library/b4com.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/vendors/library/cisco.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/vendors/library/huawei.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/vendors/library/iosxr.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/vendors/library/juniper.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/vendors/library/nexus.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/vendors/library/nokia.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/vendors/library/optixtrans.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/vendors/library/pc.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/vendors/library/ribbon.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/vendors/library/routeros.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/vendors/registry.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet/vendors/tabparser.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet.egg-info/dependency_links.txt +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet.egg-info/entry_points.txt +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet.egg-info/requires.txt +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet.egg-info/top_level.txt +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet_generators/example/__init__.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet_generators/example/hostname.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet_generators/example/lldp.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet_generators/mesh_example/__init__.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet_generators/mesh_example/bgp.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet_generators/mesh_example/mesh_logic.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet_generators/rpl_example/__init__.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet_generators/rpl_example/generator.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet_generators/rpl_example/items.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet_generators/rpl_example/mesh.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/annet_generators/rpl_example/route_policy.py +0 -0
- {annet-3.15.0 → annet-3.16.0}/requirements.txt +0 -0
- {annet-3.15.0 → annet-3.16.0}/setup.cfg +0 -0
- {annet-3.15.0 → annet-3.16.0}/setup.py +0 -0
|
@@ -10,6 +10,7 @@ from annet.connectors import AdapterWithName
|
|
|
10
10
|
from annet.hardware import hardware_connector
|
|
11
11
|
from annet.storage import Device as DeviceProtocol
|
|
12
12
|
from annet.storage import Query, Storage, StorageProvider
|
|
13
|
+
from annet.adapters.netbox.common.manufacturer import get_breed
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
@dataclass
|
|
@@ -30,6 +31,7 @@ class DeviceStorage:
|
|
|
30
31
|
vendor: Optional[str] = None
|
|
31
32
|
hw_model: Optional[str] = None
|
|
32
33
|
sw_version: Optional[str] = None
|
|
34
|
+
breed: Optional[str] = None
|
|
33
35
|
|
|
34
36
|
hostname: Optional[str] = None
|
|
35
37
|
serial: Optional[str] = None
|
|
@@ -59,6 +61,14 @@ class DeviceStorage:
|
|
|
59
61
|
self.hw_model = hw.model
|
|
60
62
|
self.hw = hw
|
|
61
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
|
+
|
|
62
72
|
if isinstance(self.interfaces, list):
|
|
63
73
|
interfaces = []
|
|
64
74
|
for iface in self.interfaces:
|
|
@@ -107,7 +117,7 @@ class Device(DeviceProtocol, DumpableView):
|
|
|
107
117
|
|
|
108
118
|
@property
|
|
109
119
|
def breed(self) -> str:
|
|
110
|
-
return self.dev.
|
|
120
|
+
return self.dev.breed
|
|
111
121
|
|
|
112
122
|
@property
|
|
113
123
|
def neighbours_ids(self):
|
|
@@ -20,23 +20,24 @@
|
|
|
20
20
|
"Cisco.Catalyst.C3700": "37\\d\\d",
|
|
21
21
|
"Cisco.Catalyst.C4900": "49\\d\\d",
|
|
22
22
|
"Cisco.Catalyst.C6500": "65\\d\\d",
|
|
23
|
-
|
|
24
|
-
"Cisco.Nexus
|
|
25
|
-
"Cisco.Nexus.N3x
|
|
26
|
-
"Cisco.Nexus.N3x.
|
|
27
|
-
"Cisco.Nexus.N3x.
|
|
28
|
-
"Cisco.Nexus.
|
|
29
|
-
"Cisco.Nexus.N5x
|
|
30
|
-
"Cisco.Nexus.N5x.
|
|
31
|
-
"Cisco.Nexus.
|
|
32
|
-
"Cisco.Nexus.N6x
|
|
33
|
-
"Cisco.Nexus.
|
|
34
|
-
"Cisco.Nexus.N7x
|
|
35
|
-
"Cisco.Nexus.N7x.
|
|
23
|
+
|
|
24
|
+
"Cisco.Nexus": " [Nn](exus)?",
|
|
25
|
+
"Cisco.Nexus.N3x": " (N3K-[A-Z])?3\\d\\d\\d",
|
|
26
|
+
"Cisco.Nexus.N3x.N3100": "31\\d\\d",
|
|
27
|
+
"Cisco.Nexus.N3x.N3432": "3432",
|
|
28
|
+
"Cisco.Nexus.N3x.N3500": "35\\d\\d",
|
|
29
|
+
"Cisco.Nexus.N5x": " (N5K-[A-Z])?5\\d\\d\\d",
|
|
30
|
+
"Cisco.Nexus.N5x.N5000": "50\\d\\d",
|
|
31
|
+
"Cisco.Nexus.N5x.N5500": "55\\d\\d",
|
|
32
|
+
"Cisco.Nexus.N6x": " (N6K-[A-Z])?6\\d\\d\\d",
|
|
33
|
+
"Cisco.Nexus.N6x.N6000": "60\\d\\d",
|
|
34
|
+
"Cisco.Nexus.N7x": " (N7K-[A-Z])?7\\d\\d\\d",
|
|
35
|
+
"Cisco.Nexus.N7x.N7000": "70\\d\\d",
|
|
36
|
+
"Cisco.Nexus.N7x.N7700": "77\\d\\d",
|
|
36
37
|
"Cisco.Nexus.N9x": " (N9K-[A-Z])?9\\d\\d\\d",
|
|
37
|
-
"Cisco.Nexus.N9x.N9316": "
|
|
38
|
-
"Cisco.Nexus.N9x.N9500": "
|
|
39
|
-
"Cisco.Nexus.N9x.N9364": "
|
|
38
|
+
"Cisco.Nexus.N9x.N9316": "9316",
|
|
39
|
+
"Cisco.Nexus.N9x.N9500": "95\\d\\d",
|
|
40
|
+
"Cisco.Nexus.N9x.N9364": "9364",
|
|
40
41
|
|
|
41
42
|
"Huawei": "^[Hh]uawei",
|
|
42
43
|
"Huawei.CE": " CE\\d+",
|
|
@@ -110,9 +111,9 @@
|
|
|
110
111
|
"Nokia.NS7750": " 7750",
|
|
111
112
|
"Nokia.SR_1s": "SR-1s",
|
|
112
113
|
|
|
113
|
-
"PC": "^(PC|pc|[Mm]ellanox SN|[Ee]dge-?[Cc]ore|[Mm]oxa|NVIDIA|Nebius|[Aa]sterfusion CX|[Uu]fi[Ss]pace|(VERTIV )?[Aa]vocent)",
|
|
114
|
+
"PC": "^(PC|pc|[Mm]ellanox SN|[Ee]dge-?[Cc]ore|[Mm]oxa|NVIDIA|Nebius|[Aa]sterfusion CX|[Uu]fi[Ss]pace|NADDOD|FSCOM|(VERTIV )?[Aa]vocent)",
|
|
114
115
|
|
|
115
|
-
"PC.Whitebox": "([Mm]ellanox SN|[Ee]dge-?[Cc]ore|NVIDIA|[Aa]sterfusion CX|[Uu]fi[Ss]pace)",
|
|
116
|
+
"PC.Whitebox": "([Mm]ellanox SN|[Ee]dge-?[Cc]ore|NVIDIA|[Aa]sterfusion CX|[Uu]fi[Ss]pace|NADDOD|FSCOM)",
|
|
116
117
|
"PC.Whitebox.Mellanox": "[Mm]ellanox",
|
|
117
118
|
"PC.Whitebox.Mellanox.SN": " SN",
|
|
118
119
|
"PC.Whitebox.Mellanox.SN.SN2100": " SN2100",
|
|
@@ -36,9 +36,12 @@ class HardwareLeaf(DumpableView):
|
|
|
36
36
|
raise AttributeError("HW: " + ".".join(path))
|
|
37
37
|
|
|
38
38
|
def __str__(self):
|
|
39
|
-
|
|
39
|
+
for seq in sorted(self.__true_sequences, key=len, reverse=True):
|
|
40
|
+
return ".".join(seq)
|
|
41
|
+
return ""
|
|
40
42
|
|
|
41
|
-
__repr__
|
|
43
|
+
def __repr__(self):
|
|
44
|
+
return str(" | ".join(".".join(x) for x in self.__true_sequences))
|
|
42
45
|
|
|
43
46
|
def dump(self, prefix, **kwargs): # pylint: disable=arguments-differ
|
|
44
47
|
ret = super().dump(prefix, **kwargs)
|
|
@@ -103,4 +106,6 @@ def lag_name(hw: HardwareView, nlagg: int) -> str:
|
|
|
103
106
|
return f"lagg{nlagg}"
|
|
104
107
|
if hw.Nokia:
|
|
105
108
|
return f"lagg-{nlagg}"
|
|
109
|
+
if hw.H3C:
|
|
110
|
+
return f"Bridge-Aggregation{nlagg}"
|
|
106
111
|
raise NotImplementedError(hw)
|
|
@@ -114,7 +114,7 @@ class Dumpable(abc.ABC):
|
|
|
114
114
|
pass
|
|
115
115
|
|
|
116
116
|
|
|
117
|
-
def print_as_json(data, fh=sys.stdout):
|
|
117
|
+
def print_as_json(data, fh=sys.stdout, highlight=True):
|
|
118
118
|
"""
|
|
119
119
|
В выводе dict ключи отсортированы по имени, кроме ключей в OrderedDict,
|
|
120
120
|
которые сохраняют свой оригинальный порядок
|
|
@@ -176,17 +176,19 @@ def print_as_json(data, fh=sys.stdout):
|
|
|
176
176
|
except TypeError:
|
|
177
177
|
raise TypeError("can't serialize %r to json" % o)
|
|
178
178
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
179
|
+
dump_data = UnsortableOdict.convert_odicts(data)
|
|
180
|
+
dump_kwargs = dict(cls=JsonEncoder, ensure_ascii=False)
|
|
181
|
+
|
|
182
|
+
if fh.isatty() and highlight:
|
|
182
183
|
pygments.highlight(
|
|
183
|
-
code=
|
|
184
|
+
code=json.dumps(dump_data, indent=4, sort_keys=True, **dump_kwargs) + "\n",
|
|
184
185
|
lexer=pygments.lexers.data.JsonLexer(),
|
|
185
186
|
formatter=pygments.formatters.TerminalFormatter(bg="dark"),
|
|
186
187
|
outfile=fh,
|
|
187
188
|
)
|
|
188
189
|
else:
|
|
189
|
-
|
|
190
|
+
json.dump(dump_data, fh, **dump_kwargs)
|
|
191
|
+
fh.write("\n")
|
|
190
192
|
|
|
191
193
|
|
|
192
194
|
class TextArgs:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
VENDOR_ALIASES: dict[str, str] = {}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from annet.annlib.types import Op
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def user(key, diff, **_):
|
|
5
|
+
check_for_remove = True
|
|
6
|
+
added = []
|
|
7
|
+
for add in diff[Op.ADDED]:
|
|
8
|
+
added.append((True, add["row"], None))
|
|
9
|
+
if add["row"].startswith("local-user %s password" % key[0]):
|
|
10
|
+
check_for_remove = False
|
|
11
|
+
if check_for_remove:
|
|
12
|
+
for rem in diff[Op.REMOVED]:
|
|
13
|
+
# we abe able to overwrite new command without undo
|
|
14
|
+
if rem["row"].startswith("local-user %s password" % key[0]):
|
|
15
|
+
yield (False, "undo local-user %s" % key[0], None)
|
|
16
|
+
return
|
|
17
|
+
if (rem["row"].startswith("local-user %s privilege" % key[0])
|
|
18
|
+
and not _added_contains(diff[Op.ADDED], "local-user %s privilege" % key[0])):
|
|
19
|
+
yield (False, "undo local-user %s" % key[0], None)
|
|
20
|
+
return
|
|
21
|
+
yield from added
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _added_contains(array: list[dict], lookup_string: str) -> bool:
|
|
25
|
+
for item in array:
|
|
26
|
+
if item["row"].startswith(lookup_string):
|
|
27
|
+
return True
|
|
28
|
+
return False
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import socket
|
|
2
|
+
|
|
3
|
+
from annet.annlib.types import Op
|
|
4
|
+
|
|
5
|
+
from annet.rulebook import common
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def peer(rule, key, diff, **_): # pylint: disable=unused-argument
|
|
9
|
+
"""
|
|
10
|
+
The peculiarity of peer commands is that
|
|
11
|
+
peer IP as-number N
|
|
12
|
+
is the main command, and it can only be removed with
|
|
13
|
+
undo peer IP
|
|
14
|
+
which completely deletes all settings of the peer.
|
|
15
|
+
At the same time, the as-number can also be set for a group:
|
|
16
|
+
group SPINES
|
|
17
|
+
peer SPINES as-number 13238
|
|
18
|
+
In this case, we ignore it and allow this setting to be deleted, since it does not define the group itself:
|
|
19
|
+
undo peer SPINES as-number
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
assert not diff[Op.AFFECTED], "Peer commands could not contain subcommands"
|
|
23
|
+
for action in sorted(diff[Op.REMOVED], key=lambda act: "as-number" not in act["row"].split()):
|
|
24
|
+
tokens = action["row"].split()
|
|
25
|
+
(_, addr_or_group_name, param, *__) = tokens
|
|
26
|
+
if param == "as-number":
|
|
27
|
+
if _is_ip_addr(addr_or_group_name):
|
|
28
|
+
yield (False, "undo peer {}".format(*key), None)
|
|
29
|
+
else:
|
|
30
|
+
# We can’t use common.default because the rule is defined as "peer *" and not "peer * *".
|
|
31
|
+
# Therefore, the default behavior here would be "undo peer PEERGROUP", which is not what we want.
|
|
32
|
+
yield (False, "undo peer {} as-number".format(*key), None)
|
|
33
|
+
break
|
|
34
|
+
|
|
35
|
+
if param in ["connect-interface", "ebgp-max-hop", "local-as", "substitute-as", "password", "preferred-value"]:
|
|
36
|
+
yield (False, "undo " + " ".join(tokens[:3]), None)
|
|
37
|
+
else:
|
|
38
|
+
yield (False, "undo " + action["row"], None)
|
|
39
|
+
|
|
40
|
+
for action in sorted(diff[Op.ADDED], key=lambda act: "as-number" not in act["row"]):
|
|
41
|
+
yield (True, action["row"], None)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def bfd(rule, key, diff, **_):
|
|
45
|
+
"""
|
|
46
|
+
[*vla-1x1-bgp]undo peer SPINE1 bfd min-tx-interval 500 min-rx-interval 500 detect-multiplier 4
|
|
47
|
+
│Error: Unrecognized command found at '^' position.
|
|
48
|
+
|
|
49
|
+
[*vla-1x1-bgp]undo peer SPINE1 bfd min-rx-interval
|
|
50
|
+
[~vla-1x1-bgp]undo peer SPINE1 bfd min-tx-interval
|
|
51
|
+
[*vla-1x1-bgp]undo peer SPINE1 bfd detect-multiplier
|
|
52
|
+
"""
|
|
53
|
+
if diff[Op.REMOVED]:
|
|
54
|
+
assert len(diff[Op.REMOVED]) <= 1 and len(diff[Op.ADDED]) <= 1
|
|
55
|
+
new_params = set()
|
|
56
|
+
if diff[Op.ADDED]:
|
|
57
|
+
new_params = set(_bfd_params_used(diff[Op.ADDED][0]["row"]))
|
|
58
|
+
for token in _bfd_params_used(diff[Op.REMOVED][0]["row"]):
|
|
59
|
+
if token not in new_params:
|
|
60
|
+
yield (False, rule["reverse"].format(*key) + " " + token, None)
|
|
61
|
+
diff[Op.REMOVED] = []
|
|
62
|
+
if diff[Op.ADDED]:
|
|
63
|
+
yield from common.default(rule, key, diff, **_)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _is_ip_addr(addr_or_string):
|
|
67
|
+
ret = None
|
|
68
|
+
for af in (socket.AF_INET6, socket.AF_INET):
|
|
69
|
+
try:
|
|
70
|
+
ret = socket.inet_pton(af, addr_or_string)
|
|
71
|
+
except OSError:
|
|
72
|
+
pass
|
|
73
|
+
else:
|
|
74
|
+
break
|
|
75
|
+
return bool(ret)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _bfd_params_used(row):
|
|
79
|
+
prev = None
|
|
80
|
+
for token in row.split():
|
|
81
|
+
if prev and token.isnumeric():
|
|
82
|
+
if prev and token.isnumeric():
|
|
83
|
+
yield prev
|
|
84
|
+
prev = token
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from annet.annlib.types import Op
|
|
2
|
+
|
|
3
|
+
from annet.rulebook import common
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# [NOCDEV-2180] After vrf was change we put ip config one more time
|
|
7
|
+
def binding_change(old, new, diff_pre, _pops=(Op.AFFECTED,)):
|
|
8
|
+
ret = common.default_diff(old, new, diff_pre, _pops)
|
|
9
|
+
vpn_changed = False
|
|
10
|
+
for (op, cmd, _, _) in ret:
|
|
11
|
+
if op in {Op.ADDED, Op.REMOVED}:
|
|
12
|
+
vpn_changed |= _is_vpn_cmd(cmd)
|
|
13
|
+
if vpn_changed:
|
|
14
|
+
for cmd in list(old.keys()):
|
|
15
|
+
if not _is_vpn_cmd(cmd):
|
|
16
|
+
del old[cmd]
|
|
17
|
+
ret = common.default_diff(old, new, diff_pre, _pops)
|
|
18
|
+
return ret
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _is_vpn_cmd(cmd):
|
|
22
|
+
return cmd.startswith("ip binding vpn-instance")
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import copy
|
|
2
|
+
|
|
3
|
+
from annet.annlib.types import Op
|
|
4
|
+
|
|
5
|
+
from annet.rulebook import common
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def rp_node(rule, key, diff, **_):
|
|
9
|
+
# route-policy NAME ACTION node NUM
|
|
10
|
+
(rp_name, node_id) = key
|
|
11
|
+
if diff[Op.REMOVED]:
|
|
12
|
+
if diff[Op.ADDED]:
|
|
13
|
+
sub_diff = {Op.AFFECTED: [], Op.ADDED: [], Op.REMOVED: [], Op.MOVED: [], Op.UNCHANGED: []}
|
|
14
|
+
sub_diff[Op.AFFECTED] = diff[Op.REMOVED]
|
|
15
|
+
yield from common.default(rule, key, sub_diff)
|
|
16
|
+
else:
|
|
17
|
+
yield (False, "undo route-policy %s node %s" % (rp_name, node_id), None)
|
|
18
|
+
|
|
19
|
+
if diff[Op.AFFECTED] or diff[Op.ADDED]:
|
|
20
|
+
yield from common.default(rule, key, diff)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def undo_redo(rule, key, diff, **_):
|
|
24
|
+
yield from common.undo_redo(rule, key, diff, **_)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def prefix_list(rule, key, diff, **kwargs):
|
|
28
|
+
# To determine whether the prefix list is being fully modified,
|
|
29
|
+
# the key (family, name) is defined in the h3c.rul rulebook.
|
|
30
|
+
# However, from the command’s point of view, each index represents a separate command.
|
|
31
|
+
# Therefore, we group them by index here and pass them to common.
|
|
32
|
+
diff_by_index = {}
|
|
33
|
+
for op, rows in diff.items():
|
|
34
|
+
for row in rows:
|
|
35
|
+
# prefix list format:
|
|
36
|
+
# ip ip-prefix PRFX_CT_LU_ALLOWED_ROUTES index 15 ..
|
|
37
|
+
# ip ipv6-prefix PFXS_SPECIALv6 index 20 ..
|
|
38
|
+
_ip, _family, _name, _index, index, *_ = row["row"].split()
|
|
39
|
+
if index not in diff_by_index:
|
|
40
|
+
sub_diff = {op: [] for op in diff.keys()}
|
|
41
|
+
diff_by_index[index] = sub_diff
|
|
42
|
+
diff_by_index[index][op].append(row)
|
|
43
|
+
|
|
44
|
+
family, name = key
|
|
45
|
+
if family not in {"ip", "ipv6"}:
|
|
46
|
+
raise NotImplementedError("Unknown family '%s'" % family)
|
|
47
|
+
if diff[Op.ADDED] or diff[Op.REMOVED] or diff[Op.MOVED]:
|
|
48
|
+
# Since the rule key originally doesn’t include the index,
|
|
49
|
+
# we need to add it; otherwise, the undo rule will be missing it.
|
|
50
|
+
indexed_rule = copy.deepcopy(rule)
|
|
51
|
+
indexed_rule["reverse"] = "undo ip {}-prefix {} index {}"
|
|
52
|
+
|
|
53
|
+
# The stub_index is referenced in the h3c.order rulebook
|
|
54
|
+
# to ensure that the stub is added or removed first or last in order.
|
|
55
|
+
|
|
56
|
+
stub, stub_index = "", 99999999
|
|
57
|
+
|
|
58
|
+
# If we’re only adding new commands (for example, creating entries) in the prefix list,
|
|
59
|
+
# or deleting/moving them while keeping some parts unchanged,
|
|
60
|
+
# h3c will not treat the list as being removed, and the stub rule is not needed.
|
|
61
|
+
|
|
62
|
+
if (diff[Op.REMOVED] or diff[Op.MOVED]) and not diff[Op.UNCHANGED]:
|
|
63
|
+
stub = "deny 0.0.0.0 32" if family == "ip" else "deny :: 128"
|
|
64
|
+
if stub:
|
|
65
|
+
yield (True, f"ip {family}-prefix {name} index {stub_index} {stub}", None)
|
|
66
|
+
for index, sub_diff in diff_by_index.items():
|
|
67
|
+
yield from common.undo_redo(indexed_rule, (family, name, index), sub_diff, **kwargs)
|
|
68
|
+
if stub:
|
|
69
|
+
yield (False, f"undo ip {family}-prefix {name} index {stub_index}", None)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def static(rule, key, diff, **_):
|
|
73
|
+
"""
|
|
74
|
+
To roll back a static route, we actually need to pass almost all arguments
|
|
75
|
+
except for the various "track" options.
|
|
76
|
+
At the same time, the number of arguments may vary — optional VRF, optional interface.
|
|
77
|
+
Therefore, we don’t parse the command itself; we just remove the unnecessary arguments.
|
|
78
|
+
"""
|
|
79
|
+
if diff[Op.REMOVED]:
|
|
80
|
+
param = key[0]
|
|
81
|
+
idx = param.find(" track")
|
|
82
|
+
if idx > 0:
|
|
83
|
+
key = (param[0:idx],)
|
|
84
|
+
idx = param.find(" description")
|
|
85
|
+
if idx > 0:
|
|
86
|
+
key = (param[0:idx],)
|
|
87
|
+
idx = param.find(" preference")
|
|
88
|
+
if idx > 0:
|
|
89
|
+
key = (param[0:idx],)
|
|
90
|
+
yield from common.default(rule, key, diff)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def port_queue(rule, key, diff, **_):
|
|
94
|
+
"""
|
|
95
|
+
To roll back the port-queue configuration on an interface, only a partial parameter specification is required.
|
|
96
|
+
Example of disabling/enabling:
|
|
97
|
+
interface 100GE0/1/33
|
|
98
|
+
undo port-queue af3 wfq outbound
|
|
99
|
+
port-queue af3 wfq weight 30 port-wred WRED outbound
|
|
100
|
+
|
|
101
|
+
Essentially, we need to remove all parameters between 'wfq' and 'outbound'.
|
|
102
|
+
NOC-19414
|
|
103
|
+
"""
|
|
104
|
+
if diff[Op.REMOVED]:
|
|
105
|
+
param = key[0]
|
|
106
|
+
idx = param.find("weight")
|
|
107
|
+
if idx > 0:
|
|
108
|
+
key = (param[0:idx] + "outbound",)
|
|
109
|
+
yield from common.default(rule, key, diff)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def netstream_undo(rule, key, diff, **_):
|
|
113
|
+
if diff[Op.REMOVED]:
|
|
114
|
+
# The only part we need is the last keyword: inbound or outbound
|
|
115
|
+
# Unfortunately, key is a tuple so we cast it to a list and back
|
|
116
|
+
key = list(key)
|
|
117
|
+
key[1] = key[1].split(" ")[-1]
|
|
118
|
+
key = tuple(key)
|
|
119
|
+
yield from common.default(rule, key, diff)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def snmpagent_sysinfo_version(rule, key, diff, hw, **_):
|
|
123
|
+
yield from common.default(rule, key, diff)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def vty_acl_undo(rule, key, diff, **_):
|
|
127
|
+
if diff[Op.REMOVED]:
|
|
128
|
+
chunks = key[0].split()
|
|
129
|
+
result_chunks = ["undo acl"]
|
|
130
|
+
if len(chunks) == 3 and chunks[0] == "ipv6":
|
|
131
|
+
result_chunks.append("ipv6")
|
|
132
|
+
result_chunks.append(chunks[-1])
|
|
133
|
+
yield False, " ".join(result_chunks), None
|
|
134
|
+
else:
|
|
135
|
+
yield from common.default(rule, key, diff)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def port_split(rule, key, diff, **_):
|
|
139
|
+
# pylint: disable=unused-argument
|
|
140
|
+
def _port_split(old, new, old_row, new_row):
|
|
141
|
+
removed = set(old).difference(new)
|
|
142
|
+
added = set(new).difference(old)
|
|
143
|
+
if old and new:
|
|
144
|
+
for ifname in removed:
|
|
145
|
+
yield (False, "undo port split dimension interface " + ifname, None)
|
|
146
|
+
for ifname in added:
|
|
147
|
+
yield (True, "port split dimension interface " + ifname, None)
|
|
148
|
+
elif old and not new:
|
|
149
|
+
yield (False, "undo " + old_row, None)
|
|
150
|
+
elif new and not old:
|
|
151
|
+
yield (True, new_row, None)
|
|
152
|
+
|
|
153
|
+
def _row_slot(row):
|
|
154
|
+
res = ""
|
|
155
|
+
for ch in row:
|
|
156
|
+
if ch == "/":
|
|
157
|
+
break
|
|
158
|
+
res = res + ch if ch.isnumeric() else ""
|
|
159
|
+
return int(res) if res else 0
|
|
160
|
+
|
|
161
|
+
old_by_slot = {_row_slot(x["row"]): x["row"] for x in diff[Op.REMOVED]}
|
|
162
|
+
new_by_slot = {_row_slot(x["row"]): x["row"] for x in diff[Op.ADDED]}
|
|
163
|
+
for slot in set(old_by_slot.keys()).union(new_by_slot.keys()):
|
|
164
|
+
old_row = old_by_slot[slot] if slot in old_by_slot else ""
|
|
165
|
+
new_row = new_by_slot[slot] if slot in new_by_slot else ""
|
|
166
|
+
old = _expand_portsplit(old_row)
|
|
167
|
+
new = _expand_portsplit(new_row)
|
|
168
|
+
yield from _port_split(old, new, old_row, new_row)
|
|
169
|
+
if old_by_slot or new_by_slot:
|
|
170
|
+
yield (True, "port split refresh", None)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _expand_portsplit(row):
|
|
174
|
+
expanded = []
|
|
175
|
+
row_parts = row.split()
|
|
176
|
+
for (index, part) in enumerate(row_parts):
|
|
177
|
+
if part == "to":
|
|
178
|
+
iface_base = "/".join(row_parts[index - 1].split("/")[:-1])
|
|
179
|
+
left = int(row_parts[index - 1].split("/")[-1])
|
|
180
|
+
right = int(row_parts[index + 1].split("/")[-1])
|
|
181
|
+
for i in range(left + 1, right):
|
|
182
|
+
expanded.append(iface_base + "/" + str(i))
|
|
183
|
+
else:
|
|
184
|
+
expanded.append(part)
|
|
185
|
+
return expanded
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def classifier(rule, key, diff, **_):
|
|
189
|
+
# if type changes firstly remove all if-match
|
|
190
|
+
# and then recreate classifier
|
|
191
|
+
if diff[Op.ADDED] and diff[Op.REMOVED]:
|
|
192
|
+
yield (True, diff[Op.REMOVED][0]["row"], diff[Op.REMOVED][0]["children"])
|
|
193
|
+
yield from common.default(rule, key, diff)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def undo_children(rule, key, diff, **_):
|
|
197
|
+
def removed_count(subdiff):
|
|
198
|
+
ret = 0
|
|
199
|
+
for child in subdiff["children"].values():
|
|
200
|
+
for child_diff in child["items"].values():
|
|
201
|
+
ret += len(child_diff[Op.REMOVED])
|
|
202
|
+
return ret
|
|
203
|
+
|
|
204
|
+
def common_default(op, subdiff):
|
|
205
|
+
newdiff = {Op.ADDED: [], Op.REMOVED: [], Op.MOVED: [], Op.AFFECTED: [], Op.UNCHANGED: []}
|
|
206
|
+
newdiff[op] = [subdiff]
|
|
207
|
+
yield from common.default(rule, key, newdiff)
|
|
208
|
+
|
|
209
|
+
# we should say undo because we pretend as single block
|
|
210
|
+
for subdiff in diff[Op.REMOVED]:
|
|
211
|
+
# firstly remove all group-members
|
|
212
|
+
if diff[Op.REMOVED][0]["children"]:
|
|
213
|
+
yield (True, diff[Op.REMOVED][0]["row"], diff[Op.REMOVED][0]["children"])
|
|
214
|
+
yield False, "undo " + subdiff["row"], None
|
|
215
|
+
# firstly destroy affected because inside we can have an undo
|
|
216
|
+
for subdiff in sorted(diff[Op.AFFECTED], key=removed_count, reverse=True):
|
|
217
|
+
yield from common_default(Op.AFFECTED, subdiff)
|
|
218
|
+
for subdiff in diff[Op.ADDED]:
|
|
219
|
+
yield from common_default(Op.ADDED, subdiff)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def clear_instead_undo(rule, key, diff, **_):
|
|
223
|
+
# For some configuration lines, a persistent diff occurs because the line in the config is either explicitly enabled
|
|
224
|
+
# or explicitly disabled. If it is not described in the generator (i.e., we rely on the default),
|
|
225
|
+
# then by using "clear" instead of "undo" we return the configuration to its default state.
|
|
226
|
+
# NOC-20102 @gslv 11-02-2022
|
|
227
|
+
if diff[Op.REMOVED]:
|
|
228
|
+
if diff[Op.REMOVED][0]["row"].endswith(" disable"):
|
|
229
|
+
cmd = diff[Op.REMOVED][0]["row"].replace(" disable", "")
|
|
230
|
+
yield (True, "clear " + cmd, False)
|
|
231
|
+
else:
|
|
232
|
+
yield from common.default(rule, key, diff)
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def undo_port_link_mode(rule, key, diff, **_):
|
|
236
|
+
"""
|
|
237
|
+
Сhanging port mode from bridge to route and back
|
|
238
|
+
"""
|
|
239
|
+
|
|
240
|
+
if diff[Op.REMOVED]:
|
|
241
|
+
if key[0] == "route":
|
|
242
|
+
cmd = f"{diff[Op.REMOVED][0]['row']}".replace(key[0], "bridge")
|
|
243
|
+
yield (True, cmd, False)
|
|
244
|
+
|
|
245
|
+
if key[0] == "bridge":
|
|
246
|
+
cmd = f"{diff[Op.REMOVED][0]['row']}".replace(key[0], "route")
|
|
247
|
+
yield (True, cmd, False)
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def hardware_resource_bfd(rule, key, diff, **_):
|
|
251
|
+
"""
|
|
252
|
+
Changing hardware-resource firmware-mode from software
|
|
253
|
+
to hardware and back without undo
|
|
254
|
+
"""
|
|
255
|
+
|
|
256
|
+
if diff[Op.REMOVED]:
|
|
257
|
+
if key[0] == "INT-PTP":
|
|
258
|
+
cmd = f"{diff[Op.REMOVED][0]['row']}".replace(key[0], "INT-BFD")
|
|
259
|
+
yield (True, cmd, False)
|
|
260
|
+
|
|
261
|
+
if key[0] == "INT-BFD":
|
|
262
|
+
cmd = f"{diff[Op.REMOVED][0]['row']}".replace(key[0], "INT-PTP")
|
|
263
|
+
yield (True, cmd, False)
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def change_user_password(rule, key, diff, **_):
|
|
267
|
+
"""
|
|
268
|
+
If we have to change password hash
|
|
269
|
+
then we just push new hash to configuration
|
|
270
|
+
without undo
|
|
271
|
+
"""
|
|
272
|
+
|
|
273
|
+
if diff[Op.REMOVED]:
|
|
274
|
+
if key[0] == "route":
|
|
275
|
+
cmd = f"{diff[Op.REMOVED][0]['row']}".replace(key[0], "bridge")
|
|
276
|
+
yield (True, cmd, False)
|
|
277
|
+
|
|
278
|
+
if key[0] == "bridge":
|
|
279
|
+
cmd = f"{diff[Op.REMOVED][0]['row']}".replace(key[0], "route")
|
|
280
|
+
yield (True, cmd, False)
|