annet 0.13.2__py3-none-any.whl → 0.13.4__py3-none-any.whl

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.

@@ -39,6 +39,22 @@ class DeviceIp:
39
39
  family: int
40
40
 
41
41
 
42
+ @dataclass
43
+ class Prefix:
44
+ id: int
45
+ prefix: str
46
+ site: Entity | None
47
+ vrf: Entity | None
48
+ tenant: Entity | None
49
+ vlan: Entity | None
50
+ role: Entity | None
51
+ status: Label
52
+ is_pool: bool
53
+ custom_fields: dict[str, Any]
54
+ created: datetime
55
+ last_updated: datetime
56
+
57
+
42
58
  @dataclass
43
59
  class IpAddress:
44
60
  id: int
@@ -50,12 +66,27 @@ class IpAddress:
50
66
  tags: List[Entity]
51
67
  created: datetime
52
68
  last_updated: datetime
69
+ prefix: Optional[Prefix] = None
70
+
71
+
72
+ @dataclass
73
+ class InterfaceConnectedEndpoint(Entity):
74
+ device: Entity
75
+
76
+
77
+ @dataclass
78
+ class InterfaceType:
79
+ value: str
80
+ label: str
53
81
 
54
82
 
55
83
  @dataclass
56
84
  class Interface(Entity):
57
85
  device: Entity
58
86
  enabled: bool
87
+ description: str
88
+ type: InterfaceType
89
+ connected_endpoints: Optional[list[InterfaceConnectedEndpoint]]
59
90
  display: str = ""
60
91
  ip_addresses: List[IpAddress] = field(default_factory=list)
61
92
 
@@ -64,7 +95,6 @@ class Interface(Entity):
64
95
  class NetboxDevice(Entity):
65
96
  url: str
66
97
  storage: Storage
67
- neighbours_ids: List[int]
68
98
 
69
99
  display: str
70
100
  device_type: DeviceType
@@ -92,6 +122,7 @@ class NetboxDevice(Entity):
92
122
  breed: str
93
123
 
94
124
  interfaces: List[Interface]
125
+ neighbours: Optional[List["NetboxDevice"]]
95
126
 
96
127
  # compat
97
128
 
@@ -1,5 +1,7 @@
1
1
  from logging import getLogger
2
- from typing import Optional, List, Union
2
+ from typing import Optional, List, Union, Dict
3
+ from ipaddress import ip_interface
4
+ from collections import defaultdict
3
5
 
4
6
  from adaptix import P
5
7
  from adaptix.conversion import impl_converter, link
@@ -28,18 +30,21 @@ def extend_device_base(
28
30
  interfaces: List[models.Interface],
29
31
  hw: Optional[HardwareView],
30
32
  breed: str,
33
+ neighbours: Optional[List[models.NetboxDevice]],
31
34
  storage: Storage,
32
- neighbours_ids: List[int],
33
35
  ) -> models.NetboxDevice:
34
36
  ...
35
37
 
36
38
 
37
39
  def extend_device(
38
- device: api_models.Device, storage: Storage,
40
+ device: api_models.Device,
41
+ interfaces: List[models.Interface],
42
+ neighbours: Optional[List[models.NetboxDevice]],
43
+ storage: Storage,
39
44
  ) -> models.NetboxDevice:
