annet 0.1__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.
- annet/__init__.py +61 -0
- annet/annet.py +25 -0
- annet/annlib/__init__.py +7 -0
- annet/annlib/command.py +49 -0
- annet/annlib/diff.py +158 -0
- annet/annlib/errors.py +8 -0
- annet/annlib/filter_acl.py +196 -0
- annet/annlib/jsontools.py +89 -0
- annet/annlib/lib.py +495 -0
- annet/annlib/netdev/__init__.py +0 -0
- annet/annlib/netdev/db.py +62 -0
- annet/annlib/netdev/devdb/__init__.py +28 -0
- annet/annlib/netdev/devdb/data/devdb.json +137 -0
- annet/annlib/netdev/views/__init__.py +0 -0
- annet/annlib/netdev/views/dump.py +121 -0
- annet/annlib/netdev/views/hardware.py +112 -0
- annet/annlib/output.py +246 -0
- annet/annlib/patching.py +533 -0
- annet/annlib/rbparser/__init__.py +0 -0
- annet/annlib/rbparser/acl.py +120 -0
- annet/annlib/rbparser/deploying.py +55 -0
- annet/annlib/rbparser/ordering.py +52 -0
- annet/annlib/rbparser/platform.py +51 -0
- annet/annlib/rbparser/syntax.py +115 -0
- annet/annlib/rulebook/__init__.py +0 -0
- annet/annlib/rulebook/common.py +350 -0
- annet/annlib/tabparser.py +648 -0
- annet/annlib/types.py +35 -0
- annet/api/__init__.py +807 -0
- annet/argparse.py +415 -0
- annet/cli.py +192 -0
- annet/cli_args.py +493 -0
- annet/configs/context.yml +18 -0
- annet/configs/logging.yaml +39 -0
- annet/connectors.py +64 -0
- annet/deploy.py +441 -0
- annet/diff.py +85 -0
- annet/executor.py +551 -0
- annet/filtering.py +40 -0
- annet/gen.py +828 -0
- annet/generators/__init__.py +987 -0
- annet/generators/common/__init__.py +0 -0
- annet/generators/common/initial.py +33 -0
- annet/hardware.py +45 -0
- annet/implicit.py +139 -0
- annet/lib.py +128 -0
- annet/output.py +170 -0
- annet/parallel.py +448 -0
- annet/patching.py +25 -0
- annet/reference.py +148 -0
- annet/rulebook/__init__.py +114 -0
- annet/rulebook/arista/__init__.py +0 -0
- annet/rulebook/arista/iface.py +16 -0
- annet/rulebook/aruba/__init__.py +16 -0
- annet/rulebook/aruba/ap_env.py +146 -0
- annet/rulebook/aruba/misc.py +8 -0
- annet/rulebook/cisco/__init__.py +0 -0
- annet/rulebook/cisco/iface.py +68 -0
- annet/rulebook/cisco/misc.py +57 -0
- annet/rulebook/cisco/vlandb.py +90 -0
- annet/rulebook/common.py +19 -0
- annet/rulebook/deploying.py +87 -0
- annet/rulebook/huawei/__init__.py +0 -0
- annet/rulebook/huawei/aaa.py +75 -0
- annet/rulebook/huawei/bgp.py +97 -0
- annet/rulebook/huawei/iface.py +33 -0
- annet/rulebook/huawei/misc.py +337 -0
- annet/rulebook/huawei/vlandb.py +115 -0
- annet/rulebook/juniper/__init__.py +107 -0
- annet/rulebook/nexus/__init__.py +0 -0
- annet/rulebook/nexus/iface.py +92 -0
- annet/rulebook/patching.py +143 -0
- annet/rulebook/ribbon/__init__.py +12 -0
- annet/rulebook/texts/arista.deploy +20 -0
- annet/rulebook/texts/arista.order +125 -0
- annet/rulebook/texts/arista.rul +59 -0
- annet/rulebook/texts/aruba.deploy +20 -0
- annet/rulebook/texts/aruba.order +83 -0
- annet/rulebook/texts/aruba.rul +87 -0
- annet/rulebook/texts/cisco.deploy +27 -0
- annet/rulebook/texts/cisco.order +82 -0
- annet/rulebook/texts/cisco.rul +105 -0
- annet/rulebook/texts/huawei.deploy +188 -0
- annet/rulebook/texts/huawei.order +388 -0
- annet/rulebook/texts/huawei.rul +471 -0
- annet/rulebook/texts/juniper.rul +120 -0
- annet/rulebook/texts/nexus.deploy +24 -0
- annet/rulebook/texts/nexus.order +85 -0
- annet/rulebook/texts/nexus.rul +83 -0
- annet/rulebook/texts/nokia.rul +31 -0
- annet/rulebook/texts/pc.order +5 -0
- annet/rulebook/texts/pc.rul +9 -0
- annet/rulebook/texts/ribbon.deploy +22 -0
- annet/rulebook/texts/ribbon.rul +77 -0
- annet/rulebook/texts/routeros.order +38 -0
- annet/rulebook/texts/routeros.rul +45 -0
- annet/storage.py +121 -0
- annet/tabparser.py +36 -0
- annet/text_term_format.py +95 -0
- annet/tracing.py +170 -0
- annet/types.py +223 -0
- annet-0.1.dist-info/AUTHORS +21 -0
- annet-0.1.dist-info/LICENSE +21 -0
- annet-0.1.dist-info/METADATA +24 -0
- annet-0.1.dist-info/RECORD +113 -0
- annet-0.1.dist-info/WHEEL +5 -0
- annet-0.1.dist-info/entry_points.txt +6 -0
- annet-0.1.dist-info/top_level.txt +3 -0
- annet_generators/__init__.py +0 -0
- annet_generators/example/__init__.py +12 -0
- annet_generators/example/lldp.py +52 -0
- annet_nbexport/__init__.py +220 -0
- annet_nbexport/main.py +46 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import csv
|
|
2
|
+
import dataclasses
|
|
3
|
+
import operator
|
|
4
|
+
import os
|
|
5
|
+
import os.path
|
|
6
|
+
import pathlib
|
|
7
|
+
from typing import List
|
|
8
|
+
|
|
9
|
+
from annet.storage import Device, Storage, Query, StorageOpts, StorageProvider
|
|
10
|
+
from annet.annlib.netdev.views.hardware import HardwareView
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AnnetNbExportProvder(StorageProvider):
|
|
14
|
+
def storage(self):
|
|
15
|
+
return AnnetNbExportStorage
|
|
16
|
+
|
|
17
|
+
def opts(self):
|
|
18
|
+
return AnnetNbExportStorageOpts
|
|
19
|
+
|
|
20
|
+
def query(self):
|
|
21
|
+
return AnnetNbExportQuery
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclasses.dataclass
|
|
25
|
+
class AnnetNbExportQuery(Query):
|
|
26
|
+
query: List[str]
|
|
27
|
+
|
|
28
|
+
@classmethod
|
|
29
|
+
def new(cls, query, hosts_range) -> "AnnetNbExportQuery":
|
|
30
|
+
if hosts_range is not None:
|
|
31
|
+
raise ValueError("host_range is not supported")
|
|
32
|
+
return cls(query=query)
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def globs(self):
|
|
36
|
+
# We process every query host as a glob
|
|
37
|
+
return self.query
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclasses.dataclass
|
|
41
|
+
class AnnetNbExportStorageOpts(StorageOpts):
|
|
42
|
+
@classmethod
|
|
43
|
+
def from_cli_opts(cls, cli_opts) -> "AnnetNbExportQuery":
|
|
44
|
+
return cls()
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class DeviceNb(Device):
|
|
48
|
+
def __init__(self, storage: "Storage", dto: "NetboxDTO", region: str):
|
|
49
|
+
self.dto = dto
|
|
50
|
+
self.storage = storage
|
|
51
|
+
self.region = region
|
|
52
|
+
|
|
53
|
+
def __hash__(self):
|
|
54
|
+
return hash(self.id)
|
|
55
|
+
|
|
56
|
+
def is_pc(self):
|
|
57
|
+
return self.dto.manufacturer == "Mellanox"
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def hw(self):
|
|
61
|
+
manufacturer = self.dto.manufacturer
|
|
62
|
+
model_name = self.dto.model_name
|
|
63
|
+
# по какой-то причине модели mellanox SN в нетбоксе называются MSN
|
|
64
|
+
# чтобы использовать выгрузку as is и не править devdb.json патчим тут
|
|
65
|
+
if manufacturer == "Mellanox" and model_name.startswith("MSN"):
|
|
66
|
+
model_name = model_name.replace("MSN", "SN", 1)
|
|
67
|
+
hw = _vendor_to_hw(manufacturer + " " + model_name)
|
|
68
|
+
assert hw.vendor, "unsupported manufacturer %s" % self.dto.manufacturer
|
|
69
|
+
return hw
|
|
70
|
+
|
|
71
|
+
@property
|
|
72
|
+
def id(self):
|
|
73
|
+
return self.dto.name
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def fqdn(self):
|
|
77
|
+
return self.dto.name
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def hostname(self):
|
|
81
|
+
return self.dto.name
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def neighbours_ids(self):
|
|
85
|
+
return set()
|
|
86
|
+
|
|
87
|
+
@property
|
|
88
|
+
def breed(self):
|
|
89
|
+
if self.dto.manufacturer == "Huawei" and self.dto.model_name.startswith("CE"):
|
|
90
|
+
return "vrp85"
|
|
91
|
+
elif self.dto.manufacturer == "Huawei" and self.dto.model_name.startswith("NE"):
|
|
92
|
+
return "vrp85"
|
|
93
|
+
elif self.dto.manufacturer == "Huawei":
|
|
94
|
+
return "vrp55"
|
|
95
|
+
elif self.dto.manufacturer == "Mellanox":
|
|
96
|
+
return "cuml2"
|
|
97
|
+
elif self.dto.manufacturer == "Juniper":
|
|
98
|
+
return "jun10"
|
|
99
|
+
elif self.dto.manufacturer == "Cisco":
|
|
100
|
+
return "ios12"
|
|
101
|
+
elif self.dto.manufacturer == "Adva":
|
|
102
|
+
return "adva8"
|
|
103
|
+
elif self.dto.manufacturer == "Arista":
|
|
104
|
+
return "eos4"
|
|
105
|
+
assert False, "unknown manufacturer %s" % self.dto.manufacturer
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@dataclasses.dataclass
|
|
109
|
+
class NetboxDTO:
|
|
110
|
+
name: str
|
|
111
|
+
device_role: str
|
|
112
|
+
tenant: str
|
|
113
|
+
manufacturer: str
|
|
114
|
+
model_name: str
|
|
115
|
+
platform: str
|
|
116
|
+
serial: str
|
|
117
|
+
asset_tag: str
|
|
118
|
+
status: str
|
|
119
|
+
site: str
|
|
120
|
+
rack_group: str
|
|
121
|
+
rack_name: str
|
|
122
|
+
position: str
|
|
123
|
+
face: str
|
|
124
|
+
comments: str
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class AnnetNbExportStorage(Storage):
|
|
128
|
+
def __init__(self, opts: "Optional[StorageOpts]" = None):
|
|
129
|
+
self._dump_dir = os.path.join(os.path.dirname(__file__))
|
|
130
|
+
|
|
131
|
+
def __enter__(self):
|
|
132
|
+
return self
|
|
133
|
+
|
|
134
|
+
def __exit__(self, _, __, ___):
|
|
135
|
+
pass
|
|
136
|
+
|
|
137
|
+
def resolve_object_ids_by_query(self, query):
|
|
138
|
+
ret = []
|
|
139
|
+
for device_data in _read_dump(self._dump_dir):
|
|
140
|
+
if _match_query(query, device_data):
|
|
141
|
+
ret.append(device_data["name"])
|
|
142
|
+
return ret
|
|
143
|
+
|
|
144
|
+
def resolve_fdnds_by_query(self, query):
|
|
145
|
+
return self.resolve_object_ids_by_query(query)
|
|
146
|
+
|
|
147
|
+
def make_devices(
|
|
148
|
+
self,
|
|
149
|
+
query: "inventory.Query",
|
|
150
|
+
preload_neighbors=False,
|
|
151
|
+
use_mesh=None,
|
|
152
|
+
preload_extra_fields=False,
|
|
153
|
+
**kwargs,
|
|
154
|
+
):
|
|
155
|
+
ret = []
|
|
156
|
+
for file_path, device_data in _read_dump(self._dump_dir):
|
|
157
|
+
if _match_query(query, device_data):
|
|
158
|
+
ret.append(DeviceNb(self, NetboxDTO(**device_data), region=file_path.parent.name))
|
|
159
|
+
return ret
|
|
160
|
+
|
|
161
|
+
def get_device(self, obj_id, preload_neighbors=False, use_mesh=None, **kwargs) -> "DeviceView":
|
|
162
|
+
for file_path, device_data in _read_dump(self._dump_dir):
|
|
163
|
+
if device_data["name"] == obj_id:
|
|
164
|
+
return DeviceNb(self, NetboxDTO(**device_data), region=file_path.parent.name)
|
|
165
|
+
|
|
166
|
+
def flush_perf(self):
|
|
167
|
+
pass
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def _read_dump(dump_dir):
|
|
171
|
+
for (dirpath, _dirnames, filenames) in os.walk(dump_dir):
|
|
172
|
+
for filename in filenames:
|
|
173
|
+
if filename != "devices.csv":
|
|
174
|
+
continue
|
|
175
|
+
with open(os.path.join(dirpath, filename)) as fh:
|
|
176
|
+
file_path = pathlib.Path(os.path.join(dirpath, filename))
|
|
177
|
+
for device_data in csv.DictReader(fh):
|
|
178
|
+
yield file_path, device_data
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def _match_query(query, device_data) -> bool:
|
|
182
|
+
for subquery in query.globs:
|
|
183
|
+
matches = []
|
|
184
|
+
for field_filter in subquery.split("@"):
|
|
185
|
+
if "=" in field_filter:
|
|
186
|
+
field, value = field_filter.split("=")
|
|
187
|
+
field = field.strip()
|
|
188
|
+
value = value.strip()
|
|
189
|
+
op = operator.eq
|
|
190
|
+
else:
|
|
191
|
+
field = "name"
|
|
192
|
+
value = field_filter.strip()
|
|
193
|
+
op = operator.contains
|
|
194
|
+
if field in device_data and op(device_data[field], value):
|
|
195
|
+
matches.append(True)
|
|
196
|
+
else:
|
|
197
|
+
matches.append(False)
|
|
198
|
+
if all(matches):
|
|
199
|
+
return True
|
|
200
|
+
return False
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def _vendor_to_hw(vendor):
|
|
204
|
+
hw = HardwareView(
|
|
205
|
+
{
|
|
206
|
+
"cisco": "Cisco",
|
|
207
|
+
"catalyst": "Cisco Catalyst",
|
|
208
|
+
"nexus": "Cisco Nexus",
|
|
209
|
+
"huawei": "Huawei",
|
|
210
|
+
"juniper": "Juniper",
|
|
211
|
+
"arista": "Arista",
|
|
212
|
+
"pc": "PC",
|
|
213
|
+
"nokia": "Nokia",
|
|
214
|
+
"aruba": "Aruba",
|
|
215
|
+
"routeros": "RouterOS",
|
|
216
|
+
"ribbon": "Ribbon",
|
|
217
|
+
}.get(vendor.lower(), vendor),
|
|
218
|
+
None,
|
|
219
|
+
)
|
|
220
|
+
return hw
|
annet_nbexport/main.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import asyncio
|
|
5
|
+
import os
|
|
6
|
+
import typing
|
|
7
|
+
|
|
8
|
+
import aiohttp
|
|
9
|
+
import yarl
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
async def download(url: yarl.URL, dest: typing.BinaryIO):
|
|
13
|
+
async with aiohttp.ClientSession() as session:
|
|
14
|
+
async with session.get(url) as resp:
|
|
15
|
+
async for b in resp.content.iter_any():
|
|
16
|
+
dest.write(b)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
async def download_devices(url: yarl.URL, region: str):
|
|
20
|
+
basedir = os.path.dirname(__file__)
|
|
21
|
+
regiondir = os.path.join(basedir, "data", region)
|
|
22
|
+
os.makedirs(regiondir, exist_ok=True)
|
|
23
|
+
with open(os.path.join(regiondir, "devices.csv"), "wb") as fh:
|
|
24
|
+
await download(url.with_path("/dcim/devices/").with_query("export"), fh)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
async def amain():
|
|
28
|
+
p = argparse.ArgumentParser()
|
|
29
|
+
p.add_argument("--region-name", default=None, help="The name of the region to download cvs to")
|
|
30
|
+
p.add_argument("netbox_url", type=yarl.URL, help="The URL to access netbox API")
|
|
31
|
+
args = p.parse_args()
|
|
32
|
+
|
|
33
|
+
if args.region_name is None:
|
|
34
|
+
region_name = args.netbox_url.host
|
|
35
|
+
else:
|
|
36
|
+
region_name = args.region_name
|
|
37
|
+
|
|
38
|
+
await download_devices(args.netbox_url, region_name)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def main():
|
|
42
|
+
asyncio.run(amain())
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
if __name__ == "__main__":
|
|
46
|
+
main()
|