Module cvpysdk.drorchestration.blr_pairs
Main file for performing BLR pair specific operations.
BLRPairs and BLRPair are classes defined in this file.
BLRPairs: Class for representing all the BLR pairs
PairStatus: Enum for all possible pair status
EndPointTypes: Enum for all allowed end points for BLR pairs
PendingStatus: Enum for all pending status codes
RecoveryType: Enum of all possible recovery point types
DROperations: Enum of DR Operations that can be applied on the BLR pair
BLRPair: Class for a single BLR Pair
Blrpairs
init(commcell_object) – Initialise object of BLRPairs class
repr() – Returns the string for the instance of the BLRPairs class
blr_pairs() – Returns a dictionary of BLR pair names mapping with their IDs
has_blr_pair( source_name, destination_name) – Checks if BLR pair exists with the given source and destination client name
create_fsblr_pair(source_client_id, – Creates FSBLR replication pair for given set of options destination_client_id, source_volumes, destination_volumes, recovery_type, **kwargs)
get(source_name, destination_name) – Returns the BLRPair class object for the source and destination client name
delete(source_name, destination_name) – Delete BLR pair with the source and destination client name
refresh() – Refresh all BLR pairs created on the commcell
get_rpstore_id(rpstore_name) – Get the RPStore ID for the given name
get_rpstore_mountpath(rpstore_name) – Get the RPstore mounth path for the given name
internal methods
_update_data() – REST API call to get all BLR pairs in the commcell
Blrpair
init(commcell_object, pair_name) – Initialise object of BLRPair class for pair name
repr() – Returns the string for the instance of the BLRPair class
pair_properties – Returns the properties of the pair
pair_status – Returns the status of the pair
source – Returns the dictionary for all source client properties
destination – Returns the dictionary for all destination client properties
lag_time – Returns the replication lag time in minutes
replication_group_name – (VSABLR) Returns the replication group name of the pair
pending_status – Returns the reason for replication lag
pair_flag – Returns the integer for the pair's flag
subclient_props – Returns the properties of the subclient associated with the pair
pair_recovery_type – Returns the enum for recovery type of the pair
pair_rpstore_intervals – Returns the RPStore interval options set for the pair
pair_volume_map – (FSBLR) Returns the mapping of volumes for FSBLR pairs
pair_latest_stats – Returns the pair's latest data stats
get_pair_stats() – Returns the pair's data stats
get_recovery_point_stores() – Returns the RPstore points for the pair
create_replica_copy() – Creates a replica copy task and return the job/task ID
refresh() – Refresh the BLR pair properties
internal methods
_get_pair_id() – Returns the BLR pair ID from the BLR pairs dictionary
_get_pair_properties() – Returns the BLR pair properties
Expand source code Browse git
# -*- coding: utf-8 -*-
# --------------------------------------------------------------------------
# Copyright Commvault Systems, Inc.
#
# 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.
# --------------------------------------------------------------------------
"""Main file for performing BLR pair specific operations.
BLRPairs and BLRPair are classes defined in this file.
BLRPairs: Class for representing all the BLR pairs
PairStatus: Enum for all possible pair status
EndPointTypes: Enum for all allowed end points for BLR pairs
PendingStatus: Enum for all pending status codes
RecoveryType: Enum of all possible recovery point types
DROperations: Enum of DR Operations that can be applied on the BLR pair
BLRPair: Class for a single BLR Pair
BLRPairs:
__init__(commcell_object) -- Initialise object of BLRPairs class
__repr__() -- Returns the string for the instance of the
BLRPairs class
blr_pairs() -- Returns a dictionary of BLR pair names mapping with their IDs
has_blr_pair(
source_name, destination_name) -- Checks if BLR pair exists with the given source and
destination client name
create_fsblr_pair(source_client_id, -- Creates FSBLR replication pair for given set of options
destination_client_id,
source_volumes,
destination_volumes,
recovery_type,
**kwargs)
get(source_name, destination_name) -- Returns the BLRPair class object for the source and
destination client name
delete(source_name, destination_name) -- Delete BLR pair with the source and
destination client name
refresh() -- Refresh all BLR pairs created on the commcell
get_rpstore_id(rpstore_name) -- Get the RPStore ID for the given name
get_rpstore_mountpath(rpstore_name) -- Get the RPstore mounth path for the given name
#### internal methods ###
_update_data() -- REST API call to get all BLR pairs in the commcell
BLRPair:
__init__(commcell_object, pair_name) -- Initialise object of BLRPair class for pair name
__repr__() -- Returns the string for the instance of the
BLRPair class
pair_properties -- Returns the properties of the pair
pair_status -- Returns the status of the pair
source -- Returns the dictionary for all source client properties
destination -- Returns the dictionary for all destination client properties
lag_time -- Returns the replication lag time in minutes
replication_group_name -- (VSABLR) Returns the replication group name of the pair
pending_status -- Returns the reason for replication lag
pair_flag -- Returns the integer for the pair's flag
subclient_props -- Returns the properties of the subclient associated with the pair
pair_recovery_type -- Returns the enum for recovery type of the pair
pair_rpstore_intervals -- Returns the RPStore interval options set for the pair
pair_volume_map -- (FSBLR) Returns the mapping of volumes for FSBLR pairs
pair_latest_stats -- Returns the pair's latest data stats
get_pair_stats() -- Returns the pair's data stats
get_recovery_point_stores() -- Returns the RPstore points for the pair
create_replica_copy() -- Creates a replica copy task and return the job/task ID
refresh() -- Refresh the BLR pair properties
#### internal methods ###
_get_pair_id() -- Returns the BLR pair ID from the BLR pairs dictionary
_get_pair_properties() -- Returns the BLR pair properties
"""
import time
from cvpysdk.job import Job
from cvpysdk.schedules import Schedules
from ..exception import SDKException
from enum import Enum
import xmltodict
class BLRPairs:
"""Class for getting all BLR pairs in commcell."""
class PairStatus(Enum):
NOT_SYNCED = 0
BACKING_UP = 1
RESTORING = 2
RESYNCING = 3
REPLICATING = 4
SUSPENDED = 5
STOPPED = 6
VERIFYING = 7
PROBLEM = 8
FAILED = 9
STARTING = 10
STOPPING = 11
SUSPENDING = 12
RESUMING = 13
FAILING_OVER = 14
FAILOVER_FAILED = 15
FAILOVER_DONE = 16
FAILING_BACK = 17
FAILBACK_FAILED = 18
SWITCHING_ROLES = 19
SWITCH_ROLES_FAILED = 20
class EndPointTypes(Enum):
VIRTUALIZATION = 1
FILESYSTEM = 2
DATABASE = 3
class AgentTypes(Enum):
VIRTUALIZATION = 'Virtual Server'
FILESYSTEM = 'File system'
DATABASE = 'Database'
class PendingStatus(Enum):
NOLAG = 0
class RecoveryType(Enum):
LIVE = 1
SNAPSHOT = 2
GRANULAR = 3
GRANULARV2 = 4
class DROperations(Enum):
TEST_BOOT = 1,
VSA_FAIL_OVER = 2
DELETE = 3
PERMANENT_BOOT = 4
TEST_BOOT_EXTEND = 5
MANAGE_TEST_BOOT = 6
TEST_FS_MOUNT = 7
PERM_FS_MOUNT = 8
TEST_FSMOUNT_EXTEND = 9
MANAGE_TEST_MOUNT = 10
VSA_FAIL_BACK = 11
FS_FAIL_OVER = 12
FS_FAIL_BACK = 13
RESUME_VSA_FAILOVER = 14
CANCEL_VSA_FAILOVER = 15
RESUME_FS_FAILOVER = 16
CANCEL_FS_FAILOVER = 17
ABORT_VSA_FAILBACK = 18
ABORT_FS_FAILBACK = 19
def __init__(self, commcell_object, replication_group_name: str = ''):
"""Initialize object of the BLR Pairs
Args:
commcell_object (Commcell) -- instance of the Commcell class
replication_group_name (str)-- Name of the replication group (only for VSA BLR)
"""
self._commcell_object = commcell_object
self._services = commcell_object._services
self._replication_group_name = replication_group_name.lower()
self._replication_group_id = None
if self._replication_group_name:
self._replication_group_id = self._get_replication_group_id()
self._LIST_BLR_PAIRS = self._services['GET_BLR_PAIRS']
self._DELETE_BLR = self._services['DELETE_BLR_PAIR']
self._QEXEC = self._services['EXEC_QCOMMAND']
self._site_info = None
self._summary = None
self._rpstore_list = None
self.refresh()
def __repr__(self):
"""Representation string for the instance of the BLR Pairs class."""
if not self._replication_group_name:
return "BLR Pairs for Commserv: '{0}'".format(self._commcell_object.commserv_name)
else:
return "BLR Pairs for Replication group: '{0}'".format(self._replication_group_name)
def _get_replication_group_id(self):
"""Returns the replication group ID
Args:
Returns:
replication_group_id (str): The ID of the associated replication group
Raises:
"""
if not self._replication_group_name:
return ''
return (self._commcell_object.replication_groups.replication_groups
.get(self._replication_group_name, {}).get('id'))
def _update_data(self):
"""REST API call for getting all the info for all pairs in the commcell.
Args:
Returns:
Raises:
SDKException:
if response is empty
if response is not success
"""
flag, response = self._commcell_object._cvpysdk_object.make_request(
'GET', self._LIST_BLR_PAIRS)
if flag:
if response.json():
self._summary = response.json().get('summary', {})
if self._replication_group_id:
self._site_info = [site_info for site_info in response.json().get('siteInfo', [])
if str(site_info.get('replicationGroup', {}).get('replicationGroupId', 0))
== self._replication_group_id]
else:
self._site_info = response.json().get('siteInfo', [])
else:
raise SDKException('Response', '102')
else:
response_string = self._commcell_object._update_response_(response.text)
raise SDKException('Response', '101', response_string)
def _update_rpstorelist(self):
"""REST API call for getting all the rp store in the commcell.
Args:
Returns:
Raises:
SDKException:
if response is empty
if response is not success
"""
response = self._commcell_object.qoperation_execute('<EVGui_GetLibraryListWCReq libraryType="RPSTORE" />')
if response and 'libraryList' in response:
self._rpstore_list = response.get('libraryList', [])
else:
raise SDKException('Response', '102')
@property
def blr_pairs(self):
"""REST API call for getting all the BLR pairs in the commcell.
Args:
Returns:
dict - consists of all BLR pairs
{
"blr_id_1": {
"sourceName": "vm1",
"destinationName": "DRvm1",
"subclientName": "BLR_vm1(<guid>)"
},
"blr_id_2": {
"sourceName": "vm1",
"destinationName": "DRvm1",
"subclientName": "BLRSC_vm1_DRvm1_E:"
},
}
"""
pairs = {}
for pair_row in self._site_info:
pair_id = pair_row.get('id')
if pair_id:
pairs[str(pair_id)] = {
'sourceName': pair_row.get('sourceName', ''),
'destinationName': pair_row.get('destinationName', ''),
'subclientName': pair_row.get('entity', {}).get('subclientName', '')
}
return pairs
def has_blr_pair(self, source_name, destination_name):
"""Checks if BLR pair exists or not
Args:
source_name (str): Name of the source client
destination_name (str): Name of the destination client
Returns:
bool - boolean output whether BLR pair exists or not
Raises:
SDKException:
if proper inputs are not provided
"""
if not isinstance(source_name, str) or not isinstance(destination_name, str):
raise SDKException('BLRPairs', '101')
source_name = source_name.lower()
destination_name = destination_name.lower()
for _, pair_row in self.blr_pairs.items():
if (source_name == pair_row.get('sourceName', '').lower() and
destination_name == pair_row.get('destinationName', '').lower()):
return True
else:
return False
def create_fsblr_pair(self,
source_client_id,
destination_client_id,
source_volumes,
destination_volumes,
recovery_type,
**kwargs):
"""Creates a new FSBLR pair on the commcell with the specified options
Args:
source_client_id (str) : The source client's ID
destination_client_id (str) : The destination client's ID
source_volumes (list) : The list of all source volumes
destination_volumes (list) : The list of all destination volumes
recovery_type (RecoveryType): The enum to specify what type of recovery pair is supposed to be
**kwargs (dict) : Only used for granular type FSBLR pairs
rpstore_id (str) : The ID of the RPstore to be used
rpstore_name (str) : The name of the RPStore
ccrp_interval (int) : The number of minutes after which CCRP is taken
acrp_interval (int) : The number of minutes after which ACRP is taken
max_rp_interval (int) : The number of minutes after which RP store's retention is ended
rp_merge_delay (int) : Merge recovery points older than time in minutes
rp_retention (int) : The number of minutes for which RPstore is retained for
rpstore_switch_live(int): The time in minutes after which pair is switch to
live if RPstore is offline
merge_only_off_peak(bool):Whether to merge RPstore only during off-peak time
"""
blr_options = {
'BlockReplication_BLRRecoveryOptions':
{
'@recoveryType': recovery_type.value,
'granularV2': {
'@ccrpInterval': kwargs.get('ccrp_interval', 300),
'@acrpInterval': kwargs.get('acrp_interval', 0),
'@maxRpInterval': kwargs.get('max_rp_interval', 21600),
'@rpMergeDelay': kwargs.get('rp_merge_delay', 172800),
'@rpRetention': kwargs.get('rp_retention', 604800),
'@maxRpStoreOfflineTime': kwargs.get('rpstore_switch_live', 0),
'@useOffPeakSchedule': int(kwargs.get('merge_only_off_peak', False)),
}},
}
if kwargs.get('rpstore_id') and kwargs.get('rpstore_name'):
granularv2 = blr_options['BlockReplication_BLRRecoveryOptions']['granularV2']
granularv2['@rpStoreId'] = int(kwargs.get('rpstore_id', 0))
granularv2['@rpStoreName'] = kwargs.get('rpstore_name')
blr_options['BlockReplication_BLRRecoveryOptions']['granularV2'] = granularv2
source_client = self._commcell_object.clients.get(int(source_client_id))
destination_client = self._commcell_object.clients.get(int(destination_client_id))
source_client_volumes = source_client.get_mount_volumes(source_volumes)
destination_client_volumes = destination_client.get_mount_volumes(destination_volumes)
request_json = {
"destEndPointType": self.EndPointTypes.FILESYSTEM.value,
"blrRecoveryOpts": xmltodict.unparse(blr_options, short_empty_elements=True).replace('\n', ''),
"srcEndPointType": self.EndPointTypes.FILESYSTEM.value,
"srcDestVolumeMap": [],
"destEntity": {
"client": {
"clientId": int(destination_client_id),
"clientName": destination_client.client_name,
"hasDrivesInPair": True,
"tabLevel": "level-0",
"checked": True,
}
},
"sourceEntity": {
"client": {
"clientId": int(source_client_id),
"clientName": source_client.client_name,
"hasDrivesInPair": True,
"tabLevel": "level-0",
"checked": True,
}
}
}
for source, destination in zip(source_client_volumes, destination_client_volumes):
request_json['srcDestVolumeMap'].append({
"sourceVolumeGUID": source['guid'],
"sourceVolume": source['accessPathList'][0],
"destVolumeGUID": destination['guid'],
"destVolume": destination['accessPathList'][0],
"sourceVolumeSize": source['size'],
"disabled": "",
})
flag, response = (self._commcell_object._cvpysdk_object
.make_request('POST', self._services['CREATE_BLR_PAIR'], request_json))
if flag:
if response and response.json():
if response.json().get('errorCode', 0) != 0:
response_string = self._commcell_object._update_response_(
response.text)
raise SDKException('Response', '101', response_string)
else:
raise SDKException('Response', '102')
else:
raise SDKException('Response', '101')
def get(self, source_name, destination_name):
"""Get pair name on the basis of source and destination name and return pair object
Args:
source_name (str): Name of the source client
destination_name (str): Name of the destination client
Returns: BLRPair object for source and destination
Raises:
SDKException:
if proper inputs are not provided
"""
if not isinstance(source_name, str) or not isinstance(destination_name, str):
raise SDKException('BLRPairs', '101')
source_name = source_name.lower()
destination_name = destination_name.lower()
for _, pair_row in self.blr_pairs.items():
if (source_name == pair_row.get('sourceName', '').lower() and
destination_name == pair_row.get('destinationName', '').lower()):
return BLRPair(self._commcell_object, source_name, destination_name)
else:
raise SDKException('BLRPairs', '102',
'No BLR pair exists with source: "{0}" and destination: "{1}"'
.format(source_name, destination_name))
def delete(self, source_name, destination_name):
""" Deletes the blr pair with source and destination names
Args:
source_name (str): Name of the source client
destination_name (str): Name of the destination client
Returns: BLRPair object for source and destination
Raises:
SDKException:
if proper inputs are not provided
if response is empty
if response is not success
"""
if not isinstance(source_name, str) or not isinstance(destination_name, str):
raise SDKException('BLRPairs', '101')
source_name = source_name.lower()
destination_name = destination_name.lower()
if self.has_blr_pair(source_name, destination_name):
for pair_id, pair_row in self.blr_pairs.items():
if (source_name == pair_row.get('sourceName', '').lower() and
destination_name == pair_row.get('destinationName', '').lower()):
flag, response = self._commcell_object._cvpysdk_object.make_request(
method='DELETE', url=self._DELETE_BLR % pair_id)
if flag:
if 'error' in response.json():
error_message = response.json(
)['error']['errorMessage']
o_str = 'Failed to delete Source: {0} and Destination: {1} \nError: "{2}"' \
.format(source_name, destination_name, error_message)
raise SDKException('Response', '101', o_str)
else:
self.refresh()
else:
raise SDKException('Response', '102')
break
else:
raise SDKException('Response', '102')
else:
raise SDKException('BLRPairs', '102',
'No BLR pair exists with source: "{0}" and destination: "{1}"'
.format(source_name, destination_name))
def refresh(self):
""" Refresh the BLR pairs created in the commcell.
Args:
Returns:
Raises:
"""
self._update_data()
self._update_rpstorelist()
def get_rpstore_id(self, rpstore_name):
"""Gets the RPStore ID for the given name
Args:
rpstore_name (str) : The name of the RP store
"""
for rpstore in self._rpstore_list:
if rpstore.get('library', {}).get('libraryName') == rpstore_name and rpstore.get('MountPathList', ''):
return str(rpstore.get('MountPathList')[0]
.get('rpStoreLibraryInfo', {}).get('rpStoreId', ''))
else:
raise SDKException('BLRPairs', '103', f'No RP Store found with name {rpstore_name}')
def get_rpstore_mountpath(self, rpstore_name):
"""Gets the RPStore mount path for the given name
Args:
rpstore_name (str) : The name of the RP store
"""
for rpstore in self._rpstore_list:
if rpstore.get('library', {}).get('libraryName') == rpstore_name:
mount_path = str(rpstore.get('MountPathList', {})[0]['mountPathName'])
return mount_path.split()[1].strip()
else:
raise SDKException('BLRPairs', '103', f'No RP Store found with name {rpstore_name}')
class BLRPair:
class PairOperationsStatus(Enum):
SUSPEND = BLRPairs.PairStatus.SUSPENDED
START = BLRPairs.PairStatus.REPLICATING
RESUME = BLRPairs.PairStatus.REPLICATING
STOP = BLRPairs.PairStatus.STOPPED
RESYNC = BLRPairs.PairStatus.RESYNCING
def __init__(self, commcell_object, source_name, destination_name):
"""Initialise the ReplicationGroup object for the given group name
Args:
commcell_object (Commcell) -- instance of the Commcell class
source_name (str) -- Name of the source of BLR pair
destination_name (str) -- Name of the destination of BLR pair
"""
self._commcell_object = commcell_object
self._services = commcell_object._services
self._source_name = source_name.lower()
self._destination_name = destination_name.lower()
self._pair_id = self._get_pair_id()
self._pair_properties = None
self._source_instance = None
self._destination_instance = None
self._GET_PAIR = self._services['GET_BLR_PAIR']
self._PAIR_STATS = self._services['GET_BLR_PAIR_STATS']
self._GRANULAR_RP_STORES = self._services['GRANULAR_BLR_POINTS']
self._BOOT_DETAILS = self._services['BLR_BOOT_DETAILS']
self.refresh()
def __repr__(self):
"""String representation of the instance of the BLR pair"""
representation_string = 'BLR pair class instance for pair: "{0} -> {1}"'
return representation_string.format(self._source_name, self._destination_name)
def __str__(self):
"""String representation of the instance of BLR pair"""
return f'BLR Pair: {self._source_name} -> {self._destination_name}'
def _get_pair_id(self):
""" Gets BLR pair Id from the BLRPairs class"""
for pair_id, blr_pair in self._commcell_object.blr_pairs.blr_pairs.items():
if (blr_pair.get('sourceName').lower() == self._source_name and
blr_pair.get('destinationName').lower() == self._destination_name):
return pair_id
raise SDKException('BLRPairs', '102')
def _get_pair_properties(self):
""" Gets BLR pair properties
Args:
Returns: Gets the BLR pair properties dictionary
Raises:
SDKException:
if response is empty
if response is not success
"""
flag, response = self._commcell_object._cvpysdk_object.make_request(
'GET', self._GET_PAIR % self._pair_id)
if flag:
if response.json() and 'siteInfo' in response.json():
self._pair_properties = response.json().get('siteInfo')[0]
else:
raise SDKException('Response', '102')
else:
response_string = self._commcell_object._update_response_(
response.text)
raise SDKException('Response', '101', response_string)
def _perform_action(self, operation_type):
"""Performs an action on BLR pair
Args:
operation_type (PairOperations): An enum of pair operations of BLRPair class
Returns:
Raises:
SDKException:
if response is empty
if response is not success
"""
status_to_be_set = operation_type.value
site_info = [self._pair_properties.copy()]
site_info[0]['status'] = status_to_be_set.value
flag, response = self._commcell_object._cvpysdk_object.make_request('PUT',
self._services['GET_BLR_PAIRS'],
{'siteInfo': site_info})
if flag:
if response.json():
error_code = response.json().get('errorCode', -1)
if error_code != 0:
raise SDKException('Response', '101')
else:
raise SDKException('Response', '102')
else:
response_string = self._commcell_object._update_response_(
response.text)
raise SDKException('Response', '101', response_string)
@property
def pair_properties(self):
"""Returns a dictionary of the pair properties"""
return self._pair_properties
@property
def pair_status(self):
"""Returns the status of the pair according the to PairStatus enum"""
self.refresh()
return BLRPairs.PairStatus(self._pair_properties.get('status'))
@property
def source(self):
"""Returns: (dict) The properties of the source client
eg: {
name: 'clientName',
client_id: 'client id'
proxy_client_id: 'head proxy_id',
guid: 'client guid',
endpoint: 'endpoint enum'
}
"""
return {
"name": self.pair_properties.get('sourceName'),
"client_id": str(self.pair_properties.get('srcClientId')),
"proxy_client_id": str(self.pair_properties.get('headClientId')),
"guid": self.pair_properties.get('sourceGuid'),
"endpoint": BLRPairs.EndPointTypes(self.pair_properties.get('srcEndPointType')),
}
@property
def source_vm(self):
"""Returns (str): the source VM name"""
return self.source.get('name')
@property
def destination(self):
"""Returns: (dict) The properties of the destination client
eg: {
name: 'clientName',
proxy_client_id: 'tail proxy_id',
guid: 'client guid',
endpoint: 'endpoint enum'
}
"""
return {
"name": self.pair_properties.get('destinationName'),
"client_id": str(self.pair_properties.get('destClientId')),
"proxy_client_id": str(self.pair_properties.get('tailClientId')),
"guid": self.pair_properties.get('destinationGuid'),
"endpoint": BLRPairs.EndPointTypes(self.pair_properties.get('destEndPointType')),
}
@property
def destination_vm(self):
"""Returns (str): the destination VM name"""
return self.destination.get('name')
@property
def lag_time(self):
"""Returns: (int) The replication lag for pair in minutes"""
return self.pair_properties.get('lagTime')
@property
def replication_group_name(self):
"""Returns: (str) The name of the replication group"""
return self.pair_properties.get('replicationGroup', {}).get('replicationGroupName')
@property
def pending_status(self):
"""Returns: (enum) The pair pending Status"""
return BLRPairs.PendingStatus(self.pair_properties.get('pendingStatusCode'))
@property
def pair_flag(self):
"""Returns: (int) The pair's flag status"""
return self.pair_properties.get('flags')
@property
def subclient_props(self):
"""Returns: (dict) The subclient associated with pair's properties
eg: {
"subclientName": "subclient name",
"subclientId": subclient ID,
"instanceId": instance ID,
"backupsetId": backupset ID,
"clientId": client ID
}
"""
return self.pair_properties.get('entity')
@property
def pair_recovery_type(self):
"""Returns: (enum) Returns whether the pair is granular or live"""
return BLRPairs.RecoveryType(self.pair_properties.get('blrRecoveryOpts', {}).get('recoveryType'))
@property
def pair_rpstore_intervals(self):
"""Returns: (dict) A dictionary of intervals set for RPstores
eg: {
'ccrpInterval': 15,
'maxRpStoreOfflineTime': 900,
'useOffPeakSchedule': True,
'acrpInterval': 3600,
'rpMergeDelay': 43200,
'maxRpInterval': 21600,
'rpStoreId': 0,
'rpRetention': 604800,
'rpStoreName': 'N/A'
}
"""
return self.pair_properties.get('blrRecoveryOpts', {}).get('granularV2', {})
@property
def pair_volume_map(self):
"""Returns: (list) Returns a list of volume mappings for FSBLR
eg: [{
'sourceVolumeGUID': 'F961A090-90B3-403A-8629-10203C81517F',
'destVolume': 'F:',
'destVolumeGUID': '0A800478-57E2-42B2-80BA-F7BA1B2E0BE1',
'sourceVolume': 'E:'
}]
"""
return self.pair_properties.get('srcDestVolumeMap', [])
@property
def pair_latest_stats(self):
"""Returns: (list) A list of dictionary of latest statistics for BLR pair
eg: [{
'repDataDeltaActual': 226051,
'ioDelta': 303935,
'repSetSize': 10522460160,
'iopsDelta': 160,
'sizeInRpStore': 0,
'id': 14195,
'repDataDeltaComp': 226051,
'retention': 0,
'timeStamp': {'time': 1622714743}
}]
"""
return self.pair_properties.get('statsList')
def get_pair_stats(self):
"""Returns: (list) A list of dictionary of statistics for BLR pair
eg: [{
'repDataDeltaActual': 226051,
'ioDelta': 303935,
'repSetSize': 10522460160,
'iopsDelta': 160,
'sizeInRpStore': 0,
'id': 14195,
'repDataDeltaComp': 226051,
'retention': 0,
'timeStamp': {'time': 1622714743}
}]
"""
flag, response = (self._commcell_object._cvpysdk_object
.make_request('GET', self._PAIR_STATS % self._pair_id))
if flag:
if response.json() and 'statsList' in response.json():
return response.json().get('statsList', [])
else:
raise SDKException('Response', '102')
else:
response_string = self._commcell_object._update_response_(
response.text)
raise SDKException('Response', '101', response_string)
def get_recovery_point_stores(self):
"""Returns a list of all recovery point stores for the BLR pair
Args:
Returns: Gets the BLR rpstores list
Raises:
SDKException:
if response is empty
if response is not success
"""
destination_client = self._commcell_object.clients.get(int(self.destination['client_id']))
flag, response = self._commcell_object._cvpysdk_object.make_request('GET', self._GRANULAR_RP_STORES %
(self.destination['proxy_client_id'],
str(self.subclient_props['subclientId']),
destination_client.client_guid))
if flag:
if response.json() and 'vmScale' in response.json():
return response.json().get('vmScale', {}).get('restorePoints', [])
else:
raise SDKException('Response', '102')
else:
response_string = self._commcell_object._update_response_(
response.text)
raise SDKException('Response', '101', response_string)
def get_test_failover_vms(self, boot_type: int = 1):
"""Returns the BLRTestFailovers object for all test failover VMs of the BLR pair
Args:
boot_type (int): Refer to BLRTestFailover.BLRBootType
Returns:
BLRTestFailovers object
"""
boot_type = BLRTestFailovers.BLRBootType(boot_type)
return BLRTestFailovers(self._commcell_object, self._pair_id, boot_type)
def stop(self):
"""Stops the BLR Pair
Args:
Returns:
Raises:
SDKException:
if response is empty
if response is not success
"""
self._perform_action(self.PairOperationsStatus.STOP)
def start(self):
"""Starts the BLR Pair
Args:
Returns:
Raises:
SDKException:
if response is empty
if response is not success
"""
self._perform_action(self.PairOperationsStatus.START)
def resume(self):
"""Resumes the BLR Pair
Args:
Returns:
Raises:
SDKException:
if response is empty
if response is not success
"""
self._perform_action(self.PairOperationsStatus.RESUME)
def resync(self):
"""Resyncs the BLR pair
Args:
Returns:
Raises:
SDKException:
if response is empty
if response is not success
"""
self._perform_action(self.PairOperationsStatus.RESYNC)
def suspend(self):
"""Suspends the BLR Pair
Args:
Returns:
Raises:
SDKException:
if response is empty
if response is not success
"""
self._perform_action(self.PairOperationsStatus.SUSPEND)
def wait_for_pair_status(self, expected_status, timeout=30):
"""
Waits for the BLR pair to reach the expected status
Args:
expected_status (enum or str): Enum of PairStatus or string value for pair status
timeout (int) : The amount of time in minutes to wait for pair to
reach status before exiting
Returns:
True, if the expected status is met
False, if the expected status was not met in given time
"""
if isinstance(expected_status, str):
expected_status = BLRPairs.PairStatus[expected_status.upper()]
start_time = time.time()
self.refresh()
while not self.pair_status == expected_status:
time.sleep(30)
if time.time() - start_time > timeout * 60:
break
return self.pair_status == expected_status
def create_replica_copy(self, destination_volumes, copy_volumes, timestamp=None):
"""Perform the DR operation for the BLR pair
Args:
destination_volumes (list) : The destination volumes list
copy_volumes (list) : The copy volumes list
timestamp (int) : The timestamp of the RPstore, only for granular pairs
Returns:
Raises:
SDKException:
if response is empty
if response is not success
"""
restore_point = None
if self.pair_recovery_type == BLRPairs.RecoveryType.GRANULARV2:
if timestamp is not None:
restore_point = [replica_point for replica_point in self.get_recovery_point_stores()
if int(replica_point['timeStamp']) == timestamp][0]
else:
restore_point = self.get_recovery_point_stores()[-1]
destination_client = self._commcell_object.clients.get(int(self.destination['client_id']))
destination_volumes = destination_client.get_mount_volumes(destination_volumes)
copy_volumes = destination_client.get_mount_volumes(copy_volumes)
request_json = {
"taskInfo": {
"task": {
"ownerId": 1,
"taskType": 1,
"ownerName": "",
"initiatedFrom": 1,
"taskFlags": {
"disabled": False
}
},
"subTasks": [
{
"subTaskOperation": 1,
"subTask": {
"subTaskType": 1,
"operationType": 4047
},
"options": {
"backupOpts": {
"mediaOpt": {
"auxcopyJobOption": {
"maxNumberOfStreams": 0,
"allCopies": True,
"useMaximumStreams": True,
"useScallableResourceManagement": False
}
}
},
"adminOpts": {
"blockOperation": {
"operations": [
{
"appId": int(self.subclient_props['subclientId']),
"opType": 8,
"dstProxyClientId": int(self.destination['client_id']),
"fsMountInfo": {
"doLiveMount": True,
"lifeTimeInSec": 7200,
"blrPairId": int(self._pair_id),
"mountPathPairs": [{
"mountPath": copy['accessPathList'][0],
"srcPath": destination['accessPathList'][0],
"srcGuid": destination['guid'],
"dstGuid": copy['guid']} for destination, copy in
zip(destination_volumes, copy_volumes)],
"rp": {
"timeStamp": int(restore_point['timeStamp']),
"sequenceNumber": int(restore_point['sequenceNumber']),
"rpType": 1,
"appConsistent": False,
"dataChangedSize": int(restore_point['dataChangedSize'])
} if restore_point is not None else None
}
}
]
}
},
}
}
]
}
}
if restore_point is None:
admin_opts = request_json['taskInfo']['subTasks'][0]['options']['adminOpts']
del admin_opts['blockOperation']['operations'][0]['fsMountInfo']['rp']
flag, response = self._commcell_object._cvpysdk_object.make_request('POST',
self._services['RESTORE'],
request_json)
if flag:
if response.json():
if "jobIds" in response.json():
return Job(self._commcell_object, response.json()['jobIds'][0])
elif "taskId" in response.json():
return Schedules(self._commcell_object).get(task_id=response.json()['taskId'])
elif "errorCode" in response.json():
error_message = response.json()['errorMessage']
o_str = 'Restore job failed\nError: "{0}"'.format(error_message)
raise SDKException('Job', '102', o_str)
else:
raise SDKException('Job', '102', 'Failed to run the restore job')
else:
raise SDKException('Response', '102')
else:
response_string = self._commcell_object._update_response_(
response.text)
raise SDKException('Response', '101', response_string)
def refresh(self):
""" Refresh the BLR pair properties """
self._get_pair_properties()
class BLRTestFailovers:
"""Class for getting all test failover VMs for BLR pair"""
class BLRBootType(Enum):
TESTBOOT = 1
PERMANENT = 2
class BootStatus(Enum):
NONE = 0
IN_PROGRESS = 1
SUCCESS = 2
FAILED = 3
ABOUT_TO_EXPIRE = 4
EXPIRED = 5
USER_DELETED = 6
DELETE_FAILED = 7
PARTIAL_SUCCESS = 8
def __init__(self, commcell_object, pair_id, boot_type: BLRBootType):
"""Method for making initial data members
Args:
commcell_object (Commcell) -- instance of the Commcell class
pair_id (str) -- Id of the BLR pair
boot_type (BLRBootType) -- The type of the boot for VMs
"""
self._commcell_object = commcell_object
self._services = commcell_object._services
self._pair_id = pair_id
self._boot_type = boot_type
self._boot_vm_dict = None
self._GET_BOOT_VMS = self._services['BLR_BOOT_DETAILS']
self.refresh()
def __repr__(self):
"""String representation of the instance of BLRTestFailovers"""
representation_string = 'BLR Test failover VMs class instance for pair id: {0}'
return representation_string.format(self._pair_id)
def _get_test_failover_vms(self):
"""Gets the list of all test failover VMs
Args:
Returns: Gets the BLR test failover VMs list
Raises:
SDKException:
if response is empty
if response is not success
"""
flag, response = self._commcell_object._cvpysdk_object.make_request(
'GET', self._GET_BOOT_VMS % (self._pair_id, self._boot_type.value))
if flag:
if response.json() and 'siteInfo' in response.json():
return response.json()['siteInfo']
else:
raise SDKException('Response', '102')
else:
response_string = self._commcell_object._update_response_(
response.text)
raise SDKException('Response', '101', response_string)
def refresh(self):
"""Refresh the VMs list for BLR pair"""
self._boot_vm_dict = {boot_dict.get('name'): boot_dict
for boot_dict in self._get_test_failover_vms()}
@property
def all_boot_vms(self):
"""Returns a list of all boot VMs"""
return list(self._boot_vm_dict.keys())
def get_boot_vm_details(self, vm_name):
"""Get the boot VM details for a given vm name
Args:
vm_name (str): The name of the VM to fetch details for
Returns:
dict: {
id (int) : The ID of the test failover VM
uuid (str) : The UUID of the test failover VM
name (str) : The name of the test failover VM
creation_time (int) : The timestamp of VM creation
vm_status (BootStatus) : The status of the test failover VM
status_message (str) : The description of the VM status
}
Raises:
BLRPair not found: If test boot VM name is not found
"""
vm_dict = self._boot_vm_dict.get(vm_name)
if not vm_dict:
raise SDKException('BLRPairs', '102', f'BLR test boot VM with name {vm_name} not found')
return {
'id': vm_dict.get('id'),
'uuid': vm_dict.get('uuid'),
'name': vm_dict.get('name'),
'creation_time': vm_dict.get('creationTime'),
'vm_status': self.BootStatus(vm_dict.get('status')) if vm_dict.get('status') else None,
'status_message': vm_dict.get('statusMessage'),
'expiration_time': vm_dict.get('bestBefore')
}
Classes
class BLRPair (commcell_object, source_name, destination_name)
-
Initialise the ReplicationGroup object for the given group name
Args
commcell_object (Commcell) – instance of the Commcell class source_name (str) – Name of the source of BLR pair destination_name (str) – Name of the destination of BLR pair
Expand source code Browse git
class BLRPair: class PairOperationsStatus(Enum): SUSPEND = BLRPairs.PairStatus.SUSPENDED START = BLRPairs.PairStatus.REPLICATING RESUME = BLRPairs.PairStatus.REPLICATING STOP = BLRPairs.PairStatus.STOPPED RESYNC = BLRPairs.PairStatus.RESYNCING def __init__(self, commcell_object, source_name, destination_name): """Initialise the ReplicationGroup object for the given group name Args: commcell_object (Commcell) -- instance of the Commcell class source_name (str) -- Name of the source of BLR pair destination_name (str) -- Name of the destination of BLR pair """ self._commcell_object = commcell_object self._services = commcell_object._services self._source_name = source_name.lower() self._destination_name = destination_name.lower() self._pair_id = self._get_pair_id() self._pair_properties = None self._source_instance = None self._destination_instance = None self._GET_PAIR = self._services['GET_BLR_PAIR'] self._PAIR_STATS = self._services['GET_BLR_PAIR_STATS'] self._GRANULAR_RP_STORES = self._services['GRANULAR_BLR_POINTS'] self._BOOT_DETAILS = self._services['BLR_BOOT_DETAILS'] self.refresh() def __repr__(self): """String representation of the instance of the BLR pair""" representation_string = 'BLR pair class instance for pair: "{0} -> {1}"' return representation_string.format(self._source_name, self._destination_name) def __str__(self): """String representation of the instance of BLR pair""" return f'BLR Pair: {self._source_name} -> {self._destination_name}' def _get_pair_id(self): """ Gets BLR pair Id from the BLRPairs class""" for pair_id, blr_pair in self._commcell_object.blr_pairs.blr_pairs.items(): if (blr_pair.get('sourceName').lower() == self._source_name and blr_pair.get('destinationName').lower() == self._destination_name): return pair_id raise SDKException('BLRPairs', '102') def _get_pair_properties(self): """ Gets BLR pair properties Args: Returns: Gets the BLR pair properties dictionary Raises: SDKException: if response is empty if response is not success """ flag, response = self._commcell_object._cvpysdk_object.make_request( 'GET', self._GET_PAIR % self._pair_id) if flag: if response.json() and 'siteInfo' in response.json(): self._pair_properties = response.json().get('siteInfo')[0] else: raise SDKException('Response', '102') else: response_string = self._commcell_object._update_response_( response.text) raise SDKException('Response', '101', response_string) def _perform_action(self, operation_type): """Performs an action on BLR pair Args: operation_type (PairOperations): An enum of pair operations of BLRPair class Returns: Raises: SDKException: if response is empty if response is not success """ status_to_be_set = operation_type.value site_info = [self._pair_properties.copy()] site_info[0]['status'] = status_to_be_set.value flag, response = self._commcell_object._cvpysdk_object.make_request('PUT', self._services['GET_BLR_PAIRS'], {'siteInfo': site_info}) if flag: if response.json(): error_code = response.json().get('errorCode', -1) if error_code != 0: raise SDKException('Response', '101') else: raise SDKException('Response', '102') else: response_string = self._commcell_object._update_response_( response.text) raise SDKException('Response', '101', response_string) @property def pair_properties(self): """Returns a dictionary of the pair properties""" return self._pair_properties @property def pair_status(self): """Returns the status of the pair according the to PairStatus enum""" self.refresh() return BLRPairs.PairStatus(self._pair_properties.get('status')) @property def source(self): """Returns: (dict) The properties of the source client eg: { name: 'clientName', client_id: 'client id' proxy_client_id: 'head proxy_id', guid: 'client guid', endpoint: 'endpoint enum' } """ return { "name": self.pair_properties.get('sourceName'), "client_id": str(self.pair_properties.get('srcClientId')), "proxy_client_id": str(self.pair_properties.get('headClientId')), "guid": self.pair_properties.get('sourceGuid'), "endpoint": BLRPairs.EndPointTypes(self.pair_properties.get('srcEndPointType')), } @property def source_vm(self): """Returns (str): the source VM name""" return self.source.get('name') @property def destination(self): """Returns: (dict) The properties of the destination client eg: { name: 'clientName', proxy_client_id: 'tail proxy_id', guid: 'client guid', endpoint: 'endpoint enum' } """ return { "name": self.pair_properties.get('destinationName'), "client_id": str(self.pair_properties.get('destClientId')), "proxy_client_id": str(self.pair_properties.get('tailClientId')), "guid": self.pair_properties.get('destinationGuid'), "endpoint": BLRPairs.EndPointTypes(self.pair_properties.get('destEndPointType')), } @property def destination_vm(self): """Returns (str): the destination VM name""" return self.destination.get('name') @property def lag_time(self): """Returns: (int) The replication lag for pair in minutes""" return self.pair_properties.get('lagTime') @property def replication_group_name(self): """Returns: (str) The name of the replication group""" return self.pair_properties.get('replicationGroup', {}).get('replicationGroupName') @property def pending_status(self): """Returns: (enum) The pair pending Status""" return BLRPairs.PendingStatus(self.pair_properties.get('pendingStatusCode')) @property def pair_flag(self): """Returns: (int) The pair's flag status""" return self.pair_properties.get('flags') @property def subclient_props(self): """Returns: (dict) The subclient associated with pair's properties eg: { "subclientName": "subclient name", "subclientId": subclient ID, "instanceId": instance ID, "backupsetId": backupset ID, "clientId": client ID } """ return self.pair_properties.get('entity') @property def pair_recovery_type(self): """Returns: (enum) Returns whether the pair is granular or live""" return BLRPairs.RecoveryType(self.pair_properties.get('blrRecoveryOpts', {}).get('recoveryType')) @property def pair_rpstore_intervals(self): """Returns: (dict) A dictionary of intervals set for RPstores eg: { 'ccrpInterval': 15, 'maxRpStoreOfflineTime': 900, 'useOffPeakSchedule': True, 'acrpInterval': 3600, 'rpMergeDelay': 43200, 'maxRpInterval': 21600, 'rpStoreId': 0, 'rpRetention': 604800, 'rpStoreName': 'N/A' } """ return self.pair_properties.get('blrRecoveryOpts', {}).get('granularV2', {}) @property def pair_volume_map(self): """Returns: (list) Returns a list of volume mappings for FSBLR eg: [{ 'sourceVolumeGUID': 'F961A090-90B3-403A-8629-10203C81517F', 'destVolume': 'F:', 'destVolumeGUID': '0A800478-57E2-42B2-80BA-F7BA1B2E0BE1', 'sourceVolume': 'E:' }] """ return self.pair_properties.get('srcDestVolumeMap', []) @property def pair_latest_stats(self): """Returns: (list) A list of dictionary of latest statistics for BLR pair eg: [{ 'repDataDeltaActual': 226051, 'ioDelta': 303935, 'repSetSize': 10522460160, 'iopsDelta': 160, 'sizeInRpStore': 0, 'id': 14195, 'repDataDeltaComp': 226051, 'retention': 0, 'timeStamp': {'time': 1622714743} }] """ return self.pair_properties.get('statsList') def get_pair_stats(self): """Returns: (list) A list of dictionary of statistics for BLR pair eg: [{ 'repDataDeltaActual': 226051, 'ioDelta': 303935, 'repSetSize': 10522460160, 'iopsDelta': 160, 'sizeInRpStore': 0, 'id': 14195, 'repDataDeltaComp': 226051, 'retention': 0, 'timeStamp': {'time': 1622714743} }] """ flag, response = (self._commcell_object._cvpysdk_object .make_request('GET', self._PAIR_STATS % self._pair_id)) if flag: if response.json() and 'statsList' in response.json(): return response.json().get('statsList', []) else: raise SDKException('Response', '102') else: response_string = self._commcell_object._update_response_( response.text) raise SDKException('Response', '101', response_string) def get_recovery_point_stores(self): """Returns a list of all recovery point stores for the BLR pair Args: Returns: Gets the BLR rpstores list Raises: SDKException: if response is empty if response is not success """ destination_client = self._commcell_object.clients.get(int(self.destination['client_id'])) flag, response = self._commcell_object._cvpysdk_object.make_request('GET', self._GRANULAR_RP_STORES % (self.destination['proxy_client_id'], str(self.subclient_props['subclientId']), destination_client.client_guid)) if flag: if response.json() and 'vmScale' in response.json(): return response.json().get('vmScale', {}).get('restorePoints', []) else: raise SDKException('Response', '102') else: response_string = self._commcell_object._update_response_( response.text) raise SDKException('Response', '101', response_string) def get_test_failover_vms(self, boot_type: int = 1): """Returns the BLRTestFailovers object for all test failover VMs of the BLR pair Args: boot_type (int): Refer to BLRTestFailover.BLRBootType Returns: BLRTestFailovers object """ boot_type = BLRTestFailovers.BLRBootType(boot_type) return BLRTestFailovers(self._commcell_object, self._pair_id, boot_type) def stop(self): """Stops the BLR Pair Args: Returns: Raises: SDKException: if response is empty if response is not success """ self._perform_action(self.PairOperationsStatus.STOP) def start(self): """Starts the BLR Pair Args: Returns: Raises: SDKException: if response is empty if response is not success """ self._perform_action(self.PairOperationsStatus.START) def resume(self): """Resumes the BLR Pair Args: Returns: Raises: SDKException: if response is empty if response is not success """ self._perform_action(self.PairOperationsStatus.RESUME) def resync(self): """Resyncs the BLR pair Args: Returns: Raises: SDKException: if response is empty if response is not success """ self._perform_action(self.PairOperationsStatus.RESYNC) def suspend(self): """Suspends the BLR Pair Args: Returns: Raises: SDKException: if response is empty if response is not success """ self._perform_action(self.PairOperationsStatus.SUSPEND) def wait_for_pair_status(self, expected_status, timeout=30): """ Waits for the BLR pair to reach the expected status Args: expected_status (enum or str): Enum of PairStatus or string value for pair status timeout (int) : The amount of time in minutes to wait for pair to reach status before exiting Returns: True, if the expected status is met False, if the expected status was not met in given time """ if isinstance(expected_status, str): expected_status = BLRPairs.PairStatus[expected_status.upper()] start_time = time.time() self.refresh() while not self.pair_status == expected_status: time.sleep(30) if time.time() - start_time > timeout * 60: break return self.pair_status == expected_status def create_replica_copy(self, destination_volumes, copy_volumes, timestamp=None): """Perform the DR operation for the BLR pair Args: destination_volumes (list) : The destination volumes list copy_volumes (list) : The copy volumes list timestamp (int) : The timestamp of the RPstore, only for granular pairs Returns: Raises: SDKException: if response is empty if response is not success """ restore_point = None if self.pair_recovery_type == BLRPairs.RecoveryType.GRANULARV2: if timestamp is not None: restore_point = [replica_point for replica_point in self.get_recovery_point_stores() if int(replica_point['timeStamp']) == timestamp][0] else: restore_point = self.get_recovery_point_stores()[-1] destination_client = self._commcell_object.clients.get(int(self.destination['client_id'])) destination_volumes = destination_client.get_mount_volumes(destination_volumes) copy_volumes = destination_client.get_mount_volumes(copy_volumes) request_json = { "taskInfo": { "task": { "ownerId": 1, "taskType": 1, "ownerName": "", "initiatedFrom": 1, "taskFlags": { "disabled": False } }, "subTasks": [ { "subTaskOperation": 1, "subTask": { "subTaskType": 1, "operationType": 4047 }, "options": { "backupOpts": { "mediaOpt": { "auxcopyJobOption": { "maxNumberOfStreams": 0, "allCopies": True, "useMaximumStreams": True, "useScallableResourceManagement": False } } }, "adminOpts": { "blockOperation": { "operations": [ { "appId": int(self.subclient_props['subclientId']), "opType": 8, "dstProxyClientId": int(self.destination['client_id']), "fsMountInfo": { "doLiveMount": True, "lifeTimeInSec": 7200, "blrPairId": int(self._pair_id), "mountPathPairs": [{ "mountPath": copy['accessPathList'][0], "srcPath": destination['accessPathList'][0], "srcGuid": destination['guid'], "dstGuid": copy['guid']} for destination, copy in zip(destination_volumes, copy_volumes)], "rp": { "timeStamp": int(restore_point['timeStamp']), "sequenceNumber": int(restore_point['sequenceNumber']), "rpType": 1, "appConsistent": False, "dataChangedSize": int(restore_point['dataChangedSize']) } if restore_point is not None else None } } ] } }, } } ] } } if restore_point is None: admin_opts = request_json['taskInfo']['subTasks'][0]['options']['adminOpts'] del admin_opts['blockOperation']['operations'][0]['fsMountInfo']['rp'] flag, response = self._commcell_object._cvpysdk_object.make_request('POST', self._services['RESTORE'], request_json) if flag: if response.json(): if "jobIds" in response.json(): return Job(self._commcell_object, response.json()['jobIds'][0]) elif "taskId" in response.json(): return Schedules(self._commcell_object).get(task_id=response.json()['taskId']) elif "errorCode" in response.json(): error_message = response.json()['errorMessage'] o_str = 'Restore job failed\nError: "{0}"'.format(error_message) raise SDKException('Job', '102', o_str) else: raise SDKException('Job', '102', 'Failed to run the restore job') else: raise SDKException('Response', '102') else: response_string = self._commcell_object._update_response_( response.text) raise SDKException('Response', '101', response_string) def refresh(self): """ Refresh the BLR pair properties """ self._get_pair_properties()
Class variables
var PairOperationsStatus
-
An enumeration.
Instance variables
var destination
-
Returns: (dict) The properties of the destination client eg: { name: 'clientName', proxy_client_id: 'tail proxy_id', guid: 'client guid', endpoint: 'endpoint enum' }
Expand source code Browse git
@property def destination(self): """Returns: (dict) The properties of the destination client eg: { name: 'clientName', proxy_client_id: 'tail proxy_id', guid: 'client guid', endpoint: 'endpoint enum' } """ return { "name": self.pair_properties.get('destinationName'), "client_id": str(self.pair_properties.get('destClientId')), "proxy_client_id": str(self.pair_properties.get('tailClientId')), "guid": self.pair_properties.get('destinationGuid'), "endpoint": BLRPairs.EndPointTypes(self.pair_properties.get('destEndPointType')), }
var destination_vm
-
Returns (str): the destination VM name
Expand source code Browse git
@property def destination_vm(self): """Returns (str): the destination VM name""" return self.destination.get('name')
var lag_time
-
Returns: (int) The replication lag for pair in minutes
Expand source code Browse git
@property def lag_time(self): """Returns: (int) The replication lag for pair in minutes""" return self.pair_properties.get('lagTime')
var pair_flag
-
Returns: (int) The pair's flag status
Expand source code Browse git
@property def pair_flag(self): """Returns: (int) The pair's flag status""" return self.pair_properties.get('flags')
var pair_latest_stats
-
Returns: (list) A list of dictionary of latest statistics for BLR pair eg: [{ 'repDataDeltaActual': 226051, 'ioDelta': 303935, 'repSetSize': 10522460160, 'iopsDelta': 160, 'sizeInRpStore': 0, 'id': 14195, 'repDataDeltaComp': 226051, 'retention': 0, 'timeStamp': {'time': 1622714743} }]
Expand source code Browse git
@property def pair_latest_stats(self): """Returns: (list) A list of dictionary of latest statistics for BLR pair eg: [{ 'repDataDeltaActual': 226051, 'ioDelta': 303935, 'repSetSize': 10522460160, 'iopsDelta': 160, 'sizeInRpStore': 0, 'id': 14195, 'repDataDeltaComp': 226051, 'retention': 0, 'timeStamp': {'time': 1622714743} }] """ return self.pair_properties.get('statsList')
var pair_properties
-
Returns a dictionary of the pair properties
Expand source code Browse git
@property def pair_properties(self): """Returns a dictionary of the pair properties""" return self._pair_properties
var pair_recovery_type
-
Returns: (enum) Returns whether the pair is granular or live
Expand source code Browse git
@property def pair_recovery_type(self): """Returns: (enum) Returns whether the pair is granular or live""" return BLRPairs.RecoveryType(self.pair_properties.get('blrRecoveryOpts', {}).get('recoveryType'))
var pair_rpstore_intervals
-
Returns: (dict) A dictionary of intervals set for RPstores eg: { 'ccrpInterval': 15, 'maxRpStoreOfflineTime': 900, 'useOffPeakSchedule': True, 'acrpInterval': 3600, 'rpMergeDelay': 43200, 'maxRpInterval': 21600, 'rpStoreId': 0, 'rpRetention': 604800, 'rpStoreName': 'N/A' }
Expand source code Browse git
@property def pair_rpstore_intervals(self): """Returns: (dict) A dictionary of intervals set for RPstores eg: { 'ccrpInterval': 15, 'maxRpStoreOfflineTime': 900, 'useOffPeakSchedule': True, 'acrpInterval': 3600, 'rpMergeDelay': 43200, 'maxRpInterval': 21600, 'rpStoreId': 0, 'rpRetention': 604800, 'rpStoreName': 'N/A' } """ return self.pair_properties.get('blrRecoveryOpts', {}).get('granularV2', {})
var pair_status
-
Returns the status of the pair according the to PairStatus enum
Expand source code Browse git
@property def pair_status(self): """Returns the status of the pair according the to PairStatus enum""" self.refresh() return BLRPairs.PairStatus(self._pair_properties.get('status'))
var pair_volume_map
-
Returns: (list) Returns a list of volume mappings for FSBLR eg: [{ 'sourceVolumeGUID': 'F961A090-90B3-403A-8629-10203C81517F', 'destVolume': 'F:', 'destVolumeGUID': '0A800478-57E2-42B2-80BA-F7BA1B2E0BE1', 'sourceVolume': 'E:' }]
Expand source code Browse git
@property def pair_volume_map(self): """Returns: (list) Returns a list of volume mappings for FSBLR eg: [{ 'sourceVolumeGUID': 'F961A090-90B3-403A-8629-10203C81517F', 'destVolume': 'F:', 'destVolumeGUID': '0A800478-57E2-42B2-80BA-F7BA1B2E0BE1', 'sourceVolume': 'E:' }] """ return self.pair_properties.get('srcDestVolumeMap', [])
var pending_status
-
Returns: (enum) The pair pending Status
Expand source code Browse git
@property def pending_status(self): """Returns: (enum) The pair pending Status""" return BLRPairs.PendingStatus(self.pair_properties.get('pendingStatusCode'))
var replication_group_name
-
Returns: (str) The name of the replication group
Expand source code Browse git
@property def replication_group_name(self): """Returns: (str) The name of the replication group""" return self.pair_properties.get('replicationGroup', {}).get('replicationGroupName')
var source
-
Returns: (dict) The properties of the source client eg: { name: 'clientName', client_id: 'client id' proxy_client_id: 'head proxy_id', guid: 'client guid', endpoint: 'endpoint enum' }
Expand source code Browse git
@property def source(self): """Returns: (dict) The properties of the source client eg: { name: 'clientName', client_id: 'client id' proxy_client_id: 'head proxy_id', guid: 'client guid', endpoint: 'endpoint enum' } """ return { "name": self.pair_properties.get('sourceName'), "client_id": str(self.pair_properties.get('srcClientId')), "proxy_client_id": str(self.pair_properties.get('headClientId')), "guid": self.pair_properties.get('sourceGuid'), "endpoint": BLRPairs.EndPointTypes(self.pair_properties.get('srcEndPointType')), }
var source_vm
-
Returns (str): the source VM name
Expand source code Browse git
@property def source_vm(self): """Returns (str): the source VM name""" return self.source.get('name')
var subclient_props
-
Returns: (dict) The subclient associated with pair's properties eg: { "subclientName": "subclient name", "subclientId": subclient ID, "instanceId": instance ID, "backupsetId": backupset ID, "clientId": client ID }
Expand source code Browse git
@property def subclient_props(self): """Returns: (dict) The subclient associated with pair's properties eg: { "subclientName": "subclient name", "subclientId": subclient ID, "instanceId": instance ID, "backupsetId": backupset ID, "clientId": client ID } """ return self.pair_properties.get('entity')
Methods
def create_replica_copy(self, destination_volumes, copy_volumes, timestamp=None)
-
Perform the DR operation for the BLR pair
Args
destination_volumes (list) : The destination volumes list copy_volumes (list) : The copy volumes list timestamp (int) : The timestamp of the RPstore, only for granular pairs Returns:
Raises: SDKException: if response is empty if response is not success
Expand source code Browse git
def create_replica_copy(self, destination_volumes, copy_volumes, timestamp=None): """Perform the DR operation for the BLR pair Args: destination_volumes (list) : The destination volumes list copy_volumes (list) : The copy volumes list timestamp (int) : The timestamp of the RPstore, only for granular pairs Returns: Raises: SDKException: if response is empty if response is not success """ restore_point = None if self.pair_recovery_type == BLRPairs.RecoveryType.GRANULARV2: if timestamp is not None: restore_point = [replica_point for replica_point in self.get_recovery_point_stores() if int(replica_point['timeStamp']) == timestamp][0] else: restore_point = self.get_recovery_point_stores()[-1] destination_client = self._commcell_object.clients.get(int(self.destination['client_id'])) destination_volumes = destination_client.get_mount_volumes(destination_volumes) copy_volumes = destination_client.get_mount_volumes(copy_volumes) request_json = { "taskInfo": { "task": { "ownerId": 1, "taskType": 1, "ownerName": "", "initiatedFrom": 1, "taskFlags": { "disabled": False } }, "subTasks": [ { "subTaskOperation": 1, "subTask": { "subTaskType": 1, "operationType": 4047 }, "options": { "backupOpts": { "mediaOpt": { "auxcopyJobOption": { "maxNumberOfStreams": 0, "allCopies": True, "useMaximumStreams": True, "useScallableResourceManagement": False } } }, "adminOpts": { "blockOperation": { "operations": [ { "appId": int(self.subclient_props['subclientId']), "opType": 8, "dstProxyClientId": int(self.destination['client_id']), "fsMountInfo": { "doLiveMount": True, "lifeTimeInSec": 7200, "blrPairId": int(self._pair_id), "mountPathPairs": [{ "mountPath": copy['accessPathList'][0], "srcPath": destination['accessPathList'][0], "srcGuid": destination['guid'], "dstGuid": copy['guid']} for destination, copy in zip(destination_volumes, copy_volumes)], "rp": { "timeStamp": int(restore_point['timeStamp']), "sequenceNumber": int(restore_point['sequenceNumber']), "rpType": 1, "appConsistent": False, "dataChangedSize": int(restore_point['dataChangedSize']) } if restore_point is not None else None } } ] } }, } } ] } } if restore_point is None: admin_opts = request_json['taskInfo']['subTasks'][0]['options']['adminOpts'] del admin_opts['blockOperation']['operations'][0]['fsMountInfo']['rp'] flag, response = self._commcell_object._cvpysdk_object.make_request('POST', self._services['RESTORE'], request_json) if flag: if response.json(): if "jobIds" in response.json(): return Job(self._commcell_object, response.json()['jobIds'][0]) elif "taskId" in response.json(): return Schedules(self._commcell_object).get(task_id=response.json()['taskId']) elif "errorCode" in response.json(): error_message = response.json()['errorMessage'] o_str = 'Restore job failed\nError: "{0}"'.format(error_message) raise SDKException('Job', '102', o_str) else: raise SDKException('Job', '102', 'Failed to run the restore job') else: raise SDKException('Response', '102') else: response_string = self._commcell_object._update_response_( response.text) raise SDKException('Response', '101', response_string)
def get_pair_stats(self)
-
Returns: (list) A list of dictionary of statistics for BLR pair eg: [{ 'repDataDeltaActual': 226051, 'ioDelta': 303935, 'repSetSize': 10522460160, 'iopsDelta': 160, 'sizeInRpStore': 0, 'id': 14195, 'repDataDeltaComp': 226051, 'retention': 0, 'timeStamp': {'time': 1622714743} }]
Expand source code Browse git
def get_pair_stats(self): """Returns: (list) A list of dictionary of statistics for BLR pair eg: [{ 'repDataDeltaActual': 226051, 'ioDelta': 303935, 'repSetSize': 10522460160, 'iopsDelta': 160, 'sizeInRpStore': 0, 'id': 14195, 'repDataDeltaComp': 226051, 'retention': 0, 'timeStamp': {'time': 1622714743} }] """ flag, response = (self._commcell_object._cvpysdk_object .make_request('GET', self._PAIR_STATS % self._pair_id)) if flag: if response.json() and 'statsList' in response.json(): return response.json().get('statsList', []) else: raise SDKException('Response', '102') else: response_string = self._commcell_object._update_response_( response.text) raise SDKException('Response', '101', response_string)
def get_recovery_point_stores(self)
-
Returns a list of all recovery point stores for the BLR pair Args:
Returns: Gets the BLR rpstores list
Raises: SDKException: if response is empty
if response is not success
Expand source code Browse git
def get_recovery_point_stores(self): """Returns a list of all recovery point stores for the BLR pair Args: Returns: Gets the BLR rpstores list Raises: SDKException: if response is empty if response is not success """ destination_client = self._commcell_object.clients.get(int(self.destination['client_id'])) flag, response = self._commcell_object._cvpysdk_object.make_request('GET', self._GRANULAR_RP_STORES % (self.destination['proxy_client_id'], str(self.subclient_props['subclientId']), destination_client.client_guid)) if flag: if response.json() and 'vmScale' in response.json(): return response.json().get('vmScale', {}).get('restorePoints', []) else: raise SDKException('Response', '102') else: response_string = self._commcell_object._update_response_( response.text) raise SDKException('Response', '101', response_string)
def get_test_failover_vms(self, boot_type: int = 1)
-
Returns the BLRTestFailovers object for all test failover VMs of the BLR pair
Args
boot_type
:int
- Refer to BLRTestFailover.BLRBootType
Returns
BLRTestFailovers object
Expand source code Browse git
def get_test_failover_vms(self, boot_type: int = 1): """Returns the BLRTestFailovers object for all test failover VMs of the BLR pair Args: boot_type (int): Refer to BLRTestFailover.BLRBootType Returns: BLRTestFailovers object """ boot_type = BLRTestFailovers.BLRBootType(boot_type) return BLRTestFailovers(self._commcell_object, self._pair_id, boot_type)
def refresh(self)
-
Refresh the BLR pair properties
Expand source code Browse git
def refresh(self): """ Refresh the BLR pair properties """ self._get_pair_properties()
def resume(self)
-
Resumes the BLR Pair Args:
Returns:
Raises: SDKException: if response is empty if response is not success
Expand source code Browse git
def resume(self): """Resumes the BLR Pair Args: Returns: Raises: SDKException: if response is empty if response is not success """ self._perform_action(self.PairOperationsStatus.RESUME)
def resync(self)
-
Resyncs the BLR pair Args:
Returns:
Raises: SDKException: if response is empty if response is not success
Expand source code Browse git
def resync(self): """Resyncs the BLR pair Args: Returns: Raises: SDKException: if response is empty if response is not success """ self._perform_action(self.PairOperationsStatus.RESYNC)
def start(self)
-
Starts the BLR Pair Args:
Returns:
Raises: SDKException: if response is empty if response is not success
Expand source code Browse git
def start(self): """Starts the BLR Pair Args: Returns: Raises: SDKException: if response is empty if response is not success """ self._perform_action(self.PairOperationsStatus.START)
def stop(self)
-
Stops the BLR Pair Args:
Returns:
Raises: SDKException: if response is empty if response is not success
Expand source code Browse git
def stop(self): """Stops the BLR Pair Args: Returns: Raises: SDKException: if response is empty if response is not success """ self._perform_action(self.PairOperationsStatus.STOP)
def suspend(self)
-
Suspends the BLR Pair Args:
Returns:
Raises: SDKException: if response is empty if response is not success
Expand source code Browse git
def suspend(self): """Suspends the BLR Pair Args: Returns: Raises: SDKException: if response is empty if response is not success """ self._perform_action(self.PairOperationsStatus.SUSPEND)
def wait_for_pair_status(self, expected_status, timeout=30)
-
Waits for the BLR pair to reach the expected status Args: expected_status (enum or str): Enum of PairStatus or string value for pair status timeout (int) : The amount of time in minutes to wait for pair to reach status before exiting Returns: True, if the expected status is met False, if the expected status was not met in given time
Expand source code Browse git
def wait_for_pair_status(self, expected_status, timeout=30): """ Waits for the BLR pair to reach the expected status Args: expected_status (enum or str): Enum of PairStatus or string value for pair status timeout (int) : The amount of time in minutes to wait for pair to reach status before exiting Returns: True, if the expected status is met False, if the expected status was not met in given time """ if isinstance(expected_status, str): expected_status = BLRPairs.PairStatus[expected_status.upper()] start_time = time.time() self.refresh() while not self.pair_status == expected_status: time.sleep(30) if time.time() - start_time > timeout * 60: break return self.pair_status == expected_status
class BLRPairs (commcell_object, replication_group_name: str = '')
-
Class for getting all BLR pairs in commcell.
Initialize object of the BLR Pairs
Args
commcell_object (Commcell) – instance of the Commcell class replication_group_name (str)– Name of the replication group (only for VSA BLR)
Expand source code Browse git
class BLRPairs: """Class for getting all BLR pairs in commcell.""" class PairStatus(Enum): NOT_SYNCED = 0 BACKING_UP = 1 RESTORING = 2 RESYNCING = 3 REPLICATING = 4 SUSPENDED = 5 STOPPED = 6 VERIFYING = 7 PROBLEM = 8 FAILED = 9 STARTING = 10 STOPPING = 11 SUSPENDING = 12 RESUMING = 13 FAILING_OVER = 14 FAILOVER_FAILED = 15 FAILOVER_DONE = 16 FAILING_BACK = 17 FAILBACK_FAILED = 18 SWITCHING_ROLES = 19 SWITCH_ROLES_FAILED = 20 class EndPointTypes(Enum): VIRTUALIZATION = 1 FILESYSTEM = 2 DATABASE = 3 class AgentTypes(Enum): VIRTUALIZATION = 'Virtual Server' FILESYSTEM = 'File system' DATABASE = 'Database' class PendingStatus(Enum): NOLAG = 0 class RecoveryType(Enum): LIVE = 1 SNAPSHOT = 2 GRANULAR = 3 GRANULARV2 = 4 class DROperations(Enum): TEST_BOOT = 1, VSA_FAIL_OVER = 2 DELETE = 3 PERMANENT_BOOT = 4 TEST_BOOT_EXTEND = 5 MANAGE_TEST_BOOT = 6 TEST_FS_MOUNT = 7 PERM_FS_MOUNT = 8 TEST_FSMOUNT_EXTEND = 9 MANAGE_TEST_MOUNT = 10 VSA_FAIL_BACK = 11 FS_FAIL_OVER = 12 FS_FAIL_BACK = 13 RESUME_VSA_FAILOVER = 14 CANCEL_VSA_FAILOVER = 15 RESUME_FS_FAILOVER = 16 CANCEL_FS_FAILOVER = 17 ABORT_VSA_FAILBACK = 18 ABORT_FS_FAILBACK = 19 def __init__(self, commcell_object, replication_group_name: str = ''): """Initialize object of the BLR Pairs Args: commcell_object (Commcell) -- instance of the Commcell class replication_group_name (str)-- Name of the replication group (only for VSA BLR) """ self._commcell_object = commcell_object self._services = commcell_object._services self._replication_group_name = replication_group_name.lower() self._replication_group_id = None if self._replication_group_name: self._replication_group_id = self._get_replication_group_id() self._LIST_BLR_PAIRS = self._services['GET_BLR_PAIRS'] self._DELETE_BLR = self._services['DELETE_BLR_PAIR'] self._QEXEC = self._services['EXEC_QCOMMAND'] self._site_info = None self._summary = None self._rpstore_list = None self.refresh() def __repr__(self): """Representation string for the instance of the BLR Pairs class.""" if not self._replication_group_name: return "BLR Pairs for Commserv: '{0}'".format(self._commcell_object.commserv_name) else: return "BLR Pairs for Replication group: '{0}'".format(self._replication_group_name) def _get_replication_group_id(self): """Returns the replication group ID Args: Returns: replication_group_id (str): The ID of the associated replication group Raises: """ if not self._replication_group_name: return '' return (self._commcell_object.replication_groups.replication_groups .get(self._replication_group_name, {}).get('id')) def _update_data(self): """REST API call for getting all the info for all pairs in the commcell. Args: Returns: Raises: SDKException: if response is empty if response is not success """ flag, response = self._commcell_object._cvpysdk_object.make_request( 'GET', self._LIST_BLR_PAIRS) if flag: if response.json(): self._summary = response.json().get('summary', {}) if self._replication_group_id: self._site_info = [site_info for site_info in response.json().get('siteInfo', []) if str(site_info.get('replicationGroup', {}).get('replicationGroupId', 0)) == self._replication_group_id] else: self._site_info = response.json().get('siteInfo', []) else: raise SDKException('Response', '102') else: response_string = self._commcell_object._update_response_(response.text) raise SDKException('Response', '101', response_string) def _update_rpstorelist(self): """REST API call for getting all the rp store in the commcell. Args: Returns: Raises: SDKException: if response is empty if response is not success """ response = self._commcell_object.qoperation_execute('<EVGui_GetLibraryListWCReq libraryType="RPSTORE" />') if response and 'libraryList' in response: self._rpstore_list = response.get('libraryList', []) else: raise SDKException('Response', '102') @property def blr_pairs(self): """REST API call for getting all the BLR pairs in the commcell. Args: Returns: dict - consists of all BLR pairs { "blr_id_1": { "sourceName": "vm1", "destinationName": "DRvm1", "subclientName": "BLR_vm1(<guid>)" }, "blr_id_2": { "sourceName": "vm1", "destinationName": "DRvm1", "subclientName": "BLRSC_vm1_DRvm1_E:" }, } """ pairs = {} for pair_row in self._site_info: pair_id = pair_row.get('id') if pair_id: pairs[str(pair_id)] = { 'sourceName': pair_row.get('sourceName', ''), 'destinationName': pair_row.get('destinationName', ''), 'subclientName': pair_row.get('entity', {}).get('subclientName', '') } return pairs def has_blr_pair(self, source_name, destination_name): """Checks if BLR pair exists or not Args: source_name (str): Name of the source client destination_name (str): Name of the destination client Returns: bool - boolean output whether BLR pair exists or not Raises: SDKException: if proper inputs are not provided """ if not isinstance(source_name, str) or not isinstance(destination_name, str): raise SDKException('BLRPairs', '101') source_name = source_name.lower() destination_name = destination_name.lower() for _, pair_row in self.blr_pairs.items(): if (source_name == pair_row.get('sourceName', '').lower() and destination_name == pair_row.get('destinationName', '').lower()): return True else: return False def create_fsblr_pair(self, source_client_id, destination_client_id, source_volumes, destination_volumes, recovery_type, **kwargs): """Creates a new FSBLR pair on the commcell with the specified options Args: source_client_id (str) : The source client's ID destination_client_id (str) : The destination client's ID source_volumes (list) : The list of all source volumes destination_volumes (list) : The list of all destination volumes recovery_type (RecoveryType): The enum to specify what type of recovery pair is supposed to be **kwargs (dict) : Only used for granular type FSBLR pairs rpstore_id (str) : The ID of the RPstore to be used rpstore_name (str) : The name of the RPStore ccrp_interval (int) : The number of minutes after which CCRP is taken acrp_interval (int) : The number of minutes after which ACRP is taken max_rp_interval (int) : The number of minutes after which RP store's retention is ended rp_merge_delay (int) : Merge recovery points older than time in minutes rp_retention (int) : The number of minutes for which RPstore is retained for rpstore_switch_live(int): The time in minutes after which pair is switch to live if RPstore is offline merge_only_off_peak(bool):Whether to merge RPstore only during off-peak time """ blr_options = { 'BlockReplication_BLRRecoveryOptions': { '@recoveryType': recovery_type.value, 'granularV2': { '@ccrpInterval': kwargs.get('ccrp_interval', 300), '@acrpInterval': kwargs.get('acrp_interval', 0), '@maxRpInterval': kwargs.get('max_rp_interval', 21600), '@rpMergeDelay': kwargs.get('rp_merge_delay', 172800), '@rpRetention': kwargs.get('rp_retention', 604800), '@maxRpStoreOfflineTime': kwargs.get('rpstore_switch_live', 0), '@useOffPeakSchedule': int(kwargs.get('merge_only_off_peak', False)), }}, } if kwargs.get('rpstore_id') and kwargs.get('rpstore_name'): granularv2 = blr_options['BlockReplication_BLRRecoveryOptions']['granularV2'] granularv2['@rpStoreId'] = int(kwargs.get('rpstore_id', 0)) granularv2['@rpStoreName'] = kwargs.get('rpstore_name') blr_options['BlockReplication_BLRRecoveryOptions']['granularV2'] = granularv2 source_client = self._commcell_object.clients.get(int(source_client_id)) destination_client = self._commcell_object.clients.get(int(destination_client_id)) source_client_volumes = source_client.get_mount_volumes(source_volumes) destination_client_volumes = destination_client.get_mount_volumes(destination_volumes) request_json = { "destEndPointType": self.EndPointTypes.FILESYSTEM.value, "blrRecoveryOpts": xmltodict.unparse(blr_options, short_empty_elements=True).replace('\n', ''), "srcEndPointType": self.EndPointTypes.FILESYSTEM.value, "srcDestVolumeMap": [], "destEntity": { "client": { "clientId": int(destination_client_id), "clientName": destination_client.client_name, "hasDrivesInPair": True, "tabLevel": "level-0", "checked": True, } }, "sourceEntity": { "client": { "clientId": int(source_client_id), "clientName": source_client.client_name, "hasDrivesInPair": True, "tabLevel": "level-0", "checked": True, } } } for source, destination in zip(source_client_volumes, destination_client_volumes): request_json['srcDestVolumeMap'].append({ "sourceVolumeGUID": source['guid'], "sourceVolume": source['accessPathList'][0], "destVolumeGUID": destination['guid'], "destVolume": destination['accessPathList'][0], "sourceVolumeSize": source['size'], "disabled": "", }) flag, response = (self._commcell_object._cvpysdk_object .make_request('POST', self._services['CREATE_BLR_PAIR'], request_json)) if flag: if response and response.json(): if response.json().get('errorCode', 0) != 0: response_string = self._commcell_object._update_response_( response.text) raise SDKException('Response', '101', response_string) else: raise SDKException('Response', '102') else: raise SDKException('Response', '101') def get(self, source_name, destination_name): """Get pair name on the basis of source and destination name and return pair object Args: source_name (str): Name of the source client destination_name (str): Name of the destination client Returns: BLRPair object for source and destination Raises: SDKException: if proper inputs are not provided """ if not isinstance(source_name, str) or not isinstance(destination_name, str): raise SDKException('BLRPairs', '101') source_name = source_name.lower() destination_name = destination_name.lower() for _, pair_row in self.blr_pairs.items(): if (source_name == pair_row.get('sourceName', '').lower() and destination_name == pair_row.get('destinationName', '').lower()): return BLRPair(self._commcell_object, source_name, destination_name) else: raise SDKException('BLRPairs', '102', 'No BLR pair exists with source: "{0}" and destination: "{1}"' .format(source_name, destination_name)) def delete(self, source_name, destination_name): """ Deletes the blr pair with source and destination names Args: source_name (str): Name of the source client destination_name (str): Name of the destination client Returns: BLRPair object for source and destination Raises: SDKException: if proper inputs are not provided if response is empty if response is not success """ if not isinstance(source_name, str) or not isinstance(destination_name, str): raise SDKException('BLRPairs', '101') source_name = source_name.lower() destination_name = destination_name.lower() if self.has_blr_pair(source_name, destination_name): for pair_id, pair_row in self.blr_pairs.items(): if (source_name == pair_row.get('sourceName', '').lower() and destination_name == pair_row.get('destinationName', '').lower()): flag, response = self._commcell_object._cvpysdk_object.make_request( method='DELETE', url=self._DELETE_BLR % pair_id) if flag: if 'error' in response.json(): error_message = response.json( )['error']['errorMessage'] o_str = 'Failed to delete Source: {0} and Destination: {1} \nError: "{2}"' \ .format(source_name, destination_name, error_message) raise SDKException('Response', '101', o_str) else: self.refresh() else: raise SDKException('Response', '102') break else: raise SDKException('Response', '102') else: raise SDKException('BLRPairs', '102', 'No BLR pair exists with source: "{0}" and destination: "{1}"' .format(source_name, destination_name)) def refresh(self): """ Refresh the BLR pairs created in the commcell. Args: Returns: Raises: """ self._update_data() self._update_rpstorelist() def get_rpstore_id(self, rpstore_name): """Gets the RPStore ID for the given name Args: rpstore_name (str) : The name of the RP store """ for rpstore in self._rpstore_list: if rpstore.get('library', {}).get('libraryName') == rpstore_name and rpstore.get('MountPathList', ''): return str(rpstore.get('MountPathList')[0] .get('rpStoreLibraryInfo', {}).get('rpStoreId', '')) else: raise SDKException('BLRPairs', '103', f'No RP Store found with name {rpstore_name}') def get_rpstore_mountpath(self, rpstore_name): """Gets the RPStore mount path for the given name Args: rpstore_name (str) : The name of the RP store """ for rpstore in self._rpstore_list: if rpstore.get('library', {}).get('libraryName') == rpstore_name: mount_path = str(rpstore.get('MountPathList', {})[0]['mountPathName']) return mount_path.split()[1].strip() else: raise SDKException('BLRPairs', '103', f'No RP Store found with name {rpstore_name}')
Class variables
var AgentTypes
-
An enumeration.
var DROperations
-
An enumeration.
var EndPointTypes
-
An enumeration.
var PairStatus
-
An enumeration.
var PendingStatus
-
An enumeration.
var RecoveryType
-
An enumeration.
Instance variables
var blr_pairs
-
REST API call for getting all the BLR pairs in the commcell. Args:
Returns
dict - consists of all BLR pairs { "blr_id_1": { "sourceName": "vm1", "destinationName": "DRvm1", "subclientName": "BLR_vm1(
)" }, "blr_id_2": { "sourceName": "vm1", "destinationName": "DRvm1", "subclientName": "BLRSC_vm1_DRvm1_E:" }, } Expand source code Browse git
@property def blr_pairs(self): """REST API call for getting all the BLR pairs in the commcell. Args: Returns: dict - consists of all BLR pairs { "blr_id_1": { "sourceName": "vm1", "destinationName": "DRvm1", "subclientName": "BLR_vm1(<guid>)" }, "blr_id_2": { "sourceName": "vm1", "destinationName": "DRvm1", "subclientName": "BLRSC_vm1_DRvm1_E:" }, } """ pairs = {} for pair_row in self._site_info: pair_id = pair_row.get('id') if pair_id: pairs[str(pair_id)] = { 'sourceName': pair_row.get('sourceName', ''), 'destinationName': pair_row.get('destinationName', ''), 'subclientName': pair_row.get('entity', {}).get('subclientName', '') } return pairs
Methods
def create_fsblr_pair(self, source_client_id, destination_client_id, source_volumes, destination_volumes, recovery_type, **kwargs)
-
Creates a new FSBLR pair on the commcell with the specified options
Args
- source_client_id (str) : The source client's ID
- destination_client_id (str) : The destination client's ID
- source_volumes (list) : The list of all source volumes
- destination_volumes (list) : The list of all destination volumes
recovery_type
:RecoveryType
- The enum to specify what type of recovery pair is supposed to be
**kwargs (dict) : Only used for granular type FSBLR pairs rpstore_id (str) : The ID of the RPstore to be used rpstore_name (str) : The name of the RPStore ccrp_interval (int) : The number of minutes after which CCRP is taken acrp_interval (int) : The number of minutes after which ACRP is taken max_rp_interval (int) : The number of minutes after which RP store's retention is ended rp_merge_delay (int) : Merge recovery points older than time in minutes rp_retention (int) : The number of minutes for which RPstore is retained for rpstore_switch_live(int): The time in minutes after which pair is switch to live if RPstore is offline merge_only_off_peak(bool):Whether to merge RPstore only during off-peak time
Expand source code Browse git
def create_fsblr_pair(self, source_client_id, destination_client_id, source_volumes, destination_volumes, recovery_type, **kwargs): """Creates a new FSBLR pair on the commcell with the specified options Args: source_client_id (str) : The source client's ID destination_client_id (str) : The destination client's ID source_volumes (list) : The list of all source volumes destination_volumes (list) : The list of all destination volumes recovery_type (RecoveryType): The enum to specify what type of recovery pair is supposed to be **kwargs (dict) : Only used for granular type FSBLR pairs rpstore_id (str) : The ID of the RPstore to be used rpstore_name (str) : The name of the RPStore ccrp_interval (int) : The number of minutes after which CCRP is taken acrp_interval (int) : The number of minutes after which ACRP is taken max_rp_interval (int) : The number of minutes after which RP store's retention is ended rp_merge_delay (int) : Merge recovery points older than time in minutes rp_retention (int) : The number of minutes for which RPstore is retained for rpstore_switch_live(int): The time in minutes after which pair is switch to live if RPstore is offline merge_only_off_peak(bool):Whether to merge RPstore only during off-peak time """ blr_options = { 'BlockReplication_BLRRecoveryOptions': { '@recoveryType': recovery_type.value, 'granularV2': { '@ccrpInterval': kwargs.get('ccrp_interval', 300), '@acrpInterval': kwargs.get('acrp_interval', 0), '@maxRpInterval': kwargs.get('max_rp_interval', 21600), '@rpMergeDelay': kwargs.get('rp_merge_delay', 172800), '@rpRetention': kwargs.get('rp_retention', 604800), '@maxRpStoreOfflineTime': kwargs.get('rpstore_switch_live', 0), '@useOffPeakSchedule': int(kwargs.get('merge_only_off_peak', False)), }}, } if kwargs.get('rpstore_id') and kwargs.get('rpstore_name'): granularv2 = blr_options['BlockReplication_BLRRecoveryOptions']['granularV2'] granularv2['@rpStoreId'] = int(kwargs.get('rpstore_id', 0)) granularv2['@rpStoreName'] = kwargs.get('rpstore_name') blr_options['BlockReplication_BLRRecoveryOptions']['granularV2'] = granularv2 source_client = self._commcell_object.clients.get(int(source_client_id)) destination_client = self._commcell_object.clients.get(int(destination_client_id)) source_client_volumes = source_client.get_mount_volumes(source_volumes) destination_client_volumes = destination_client.get_mount_volumes(destination_volumes) request_json = { "destEndPointType": self.EndPointTypes.FILESYSTEM.value, "blrRecoveryOpts": xmltodict.unparse(blr_options, short_empty_elements=True).replace('\n', ''), "srcEndPointType": self.EndPointTypes.FILESYSTEM.value, "srcDestVolumeMap": [], "destEntity": { "client": { "clientId": int(destination_client_id), "clientName": destination_client.client_name, "hasDrivesInPair": True, "tabLevel": "level-0", "checked": True, } }, "sourceEntity": { "client": { "clientId": int(source_client_id), "clientName": source_client.client_name, "hasDrivesInPair": True, "tabLevel": "level-0", "checked": True, } } } for source, destination in zip(source_client_volumes, destination_client_volumes): request_json['srcDestVolumeMap'].append({ "sourceVolumeGUID": source['guid'], "sourceVolume": source['accessPathList'][0], "destVolumeGUID": destination['guid'], "destVolume": destination['accessPathList'][0], "sourceVolumeSize": source['size'], "disabled": "", }) flag, response = (self._commcell_object._cvpysdk_object .make_request('POST', self._services['CREATE_BLR_PAIR'], request_json)) if flag: if response and response.json(): if response.json().get('errorCode', 0) != 0: response_string = self._commcell_object._update_response_( response.text) raise SDKException('Response', '101', response_string) else: raise SDKException('Response', '102') else: raise SDKException('Response', '101')
def delete(self, source_name, destination_name)
-
Deletes the blr pair with source and destination names
Args
source_name
:str
- Name of the source client
destination_name
:str
- Name of the destination client
Returns: BLRPair object for source and destination
Raises
SDKException: if proper inputs are not provided if response is empty if response is not success
Expand source code Browse git
def delete(self, source_name, destination_name): """ Deletes the blr pair with source and destination names Args: source_name (str): Name of the source client destination_name (str): Name of the destination client Returns: BLRPair object for source and destination Raises: SDKException: if proper inputs are not provided if response is empty if response is not success """ if not isinstance(source_name, str) or not isinstance(destination_name, str): raise SDKException('BLRPairs', '101') source_name = source_name.lower() destination_name = destination_name.lower() if self.has_blr_pair(source_name, destination_name): for pair_id, pair_row in self.blr_pairs.items(): if (source_name == pair_row.get('sourceName', '').lower() and destination_name == pair_row.get('destinationName', '').lower()): flag, response = self._commcell_object._cvpysdk_object.make_request( method='DELETE', url=self._DELETE_BLR % pair_id) if flag: if 'error' in response.json(): error_message = response.json( )['error']['errorMessage'] o_str = 'Failed to delete Source: {0} and Destination: {1} \nError: "{2}"' \ .format(source_name, destination_name, error_message) raise SDKException('Response', '101', o_str) else: self.refresh() else: raise SDKException('Response', '102') break else: raise SDKException('Response', '102') else: raise SDKException('BLRPairs', '102', 'No BLR pair exists with source: "{0}" and destination: "{1}"' .format(source_name, destination_name))
def get(self, source_name, destination_name)
-
Get pair name on the basis of source and destination name and return pair object
Args
source_name
:str
- Name of the source client
destination_name
:str
- Name of the destination client
Returns: BLRPair object for source and destination
Raises
SDKException: if proper inputs are not provided
Expand source code Browse git
def get(self, source_name, destination_name): """Get pair name on the basis of source and destination name and return pair object Args: source_name (str): Name of the source client destination_name (str): Name of the destination client Returns: BLRPair object for source and destination Raises: SDKException: if proper inputs are not provided """ if not isinstance(source_name, str) or not isinstance(destination_name, str): raise SDKException('BLRPairs', '101') source_name = source_name.lower() destination_name = destination_name.lower() for _, pair_row in self.blr_pairs.items(): if (source_name == pair_row.get('sourceName', '').lower() and destination_name == pair_row.get('destinationName', '').lower()): return BLRPair(self._commcell_object, source_name, destination_name) else: raise SDKException('BLRPairs', '102', 'No BLR pair exists with source: "{0}" and destination: "{1}"' .format(source_name, destination_name))
def get_rpstore_id(self, rpstore_name)
-
Gets the RPStore ID for the given name
Args
rpstore_name (str) : The name of the RP store
Expand source code Browse git
def get_rpstore_id(self, rpstore_name): """Gets the RPStore ID for the given name Args: rpstore_name (str) : The name of the RP store """ for rpstore in self._rpstore_list: if rpstore.get('library', {}).get('libraryName') == rpstore_name and rpstore.get('MountPathList', ''): return str(rpstore.get('MountPathList')[0] .get('rpStoreLibraryInfo', {}).get('rpStoreId', '')) else: raise SDKException('BLRPairs', '103', f'No RP Store found with name {rpstore_name}')
def get_rpstore_mountpath(self, rpstore_name)
-
Gets the RPStore mount path for the given name
Args
rpstore_name (str) : The name of the RP store
Expand source code Browse git
def get_rpstore_mountpath(self, rpstore_name): """Gets the RPStore mount path for the given name Args: rpstore_name (str) : The name of the RP store """ for rpstore in self._rpstore_list: if rpstore.get('library', {}).get('libraryName') == rpstore_name: mount_path = str(rpstore.get('MountPathList', {})[0]['mountPathName']) return mount_path.split()[1].strip() else: raise SDKException('BLRPairs', '103', f'No RP Store found with name {rpstore_name}')
def has_blr_pair(self, source_name, destination_name)
-
Checks if BLR pair exists or not
Args
source_name
:str
- Name of the source client
destination_name
:str
- Name of the destination client
Returns
bool - boolean output whether BLR pair exists or not
Raises
SDKException: if proper inputs are not provided
Expand source code Browse git
def has_blr_pair(self, source_name, destination_name): """Checks if BLR pair exists or not Args: source_name (str): Name of the source client destination_name (str): Name of the destination client Returns: bool - boolean output whether BLR pair exists or not Raises: SDKException: if proper inputs are not provided """ if not isinstance(source_name, str) or not isinstance(destination_name, str): raise SDKException('BLRPairs', '101') source_name = source_name.lower() destination_name = destination_name.lower() for _, pair_row in self.blr_pairs.items(): if (source_name == pair_row.get('sourceName', '').lower() and destination_name == pair_row.get('destinationName', '').lower()): return True else: return False
def refresh(self)
-
Refresh the BLR pairs created in the commcell. Args:
Returns:
Raises:
Expand source code Browse git
def refresh(self): """ Refresh the BLR pairs created in the commcell. Args: Returns: Raises: """ self._update_data() self._update_rpstorelist()
class BLRTestFailovers (commcell_object, pair_id, boot_type: BLRTestFailovers.BLRBootType)
-
Class for getting all test failover VMs for BLR pair
Method for making initial data members
Args
commcell_object (Commcell) – instance of the Commcell class pair_id (str) – Id of the BLR pair boot_type (BLRBootType) – The type of the boot for VMs
Expand source code Browse git
class BLRTestFailovers: """Class for getting all test failover VMs for BLR pair""" class BLRBootType(Enum): TESTBOOT = 1 PERMANENT = 2 class BootStatus(Enum): NONE = 0 IN_PROGRESS = 1 SUCCESS = 2 FAILED = 3 ABOUT_TO_EXPIRE = 4 EXPIRED = 5 USER_DELETED = 6 DELETE_FAILED = 7 PARTIAL_SUCCESS = 8 def __init__(self, commcell_object, pair_id, boot_type: BLRBootType): """Method for making initial data members Args: commcell_object (Commcell) -- instance of the Commcell class pair_id (str) -- Id of the BLR pair boot_type (BLRBootType) -- The type of the boot for VMs """ self._commcell_object = commcell_object self._services = commcell_object._services self._pair_id = pair_id self._boot_type = boot_type self._boot_vm_dict = None self._GET_BOOT_VMS = self._services['BLR_BOOT_DETAILS'] self.refresh() def __repr__(self): """String representation of the instance of BLRTestFailovers""" representation_string = 'BLR Test failover VMs class instance for pair id: {0}' return representation_string.format(self._pair_id) def _get_test_failover_vms(self): """Gets the list of all test failover VMs Args: Returns: Gets the BLR test failover VMs list Raises: SDKException: if response is empty if response is not success """ flag, response = self._commcell_object._cvpysdk_object.make_request( 'GET', self._GET_BOOT_VMS % (self._pair_id, self._boot_type.value)) if flag: if response.json() and 'siteInfo' in response.json(): return response.json()['siteInfo'] else: raise SDKException('Response', '102') else: response_string = self._commcell_object._update_response_( response.text) raise SDKException('Response', '101', response_string) def refresh(self): """Refresh the VMs list for BLR pair""" self._boot_vm_dict = {boot_dict.get('name'): boot_dict for boot_dict in self._get_test_failover_vms()} @property def all_boot_vms(self): """Returns a list of all boot VMs""" return list(self._boot_vm_dict.keys()) def get_boot_vm_details(self, vm_name): """Get the boot VM details for a given vm name Args: vm_name (str): The name of the VM to fetch details for Returns: dict: { id (int) : The ID of the test failover VM uuid (str) : The UUID of the test failover VM name (str) : The name of the test failover VM creation_time (int) : The timestamp of VM creation vm_status (BootStatus) : The status of the test failover VM status_message (str) : The description of the VM status } Raises: BLRPair not found: If test boot VM name is not found """ vm_dict = self._boot_vm_dict.get(vm_name) if not vm_dict: raise SDKException('BLRPairs', '102', f'BLR test boot VM with name {vm_name} not found') return { 'id': vm_dict.get('id'), 'uuid': vm_dict.get('uuid'), 'name': vm_dict.get('name'), 'creation_time': vm_dict.get('creationTime'), 'vm_status': self.BootStatus(vm_dict.get('status')) if vm_dict.get('status') else None, 'status_message': vm_dict.get('statusMessage'), 'expiration_time': vm_dict.get('bestBefore') }
Class variables
var BLRBootType
-
An enumeration.
var BootStatus
-
An enumeration.
Instance variables
var all_boot_vms
-
Returns a list of all boot VMs
Expand source code Browse git
@property def all_boot_vms(self): """Returns a list of all boot VMs""" return list(self._boot_vm_dict.keys())
Methods
def get_boot_vm_details(self, vm_name)
-
Get the boot VM details for a given vm name
Args
vm_name
:str
- The name of the VM to fetch details for
Returns
dict
- { id (int) : The ID of the test failover VM uuid (str) : The UUID of the test failover VM name (str) : The name of the test failover VM creation_time (int) : The timestamp of VM creation vm_status (BootStatus) : The status of the test failover VM status_message (str) : The description of the VM status
}
Raises
BLRPair not found
- If test boot VM name is not found
Expand source code Browse git
def get_boot_vm_details(self, vm_name): """Get the boot VM details for a given vm name Args: vm_name (str): The name of the VM to fetch details for Returns: dict: { id (int) : The ID of the test failover VM uuid (str) : The UUID of the test failover VM name (str) : The name of the test failover VM creation_time (int) : The timestamp of VM creation vm_status (BootStatus) : The status of the test failover VM status_message (str) : The description of the VM status } Raises: BLRPair not found: If test boot VM name is not found """ vm_dict = self._boot_vm_dict.get(vm_name) if not vm_dict: raise SDKException('BLRPairs', '102', f'BLR test boot VM with name {vm_name} not found') return { 'id': vm_dict.get('id'), 'uuid': vm_dict.get('uuid'), 'name': vm_dict.get('name'), 'creation_time': vm_dict.get('creationTime'), 'vm_status': self.BootStatus(vm_dict.get('status')) if vm_dict.get('status') else None, 'status_message': vm_dict.get('statusMessage'), 'expiration_time': vm_dict.get('bestBefore') }
def refresh(self)
-
Refresh the VMs list for BLR pair
Expand source code Browse git
def refresh(self): """Refresh the VMs list for BLR pair""" self._boot_vm_dict = {boot_dict.get('name'): boot_dict for boot_dict in self._get_test_failover_vms()}