synology_apm_repo.sdk.api.catalog module

Catalog/Frame: one catalog’s own workload/version/provider operations, part of the Repository Layer (the Session/Repository/ Catalog split CLI/TUI code imports directly; everything below it is an implementation detail this package hides). Catalog is obtained from Repository.catalogs()/Repository.catalog_by_id() (the sibling api.repository module); the two modules meet at Repository.resolve()’s canonical/human-ref dispatch, which api.repository itself owns.

class synology_apm_repo.sdk.api.catalog.Frame(level, catalog=None, workload=None, version=None, provider=None, node=None)

Bases: object

Where a human ref’s segments landed after Repository.walk_human_ref — exactly as deep as they went, no deeper. level says which of the fields below (if any) is meaningful; the others are carried along for breadcrumb rendering and for continuing the walk one level further. Resolving a version always continues straight into its node tree (at least provider.root()), so level is never "version" on its own — a version and its provider only ever appear together with level="node".

level: Literal['root', 'catalog', 'workload', 'node']
catalog: Catalog | None = None
workload: Workload | None = None
version: Version | None = None
provider: UnitProvider | None = None
node: Node | None = None
class synology_apm_repo.sdk.api.catalog.Catalog(dedup_repo, connection, *, saas_streams, track, require_key_verified)

Bases: object

One catalog within an opened Repository: a single db/ connection_config row (connection) plus the workload/version/ provider operations scoped to it. Never constructed directly.

For a vault, several sibling Catalog``s share one underlying ``DedupRepo (one physical dedup pool) — this mirrors one opened Pool/db set queried for several connection_config rows. For object storage, each Catalog owns its own independently-opened DedupRepo — a distinct physical pool per sibling repo-id (FORMAT-SPEC.md: no cross-repo-id dedup).

saas_streams is shared, not owned: it’s the Repository-level SaasStreamCache built against this same dedup_repo (see api.repository._OpenCatalog), borrowed by every SaaS provider this catalog hands out via provider() rather than each opening its own.

A provider this hands out is tracked by the owning Repository — Repository.close() closes it, not this object; Catalog itself has no close().

property display_name: str
property catalog_id: CatalogId

The shared repo-id/connection-config-id fallback formula (also used by units.node_ref.canonical_ref_for, so both call sites derive the same id): self._dedup_repo.layout.repo_id (this catalog’s own repo-id, set on every RepoLayout catalog_repo_layouts() derives for OBJECT_STORE) is used when available, falling back to str(connection_config_id) for a vault (whose RepoLayout.repo_id is always None) and for the one object-storage edge case with no derivable repo-id (see storage.layout’s _data_dir_ancestor) — safe there too, since that case only arises when this is the sole catalog reachable from its Repository anyway.

property info: RepoInfo

This catalog’s own repo_info — genuinely per-catalog for object storage (each repo-id has its own marker file), and the vault-wide one shared by every sibling for a vault.

async workloads()

Raises KeyRequiredError/KeyMismatchError before any I/O if the owning repository is encrypted and not yet key-verified — see Repository._require_key_verified. Without this, a locked repository’s workload list would otherwise come back silently empty or wrong, indistinguishable from “this catalog genuinely has no workloads”.

async versions(workload, *, include_deleted=False)

The raw catalog read (newest-first by real backup time, ties broken by version_id descending) — nothing is filtered out. A version whose content later turns out unresolvable (a genuine gap, or routine backend-side generation rotation the resolving Unit already absorbs — see units.saas.stream) raises when actually opened (VM/FS, GW/M365) or surfaces a diagnostic node in place of the missing disk (PC/PS — see units.device_pcps), the same as verify() already treats it; this method never hides a real catalog row in advance of that. Same key gate as workloads().

async provider(version, *, object_db_id=None, force_raw=False)

Dispatches version to the right UnitProvider by target_type/Workload.sub_type, degrading to RawObjectProvider when nothing recognizes it.

async verify(level=VerifyLevel.QUICK, *, progress=None)

Integrity check over this catalog’s own DedupRepo — for a vault, where several sibling Catalog``s share one ``DedupRepo, this is the same check as every sibling’s own verify(), not one scoped to this catalog’s own rows alone (the top-down walk checks the shared pool via every catalog’s own workloads/versions, not one connection’s data alone). Same key gate as workloads()/versions().

async walk_human_ref(segments, *, object_db_id=None)

The three levels below a chosen catalog — workload -> version -> item — raising NotFoundError the moment a segment doesn’t match. segments is never empty here: Repository.walk_human_ref (this method’s only caller) already returns its own "catalog" Frame directly once a human ref names nothing past the catalog itself.