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.

Files changed (194) hide show
  1. {annet-1.0.3/annet.egg-info → annet-1.0.4}/PKG-INFO +1 -1
  2. {annet-1.0.3 → annet-1.0.4}/README.md +5 -0
  3. {annet-1.0.3 → annet-1.0.4}/annet/annlib/netdev/devdb/data/devdb.json +3 -0
  4. {annet-1.0.3 → annet-1.0.4}/annet/bgp_models.py +83 -1
  5. {annet-1.0.3 → annet-1.0.4}/annet/mesh/device_models.py +12 -0
  6. {annet-1.0.3 → annet-1.0.4}/annet/mesh/models_converter.py +3 -1
  7. annet-1.0.4/annet/rulebook/arista/aaa.py +63 -0
  8. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/arista.rul +1 -1
  9. {annet-1.0.3 → annet-1.0.4/annet.egg-info}/PKG-INFO +1 -1
  10. {annet-1.0.3 → annet-1.0.4}/annet.egg-info/SOURCES.txt +1 -0
  11. {annet-1.0.3 → annet-1.0.4}/AUTHORS +0 -0
  12. {annet-1.0.3 → annet-1.0.4}/LICENSE +0 -0
  13. {annet-1.0.3 → annet-1.0.4}/MANIFEST.in +0 -0
  14. {annet-1.0.3 → annet-1.0.4}/annet/__init__.py +0 -0
  15. {annet-1.0.3 → annet-1.0.4}/annet/adapters/__init__.py +0 -0
  16. {annet-1.0.3 → annet-1.0.4}/annet/adapters/fetchers/__init__.py +0 -0
  17. {annet-1.0.3 → annet-1.0.4}/annet/adapters/fetchers/stub/__init__.py +0 -0
  18. {annet-1.0.3 → annet-1.0.4}/annet/adapters/fetchers/stub/fetcher.py +0 -0
  19. {annet-1.0.3 → annet-1.0.4}/annet/adapters/file/__init__.py +0 -0
  20. {annet-1.0.3 → annet-1.0.4}/annet/adapters/file/provider.py +0 -0
  21. {annet-1.0.3 → annet-1.0.4}/annet/adapters/netbox/__init__.py +0 -0
  22. {annet-1.0.3 → annet-1.0.4}/annet/adapters/netbox/common/__init__.py +0 -0
  23. {annet-1.0.3 → annet-1.0.4}/annet/adapters/netbox/common/client.py +0 -0
  24. {annet-1.0.3 → annet-1.0.4}/annet/adapters/netbox/common/manufacturer.py +0 -0
  25. {annet-1.0.3 → annet-1.0.4}/annet/adapters/netbox/common/models.py +0 -0
  26. {annet-1.0.3 → annet-1.0.4}/annet/adapters/netbox/common/query.py +0 -0
  27. {annet-1.0.3 → annet-1.0.4}/annet/adapters/netbox/common/status_client.py +0 -0
  28. {annet-1.0.3 → annet-1.0.4}/annet/adapters/netbox/common/storage_opts.py +0 -0
  29. {annet-1.0.3 → annet-1.0.4}/annet/adapters/netbox/provider.py +0 -0
  30. {annet-1.0.3 → annet-1.0.4}/annet/adapters/netbox/v24/__init__.py +0 -0
  31. {annet-1.0.3 → annet-1.0.4}/annet/adapters/netbox/v24/storage.py +0 -0
  32. {annet-1.0.3 → annet-1.0.4}/annet/adapters/netbox/v37/__init__.py +0 -0
  33. {annet-1.0.3 → annet-1.0.4}/annet/adapters/netbox/v37/storage.py +0 -0
  34. {annet-1.0.3 → annet-1.0.4}/annet/annet.py +0 -0
  35. {annet-1.0.3 → annet-1.0.4}/annet/annlib/__init__.py +0 -0
  36. {annet-1.0.3 → annet-1.0.4}/annet/annlib/command.py +0 -0
  37. {annet-1.0.3 → annet-1.0.4}/annet/annlib/diff.py +0 -0
  38. {annet-1.0.3 → annet-1.0.4}/annet/annlib/errors.py +0 -0
  39. {annet-1.0.3 → annet-1.0.4}/annet/annlib/filter_acl.py +0 -0
  40. {annet-1.0.3 → annet-1.0.4}/annet/annlib/jsontools.py +0 -0
  41. {annet-1.0.3 → annet-1.0.4}/annet/annlib/lib.py +0 -0
  42. {annet-1.0.3 → annet-1.0.4}/annet/annlib/netdev/__init__.py +0 -0
  43. {annet-1.0.3 → annet-1.0.4}/annet/annlib/netdev/db.py +0 -0
  44. {annet-1.0.3 → annet-1.0.4}/annet/annlib/netdev/devdb/__init__.py +0 -0
  45. {annet-1.0.3 → annet-1.0.4}/annet/annlib/netdev/views/__init__.py +0 -0
  46. {annet-1.0.3 → annet-1.0.4}/annet/annlib/netdev/views/dump.py +0 -0
  47. {annet-1.0.3 → annet-1.0.4}/annet/annlib/netdev/views/hardware.py +0 -0
  48. {annet-1.0.3 → annet-1.0.4}/annet/annlib/output.py +0 -0
  49. {annet-1.0.3 → annet-1.0.4}/annet/annlib/patching.py +0 -0
  50. {annet-1.0.3 → annet-1.0.4}/annet/annlib/rbparser/__init__.py +0 -0
  51. {annet-1.0.3 → annet-1.0.4}/annet/annlib/rbparser/acl.py +0 -0
  52. {annet-1.0.3 → annet-1.0.4}/annet/annlib/rbparser/deploying.py +0 -0
  53. {annet-1.0.3 → annet-1.0.4}/annet/annlib/rbparser/ordering.py +0 -0
  54. {annet-1.0.3 → annet-1.0.4}/annet/annlib/rbparser/platform.py +0 -0
  55. {annet-1.0.3 → annet-1.0.4}/annet/annlib/rbparser/syntax.py +0 -0
  56. {annet-1.0.3 → annet-1.0.4}/annet/annlib/rulebook/__init__.py +0 -0
  57. {annet-1.0.3 → annet-1.0.4}/annet/annlib/rulebook/common.py +0 -0
  58. {annet-1.0.3 → annet-1.0.4}/annet/annlib/tabparser.py +0 -0
  59. {annet-1.0.3 → annet-1.0.4}/annet/annlib/types.py +0 -0
  60. {annet-1.0.3 → annet-1.0.4}/annet/api/__init__.py +0 -0
  61. {annet-1.0.3 → annet-1.0.4}/annet/argparse.py +0 -0
  62. {annet-1.0.3 → annet-1.0.4}/annet/cli.py +0 -0
  63. {annet-1.0.3 → annet-1.0.4}/annet/cli_args.py +0 -0
  64. {annet-1.0.3 → annet-1.0.4}/annet/configs/context.yml +0 -0
  65. {annet-1.0.3 → annet-1.0.4}/annet/configs/logging.yaml +0 -0
  66. {annet-1.0.3 → annet-1.0.4}/annet/connectors.py +0 -0
  67. {annet-1.0.3 → annet-1.0.4}/annet/deploy.py +0 -0
  68. {annet-1.0.3 → annet-1.0.4}/annet/deploy_ui.py +0 -0
  69. {annet-1.0.3 → annet-1.0.4}/annet/diff.py +0 -0
  70. {annet-1.0.3 → annet-1.0.4}/annet/executor.py +0 -0
  71. {annet-1.0.3 → annet-1.0.4}/annet/filtering.py +0 -0
  72. {annet-1.0.3 → annet-1.0.4}/annet/gen.py +0 -0
  73. {annet-1.0.3 → annet-1.0.4}/annet/generators/__init__.py +0 -0
  74. {annet-1.0.3 → annet-1.0.4}/annet/generators/base.py +0 -0
  75. {annet-1.0.3 → annet-1.0.4}/annet/generators/common/__init__.py +0 -0
  76. {annet-1.0.3 → annet-1.0.4}/annet/generators/common/initial.py +0 -0
  77. {annet-1.0.3 → annet-1.0.4}/annet/generators/entire.py +0 -0
  78. {annet-1.0.3 → annet-1.0.4}/annet/generators/exceptions.py +0 -0
  79. {annet-1.0.3 → annet-1.0.4}/annet/generators/jsonfragment.py +0 -0
  80. {annet-1.0.3 → annet-1.0.4}/annet/generators/partial.py +0 -0
  81. {annet-1.0.3 → annet-1.0.4}/annet/generators/perf.py +0 -0
  82. {annet-1.0.3 → annet-1.0.4}/annet/generators/ref.py +0 -0
  83. {annet-1.0.3 → annet-1.0.4}/annet/generators/result.py +0 -0
  84. {annet-1.0.3 → annet-1.0.4}/annet/hardware.py +0 -0
  85. {annet-1.0.3 → annet-1.0.4}/annet/implicit.py +0 -0
  86. {annet-1.0.3 → annet-1.0.4}/annet/lib.py +0 -0
  87. {annet-1.0.3 → annet-1.0.4}/annet/mesh/__init__.py +0 -0
  88. {annet-1.0.3 → annet-1.0.4}/annet/mesh/basemodel.py +0 -0
  89. {annet-1.0.3 → annet-1.0.4}/annet/mesh/executor.py +0 -0
  90. {annet-1.0.3 → annet-1.0.4}/annet/mesh/match_args.py +0 -0
  91. {annet-1.0.3 → annet-1.0.4}/annet/mesh/peer_models.py +0 -0
  92. {annet-1.0.3 → annet-1.0.4}/annet/mesh/port_processor.py +0 -0
  93. {annet-1.0.3 → annet-1.0.4}/annet/mesh/registry.py +0 -0
  94. {annet-1.0.3 → annet-1.0.4}/annet/output.py +0 -0
  95. {annet-1.0.3 → annet-1.0.4}/annet/parallel.py +0 -0
  96. {annet-1.0.3 → annet-1.0.4}/annet/patching.py +0 -0
  97. {annet-1.0.3 → annet-1.0.4}/annet/reference.py +0 -0
  98. {annet-1.0.3 → annet-1.0.4}/annet/rpl/__init__.py +0 -0
  99. {annet-1.0.3 → annet-1.0.4}/annet/rpl/action.py +0 -0
  100. {annet-1.0.3 → annet-1.0.4}/annet/rpl/condition.py +0 -0
  101. {annet-1.0.3 → annet-1.0.4}/annet/rpl/match_builder.py +0 -0
  102. {annet-1.0.3 → annet-1.0.4}/annet/rpl/policy.py +0 -0
  103. {annet-1.0.3 → annet-1.0.4}/annet/rpl/result.py +0 -0
  104. {annet-1.0.3 → annet-1.0.4}/annet/rpl/routemap.py +0 -0
  105. {annet-1.0.3 → annet-1.0.4}/annet/rpl/statement_builder.py +0 -0
  106. {annet-1.0.3 → annet-1.0.4}/annet/rpl_generators/__init__.py +0 -0
  107. {annet-1.0.3 → annet-1.0.4}/annet/rpl_generators/aspath.py +0 -0
  108. {annet-1.0.3 → annet-1.0.4}/annet/rpl_generators/community.py +0 -0
  109. {annet-1.0.3 → annet-1.0.4}/annet/rpl_generators/cumulus_frr.py +0 -0
  110. {annet-1.0.3 → annet-1.0.4}/annet/rpl_generators/entities.py +0 -0
  111. {annet-1.0.3 → annet-1.0.4}/annet/rpl_generators/execute.py +0 -0
  112. {annet-1.0.3 → annet-1.0.4}/annet/rpl_generators/policy.py +0 -0
  113. {annet-1.0.3 → annet-1.0.4}/annet/rpl_generators/prefix_lists.py +0 -0
  114. {annet-1.0.3 → annet-1.0.4}/annet/rpl_generators/rd.py +0 -0
  115. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/__init__.py +0 -0
  116. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/arista/__init__.py +0 -0
  117. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/arista/iface.py +0 -0
  118. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/aruba/__init__.py +0 -0
  119. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/aruba/ap_env.py +0 -0
  120. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/aruba/misc.py +0 -0
  121. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/b4com/__init__.py +0 -0
  122. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/b4com/file.py +0 -0
  123. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/b4com/iface.py +0 -0
  124. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/cisco/__init__.py +0 -0
  125. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/cisco/iface.py +0 -0
  126. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/cisco/misc.py +0 -0
  127. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/cisco/vlandb.py +0 -0
  128. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/common.py +0 -0
  129. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/deploying.py +0 -0
  130. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/huawei/__init__.py +0 -0
  131. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/huawei/aaa.py +0 -0
  132. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/huawei/bgp.py +0 -0
  133. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/huawei/iface.py +0 -0
  134. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/huawei/misc.py +0 -0
  135. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/huawei/vlandb.py +0 -0
  136. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/juniper/__init__.py +0 -0
  137. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/nexus/__init__.py +0 -0
  138. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/nexus/iface.py +0 -0
  139. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/patching.py +0 -0
  140. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/ribbon/__init__.py +0 -0
  141. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/routeros/__init__.py +0 -0
  142. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/routeros/file.py +0 -0
  143. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/arista.deploy +0 -0
  144. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/arista.order +0 -0
  145. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/aruba.deploy +0 -0
  146. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/aruba.order +0 -0
  147. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/aruba.rul +0 -0
  148. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/b4com.deploy +0 -0
  149. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/b4com.order +0 -0
  150. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/b4com.rul +0 -0
  151. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/cisco.deploy +0 -0
  152. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/cisco.order +0 -0
  153. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/cisco.rul +0 -0
  154. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/huawei.deploy +0 -0
  155. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/huawei.order +0 -0
  156. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/huawei.rul +0 -0
  157. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/juniper.rul +0 -0
  158. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/nexus.deploy +0 -0
  159. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/nexus.order +0 -0
  160. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/nexus.rul +0 -0
  161. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/nokia.rul +0 -0
  162. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/optixtrans.deploy +0 -0
  163. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/optixtrans.order +0 -0
  164. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/optixtrans.rul +0 -0
  165. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/pc.deploy +0 -0
  166. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/pc.order +0 -0
  167. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/pc.rul +0 -0
  168. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/ribbon.deploy +0 -0
  169. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/ribbon.rul +0 -0
  170. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/routeros.order +0 -0
  171. {annet-1.0.3 → annet-1.0.4}/annet/rulebook/texts/routeros.rul +0 -0
  172. {annet-1.0.3 → annet-1.0.4}/annet/storage.py +0 -0
  173. {annet-1.0.3 → annet-1.0.4}/annet/tabparser.py +0 -0
  174. {annet-1.0.3 → annet-1.0.4}/annet/text_term_format.py +0 -0
  175. {annet-1.0.3 → annet-1.0.4}/annet/tracing.py +0 -0
  176. {annet-1.0.3 → annet-1.0.4}/annet/types.py +0 -0
  177. {annet-1.0.3 → annet-1.0.4}/annet.egg-info/dependency_links.txt +0 -0
  178. {annet-1.0.3 → annet-1.0.4}/annet.egg-info/entry_points.txt +0 -0
  179. {annet-1.0.3 → annet-1.0.4}/annet.egg-info/requires.txt +0 -0
  180. {annet-1.0.3 → annet-1.0.4}/annet.egg-info/top_level.txt +0 -0
  181. {annet-1.0.3 → annet-1.0.4}/annet_generators/__init__.py +0 -0
  182. {annet-1.0.3 → annet-1.0.4}/annet_generators/example/__init__.py +0 -0
  183. {annet-1.0.3 → annet-1.0.4}/annet_generators/example/lldp.py +0 -0
  184. {annet-1.0.3 → annet-1.0.4}/annet_generators/mesh_example/__init__.py +0 -0
  185. {annet-1.0.3 → annet-1.0.4}/annet_generators/mesh_example/bgp.py +0 -0
  186. {annet-1.0.3 → annet-1.0.4}/annet_generators/mesh_example/mesh_logic.py +0 -0
  187. {annet-1.0.3 → annet-1.0.4}/annet_generators/rpl_example/__init__.py +0 -0
  188. {annet-1.0.3 → annet-1.0.4}/annet_generators/rpl_example/generator.py +0 -0
  189. {annet-1.0.3 → annet-1.0.4}/annet_generators/rpl_example/items.py +0 -0
  190. {annet-1.0.3 → annet-1.0.4}/annet_generators/rpl_example/mesh.py +0 -0
  191. {annet-1.0.3 → annet-1.0.4}/annet_generators/rpl_example/route_policy.py +0 -0
  192. {annet-1.0.3 → annet-1.0.4}/requirements.txt +0 -0
  193. {annet-1.0.3 → annet-1.0.4}/setup.cfg +0 -0
  194. {annet-1.0.3 → annet-1.0.4}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: annet
3
- Version: 1.0.3
3
+ Version: 1.0.4
4
4
  Summary: annet
5
5
  Home-page: https://github.com/annetutil/annet
6
6
  License: MIT
@@ -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)
@@ -5,7 +5,7 @@ qos profile *
5
5
  ~ %global
6
6
 
7
7
  username * ssh-key
8
- username *
8
+ username * %logic=arista.aaa.user
9
9
 
10
10
  aaa group ~
11
11
  ~ %global
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: annet
3
- Version: 1.0.3
3
+ Version: 1.0.4
4
4
  Summary: annet
5
5
  Home-page: https://github.com/annetutil/annet
6
6
  License: MIT
@@ -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