Source code for PyU4V.clone

# Copyright (c) 2023 Dell Inc. or its subsidiaries.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#        http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""clone.py."""

import logging

from PyU4V.common import CommonFunctions
from PyU4V.provisioning import ProvisioningFunctions
from PyU4V.utils import constants

LOG = logging.getLogger(__name__)

# Resource constants
ASYNC_UPDATE = constants.ASYNC_UPDATE
REPLICATION = constants.REPLICATION
SYMMETRIX = constants.SYMMETRIX
CAPABILITIES = constants.CAPABILITIES
STORAGEGROUP = constants.STORAGEGROUP
RDFG = constants.RDFG
RDF_GROUP = constants.RDF_GROUP
METRO_DR = constants.METRO_DR
ASYNCHRONOUS = constants.ASYNCHRONOUS_CC
ADAPTIVE_COPY = constants.ADAPTIVE_COPY


[docs] class CloneFunctions(object): """Clone Functions.""" def __init__(self, array_id, rest_client): """__init__.""" self.provisioning = ProvisioningFunctions(array_id, rest_client) self.common = CommonFunctions(rest_client) self.get_resource = self.common.get_resource self.create_resource = self.common.create_resource self.modify_resource = self.common.modify_resource self.delete_resource = self.common.delete_resource self.array_id = array_id self.version = constants.UNISPHERE_VERSION
[docs] def get_clone_target_storage_group_list( self, storage_group_id, array_id=None, target_storage_group=None, target_storage_group_volume_count=None, volume_pair_count=None, state=None, modified_tracks=None, src_protected_tracks=None, src_modified_tracks=None, background_copy=None, differential=None, precopy=None, vse=None): """Get Clone target storage group list. :param storage_group_id: The Storage Group ID -- string :param array_id: The storage array ID -- string :param target_storage_group: Value that filters returned list to include target storage groups equal to or like the provided name -- string :param target_storage_group_volume_count: Value that filters returned list to include target storage groups with the specified number of volumes -- string :param volume_pair_count: Value that filters returned list to include target storage groups with the specified number of volume pairs -- string :param state: Value that filters returned list to include target storage groups with pairs in the specified states -- array :param modified_tracks: Value that filters returned list to include target storage groups with the specified modified tracks -- string :param src_protected_tracks: Value that filters returned list to include target storage groups with the specified src protected tracks -- str :param src_modified_tracks: Value that filters returned list to include target storage groups with the specified src modified tracks -- string :param background_copy: Value that filters returned list to include target storage groups with background copy flag -- boolean :param differential: Value that filters returned list to include target storage groups with differential flag -- boolean :param precopy: Value that filters returned list to include target storage groups with the precopy flag -- boolean :param vse: Value that filters returned list to include target storage groups with the vse -- boolean :returns: a list of target storage groups -- list """ query_params = { 'target_storage_group': target_storage_group, 'target_storage_group_volume_count': target_storage_group_volume_count, 'volume_pair_count': volume_pair_count, 'state': state, 'modified_tracks': modified_tracks, 'src_protected_tracks': src_protected_tracks, 'src_modified_tracks': src_modified_tracks, 'background_copy': background_copy, 'differential': differential, 'precopy': precopy, 'vse': vse, } array_id = array_id if array_id else self.array_id response = self.common.get_request( target_uri=f"/{self.version}/replication/symmetrix/{array_id}" f"/storagegroup/{storage_group_id}/clone/storagegroup", resource_type=None, params=query_params) if response and response.get('clone_target_sg_names'): target_sg_names_list = response.get('clone_target_sg_names') else: target_sg_names_list = [] return target_sg_names_list
[docs] def get_clone_pairs_list(self, storage_group_id, array_id=None): """Get Clone Pairs List. :param: array_id The storage array ID -- string :param: storage_group_id The Storage Group ID -- string :returns: count and list of clone pairs -- dict """ array_id = array_id if array_id else self.array_id return self.common.get_request( target_uri=f"/{self.version}/replication/symmetrix" f"/{array_id}/storagegroup/{storage_group_id}" f"/clone/volume", resource_type=None)
[docs] def get_clone_storage_group_pair_details( self, storage_group_id, target_storage_group_id, array_id=None): """Get Clone storage group pair details. :param storage_group_id The Storage Group ID -- string :param target_storage_group_id The Target Storage Group ID -- string :param array_id The storage array ID -- string """ query_params = {} array_id = array_id if array_id else self.array_id return self.common.get_request( target_uri=f"/{self.version}/replication/symmetrix/{array_id}" f"/storagegroup/{storage_group_id}/clone/storagegroup/" f"{target_storage_group_id}", resource_type=None, params=query_params)
[docs] def create_clone( self, storage_group_id, target_storage_group_id, consistent=True, establish_terminate=False, array_id=None, force=False, star=False, skip=False): """Create Clone. :param storage_group_id: The Storage Group ID -- string :param consistent: creates the clone crash consistent using ECA technology -- bool :param establish_terminate: creates the clone and immediately terminates, very useful if you want to make an independent copy available immediately but don't intend to use for restore purposes -- bool :param array_id: The storage array ID -- string :param target_storage_group_id: name of storage group to contain clone devices -- string :param force: Attempts to force the operation even though one or more volumes may not be in the normal, expected state(s) for the specified operation -- bool :param star: Acknowledge the volumes are in an SRDF/Star configuration -- bool :param skip: Skips the source locks action -- bool :returns: dict """ array_id = array_id if array_id else self.array_id payload = { "target_storage_group_name": target_storage_group_id, "establish_terminate": establish_terminate, "consistent": consistent, "force": force, "star": star, "skip": skip } return self.common.create_resource( target_uri=( f"/{self.version}/replication/symmetrix" f"/{array_id}/storagegroup/{storage_group_id}" f"/clone/storagegroup"), payload=payload)
[docs] def terminate_clone(self, storage_group_id, target_storage_group_id=None, array_id=None, force=False, symforce=False, star=False, skip=False, not_ready=False, restored=None): """Terminate Clone Session. :param array_id: The storage array ID -- string :param storage_group_id: The Storage Group ID -- string :param target_storage_group_id: name of storage group to contain clone devices -- string :param force: Attempts to force the operation even though one or more volumes may not be in the normal, expected state(s) for the specified operation -- bool :param star: Acknowledge the volumes are in an SRDF/Star configuration -- bool :param skip: Skips the source locks action -- bool :param not_ready: sets clone devices to not ready after operation -- bool :param restored: removes the restore flag from clone session, leaves clone session intact for incremental clone operations. Used following restore session -- bool :returns: dict """ array_id = array_id if array_id else self.array_id params = { "target_storage_group": target_storage_group_id, "force": force, "star": star, "skip": skip, "symforce": symforce, "not_ready": not_ready, "restored": restored } return self.common.delete_resource( target_uri=(f"/{self.version}/replication/symmetrix" f"/{array_id}/storagegroup/{storage_group_id}" f"/clone/storagegroup"), params=params)
[docs] def establish_clone(self, storage_group_id, target_storage_group_id, array_id=None, consistent=True, not_ready=False, vse=False, force=False, star=False, skip=False, _async=False): """Perform establish against a clone storage group. :param storage_group_id: The Storage Group ID -- string :param target_storage_group_id: name of storage group to contain clone devices -- string :param array_id: The storage array ID -- string :param consistent: creates the clone crash consistent using ECA technology -- bool :param not_ready: sets target storage group to not ready following establish operation -- bool :param vse: uses VSE close -- bool :param force: Attempts to force the operation even though one or more volumes may not be in the normal, expected state(s) for the specified operation -- bool :param star: Acknowledge the volumes are in an SRDF/Star configuration -- bool :param skip: Skips the source locks action -- bool :param _async: if call should be async -- bool :returns: dict """ array_id = array_id if array_id else self.array_id payload = { 'action': 'Establish', 'establish': { 'consistent': consistent, 'not_ready': not_ready, 'vse': vse, 'force': force, 'star': star, 'skip': skip }} if _async: payload.update(ASYNC_UPDATE) return self.common.modify_resource(target_uri=( f"/{self.version}/replication/symmetrix/{array_id}/storagegroup/" f"{storage_group_id}/clone/storagegroup/" f"{target_storage_group_id}"), resource_type=None, payload=payload)
[docs] def split_clone( self, storage_group_id, target_storage_group_id, array_id=None, star=False, skip=False, force=False, _async=False): """Perform split actions against a clone storage group that is in the restored state. :param storage_group_id: The Storage Group ID -- string :param target_storage_group_id: The Storage Group ID of Target storage group -- string :param star: Acknowledge the volumes are in an SRDF/Star configuration -- bool :param skip: Skips the source locks action -- bool :param force: Attempts to force the operation even though one or more volumes may not be in the normal, expected state(s) for the specified operation -- bool :param array_id: The storage array ID -- string :param _async: if call should be async -- bool :returns: dict """ array_id = array_id if array_id else self.array_id payload = { 'action': 'Split', 'split': { 'force': force, 'star': star, 'skip': skip }} if _async: payload.update(ASYNC_UPDATE) return self.common.modify_resource( target_uri=f"/{self.version}/replication/symmetrix/{array_id}" f"/storagegroup/{storage_group_id}/clone/storagegroup/" f"{target_storage_group_id}", resource_type=None, payload=payload)
[docs] def restore_clone( self, storage_group_id, target_storage_group_id, array_id=None, star=False, force=False, _async=False): """Perform split actions against a clone storage group that is in the restored state. :param storage_group_id: The Storage Group ID -- string :param target_storage_group_id: The Storage Group ID of Target storage group -- string :param array_id: The storage array ID -- string :param force: Attempts to force the operation even though one or more volumes may not be in the normal, expected state(s) for the specified operation -- bool :param star: Acknowledge the volumes are in an SRDF/Star configuration -- bool :param _async: if call should be async -- bool :returns: dict """ array_id = array_id if array_id else self.array_id payload = { 'action': 'Restore', 'restore': { 'force': force, 'star': star, }} if _async: payload.update(ASYNC_UPDATE) return self.common.modify_resource( target_uri=f"/{self.version}/replication/symmetrix/{array_id}" f"/storagegroup/{storage_group_id}/clone/storagegroup/" f"{target_storage_group_id}", resource_type=None, payload=payload)