From 21abfda55a8c529d9ab47763ccd2ab5da5eb1dcc Mon Sep 17 00:00:00 2001 From: Sourabh Date: Tue, 21 Jul 2026 12:21:36 +0530 Subject: [PATCH 1/3] CP-13518 vSDK Decorator Implementation & Manifest Write --- .bumpversion.cfg | 2 +- common/pyproject.toml | 4 +- dvp/pyproject.toml | 10 +- libs/pyproject.toml | 6 +- platform/pyproject.toml | 6 +- .../dlpx/virtualization/platform/_linked.py | 152 ++++++++++++++++++ .../dlpx/virtualization/platform/_virtual.py | 82 ++++++++++ .../dlpx/virtualization/platform/operation.py | 2 + .../python/dlpx/virtualization/test_plugin.py | 96 +++++++++++ tools/pyproject.toml | 6 +- .../_internal/plugin_importer.py | 4 + .../virtualization/_internal/settings.cfg | 2 +- .../validation_schemas/plugin_importer.yaml | 24 +++ .../dlpx/virtualization/_internal/conftest.py | 4 + .../_internal/engine_version.cfg | 2 +- .../_internal/test_package_util.py | 4 +- 16 files changed, 385 insertions(+), 21 deletions(-) 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/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..4f4f7016 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 Virtual 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 Virtual 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: From 94bb68ce97bd2e76eebe8a346ee81cf48a4e41d9 Mon Sep 17 00:00:00 2001 From: Sourabh Date: Tue, 21 Jul 2026 12:21:54 +0530 Subject: [PATCH 2/3] CP-13523 vSDK Documentation --- docs/docs/References/Decorators.md | 3 + docs/docs/References/Plugin_Operations.md | 141 ++++++++++++++++++ docs/docs/References/Version_Compatibility.md | 2 + .../html/LinkedSourceToPhysical.html | 9 +- .../html/VirtualSourceToPhysical.html | 9 +- .../images/LinkedSourceToPhysical.png | Bin 17911 -> 16893 bytes .../images/VirtualSourceToPhysical.png | Bin 18518 -> 16987 bytes docs/docs/Release_Notes/.pages | 1 + docs/docs/Release_Notes/5.2.0/5.2.0.md | 14 ++ docs/mkdocs.yml | 2 +- 10 files changed, 172 insertions(+), 9 deletions(-) create mode 100644 docs/docs/Release_Notes/5.2.0/5.2.0.md 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..484b62ae 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 [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 [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 [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 [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 9007ad7f92f8b91cd6526f86bfe6f68dc2b28bdc..e37a8bf132def0d1e003af1f239ab07229b5fddc 100644 GIT binary patch literal 16893 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 literal 17911 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 diff --git a/docs/docs/References/images/VirtualSourceToPhysical.png b/docs/docs/References/images/VirtualSourceToPhysical.png index 5d2c0dcc016ab8bdff4783eac63896ce5554c36a..ca2b239fd881f3f59e528a7af167f82edaa165c9 100644 GIT binary patch literal 16987 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 S+Ft+&NbZTU)Efzt!2btlfr%La 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/ From 16379d2b107745ebcd2d34d68d4433b5d0006489 Mon Sep 17 00:00:00 2001 From: Sourabh Date: Thu, 23 Jul 2026 18:46:10 +0530 Subject: [PATCH 3/3] CP-13518 Fix Copilot review comments on pre_source_to_physical docstrings and doc links --- docs/docs/References/Plugin_Operations.md | 4 ++-- .../main/python/dlpx/virtualization/platform/_linked.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/docs/References/Plugin_Operations.md b/docs/docs/References/Plugin_Operations.md index 484b62ae..1039d579 100644 --- a/docs/docs/References/Plugin_Operations.md +++ b/docs/docs/References/Plugin_Operations.md @@ -321,7 +321,7 @@ def linked_source_size(direct_source, repository, source_config): ## Direct Linked 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 [Direct Linked Source to Physical](#direct-linked-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.** @@ -790,7 +790,7 @@ def linked_source_size(staged_source, repository, source_config): ## Staged Linked 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 [Staged Linked Source to Physical](#staged-linked-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.** diff --git a/platform/src/main/python/dlpx/virtualization/platform/_linked.py b/platform/src/main/python/dlpx/virtualization/platform/_linked.py index 4f4f7016..aab804d9 100644 --- a/platform/src/main/python/dlpx/virtualization/platform/_linked.py +++ b/platform/src/main/python/dlpx/virtualization/platform/_linked.py @@ -355,8 +355,8 @@ def _internal_direct_pre_source_to_physical(self, request): Run pre_source_to_physical operation for a direct source. Args: - request (DirectPreSourceToPhysicalRequest): Pre Virtual to Physical - Request arguments. + request (DirectPreSourceToPhysicalRequest): Pre Direct Source to + Physical Request arguments. Returns: DirectPreSourceToPhysicalResponse: A response containing the return @@ -1018,8 +1018,8 @@ def _internal_staged_pre_source_to_physical(self, request): Run pre_source_to_physical operation for a staged source. Args: - request (StagedPreSourceToPhysicalRequest): Pre Virtual to Physical - Request arguments. + request (StagedPreSourceToPhysicalRequest): Pre Staged Source to + Physical Request arguments. Returns: StagedPreSourceToPhysicalResponse: A response containing the return