diff --git a/.github/workflows/build-container-images.yaml b/.github/workflows/build-container-images.yaml index 117ce68f7..1004836f0 100644 --- a/.github/workflows/build-container-images.yaml +++ b/.github/workflows/build-container-images.yaml @@ -5,14 +5,12 @@ on: workflow_dispatch: pull_request: paths: - - "containers/bmc-utils/**" - "containers/python311_alpine/**" - "containers/python312_alpine/**" push: branches: - main paths: - - "containers/bmc-utils/**" - "containers/python311_alpine/**" - "containers/python312_alpine/**" merge_group: @@ -22,9 +20,6 @@ on: env: VERSION_PYTHON311: 0.0.1 VERSION_PYTHON312: 0.0.1 - VERSION_ARGO_UTILS: 0.0.1 - VERSION_BMC_UTILS: 0.0.1 - VERSION_PYTHON_NAUTOBOT: 0.0.1 jobs: build-ghcr-registry: @@ -65,14 +60,3 @@ jobs: tags: ghcr.io/${{ github.repository }}/argo-python3.12.2-alpine3.19:latest,ghcr.io/${{ github.repository }}/argo-python3.12.2-alpine3.19:${{ env.VERSION_PYTHON312 }} labels: | org.opencontainers.image.version=${{ env.VERSION_PYTHON312 }} - - - name: Build and deploy BMC Utils image - uses: docker/build-push-action@ca877d9245402d1537745e0e356eab47c3520991 # v6 - with: - context: containers/bmc-utils/ - file: containers/bmc-utils/Dockerfile.bmc_utils - # push for all main branch commits - push: ${{ github.event_name != 'pull_request' }} - tags: ghcr.io/${{ github.repository }}/argo-bmc-utils-python3.11.8:latest,ghcr.io/${{ github.repository }}/argo-bmc-utils-python3.11.8:${{ env.VERSION_BMC_UTILS }} - labels: | - org.opencontainers.image.version=${{ env.VERSION_BMC_UTILS }} diff --git a/containers/bmc-utils/Dockerfile.bmc_utils b/containers/bmc-utils/Dockerfile.bmc_utils deleted file mode 100644 index fcd12ee92..000000000 --- a/containers/bmc-utils/Dockerfile.bmc_utils +++ /dev/null @@ -1,20 +0,0 @@ -ARG BASE=ghcr.io/rackerlabs/understack/python3.11.8-alpine3.19:latest - -FROM ${BASE} AS builder - -COPY --chown=appuser:appgroup requirements.txt /app -RUN --mount=type=cache,target=/root/.cache/.pip pip install --no-cache-dir -r /app/requirements.txt - -FROM ${BASE} AS prod - -LABEL org.opencontainers.image.title="Python 3.11 image with BMC utils" -LABEL org.opencontainers.image.base.name="ghcr.io/rackerlabs/understack/argo-bmc-utils-python3.11.8" -LABEL org.opencontainers.image.source=https://github.com/rackerlabs/understack - -ENV PATH="/opt/venv/bin:$PATH" -COPY --from=builder /opt/venv /opt/venv - -WORKDIR /app -USER appuser -COPY --chown=appuser:appgroup code/ /app -CMD ["python", "-"] diff --git a/containers/bmc-utils/README.md b/containers/bmc-utils/README.md deleted file mode 100644 index ed1ef48d3..000000000 --- a/containers/bmc-utils/README.md +++ /dev/null @@ -1,50 +0,0 @@ -# Overview - -The WorkflowTemplates provided in this directory were created to provide common lifecycle and maintenance functions on BMC controllers. - -## Caveats - -- TODO: the bmc-sync-creds workflow logic should probably be broken to include a bmc-update-password workflow, which then likely has more utility. - -## Example -```bash -argo -n argo-events submit --from workflowtemplate/bmc-sync-creds --parameter device_id=1de4f169-9848-4d8e-921b-65338c1e00ca - -Name: bmc-sync-creds-wrn2c -Namespace: argo-events -ServiceAccount: unset -Status: Pending -Created: Tue Apr 23 14:24:07 -0400 (now) -Progress: -Parameters: - device_id: 1de4f169-9848-4d8e-921b-65338c1e00ca -``` - -```bash -argo -n argo-events get @latest -Name: bmc-sync-creds-wrn2c -Namespace: argo-events -ServiceAccount: workflow -Status: Running -Conditions: - PodRunning False -Created: Tue Apr 23 14:24:07 -0400 (58 seconds ago) -Started: Tue Apr 23 14:24:07 -0400 (58 seconds ago) -Duration: 58 seconds -Progress: 2/3 -ResourcesDuration: 0s*(1 cpu),5s*(100Mi memory) -Parameters: - device_id: 1de4f169-9848-4d8e-921b-65338c1e00ca - -STEP TEMPLATE PODNAME DURATION MESSAGE - ● bmc-sync-creds-wrn2c main - ├─✔ get-bmc-creds get-bmc-creds/main - │ └─✔ get-bmc-creds get-bmc-creds-ext/main - │ └─✔ get-bmc-creds-ext get-creds-ext/main - │ ├─✔ get-ext-num get-ext-num/main - │ └─✔ get-creds-ext get-creds-ext bmc-sync-creds-wrn2c-get-creds-ext-2059517959 5s - ├─✔ get-bmc-ip get-bmc-ip/main - │ └───✔ nautobot-query nautobot-api/main - │ └───✔ send-request http - └─◷ bmc-sync-creds bmc-sync-creds bmc-sync-creds-wrn2c-bmc-sync-creds-2727609696 28s -``` diff --git a/containers/bmc-utils/code/__init__.py b/containers/bmc-utils/code/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/containers/bmc-utils/code/bmc_sync_creds.py b/containers/bmc-utils/code/bmc_sync_creds.py deleted file mode 100644 index a657bfab5..000000000 --- a/containers/bmc-utils/code/bmc_sync_creds.py +++ /dev/null @@ -1,160 +0,0 @@ -import logging -import os -import sys -import argparse -import requests -import time -import json -from typing import List, Dict -import urllib3 - -urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) - -logger = logging.getLogger(__name__) -handler = logging.StreamHandler() -formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") -handler.setFormatter(formatter) -logger.addHandler(handler) -logger.setLevel(logging.DEBUG) - - -def redfish_request( - host: str, - uri: str, - username: str, - password: str, - method: str = "GET", - payload: Dict | None = None, -) -> dict: - try: - r = requests.request( - method, - f"https://{host}{uri}", - verify=False, - auth=(username, password), - timeout=15, - json=payload, - ) - r.raise_for_status() - return r.json() - except Exception: - raise - - -def verify_auth(host: str, username: str = "root", password: str = "") -> bool: - """Verify auth credentials against a secured API endpoint""" - try: - r = redfish_request(host, "/redfish/v1", username, password) - account_service_uri = r["AccountService"]["@odata.id"] - redfish_request(host, account_service_uri, username, password) - return True - except Exception as e: - logger.warning(f"Exception: {e}") - return False - - -def get_bmc_accounts(host: str, username: str, password: str) -> List[Dict]: - """A vendor agnostic approach to crawling the API for BMC accounts""" - try: - # get account service - r = redfish_request(host, "/redfish/v1", username, password) - account_service_uri = r["AccountService"]["@odata.id"] - logger.debug(f"account_service_url: {account_service_uri}") - - # get account collection uri - r = redfish_request(host, account_service_uri, username, password) - accounts_uri = r["Accounts"]["@odata.id"] - logger.debug(f"accounts_url: {accounts_uri}") - - # get accounts - r = redfish_request(host, accounts_uri, username, password) - accounts = r["Members"] - logger.debug(f"accounts: {accounts}") - - return accounts - except Exception: - logger.exception("Unable to fetch accounts from Redfish account service.") - raise - - -def set_bmc_creds( - host: str, - username: str, - password: str, - expected_username: str, - expected_password: str, -) -> bool: - """Find the account associated with the username in question""" - try: - accounts = get_bmc_accounts(host, username, password) - - matched_account = None - for account in accounts: - account_url = account["@odata.id"] - - a = redfish_request(host, account_url, username, password) - if expected_username == a["UserName"]: - logger.debug(f"found account: {a}") - matched_account = a - break - - if not matched_account: - raise Exception(f"Unable to find BMC account for {expected_username}") - - account_uri = matched_account["@odata.id"] - - payload = {"Password": expected_password} - redfish_request(host, account_uri, username, password, "PATCH", payload) - return True - except Exception as e: - logger.error(f"Exception: {e}") - return False - - -if __name__ == "__main__": - parser = argparse.ArgumentParser( - prog=os.path.basename(__file__), - description="Attempts to find the correct BMC credentials for a device", - ) - parser.add_argument( - "--host", required=True, help="the address of the bmc interface for the device" - ) - - args = parser.parse_args() - host = args.host - expected_username = os.environ["BMC_USERNAME"] - expected_password = os.environ["BMC_PASSWORD"] - - legacy_passwords = json.loads(os.getenv("BMC_LEGACY_PASSWORDS", "[]")) - if not legacy_passwords: - logger.info("env variable BMC_LEGACY_PASSWORDS was not set.") - sys.exit(1) - - logger.info("Ensuring BMC credentials are synced correctly ...") - - if verify_auth(host, expected_username, expected_password): - logger.info("BMC credentials are in sync.") - sys.exit(0) - else: - logger.info( - "BMC credentials are NOT in sync. Trying known legacy/vendor credentials ..." - ) - - # iDRAC defaults to blocking an IP address after 3 bad login attempts within 60 second. Since we have the - # initial attempt above, we will sleep 35 seconds between any additional attempts. - delay = 60 - username = os.getenv("BMC_LEGACY_USER", "root") - for password in legacy_passwords: - logger.info( - f"Delaying for {delay} seconds to prevent failed auth lockouts ..." - ) - time.sleep(delay) - if verify_auth(host, username, password): - if set_bmc_creds( - host, username, password, expected_username, expected_password - ): - logger.info("BMC password has been synced.") - sys.exit(0) - - logger.info("Unable to sync the BMC password.") - sys.exit(1) diff --git a/containers/bmc-utils/requirements.txt b/containers/bmc-utils/requirements.txt deleted file mode 100644 index 263acbb2d..000000000 --- a/containers/bmc-utils/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -requests==2.32.3 -pynautobot==2.5.0 -sushy==5.4.0