all:
  children:
    zuul_unreachable:
      hosts: {}
  hosts:
    debian-bookworm:
      ansible_connection: ssh
      ansible_host: 2a13:1a81:8000:3124:f816:3eff:fe92:db
      ansible_port: 22
      ansible_python_interpreter: auto
      ansible_user: zuul
      docker_namespace: osism
      docker_registry: osism.harbor.regio.digital
      nodepool:
        az: nova
        cloud: regiocloud-a-ci
        external_id: f913aaea-065a-4a25-8c71-96229018a38c
        host_id: df70f2a3f3d0c26428f22685cfc722ebc48099d99e7332c39e017195
        interface_ip: 2a13:1a81:8000:3124:f816:3eff:fe92:db
        label: debian-bookworm
        node_properties: {}
        private_ipv4: 10.128.7.76
        private_ipv6: null
        provider: regiocloud-a
        public_ipv4: 81.163.193.82
        public_ipv6: 2a13:1a81:8000:3124:f816:3eff:fe92:db
        region: null
        slot: null
  vars:
    docker_namespace: osism
    docker_registry: osism.harbor.regio.digital
    zuul:
      _inheritance_path:
      - '<Job base explicit: None implied: {MatchAny:{ImpliedBranchMatcher:main}}
        source: osism/zuul-config/zuul.d/jobs.yaml@main#2>'
      - '<Job container-image-python-osism-build explicit: None implied: {MatchAny:{ImpliedBranchMatcher:main}}
        source: osism/python-osism/.zuul.yaml@main#79>'
      - '<Job container-image-python-osism-build explicit: None implied: None source:
        osism/python-osism/.zuul.yaml@main#143>'
      ansible_version: '9'
      attempts: 1
      branch: main
      build: e8fe6eea4694477fbf5aa04d63e2f178
      build_refs:
      - branch: main
        change: '2463'
        change_message: "Add unit tests for osism/api.py helpers and Pydantic models\n\nAdds
          `tests/unit/test_api_helpers.py` covering the module-level helpers and Pydantic
          models of the OSISM API server (`osism/api.py`), as specified in #2359:\n\n-
          **`_mask_inventory_secrets()`** \u2014 secret-key matching (`password`/`secret`
          substrings, `ironic_osism_` prefix, using the real `_is_secret_key`), Ansible
          Vault values (including leading whitespace, and the marker mid-string staying
          unchanged), nested dict recursion, secret keys with dict values masked as
          a whole, pass-through of non-dict/non-string values and non-string keys,
          non-traversal of lists, empty dict, input immutability.\n- **`find_device_by_identifier()`**
          \u2014 lookup precedence `name` \u2192 `cf_inventory_hostname` \u2192 `serial`
          with call-order assertions, early `None` return without a NetBox connection,
          first-match semantics for multiple results. `utils.nb` is injected via `patch.dict(\"osism.utils.__dict__\",
          ...)` to avoid triggering the lazy module `__getattr__`, matching the existing
          pattern in `tests/unit/commands/`.\n- **`process_netbox_webhook()`** \u2014
          `reconciler.run.delay()` dispatched exactly once for managed servers; no
          dispatch for switches, interfaces (device fetched via `nb.dcim.devices.get(id=...)`,
          asserted), `device_type` fallback to `\"node\"`, unknown URLs (early return
          without NetBox access), and unmanaged devices; `KeyError` propagation for
          payloads without `url`/`name`.\n- **`LogConfig`** \u2014 defaults, `model_dump()`
          structure (formatter, `StreamHandler` on `ext://sys.stderr`, all five loggers),
          `dictConfig` compatibility.\n- **Pydantic models** \u2014 `NotificationBaremetal`
          and `WebhookNetboxData` validation and coercion (UUID, datetime, per-field
          required checks), `BaremetalNode` optional fields and `default_factory`
          independence, all-optional models, and parametrized required-field enforcement
          plus `model_dump()` round trips for the response models, including dict-to-`BaremetalNodeNetboxInfo`
          coercion and `Any`-typed value fields.\n\nEndpoint behavior (HTTP status
          codes, masking applied in responses) is out of scope here and belongs to
          the companion endpoint issue (Tier 7, split 2/2).\n\nCloses #2359\n\n\U0001F916
          Generated with [Claude Code](https://claude.com/claude-code)"
        change_url: https://github.com/osism/python-osism/pull/2463
        commit_id: e6a91edf79b265bd9be4fe850ffa170784196ba3
        patchset: e6a91edf79b265bd9be4fe850ffa170784196ba3
        project:
          canonical_hostname: github.com
          canonical_name: github.com/osism/python-osism
          name: osism/python-osism
          short_name: python-osism
        src_dir: src/github.com/osism/python-osism
        topic: null
      buildset: 458044dc27414b77afdc651888c76a4b
      buildset_refs:
      - branch: main
        change: '2463'
        change_message: "Add unit tests for osism/api.py helpers and Pydantic models\n\nAdds
          `tests/unit/test_api_helpers.py` covering the module-level helpers and Pydantic
          models of the OSISM API server (`osism/api.py`), as specified in #2359:\n\n-
          **`_mask_inventory_secrets()`** \u2014 secret-key matching (`password`/`secret`
          substrings, `ironic_osism_` prefix, using the real `_is_secret_key`), Ansible
          Vault values (including leading whitespace, and the marker mid-string staying
          unchanged), nested dict recursion, secret keys with dict values masked as
          a whole, pass-through of non-dict/non-string values and non-string keys,
          non-traversal of lists, empty dict, input immutability.\n- **`find_device_by_identifier()`**
          \u2014 lookup precedence `name` \u2192 `cf_inventory_hostname` \u2192 `serial`
          with call-order assertions, early `None` return without a NetBox connection,
          first-match semantics for multiple results. `utils.nb` is injected via `patch.dict(\"osism.utils.__dict__\",
          ...)` to avoid triggering the lazy module `__getattr__`, matching the existing
          pattern in `tests/unit/commands/`.\n- **`process_netbox_webhook()`** \u2014
          `reconciler.run.delay()` dispatched exactly once for managed servers; no
          dispatch for switches, interfaces (device fetched via `nb.dcim.devices.get(id=...)`,
          asserted), `device_type` fallback to `\"node\"`, unknown URLs (early return
          without NetBox access), and unmanaged devices; `KeyError` propagation for
          payloads without `url`/`name`.\n- **`LogConfig`** \u2014 defaults, `model_dump()`
          structure (formatter, `StreamHandler` on `ext://sys.stderr`, all five loggers),
          `dictConfig` compatibility.\n- **Pydantic models** \u2014 `NotificationBaremetal`
          and `WebhookNetboxData` validation and coercion (UUID, datetime, per-field
          required checks), `BaremetalNode` optional fields and `default_factory`
          independence, all-optional models, and parametrized required-field enforcement
          plus `model_dump()` round trips for the response models, including dict-to-`BaremetalNodeNetboxInfo`
          coercion and `Any`-typed value fields.\n\nEndpoint behavior (HTTP status
          codes, masking applied in responses) is out of scope here and belongs to
          the companion endpoint issue (Tier 7, split 2/2).\n\nCloses #2359\n\n\U0001F916
          Generated with [Claude Code](https://claude.com/claude-code)"
        change_url: https://github.com/osism/python-osism/pull/2463
        commit_id: e6a91edf79b265bd9be4fe850ffa170784196ba3
        patchset: e6a91edf79b265bd9be4fe850ffa170784196ba3
        project:
          canonical_hostname: github.com
          canonical_name: github.com/osism/python-osism
          name: osism/python-osism
          short_name: python-osism
        src_dir: src/github.com/osism/python-osism
        topic: null
      change: '2463'
      change_message: "Add unit tests for osism/api.py helpers and Pydantic models\n\nAdds
        `tests/unit/test_api_helpers.py` covering the module-level helpers and Pydantic
        models of the OSISM API server (`osism/api.py`), as specified in #2359:\n\n-
        **`_mask_inventory_secrets()`** \u2014 secret-key matching (`password`/`secret`
        substrings, `ironic_osism_` prefix, using the real `_is_secret_key`), Ansible
        Vault values (including leading whitespace, and the marker mid-string staying
        unchanged), nested dict recursion, secret keys with dict values masked as
        a whole, pass-through of non-dict/non-string values and non-string keys, non-traversal
        of lists, empty dict, input immutability.\n- **`find_device_by_identifier()`**
        \u2014 lookup precedence `name` \u2192 `cf_inventory_hostname` \u2192 `serial`
        with call-order assertions, early `None` return without a NetBox connection,
        first-match semantics for multiple results. `utils.nb` is injected via `patch.dict(\"osism.utils.__dict__\",
        ...)` to avoid triggering the lazy module `__getattr__`, matching the existing
        pattern in `tests/unit/commands/`.\n- **`process_netbox_webhook()`** \u2014
        `reconciler.run.delay()` dispatched exactly once for managed servers; no dispatch
        for switches, interfaces (device fetched via `nb.dcim.devices.get(id=...)`,
        asserted), `device_type` fallback to `\"node\"`, unknown URLs (early return
        without NetBox access), and unmanaged devices; `KeyError` propagation for
        payloads without `url`/`name`.\n- **`LogConfig`** \u2014 defaults, `model_dump()`
        structure (formatter, `StreamHandler` on `ext://sys.stderr`, all five loggers),
        `dictConfig` compatibility.\n- **Pydantic models** \u2014 `NotificationBaremetal`
        and `WebhookNetboxData` validation and coercion (UUID, datetime, per-field
        required checks), `BaremetalNode` optional fields and `default_factory` independence,
        all-optional models, and parametrized required-field enforcement plus `model_dump()`
        round trips for the response models, including dict-to-`BaremetalNodeNetboxInfo`
        coercion and `Any`-typed value fields.\n\nEndpoint behavior (HTTP status codes,
        masking applied in responses) is out of scope here and belongs to the companion
        endpoint issue (Tier 7, split 2/2).\n\nCloses #2359\n\n\U0001F916 Generated
        with [Claude Code](https://claude.com/claude-code)"
      change_url: https://github.com/osism/python-osism/pull/2463
      child_jobs: []
      commit_id: e6a91edf79b265bd9be4fe850ffa170784196ba3
      event_id: 8471ece0-810e-11f1-8886-5c3299d08770
      executor:
        hostname: 521e9411259a
        inventory_file: /var/lib/zuul/builds/e8fe6eea4694477fbf5aa04d63e2f178/ansible/inventory.yaml
        log_root: /var/lib/zuul/builds/e8fe6eea4694477fbf5aa04d63e2f178/work/logs
        result_data_file: /var/lib/zuul/builds/e8fe6eea4694477fbf5aa04d63e2f178/work/results.json
        src_root: /var/lib/zuul/builds/e8fe6eea4694477fbf5aa04d63e2f178/work/src
        work_root: /var/lib/zuul/builds/e8fe6eea4694477fbf5aa04d63e2f178/work
      include_vars: []
      items:
      - branch: main
        change: '2463'
        change_message: "Add unit tests for osism/api.py helpers and Pydantic models\n\nAdds
          `tests/unit/test_api_helpers.py` covering the module-level helpers and Pydantic
          models of the OSISM API server (`osism/api.py`), as specified in #2359:\n\n-
          **`_mask_inventory_secrets()`** \u2014 secret-key matching (`password`/`secret`
          substrings, `ironic_osism_` prefix, using the real `_is_secret_key`), Ansible
          Vault values (including leading whitespace, and the marker mid-string staying
          unchanged), nested dict recursion, secret keys with dict values masked as
          a whole, pass-through of non-dict/non-string values and non-string keys,
          non-traversal of lists, empty dict, input immutability.\n- **`find_device_by_identifier()`**
          \u2014 lookup precedence `name` \u2192 `cf_inventory_hostname` \u2192 `serial`
          with call-order assertions, early `None` return without a NetBox connection,
          first-match semantics for multiple results. `utils.nb` is injected via `patch.dict(\"osism.utils.__dict__\",
          ...)` to avoid triggering the lazy module `__getattr__`, matching the existing
          pattern in `tests/unit/commands/`.\n- **`process_netbox_webhook()`** \u2014
          `reconciler.run.delay()` dispatched exactly once for managed servers; no
          dispatch for switches, interfaces (device fetched via `nb.dcim.devices.get(id=...)`,
          asserted), `device_type` fallback to `\"node\"`, unknown URLs (early return
          without NetBox access), and unmanaged devices; `KeyError` propagation for
          payloads without `url`/`name`.\n- **`LogConfig`** \u2014 defaults, `model_dump()`
          structure (formatter, `StreamHandler` on `ext://sys.stderr`, all five loggers),
          `dictConfig` compatibility.\n- **Pydantic models** \u2014 `NotificationBaremetal`
          and `WebhookNetboxData` validation and coercion (UUID, datetime, per-field
          required checks), `BaremetalNode` optional fields and `default_factory`
          independence, all-optional models, and parametrized required-field enforcement
          plus `model_dump()` round trips for the response models, including dict-to-`BaremetalNodeNetboxInfo`
          coercion and `Any`-typed value fields.\n\nEndpoint behavior (HTTP status
          codes, masking applied in responses) is out of scope here and belongs to
          the companion endpoint issue (Tier 7, split 2/2).\n\nCloses #2359\n\n\U0001F916
          Generated with [Claude Code](https://claude.com/claude-code)"
        change_url: https://github.com/osism/python-osism/pull/2463
        commit_id: e6a91edf79b265bd9be4fe850ffa170784196ba3
        patchset: e6a91edf79b265bd9be4fe850ffa170784196ba3
        project:
          canonical_hostname: github.com
          canonical_name: github.com/osism/python-osism
          name: osism/python-osism
          short_name: python-osism
          src_dir: src/github.com/osism/python-osism
        topic: null
      job: container-image-python-osism-build
      jobtags: []
      max_attempts: 3
      message: QWRkIHVuaXQgdGVzdHMgZm9yIG9zaXNtL2FwaS5weSBoZWxwZXJzIGFuZCBQeWRhbnRpYyBtb2RlbHMKCkFkZHMgYHRlc3RzL3VuaXQvdGVzdF9hcGlfaGVscGVycy5weWAgY292ZXJpbmcgdGhlIG1vZHVsZS1sZXZlbCBoZWxwZXJzIGFuZCBQeWRhbnRpYyBtb2RlbHMgb2YgdGhlIE9TSVNNIEFQSSBzZXJ2ZXIgKGBvc2lzbS9hcGkucHlgKSwgYXMgc3BlY2lmaWVkIGluICMyMzU5OgoKLSAqKmBfbWFza19pbnZlbnRvcnlfc2VjcmV0cygpYCoqIOKAlCBzZWNyZXQta2V5IG1hdGNoaW5nIChgcGFzc3dvcmRgL2BzZWNyZXRgIHN1YnN0cmluZ3MsIGBpcm9uaWNfb3Npc21fYCBwcmVmaXgsIHVzaW5nIHRoZSByZWFsIGBfaXNfc2VjcmV0X2tleWApLCBBbnNpYmxlIFZhdWx0IHZhbHVlcyAoaW5jbHVkaW5nIGxlYWRpbmcgd2hpdGVzcGFjZSwgYW5kIHRoZSBtYXJrZXIgbWlkLXN0cmluZyBzdGF5aW5nIHVuY2hhbmdlZCksIG5lc3RlZCBkaWN0IHJlY3Vyc2lvbiwgc2VjcmV0IGtleXMgd2l0aCBkaWN0IHZhbHVlcyBtYXNrZWQgYXMgYSB3aG9sZSwgcGFzcy10aHJvdWdoIG9mIG5vbi1kaWN0L25vbi1zdHJpbmcgdmFsdWVzIGFuZCBub24tc3RyaW5nIGtleXMsIG5vbi10cmF2ZXJzYWwgb2YgbGlzdHMsIGVtcHR5IGRpY3QsIGlucHV0IGltbXV0YWJpbGl0eS4KLSAqKmBmaW5kX2RldmljZV9ieV9pZGVudGlmaWVyKClgKiog4oCUIGxvb2t1cCBwcmVjZWRlbmNlIGBuYW1lYCDihpIgYGNmX2ludmVudG9yeV9ob3N0bmFtZWAg4oaSIGBzZXJpYWxgIHdpdGggY2FsbC1vcmRlciBhc3NlcnRpb25zLCBlYXJseSBgTm9uZWAgcmV0dXJuIHdpdGhvdXQgYSBOZXRCb3ggY29ubmVjdGlvbiwgZmlyc3QtbWF0Y2ggc2VtYW50aWNzIGZvciBtdWx0aXBsZSByZXN1bHRzLiBgdXRpbHMubmJgIGlzIGluamVjdGVkIHZpYSBgcGF0Y2guZGljdCgib3Npc20udXRpbHMuX19kaWN0X18iLCAuLi4pYCB0byBhdm9pZCB0cmlnZ2VyaW5nIHRoZSBsYXp5IG1vZHVsZSBgX19nZXRhdHRyX19gLCBtYXRjaGluZyB0aGUgZXhpc3RpbmcgcGF0dGVybiBpbiBgdGVzdHMvdW5pdC9jb21tYW5kcy9gLgotICoqYHByb2Nlc3NfbmV0Ym94X3dlYmhvb2soKWAqKiDigJQgYHJlY29uY2lsZXIucnVuLmRlbGF5KClgIGRpc3BhdGNoZWQgZXhhY3RseSBvbmNlIGZvciBtYW5hZ2VkIHNlcnZlcnM7IG5vIGRpc3BhdGNoIGZvciBzd2l0Y2hlcywgaW50ZXJmYWNlcyAoZGV2aWNlIGZldGNoZWQgdmlhIGBuYi5kY2ltLmRldmljZXMuZ2V0KGlkPS4uLilgLCBhc3NlcnRlZCksIGBkZXZpY2VfdHlwZWAgZmFsbGJhY2sgdG8gYCJub2RlImAsIHVua25vd24gVVJMcyAoZWFybHkgcmV0dXJuIHdpdGhvdXQgTmV0Qm94IGFjY2VzcyksIGFuZCB1bm1hbmFnZWQgZGV2aWNlczsgYEtleUVycm9yYCBwcm9wYWdhdGlvbiBmb3IgcGF5bG9hZHMgd2l0aG91dCBgdXJsYC9gbmFtZWAuCi0gKipgTG9nQ29uZmlnYCoqIOKAlCBkZWZhdWx0cywgYG1vZGVsX2R1bXAoKWAgc3RydWN0dXJlIChmb3JtYXR0ZXIsIGBTdHJlYW1IYW5kbGVyYCBvbiBgZXh0Oi8vc3lzLnN0ZGVycmAsIGFsbCBmaXZlIGxvZ2dlcnMpLCBgZGljdENvbmZpZ2AgY29tcGF0aWJpbGl0eS4KLSAqKlB5ZGFudGljIG1vZGVscyoqIOKAlCBgTm90aWZpY2F0aW9uQmFyZW1ldGFsYCBhbmQgYFdlYmhvb2tOZXRib3hEYXRhYCB2YWxpZGF0aW9uIGFuZCBjb2VyY2lvbiAoVVVJRCwgZGF0ZXRpbWUsIHBlci1maWVsZCByZXF1aXJlZCBjaGVja3MpLCBgQmFyZW1ldGFsTm9kZWAgb3B0aW9uYWwgZmllbGRzIGFuZCBgZGVmYXVsdF9mYWN0b3J5YCBpbmRlcGVuZGVuY2UsIGFsbC1vcHRpb25hbCBtb2RlbHMsIGFuZCBwYXJhbWV0cml6ZWQgcmVxdWlyZWQtZmllbGQgZW5mb3JjZW1lbnQgcGx1cyBgbW9kZWxfZHVtcCgpYCByb3VuZCB0cmlwcyBmb3IgdGhlIHJlc3BvbnNlIG1vZGVscywgaW5jbHVkaW5nIGRpY3QtdG8tYEJhcmVtZXRhbE5vZGVOZXRib3hJbmZvYCBjb2VyY2lvbiBhbmQgYEFueWAtdHlwZWQgdmFsdWUgZmllbGRzLgoKRW5kcG9pbnQgYmVoYXZpb3IgKEhUVFAgc3RhdHVzIGNvZGVzLCBtYXNraW5nIGFwcGxpZWQgaW4gcmVzcG9uc2VzKSBpcyBvdXQgb2Ygc2NvcGUgaGVyZSBhbmQgYmVsb25ncyB0byB0aGUgY29tcGFuaW9uIGVuZHBvaW50IGlzc3VlIChUaWVyIDcsIHNwbGl0IDIvMikuCgpDbG9zZXMgIzIzNTkKCvCfpJYgR2VuZXJhdGVkIHdpdGggW0NsYXVkZSBDb2RlXShodHRwczovL2NsYXVkZS5jb20vY2xhdWRlLWNvZGUp
      patchset: e6a91edf79b265bd9be4fe850ffa170784196ba3
      pipeline: check
      playbook_context:
        playbook_projects:
          trusted/project_0/github.com/osism/zuul-config:
            canonical_name: github.com/osism/zuul-config
            checkout: main
            commit: 71b711c422c10c242a8f785dc6d41ed9d12e9cd3
          trusted/project_1/github.com/osism/openinfra-zuul-jobs:
            canonical_name: github.com/osism/openinfra-zuul-jobs
            checkout: master
            commit: 242732586e2cba04d04867d3e302888c766bc231
          trusted/project_2/github.com/osism/zuul-jobs:
            canonical_name: github.com/osism/zuul-jobs
            checkout: main
            commit: 1d71e58c93442038e695a5d883d57a603182c7ae
          untrusted/project_0/github.com/osism/python-osism:
            canonical_name: github.com/osism/python-osism
            checkout: main
            commit: 0d22e944d4a67362665618bb242751815054f1dc
          untrusted/project_1/github.com/osism/zuul-config:
            canonical_name: github.com/osism/zuul-config
            checkout: main
            commit: 71b711c422c10c242a8f785dc6d41ed9d12e9cd3
          untrusted/project_2/github.com/osism/openinfra-zuul-jobs:
            canonical_name: github.com/osism/openinfra-zuul-jobs
            checkout: master
            commit: 242732586e2cba04d04867d3e302888c766bc231
          untrusted/project_3/github.com/osism/zuul-jobs:
            canonical_name: github.com/osism/zuul-jobs
            checkout: main
            commit: 1d71e58c93442038e695a5d883d57a603182c7ae
        playbooks:
        - path: untrusted/project_0/github.com/osism/python-osism/playbooks/build.yml
          roles:
          - checkout: main
            checkout_description: zuul branch
            link_name: ansible/playbook_0/role_1/zuul-config
            link_target: untrusted/project_1/github.com/osism/zuul-config
            role_path: ansible/playbook_0/role_1/zuul-config/roles
          - checkout: master
            checkout_description: project default branch
            link_name: ansible/playbook_0/role_2/openinfra-zuul-jobs
            link_target: untrusted/project_2/github.com/osism/openinfra-zuul-jobs
            role_path: ansible/playbook_0/role_2/openinfra-zuul-jobs/roles
          - checkout: main
            checkout_description: zuul branch
            link_name: ansible/playbook_0/role_3/zuul-jobs
            link_target: untrusted/project_3/github.com/osism/zuul-jobs
            role_path: ansible/playbook_0/role_3/zuul-jobs/roles
      post_review: false
      post_timeout: 1800
      pre_timeout: null
      project:
        canonical_hostname: github.com
        canonical_name: github.com/osism/python-osism
        name: osism/python-osism
        short_name: python-osism
        src_dir: src/github.com/osism/python-osism
      projects:
        github.com/osism/python-osism:
          canonical_hostname: github.com
          canonical_name: github.com/osism/python-osism
          checkout: main
          checkout_description: zuul branch
          commit: 0d22e944d4a67362665618bb242751815054f1dc
          name: osism/python-osism
          required: false
          short_name: python-osism
          src_dir: src/github.com/osism/python-osism
      ref: refs/pull/2463/head
      resources: {}
      tenant: osism
      timeout: 1800
      topic: null
      voting: true
