annet 0.16.34__tar.gz → 0.16.36__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 (193) hide show
  1. {annet-0.16.34/annet.egg-info → annet-0.16.36}/PKG-INFO +1 -1
  2. {annet-0.16.34 → annet-0.16.36}/annet/adapters/netbox/common/manufacturer.py +1 -1
  3. {annet-0.16.34 → annet-0.16.36}/annet/adapters/netbox/common/models.py +2 -1
  4. annet-0.16.36/annet/adapters/netbox/common/query.py +61 -0
  5. {annet-0.16.34 → annet-0.16.36}/annet/adapters/netbox/v37/storage.py +81 -42
  6. {annet-0.16.34 → annet-0.16.36}/annet/annlib/netdev/devdb/data/devdb.json +3 -1
  7. {annet-0.16.34 → annet-0.16.36}/annet/bgp_models.py +5 -2
  8. {annet-0.16.34 → annet-0.16.36}/annet/rpl_generators/prefix_lists.py +1 -2
  9. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/texts/arista.order +1 -0
  10. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/texts/huawei.deploy +1 -1
  11. {annet-0.16.34 → annet-0.16.36/annet.egg-info}/PKG-INFO +1 -1
  12. annet-0.16.34/annet/adapters/netbox/common/query.py +0 -26
  13. {annet-0.16.34 → annet-0.16.36}/AUTHORS +0 -0
  14. {annet-0.16.34 → annet-0.16.36}/LICENSE +0 -0
  15. {annet-0.16.34 → annet-0.16.36}/MANIFEST.in +0 -0
  16. {annet-0.16.34 → annet-0.16.36}/README.md +0 -0
  17. {annet-0.16.34 → annet-0.16.36}/annet/__init__.py +0 -0
  18. {annet-0.16.34 → annet-0.16.36}/annet/adapters/__init__.py +0 -0
  19. {annet-0.16.34 → annet-0.16.36}/annet/adapters/fetchers/__init__.py +0 -0
  20. {annet-0.16.34 → annet-0.16.36}/annet/adapters/fetchers/stub/__init__.py +0 -0
  21. {annet-0.16.34 → annet-0.16.36}/annet/adapters/fetchers/stub/fetcher.py +0 -0
  22. {annet-0.16.34 → annet-0.16.36}/annet/adapters/file/__init__.py +0 -0
  23. {annet-0.16.34 → annet-0.16.36}/annet/adapters/file/provider.py +0 -0
  24. {annet-0.16.34 → annet-0.16.36}/annet/adapters/netbox/__init__.py +0 -0
  25. {annet-0.16.34 → annet-0.16.36}/annet/adapters/netbox/common/__init__.py +0 -0
  26. {annet-0.16.34 → annet-0.16.36}/annet/adapters/netbox/common/client.py +0 -0
  27. {annet-0.16.34 → annet-0.16.36}/annet/adapters/netbox/common/status_client.py +0 -0
  28. {annet-0.16.34 → annet-0.16.36}/annet/adapters/netbox/common/storage_opts.py +0 -0
  29. {annet-0.16.34 → annet-0.16.36}/annet/adapters/netbox/provider.py +0 -0
  30. {annet-0.16.34 → annet-0.16.36}/annet/adapters/netbox/v24/__init__.py +0 -0
  31. {annet-0.16.34 → annet-0.16.36}/annet/adapters/netbox/v24/storage.py +0 -0
  32. {annet-0.16.34 → annet-0.16.36}/annet/adapters/netbox/v37/__init__.py +0 -0
  33. {annet-0.16.34 → annet-0.16.36}/annet/annet.py +0 -0
  34. {annet-0.16.34 → annet-0.16.36}/annet/annlib/__init__.py +0 -0
  35. {annet-0.16.34 → annet-0.16.36}/annet/annlib/command.py +0 -0
  36. {annet-0.16.34 → annet-0.16.36}/annet/annlib/diff.py +0 -0
  37. {annet-0.16.34 → annet-0.16.36}/annet/annlib/errors.py +0 -0
  38. {annet-0.16.34 → annet-0.16.36}/annet/annlib/filter_acl.py +0 -0
  39. {annet-0.16.34 → annet-0.16.36}/annet/annlib/jsontools.py +0 -0
  40. {annet-0.16.34 → annet-0.16.36}/annet/annlib/lib.py +0 -0
  41. {annet-0.16.34 → annet-0.16.36}/annet/annlib/netdev/__init__.py +0 -0
  42. {annet-0.16.34 → annet-0.16.36}/annet/annlib/netdev/db.py +0 -0
  43. {annet-0.16.34 → annet-0.16.36}/annet/annlib/netdev/devdb/__init__.py +0 -0
  44. {annet-0.16.34 → annet-0.16.36}/annet/annlib/netdev/views/__init__.py +0 -0
  45. {annet-0.16.34 → annet-0.16.36}/annet/annlib/netdev/views/dump.py +0 -0
  46. {annet-0.16.34 → annet-0.16.36}/annet/annlib/netdev/views/hardware.py +0 -0
  47. {annet-0.16.34 → annet-0.16.36}/annet/annlib/output.py +0 -0
  48. {annet-0.16.34 → annet-0.16.36}/annet/annlib/patching.py +0 -0
  49. {annet-0.16.34 → annet-0.16.36}/annet/annlib/rbparser/__init__.py +0 -0
  50. {annet-0.16.34 → annet-0.16.36}/annet/annlib/rbparser/acl.py +0 -0
  51. {annet-0.16.34 → annet-0.16.36}/annet/annlib/rbparser/deploying.py +0 -0
  52. {annet-0.16.34 → annet-0.16.36}/annet/annlib/rbparser/ordering.py +0 -0
  53. {annet-0.16.34 → annet-0.16.36}/annet/annlib/rbparser/platform.py +0 -0
  54. {annet-0.16.34 → annet-0.16.36}/annet/annlib/rbparser/syntax.py +0 -0
  55. {annet-0.16.34 → annet-0.16.36}/annet/annlib/rulebook/__init__.py +0 -0
  56. {annet-0.16.34 → annet-0.16.36}/annet/annlib/rulebook/common.py +0 -0
  57. {annet-0.16.34 → annet-0.16.36}/annet/annlib/tabparser.py +0 -0
  58. {annet-0.16.34 → annet-0.16.36}/annet/annlib/types.py +0 -0
  59. {annet-0.16.34 → annet-0.16.36}/annet/api/__init__.py +0 -0
  60. {annet-0.16.34 → annet-0.16.36}/annet/argparse.py +0 -0
  61. {annet-0.16.34 → annet-0.16.36}/annet/cli.py +0 -0
  62. {annet-0.16.34 → annet-0.16.36}/annet/cli_args.py +0 -0
  63. {annet-0.16.34 → annet-0.16.36}/annet/configs/context.yml +0 -0
  64. {annet-0.16.34 → annet-0.16.36}/annet/configs/logging.yaml +0 -0
  65. {annet-0.16.34 → annet-0.16.36}/annet/connectors.py +0 -0
  66. {annet-0.16.34 → annet-0.16.36}/annet/deploy.py +0 -0
  67. {annet-0.16.34 → annet-0.16.36}/annet/diff.py +0 -0
  68. {annet-0.16.34 → annet-0.16.36}/annet/executor.py +0 -0
  69. {annet-0.16.34 → annet-0.16.36}/annet/filtering.py +0 -0
  70. {annet-0.16.34 → annet-0.16.36}/annet/gen.py +0 -0
  71. {annet-0.16.34 → annet-0.16.36}/annet/generators/__init__.py +0 -0
  72. {annet-0.16.34 → annet-0.16.36}/annet/generators/base.py +0 -0
  73. {annet-0.16.34 → annet-0.16.36}/annet/generators/common/__init__.py +0 -0
  74. {annet-0.16.34 → annet-0.16.36}/annet/generators/common/initial.py +0 -0
  75. {annet-0.16.34 → annet-0.16.36}/annet/generators/entire.py +0 -0
  76. {annet-0.16.34 → annet-0.16.36}/annet/generators/exceptions.py +0 -0
  77. {annet-0.16.34 → annet-0.16.36}/annet/generators/jsonfragment.py +0 -0
  78. {annet-0.16.34 → annet-0.16.36}/annet/generators/partial.py +0 -0
  79. {annet-0.16.34 → annet-0.16.36}/annet/generators/perf.py +0 -0
  80. {annet-0.16.34 → annet-0.16.36}/annet/generators/ref.py +0 -0
  81. {annet-0.16.34 → annet-0.16.36}/annet/generators/result.py +0 -0
  82. {annet-0.16.34 → annet-0.16.36}/annet/hardware.py +0 -0
  83. {annet-0.16.34 → annet-0.16.36}/annet/implicit.py +0 -0
  84. {annet-0.16.34 → annet-0.16.36}/annet/lib.py +0 -0
  85. {annet-0.16.34 → annet-0.16.36}/annet/mesh/__init__.py +0 -0
  86. {annet-0.16.34 → annet-0.16.36}/annet/mesh/basemodel.py +0 -0
  87. {annet-0.16.34 → annet-0.16.36}/annet/mesh/device_models.py +0 -0
  88. {annet-0.16.34 → annet-0.16.36}/annet/mesh/executor.py +0 -0
  89. {annet-0.16.34 → annet-0.16.36}/annet/mesh/match_args.py +0 -0
  90. {annet-0.16.34 → annet-0.16.36}/annet/mesh/models_converter.py +0 -0
  91. {annet-0.16.34 → annet-0.16.36}/annet/mesh/peer_models.py +0 -0
  92. {annet-0.16.34 → annet-0.16.36}/annet/mesh/port_processor.py +0 -0
  93. {annet-0.16.34 → annet-0.16.36}/annet/mesh/registry.py +0 -0
  94. {annet-0.16.34 → annet-0.16.36}/annet/output.py +0 -0
  95. {annet-0.16.34 → annet-0.16.36}/annet/parallel.py +0 -0
  96. {annet-0.16.34 → annet-0.16.36}/annet/patching.py +0 -0
  97. {annet-0.16.34 → annet-0.16.36}/annet/reference.py +0 -0
  98. {annet-0.16.34 → annet-0.16.36}/annet/rpl/__init__.py +0 -0
  99. {annet-0.16.34 → annet-0.16.36}/annet/rpl/action.py +0 -0
  100. {annet-0.16.34 → annet-0.16.36}/annet/rpl/condition.py +0 -0
  101. {annet-0.16.34 → annet-0.16.36}/annet/rpl/match_builder.py +0 -0
  102. {annet-0.16.34 → annet-0.16.36}/annet/rpl/policy.py +0 -0
  103. {annet-0.16.34 → annet-0.16.36}/annet/rpl/result.py +0 -0
  104. {annet-0.16.34 → annet-0.16.36}/annet/rpl/routemap.py +0 -0
  105. {annet-0.16.34 → annet-0.16.36}/annet/rpl/statement_builder.py +0 -0
  106. {annet-0.16.34 → annet-0.16.36}/annet/rpl_generators/__init__.py +0 -0
  107. {annet-0.16.34 → annet-0.16.36}/annet/rpl_generators/aspath.py +0 -0
  108. {annet-0.16.34 → annet-0.16.36}/annet/rpl_generators/community.py +0 -0
  109. {annet-0.16.34 → annet-0.16.36}/annet/rpl_generators/cumulus_frr.py +0 -0
  110. {annet-0.16.34 → annet-0.16.36}/annet/rpl_generators/entities.py +0 -0
  111. {annet-0.16.34 → annet-0.16.36}/annet/rpl_generators/execute.py +0 -0
  112. {annet-0.16.34 → annet-0.16.36}/annet/rpl_generators/policy.py +0 -0
  113. {annet-0.16.34 → annet-0.16.36}/annet/rpl_generators/rd.py +0 -0
  114. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/__init__.py +0 -0
  115. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/arista/__init__.py +0 -0
  116. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/arista/iface.py +0 -0
  117. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/aruba/__init__.py +0 -0
  118. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/aruba/ap_env.py +0 -0
  119. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/aruba/misc.py +0 -0
  120. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/b4com/__init__.py +0 -0
  121. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/b4com/file.py +0 -0
  122. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/b4com/iface.py +0 -0
  123. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/cisco/__init__.py +0 -0
  124. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/cisco/iface.py +0 -0
  125. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/cisco/misc.py +0 -0
  126. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/cisco/vlandb.py +0 -0
  127. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/common.py +0 -0
  128. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/deploying.py +0 -0
  129. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/huawei/__init__.py +0 -0
  130. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/huawei/aaa.py +0 -0
  131. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/huawei/bgp.py +0 -0
  132. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/huawei/iface.py +0 -0
  133. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/huawei/misc.py +0 -0
  134. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/huawei/vlandb.py +0 -0
  135. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/juniper/__init__.py +0 -0
  136. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/nexus/__init__.py +0 -0
  137. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/nexus/iface.py +0 -0
  138. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/patching.py +0 -0
  139. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/ribbon/__init__.py +0 -0
  140. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/routeros/__init__.py +0 -0
  141. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/routeros/file.py +0 -0
  142. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/texts/arista.deploy +0 -0
  143. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/texts/arista.rul +0 -0
  144. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/texts/aruba.deploy +0 -0
  145. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/texts/aruba.order +0 -0
  146. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/texts/aruba.rul +0 -0
  147. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/texts/b4com.deploy +0 -0
  148. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/texts/b4com.order +0 -0
  149. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/texts/b4com.rul +0 -0
  150. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/texts/cisco.deploy +0 -0
  151. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/texts/cisco.order +0 -0
  152. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/texts/cisco.rul +0 -0
  153. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/texts/huawei.order +0 -0
  154. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/texts/huawei.rul +0 -0
  155. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/texts/juniper.rul +0 -0
  156. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/texts/nexus.deploy +0 -0
  157. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/texts/nexus.order +0 -0
  158. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/texts/nexus.rul +0 -0
  159. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/texts/nokia.rul +0 -0
  160. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/texts/optixtrans.deploy +0 -0
  161. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/texts/optixtrans.order +0 -0
  162. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/texts/optixtrans.rul +0 -0
  163. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/texts/pc.deploy +0 -0
  164. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/texts/pc.order +0 -0
  165. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/texts/pc.rul +0 -0
  166. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/texts/ribbon.deploy +0 -0
  167. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/texts/ribbon.rul +0 -0
  168. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/texts/routeros.order +0 -0
  169. {annet-0.16.34 → annet-0.16.36}/annet/rulebook/texts/routeros.rul +0 -0
  170. {annet-0.16.34 → annet-0.16.36}/annet/storage.py +0 -0
  171. {annet-0.16.34 → annet-0.16.36}/annet/tabparser.py +0 -0
  172. {annet-0.16.34 → annet-0.16.36}/annet/text_term_format.py +0 -0
  173. {annet-0.16.34 → annet-0.16.36}/annet/tracing.py +0 -0
  174. {annet-0.16.34 → annet-0.16.36}/annet/types.py +0 -0
  175. {annet-0.16.34 → annet-0.16.36}/annet.egg-info/SOURCES.txt +0 -0
  176. {annet-0.16.34 → annet-0.16.36}/annet.egg-info/dependency_links.txt +0 -0
  177. {annet-0.16.34 → annet-0.16.36}/annet.egg-info/entry_points.txt +0 -0
  178. {annet-0.16.34 → annet-0.16.36}/annet.egg-info/requires.txt +0 -0
  179. {annet-0.16.34 → annet-0.16.36}/annet.egg-info/top_level.txt +0 -0
  180. {annet-0.16.34 → annet-0.16.36}/annet_generators/__init__.py +0 -0
  181. {annet-0.16.34 → annet-0.16.36}/annet_generators/example/__init__.py +0 -0
  182. {annet-0.16.34 → annet-0.16.36}/annet_generators/example/lldp.py +0 -0
  183. {annet-0.16.34 → annet-0.16.36}/annet_generators/mesh_example/__init__.py +0 -0
  184. {annet-0.16.34 → annet-0.16.36}/annet_generators/mesh_example/bgp.py +0 -0
  185. {annet-0.16.34 → annet-0.16.36}/annet_generators/mesh_example/mesh_logic.py +0 -0
  186. {annet-0.16.34 → annet-0.16.36}/annet_generators/rpl_example/__init__.py +0 -0
  187. {annet-0.16.34 → annet-0.16.36}/annet_generators/rpl_example/generator.py +0 -0
  188. {annet-0.16.34 → annet-0.16.36}/annet_generators/rpl_example/items.py +0 -0
  189. {annet-0.16.34 → annet-0.16.36}/annet_generators/rpl_example/mesh.py +0 -0
  190. {annet-0.16.34 → annet-0.16.36}/annet_generators/rpl_example/route_policy.py +0 -0
  191. {annet-0.16.34 → annet-0.16.36}/requirements.txt +0 -0
  192. {annet-0.16.34 → annet-0.16.36}/setup.cfg +0 -0
  193. {annet-0.16.34 → annet-0.16.36}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: annet
3
- Version: 0.16.34
3
+ Version: 0.16.36
4
4
  Summary: annet
5
5
  Home-page: https://github.com/annetutil/annet
6
6
  License: MIT
@@ -54,7 +54,7 @@ def get_breed(manufacturer: str, model: str):
54
54
  return "bcom-os"
55
55
  elif manufacturer == "MikroTik":
56
56
  return "routeros"
57
- elif manufacturer == "Moxa":
57
+ elif manufacturer in ("Moxa", "Nebius"):
58
58
  return "moxa"
59
59
  elif manufacturer == "PC":
60
60
  return "pc"
@@ -214,7 +214,8 @@ class NetboxDevice(Entity):
214
214
  return type(self) is type(other) and self.url == other.url
215
215
 
216
216
  def is_pc(self) -> bool:
217
- return self.device_type.manufacturer.name in ("Mellanox", "NVIDIA", "Moxa") or self.breed == "pc"
217
+ custom_breed_pc = ("Mellanox", "NVIDIA", "Moxa", "Nebius")
218
+ return self.device_type.manufacturer.name in custom_breed_pc or self.breed == "pc"
218
219
 
219
220
  def _make_interface(self, name: str, type: InterfaceType) -> Interface:
220
221
  return Interface(
@@ -0,0 +1,61 @@
1
+ from collections import defaultdict
2
+ from dataclasses import dataclass
3
+ from typing import cast, List, Union, Iterable, Optional, TypedDict
4
+
5
+ from annet.storage import Query
6
+
7
+ FIELD_VALUE_SEPARATOR = ":"
8
+ ALLOWED_GLOB_GROUPS = ["site", "tag", "role"]
9
+
10
+
11
+ class Filter(TypedDict, total=False):
12
+ site: list[str]
13
+ tag: list[str]
14
+ role: list[str]
15
+ name: list[str]
16
+
17
+
18
+ @dataclass
19
+ class NetboxQuery(Query):
20
+ query: List[str]
21
+
22
+ @classmethod
23
+ def new(
24
+ cls, query: Union[str, Iterable[str]],
25
+ hosts_range: Optional[slice] = None,
26
+ ) -> "NetboxQuery":
27
+ if hosts_range is not None:
28
+ raise ValueError("host_range is not supported")
29
+ return cls(query=list(query))
30
+
31
+ @property
32
+ def globs(self):
33
+ # We process every query host as a glob
34
+ return self.query
35
+
36
+ def parse_query(self) -> Filter:
37
+ query_groups = defaultdict(list)
38
+ for q in self.globs:
39
+ if FIELD_VALUE_SEPARATOR in q:
40
+ glob_type, param = q.split(FIELD_VALUE_SEPARATOR, 2)
41
+ if glob_type not in ALLOWED_GLOB_GROUPS:
42
+ raise Exception(f"unknown query type: '{glob_type}'")
43
+ if not param:
44
+ raise Exception(f"empty param for '{glob_type}'")
45
+ query_groups[glob_type].append(param)
46
+ else:
47
+ query_groups["name"].append(q)
48
+
49
+ query_groups.default_factory = None
50
+ return cast(Filter, query_groups)
51
+
52
+ def is_empty(self) -> bool:
53
+ return len(self.query) == 0
54
+
55
+ def is_host_query(self) -> bool:
56
+ if not self.globs:
57
+ return False
58
+ for q in self.globs:
59
+ if FIELD_VALUE_SEPARATOR in q:
60
+ return False
61
+ return True
@@ -1,8 +1,8 @@
1
- from logging import getLogger
2
- from typing import Any, Optional, List, Union, Dict
3
- from ipaddress import ip_interface
4
- from collections import defaultdict
5
1
  import ssl
2
+ from collections import defaultdict
3
+ from ipaddress import ip_interface
4
+ from logging import getLogger
5
+ from typing import Any, Optional, List, Union, Dict, cast
6
6
 
7
7
  from adaptix import P
8
8
  from adaptix.conversion import impl_converter, link, link_constant
@@ -13,7 +13,7 @@ from annet.adapters.netbox.common import models
13
13
  from annet.adapters.netbox.common.manufacturer import (
14
14
  get_hw, get_breed,
15
15
  )
16
- from annet.adapters.netbox.common.query import NetboxQuery
16
+ from annet.adapters.netbox.common.query import NetboxQuery, FIELD_VALUE_SEPARATOR
17
17
  from annet.adapters.netbox.common.storage_opts import NetboxStorageOpts
18
18
  from annet.annlib.netdev.views.hardware import HardwareView
19
19
  from annet.storage import Storage, Device, Interface
@@ -101,6 +101,9 @@ class NetboxStorageV37(Storage):
101
101
  self.exact_host_filter = opts.exact_host_filter
102
102
  self.netbox = NetboxV37(url=url, token=token, ssl_context=ctx)
103
103
  self._all_fqdns: Optional[list[str]] = None
104
+ self._id_devices: dict[int, models.NetboxDevice] = {}
105
+ self._name_devices: dict[str, models.NetboxDevice] = {}
106
+ self._short_name_devices: dict[str, models.NetboxDevice] = {}
104
107
 
105
108
  def __enter__(self):
106
109
  return self
@@ -136,6 +139,37 @@ class NetboxStorageV37(Storage):
136
139
  ) -> List[models.NetboxDevice]:
