From 699afccefa38c38ed5c1d291ea7ab9164d353851 Mon Sep 17 00:00:00 2001 From: Doug Goldstein Date: Tue, 24 Mar 2026 08:59:22 -0500 Subject: [PATCH 1/2] fix(undersync-switch): ensure input is URL encoded Ensure that the input argument is URL encoded. Rename from vlan_group_uuid to physical_network to accurately reflect what the value is today. --- .../templates/sensor-ironic-port-delete.yaml | 2 +- .../main/undersync_switch.py | 8 +++---- .../understack_workflows/undersync/client.py | 24 +++++++++++-------- .../workflowtemplates/undersync-switch.yaml | 6 ++--- 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/charts/site-workflows/templates/sensor-ironic-port-delete.yaml b/charts/site-workflows/templates/sensor-ironic-port-delete.yaml index 453dffadd..fe881ae97 100644 --- a/charts/site-workflows/templates/sensor-ironic-port-delete.yaml +++ b/charts/site-workflows/templates/sensor-ironic-port-delete.yaml @@ -60,7 +60,7 @@ spec: # defines the parameters being replaced above arguments: parameters: - - name: vlan_group_uuid + - name: physical_network - name: force value: false - name: dry_run diff --git a/python/understack-workflows/understack_workflows/main/undersync_switch.py b/python/understack-workflows/understack_workflows/main/undersync_switch.py index 40432ef0d..b2c336c15 100644 --- a/python/understack-workflows/understack_workflows/main/undersync_switch.py +++ b/python/understack-workflows/understack_workflows/main/undersync_switch.py @@ -20,12 +20,12 @@ def call_undersync(args): try: logger.debug( "Syncing switches in vlan group %s args.dry_run=%s args.force=%s", - args.vlan_group_uuid, + args.physical_network, args.dry_run, args.force, ) return undersync.sync_devices( - args.vlan_group_uuid, + args.physical_network, dry_run=args.dry_run, force=args.force, ) @@ -40,10 +40,10 @@ def argument_parser(): description="Trigger undersync run for a set of switches.", ) parser.add_argument( - "--vlan_group_uuid", + "--physical-network", type=str, required=True, - help="UUID of Nautobot VlanGroup containing the switches to Undersync", + help="Port physical_network / Nautobot VLANGroup", ) parser.add_argument( "--force", diff --git a/python/understack-workflows/understack_workflows/undersync/client.py b/python/understack-workflows/understack_workflows/undersync/client.py index 1651bb4b7..8f0a61aa4 100644 --- a/python/understack-workflows/understack_workflows/undersync/client.py +++ b/python/understack-workflows/understack_workflows/undersync/client.py @@ -1,4 +1,5 @@ from functools import cached_property +from urllib.parse import quote import requests @@ -13,13 +14,13 @@ def __init__( self.token = auth_token self.api_url = api_url - def sync_devices(self, vlan_group_uuid: str, force=False, dry_run=False): + def sync_devices(self, physical_network: str, force=False, dry_run=False): if dry_run: - return self.dry_run(vlan_group_uuid) + return self.dry_run(physical_network) elif force: - return self.force(vlan_group_uuid) + return self.force(physical_network) else: - return self.sync(vlan_group_uuid) + return self.sync(physical_network) @cached_property def client(self): @@ -30,17 +31,20 @@ def client(self): } return session - def sync(self, uuids: str) -> requests.Response: - response = self.client.post(f"{self.api_url}/v1/vlan-group/{uuids}/sync") + def sync(self, physical_network: str) -> requests.Response: + physnet = quote(physical_network, safe="") + response = self.client.post(f"{self.api_url}/v1/vlan-group/{physnet}/sync") response.raise_for_status() return response - def dry_run(self, uuids: str) -> requests.Response: - response = self.client.post(f"{self.api_url}/v1/vlan-group/{uuids}/dry-run") + def dry_run(self, physical_network: str) -> requests.Response: + physnet = quote(physical_network, safe="") + response = self.client.post(f"{self.api_url}/v1/vlan-group/{physnet}/dry-run") response.raise_for_status() return response - def force(self, uuids: str) -> requests.Response: - response = self.client.post(f"{self.api_url}/v1/vlan-group/{uuids}/force") + def force(self, physical_network: str) -> requests.Response: + physnet = quote(physical_network, safe="") + response = self.client.post(f"{self.api_url}/v1/vlan-group/{physnet}/force") response.raise_for_status() return response diff --git a/workflows/argo-events/workflowtemplates/undersync-switch.yaml b/workflows/argo-events/workflowtemplates/undersync-switch.yaml index 3d855a5b6..92b9c28eb 100644 --- a/workflows/argo-events/workflowtemplates/undersync-switch.yaml +++ b/workflows/argo-events/workflowtemplates/undersync-switch.yaml @@ -16,8 +16,8 @@ spec: command: - undersync-switch args: - - --vlan_group_uuid - - "{{workflow.parameters.vlan_group_uuid}}" + - --physical-network + - "{{workflow.parameters.physical_network}}" - --dry-run - "{{workflow.parameters.dry_run}}" - --force @@ -37,7 +37,7 @@ spec: key: url inputs: parameters: - - name: vlan_group_uuid + - name: physical_network - name: force - name: dry_run volumes: From 4ec10f697941bb12266dd95cbdccce4fe03cf702 Mon Sep 17 00:00:00 2001 From: Doug Goldstein Date: Tue, 24 Mar 2026 09:29:25 -0500 Subject: [PATCH 2/2] fix(undersync-switch): adjust inputs and document the flow inputs Adjust the inputs to be displayed and document what they actually are and consume them from the inputs so that in the logging it shows how all the arguments were passed in. --- .../templates/sensor-ironic-port-delete.yaml | 4 +- .../workflowtemplates/undersync-switch.yaml | 51 ++++++++++++++++--- 2 files changed, 45 insertions(+), 10 deletions(-) diff --git a/charts/site-workflows/templates/sensor-ironic-port-delete.yaml b/charts/site-workflows/templates/sensor-ironic-port-delete.yaml index fe881ae97..ed6d6ac0c 100644 --- a/charts/site-workflows/templates/sensor-ironic-port-delete.yaml +++ b/charts/site-workflows/templates/sensor-ironic-port-delete.yaml @@ -62,9 +62,9 @@ spec: parameters: - name: physical_network - name: force - value: false + value: "false" - name: dry_run - value: false + value: "false" # references the workflow workflowTemplateRef: name: undersync-switch diff --git a/workflows/argo-events/workflowtemplates/undersync-switch.yaml b/workflows/argo-events/workflowtemplates/undersync-switch.yaml index 92b9c28eb..736fc45a0 100644 --- a/workflows/argo-events/workflowtemplates/undersync-switch.yaml +++ b/workflows/argo-events/workflowtemplates/undersync-switch.yaml @@ -4,24 +4,64 @@ metadata: annotations: workflows.argoproj.io/title: Requests an Undersync run on a pair of switches workflows.argoproj.io/description: | + Requests an Undersync run for a switch pair identified by `physical_network`. + + Parameters: + + - `physical_network`: required switch pair / physical network identifier + - `force`: push non-safe changes, without this config drift in non-safe areas won't apply + - `dry_run`: show the intended changes without applying them + + To test this workflow you can run it with the following: + + ``` + argo -n argo-events submit --from workflowtemplate/undersync-switch \ + -p physical_network=physnet1 -p force=false -p dry_run=false + ``` + Defined in `workflows/argo-events/workflowtemplates/undersync-switch.yaml` kind: WorkflowTemplate spec: entrypoint: undersync-switch serviceAccountName: workflow + arguments: + parameters: + - name: physical_network + description: Name of the physical_network (a.k.a. VLAN Group, e.g. "a1-1-network"), tells Undersync which switch pair to configure. + - name: force + description: | + Override safety guardrails when "Change requires approval" + When force=true it disables undersync safety features that would otherwise guard against taking the switch offline, guard against operating on the wrong switch (e.g. DNS snafu), allow the NOC to suspend operations, etc. + When the guardrails are preventing a change from being pushed to the switch, undersync will respond with an error that includes a status like "Change requires approval". + This parameter should never be needed in normal operations. + default: "false" + enum: + - "true" + - "false" + - name: dry_run + description: Preview the changes without making any updates. + default: "false" + enum: + - "true" + - "false" templates: - name: undersync-switch + inputs: + parameters: + - name: physical_network + - name: force + - name: dry_run container: image: ghcr.io/rackerlabs/understack/ironic-nautobot-client:latest command: - undersync-switch args: - --physical-network - - "{{workflow.parameters.physical_network}}" + - "{{inputs.parameters.physical_network}}" - --dry-run - - "{{workflow.parameters.dry_run}}" + - "{{inputs.parameters.dry_run}}" - --force - - "{{workflow.parameters.force}}" + - "{{inputs.parameters.force}}" volumeMounts: - mountPath: /etc/nb-token/ name: nb-token @@ -35,11 +75,6 @@ spec: secretKeyRef: name: nautobot-token key: url - inputs: - parameters: - - name: physical_network - - name: force - - name: dry_run volumes: - name: nb-token secret: