diff --git a/python/understack-workflows/pyproject.toml b/python/understack-workflows/pyproject.toml index 40c7594a9..6d75bb538 100644 --- a/python/understack-workflows/pyproject.toml +++ b/python/understack-workflows/pyproject.toml @@ -49,6 +49,7 @@ sync-bmc-creds = "understack_workflows.main.sync_bmc_creds:main" sync-server = "understack_workflows.main.sync_server:main" sync-provision-state = "understack_workflows.main.sync_provision_state:main" sync-nautobot-interfaces = "understack_workflows.main.sync_nautobot_interfaces:main" +sync-nautobot-system-info = "understack_workflows.main.sync_nautobot_system_info:main" undersync-switch = "understack_workflows.main.undersync_switch:main" undersync-device = "understack_workflows.main.undersync_device:main" nautobot-update-cf = "understack_workflows.main.nautobot_update_cf:main" diff --git a/python/understack-workflows/understack_workflows/main/sync_nautobot_system_info.py b/python/understack-workflows/understack_workflows/main/sync_nautobot_system_info.py new file mode 100644 index 000000000..7327974e0 --- /dev/null +++ b/python/understack-workflows/understack_workflows/main/sync_nautobot_system_info.py @@ -0,0 +1,49 @@ +import argparse +import os +from uuid import UUID + +from understack_workflows.helpers import credential +from understack_workflows.helpers import oob_sushy_session +from understack_workflows.helpers import parser_nautobot_args +from understack_workflows.helpers import setup_logger +from understack_workflows.models import Chassis +from understack_workflows.nautobot import Nautobot + +logger = setup_logger(__name__) + + +def argument_parser(name): + parser = argparse.ArgumentParser( + prog=os.path.basename(name), description="Nautobot Interface sync" + ) + parser.add_argument( + "--device-id", type=UUID, required=True, help="Nautobot device ID" + ) + parser.add_argument("--bmc_username", required=False, help="BMC username") + parser.add_argument("--bmc_password", required=False, help="BMC password") + parser = parser_nautobot_args(parser) + return parser + + +def do_sync(device_id: UUID, nautobot: Nautobot, bmc_user: str, bmc_pass: str): + bmc_ip = nautobot.device_oob_ip(device_id) + bmc_obj = oob_sushy_session(bmc_ip, bmc_user, bmc_pass) + + chassis = Chassis.from_redfish(bmc_obj) + nautobot.update_system_info(device_id, chassis.system_info) + + +def main(): + parser = argument_parser(__file__) + args = parser.parse_args() + + nb_token = args.nautobot_token or credential("nb-token", "token") + bmc_user = args.oob_username or credential("bmc-secrets", "username") + bmc_pass = args.oob_password or credential("bmc-secrets", "password") + nautobot = Nautobot(args.nautobot_url, nb_token, logger=logger) + + do_sync(args.device_id, nautobot, bmc_user, bmc_pass) + + +if __name__ == "__main__": + main() diff --git a/python/understack-workflows/understack_workflows/models.py b/python/understack-workflows/understack_workflows/models.py index ac899e508..db3d6db86 100644 --- a/python/understack-workflows/understack_workflows/models.py +++ b/python/understack-workflows/understack_workflows/models.py @@ -96,11 +96,25 @@ def fetch_macaddr_from_sys_resource(cls, data: NetworkPort) -> str: return macaddr +@dataclass +class Systeminfo: + asset_tag: str + serial_number: str + platform: str + + @classmethod + def from_redfish(cls, chassis_data) -> Systeminfo: + return cls(asset_tag=chassis_data.sku, + serial_number=chassis_data.serial_number, + platform=chassis_data.model) + + @dataclass class Chassis: name: str nics: list[NIC] network_interfaces: list[Interface] + system_info: Systeminfo @classmethod def check_manufacturer(cls, manufacturer: str) -> None: @@ -129,11 +143,12 @@ def from_redfish(cls, oob_obj: Sushy) -> Chassis: if cls.bmc_is_ilo4(chassis_data): return cls.from_hp_json(oob_obj, chassis_data.name) - chassis = cls(chassis_data.name, [], []) + chassis = cls(chassis_data.name, [], [], []) chassis.nics = [ NIC.from_redfish(i) for i in chassis_data.network_adapters.get_members() ] chassis.network_interfaces = cls.interfaces_from_nics(chassis.nics) + chassis.system_info = Systeminfo.from_redfish(chassis_data) return chassis @classmethod diff --git a/python/understack-workflows/understack_workflows/nautobot.py b/python/understack-workflows/understack_workflows/nautobot.py index c8377eb3f..46447b9a0 100644 --- a/python/understack-workflows/understack_workflows/nautobot.py +++ b/python/understack-workflows/understack_workflows/nautobot.py @@ -15,6 +15,12 @@ class Interface(Protocol): location: str +class Systeminfo(Protocol): + asset_tag: str + serial_number: str + platform: str + + class Nautobot: def __init__(self, url, token, logger=None, session=None): """Initialize our Nautobot API wrapper.""" @@ -202,3 +208,12 @@ def uplink_switches(self, device_id: UUID) -> list[str]: ids.add(remote_switch.id) return list(ids) + + def update_system_info(self, device_id: UUID, system_info: Systeminfo): + device = self.device_by_id(device_id) + device.serial = system_info.serial_number + device.platform = system_info.platform + device.asset_tag = system_info.asset_tag + response = device.save() + self.logger.info(f"save result: {response}") + return response diff --git a/workflows/argo-events/workflowtemplates/sync-system-info-to-nautobot.yaml b/workflows/argo-events/workflowtemplates/sync-system-info-to-nautobot.yaml new file mode 100644 index 000000000..b802ace8c --- /dev/null +++ b/workflows/argo-events/workflowtemplates/sync-system-info-to-nautobot.yaml @@ -0,0 +1,39 @@ +apiVersion: argoproj.io/v1alpha1 +metadata: + name: sync-system-info-to-nautobot + annotations: + workflows.argoproj.io/title: Sync Redfish system info (serial number, service tag, model name) data to Nautobot + workflows.argoproj.io/description: | + Defined in `workflows/argo-events/workflowtemplates/sync-system-info-to-nautobot.yaml` +kind: WorkflowTemplate +spec: + arguments: + parameters: + - name: device_id + value: "{}" + - name: bmc_secret + value: "{}" + templates: + - name: sync-system-info + container: + image: ghcr.io/rackerlabs/understack/ironic-nautobot-client:latest + command: + - sync-nautobot-system-info + args: ["--device-id", "{{workflow.parameters.device_id}}"] + volumeMounts: + - mountPath: /etc/nb-token/ + name: nb-token + readOnly: true + - mountPath: /etc/bmc-secrets/ + name: bmc-secrets + readOnly: true + inputs: + parameters: + - name: bmc_secret + volumes: + - name: nb-token + secret: + secretName: nautobot-token + - name: bmc-secrets + secret: + secretName: "{{inputs.parameters.bmc_secret}}"