Skip to content

Commit a4e7f89

Browse files
Refactor(cv_deploy): Add cv_deploy schema files (#6679)
Co-authored-by: Guillaume Mulocher <gmulocher@arista.com>
1 parent a58d9ea commit a4e7f89

17 files changed

Lines changed: 820 additions & 18 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ uv.lock
5454
# Hash files created when running pyavd from source
5555
python-avd/pyavd/*/j2templates/.hash
5656
python-avd/pyavd/*/schema/schema_fragments/.hash
57+
schemas/*/schema_fragments/.hash
5758

5859
# gz of schema store
5960
python-avd/pyavd/_schema/schemas.json.gz

.pre-commit-config.yaml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ repos:
5151
(ansible_collections/arista/avd/roles/.*/(handlers|tasks)/.*\.yml
5252
|python-avd/pyavd/_(eos_cli_config_gen|eos_designs)/schema/.*\.yml
5353
|python-avd/pyavd/_eos_designs/eos_designs_facts/schema/.*\.yml
54+
|schemas/.*/.*\.yml
5455
)
5556
5657
args:
@@ -267,7 +268,7 @@ repos:
267268
name: Build AVD schemas and documentation.
268269
entry: sh -c 'exec python-avd/scripts/build_schemas.py'
269270
language: python
270-
files: python-avd/pyavd/[a-z_]+/schema
271+
files: (python-avd/pyavd/[a-z_]+/schema|schemas/[a-z_]+)
271272
pass_filenames: false
272273
additional_dependencies: ['deepmerge>=1.1.0', 'PyYAML>=6.0.0', 'pydantic>=2.3.0', 'jsonschema-rs>=0.24', 'referencing>=0.35.0', 'ruff==0.7.2']
273274

@@ -277,13 +278,15 @@ repos:
277278
language: python
278279
args:
279280
- --root-path
280-
- "ansible_collections/arista/avd"
281+
- "."
281282
- --table-files
282283
- "ansible_collections/arista/avd/roles/eos_cli_config_gen/docs/tables/*.md"
283284
- "ansible_collections/arista/avd/roles/eos_designs/docs/tables/*.md"
285+
- "schemas/cv_deploy/docs/tables/*.md"
284286
- --global-docs
285287
- "ansible_collections/arista/avd/roles/eos_cli_config_gen/docs/*.md"
286288
- "ansible_collections/arista/avd/roles/eos_designs/docs/*.md"
289+
- "ansible_collections/arista/avd/roles/cv_deploy/README.md"
287290
- --ignore-files
288291
- ansible_collections/arista/avd/roles/eos_cli_config_gen/docs/tables/role-input-validation.md
289292
- ansible_collections/arista/avd/roles/eos_cli_config_gen/docs/tables/service-unsupported-transceiver.md
@@ -292,6 +295,9 @@ repos:
292295
- ansible_collections/arista/avd/roles/eos_designs/docs/tables/role-input-validation.md
293296
- ansible_collections/arista/avd/roles/eos_designs/docs/tables/unsupported-transceiver.md
294297
- ansible_collections/arista/avd/roles/eos_designs/docs/tables/removed-keys.md
298+
- schemas/cv_deploy/docs/tables/metadata.md
299+
# TODO: Remove once cv_deploy schema integration is complete.
300+
- schemas/cv_deploy/docs/tables/cv_deploy.md
295301
pass_filenames: false
296302

297303
- id: sort-hosts-yml

pylintrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ ignore-paths=
88
python-avd/pyavd/_eos_cli_config_gen/schema/__init__.py,
99
python-avd/pyavd/_eos_designs/schema/__init__.py,
1010
python-avd/pyavd/_eos_designs/eos_designs_facts/schema/protocol.py,
11+
python-avd/pyavd/_cv/schema/__init__.py,
1112
python-avd/tests/pyavd/schema/data_merging_schema_class.py,
1213

