diff --git a/.bumpversion.cfg b/.bumpversion.cfg index c8ea28be..e5efc804 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -3,7 +3,7 @@ # [bumpversion] -current_version = 5.1.0 +current_version = 5.2.0 commit = False tag = False parse = (?P\d+)\.(?P\d+)\.(?P\d+)(\.(?P[a-z]+)(?P\d+))? diff --git a/common/pyproject.toml b/common/pyproject.toml index 65d0c213..c55ba570 100644 --- a/common/pyproject.toml +++ b/common/pyproject.toml @@ -8,7 +8,7 @@ build-backend = "setuptools.build_meta" [project] name = "dvp-common" -version = "5.1.0" +version = "5.2.0" readme = "README.md" requires-python = ">=3.11, <3.12" authors = [ @@ -23,7 +23,7 @@ classifiers = [ "Operating System :: OS Independent", ] dependencies = [ - "dvp-api == 1.10.0", + "dvp-api == 1.11.0.dev0", "six >= 1.17, < 1.18", ] diff --git a/docs/docs/References/Decorators.md b/docs/docs/References/Decorators.md index b5ba747a..85c2f35f 100644 --- a/docs/docs/References/Decorators.md +++ b/docs/docs/References/Decorators.md @@ -28,6 +28,7 @@ Plugin Operation | Decorator [Direct Linked Source Pre-Snapshot](Plugin_Operations.md#direct-linked-source-pre-snapshot) | `@plugin.linked.pre_snapshot()` [Direct Linked Source Post-Snapshot](Plugin_Operations.md#direct-linked-source-post-snapshot) | `@plugin.linked.post_snapshot()` [Direct Linked Source Size](Plugin_Operations.md#direct-linked-source-size) | `@plugin.linked.source_size()` +[Direct Linked Source Pre-Source-to-Physical](Plugin_Operations.md#direct-linked-source-pre-source-to-physical) | `@plugin.linked.pre_source_to_physical()` [Direct Linked Source to Physical](Plugin_Operations.md#direct-linked-source-to-physical) | `@plugin.linked.source_to_physical()` [Staged Linked Source Pre-Snapshot](Plugin_Operations.md#staged-linked-source-pre-snapshot) | `@plugin.linked.pre_snapshot()` [Staged Linked Source Post-Snapshot](Plugin_Operations.md#staged-linked-source-post-snapshot) | `@plugin.linked.post_snapshot()` @@ -37,6 +38,7 @@ Plugin Operation | Decorator [Staged Linked Source Worker](Plugin_Operations.md#staged-linked-source-worker) | `@plugin.linked.worker()` [Staged Linked Source Mount Specification](Plugin_Operations.md#staged-linked-source-mount-specification) | `@plugin.linked.mount_specification()` [Staged Linked Source Size](Plugin_Operations.md#staged-linked-source-size) | `@plugin.linked.source_size()` +[Staged Linked Source Pre-Source-to-Physical](Plugin_Operations.md#staged-linked-source-pre-source-to-physical) | `@plugin.linked.pre_source_to_physical()` [Staged Linked Source to Physical](Plugin_Operations.md#staged-linked-source-to-physical) | `@plugin.linked.source_to_physical()` [Virtual Source Configure](Plugin_Operations.md#virtual-source-configure) | `@plugin.virtual.configure()` [Virtual Source Initialize](Plugin_Operations.md#virtual-source-initialize) | `@plugin.virtual.initialize()` @@ -50,6 +52,7 @@ Plugin Operation | Decorator [Virtual Source Mount Specification](Plugin_Operations.md#virtual-source-mount-specification) | `@plugin.virtual.mount_specification()` [Virtual Source Status](Plugin_Operations.md#virtual-source-status) | `@plugin.virtual.status()` [Virtual Source Size](Plugin_Operations.md#virtual-source-size) | `@plugin.virtual.source_size()` +[Virtual Source Pre-Source-to-Physical](Plugin_Operations.md#virtual-source-pre-source-to-physical) | `@plugin.virtual.pre_source_to_physical()` [Virtual Source to Physical](Plugin_Operations.md#virtual-source-to-physical) | `@plugin.virtual.source_to_physical()` [Repository Data Migration](Plugin_Operations.md#repository-data-migration) | `@plugin.upgrade.repository(migration_id)` [Source Config Data Migration](Plugin_Operations.md#source-config-data-migration) | `@plugin.upgrade.source_config(migration_id)` diff --git a/docs/docs/References/Plugin_Operations.md b/docs/docs/References/Plugin_Operations.md index c11a915a..1039d579 100644 --- a/docs/docs/References/Plugin_Operations.md +++ b/docs/docs/References/Plugin_Operations.md @@ -15,6 +15,7 @@ Plugin Operation | **Required** | Decorator | Delphix Engine Operations [Direct Linked Source
Pre-Snapshot](#direct-linked-source-pre-snapshot) | **No** | `linked.pre_snapshot()` | [Linked Source Sync](Workflows.md#linked-source-sync) [Direct Linked Source
Post-Snapshot](#direct-linked-source-post-snapshot) | **Yes** | `linked.post_snapshot()` | [Linked Source Sync](Workflows.md#linked-source-sync) [Direct Linked Source
Source Size](#direct-linked-source-size) | **No** | `linked.source_size()` | N/A +[Direct Linked Source
Pre-Source-to-Physical](#direct-linked-source-pre-source-to-physical) | **No** | `linked.pre_source_to_physical()` | [Linked Source to Physical](Workflows.md#linked-source-to-physical) [Direct Linked Source
to Physical](#direct-linked-source-to-physical) | **No** | `linked.source_to_physical()` | [Linked Source to Physical](Workflows.md#linked-source-to-physical) [Staged Linked Source
Pre-Snapshot](#staged-linked-source-pre-snapshot) | **No** | `linked.pre_snapshot()` | [Linked Source Sync](Workflows.md#linked-source-sync) [Staged Linked Source
Post-Snapshot](#staged-linked-source-post-snapshot) | **Yes** | `linked.post_snapshot()` | [Linked Source Sync](Workflows.md#linked-source-sync) @@ -24,6 +25,7 @@ Plugin Operation | **Required** | Decorator | Delphix Engine Operations [Staged Linked Source
Worker](#staged-linked-source-worker) | **No** |`linked.worker()` | N/A [Staged Linked Source
Mount Specification](#staged-linked-source-mount-specification) | **Yes** | `linked.mount_specification()` | [Linked Source Sync](Workflows.md#linked-source-sync)
[Linked Source Enable](Workflows.md#linked-source-enable) [Staged Linked Source
Source Size](#staged-linked-source-size) | **No** | `linked.source_size()` | N/A +[Staged Linked Source
Pre-Source-to-Physical](#staged-linked-source-pre-source-to-physical) | **No** | `linked.pre_source_to_physical()` | [Linked Source to Physical](Workflows.md#linked-source-to-physical) [Staged Linked Source
to Physical](#staged-linked-source-to-physical) | **No** | `linked.source_to_physical()` | [Linked Source to Physical](Workflows.md#linked-source-to-physical) [Virtual Source
Initialize](#virtual-source-initialize) | **No** | `virtual.initialize()` | [Virtual Source Create Empty VDB](Workflows.md#virtual-source-create-empty-vdb) [Virtual Source
Configure](#virtual-source-configure) | **Yes** | `virtual.configure()` | [Virtual Source Provision](Workflows.md#virtual-source-provision)
[Virtual Source Refresh](Workflows.md#virtual-source-refresh) @@ -37,6 +39,7 @@ Plugin Operation | **Required** | Decorator | Delphix Engine Operations [Virtual Source
Mount Specification](#virtual-source-mount-specification) | **Yes** | `virtual.mount_specification()` | [Virtual Source Enable](Workflows.md#virtual-source-enable)
[Virtual Source Provision](Workflows.md#virtual-source-provision)
[Virtual Source Refresh](Workflows.md#virtual-source-refresh)
[Virtual Source Rollback](Workflows.md#virtual-source-rollback)
[Virtual Source Start](Workflows.md#virtual-source-start) [Virtual Source
Status](#virtual-source-status) | **No** | `virtual.status()` | [Virtual Source Enable](Workflows.md#virtual-source-enable) [Virtual Source
Source Size](#virtual-source-size) | **No** | `virtual.source_size()` | N/A +[Virtual Source
Pre-Source-to-Physical](#virtual-source-pre-source-to-physical) | **No** | `virtual.pre_source_to_physical()` | [Virtual Source to Physical](Workflows.md#virtual-source-to-physical) [Virtual Source
to Physical](#virtual-source-to-physical) | **No** | `virtual.source_to_physical()` | [Virtual Source to Physical](Workflows.md#virtual-source-to-physical) [Repository Data Migration](#repository-data-migration) | **No** | `upgrade.repository(migration_id)` | [Upgrade](Workflows.md#upgrade) [Source Config Data Migration](#source-config-data-migration) | **No** | `upgrade.source_config(migration_id)` | [Upgrade](Workflows.md#upgrade) @@ -316,6 +319,52 @@ def linked_source_size(direct_source, repository, source_config): return database_size ``` +## Direct Linked Source Pre-Source-to-Physical + +Executed immediately before the file-copy for a [Linked Source to Physical](Workflows.md#linked-source-to-physical) workflow begins, giving a plugin the chance to reject an unsuitable target before any data moves. Its post-copy counterpart is [Direct Linked Source to Physical](#direct-linked-source-to-physical). + +### Required / Optional +**Optional.** + +### Delphix Engine Operations + +* [Linked Source to Physical](Workflows.md#linked-source-to-physical) + +### Signature + +`def direct_pre_source_to_physical(direct_source, repository, source_config, snapshot, physical_source)` + +### Decorator + +`linked.pre_source_to_physical()` + +### Arguments + +Argument | Type | Description +-------- | ---- | ----------- +direct_source | [DirectSource](Classes.md#directsource) | The direct linked source being exported. +repository | [RepositoryDefinition](Schemas_and_Autogenerated_Classes.md#repositorydefinition-class) | The repository associated with this source. +source_config | [SourceConfigDefinition](Schemas_and_Autogenerated_Classes.md#sourceconfigdefinition-class) | The source config associated with this source. +snapshot | [SnapshotDefinition](Schemas_and_Autogenerated_Classes.md#snapshotdefinition-class) | The snapshot being exported. +physical_source | [PhysicalSource](Classes.md#physicalsource) | The target physical source, including connection, target directory, and user-defined parameters from the [VirtualToPhysicalDefinition](Schemas_and_Autogenerated_Classes.md#virtualtophysicaldefinition-class). + +### Returns +None + +### Example + +```python +from dlpx.virtualization.platform import Plugin + +plugin = Plugin() + +@plugin.linked.pre_source_to_physical() +def direct_pre_source_to_physical(direct_source, repository, source_config, snapshot, physical_source): + target_dir = physical_source.target_directory + # Validate target_dir is suitable before the copy begins; raise to abort the copy. + pass +``` + ## Direct Linked Source to Physical Converts a [Direct Linked Source](Glossary.md#direct-linking) to a physical source. Only applies when a physical export is requested on a dSource using a [Direct Linking](Glossary.md#direct-linking) strategy. @@ -739,6 +788,52 @@ def linked_source_size(staged_source, repository, source_config): return database_size ``` +## Staged Linked Source Pre-Source-to-Physical + +Executed immediately before the file-copy for a [Linked Source to Physical](Workflows.md#linked-source-to-physical) workflow begins, giving a plugin the chance to reject an unsuitable target before any data moves. Its post-copy counterpart is [Staged Linked Source to Physical](#staged-linked-source-to-physical). + +### Required / Optional +**Optional.** + +### Delphix Engine Operations + +* [Linked Source to Physical](Workflows.md#linked-source-to-physical) + +### Signature + +`def staged_pre_source_to_physical(staged_source, repository, source_config, snapshot, physical_source)` + +### Decorator + +`linked.pre_source_to_physical()` + +### Arguments + +Argument | Type | Description +-------- | ---- | ----------- +staged_source | [StagedSource](Classes.md#stagedsource) | The staged linked source being exported. +repository | [RepositoryDefinition](Schemas_and_Autogenerated_Classes.md#repositorydefinition-class) | The repository associated with this source. +source_config | [SourceConfigDefinition](Schemas_and_Autogenerated_Classes.md#sourceconfigdefinition-class) | The source config associated with this source. +snapshot | [SnapshotDefinition](Schemas_and_Autogenerated_Classes.md#snapshotdefinition-class) | The snapshot being exported. +physical_source | [PhysicalSource](Classes.md#physicalsource) | The target physical source, including connection, target directory, and user-defined parameters from the [VirtualToPhysicalDefinition](Schemas_and_Autogenerated_Classes.md#virtualtophysicaldefinition-class). + +### Returns +None + +### Example + +```python +from dlpx.virtualization.platform import Plugin + +plugin = Plugin() + +@plugin.linked.pre_source_to_physical() +def staged_pre_source_to_physical(staged_source, repository, source_config, snapshot, physical_source): + target_dir = physical_source.target_directory + # Validate target_dir is suitable before the copy begins; raise to abort the copy. + pass +``` + ## Staged Linked Source to Physical Converts a [Staged Linked Source](Glossary.md#staged-linking) to a physical source. Only applies when a physical export is requested on a dSource using a [Staged Linking](Glossary.md#staged-linking) strategy. @@ -1390,6 +1485,52 @@ def virtual_source_size(virtual_source, repository, source_config): ``` +## Virtual Source Pre-Source-to-Physical + +Executed immediately before the file-copy for a [Virtual Source to Physical](Workflows.md#virtual-source-to-physical) workflow begins, giving a plugin the chance to reject an unsuitable target before any data moves. Its post-copy counterpart is [Virtual Source to Physical](#virtual-source-to-physical). + +### Required / Optional +**Optional.** + +### Delphix Engine Operations + +* [Virtual Source to Physical](Workflows.md#virtual-source-to-physical) + +### Signature + +`def virtual_pre_source_to_physical(virtual_source, repository, source_config, snapshot, physical_source)` + +### Decorator + +`virtual.pre_source_to_physical()` + +### Arguments + +Argument | Type | Description +-------- | ---- | ----------- +virtual_source | [VirtualSource](Classes.md#virtualsource) | The virtual source being exported. +repository | [RepositoryDefinition](Schemas_and_Autogenerated_Classes.md#repositorydefinition-class) | The repository associated with this source. +source_config | [SourceConfigDefinition](Schemas_and_Autogenerated_Classes.md#sourceconfigdefinition-class) | The source config associated with this source. +snapshot | [SnapshotDefinition](Schemas_and_Autogenerated_Classes.md#snapshotdefinition-class) | The snapshot being exported. +physical_source | [PhysicalSource](Classes.md#physicalsource) | The target physical source, including connection, target directory, and user-defined parameters from the [VirtualToPhysicalDefinition](Schemas_and_Autogenerated_Classes.md#virtualtophysicaldefinition-class). + +### Returns +None + +### Example + +```python +from dlpx.virtualization.platform import Plugin + +plugin = Plugin() + +@plugin.virtual.pre_source_to_physical() +def virtual_pre_source_to_physical(virtual_source, repository, source_config, snapshot, physical_source): + target_dir = physical_source.target_directory + # Validate target_dir is suitable before the copy begins; raise to abort the copy. + pass +``` + ## Virtual Source to Physical Converts a [Virtual Source](Glossary.md#virtual-source) to a physical source. Only applies when a physical export is requested on a VDB. diff --git a/docs/docs/References/Version_Compatibility.md b/docs/docs/References/Version_Compatibility.md index e5365419..f12da818 100644 --- a/docs/docs/References/Version_Compatibility.md +++ b/docs/docs/References/Version_Compatibility.md @@ -4,6 +4,7 @@ | vSDK Version | Earliest Supported DE Version | Latest Supported DE Version | |------------------------------------------|:-----------------------------:|:-----------------------------------------------------------------:| +| [5.2.0](../Release_Notes/5.2.0/5.2.0.md) | 2026.5.0.0 | [Latest Release](https://help.delphix.com/cd/current/content/release_notes.htm) | | [5.1.0](../Release_Notes/5.1.0/5.1.0.md) | 2026.4.0.0 | [Latest Release](https://help.delphix.com/cd/current/content/release_notes.htm) | | [5.0.1](../Release_Notes/5.0.1/5.0.1.md) | 2025.2.0.0 | [Latest Release](https://help.delphix.com/cd/current/content/release_notes.htm) | | [5.0.0](../Release_Notes/5.0.0/5.0.0.md) | 29.0.0.0 | [Latest Release](https://help.delphix.com/cd/current/content/release_notes.htm) | @@ -21,6 +22,7 @@ | vSDK Version | Python Version | |------------------------------------------|:--------------:| +| [5.2.0](../Release_Notes/5.2.0/5.2.0.md) | 3.11 | | [5.1.0](../Release_Notes/5.1.0/5.1.0.md) | 3.11 | | [5.0.1](../Release_Notes/5.0.1/5.0.1.md) | 3.11 | | [5.0.0](../Release_Notes/5.0.0/5.0.0.md) | 3.11 | diff --git a/docs/docs/References/html/LinkedSourceToPhysical.html b/docs/docs/References/html/LinkedSourceToPhysical.html index ce8ff82e..123fb516 100644 --- a/docs/docs/References/html/LinkedSourceToPhysical.html +++ b/docs/docs/References/html/LinkedSourceToPhysical.html @@ -2,10 +2,11 @@ -Untitled Diagram +LinkedSourceToPhysical.html -
- + +
+ - \ No newline at end of file + diff --git a/docs/docs/References/html/VirtualSourceToPhysical.html b/docs/docs/References/html/VirtualSourceToPhysical.html index 452864d5..05b3ead2 100644 --- a/docs/docs/References/html/VirtualSourceToPhysical.html +++ b/docs/docs/References/html/VirtualSourceToPhysical.html @@ -2,10 +2,11 @@ -Untitled Diagram +VirtualSourceToPhysical.html -
- + +
+ - \ No newline at end of file + diff --git a/docs/docs/References/images/LinkedSourceToPhysical.png b/docs/docs/References/images/LinkedSourceToPhysical.png index 9007ad7f..e37a8bf1 100644 Binary files a/docs/docs/References/images/LinkedSourceToPhysical.png and b/docs/docs/References/images/LinkedSourceToPhysical.png differ diff --git a/docs/docs/References/images/VirtualSourceToPhysical.png b/docs/docs/References/images/VirtualSourceToPhysical.png index 5d2c0dcc..ca2b239f 100644 Binary files a/docs/docs/References/images/VirtualSourceToPhysical.png and b/docs/docs/References/images/VirtualSourceToPhysical.png differ diff --git a/docs/docs/Release_Notes/.pages b/docs/docs/Release_Notes/.pages index d7881402..88b115ed 100644 --- a/docs/docs/Release_Notes/.pages +++ b/docs/docs/Release_Notes/.pages @@ -1,4 +1,5 @@ arrange: + - 5.2.0 - 5.1.0 - 5.0.1 - 5.0.0 diff --git a/docs/docs/Release_Notes/5.2.0/5.2.0.md b/docs/docs/Release_Notes/5.2.0/5.2.0.md new file mode 100644 index 00000000..9a371ec6 --- /dev/null +++ b/docs/docs/Release_Notes/5.2.0/5.2.0.md @@ -0,0 +1,14 @@ +# Release - v5.2.0 + +To install or upgrade the SDK, refer to instructions [here](../../Getting_Started.md#installation). + +## New & Improved + +* Added support for validating a target *before* a virtual or linked source is exported to a physical source, via the new `pre_source_to_physical` operation — a pre-copy counterpart to the existing `source_to_physical` operation. + * Use `@plugin.virtual.pre_source_to_physical()` to validate the target before physical export for VDBs. See [Virtual Source Pre-Source-to-Physical](../../References/Plugin_Operations.md#virtual-source-pre-source-to-physical). + * Use `@plugin.linked.pre_source_to_physical()` to validate the target before physical export for dSources (both direct and staged linking). See [Direct Linked Source Pre-Source-to-Physical](../../References/Plugin_Operations.md#direct-linked-source-pre-source-to-physical) and [Staged Linked Source Pre-Source-to-Physical](../../References/Plugin_Operations.md#staged-linked-source-pre-source-to-physical). + * Reuses the existing [PhysicalSource](../../References/Classes.md#physicalsource) class — no new schema or generated classes. + +## Breaking Changes + +None. diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index cfb23989..a23c583d 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -1,4 +1,4 @@ -site_name: Delphix Virtualization SDK 5.1.0 +site_name: Delphix Virtualization SDK 5.2.0 repo_name: Delphix Virtualization SDK repo_url: https://github.com/delphix/virtualization-sdk/ diff --git a/dvp/pyproject.toml b/dvp/pyproject.toml index df862ab7..3037239c 100644 --- a/dvp/pyproject.toml +++ b/dvp/pyproject.toml @@ -8,7 +8,7 @@ build-backend = "setuptools.build_meta" [project] name = "dvp" -version = "5.1.0" +version = "5.2.0" description = "Delphix Virtualization Platform SDK" readme = "README.md" requires-python = ">=3.11, <3.12" @@ -26,10 +26,10 @@ classifiers = [ ] # Sibling-package pins; .bumpversion.cfg keeps these in sync with [project].version. dependencies = [ - "dvp-common == 5.1.0", - "dvp-libs == 5.1.0", - "dvp-platform == 5.1.0", - "dvp-tools == 5.1.0", + "dvp-common == 5.2.0", + "dvp-libs == 5.2.0", + "dvp-platform == 5.2.0", + "dvp-tools == 5.2.0", ] [project.optional-dependencies] diff --git a/libs/pyproject.toml b/libs/pyproject.toml index f7a4c126..8574b586 100644 --- a/libs/pyproject.toml +++ b/libs/pyproject.toml @@ -8,7 +8,7 @@ build-backend = "setuptools.build_meta" [project] name = "dvp-libs" -version = "5.1.0" +version = "5.2.0" description = "Delphix Virtualization Platform Libraries" readme = "README.md" requires-python = ">=3.11, <3.12" @@ -25,8 +25,8 @@ classifiers = [ ] # dvp-common pin is kept in sync with [project].version via .bumpversion.cfg. dependencies = [ - "dvp-api == 1.10.0", - "dvp-common == 5.1.0", + "dvp-api == 1.11.0.dev0", + "dvp-common == 5.2.0", "six >= 1.17, < 1.18", ] diff --git a/platform/pyproject.toml b/platform/pyproject.toml index f03e2346..9429092a 100644 --- a/platform/pyproject.toml +++ b/platform/pyproject.toml @@ -8,7 +8,7 @@ build-backend = "setuptools.build_meta" [project] name = "dvp-platform" -version = "5.1.0" +version = "5.2.0" description = "Delphix Virtualization Platform APIs" readme = "README.md" requires-python = ">=3.11, <3.12" @@ -25,8 +25,8 @@ classifiers = [ ] # dvp-common pin is kept in sync with [project].version via .bumpversion.cfg. dependencies = [ - "dvp-api == 1.10.0", - "dvp-common == 5.1.0", + "dvp-api == 1.11.0.dev0", + "dvp-common == 5.2.0", "six >= 1.17, < 1.18", ] diff --git a/platform/src/main/python/dlpx/virtualization/platform/_linked.py b/platform/src/main/python/dlpx/virtualization/platform/_linked.py index dbc2a94c..aab804d9 100644 --- a/platform/src/main/python/dlpx/virtualization/platform/_linked.py +++ b/platform/src/main/python/dlpx/virtualization/platform/_linked.py @@ -33,6 +33,7 @@ def __init__(self): self.worker_impl = None self.mount_specification_impl = None self.source_size_impl = None + self.pre_source_to_physical_impl = None self.source_to_physical_impl = None def pre_snapshot(self): @@ -113,6 +114,16 @@ def source_size_decorator(source_size_impl): return source_size_decorator + def pre_source_to_physical(self): + def pre_source_to_physical_decorator(pre_source_to_physical_impl): + if self.pre_source_to_physical_impl: + raise OperationAlreadyDefinedError(Op.LINKED_PRE_SOURCE_TO_PHYSICAL) + self.pre_source_to_physical_impl = v.check_function( + pre_source_to_physical_impl, Op.LINKED_PRE_SOURCE_TO_PHYSICAL) + return pre_source_to_physical_impl + + return pre_source_to_physical_decorator + def source_to_physical(self): def source_to_physical_decorator(source_to_physical_impl): if self.source_to_physical_impl: @@ -334,6 +345,73 @@ def _internal_direct_source_size(self, request): return direct_source_size_response + def _internal_direct_pre_source_to_physical(self, request): + """Pre Direct Source to Physical Wrapper for direct plugins. + + Executed immediately before the file-copy subtask begins, giving + the plugin a chance to reject an unsuitable target before any data + moves. + + Run pre_source_to_physical operation for a direct source. + + Args: + request (DirectPreSourceToPhysicalRequest): Pre Direct Source to + Physical Request arguments. + + Returns: + DirectPreSourceToPhysicalResponse: A response containing the return + value - DirectPreSourceToPhysicalResult. In case of errors, response + object will contain PluginErrorResult. + """ + # Reasoning for method imports are in this file's docstring. + from generated.definitions import RepositoryDefinition + from generated.definitions import LinkedSourceDefinition + from generated.definitions import SourceConfigDefinition + from generated.definitions import SnapshotDefinition + from generated.definitions import VirtualToPhysicalDefinition + + # + # While linked.pre_source_to_physical() is not a required operation, + # this should not be called if it wasn't implemented. + # + if not self.pre_source_to_physical_impl: + raise OperationNotDefinedError(Op.LINKED_PRE_SOURCE_TO_PHYSICAL) + + direct_source_definition = LinkedSourceDefinition.from_dict( + json.loads(request.direct_source.linked_source.parameters.json)) + direct_source = DirectSource( + guid=request.direct_source.linked_source.guid, + connection=RemoteConnection.from_proto( + request.direct_source.connection), + parameters=direct_source_definition) + virtual_to_physical_source_definition = VirtualToPhysicalDefinition.from_dict( + json.loads(request.physical_source.parameters.json)) + physical_source = PhysicalSource( + guid=request.physical_source.guid, + connection=RemoteConnection.from_proto(request.physical_source.connection), + target_directory=request.physical_source.target_directory, + parameters=virtual_to_physical_source_definition) + + repository = RepositoryDefinition.from_dict( + json.loads(request.repository.parameters.json)) + source_config = SourceConfigDefinition.from_dict( + json.loads(request.source_config.parameters.json)) + snapshot = SnapshotDefinition.from_dict( + json.loads(request.snapshot.parameters.json)) + + self.pre_source_to_physical_impl( + direct_source=direct_source, + repository=repository, + source_config=source_config, + snapshot=snapshot, + physical_source=physical_source) + + direct_pre_to_physical_response = ( + platform_pb2.DirectPreSourceToPhysicalResponse()) + direct_pre_to_physical_response.return_value.CopyFrom( + platform_pb2.DirectPreSourceToPhysicalResult()) + return direct_pre_to_physical_response + def _internal_direct_source_to_physical(self, request): """Direct Source to Physical Wrapper for direct plugins. @@ -930,6 +1008,80 @@ def _internal_staged_source_size(self, request): return staged_source_size_response + def _internal_staged_pre_source_to_physical(self, request): + """Pre Staged Source to Physical Wrapper for staged plugins. + + Executed immediately before the file-copy subtask begins, giving + the plugin a chance to reject an unsuitable target before any data + moves. + + Run pre_source_to_physical operation for a staged source. + + Args: + request (StagedPreSourceToPhysicalRequest): Pre Staged Source to + Physical Request arguments. + + Returns: + StagedPreSourceToPhysicalResponse: A response containing the return + value - StagedPreSourceToPhysicalResult. In case of errors, response + object will contain PluginErrorResult. + """ + # Reasoning for method imports are in this file's docstring. + from generated.definitions import RepositoryDefinition + from generated.definitions import LinkedSourceDefinition + from generated.definitions import SourceConfigDefinition + from generated.definitions import SnapshotDefinition + from generated.definitions import VirtualToPhysicalDefinition + + # + # While linked.pre_source_to_physical() is not a required operation, + # this should not be called if it wasn't implemented. + # + if not self.pre_source_to_physical_impl: + raise OperationNotDefinedError(Op.LINKED_PRE_SOURCE_TO_PHYSICAL) + + staged_source_definition = LinkedSourceDefinition.from_dict( + json.loads(request.staged_source.linked_source.parameters.json)) + staged_mount, mounts = LinkedOperations._get_mounts_from_request(request) + staged_source = StagedSource( + guid=request.staged_source.linked_source.guid, + source_connection=RemoteConnection.from_proto( + request.staged_source.source_connection), + parameters=staged_source_definition, + mount=staged_mount, + staged_connection=RemoteConnection.from_proto( + request.staged_source.staged_connection), + mounts=mounts) + + virtual_to_physical_source_definition = VirtualToPhysicalDefinition.from_dict( + json.loads(request.physical_source.parameters.json)) + physical_source = PhysicalSource( + guid=request.physical_source.guid, + connection=RemoteConnection.from_proto(request.physical_source.connection), + target_directory=request.physical_source.target_directory, + parameters=virtual_to_physical_source_definition) + + repository = RepositoryDefinition.from_dict( + json.loads(request.repository.parameters.json)) + source_config = SourceConfigDefinition.from_dict( + json.loads(request.source_config.parameters.json)) + snapshot = SnapshotDefinition.from_dict( + json.loads(request.snapshot.parameters.json)) + + self.pre_source_to_physical_impl( + staged_source=staged_source, + repository=repository, + source_config=source_config, + snapshot=snapshot, + physical_source=physical_source + ) + + staged_pre_to_physical_response = ( + platform_pb2.StagedPreSourceToPhysicalResponse()) + staged_pre_to_physical_response.return_value.CopyFrom( + platform_pb2.StagedPreSourceToPhysicalResult()) + return staged_pre_to_physical_response + def _internal_staged_source_to_physical(self, request): """Staged Source to Physical Wrapper for staged plugins. diff --git a/platform/src/main/python/dlpx/virtualization/platform/_virtual.py b/platform/src/main/python/dlpx/virtualization/platform/_virtual.py index f947efad..30ffdb25 100644 --- a/platform/src/main/python/dlpx/virtualization/platform/_virtual.py +++ b/platform/src/main/python/dlpx/virtualization/platform/_virtual.py @@ -35,6 +35,7 @@ def __init__(self): self.initialize_impl = None self.mount_specification_impl = None self.source_size_impl = None + self.pre_source_to_physical_impl = None self.source_to_physical_impl = None def configure(self): @@ -154,6 +155,17 @@ def source_size_decorator(source_size_impl): return source_size_decorator + def pre_source_to_physical(self): + def pre_source_to_physical_decorator(pre_source_to_physical_impl): + if self.pre_source_to_physical_impl: + raise OperationAlreadyDefinedError( + Op.VIRTUAL_PRE_SOURCE_TO_PHYSICAL) + self.pre_source_to_physical_impl = v.check_function( + pre_source_to_physical_impl, Op.VIRTUAL_PRE_SOURCE_TO_PHYSICAL) + return pre_source_to_physical_impl + + return pre_source_to_physical_decorator + def source_to_physical(self): def source_to_physical_decorator(source_to_physical_impl): if self.source_to_physical_impl: @@ -861,6 +873,76 @@ def _internal_virtual_source_size(self, request): return virtual_source_size_response + def _internal_virtual_pre_source_to_physical(self, request): + """Pre Virtual to Physical Wrapper. + + Executed immediately before the file-copy subtask begins, giving + the plugin a chance to reject an unsuitable target before any data + moves. + + Run pre_source_to_physical operation for a virtual source. + + Args: + request (VirtualPreSourceToPhysicalRequest): Pre Virtual to Physical + Request arguments. + + Returns: + VirtualPreSourceToPhysicalResponse: A response containing the return + value - VirtualPreSourceToPhysicalResult. In case of errors, response + object will contain PluginErrorResult. + """ + # Reasoning for method imports are in this file's docstring. + from generated.definitions import VirtualSourceDefinition + from generated.definitions import RepositoryDefinition + from generated.definitions import SourceConfigDefinition + from generated.definitions import SnapshotDefinition + from generated.definitions import VirtualToPhysicalDefinition + + # + # While virtual.pre_source_to_physical() is not a required operation, + # this should not be called if it wasn't implemented. + # + if not self.pre_source_to_physical_impl: + raise OperationNotDefinedError(Op.VIRTUAL_PRE_SOURCE_TO_PHYSICAL) + + virtual_source_definition = VirtualSourceDefinition.from_dict( + json.loads(request.virtual_source.parameters.json)) + mounts = [ + VirtualOperations._from_protobuf_single_subset_mount(m) + for m in request.virtual_source.mounts + ] + virtual_source = VirtualSource(guid=request.virtual_source.guid, + connection=RemoteConnection.from_proto( + request.virtual_source.connection), + parameters=virtual_source_definition, + mounts=mounts) + virtual_to_physical_source_definition = VirtualToPhysicalDefinition.from_dict( + json.loads(request.physical_source.parameters.json)) + physical_source = PhysicalSource( + guid=request.physical_source.guid, + connection=RemoteConnection.from_proto(request.physical_source.connection), + target_directory=request.physical_source.target_directory, + parameters=virtual_to_physical_source_definition) + repository = RepositoryDefinition.from_dict( + json.loads(request.repository.parameters.json)) + source_config = SourceConfigDefinition.from_dict( + json.loads(request.source_config.parameters.json)) + snapshot = SnapshotDefinition.from_dict( + json.loads(request.snapshot.parameters.json)) + + self.pre_source_to_physical_impl( + virtual_source=virtual_source, + repository=repository, + source_config=source_config, + snapshot=snapshot, + physical_source=physical_source) + + virtual_pre_to_physical_response = ( + platform_pb2.VirtualPreSourceToPhysicalResponse()) + virtual_pre_to_physical_response.return_value.CopyFrom( + platform_pb2.VirtualPreSourceToPhysicalResult()) + return virtual_pre_to_physical_response + def _internal_virtual_source_to_physical(self, request): """Virtual to Physical Wrapper. diff --git a/platform/src/main/python/dlpx/virtualization/platform/operation.py b/platform/src/main/python/dlpx/virtualization/platform/operation.py index 8eeeef5f..68b3a3a9 100644 --- a/platform/src/main/python/dlpx/virtualization/platform/operation.py +++ b/platform/src/main/python/dlpx/virtualization/platform/operation.py @@ -18,6 +18,7 @@ class Operation(Enum): LINKED_WORKER = 'linked.worker()' LINKED_MOUNT_SPEC = 'linked.mount_specification()' LINKED_SOURCE_SIZE = 'linked.source_size()' + LINKED_PRE_SOURCE_TO_PHYSICAL = 'linked.pre_source_to_physical()' LINKED_SOURCE_TO_PHYSICAL = 'linked.source_to_physical()' VIRTUAL_CONFIGURE = 'virtual.configure()' @@ -32,6 +33,7 @@ class Operation(Enum): VIRTUAL_INITIALIZE = 'virtual.initialize()' VIRTUAL_MOUNT_SPEC = 'virtual.mount_specification()' VIRTUAL_SOURCE_SIZE = 'virtual.source_size()' + VIRTUAL_PRE_SOURCE_TO_PHYSICAL = 'virtual.pre_source_to_physical()' VIRTUAL_SOURCE_TO_PHYSICAL = 'virtual.source_to_physical()' UPGRADE_REPOSITORY = 'upgrade.repository()' diff --git a/platform/src/test/python/dlpx/virtualization/test_plugin.py b/platform/src/test/python/dlpx/virtualization/test_plugin.py index fb7adbf5..9ba65ca7 100755 --- a/platform/src/test/python/dlpx/virtualization/test_plugin.py +++ b/platform/src/test/python/dlpx/virtualization/test_plugin.py @@ -856,6 +856,37 @@ def virtual_source_to_physical_impl(virtual_source, repository, 'result') == 'return_value' assert virtual_to_physical_response.return_value == expected_result + @staticmethod + def test_virtual_pre_source_to_physical(my_plugin, virtual_source, repository, + source_config, snapshot, physical_source): + + @my_plugin.virtual.pre_source_to_physical() + def virtual_pre_source_to_physical_impl(virtual_source, repository, + source_config, snapshot, + physical_source): + TestPlugin.assert_plugin_args(virtual_source=virtual_source, + repository=repository, + source_config=source_config, + snapshot=snapshot, + physical_source=physical_source) + + virtual_pre_to_physical_request = ( + platform_pb2.VirtualPreSourceToPhysicalRequest()) + TestPlugin.setup_request(request=virtual_pre_to_physical_request, + virtual_source=virtual_source, + repository=repository, + source_config=source_config, + snapshot=snapshot, + physical_source=physical_source) + + virtual_pre_to_physical_response = my_plugin.virtual. \ + _internal_virtual_pre_source_to_physical(virtual_pre_to_physical_request) + expected_result = platform_pb2.VirtualPreSourceToPhysicalResult() + # Check that the response's oneof is set to return_value and not error + assert virtual_pre_to_physical_response.WhichOneof( + 'result') == 'return_value' + assert virtual_pre_to_physical_response.return_value == expected_result + @staticmethod def test_repository_discovery(my_plugin, connection): @my_plugin.discovery.repository() @@ -1102,6 +1133,36 @@ def direct_source_to_physical_impl(direct_source, repository, 'result') == 'return_value' assert direct_to_physical_response.return_value == expected_result + @staticmethod + def test_direct_pre_source_to_physical(my_plugin, direct_source, repository, + source_config, snapshot, physical_source): + + @my_plugin.linked.pre_source_to_physical() + def direct_pre_source_to_physical_impl(direct_source, repository, + source_config, snapshot, + physical_source): + TestPlugin.assert_plugin_args(direct_source=direct_source, + repository=repository, + source_config=source_config, + snapshot=snapshot, + physical_source=physical_source) + + direct_pre_to_physical_request = platform_pb2.DirectPreSourceToPhysicalRequest() + TestPlugin.setup_request(request=direct_pre_to_physical_request, + direct_source=direct_source, + repository=repository, + source_config=source_config, + snapshot=snapshot, + physical_source=physical_source) + + direct_pre_to_physical_response = my_plugin.linked. \ + _internal_direct_pre_source_to_physical(direct_pre_to_physical_request) + expected_result = platform_pb2.DirectPreSourceToPhysicalResult() + # Check that the response's oneof is set to return_value and not error + assert direct_pre_to_physical_response.WhichOneof( + 'result') == 'return_value' + assert direct_pre_to_physical_response.return_value == expected_result + @staticmethod @pytest.mark.parametrize("staged_source", ["mount", "mounts", "Both"], indirect=["staged_source"]) @@ -1376,6 +1437,41 @@ def staged_source_to_physical_impl(staged_source, repository, 'result') == 'return_value' assert staged_to_physical_response.return_value == expected_result + @staticmethod + @pytest.mark.parametrize("staged_source", ["mount", "mounts", "Both"], + indirect=["staged_source"]) + def test_staged_pre_source_to_physical(my_plugin, staged_source, repository, + source_config, snapshot, physical_source): + + @my_plugin.linked.pre_source_to_physical() + def staged_pre_source_to_physical_impl(staged_source, repository, + source_config, snapshot, + physical_source): + TestPlugin.assert_plugin_args(staged_source=staged_source, + repository=repository, + source_config=source_config, + snapshot=snapshot, + physical_source=physical_source) + + staged_pre_to_physical_request = platform_pb2.StagedPreSourceToPhysicalRequest() + TestPlugin.setup_request(request=staged_pre_to_physical_request, + staged_source=staged_source, + repository=repository, + source_config=source_config, + snapshot=snapshot, + physical_source=physical_source) + + staged_pre_to_physical_response = TestPlugin._call_stage_methods( + my_plugin.linked._internal_staged_pre_source_to_physical, + staged_pre_to_physical_request, + TestPlugin._raise_staged_source_both_mounts_exception(staged_source)) + expected_result = platform_pb2.StagedPreSourceToPhysicalResult() + # Check that the response's oneof is set to return_value and not error + if staged_pre_to_physical_response: + assert staged_pre_to_physical_response.WhichOneof( + 'result') == 'return_value' + assert staged_pre_to_physical_response.return_value == expected_result + @staticmethod @pytest.mark.parametrize("staged_source", ["mount", "mounts", "Both"], indirect=["staged_source"]) diff --git a/tools/pyproject.toml b/tools/pyproject.toml index 9a60701a..0d7a3763 100644 --- a/tools/pyproject.toml +++ b/tools/pyproject.toml @@ -8,7 +8,7 @@ build-backend = "setuptools.build_meta" [project] name = "dvp-tools" -version = "5.1.0" +version = "5.2.0" description = "Delphix Virtualization SDK Tools" readme = "README.md" requires-python = ">=3.11, <3.12" @@ -30,8 +30,8 @@ dependencies = [ "click == 7.1.2", "click-configfile == 0.2.3", "configparser >= 7.2, < 7.3", - "dvp-libs == 5.1.0", - "dvp-platform == 5.1.0", + "dvp-libs == 5.2.0", + "dvp-platform == 5.2.0", "flake8 >= 7.3, < 7.4", "httpretty >= 1.0, < 1.1", "importlib-resources >= 6.5, < 6.6", diff --git a/tools/src/main/python/dlpx/virtualization/_internal/plugin_importer.py b/tools/src/main/python/dlpx/virtualization/_internal/plugin_importer.py index 48ff97a4..08dfdf27 100644 --- a/tools/src/main/python/dlpx/virtualization/_internal/plugin_importer.py +++ b/tools/src/main/python/dlpx/virtualization/_internal/plugin_importer.py @@ -360,6 +360,8 @@ def _prepare_manifest(entry_point, module_content): bool(plugin_object.linked.mount_specification_impl), 'hasLinkedSourceSize': bool(plugin_object.linked.source_size_impl), + 'hasLinkedPreSourceToPhysical': + bool(plugin_object.linked.pre_source_to_physical_impl), 'hasLinkedSourceToPhysical': bool(plugin_object.linked.source_to_physical_impl), 'hasVirtualConfigure': @@ -386,6 +388,8 @@ def _prepare_manifest(entry_point, module_content): bool(plugin_object.virtual.initialize_impl), 'hasVirtualSourceSize': bool(plugin_object.virtual.source_size_impl), + 'hasVirtualPreSourceToPhysical': + bool(plugin_object.virtual.pre_source_to_physical_impl), 'hasVirtualSourceToPhysical': bool(plugin_object.virtual.source_to_physical_impl), 'migrationIdList': diff --git a/tools/src/main/python/dlpx/virtualization/_internal/settings.cfg b/tools/src/main/python/dlpx/virtualization/_internal/settings.cfg index 21d61d2a..b7044474 100644 --- a/tools/src/main/python/dlpx/virtualization/_internal/settings.cfg +++ b/tools/src/main/python/dlpx/virtualization/_internal/settings.cfg @@ -20,7 +20,7 @@ # versions in those packages until they are shipped out of band. # [General] -engine_api_version = 1.11.49 +engine_api_version = 1.11.51 distribution_name = dvp-tools package_author = Delphix namespace_package = dlpx diff --git a/tools/src/main/python/dlpx/virtualization/_internal/validation_schemas/plugin_importer.yaml b/tools/src/main/python/dlpx/virtualization/_internal/validation_schemas/plugin_importer.yaml index 018da1e8..40c3f3c3 100644 --- a/tools/src/main/python/dlpx/virtualization/_internal/validation_schemas/plugin_importer.yaml +++ b/tools/src/main/python/dlpx/virtualization/_internal/validation_schemas/plugin_importer.yaml @@ -42,6 +42,12 @@ EXPECTED_STAGED_ARGS_BY_OP: - staged_source - repository - source_config + pre_source_to_physical_impl: + - staged_source + - repository + - source_config + - snapshot + - physical_source source_to_physical_impl: - staged_source - repository @@ -96,6 +102,12 @@ EXPECTED_STAGED_ARGS_BY_OP: - virtual_source - repository - source_config + pre_source_to_physical_impl: + - virtual_source + - repository + - source_config + - snapshot + - physical_source source_to_physical_impl: - virtual_source - repository @@ -126,6 +138,12 @@ EXPECTED_DIRECT_ARGS_BY_OP: - direct_source - repository - source_config + pre_source_to_physical_impl: + - direct_source + - repository + - source_config + - snapshot + - physical_source source_to_physical_impl: - direct_source - repository @@ -180,6 +198,12 @@ EXPECTED_DIRECT_ARGS_BY_OP: - virtual_source - repository - source_config + pre_source_to_physical_impl: + - virtual_source + - repository + - source_config + - snapshot + - physical_source source_to_physical_impl: - virtual_source - repository diff --git a/tools/src/test/python/dlpx/virtualization/_internal/conftest.py b/tools/src/test/python/dlpx/virtualization/_internal/conftest.py index e9db3e2d..dca20a1a 100644 --- a/tools/src/test/python/dlpx/virtualization/_internal/conftest.py +++ b/tools/src/test/python/dlpx/virtualization/_internal/conftest.py @@ -371,6 +371,7 @@ def post_snapshot(direct_source, repository, source_config): linked.worker_impl = None linked.mount_specification_impl = None linked.source_size_impl = None + linked.pre_source_to_physical_impl = None linked.source_to_physical_impl = None return linked @@ -413,6 +414,7 @@ def mount_specification(virtual_source, repository): virtual.initialize_impl = None virtual.cleanup_impl = None virtual.source_size_impl = None + virtual.pre_source_to_physical_impl = None virtual.source_to_physical_impl = None return virtual @@ -448,6 +450,7 @@ def plugin_manifest(upgrade_operation): 'hasLinkedWorker': False, 'hasLinkedMountSpecification': False, 'hasLinkedSourceSize': False, + 'hasLinkedPreSourceToPhysical': False, 'hasLinkedSourceToPhysical': False, 'hasVirtualConfigure': True, 'hasVirtualUnconfigure': False, @@ -462,6 +465,7 @@ def plugin_manifest(upgrade_operation): 'hasInitialize': False, 'migrationIdList': upgrade_operation.migration_id_list, 'hasVirtualCleanup': False, + 'hasVirtualPreSourceToPhysical': False, 'hasVirtualSourceToPhysical': False, } return manifest diff --git a/tools/src/test/python/dlpx/virtualization/_internal/engine_version.cfg b/tools/src/test/python/dlpx/virtualization/_internal/engine_version.cfg index b9046630..44f163ff 100644 --- a/tools/src/test/python/dlpx/virtualization/_internal/engine_version.cfg +++ b/tools/src/test/python/dlpx/virtualization/_internal/engine_version.cfg @@ -11,4 +11,4 @@ # This file also should not be updated manually with feature changes. # [General] -engine_api_version = 1.11.49 +engine_api_version = 1.11.51 diff --git a/tools/src/test/python/dlpx/virtualization/_internal/test_package_util.py b/tools/src/test/python/dlpx/virtualization/_internal/test_package_util.py index 9fe28878..f72bbba9 100644 --- a/tools/src/test/python/dlpx/virtualization/_internal/test_package_util.py +++ b/tools/src/test/python/dlpx/virtualization/_internal/test_package_util.py @@ -7,8 +7,8 @@ from dlpx.virtualization._internal import package_util -DVP_VERSION = '5.1.0' -DVP_API_VERSION = '1.10.0' +DVP_VERSION = '5.2.0' +DVP_API_VERSION = '1.11.0' class TestPackageUtil: