#!/usr/bin/env python3 # purpose: upsert one 1Password login per `bundle:routeros` node, keyed on the bw node id. from bundlewrap.repo import Repository from os.path import realpath, dirname import json import os import subprocess from dataclasses import dataclass from typing import Optional, List bw = Repository(dirname(dirname(realpath(__file__)))) VAULT=bw.vault.decrypt('encrypt$gAAAAABpLgX_xxb5NmNCl3cgHM0JL65GT6PHVXO5gwly7IkmWoEgkCDSuAcSAkNFB8Tb4RdnTdpzVQEUL1XppTKVto_O7_b11GjATiyQYiSfiQ8KZkTKLvk=').value BW_TAG = "bw" BUNDLEWRAP_FIELD_LABEL = "bundlewrap node id" @dataclass class OpResult: stdout: str stderr: str returncode: int def main(): for node in bw.nodes_in_group('routeros'): upsert_node_item( node_name=node.name, node_uuid=node.metadata.get('id'), username=node.username, password=node.password, url=f'http://{node.hostname}', ) def run_op(args): proc = subprocess.run( ["op", "--vault", VAULT] + args, env=os.environ.copy(), capture_output=True, text=True, ) if proc.returncode != 0: raise RuntimeError( f"op {' '.join(args)} failed with code {proc.returncode}:\n" f"STDOUT:\n{proc.stdout}\n\nSTDERR:\n{proc.stderr}" ) return OpResult(stdout=proc.stdout, stderr=proc.stderr, returncode=proc.returncode) def op_item_list_bw(): out = run_op([ "item", "list", "--tags", BW_TAG, "--format", "json", ]) stdout = out.stdout.strip() return json.loads(stdout) if stdout else [] def op_item_get(item_id): args = ["item", "get", item_id, "--format", "json"] return json.loads(run_op(args).stdout) def op_item_create(title, node_uuid, username, password, url): print(f"creating {title}") return json.loads(run_op([ "item", "create", "--category", "LOGIN", "--title", title, "--tags", BW_TAG, "--url", url, "--format", "json", f"username={username}", f"password={password}", f"{BUNDLEWRAP_FIELD_LABEL}[text]={node_uuid}", ]).stdout) def op_item_edit(item_id, title, username, password, url): print(f"updating {title}") return json.loads(run_op([ "item", "edit", item_id, "--title", title, "--url", url, "--format", "json", f"username={username}", f"password={password}", ]).stdout) def find_node_item_id(node_uuid): for summary in op_item_list_bw(): item_id = summary.get("id") if not item_id: continue item = op_item_get(item_id) for field in item.get("fields") or []: label = field.get("label") value = field.get("value") if label == BUNDLEWRAP_FIELD_LABEL and value == node_uuid: return item_id return None def upsert_node_item(node_name, node_uuid, username, password, url): if item_id := find_node_item_id(node_uuid): return op_item_edit( item_id=item_id, title=node_name, username=username, password=password, url=url, ) else: return op_item_create( title=node_name, node_uuid=node_uuid, username=username, password=password, url=url, ) if __name__ == "__main__": main()