1314
[MESSAGES CONTROL]
Lines changed: 287 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
# Copyright (c) 2026 Arista Networks, Inc.
2+
# Use of this source code is governed by the Apache License 2.0
3+
# that can be found in the LICENSE file.
4+
5+
from __future__ import annotations
6+
7+
from typing import TYPE_CHECKING, ClassVar
8+
9+
from pyavd._eos_cli_config_gen.schema import EosCliConfigGen
10+
from pyavd._schema.models.avd_list import AvdList
11+
from pyavd._schema.models.avd_model import AvdModel
12+
13+
if TYPE_CHECKING:
14+
from pyavd._utils import Undefined, UndefinedType
15+
16+
17+
class CvDeploy(AvdModel):
18+
"""Subclass of AvdModel."""
19+
20+
class CvDeviceTagsItem(AvdModel):
21+
"""Subclass of AvdModel."""
22+
23+
_fields: ClassVar[dict] = {"name": {"type": str}, "value": {"type": str}}
24+
name: str
25+
value: str
26+
27+
if TYPE_CHECKING:
28+
29+
def __init__(self, *, name: str | UndefinedType = Undefined, value: str | UndefinedType = Undefined) -> None:
30+
"""
31+
CvDeviceTagsItem.
32+
33+
Subclass of AvdModel.
34+
35+
Args:
36+
name: name
37+
value: value
38+
39+
"""
40+
41+
class CvDeviceTags(AvdList[CvDeviceTagsItem]):
42+
"""Subclass of AvdList with `CvDeviceTagsItem` items."""
43+
44+
CvDeviceTags._item_type = CvDeviceTagsItem
45+
46+
class CvInterfaceTagsItem(AvdModel):
47+
"""Subclass of AvdModel."""
48+
49+
class TagsItem(AvdModel):
50+
"""Subclass of AvdModel."""
51+
52+
_fields: ClassVar[dict] = {"name": {"type": str}, "value": {"type": str}}
53+
name: str
54+
value: str
55+
56+
if TYPE_CHECKING:
57+
58+
def __init__(self, *, name: str | UndefinedType = Undefined, value: str | UndefinedType = Undefined) -> None:
59+
"""
60+
TagsItem.
61+
62+
Subclass of AvdModel.
63+
64+
Args:
65+
name: name
66+
value: value
67+
68+
"""
69+
70+
class Tags(AvdList[TagsItem]):
71+
"""Subclass of AvdList with `TagsItem` items."""
72+
73+
Tags._item_type = TagsItem
74+
75+
_fields: ClassVar[dict] = {"interface": {"type": str}, "tags": {"type": Tags}}
76+
interface: str
77+
tags: Tags
78+
"""Subclass of AvdList with `TagsItem` items."""
79+
80+
if TYPE_CHECKING:
81+
82+
def __init__(self, *, interface: str | UndefinedType = Undefined, tags: Tags | UndefinedType = Undefined) -> None:
83+
"""
84+
CvInterfaceTagsItem.
85+
86+
Subclass of AvdModel.
87+
88+
Args:
89+
interface: interface
90+
tags: Subclass of AvdList with `TagsItem` items.
91+
92+
"""
93+
94+
class CvInterfaceTags(AvdList[CvInterfaceTagsItem]):
95+
"""Subclass of AvdList with `CvInterfaceTagsItem` items."""
96+
97+
CvInterfaceTags._item_type = CvInterfaceTagsItem
98+
99+
class Metadata(AvdModel):
100+
"""Subclass of AvdModel."""
101+
102+
_fields: ClassVar[dict] = {
103+
"is_deployed": {"type": bool},
104+
"serial_number": {"type": str},
105+
"system_mac_address": {"type": str},
106+
"cv_tags": {"type": EosCliConfigGen.Metadata.CvTags},
107+
"cv_pathfinder": {"type": EosCliConfigGen.Metadata.CvPathfinder},
108+
}
109+
_allow_other_keys: ClassVar[bool] = True
110+
is_deployed: bool | None
111+
"""Key only used for documentation or validation purposes."""
112+
serial_number: str | None
113+
"""
114+
Serial Number of the device.
115+
Used only for documentation and deployment purposes. It is used by the
116+
'cv_deploy' role.
117+
"""
118+
system_mac_address: str | None
119+
cv_tags: EosCliConfigGen.Metadata.CvTags
120+
cv_pathfinder: EosCliConfigGen.Metadata.CvPathfinder
121+
"""Metadata used for CV Pathfinder visualization on CloudVision."""
122+
123+
if TYPE_CHECKING:
124+
125+
def __init__(
126+
self,
127+
*,
128+
is_deployed: bool | None | UndefinedType = Undefined,
129+
serial_number: str | None | UndefinedType = Undefined,
130+
system_mac_address: str | None | UndefinedType = Undefined,
131+
cv_tags: EosCliConfigGen.Metadata.CvTags | UndefinedType = Undefined,
132+
cv_pathfinder: EosCliConfigGen.Metadata.CvPathfinder | UndefinedType = Undefined,
133+
) -> None:
134+
"""
135+
Metadata.
136+
137+
Subclass of AvdModel.
138+
139+
Args:
140+
is_deployed: Key only used for documentation or validation purposes.
141+
serial_number:
142+
Serial Number of the device.
143+
Used only for documentation and deployment purposes. It is used by the
144+
'cv_deploy' role.
145+
system_mac_address: system_mac_address
146+
cv_tags: cv_tags
147+
cv_pathfinder: Metadata used for CV Pathfinder visualization on CloudVision.
148+
149+
"""
150+
151+
_fields: ClassVar[dict] = {
152+
"is_deployed": {"type": bool, "default": True},
153+
"serial_number": {"type": str},
154+
"system_mac_address": {"type": str},
155+
"cv_device_tags": {"type": CvDeviceTags},
156+
"cv_interface_tags": {"type": CvInterfaceTags},
157+
"cv_pathfinder_metadata": {"type": EosCliConfigGen.Metadata.CvPathfinder},
158+
"metadata": {"type": Metadata},
159+
}
160+
_allow_other_keys: ClassVar[bool] = True
161+
is_deployed: bool
162+
"""
163+
When set to `false`, the device will be skipped from all operations performed by the `cv_deploy`
164+
role.
165+
166+
Default value: `True`
167+
"""
168+
serial_number: str | None
169+
"""
170+
Serial number of the device used to identify the device in CloudVision.
171+
Takes precedence over
172+
`system_mac_address` and `inventory_hostname`.
173+
174+
Device identification precedence:
175+
1.
176+
`serial_number` (highest priority)
177+
2. `system_mac_address`
178+
3. `inventory_hostname` (lowest
179+
priority, used only if neither of the above is set)
180+
"""
181+
system_mac_address: str | None
182+
"""
183+
System MAC address of the device used to identify the device in CloudVision.
184+
Should match the MAC
185+
address shown in "show version" on the device.
186+
Used when `serial_number` is not set.
187+
188+
Device
189+
identification precedence:
190+
1. `serial_number` (highest priority)
191+
2. `system_mac_address`
192+
3.
193+
`inventory_hostname` (lowest priority, used only if neither of the above is set)
194+
"""
195+
cv_device_tags: CvDeviceTags
196+
"""
197+
List of CloudVision device tags to be assigned to this device.
198+
199+
Subclass of AvdList with
200+
`CvDeviceTagsItem` items.
201+
"""
202+
cv_interface_tags: CvInterfaceTags
203+
"""
204+
List of CloudVision interface tags to be assigned to interfaces on this device.
205+
206+
Subclass of AvdList
207+
with `CvInterfaceTagsItem` items.
208+
"""
209+
cv_pathfinder_metadata: EosCliConfigGen.Metadata.CvPathfinder
210+
"""Metadata used for CV Pathfinder visualization on CloudVision."""
211+
metadata: Metadata
212+
"""
213+
Metadata from the `eos_designs` role, loaded automatically from structured configs.
214+
For standalone
215+
usage without `eos_designs`, use the other `cv_deploy` schema keys instead.
216+
If both are provided,
217+
`metadata` takes precedence.
218+
219+
Subclass of AvdModel.
220+
"""
221+
222+
if TYPE_CHECKING:
223+
224+
def __init__(
225+
self,
226+
*,
227+
is_deployed: bool | UndefinedType = Undefined,
228+
serial_number: str | None | UndefinedType = Undefined,
229+
system_mac_address: str | None | UndefinedType = Undefined,
230+
cv_device_tags: CvDeviceTags | UndefinedType = Undefined,
231+
cv_interface_tags: CvInterfaceTags | UndefinedType = Undefined,
232+
cv_pathfinder_metadata: EosCliConfigGen.Metadata.CvPathfinder | UndefinedType = Undefined,
233+
metadata: Metadata | UndefinedType = Undefined,
234+
) -> None:
235+
"""
236+
CvDeploy.
237+
238+
Subclass of AvdModel.
239+
240+
Args:
241+
is_deployed:
242+
When set to `false`, the device will be skipped from all operations performed by the `cv_deploy`
243+
role.
244+
serial_number:
245+
Serial number of the device used to identify the device in CloudVision.
246+
Takes precedence over
247+
`system_mac_address` and `inventory_hostname`.
248+
249+
Device identification precedence:
250+
1.
251+
`serial_number` (highest priority)
252+
2. `system_mac_address`
253+
3. `inventory_hostname` (lowest
254+
priority, used only if neither of the above is set)
255+
system_mac_address:
256+
System MAC address of the device used to identify the device in CloudVision.
257+
Should match the MAC
258+
address shown in "show version" on the device.
259+
Used when `serial_number` is not set.
260+
261+
Device
262+
identification precedence:
263+
1. `serial_number` (highest priority)
264+
2. `system_mac_address`
265+
3.
266+
`inventory_hostname` (lowest priority, used only if neither of the above is set)
267+
cv_device_tags:
268+
List of CloudVision device tags to be assigned to this device.
269+
270+
Subclass of AvdList with
271+
`CvDeviceTagsItem` items.
272+
cv_interface_tags:
273+
List of CloudVision interface tags to be assigned to interfaces on this device.
274+
275+
Subclass of AvdList
276+
with `CvInterfaceTagsItem` items.
277+
cv_pathfinder_metadata: Metadata used for CV Pathfinder visualization on CloudVision.
278+
metadata:
279+
Metadata from the `eos_designs` role, loaded automatically from structured configs.
280+
For standalone
281+
usage without `eos_designs`, use the other `cv_deploy` schema keys instead.
282+
If both are provided,
283+
`metadata` takes precedence.
284+
285+
Subclass of AvdModel.
286+
287+
"""

