synology_apm_repo.sdk.storage.layout module¶
Repository layout detection (FORMAT-SPEC.md: repo-root-layout).
Two shapes exist, both placed by fixed, product-enforced logic — never nested arbitrarily deep:
Vault (APV, or any local-filesystem Copy destination): a directory (conventionally
@ActiveProtectVault, though the name itself isn’t load-bearing — detection is by content marker, not name) is the repository root directly, created exactly one level under the admin-chosen shared folder. Identified by the coexistence ofrepo_info,link.keyand.fully_createdat that exact path. Exactly one repository per such root, and exactly one vault per shared folder.Object storage (S3/Azure landed copies): the root contains an
@ActiveProtectData/<12-char-repo-id>/subtree, possibly with several sibling<repo-id>directories, and a parallel@ActiveProtectKey/{userKey,link}/tree one level up from@ActiveProtectData. Each<repo-id>is an independent repository sharing the same key tree.repo_infounder an object-store repository root is not a reliable marker by itself — it may carry a.<N>generation suffix and have no bare-named file at all.db/and@dataare both always present as bare-named directories regardless of generation suffixing, so the pair of them is the marker used here — requiring both, not justdb, reduces the chance of a false positive on some unrelated directory that merely happens to contain adbsubdirectory of its own.
Since neither shape is ever nested more than one level under whatever an
admin actually provisioned (a shared folder, a bucket), a connection
pointed anywhere from that level down to two levels above it still
finds every repository. _DEFAULT_MAX_DEPTH bounds the walk at exactly
that — not the fully open-ended search a misdirected scan of an unrelated
directory tree could run away into, but not artificially limited to a
single level either.
- class synology_apm_repo.sdk.storage.layout.RepoKind(value)¶
Bases:
EnumWhich of the two physical repository backends a
RepoLayoutdescribes.- VAULT = 'vault'¶
- OBJECT_STORE = 'object_store'¶
- class synology_apm_repo.sdk.storage.layout.RepoLayout(kind, repo_root, key_root=None, repo_id=None)¶
Bases:
objectWhere one repository lives within an
ObjectStore.repo_rootandkey_rootare store-relative paths (""means “the store’s own root”), never absolute paths — anObjectStorehas no such notion.
- async synology_apm_repo.sdk.storage.layout.iter_layouts(store, root='', *, max_depth=2)¶
Walk down from
rootlooking for repository roots, yielding each as it is found.Cheap by construction: only
exists()/listdir()calls at each level, never a scan intoPool/Composition/db. A vault root or object-store bucket root ends the walk along that branch (no repository nests inside another);max_depthbounds how far it goes otherwise, at the 2-level default derived in this module’s docstring above.Total result count is unknowable in advance — callers doing interactive discovery should treat this as an indeterminate-progress operation and consume it lazily.
- async synology_apm_repo.sdk.storage.layout.detect_layout(store, root='')¶
Detect the single repository layout directly at
root— for the common case where the caller already knowsrootis exactly one repository (e.g. it was chosen from a prioriter_layoutsscan).Raises
NotFoundErrorifrootis not itself a repository root, or is an object-store bucket root containing more than one (or zero) repository ids — callers facing that ambiguity should useiter_layoutsinstead.
- class synology_apm_repo.sdk.storage.layout.RepositoryLayout(kind, repo_root, key_root=None, catalog_ids=None)¶
Bases:
objectWhere one Repository (a bucket, or a vault’s own shared folder) lives within an
ObjectStore.repo_root/key_rootare store-relative paths (""means “the store’s own root”), never absolute — same convention asRepoLayout.catalog_idsis populated forOBJECT_STOREby listing@ActiveProtectData/’s children (cheap — already known at detection time, the same listing_layouts_at’s own second branch already does); leftNoneforVAULT, whose catalogs (db/connection_configrows) can only be found later, by querying after opening — and also leftNonefor the one genuinely ambiguousOBJECT_STOREcase:rootis itself an individual repository directory with no derivable bucket-root ancestor (see_data_dir_ancestor), where the one implicit catalog living there can’t be independently identified either. An empty list (as opposed toNone) means a real, listable@ActiveProtectDatawas found but currently has zero valid repo-id children.
- async synology_apm_repo.sdk.storage.layout.iter_repository_layouts(store, root='', *, max_depth=2)¶
Walk down from
rootlooking for Repository roots (a bucket, or a vault’s own shared folder), yielding each as it is found — the Repository-level counterpart toiter_layouts.Unlike
iter_layouts, a bucket holding several sibling catalogs yields oneRepositoryLayout(withcatalog_idslisting every sibling found), never several — there is no “which repository id” ambiguity at this level to resolve.
- synology_apm_repo.sdk.storage.layout.catalog_repo_layouts(layout)¶
The individual, directly-openable
RepoLayout(s)layoutresolves to — exactly whatDedupRepo.open()already consumes unchanged; this is the only placeRepositoryLayoutandRepoLayoutmeet.A vault (or the one genuinely ambiguous
OBJECT_STOREcase —catalog_idsisNone) opens as a singleRepoLayoutatlayout.repo_rootitself: a vault’s own catalogs come from queryingdb/connection_configafter opening, not a separate directory per catalog. Object storage opens oneRepoLayoutpercatalog_idsentry, each rooted at that catalog’s own@ActiveProtectData/<repo-id>subdirectory — every sibling shareslayout.key_root, since the key tree lives one level above@ActiveProtectData, not inside any one repo-id’s own directory.Returns an empty list only when
layout.catalog_idsis itself a real, empty list (a listable@ActiveProtectDatawith zero valid repo-id children) — nothing to open at all in that case.
- synology_apm_repo.sdk.storage.layout.key_probe_layout(layout)¶
A throwaway
RepoLayoutcarrying only the fieldsdedup.keys’ free functions (probe_encrypted/resolve_vault_key/verify) actually read for key/encryption resolution, straight off a bucket-rootedRepositoryLayout— safe to build with no catalog opened at all:VAULTreadsrepo_root(a vault’s ownrepo_rootis the same value either way),OBJECT_STOREreads onlykey_root(shared by every sibling catalog, neverrepo_root). Used both before aRepositoryexists yet (api.session’s own discovery-time key/encryption probe) and by an already-constructed one (Repository.set_key()) — the one shared place this derivation lives, rather than two copies drifting apart.
- async synology_apm_repo.sdk.storage.layout.detect_repository_layout(store, root='')¶
Detect the single Repository layout directly at
root— the Repository-level counterpart todetect_layout.Raises
NotFoundErrorifrootis not itself a repository root. Unlikedetect_layout, never raises for “several repository ids found” — that’s no longer ambiguous at the Repository level, it’scatalog_idshaving length greater than one; a caller wanting a single, already-disambiguated catalog picks one fromcatalog_ids(or queries them after opening, for a vault) rather than re-scanning at a narrower root.