137
140
  if isinstance(query, list):
138
141
  query = NetboxQuery.new(query)
142
+
143
+ devices = []
144
+ if query.is_host_query():
145
+ globs = []
146
+ for glob in query.globs:
147
+ if glob in self._name_devices:
148
+ devices.append(self._name_devices[glob])
149
+ if glob in self._short_name_devices:
150
+ devices.append(self._short_name_devices[glob])
151
+ else:
152
+ globs.append(glob)
153
+ if not globs:
154
+ return devices
155
+ query = NetboxQuery.new(globs)
156
+
157
+ return devices + self._make_devices(
158
+ query=query,
159
+ preload_neighbors=preload_neighbors,
160
+ use_mesh=use_mesh,
161
+ preload_extra_fields=preload_extra_fields,
162
+ **kwargs
163
+ )
164
+
165
+ def _make_devices(
166
+ self,
167
+ query: NetboxQuery,
168
+ preload_neighbors=False,
169
+ use_mesh=None,
170
+ preload_extra_fields=False,
171
+ **kwargs,
172
+ ) -> List[models.NetboxDevice]:
139
173
  device_ids = {
140
174
  device.id: extend_device(
141
175
  device=device,
@@ -148,6 +182,9 @@ class NetboxStorageV37(Storage):
148
182
  if not device_ids:
149
183
  return []
150
184
 
185
+ for device in device_ids.values():
186
+ self._record_device(device)
187
+
151
188
  interfaces = self._load_interfaces(list(device_ids))
152
189
  neighbours = {x.id: x for x in self._load_neighbours(interfaces)}
153
190
  neighbours_seen: dict[str, set] = defaultdict(set)
@@ -162,32 +199,22 @@ class NetboxStorageV37(Storage):
162
199
 
163
200
  return list(device_ids.values())
164
201
 
202
+ def _record_device(self, device: models.NetboxDevice):
203
+ self._id_devices[device.id] = device
204
+ self._short_name_devices[device.name] = device
205
+ if not self.exact_host_filter:
206
+ short_name = device.name.split(".")[0]
207
+ self._short_name_devices[short_name] = device
208
+
165
209
  def _load_devices(self, query: NetboxQuery) -> List[api_models.Device]:
166
210
  if not query.globs:
167
211
  return []
168
- devices = []
169
- device_ids = set()
170
- query_groups = parse_glob(query.globs)
171
- if self.exact_host_filter:
172
- name_ies = [_hostname_dot_hack(query) for query in query_groups.pop("name__ic", [])]
173
- query_groups["name__ie"].extend(name_ies)
174
- for grp, params in query_groups.items():
175
- if not params:
176
- continue
177
- try:
178
- new_devices = self.netbox.dcim_all_devices(**{grp: params}).results
179
- except Exception as e:
180
- # tag and site lookup returns 400 in case of unknown tag or site
181
- if "is not one of the available choices" in str(e):
182
- continue
183
- raise
184
- if grp == "name__ic":
185
- new_devices = [device for device in new_devices if _match_query(query, device)]
186
- for device in new_devices:
187
- if device.id not in device_ids:
188
- device_ids.add(device.id)
189
- devices.extend(new_devices)
190
- return devices
212
+ query_groups = parse_glob(self.exact_host_filter, query)
213
+ return [
214
+ device
215
+ for device in self.netbox.dcim_all_devices(**query_groups).results
216
+ if _match_query(self.exact_host_filter, query, device)
217
+ ]
191
218
 
192
219
  def _extend_interfaces(self, interfaces: List[models.Interface]) -> List[models.Interface]:
193
220
  extended_ifaces = {
@@ -238,6 +265,9 @@ class NetboxStorageV37(Storage):
238
265
  self, obj_id, preload_neighbors=False, use_mesh=None,
239
266
  **kwargs,
240
267
  ) -> models.NetboxDevice:
268
+ if obj_id in self._id_devices:
269
+ return self._id_devices[obj_id]
270
+
241
271
  device = self.netbox.dcim_device(obj_id)
242
272
  interfaces = self._load_interfaces([device.id])
243
273
  neighbours = self._load_neighbours(interfaces)
@@ -248,6 +278,7 @@ class NetboxStorageV37(Storage):
248
278
  interfaces=interfaces,
249
279
  neighbours=neighbours,
250
280
  )
281
+ self._record_device(res)
251
282
  return res
252
283
 
253
284
  def flush_perf(self):
@@ -272,9 +303,20 @@ class NetboxStorageV37(Storage):
272
303
  return res
273
304
 
274
305
 
275
- def _match_query(query: NetboxQuery, device_data: api_models.Device) -> bool:
276
- for subquery in query.globs:
277
- if subquery.strip() in device_data.name:
306
+ def _match_query(exact_host_filter: bool, query: NetboxQuery, device_data: api_models.Device) -> bool:
307
+ """
308
+ Additional filtering after netbox due to limited backend logic.
309
+ """
310
+ if exact_host_filter:
311
+ return True # nothing to check, all filtering is done by netbox
312
+ hostnames = [subquery.strip() for subquery in query.globs if FIELD_VALUE_SEPARATOR not in subquery]
313
+ if not hostnames:
314
+ return True # no hostnames to check
315
+
316
+ short_name = device_data.name.split(".")[0]
317
+ for hostname in hostnames:
318
+ hostname = hostname.strip().rstrip(".")
319
+ if short_name == hostname or device_data.name == hostname:
278
320
  return True
279
321
  return False
280
322
 
@@ -294,20 +336,17 @@ def _hostname_dot_hack(raw_query: str) -> str:
294
336
  if isinstance(raw_query, list):
295
337
  for i, name in enumerate(raw_query):
296
338
  raw_query[i] = add_dot(name)
339
+ elif isinstance(raw_query, str):
340
+ raw_query = add_dot(raw_query)
297
341
 
298
342
  return raw_query
299
343
 
300
344
 
301
- def parse_glob(globs: list[str]) -> dict[str, list[str]]:
302
- query_groups: dict[str, list[str]] = {"tag": [], "site": [], "name__ic": []}
303
- for q in globs:
304
- if ":" in q:
305
- glob_type, param = q.split(":", 2)
306
- if glob_type not in query_groups:
307
- raise Exception(f"unknown query type: '{glob_type}'")
308
- if not param:
309
- raise Exception(f"empty param for '{glob_type}'")
310
- query_groups[glob_type].append(param)
345
+ def parse_glob(exact_host_filter: bool, query: NetboxQuery) -> dict[str, list[str]]:
346
+ query_groups = cast(dict[str, list[str]], query.parse_query())
347
+ if names := query_groups.pop("name", None):
348
+ if exact_host_filter:
349
+ query_groups["name__ie"] = names
311
350
  else:
312
- query_groups["name__ic"].append(q)
351
+ query_groups["name__ic"] = [_hostname_dot_hack(name) for name in names]
313
352
  return query_groups
@@ -104,7 +104,7 @@
104
104
  "Nokia.NS7750": " 7750",
105
105
  "Nokia.SR_1s": "SR-1s",
106
106
 
107
- "PC": "^(PC|pc|[Mm]ellanox SN|[Ee]dge-?[Cc]ore|[Mm]oxa|NVIDIA|[Aa]sterfusion CX|[Uu]fi[Ss]pace)",
107
+ "PC": "^(PC|pc|[Mm]ellanox SN|[Ee]dge-?[Cc]ore|[Mm]oxa|NVIDIA|Nebius|[Aa]sterfusion CX|[Uu]fi[Ss]pace)",
108
108
 
109
109
  "PC.Whitebox": "([Mm]ellanox SN|[Ee]dge-?[Cc]ore|NVIDIA|[Aa]sterfusion CX|[Uu]fi[Ss]pace)",
110
110
  "PC.Whitebox.Mellanox": "[Mm]ellanox",
@@ -132,6 +132,8 @@
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.Nebius": "^Nebius",
136
+ "PC.Nebius.NB-E-BR-DCU-AST2600": "^Nebius NB-E-BR-DCU-AST2600",
135
137
 
136
138
  "RouterOS": "^([Rr]outer[Oo][Ss]|[Mm]ikro[Tt]ik)",
137
139
 
@@ -245,8 +245,10 @@ class VrfOptions:
245
245
  l2vpn_evpn: FamilyOptions
246
246
 
247
247
  vrf_name_global: Optional[str] = None
248
- l3vni: Optional[int] = None
248
+ import_policy: str = ""
249
+ export_policy: str = ""
249
250
  as_path_relax: bool = False
251
+ l3vni: Optional[int] = None
250
252
  rt_import: list[str] = field(default_factory=list)
251
253
  rt_export: list[str] = field(default_factory=list)
252
254
  rt_import_v4: list[str] = field(default_factory=list)
@@ -281,7 +283,7 @@ class BgpConfig:
281
283
  peers: list[Peer]
282
284
 
283
285
 
284
- def _used_policies(peer: Union[Peer, PeerGroup]) -> Iterable[str]:
286
+ def _used_policies(peer: Union[Peer, PeerGroup, VrfOptions]) -> Iterable[str]:
285
287
  if peer.import_policy:
286
288
  yield peer.import_policy
287
289
  if peer.export_policy:
@@ -309,6 +311,7 @@ def extract_policies(config: BgpConfig) -> Sequence[str]:
309
311
  for group in vrf.groups:
310
312
  result.extend(_used_policies(group))
311
313
  result.extend(_used_redistribute_policies(vrf))
314
+ result.extend(_used_policies(vrf))
312
315
  for group in config.global_options.groups:
313
316
  result.extend(_used_policies(group))
314
317
  for peer in config.peers:
@@ -128,8 +128,7 @@ class PrefixListFilterGenerator(PartialGenerator, ABC):
128
128
  name,
129
129
  f"seq {i * 5 + 5}",
130
130
  "permit",
131
- str(addr_mask.ip).upper(),
132
- str(addr_mask.network.prefixlen),
131
+ addr_mask.with_prefixlen,
133
132
  ) + (
134
133
  ("ge", str(match.greater_equal)) if match.greater_equal is not None else ()
135
134
  ) + (
@@ -88,6 +88,7 @@ interface
88
88
 
89
89
  (?:ip|ipv6) routing
90
90
 
91
+ ip prefix-list
91
92
 
92
93
  ip community-list
93
94
 
@@ -32,7 +32,7 @@ undo route-distinguisher *
32
32
  dialog: Warning: All VPN targets of this EVPN instance and all EVPN address family-related configurations under BGP will be deleted. Continue? [Y/N]: ::: Y
33
33
 
34
34
  save
35
- dialog: Warning: The current configuration will be written to the device. Continue? [Y/N] ::: Y
35
+ dialog: /Warning: The current configuration will be written to the device. Continue\? \[Y/N\]:?/ ::: Y
36
36
  dialog: Warning: The current configuration will be written to the device. Are you sure to continue? [Y/N]: ::: Y
37
37
  dialog: Are you sure to continue?[Y/N] ::: Y
38
38
  dialog: Are you sure to continue? [Y/N] ::: Y
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: annet
3
- Version: 0.16.34
3
+ Version: 0.16.36
4
4
  Summary: annet
5
5
  Home-page: https://github.com/annetutil/annet
6
6
  License: MIT
@@ -1,26 +0,0 @@
1
- from dataclasses import dataclass
2
- from typing import List, Union, Iterable, Optional
3
-
4
- from annet.storage import Query
5
-
6
-
7
- @dataclass
8
- class NetboxQuery(Query):
9
- query: List[str]
10
-
11
- @classmethod
12
- def new(
13
- cls, query: Union[str, Iterable[str]],
14
- hosts_range: Optional[slice] = None,
15
- ) -> "NetboxQuery":
16
- if hosts_range is not None:
17
- raise ValueError("host_range is not supported")
18
- return cls(query=list(query))
19
-
20
- @property
21
- def globs(self):
22
- # We process every query host as a glob
23
- return self.query
24
-
25
- def is_empty(self) -> bool:
26
- return len(self.query) == 0
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes