Skip to content

Commit 02213d1

Browse files
alexeygorbunovpre-commit-ci[bot]ClausHolbechAristacarl-baillargeon
authored
Feat(cv_deploy): Expose the details of the Workspace build validation errors (#4629)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Claus Holbech <[email protected]> Co-authored-by: Carl Baillargeon <[email protected]>
1 parent 9c89950 commit 02213d1

16 files changed

Lines changed: 1625 additions & 10 deletions

File tree

ansible_collections/arista/avd/plugins/action/cv_workflow.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
CVPathfinderMetadata,
3333
CVTimeOuts,
3434
CVWorkspace,
35+
CVWorkspaceBuildWarningsConfig,
3536
DeployToCvResult,
3637
)
3738
from pyavd._utils import get, strip_empties_from_dict
@@ -70,6 +71,14 @@
7071
"id": {"type": "str", "required": False},
7172
"requested_state": {"type": "str", "default": "built", "choices": ["pending", "built", "submitted", "abandoned", "deleted"]},
7273
"force": {"type": "bool", "default": False},
74+
"build_warnings": {
75+
"type": "dict",
76+
"options": {
77+
"enabled": {"type": "bool", "required": False, "default": True},
78+
"suppress_patterns": {"type": "list", "elements": "str", "required": False, "default": []},
79+
"suppress_portfast": {"type": "bool", "required": False, "default": False},
80+
},
81+
},
7382
},
7483
},
7584
"change_control": {
@@ -190,6 +199,11 @@ async def deploy(self, validated_args: dict, result: dict) -> dict:
190199
)
191200

192201
if work_to_do:
202+
# Pre-process workspace args to convert build_warnings to CVWorkspaceBuildWarningsConfig object.
203+
workspace_args = get(validated_args, "workspace", default={})
204+
if "build_warnings" in workspace_args:
205+
workspace_args["build_warnings"] = CVWorkspaceBuildWarningsConfig(**workspace_args["build_warnings"])
206+
193207
# Perform deployment of all objects, getting a DeployToCVResult object back.
194208
result_object = await deploy_to_cv(
195209
change_control=CVChangeControl(**get(validated_args, "change_control", default={})),
@@ -203,7 +217,7 @@ async def deploy(self, validated_args: dict, result: dict) -> dict:
203217
strict_system_mac_address=get(validated_args, "strict_system_mac_address"),
204218
strict_tags=get(validated_args, "strict_tags"),
205219
timeouts=CVTimeOuts(**get(validated_args, "timeouts", default={})),
206-
workspace=CVWorkspace(**get(validated_args, "workspace", default={})),
220+
workspace=CVWorkspace(**workspace_args),
207221
)
208222
# Errors and warnings are converted to JSON compatible strings.
209223
result_object.errors = [str(error) for error in result_object.errors]

ansible_collections/arista/avd/plugins/modules/cv_workflow.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,23 @@
154154
description: Force submit the workspace even if some devices are not actively streaming to CloudVision.
155155
type: bool
156156
default: false
157+
build_warnings:
158+
description: Configuration for Workspace build warnings handling.
159+
type: dict
160+
suboptions:
161+
enabled:
162+
description: Fetch and expose Workspace build warnings.
163+
type: bool
164+
default: true
165+
suppress_patterns:
166+
description: Arbitrary list of regex patterns used with fullmatch to suppress EOS CLI warnings.
167+
type: list
168+
elements: str
169+
default: []
170+
suppress_portfast:
171+
description: Suppress Workspace build warnings related to the usage of the `portfast` feature on switchports.
172+
type: bool
173+
default: false
157174
change_control:
158175
description: CloudVision Change Control to create for the deployment.
159176
type: dict
@@ -255,6 +272,10 @@
255272
# id: <uuid or similar>
256273
requested_state: submitted
257274
force: true
275+
build_warnings:
276+
# enabled: true
277+
suppress_patterns: [".*/32 IPv4 address is not configured on the interface.*"]
278+
# suppress_portfast: false
258279
change_control:
259280
# name:
260281
# description:

ansible_collections/arista/avd/roles/cv_deploy/README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,8 @@ By default the role will
201201
2. Push all configurations and tags.
202202
3. Unassign tags
203203
4. Build and submit the Workspace.
204-
5. Leave any created Change Control in `pending approval` state.
204+
5. Fetch and expose errors and warnings raised during the Workspace Build phase.
205+
6. Leave any created Change Control in `pending approval` state.
205206

206207
!!! warning
207208
When deploying CloudVision Tag assignments, the builtin behavior is to unassign any other tags
@@ -220,6 +221,12 @@ cv_submit_workspace: true
220221
# If set, configurations will not be validated for non-streaming devices.
221222
cv_submit_workspace_force: false
222223
224+
# Fetch and expose Workspace build warnings.
225+
# Suppress specific warnings based on pre-defined options or custom regex fullmatch pattern(s).
226+
cv_workspace_build_warnings_enabled: true
227+
cv_workspace_build_warnings_suppress_patterns: []
228+
cv_workspace_build_warnings_suppress_portfast: false
229+
223230
# Approve, start and wait for the Change Control to Complete. Otherwise the Change Control will be left in "pending approval" mode.
224231
cv_run_change_control: false
225232

ansible_collections/arista/avd/roles/cv_deploy/defaults/main/main.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ cv_submit_workspace: true
4242
# If set, configurations will not be validated for non-streaming devices.
4343
cv_submit_workspace_force: false
4444

45+
# Fetch and expose Workspace build warnings.
46+
# Suppress specific warnings based on pre-defined options or custom regex fullmatch pattern(s).
47+
cv_workspace_build_warnings_enabled: true
48+
cv_workspace_build_warnings_suppress_patterns: []
49+
cv_workspace_build_warnings_suppress_portfast: false
50+
4551
# Approve and Start Change Control. Otherwise the Change Control will be left in "pending approval" mode.
4652
cv_run_change_control: false
4753

ansible_collections/arista/avd/roles/cv_deploy/tasks/main.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@
4040
id: "{{ cv_workspace_id | arista.avd.default }}"
4141
requested_state: "{{ cv_workspace_requested_state | arista.avd.default('submitted' if cv_submit_workspace else 'built') }}"
4242
force: "{{ cv_submit_workspace_force }}"
43+
build_warnings:
44+
enabled: "{{ cv_workspace_build_warnings_enabled }}"
45+
suppress_patterns: "{{ cv_workspace_build_warnings_suppress_patterns }}"
46+
suppress_portfast: "{{ cv_workspace_build_warnings_suppress_portfast }}"
4347
change_control:
4448
name: "{{ cv_change_control_name | arista.avd.default }}"
4549
description: "{{ cv_change_control_description | arista.avd.default }}"

