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
annet/cli_args.py
ADDED
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
# pylint: disable=too-many-ancestors
|
|
2
|
+
|
|
3
|
+
import abc
|
|
4
|
+
import argparse
|
|
5
|
+
import enum
|
|
6
|
+
import os
|
|
7
|
+
|
|
8
|
+
from valkit.common import valid_string_list
|
|
9
|
+
|
|
10
|
+
from annet.argparse import Arg, ArgGroup, DefaultFromEnv
|
|
11
|
+
from annet.hardware import hardware_connector
|
|
12
|
+
from annet.storage import Query, storage_connector
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# ====
|
|
16
|
+
def valid_vendor(vendor):
|
|
17
|
+
hw_provider = hardware_connector.get()
|
|
18
|
+
hw = hw_provider.vendor_to_hw(vendor)
|
|
19
|
+
if hw:
|
|
20
|
+
return hw.vendor
|
|
21
|
+
return ""
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def convert_to_none(arg):
|
|
25
|
+
return None if arg == "-" else arg
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def valid_config_source(value):
|
|
29
|
+
if value not in ["cfglister", "running", "empty", "-"] and not os.path.exists(value):
|
|
30
|
+
raise ValueError("No such file or directory %r" % value)
|
|
31
|
+
return value
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def valid_range(value: str):
|
|
35
|
+
if value.isdigit():
|
|
36
|
+
return slice(0, int(value))
|
|
37
|
+
elif ":" in value:
|
|
38
|
+
start_str, stop_str = value.split(":", 1)
|
|
39
|
+
if not stop_str:
|
|
40
|
+
stop = None
|
|
41
|
+
else:
|
|
42
|
+
stop = int(stop_str)
|
|
43
|
+
return slice(int(start_str), stop)
|
|
44
|
+
|
|
45
|
+
raise ValueError("Invalid range: %s" % value)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def opt_query_factory(**kwargs):
|
|
49
|
+
return Arg(
|
|
50
|
+
"query",
|
|
51
|
+
help="Запрос, определяющий список устройств. Принимается fqdn, rackcode, глоб"
|
|
52
|
+
" или путь к файлу со списком запросов (нужно писать @path/to/file)",
|
|
53
|
+
**kwargs,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
# ====
|
|
58
|
+
opt_query = opt_query_factory(nargs="+")
|
|
59
|
+
|
|
60
|
+
opt_query_optional = opt_query_factory(nargs="*", default=[])
|
|
61
|
+
|
|
62
|
+
opt_dest = Arg(
|
|
63
|
+
"--dest", type=convert_to_none,
|
|
64
|
+
help="Файл или каталог для вывода сгенерированных данных"
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
opt_expand_path = Arg(
|
|
68
|
+
"--expand-path", default=False,
|
|
69
|
+
help="Разворачивать пути entire-генераторов при записи их на файловую систему"
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
opt_old = Arg(
|
|
73
|
+
"old",
|
|
74
|
+
help="Файл со старым конфигом (или каталог с пачкой)"
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
opt_new = Arg(
|
|
78
|
+
"new",
|
|
79
|
+
help="Файл с новым конфигом (или каталог с пачкой)"
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
opt_hw = Arg(
|
|
83
|
+
"--hw", default="", type=valid_vendor,
|
|
84
|
+
help="Производитель устройства (например Huawei или Cisco) или полное название модели. Если его нет - пытаемся его задетектить"
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
opt_indent = Arg(
|
|
88
|
+
"--indent", default=" ",
|
|
89
|
+
help="Отступ при форматировании блоков"
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
opt_allowed_gens = Arg(
|
|
93
|
+
"-g", "--allowed-gens", type=valid_string_list,
|
|
94
|
+
help="Список классов генераторов через запятую, которые нужно запустить"
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
opt_excluded_gens = Arg(
|
|
98
|
+
"-G", "--excluded-gens", type=valid_string_list,
|
|
99
|
+
help="Список классов генераторов через запятую, запуск которых следует исключить"
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
opt_force_enabled = Arg(
|
|
103
|
+
"--force-enabled", type=valid_string_list,
|
|
104
|
+
help="Список классов генераторов через запятую, которые не нужно считать DISABLED даже при наличии тега"
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
opt_generators_context = Arg(
|
|
108
|
+
"--generators_context", type=str, default=None,
|
|
109
|
+
help=argparse.SUPPRESS
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
opt_no_acl = Arg(
|
|
113
|
+
"--no-acl", default=False,
|
|
114
|
+
help="Отключение ACL при генерации"
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
opt_no_acl_exclusive = Arg(
|
|
118
|
+
"--no-acl-exclusive", default=False,
|
|
119
|
+
help="Проверяем что ACL выполненных генераторов не пересекаются"
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
opt_acl_safe = Arg(
|
|
123
|
+
"--acl-safe", default=False,
|
|
124
|
+
help="Использовать более строгий safe acl для фильтрации результата генерации"
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
opt_show_rules = Arg(
|
|
128
|
+
"--show-rules", default=False,
|
|
129
|
+
help="Показывать правила rulebook при выводе диффа"
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
opt_tolerate_fails = Arg(
|
|
133
|
+
"--tolerate-fails", default=False,
|
|
134
|
+
help="Рапортовать об ошибках без остановки генерации"
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
# При параллельном запуске и включённом --tolerate-fails код возврата
|
|
138
|
+
# всегда нулевой. Это не позволяет нам легко понять, прошла ли генерация
|
|
139
|
+
# успешно для всех устройств. С этим флажком код будет ненулевой, если
|
|
140
|
+
# генерация упала хотя бы для одного устройства. А в хелпе эту переменную
|
|
141
|
+
# не выводим, там и так не протолкнуться от флагов.
|
|
142
|
+
opt_strict_exit_code = Arg(
|
|
143
|
+
"--strict-exit-code", default=False,
|
|
144
|
+
help=argparse.SUPPRESS,
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
opt_required_packages_check = Arg(
|
|
148
|
+
"--required-packages-check", default=False,
|
|
149
|
+
help="Включить проверку наличия установленных deb-пакетов для Entire-генераторов"
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
opt_profile = Arg(
|
|
153
|
+
"--profile", default=False,
|
|
154
|
+
help="Показать в stderr время, затраченное на работу генераторов и обращениям к RackTables"
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
opt_parallel = Arg(
|
|
159
|
+
"-P", "--parallel", type=int, default=1,
|
|
160
|
+
help="Количество одновременных потоков генерирования"
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
opt_max_tasks = Arg(
|
|
164
|
+
"--max-tasks", type=int, default=None,
|
|
165
|
+
help="Рестартовать воркеры каждые N устройств, для сброса кеша и ограничения потребления памяти"
|
|
166
|
+
"По умолчанию - не рестартовать"
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
opt_annotate = Arg(
|
|
170
|
+
"--annotate", default=False,
|
|
171
|
+
help="Добавить к сгенерированному конфигу комментарии о том откуда строчка взялась"
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
opt_config = Arg(
|
|
175
|
+
"--config", default="running", type=valid_config_source,
|
|
176
|
+
help="'cfglister', 'running', 'empty', путь к файлу конфига, "
|
|
177
|
+
"каталогу с файлами конфига в формате <hostname>.cfg "
|
|
178
|
+
"или '-' (stdin)"
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
opt_clear = Arg(
|
|
182
|
+
"--clear", default=False,
|
|
183
|
+
help="Используя acl вычищает команды относящиеся к данному генератору"
|
|
184
|
+
"аналогично использованию return в самом начале генератора"
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
opt_filter_acl = Arg(
|
|
188
|
+
"--filter-acl", default="",
|
|
189
|
+
help="путь к файлу с дополнительным фильтрующим acl, или '-' (stdin)"
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
opt_filter_ifaces = Arg(
|
|
193
|
+
"-i", "--filter-ifaces", default=[], type=valid_string_list,
|
|
194
|
+
help="Генерирует filter-acl по имени интерфейса. "
|
|
195
|
+
"Принимает регекспы, через запятую: '-i 10GE,100GE'. "
|
|
196
|
+
"По-умолчанию добавляет '.*' к концу каждого. "
|
|
197
|
+
"Для указания имени точно, следует добавлять '$': '-i ae0$'. "
|
|
198
|
+
"Если filter-acl передан напрямую, данная опция игнорируется."
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
opt_filter_peers = Arg(
|
|
202
|
+
"-fp", "--filter-peers", default=[], type=valid_string_list,
|
|
203
|
+
help="Генерирует filter-acl по адресу/имени группы/дескрипшену пира."
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
opt_filter_policies = Arg(
|
|
207
|
+
"-frp", "--filter-policies", default=[], type=valid_string_list,
|
|
208
|
+
help="Генерирует filter-acl по названию политик, название должно строго соответствовать, частичные имена не пройдут"
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
opt_ask_pass = Arg(
|
|
212
|
+
"--ask-pass", default=False,
|
|
213
|
+
help="Спросить пароль на подключение"
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
opt_no_ask_deploy = Arg(
|
|
217
|
+
"--no-ask-deploy", default=False,
|
|
218
|
+
help="Не подтвеждать команды перед выполнением"
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
opt_no_progress = Arg(
|
|
222
|
+
"--no-progress", default=False,
|
|
223
|
+
help="Выключить графику прогресс баров комокутора"
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
opt_log_json = Arg(
|
|
227
|
+
"--log-json", default=False,
|
|
228
|
+
help="Логгировать в формате json (default: plain text)"
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
opt_log_dest = Arg(
|
|
232
|
+
"--log-dest", default="deploy/",
|
|
233
|
+
help="Логгировать в указанный файл/директорию, или в stdout, если указать '-'"
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
opt_log_nogroup = Arg(
|
|
237
|
+
"--log-nogroup", default=False,
|
|
238
|
+
help="Не создавать в директории LOG-DEST поддиректории DATE_TIME/"
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
opt_max_slots = Arg(
|
|
242
|
+
"--max-slots", default=30, type=int,
|
|
243
|
+
help="Количество одновременно обрабатываемых asyncio устройств"
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
opt_hosts_range = Arg(
|
|
247
|
+
"--hosts-range", type=valid_range,
|
|
248
|
+
help="Обработать только указанный диапазон хостов. 10 - первые 10. 10:20 - хосты с 10-го по 20-ый"
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
opt_add_comments = Arg(
|
|
252
|
+
"--add-comments", default=False,
|
|
253
|
+
help="Добавлять комменты подтверждения для rbprocess"
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
opt_no_label = Arg(
|
|
257
|
+
"--no-label", default=False,
|
|
258
|
+
help="Убрать лейбл с именем файла из вывода"
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
opt_no_color = Arg(
|
|
262
|
+
"--no-color", default=False,
|
|
263
|
+
help="Не делать ANSI-раскраску вывода (при --dest включён)"
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
opt_no_check_diff = Arg(
|
|
267
|
+
"--no-check-diff", default=False,
|
|
268
|
+
help="не запрашивать дифф после деплоя"
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
opt_dont_commit = Arg(
|
|
272
|
+
"--dont-commit", default=False,
|
|
273
|
+
help="не добавлять команду commit во время деплоя"
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
opt_rollback = Arg(
|
|
277
|
+
"--rollback", default=False,
|
|
278
|
+
help="предложить откат после деплоя где это возможно"
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
opt_fail_on_empty_config = Arg(
|
|
282
|
+
"--fail-on-empty-config", default=False,
|
|
283
|
+
help=argparse.SUPPRESS,
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
opt_show_generators_format = Arg(
|
|
288
|
+
"--format", default="text", choices=["text", "json"],
|
|
289
|
+
help="Формат выдачи"
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
class EntireReloadFlag(enum.Enum):
|
|
294
|
+
no = "no"
|
|
295
|
+
yes = "yes"
|
|
296
|
+
force = "force"
|
|
297
|
+
|
|
298
|
+
def __bool__(self):
|
|
299
|
+
return self is not self.no
|
|
300
|
+
|
|
301
|
+
def __str__(self):
|
|
302
|
+
return str(self.value)
|
|
303
|
+
|
|
304
|
+
__repr__ = __str__
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
opt_entire_reload = Arg(
|
|
308
|
+
"--entire-reload",
|
|
309
|
+
type=EntireReloadFlag,
|
|
310
|
+
default=EntireReloadFlag.yes,
|
|
311
|
+
choices=list(EntireReloadFlag),
|
|
312
|
+
const=EntireReloadFlag.yes,
|
|
313
|
+
nargs="?",
|
|
314
|
+
help="Выполнить reload() при deploy'e entire генераторов. "
|
|
315
|
+
"no/yes/force - нет/только если файл изменился/даже если не изменился"
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
opt_show_hosts_progress = Arg(
|
|
319
|
+
"--show-hosts-progress", default=False,
|
|
320
|
+
help="Показывать проценты выполнения по хостам"
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
opt_no_collapse = Arg(
|
|
324
|
+
"--no-collapse", default=False,
|
|
325
|
+
help="Не схлопывать одинаковые diff для группы устройств (при --dest включён)"
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
opt_fails_only = Arg(
|
|
329
|
+
"--fails-only", default=False,
|
|
330
|
+
help="Показать только устройства с ошибками"
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
opt_connect_timeout = Arg(
|
|
334
|
+
"--connect-timeout", default=DefaultFromEnv("ANN_CONNECT_TIMEOUT", "20.0"), type=float,
|
|
335
|
+
help="Таймаут на подключение к устройству в секундах."
|
|
336
|
+
" Значение по-умолчанию можно задать в переменной окружения ANN_CONNECT_TIMEOUT"
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
opt_selected_context_name = Arg(
|
|
340
|
+
"context-name", type=str, help="Имя контекста в файле конфигурации"
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
# ====
|
|
345
|
+
class CacheOptions(ArgGroup):
|
|
346
|
+
no_mesh = False
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
class QueryOptionsBase(CacheOptions):
|
|
350
|
+
@property
|
|
351
|
+
@abc.abstractmethod
|
|
352
|
+
def query(self) -> "Query":
|
|
353
|
+
pass
|
|
354
|
+
|
|
355
|
+
@property
|
|
356
|
+
@abc.abstractmethod
|
|
357
|
+
def hosts_range(self):
|
|
358
|
+
pass
|
|
359
|
+
|
|
360
|
+
def __init__(self, *args, **kwargs):
|
|
361
|
+
super().__init__(*args, **kwargs)
|
|
362
|
+
if not isinstance(self.query, Query):
|
|
363
|
+
query_type = storage_connector.get().query()
|
|
364
|
+
self.query = query_type.new(self.query, hosts_range=self.hosts_range)
|
|
365
|
+
|
|
366
|
+
def validate_stdin(self, arg, val, **kwargs):
|
|
367
|
+
if "storage" in kwargs and arg == "config":
|
|
368
|
+
storage = kwargs["storage"]
|
|
369
|
+
if len(storage.resolve_object_ids_by_query(self.query)) > 1:
|
|
370
|
+
raise ValueError("stdin config can not be used with multiple devices")
|
|
371
|
+
super().validate_stdin(arg, val, **kwargs)
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
class QueryOptions(QueryOptionsBase):
|
|
375
|
+
query = opt_query
|
|
376
|
+
hosts_range = opt_hosts_range
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
class QueryOptionsOptional(QueryOptionsBase):
|
|
380
|
+
query = opt_query_optional
|
|
381
|
+
hosts_range = opt_hosts_range
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
class ParallelOptions(ArgGroup):
|
|
385
|
+
parallel = opt_parallel
|
|
386
|
+
max_tasks = opt_max_tasks
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
class GenSelectOptions(ArgGroup):
|
|
390
|
+
allowed_gens = opt_allowed_gens
|
|
391
|
+
excluded_gens = opt_excluded_gens
|
|
392
|
+
force_enabled = opt_force_enabled
|
|
393
|
+
generators_context = opt_generators_context
|
|
394
|
+
ignore_disabled = False
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
class GenOptions(QueryOptions, GenSelectOptions, CacheOptions, ParallelOptions):
|
|
398
|
+
no_acl = opt_no_acl
|
|
399
|
+
no_acl_exclusive = opt_no_acl_exclusive
|
|
400
|
+
acl_safe = opt_acl_safe
|
|
401
|
+
filter_acl = opt_filter_acl
|
|
402
|
+
filter_ifaces = opt_filter_ifaces
|
|
403
|
+
filter_peers = opt_filter_peers
|
|
404
|
+
filter_policies = opt_filter_policies
|
|
405
|
+
profile = opt_profile
|
|
406
|
+
tolerate_fails = opt_tolerate_fails
|
|
407
|
+
required_packages_check = opt_required_packages_check
|
|
408
|
+
strict_exit_code = opt_strict_exit_code
|
|
409
|
+
fail_on_empty_config = opt_fail_on_empty_config
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
class ComocutorOptions(ArgGroup):
|
|
413
|
+
ask_pass = opt_ask_pass
|
|
414
|
+
max_slots = opt_max_slots
|
|
415
|
+
no_progress = opt_no_progress
|
|
416
|
+
connect_timeout = opt_connect_timeout
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
class CliLoggingOptions(ArgGroup):
|
|
420
|
+
log_json = opt_log_json
|
|
421
|
+
log_dest = opt_log_dest
|
|
422
|
+
log_nogroup = opt_log_nogroup
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
class DeviceCliOptions(ComocutorOptions, CliLoggingOptions):
|
|
426
|
+
no_ask_deploy = opt_no_ask_deploy
|
|
427
|
+
dont_commit = opt_dont_commit
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
class FileOutOptions(ArgGroup):
|
|
431
|
+
dest = opt_dest
|
|
432
|
+
expand_path = opt_expand_path
|
|
433
|
+
no_label = opt_no_label
|
|
434
|
+
no_color = opt_no_color
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
class DiffOptions(GenOptions, ComocutorOptions):
|
|
438
|
+
clear = opt_clear
|
|
439
|
+
config = opt_config
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
class FileInputOptions(ArgGroup):
|
|
443
|
+
old = opt_old
|
|
444
|
+
new = opt_new
|
|
445
|
+
hw = opt_hw
|
|
446
|
+
fails_only = opt_fails_only
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
class PatchOptions(DiffOptions):
|
|
450
|
+
add_comments = opt_add_comments
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
class DeployOptions(PatchOptions, DeviceCliOptions):
|
|
454
|
+
no_check_diff = opt_no_check_diff
|
|
455
|
+
entire_reload = opt_entire_reload
|
|
456
|
+
rollback = opt_rollback
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
class ShowGenOptions(GenOptions, FileOutOptions):
|
|
460
|
+
indent = opt_indent
|
|
461
|
+
annotate = opt_annotate
|
|
462
|
+
show_hosts_progress = opt_show_hosts_progress
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
class ShowDiffOptions(DiffOptions, FileOutOptions):
|
|
466
|
+
indent = opt_indent
|
|
467
|
+
show_rules = opt_show_rules
|
|
468
|
+
no_collapse = opt_no_collapse
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
class ShowPatchOptions(PatchOptions, FileOutOptions):
|
|
472
|
+
indent = opt_indent
|
|
473
|
+
show_hosts_progress = opt_show_hosts_progress
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
class FileDiffOptions(FileInputOptions, FileOutOptions, ParallelOptions):
|
|
477
|
+
indent = opt_indent
|
|
478
|
+
show_rules = opt_show_rules
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
class FilePatchOptions(FileInputOptions, FileOutOptions, ParallelOptions):
|
|
482
|
+
indent = opt_indent
|
|
483
|
+
add_comments = opt_add_comments
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
class ShowGeneratorsOptions(QueryOptionsOptional, GenSelectOptions):
|
|
487
|
+
format = opt_show_generators_format
|
|
488
|
+
acl_safe = opt_acl_safe
|
|
489
|
+
ignore_disabled = False
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
class SelectContext(ArgGroup):
|
|
493
|
+
context_name = opt_selected_context_name
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
connection:
|
|
2
|
+
default:
|
|
3
|
+
login: ~
|
|
4
|
+
passwords: ~
|
|
5
|
+
enable_ssh_conf: false
|
|
6
|
+
no_nocauth: false
|
|
7
|
+
ssh_forward_agent: ~
|
|
8
|
+
tunnel: ~
|
|
9
|
+
generators:
|
|
10
|
+
default:
|
|
11
|
+
- annet_generators.example
|
|
12
|
+
|
|
13
|
+
context:
|
|
14
|
+
default:
|
|
15
|
+
connection: default
|
|
16
|
+
generators: default
|
|
17
|
+
|
|
18
|
+
selected_context: default
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
version: 1
|
|
2
|
+
disable_existing_loggers: false
|
|
3
|
+
|
|
4
|
+
formatters:
|
|
5
|
+
standard:
|
|
6
|
+
(): contextlog.SmartFormatter
|
|
7
|
+
style: "{"
|
|
8
|
+
datefmt: "%H:%M:%S"
|
|
9
|
+
format: "[{asctime}] {log_color}{levelname:>7}{reset} {processName} - {pathname}:{lineno} - {message} -- {cyan}{_extra}{reset}"
|
|
10
|
+
host_progress:
|
|
11
|
+
(): contextlog.SmartFormatter
|
|
12
|
+
style: "{"
|
|
13
|
+
datefmt: "%H:%M:%S"
|
|
14
|
+
format: "[{asctime}] {cyan}[{perc:>3}%]{reset} [{status_color}{bold}{status:^6}{reset}]: {status_color}{fqdn}{reset} - {message} -- {cyan}{_extra}{reset}"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
handlers:
|
|
18
|
+
console:
|
|
19
|
+
level: DEBUG
|
|
20
|
+
class: logging.StreamHandler
|
|
21
|
+
formatter: standard
|
|
22
|
+
progress:
|
|
23
|
+
level: DEBUG
|
|
24
|
+
class: logging.StreamHandler
|
|
25
|
+
formatter: host_progress
|
|
26
|
+
|
|
27
|
+
root:
|
|
28
|
+
level: INFO
|
|
29
|
+
handlers:
|
|
30
|
+
- console
|
|
31
|
+
|
|
32
|
+
loggers:
|
|
33
|
+
progress:
|
|
34
|
+
level: INFO
|
|
35
|
+
propagate: False
|
|
36
|
+
handlers:
|
|
37
|
+
- progress
|
|
38
|
+
rtapi:
|
|
39
|
+
level: INFO
|
annet/connectors.py
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from abc import ABC
|
|
3
|
+
from functools import cached_property
|
|
4
|
+
from importlib.metadata import entry_points
|
|
5
|
+
from typing import Generic, Optional, Type, TypeVar
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
T = TypeVar("T")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Connector(ABC, Generic[T]):
|
|
12
|
+
name: str
|
|
13
|
+
ep_name: str
|
|
14
|
+
ep_group: str = "annet.connectors"
|
|
15
|
+
_cls: Optional[Type[T]] = None
|
|
16
|
+
|
|
17
|
+
def _get_default(self) -> Type[T]:
|
|
18
|
+
raise RuntimeError(f"{self.name} is not set")
|
|
19
|
+
|
|
20
|
+
@cached_property
|
|
21
|
+
def _entry_point(self) -> Optional[Type[T]]:
|
|
22
|
+
return load_entry_point(self.ep_group, self.ep_name)
|
|
23
|
+
|
|
24
|
+
def get(self, *args, **kwargs) -> T:
|
|
25
|
+
if self._cls is not None:
|
|
26
|
+
res = self._cls
|
|
27
|
+
else:
|
|
28
|
+
res = self._entry_point or self._get_default()
|
|
29
|
+
return res(*args, **kwargs)
|
|
30
|
+
|
|
31
|
+
def set(self, cls: Type[T]):
|
|
32
|
+
if self._cls is not None:
|
|
33
|
+
raise RuntimeError(f"Cannot reinitialize value of {self.name}")
|
|
34
|
+
self._cls = cls
|
|
35
|
+
|
|
36
|
+
def is_default(self) -> bool:
|
|
37
|
+
return self._cls is self._entry_point is None
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class CachedConnector(Connector[T], ABC):
|
|
41
|
+
_cache: Optional[T] = None
|
|
42
|
+
|
|
43
|
+
def get(self, *args, **kwargs) -> T:
|
|
44
|
+
assert not (args or kwargs), "Arguments forwarding is not allowed for cached connectors"
|
|
45
|
+
if self._cache is None:
|
|
46
|
+
self._cache = super().get()
|
|
47
|
+
return self._cache
|
|
48
|
+
|
|
49
|
+
def set(self, cls: Type[T]):
|
|
50
|
+
super().set(cls)
|
|
51
|
+
self._cache = None
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def load_entry_point(group: str, name: str):
|
|
55
|
+
if sys.version_info < (3, 10):
|
|
56
|
+
ep = [item for item in entry_points().get(group, []) if item.name == name]
|
|
57
|
+
else:
|
|
58
|
+
ep = entry_points(group=group, name=name) # pylint: disable=unexpected-keyword-arg
|
|
59
|
+
if not ep:
|
|
60
|
+
return None
|
|
61
|
+
if len(ep) > 1:
|
|
62
|
+
raise RuntimeError(f"Multiple entry points with the same {group=} and {name=}: {[item.value for item in ep]}")
|
|
63
|
+
for item in ep:
|
|
64
|
+
return item.load()
|