Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: workflow to undersync individual switches #195

Merged
merged 3 commits into from
Aug 5, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
apiVersion: argoproj.io/v1alpha1
metadata:
name: undersync-switch
kind: WorkflowTemplate
spec:
entrypoint: undersync-switch
serviceAccountName: workflow
templates:
- name: undersync-switch
container:
image: ghcr.io/rackerlabs/understack/ironic-nautobot-client:latest
command:
- undersync-switch
args:
- --switch_uuids
- "{{workflow.parameters.switch_uuids}}"
- --dry-run
- "{{workflow.parameters.dry_run}}"
- --force
- "{{workflow.parameters.force}}"
volumeMounts:
- mountPath: /etc/nb-token/
name: nb-token
readOnly: true
- mountPath: /etc/undersync/
name: undersync-token
readOnly: true
inputs:
parameters:
- name: switch_uuids
- name: force
- name: dry_run
volumes:
- name: nb-token
secret:
secretName: nautobot-token
- name: undersync-token
secret:
secretName: undersync-token
1 change: 1 addition & 0 deletions python/understack-workflows/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ synchronize-interfaces = "understack_workflows.main.synchronize_interfaces:main"
synchronize-obm-creds = "understack_workflows.main.synchronize_obm_creds:main"
synchronize-server = "understack_workflows.main.synchronize_server:main"
sync-nautobot-interfaces = "understack_workflows.main.sync_nautobot_interfaces:main"
undersync-switch = "understack_workflows.main.undersync_switch:main"

[tool.setuptools.packages.find]
# avoid packaging up our tests
Expand Down
41 changes: 41 additions & 0 deletions python/understack-workflows/understack_workflows/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import logging
import os
import pathlib
from functools import partial

import sushy

Expand Down Expand Up @@ -35,6 +36,46 @@ def arg_parser(name):
return parser


def undersync_switch_parser(name):
cardoe marked this conversation as resolved.
Show resolved Hide resolved
parser = argparse.ArgumentParser(
prog=os.path.basename(name),
description="Trigger undersync run for a set of switches.",
)
parser.add_argument(
"--switch_uuids",
type=__comma_list,
required=True,
help="Comma separated list of UUIDs of the switches to Undersync",
)
parser.add_argument(
"--force",
type=__boolean_args,
help="Call Undersync's force endpoint",
required=False,
)
parser.add_argument(
"--dry-run",
type=__boolean_args,
help="Call Undersync's dry-run endpoint",
required=False,
)

return parser


def __boolean_args(val):
normalised = str(val).upper()
if normalised in ["YES", "TRUE", "T", "1"]:
return True
elif normalised in ["NO", "FALSE", "F", "N", "0"]:
return False
else:
raise argparse.ArgumentTypeError("boolean expected")


__comma_list = partial(str.split, sep=",")


def credential(subpath, item):
ref = pathlib.Path("/etc").joinpath(subpath).joinpath(item)
with ref.open() as f:
Expand Down
48 changes: 48 additions & 0 deletions python/understack-workflows/understack_workflows/main/undersync.py
cardoe marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from functools import cached_property

import requests


class Undersync:
def __init__(
self,
auth_token: str,
api_url="http://undersync-service.undersync.svc.cluster.local:8080",
) -> None:
self.token = auth_token
self.api_url = api_url

def sync_devices(self, switch_uuids: str | list[str], force=False, dry_run=False):
if isinstance(switch_uuids, list):
switch_uuids = ",".join(switch_uuids)

if dry_run:
return self.dry_run(switch_uuids)
elif force:
return self.force(switch_uuids)
else:
return self.sync(switch_uuids)

@cached_property
def client(self):
session = requests.Session()
session.headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {self.token}",
}
return session

def sync(self, uuids: str) -> requests.Response:
response = self.client.post(f"{self.api_url}/v1/devices/{uuids}/sync")
response.raise_for_status()
return response

def dry_run(self, uuids: str) -> requests.Response:
response = self.client.post(f"{self.api_url}/v1/devices/{uuids}/dry-run")
response.raise_for_status()
return response

def force(self, uuids: str) -> requests.Response:
response = self.client.post(f"{self.api_url}/v1/devices/{uuids}/force")
response.raise_for_status()
return response
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import sys

from understack_workflows.helpers import credential
from understack_workflows.helpers import setup_logger
from understack_workflows.helpers import undersync_switch_parser
from understack_workflows.main.undersync import Undersync


def call_undersync(args, switches):
undersync_token = credential("undersync", "token")
if not undersync_token:
logger.error("Please provide auth token for Undersync.")
sys.exit(1)
undersync = Undersync(undersync_token)

try:
logger.debug(f"Syncing switches: {switches} {args.dry_run=} {args.force=}")
return undersync.sync_devices(switches, dry_run=args.dry_run, force=args.force)
except Exception as error:
logger.error(error)
sys.exit(2)


def main():
"""Requests an Undersync run on a pair of switches."""
parser = undersync_switch_parser(__file__)
args = parser.parse_args()

response = call_undersync(args, args.switch_uuids)
logger.info(f"Undersync returned: {response.json()}")


logger = setup_logger(__name__)
if __name__ == "__main__":
main()
Loading