python-avd/pyavd/_schema/constants.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,14 @@
1414
METASCHEMA_DIR = PYAVD_DIR.joinpath("_schema")
1515
METASCHEMA_PICKLED_SCHEMA_PATH = METASCHEMA_DIR.joinpath("avd_meta_schema.pickle")
1616

17+
CV_DEPLOY_SCHEMA_DIR = PYAVD_DIR.joinpath("_cv/schema")
18+
CV_DEPLOY_PICKLED_SCHEMA_PATH = CV_DEPLOY_SCHEMA_DIR.joinpath("cv_deploy.schema.pickle")
19+
1720
PICKLED_SCHEMAS = {
1821
"avd_meta_schema": METASCHEMA_PICKLED_SCHEMA_PATH,
1922
"eos_cli_config_gen": EOS_CLI_CONFIG_GEN_PICKLED_SCHEMA_PATH,
2023
"eos_designs": EOS_DESIGNS_PICKLED_SCHEMA_PATH,
24+
"cv_deploy": CV_DEPLOY_PICKLED_SCHEMA_PATH,
2125
}
2226

2327
ACCEPTED_COERCION_MAP = {
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# Copyright (c) 2025-2026 Arista Networks, Inc.
22
# Use of this source code is governed by the Apache License 2.0
33
# that can be found in the LICENSE file.
4+
from pyavd._cv.schema import CvDeploy as CVDeploy
45
from pyavd._eos_cli_config_gen.schema import EosCliConfigGen as EOSConfig
56
from pyavd._eos_designs.schema import EosDesigns as AVDDesign
67

7-
__all__ = ["AVDDesign", "EOSConfig"]
8+
__all__ = ["AVDDesign", "CVDeploy", "EOSConfig"]

python-avd/pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ include-package-data = true
7171
[tool.setuptools.package-data]
7272
"*" = [
7373
"avd_meta_schema.pickle",
74+
"cv_deploy.schema.pickle",
7475
"eos_cli_config_gen.schema.pickle",
7576
"eos_designs.schema.pickle",
7677
"schemas.json.gz",

0 commit comments

Comments
 (0)