docs/plugins/Modules_and_action_plugins/cv_workflow.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ The `arista.avd.cv_workflow` module is an Ansible Action Plugin providing the fo
5858
| <samp>&nbsp;&nbsp;&nbsp;&nbsp;id</samp> | str | optional | None | - | Optional ID to use for the created Workspace. If there is already a workspace with the same ID, it must be in the &#39;pending&#39; state. |
5959
| <samp>&nbsp;&nbsp;&nbsp;&nbsp;requested_state</samp> | str | optional | built | Valid values:<br>- <code>pending</code><br>- <code>built</code><br>- <code>submitted</code><br>- <code>abandoned</code><br>- <code>deleted</code> | The requested state for the Workspace.<br><br>- `pending`: Leave the Workspace in pending state.<br>- `built`: Build the Workspace but do not submit.<br>- `submitted` (default): Build and submit the Workspace.<br>- `abandoned`: Build and then abandon the Workspace.<br> Used for dry-run where no changes will be committed to CloudVision.<br>- `deleted`: Build, abort and then delete the Workspace.<br> Used for dry-run where no changes will be committed to CloudVision and the temporary Workspace will be removed to avoid &#34;clutter&#34;. |
6060
| <samp>&nbsp;&nbsp;&nbsp;&nbsp;force</samp> | bool | optional | False | - | Force submit the workspace even if some devices are not actively streaming to CloudVision. |
61+
| <samp>&nbsp;&nbsp;&nbsp;&nbsp;build_warnings</samp> | dict | optional | None | - | Configuration for Workspace build warnings handling. |
62+
| <samp>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;enabled</samp> | bool | optional | True | - | Fetch and expose Workspace build warnings. |
63+
| <samp>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;suppress_patterns</samp> | list | optional | [] | - | Arbitrary list of regex patterns used with fullmatch to suppress EOS CLI warnings. |
64+
| <samp>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;suppress_portfast</samp> | bool | optional | False | - | Suppress Workspace build warnings related to the usage of the `portfast` feature on switchports. |
6165
| <samp>change_control</samp> | dict | optional | None | - | CloudVision Change Control to create for the deployment. |
6266
| <samp>&nbsp;&nbsp;&nbsp;&nbsp;name</samp> | str | optional | None | - | Optional name to use for the created Change Control. By default the name generated by CloudVision will be kept. |
6367
| <samp>&nbsp;&nbsp;&nbsp;&nbsp;description</samp> | str | optional | None | - | Optional description to use for the created Change Control. |
@@ -129,6 +133,10 @@ The `arista.avd.cv_workflow` module is an Ansible Action Plugin providing the fo
129133
# id: <uuid or similar>
130134
requested_state: submitted
131135
force: true
136+
build_warnings:
137+
# enabled: true
138+
suppress_patterns: [".*/32 IPv4 address is not configured on the interface.*"]
139+
# suppress_portfast: false
132140
change_control:
133141
# name:
134142
# description:

python-avd/pyavd/_cv/client/workspace.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313
Response,
1414
ResponseStatus,
1515
Workspace,
16+
WorkspaceBuildDetails,
17+
WorkspaceBuildDetailsKey,
18+
WorkspaceBuildDetailsServiceStub,
19+
WorkspaceBuildDetailsStreamRequest,
1620
WorkspaceConfig,
1721
WorkspaceConfigDeleteRequest,
1822
WorkspaceConfigServiceStub,
@@ -317,3 +321,36 @@ async def wait_for_workspace_state(
317321
# Use case where stream completed without getting Workspace update in the desired state
318322
msg = f"Workspace '{workspace_id}' has not reached desired state '{state}'."
319323
raise CVWorkspaceFailed(msg)
324+
325+
@GRPCRequestHandler()
326+
async def get_workspace_build_details(
327+
self: CVClientProtocol,
328+
workspace_id: str,
329+
build_id: str,
330+
time: datetime | None = None,
331+
timeout: float = DEFAULT_API_TIMEOUT,
332+
) -> list[WorkspaceBuildDetails]:
333+
"""
334+
Get Workspace Build Details using arista.workspace.v1.WorkspaceBuildDetailsService.GetAll API.
335+
336+
Parameters:
337+
workspace_id: Unique identifier the workspace.
338+
build_id: Unique identifier of the last WS build attempt.
339+
time: Timestamp from which the information is fetched. `now()` if not set.
340+
timeout: Timeout in seconds.
341+
342+
Returns:
343+
List of WorkspaceBuildDetails objects.
344+
"""
345+
request = WorkspaceBuildDetailsStreamRequest(
346+
partial_eq_filter=[
347+
WorkspaceBuildDetails(
348+
key=WorkspaceBuildDetailsKey(workspace_id=workspace_id, build_id=build_id),
349+
),
350+
],
351+
time=time,
352+
)
353+
client = WorkspaceBuildDetailsServiceStub(self._channel)
354+
responses = client.get_all(request, metadata=self._metadata, timeout=timeout)
355+
356+
return [response.value async for response in responses]
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Copyright (c) 2025-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+
EOS_CLI_WARNINGS = {
6+
# Text pattern to match EOS CLI warning regarding utilization of the spanning-tree portfast feature.
7+
"portfast": (
8+
"^! portfast should only be enabled on ports connected to a single host. Connecting hubs, concentrators, switches, bridges, etc. "
9+
"to this interface when portfast is enabled can cause temporary bridging loops. Use with CAUTION.$"
10+
)
11+
}

0 commit comments

Comments
 (0)