40
45
  return extend_device_base(
41
46
  device=device,
42
- interfaces=[],
47
+ interfaces=interfaces,
43
48
  breed=get_breed(
44
49
  device.device_type.manufacturer.name,
45
50
  device.device_type.model,
@@ -48,7 +53,7 @@ def extend_device(
48
53
  device.device_type.manufacturer.name,
49
54
  device.device_type.model,
50
55
  ),
51
- neighbours_ids=[],
56
+ neighbours=neighbours,
52
57
  storage=storage,
53
58
  )
54
59
 
@@ -61,6 +66,13 @@ def extend_interface(
61
66
  ...
62
67
 
63
68
 
69
+ @impl_converter
70
+ def extend_ip_address(
71
+ ip_address: models.IpAddress, prefix: Optional[models.Prefix],
72
+ ) -> models.IpAddress:
73
+ ...
74
+
75
+
64
76
  class NetboxStorageV37(Storage):
65
77
  def __init__(self, opts: Optional[NetboxStorageOpts] = None):
66
78
  self.netbox = NetboxV37(
@@ -95,15 +107,29 @@ class NetboxStorageV37(Storage):
95
107
  if isinstance(query, list):
96
108
  query = NetboxQuery.new(query)
97
109
  device_ids = {
98
- device.id: extend_device(device=device, storage=self)
110
+ device.id: extend_device(
111
+ device=device,
112
+ interfaces=[],
113
+ neighbours=[],
114
+ storage=self,
115
+ )
99
116
  for device in self._load_devices(query)
100
117
  }
101
118
  if not device_ids:
102
119
  return []
103
120
 
104
121
  interfaces = self._load_interfaces(list(device_ids))
122
+ neighbours = {x.id: x for x in self._load_neighbours(interfaces)}
123
+ neighbours_seen = defaultdict(set)
124
+
105
125
  for interface in interfaces:
106
126
  device_ids[interface.device.id].interfaces.append(interface)
127
+ for e in interface.connected_endpoints or []:
128
+ neighbour = neighbours[e.device.id]
129
+ if neighbour.id not in neighbours_seen[interface.device.id]:
130
+ neighbours_seen[interface.device.id].add(neighbour.id)
131
+ device_ids[interface.device.id].neighbours.append(neighbour)
132
+
107
133
  return list(device_ids.values())
108
134
 
109
135
  def _load_devices(self, query: NetboxQuery) -> List[api_models.Device]:
@@ -118,26 +144,65 @@ class NetboxStorageV37(Storage):
118
144
  if is_supported(device.device_type.manufacturer.name)
119
145
  ]
120
146
 
121
- def _load_interfaces(self, device_ids: List[int]) -> List[
122
- models.Interface]:
123
- interfaces = self.netbox.dcim_all_interfaces(device_id=device_ids)
147
+ def _extend_interfaces(self, interfaces: List[models.Interface]) -> List[models.Interface]:
124
148
  extended_ifaces = {
125
149
  interface.id: extend_interface(interface, [])
126
- for interface in interfaces.results
150
+ for interface in interfaces
127
151
  }
128
152
 
129
153
  ips = self.netbox.ipam_all_ip_addresses(interface_id=list(extended_ifaces))
154
+ ip_to_cidrs: Dict[str, str] = {ip.address: str(ip_interface(ip.address).network) for ip in ips.results}
155
+ prefixes = self.netbox.ipam_all_prefixes(prefix=list(ip_to_cidrs.values()))
156
+ cidr_to_prefix: Dict[str, models.Prefix] = {x.prefix: x for x in prefixes.results}
157
+
130
158
  for ip in ips.results:
159
+ cidr = ip_to_cidrs[ip.address]
160
+ ip = extend_ip_address(ip, prefix=cidr_to_prefix.get(cidr))
131
161
  extended_ifaces[ip.assigned_object_id].ip_addresses.append(ip)
132
162
  return list(extended_ifaces.values())
133
163
 
164
+ def _load_interfaces(self, device_ids: List[int]) -> List[
165
+ models.Interface]:
166
+ interfaces = self.netbox.dcim_all_interfaces(device_id=device_ids)
167
+ return self._extend_interfaces(interfaces.results)
168
+
169
+ def _load_interfaces_by_id(self, ids: List[int]) -> List[models.Interface]:
170
+ interfaces = self.netbox.dcim_all_interfaces_by_id(id=ids)
171
+ return self._extend_interfaces(interfaces.results)
172
+
173
+ def _load_neighbours(self, interfaces: List[models.Interface]) -> List[models.NetboxDevice]:
174
+ endpoints = [e for i in interfaces for e in i.connected_endpoints or []]
175
+ remote_interfaces_ids = [e.id for e in endpoints]
176
+ neighbours_ids = [e.device.id for e in endpoints]
177
+ neighbours_ifaces_dics = defaultdict(list)
178
+ # load only the connected interface to speed things up
179
+ for iface in self._load_interfaces_by_id(remote_interfaces_ids):
180
+ neighbours_ifaces_dics[iface.device.id].append(iface)
181
+ neighbours = []
182
+ for neighbour in self.netbox.dcim_all_devices_by_id(id=neighbours_ids).results:
183
+ extended_neighbour = extend_device(
184
+ device=neighbour,
185
+ storage=self,
186
+ interfaces=neighbours_ifaces_dics[neighbour.id],
187
+ neighbours=None, # do not load recursively
188
+ )
189
+ neighbours.append(extended_neighbour)
190
+ return neighbours
191
+
134
192
  def get_device(
135
193
  self, obj_id, preload_neighbors=False, use_mesh=None,
136
194
  **kwargs,
137
195
  ) -> models.NetboxDevice:
138
196
  device = self.netbox.dcim_device(obj_id)
139
- res = extend_device(device=device, storage=self)
140
- res.interfaces = self._load_interfaces([device.id])
197
+ interfaces = self._load_interfaces([device.id])
198
+ neighbours = self._load_neighbours(interfaces)
199
+
200
+ res = extend_device(
201
+ device=device,
202
+ storage=self,
203
+ interfaces=interfaces[device.id],
204
+ neighbours=neighbours,
205
+ )
141
206
  return res
142
207
 
143
208
  def flush_perf(self):
@@ -61,6 +61,10 @@ class HardwareView(HardwareLeaf):
61
61
  def vendor(self) -> Optional[str]:
62
62
  return hw_to_vendor(self)
63
63
 
64
+ @property
65
+ def soft(self) -> str:
66
+ return self._soft
67
+
64
68
  def __hash__(self):
65
69
  return hash(self.model)
66
70
 
annet/gen.py CHANGED
@@ -513,9 +513,11 @@ def worker(device_id, args: ShowGenOptions, stdin, loader: "Loader", filterer: F
513
513
  dumped_data = json.dumps(data, indent=4, sort_keys=True, ensure_ascii=False)
514
514
  yield (output_driver.entire_config_dest_path(device, path), dumped_data, False)
515
515
 
516
- has_file_result = new_files or new_file_fragments
517
- has_partial_result = new or not has_file_result
518
- if device.hw.vendor in platform.VENDOR_REVERSES and has_partial_result:
516
+ # Consider result of partial run empty and create an empty dest file
517
+ # only if there are some acl rules that has been matched.
518
+ # Otherwise treat it as if no supported generators have been found.
519
+ acl_rules = res.get_acl_rules(args.acl_safe)
520
+ if device.hw.vendor in platform.VENDOR_REVERSES and acl_rules:
519
521
  orderer = patching.Orderer.from_hw(device.hw)
520
522
  yield (output_driver.cfg_file_names(device)[0],
521
523
  format_config_blocks(
@@ -606,14 +608,14 @@ def _print_perf(gen_type, perf):
606
608
  (
607
609
  (gen if not method else None),
608
610
  (method or "." * 30),
609
- sum(map(itemgetter("time"), stat)),
610
- (min(map(itemgetter("time"), stat)) if method else None),
611
- (percentile(stat, 0.95, itemgetter("time")) if method else None),
612
- (max(map(itemgetter("time"), stat)) if method else None),
613
- (len(stat) if method else None),
611
+ sum(map(itemgetter("time"), stat) if stat else None),
612
+ (min(map(itemgetter("time"), stat)) if stat else None),
613
+ (percentile(stat, 0.95, itemgetter("time")) if stat else None),
614
+ (max(map(itemgetter("time"), stat)) if stat else None),
615
+ (len(stat) if stat else None),
614
616
  (len(list(filter(
615
617
  lambda item: item in ["call", "disk_write"],
616
- map(itemgetter("op"), stat)))) if method else None),
618
+ map(itemgetter("op"), stat)))) if stat else None),
617
619
  )
618
620
 
619
621
  for (gen, gen_perf) in sorted(
@@ -125,9 +125,9 @@ def run_partial_initial(device):
125
125
 
126
126
  @tracing.function
127
127
  def run_partial_generators(
128
- gens: List["PartialGenerator"],
129
- ref_gens: List["RefGenerator"],
130
- run_args: GeneratorPartialRunArgs,
128
+ gens: List["PartialGenerator"],
129
+ ref_gens: List["RefGenerator"],
130
+ run_args: GeneratorPartialRunArgs,
131
131
  ):
132
132
  logger = get_logger(host=run_args.device.hostname)
133
133
  tracing_connector.get().set_device_attributes(tracing_connector.get().get_current_span(), run_args.device)
@@ -151,14 +151,12 @@ def run_partial_generators(
151
151
  if not result:
152
152
  continue
153
153
 
154
- config = result.safe_config if run_args.use_acl_safe else result.config
155
-
156
- ref_match = ret.ref_matcher.match(config)
154
+ ref_match = ret.ref_matcher.match(result.config)
157
155
  for ref_gen, groups in ref_match:
158
156
  gens.append(ref_gen.with_groups(groups))
159
157
  ret.ref_track.add(gen.__class__, ref_gen.__class__)
160
158
 
161
- ret.ref_track.config(gen.__class__, config)
159
+ ret.ref_track.config(gen.__class__, result.config)
162
160
  ret.add_partial(result)
163
161
 
164
162
  return ret
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: annet
3
- Version: 0.13.2
3
+ Version: 0.13.4
4
4
  Summary: annet
5
5
  Home-page: https://github.com/annetutil/annet
6
6
  License: MIT
@@ -8,7 +8,7 @@ annet/deploy.py,sha256=B8E0P_VvCrS2URjFvgmUiIkHp95g7pAWfmT34igaDeo,18893
8
8
  annet/diff.py,sha256=zLcaCnb4lZRUb7frpH1CstQ3kacRcCblZs1uLG8J5lk,3391
9
9
  annet/executor.py,sha256=EaeQ_JeRFuUUHUoR2LXDcNvY7IVg0eA2d6zSmQVJt-M,19216
10
10
  annet/filtering.py,sha256=F4ZKUCU3Yb1RlL2zKdyHtVUaWPzjWF4vWyMcdygKNYk,852
11
- annet/gen.py,sha256=Gl2-6A1ekuz4mDf7SjGAmI8CMoLXoOtjNcgOvY3SoP0,33376
11
+ annet/gen.py,sha256=jlAkT_phlIYrpOIqVc0GrApWp3Y8zr8lyxnZgIdL2F4,33533
12
12
  annet/hardware.py,sha256=HYPDfji_GdRn5S0_0fl4rvM7byOY9aHxG6VMAtsLaxE,1090
13
13
  annet/implicit.py,sha256=QigK4uoxvrFho2h9yFjOq1d9rLsC5xFlAU4bKkCuMWk,5139
14
14
  annet/lib.py,sha256=tQQlxZg1A2Q-tp18PoqBuORQmvP5dAAmAPvP6bBA6a0,3764
@@ -27,14 +27,14 @@ annet/adapters/netbox/provider.py,sha256=OM7Hq2vnMHNVBfubJvx2qJlMYm3VvKhomdLMNO8
27
27
  annet/adapters/netbox/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
28
  annet/adapters/netbox/common/client.py,sha256=-lWZmphD-OPuLIHNKhW_h2bnjrVaiyKYAD_MUPasEbo,2483
29
29
  annet/adapters/netbox/common/manufacturer.py,sha256=UH_tEKT3GXC8WSm15q0xxXRE7aj0b0icgwmR--PRWBs,1771
30
- annet/adapters/netbox/common/models.py,sha256=yHppe8UIc4Ww8UUqXChlfkmqgr96uhsOlvT8NrxvYGA,1917
30
+ annet/adapters/netbox/common/models.py,sha256=rmFQ93NUCnFM-p58aS4sjiOaTuYR0Xz65soiW7IQfDk,2516
31
31
  annet/adapters/netbox/common/query.py,sha256=OgUuF-bvshpoBUkrOs0tsMUAhjTsttzx3VV30ryFl0Y,577
32
32
  annet/adapters/netbox/common/status_client.py,sha256=W4nTb2yvBlJ2UkWUmUhKQ2PaSQb1shjhHj5ebb4s2s4,591
33
33
  annet/adapters/netbox/common/storage_opts.py,sha256=rl_0pr3VzmOy6PDZIUMkKSBfJh90gD9TFL3yBhK_8ME,337
34
34
  annet/adapters/netbox/v24/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
35
  annet/adapters/netbox/v24/storage.py,sha256=uDcAuabORu8ag3OXDHXKDK469Pd-kNXpUI_Cg-hgW_k,5919
36
36
  annet/adapters/netbox/v37/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
- annet/adapters/netbox/v37/storage.py,sha256=ZL8ebQAMWfRUJWmMbEODq41KlIDhAqV6P--2hH3APJM,4525
37
+ annet/adapters/netbox/v37/storage.py,sha256=9CxLr2tMWvGA1BLtjd5TTp2qEUQ_aYJq0eZSe-S242E,7372
38
38
  annet/annlib/__init__.py,sha256=fT1l4xV5fqqg8HPw9HqmZVN2qwS8i6X1aIm2zGDjxKY,252
39
39
  annet/annlib/command.py,sha256=uuBddMQphtn8P5MO5kzIa8_QrtMns-k05VeKv1bcAuA,1043
40
40
  annet/annlib/diff.py,sha256=UPt3kYFQdQdVVy3ePYzNHPAxMVmHxCrCnZnMCV6ou2Q,4587
@@ -52,7 +52,7 @@ annet/annlib/netdev/devdb/__init__.py,sha256=aKYjjLbJebdKBjnGDpVLQdSqrV2JL24spGm
52
52
  annet/annlib/netdev/devdb/data/devdb.json,sha256=NldGS8_whScc7eC_CZPEDB9AoxtfvK_51oJVFIu0Tlo,5072
53
53
  annet/annlib/netdev/views/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
54
54
  annet/annlib/netdev/views/dump.py,sha256=XJ6UO6sYSO5pIoe5R4eTQmKwx0_JJ71_h8QSYAURtyk,4184
55
- annet/annlib/netdev/views/hardware.py,sha256=FtYlN5MWjCPn-GkDdIQrGO5mDrVYbbY8g_T7bNmfFPk,3256
55
+ annet/annlib/netdev/views/hardware.py,sha256=o-w-k1bOORWwEnSGsDq-9Q_oIeH3yQv9eiCJn_UgG-Y,3324
56
56
  annet/annlib/rbparser/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
57
57
  annet/annlib/rbparser/acl.py,sha256=RR8yPt6t96_IiyuKVbeZ-3x32cyhBAT2wC1y99oWBO8,3931
58
58
  annet/annlib/rbparser/deploying.py,sha256=ACT8QNhDAhJx3ZKuGh2nYBOrpdka2qEKuLDxvQAGKLk,1649
@@ -64,7 +64,7 @@ annet/annlib/rulebook/common.py,sha256=9kCZwZpsH5UliF2OzaN9nLs2eLlz_d__4L7_oZ3Sr
64
64
  annet/api/__init__.py,sha256=vwmyZK_iCdAjlJ0jS5CXSroDAZFzGe3JiXL-WoZiovU,33154
65
65
  annet/configs/context.yml,sha256=nt4QnzYzrG2hqmaWOUsab2wgFl-CXmFSzbto6rqqFt4,291
66
66
  annet/configs/logging.yaml,sha256=Hu42lRK248dssp9TgkbHCaZNl0E6f4IChGb0XaQnTVo,970
67
- annet/generators/__init__.py,sha256=tk20umBuMEcd8QKET3mxJQsDj-dvjlwGnrlgTw0wbRY,15622
67
+ annet/generators/__init__.py,sha256=ACMH9UQ4I3uq2b8nDzOn2dgYxreDq4_3ojOGwht0-No,15543
68
68
  annet/generators/base.py,sha256=rgQLcQBPZm4ecbKmRhVOpBR-GFJAiVfdb_y5f2-LUR8,3670
69
69
  annet/generators/entire.py,sha256=7WySkVaXNT3ZAiHcQ0JUKdlhGJAN8cNUxF6c7ilYnO8,2928
70
70
  annet/generators/exceptions.py,sha256=GPXTBgn2xZ3Ev_bdOPlfCLGRC1kQHe_dEq88S-uyi5s,151
@@ -124,10 +124,10 @@ annet/rulebook/texts/routeros.rul,sha256=ipfxjj0mjFef6IsUlupqx4BY_Je_OUb8u_U1019
124
124
  annet_generators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
125
125
  annet_generators/example/__init__.py,sha256=zVd8_DrXuOASrNzg2Ab94rPyvJff83L-_HppDFxnUjM,228
126
126
  annet_generators/example/lldp.py,sha256=68CLrK7vxTQQy9XIBxtywuEdBNlIlfXGYj8_wYWs5UI,1146
127
- annet-0.13.2.dist-info/AUTHORS,sha256=rh3w5P6gEgqmuC-bw-HB68vBCr-yIBFhVL0PG4hguLs,878
128
- annet-0.13.2.dist-info/LICENSE,sha256=yPxl7dno02Pw7gAcFPIFONzx_gapwDoPXsIsh6Y7lC0,1079
129
- annet-0.13.2.dist-info/METADATA,sha256=YBUO24OLpM9o_u1-a6cJVcwPpqHyi8q_MVKx7Vt5Xss,694
130
- annet-0.13.2.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
131
- annet-0.13.2.dist-info/entry_points.txt,sha256=yHimujIzR2bwNIbb--MTs_GpXiAve89Egpu2LlgTEkg,119
132
- annet-0.13.2.dist-info/top_level.txt,sha256=QsoTZBsUtwp_FEcmRwuN8QITBmLOZFqjssRfKilGbP8,23
133
- annet-0.13.2.dist-info/RECORD,,
127
+ annet-0.13.4.dist-info/AUTHORS,sha256=rh3w5P6gEgqmuC-bw-HB68vBCr-yIBFhVL0PG4hguLs,878
128
+ annet-0.13.4.dist-info/LICENSE,sha256=yPxl7dno02Pw7gAcFPIFONzx_gapwDoPXsIsh6Y7lC0,1079
129
+ annet-0.13.4.dist-info/METADATA,sha256=Ug49vbZb6q3AP41fId0L8o2hCst2KTJ9V4l2MgYF2rA,694
130
+ annet-0.13.4.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
131
+ annet-0.13.4.dist-info/entry_points.txt,sha256=yHimujIzR2bwNIbb--MTs_GpXiAve89Egpu2LlgTEkg,119
132
+ annet-0.13.4.dist-info/top_level.txt,sha256=QsoTZBsUtwp_FEcmRwuN8QITBmLOZFqjssRfKilGbP8,23
133
+ annet-0.13.4.dist-info/RECORD,,
File without changes