Module cvpysdk.subclients.virtualserver.kubernetes
File for operating on a Virtual Server Kubernetes Subclient.
KubernetesVirtualServerSubclient and ApplicationGroups are the only class defined in this file.
Class: KubernetesVirtualServerSubclient: Derived class from VirtualServerSubClient Base class,representing a Kubernetes Subclient, and to perform operations on that Subclient
KubernetesVirtualServerSubclient:
__init__(
backupset_object,
subclient_name,
subclient_id) -- initialize object of Kubernetes subclient class,
associated with the VirtualServer subclient
full_vm_restore_in_place() -- restores the pod specified by the user to
the same location
full_vm_restore_out_of_place() -- restores the pod specified to the provided
Kubernetes psuedoclient
_prepare_kubernetes_restore_json() -- Restore json prep method for kubernetes
_json_restore_volumeRstOption() -- Restores json for volumeRstOptions
set_advanced_vm_restore_options() -- Advanced VM restore options
_json_restore_virtualServerRstOption() -- json for VirtualServerRst options for Kubernetes.
disk_restore() -- Function to restore disk.
enable_intelli_snap() -- Enables Intellisnap on subclient
guest_file_restore() -- Restore the files and folders to file system destionation
or to target PVC
guest_files_browse() -- Browse files in a application at any point in time
namespace_restore_in_place() -- Perform a namespace level restore in-place
namespace_restore_out_of_place()-- Perform a namespace level restore out-of-place
Class: ApplicationGroups: Derived class from Subclients Base class,representing a Kubernetes ApplicationGroups, and to perform operations on that ApplicationGroups
ApplicationGroups:
__init__(class_object) -- initialize object of Kubernetes subclient class,
associated with the VirtualServer subclient
browse() -- Browse cluster for namespace, applications, volumes, or labels
get_children_node() -- Construct the json object for content and filter
create_application_group() -- creates application group
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.
# --------------------------------------------------------------------------
"""File for operating on a Virtual Server Kubernetes Subclient.
KubernetesVirtualServerSubclient and ApplicationGroups are the only class defined in this file.
Class: KubernetesVirtualServerSubclient: Derived class from VirtualServerSubClient Base
class,representing a Kubernetes Subclient,
and to perform operations on that Subclient
KubernetesVirtualServerSubclient:
__init__(
backupset_object,
subclient_name,
subclient_id) -- initialize object of Kubernetes subclient class,
associated with the VirtualServer subclient
full_vm_restore_in_place() -- restores the pod specified by the user to
the same location
full_vm_restore_out_of_place() -- restores the pod specified to the provided
Kubernetes psuedoclient
_prepare_kubernetes_restore_json() -- Restore json prep method for kubernetes
_json_restore_volumeRstOption() -- Restores json for volumeRstOptions
set_advanced_vm_restore_options() -- Advanced VM restore options
_json_restore_virtualServerRstOption() -- json for VirtualServerRst options for Kubernetes.
disk_restore() -- Function to restore disk.
enable_intelli_snap() -- Enables Intellisnap on subclient
guest_file_restore() -- Restore the files and folders to file system destionation
or to target PVC
guest_files_browse() -- Browse files in a application at any point in time
namespace_restore_in_place() -- Perform a namespace level restore in-place
namespace_restore_out_of_place()-- Perform a namespace level restore out-of-place
Class: ApplicationGroups: Derived class from Subclients Base
class,representing a Kubernetes ApplicationGroups,
and to perform operations on that ApplicationGroups
ApplicationGroups:
__init__(class_object) -- initialize object of Kubernetes subclient class,
associated with the VirtualServer subclient
browse() -- Browse cluster for namespace, applications, volumes, or labels
get_children_node() -- Construct the json object for content and filter
create_application_group() -- creates application group
"""
import copy
from cvpysdk.subclients.vssubclient import VirtualServerSubclient
from cvpysdk.virtualmachinepolicies import VirtualMachinePolicy
from ...exception import SDKException
from ...subclient import Subclients
class KubernetesVirtualServerSubclient(VirtualServerSubclient):
"""Derived class from VirtualServerSubclient Base class.
This represents a Kubernetes virtual server subclient,
and can perform restore operations on only that subclient.
"""
def __init__(self, backupset_object, subclient_name, subclient_id=None):
"""Initialize the Instance object for the given Virtual Server instance.
Args
class_object (backupset_object, subclient_name, subclient_id) -- instance of the
backupset class, subclient name, subclient id
"""
super(KubernetesVirtualServerSubclient, self).__init__(
backupset_object, subclient_name, subclient_id)
self.diskExtension = [".yaml"]
self._disk_option = {
'Original': 0,
'Thick Lazy Zero': 1,
'Thin': 2,
'Thick Eager Zero': 3
}
self._transport_mode = {
'Auto': 0,
'SAN': 1,
'Hot Add': 2,
'NBD': 5,
'NBD SSL': 4
}
def full_app_restore_out_of_place(
self,
apps_to_restore,
restore_namespace,
restored_app_name=None,
kubernetes_client=None,
storage_class=None,
overwrite=True,
copy_precedence=0,
proxy_client=None,
):
"""Restores the FULL Application specified in the input list
to the provided Kubernetes client at the specified namespace with storage class.
If the provided client name is none then it restores the Full Application
to the source Kubernetes client and corresponding namespace and storage class.
Args:
apps_to_restore (list) -- List of Applications that is to be restored
restored_app_name (dict) -- Dictionary mapping new name of Applications
kubernetes_client (str) -- Name of the Kubernetes client where the Application should be restored
Restores to the source Kubernetes client if this value is not specified
storage_class (str) -- Storage class for the PVC to be restored with.
Uses source storage class if not specified.
restore_namespace (str) -- Target namespace where Applications are to be restored
overwrite (bool) -- overwrite the existing Applications if exists
default: True
copy_precedence (int) -- copy precedence value
default: 0
proxy_client (str) -- destination proxy client
Returns:
object - instance of the Job class for this restore job
Raises:
SDKException:
if inputs are not of correct type as per definition
if failed to initialize job
if response is empty
if response is not success
"""
restore_option = {}
# check mandatory input parameters are correct
if apps_to_restore and not isinstance(apps_to_restore, list):
raise SDKException('Subclient', '101')
# populating proxy client. It assumes the proxy controller added in instance
# properties if not specified
if proxy_client is not None:
restore_option['client'] = proxy_client
if restored_app_name:
if not(isinstance(apps_to_restore, list) or
isinstance(restored_app_name, dict)):
raise SDKException('Subclient', '101')
restore_option['restore_new_name'] = restored_app_name
if not kubernetes_client:
kubernetes_client = self._client_object.client_name
restore_option_copy = restore_option.copy()
self._set_restore_inputs(
restore_option,
in_place=False,
vcenter_client=kubernetes_client,
datastore=storage_class,
esx_host=kubernetes_client,
datacenter=restore_namespace,
unconditional_overwrite=overwrite,
vm_to_restore=self._set_vm_to_restore(apps_to_restore),
copy_precedence=copy_precedence,
volume_level_restore=1,
source_item=[]
)
request_json = self._prepare_kubernetes_restore_json(restore_option)
return self._process_restore_response(request_json)
def _prepare_kubernetes_restore_json(self, restore_option):
"""
Prepare Full Application restore Json with all getters
Args:
restore_option - dictionary with all Application restore options
value:
restore_option:
preserve_level (bool) - set the preserve level in restore
unconditional_overwrite (bool) - unconditionally overwrite the disk
in the restore path
destination_path (str)- path where the disk needs to be
restored
client_name (str) - client where the disk needs to be
restored
destination_vendor (str) - vendor id of the Hypervisor
destination_disktype (str) - type of disk needs to be restored
like VHDX,VHD,VMDK
source_item (str)- GUID of Application from which disk needs to
be restored
eg:
\\5F9FA60C-0A89-4BD9-9D02-C5ACB42745EA
copy_precedence_applicable (bool)- True if needs copy_precedence to
be honoured else False
copy_precedence (int) - the copy id from which browse and
restore needs to be performed
datastore (str) - Storage class which the Application PVC needs to be
restored with
disks (list of dict) - list with dict for each disk in Application
eg: [{
name:"pvc-1"
datastore:"storageclass-1"
}
{
name:"pvc-2"
datastore:"storageclass-2"
}
]
guid (str) - GUID of the Application needs to be restored
new_name (str) - New name for the Application to be restored
esx_host (str) - client name where Application need to be restored
name (str) - name of the Application to be restored
returns:
request_json -complete json for perfomring Full Application Restore
options
"""
if restore_option is None:
restore_option = {}
restore_option['paths'] = []
if "destination_vendor" not in restore_option:
restore_option["destination_vendor"] = \
self._backupset_object._instance_object._vendor_id
if restore_option['copy_precedence']:
restore_option['copy_precedence_applicable'] = True
# set all the restore defaults
self._set_restore_defaults(restore_option)
# set the setters
self._backupset_object._instance_object._restore_association = self._subClientEntity
self._json_restore_virtualServerRstOption(restore_option)
self._json_restore_diskLevelVMRestoreOption(restore_option)
self._json_vcenter_instance(restore_option)
_new_name_dict = restore_option.get('restore_new_name', {})
for _each_vm_to_restore in restore_option['vm_to_restore']:
restore_option["new_name"] = _new_name_dict.get(_each_vm_to_restore, _each_vm_to_restore)
namespace_app_map = restore_option.get('namespace_app_map', {})
datacenter = restore_option.get('datacenter', None)
if not namespace_app_map:
# FOR : Full Application Restores Restores
# If namespace_app_map is not passed the it is a full application restore
# so 'datacenter' should be passed. Nothing to do here
pass
elif _each_vm_to_restore in namespace_app_map:
# FOR : Namespace Level Restore
# If _each_vm_to_restore is in namespace_app_map which means it's an application
# and not a namespace, so we need to pass 'datacenter' to advanced restore options
app_ns = namespace_app_map.get(_each_vm_to_restore)
# Getting the target namespace if restoring to a new namespace name
target_ns = _new_name_dict[app_ns]
restore_option['datacenter'] = target_ns
else:
# FOR : Namespace Level Restore
# If it's a namespace, then there is no 'datacenter' needed so pop it
if 'datacenter' in restore_option:
restore_option.pop('datacenter')
self.set_advanced_vm_restore_options(_each_vm_to_restore, restore_option)
# prepare json
request_json = self._restore_json(restore_option=restore_option)
self._virtualserver_option_restore_json["diskLevelVMRestoreOption"][
"advancedRestoreOptions"] = self._advanced_restore_option_list
self._advanced_restore_option_list = []
request_json["taskInfo"]["subTasks"][0]["options"]["restoreOptions"][
"virtualServerRstOption"] = self._virtualserver_option_restore_json
request_json["taskInfo"]["subTasks"][0]["options"]["restoreOptions"][
"volumeRstOption"] = self._json_restore_volumeRstOption(
restore_option)
return request_json
def _json_restore_volumeRstOption(self, value):
"""setter for the Volume restore option for in restore json"""
if not isinstance(value, dict):
raise SDKException('Subclient', '101')
return{
"volumeLeveRestore": False,
"volumeLevelRestoreType": value.get("volume_level_restore", 0)
}
def _json_restore_virtualServerRstOption(self, value):
"""
setter for the Virtual server restore option in restore json
"""
if not isinstance(value, dict):
raise SDKException('Subclient', '101')
self._virtualserver_option_restore_json = {
"isDiskBrowse": value.get("disk_browse", True),
"isFileBrowse": value.get("file_browse", False),
"isVolumeBrowse": False,
"isVirtualLab": value.get("virtual_lab", False),
"esxServer": value.get("esx_server", ""),
"isAttachToNewVM": value.get("attach_to_new_vm", False),
"viewType": "DEFAULT",
"isBlockLevelReplication": value.get("block_level", False),
"datacenter": value.get("datacenter", "")
}
if value.get('replication_guid'):
self._virtualserver_option_restore_json['replicationGuid'] = value['replication_guid']
def _json_restore_advancedRestoreOptions(self, value):
"""setter for the Virtual server restore option in restore json"""
if not isinstance(value, dict):
raise SDKException('Subclient', '101')
self._advanced_option_restore_json = {
"disks": value.get("disks", []),
"guid": value.get("guid", ""),
"newGuid": value.get("new_guid", ""),
"newName": value.get("new_name", ""),
"esxHost": value.get("esx_host", ""),
"projectId": value.get("project_id", ""),
"cluster": value.get("cluster", ""),
"name": value.get("name", ""),
"nics": value.get("nics", []),
"vmIPAddressOptions": value.get("vm_ip_address_options", []),
"FolderPath": value.get("FolderPath", ""),
"resourcePoolPath": value.get("ResourcePool", ""),
"volumeType": value.get("volumeType", "Auto"),
"endUserVMRestore": value.get("end_user_vm_restore", False) # Required by Kubernetes Disk Level Restore
}
value_dict = {
"createPublicIP": ["createPublicIP", ["createPublicIP", ""]],
"restoreAsManagedVM": ["restoreAsManagedVM", ["restoreAsManagedVM", ""]],
"destination_os_name": ["osName", ["destination_os_name", "AUTO"]],
"resourcePoolPath": ["resourcePoolPath", ["resourcePoolPath", ""]],
"datacenter": ["datacenter", ["datacenter", ""]],
"terminationProtected": ["terminationProtected", ["terminationProtected", False]],
"securityGroups": ["securityGroups", ["securityGroups", ""]],
"keyPairList": ["keyPairList", ["keyPairList", ""]]
}
for key in value_dict:
if key in value:
inner_key = value_dict[key][0]
val1, val2 = value_dict[key][1][0], value_dict[key][1][1]
self._advanced_option_restore_json[inner_key] = value.get(val1, val2)
if "vmSize" in value:
val1, val2 = ("instanceSize", "") if not value["vmSize"] else ("vmSize", "vmSize")
self._advanced_option_restore_json["vmSize"] = value.get(val1, val2)
if "ami" in value and value["ami"] is not None:
self._advanced_option_restore_json["templateId"] = value["ami"]["templateId"]
self._advanced_option_restore_json["templateName"] = value["ami"]["templateName"]
if "iamRole" in value and value["iamRole"] is not None:
self._advanced_option_restore_json["roleInfo"] = {
"name": value["iamRole"]
}
if self._instance_object.instance_name == 'openstack':
if "securityGroups" in value and value["securityGroups"] is not None:
self._advanced_option_restore_json["securityGroups"] = [{"groupName": value["securityGroups"]}]
if "destComputerName" in value and value["destComputerName"] is not None:
self._advanced_option_restore_json["destComputerName"] = value["destComputerName"]
if "destComputerUserName" in value and value["destComputerUserName"] is not None:
self._advanced_option_restore_json["destComputerUserName"] = value["destComputerUserName"]
if "instanceAdminPassword" in value and value["instanceAdminPassword"] is not None:
self._advanced_option_restore_json["instanceAdminPassword"] = value["instanceAdminPassword"]
if self.disk_pattern.datastore.value == "DestinationPath":
self._advanced_option_restore_json["DestinationPath"] = value.get("datastore", "")
else:
self._advanced_option_restore_json["Datastore"] = value.get("datastore", "")
if "datacenter" in value: # Required for Kubernetes Disk Level Restore
self._advanced_option_restore_json['datacenter'] = value["datacenter"]
if value.get('block_level'):
self._advanced_option_restore_json["blrRecoveryOpts"] = \
self._json_restore_blrRecoveryOpts(value)
temp_dict = copy.deepcopy(self._advanced_option_restore_json)
return temp_dict
def _get_app_pvc(self, application_id):
"""Get the dictionary of PVCs in the applications with storage class info
Args:
application_id (str) -- Application GUID to get PVC
Returns:
List of dicts with PVC information
"""
app_disks, disk_metadata = self.browse('\\' + application_id)
pvc_path_list = [disk for disk in app_disks if disk.split('.')[-1] != 'yaml']
pvc_list = []
for pvc in pvc_path_list:
temp_dict = {}
pvc_metadata = disk_metadata[pvc]
vs_metadata = pvc_metadata['advanced_data']['browseMetaData']['virtualServerMetaData']
temp_dict['name'] = pvc_metadata['name']
temp_dict['storageclass'] = vs_metadata['datastore']
pvc_list.append(temp_dict)
return pvc_list
def set_advanced_vm_restore_options(self, vm_to_restore, restore_option):
"""
set the advanced restore options for all vm in restore
param
vm_to_restore - Name of the Application to restore
restore_option - restore options that need to be set for advanced restore option
power_on - power on the Application after restore
add_to_failover - Register the Application to Failover Cluster
datastore - Datastore where the Application needs to be restored
disks (list of dict) - list with dict for each disk in Application
eg: [{
name:"pvc-1"
datastore:"storageclass-1"
}
{
name:"pvc-2"
datastore:"storageclass-2"
}
]
guid - GUID of the Application needs to be restored
new_name - New name for the Application to be restored
esx_host - client name where it need to be restored
name - name of the Application to be restored
"""
# Set the new name for the restored Application.
# If new_name is not given, it restores the Application with same name
# with suffix Delete.
vm_names, vm_ids = self._get_vm_ids_and_names_dict_from_browse()
browse_result = self.vm_files_browse()
application_id = vm_ids[vm_to_restore]
# vs metadata from browse result
_metadata = browse_result[1][('\\' + vm_to_restore)]
vs_metadata = _metadata["advanced_data"]["browseMetaData"]["virtualServerMetaData"]
if restore_option['in_place']:
folder_path = vs_metadata.get("inventoryPath", '')
instance_size = vs_metadata.get("instanceSize", '')
else:
folder_path = ''
instance_size = ''
if restore_option.get('resourcePoolPath'):
restore_option['resourcePoolPath'] = vs_metadata['resourcePoolPath']
if restore_option.get('datacenter'):
restore_option['datacenter'] = restore_option.get('datacenter')
if restore_option.get('terminationProtected'):
restore_option['terminationProtected'] = vs_metadata.get('terminationProtected', '')
if restore_option.get('iamRole'):
restore_option['iamRole'] = vs_metadata.get('role', '')
if restore_option.get('securityGroups'):
_security_groups = self._find_security_groups(vs_metadata['networkSecurityGroups'])
restore_option['securityGroups'] = _security_groups
if restore_option.get('keyPairList'):
_keypair_list = self._find_keypair_list(vs_metadata['loginKeyPairs'])
restore_option['keyPairList'] = _keypair_list
# populate restore source item
restore_option['paths'].append("\\" + application_id)
restore_option['name'] = vm_to_restore
restore_option['guid'] = application_id
restore_option["FolderPath"] = folder_path
restore_option["ResourcePool"] = "/"
# populate restore disk and datastore
vm_disks = []
new_name = vm_to_restore
storage_class_map = restore_option.get('storage_class_map', None)
# To populate disk list for each app in case of namespace restore
if storage_class_map:
pvc_list = self._get_app_pvc(application_id)
for pvc in pvc_list:
storageclass_name = pvc['storageclass']
pvc_name = pvc['name']
# If 'datastore' is passed then it's full app restore, else
# it is namespace level restore.
# Namespace level restore can be passed with storage class mapping
if storageclass_name in storage_class_map:
storageclass_name = storage_class_map[storageclass_name]
_disk_dict = self._disk_dict_pattern(pvc_name, storageclass_name, pvc_name)
vm_disks.append(_disk_dict)
restore_option["disks"] = vm_disks
self._set_restore_inputs(
restore_option,
esx_host=restore_option.get('esx_host') or vs_metadata['esxHost'],
instance_size=restore_option.get('instanceSize', instance_size),
new_name=restore_option.get('new_name', "Delete" + vm_to_restore)
)
temp_dict = self._json_restore_advancedRestoreOptions(restore_option)
self._advanced_restore_option_list.append(temp_dict)
def full_app_restore_in_place(
self,
apps_to_restore=None,
overwrite=True,
copy_precedence=0,
proxy_client=None):
"""Restores the FULL Application specified in the input list
to the location same as the actual location of the Application in Kubernetes cluster.
Args:
apps_to_restore (list) -- List of applications to restore
overwrite (bool) -- overwrite the existing Applications if exists
default: True
copy_precedence (int) -- copy precedence value
default: 0
proxy_client (str) -- proxy client to be used for restore
default: proxy added in application group/cluster
Returns:
object - instance of the Job class for this restore job
Raises:
SDKException:
if inputs are not of correct type as per definition
if failed to initialize job
if response is empty
if response is not success
"""
restore_option = {}
# check input parameters are correct
if apps_to_restore and not isinstance(apps_to_restore, list):
raise SDKException('Subclient', '101')
if copy_precedence:
restore_option['copy_precedence_applicable'] = True
if proxy_client is not None:
restore_option['client'] = proxy_client
kubernetes_host = self._client_object.client_name
# set attr for all the option in restore xml from user inputs
self._set_restore_inputs(
restore_option,
vm_to_restore=self._set_vm_to_restore(apps_to_restore),
in_place=True,
esx_host=kubernetes_host,
volume_level_restore=1,
unconditional_overwrite=overwrite,
copy_precedence=copy_precedence
)
request_json = self._prepare_kubernetes_inplace_restore_json(restore_option)
return self._process_restore_response(request_json)
def _prepare_kubernetes_inplace_restore_json(self, restore_option):
"""
Prepare Full Application restore in-place Json with all getters
Args:
restore_option - dictionary with all Application restore options
value:
restore_option:
preserve_level (bool) - set the preserve level in restore
unconditional_overwrite (bool) - unconditionally overwrite the disk
in the restore path
destination_path (str)- path where the disk needs to be
restored
client_name (str) - client where the disk needs to be
restored
destination_vendor (str) - vendor id of the Hypervisor
destination_disktype (str) - type of disk needs to be restored
like VHDX,VHD,VMDK
source_item (str)- GUID of Application from which disk needs to
be restored
eg:
\\5F9FA60C-0A89-4BD9-9D02-C5ACB42745EA
copy_precedence_applicable (bool)- True if needs copy_precedence to
be honoured else False
copy_precedence (int) - the copy id from which browse and
restore needs to be performed
datastore (str) - Storage class which the Application PVC needs to be
restored with
disks (list of dict) - list with dict for each disk in Application
eg: [{
name:"pvc-1"
datastore:"storageclass-1"
}
{
name:"pvc-2"
datastore:"storageclass-2"
}
]
guid (str) - GUID of the Application needs to be restored
new_name (str) - New name for the Application to be restored
esx_host (str) - client name where Application need to be restored
name (str) - name of the Application to be restored
returns:
request_json -complete json for perfomring Full VM Restore
options
"""
if restore_option is None:
restore_option = {}
restore_option['paths'] = []
if "destination_vendor" not in restore_option:
restore_option["destination_vendor"] = \
self._backupset_object._instance_object._vendor_id
if restore_option.get('copy_precedence'):
restore_option['copy_precedence_applicable'] = True
# set all the restore defaults
self._set_restore_defaults(restore_option)
# set the setters
self._backupset_object._instance_object._restore_association = self._subClientEntity
self._json_restore_virtualServerRstOption(restore_option)
self._json_restore_diskLevelVMRestoreOption(restore_option)
self._json_vcenter_instance(restore_option)
for _each_vm_to_restore in restore_option['vm_to_restore']:
if not restore_option["in_place"]:
if 'disk_type' in restore_option:
restore_option['restoreAsManagedVM'] = restore_option['disk_type'][_each_vm_to_restore]
if ("restore_new_name" in restore_option and
restore_option["restore_new_name"] is not None):
restore_option["new_name"] = restore_option["restore_new_name"] + _each_vm_to_restore
else:
restore_option["new_name"] = "del" + _each_vm_to_restore
else:
restore_option["new_name"] = _each_vm_to_restore
self.set_advanced_vm_restore_options(_each_vm_to_restore, restore_option)
# prepare json
request_json = self._restore_json(restore_option=restore_option)
self._virtualserver_option_restore_json["diskLevelVMRestoreOption"][
"advancedRestoreOptions"] = self._advanced_restore_option_list
self._advanced_restore_option_list = []
request_json["taskInfo"]["subTasks"][0]["options"]["restoreOptions"][
"virtualServerRstOption"] = self._virtualserver_option_restore_json
request_json["taskInfo"]["subTasks"][0]["options"]["restoreOptions"][
"volumeRstOption"] = self._json_restore_volumeRstOption(
restore_option)
return request_json
def disk_restore(self,
application_name,
destination_path,
disk_name=None,
proxy_client=None,
**kwargs):
"""Restores the disk specified in the input paths list to the same location
Args:
application_name (str) -- Name of the Application added in subclient content
whose disk is selected for restore
destination_path (str) -- Staging (destination) path to restore the
disk.
disk_name (list) -- name of the disk which has to be restored
(only yaml files permitted - enter full
name of the disk)
default: None
proxy_client (str) -- Destination proxy client to be used
default: None
Kwargs:
Allows parameters to modify disk restore --
copy_precedence (int) -- SP copy precedence from which browse has to
media_agent (str) -- MA needs to use for disk browse
default :Storage policy MA
snap_proxy (str) -- proxy need to be used for disk
restores from snap
default :proxy in instance or subclient
disk_extension (str) -- Extension of disk file (Default: '.yaml')
Returns:
object - instance of the Job class for this restore job
Raises:
SDKException:
if inputs are not passed in proper expected format
if response is empty
if response is not success
"""
vm_names, vm_ids = self._get_vm_ids_and_names_dict_from_browse()
_disk_restore_option = {}
copy_precedence = kwargs.get("copy_precedence", 0)
disk_extn = kwargs.get("disk_extension", '.yaml')
unconditional_overwrite = kwargs.get('unconditional_overwrite', False)
show_deleted_files = kwargs.get('show_deleted_files', False)
# Volume level restore values -
# 4 - Manifest Restore
volume_level_restore = kwargs.get("volume_level_restore", 4)
if not disk_name:
disk_name = []
else:
disk_extn = self._get_disk_extension(disk_name)
# check if inputs are correct
if not (isinstance(application_name, str) and
isinstance(destination_path, str) and
isinstance(disk_name, list)):
raise SDKException('Subclient', '101')
if copy_precedence:
_disk_restore_option['copy_precedence_applicable'] = True
# fetching all disks from the vm
disk_list, disk_info_dict = self.disk_level_browse(
"\\" + vm_ids[application_name])
# Filter out disks with specified extension from disk list
disk_list = list(filter(lambda name: self._get_disk_extension([name]) == disk_extn, disk_list))
disk_info_dict = { disk : disk_info_dict[disk] for disk in disk_list }
if not disk_name: # if disk names are not provided, restore all disks
for each_disk_path in disk_list:
disk_name.append(each_disk_path.split('\\')[-1])
else: # else, check if the given application has a disk with the list of disks in disk_name.
for each_disk in disk_name:
# disk path has GUID in case of files, and application name in case of manifests
each_disk_path = "\\" + \
(vm_ids[application_name] if volume_level_restore != 4 else application_name) + \
"\\" + each_disk.split("\\")[-1]
if each_disk_path not in disk_list:
raise SDKException('Subclient', '111')
_disk_restore_option["destination_vendor"] = \
self._backupset_object._instance_object._vendor_id
if proxy_client is not None:
_disk_restore_option['client'] = proxy_client
else:
_disk_restore_option['client'] = self._backupset_object._instance_object.co_ordinator
# set Source item List
src_item_list = []
for each_disk in disk_name:
src_item_list.append("\\" + vm_ids[application_name] + "\\" + each_disk.split("\\")[-1])
_disk_restore_option['paths'] = src_item_list
_disk_restore_option['unconditional_overwrite'] = unconditional_overwrite
_disk_restore_option['show_deleted_files'] = show_deleted_files
# Populate volume level restore options
_disk_restore_option['volume_level_restore'] = volume_level_restore
self._set_restore_inputs(
_disk_restore_option,
in_place=False,
copy_precedence=copy_precedence,
destination_path=destination_path,
paths=src_item_list
)
request_json = self._prepare_disk_restore_json(_disk_restore_option)
return self._process_restore_response(request_json)
def enable_intelli_snap(self, snap_engine_name=None, proxy_options=None, snapshot_engine_id =None):
"""Enables Intelli Snap for the subclient.
Args:
snap_engine_name (str) -- Snap Engine Name
proxy_options (str) -- to set proxy for Kubernetes
snapshot_engine_id (int) -- Snapshot engine id
Raises:
SDKException:
if failed to enable intelli snap for subclient
"""
if snapshot_engine_id is None:
snapshot_engine_id = 82
properties_dict = {
"isSnapBackupEnabled": True,
"snapToTapeSelectedEngine": {
"snapShotEngineId": snapshot_engine_id,
"snapShotEngineName": snap_engine_name
}
}
if proxy_options is not None:
if "snap_proxy" in proxy_options:
properties_dict["snapToTapeProxyToUse"] = {
"clientName": proxy_options["snap_proxy"]
}
if "backupcopy_proxy" in proxy_options:
properties_dict["useSeparateProxyForSnapToTape"] = True
properties_dict["separateProxyForSnapToTape"] = {
"clientName": proxy_options["backupcopy_proxy"]
}
if "use_source_if_proxy_unreachable" in proxy_options:
properties_dict["snapToTapeProxyToUseSource"] = True
self._set_subclient_properties(
"_commonProperties['snapCopyInfo']", properties_dict)
def guest_file_restore(self,
application_name,
destination_path,
volume_level_restore,
disk_name=None,
proxy_client=None,
restore_list=None,
restore_pvc_guid=None,
**kwargs):
"""perform Guest file restore of the provided path
Args:
application_name_name (str) -- Name of the source application
destination_path (str) -- Path at the destination to restore at
volume_level_restore (str) -- Flag to denote volume_level_restore
Accepted values -
6 for restore to PVC
7 for FS Destination restore
disk_name (str) -- Name of the source PVC
proxy_client (str) -- Access node for restore
restore_list (str) -- List of files or folders to restore. Contains Full path
of files or folders relative to PVC mount point.
Eg. if /tmp is the mount point with files or folder /tmp/folder1/file1,
restore list should have format 'folder1/file1'
restore_pvc_guid (str) -- strGUID of the target PVC
Kwargs:
copy_precedence (int) -- To set copy precedence for restore
disk_extension (str) -- Extention of the disk
unconditional_overwrite (int) -- To set unconditional overwrite for restore
show_deleted_files (bool) -- Whether to show deleted files in browse
in_place (bool) -- If restore job is inplace
Raises:
SDK Exception if
-inputs are not of correct type as per definition
-invalid volume_level_restore passed
"""
vm_names, vm_ids = self._get_vm_ids_and_names_dict_from_browse()
_guest_file_rst_options = {}
_advanced_restore_options = {}
copy_precedence = kwargs.get("copy_precedence", 0)
disk_extn = kwargs.get("disk_extension", '')
overwrite = kwargs.get("unconditional_overwrite", 1)
unconditional_overwrite = kwargs.get('unconditional_overwrite', False)
show_deleted_files = kwargs.get('show_deleted_files', False)
in_place = kwargs.get('in_place', False)
# check if inputs are correct
if not (isinstance(application_name, str) and
isinstance(destination_path, str) and
isinstance(disk_name, str)):
raise SDKException('Subclient', '101')
if volume_level_restore not in [6, 7]:
raise SDKException("Subclient", "102", "Invalid volume level restore type passed")
if copy_precedence:
_guest_file_rst_options['copy_precedence_applicable'] = True
# fetching all disks from the application
disk_list, disk_info_dict = self.disk_level_browse(
"\\" + vm_ids[application_name])
# Filter out disks with specified extension from disk list
disk_list = list(filter(lambda name: self._get_disk_extension([name]) == disk_extn, disk_list))
disk_info_dict = {disk: disk_info_dict[disk] for disk in disk_list}
_guest_file_rst_options["destination_vendor"] = \
self._backupset_object._instance_object._vendor_id
if proxy_client is not None:
_guest_file_rst_options['client'] = proxy_client
else:
_guest_file_rst_options['client'] = self._backupset_object._instance_object.co_ordinator
# set Source item List
src_item_list = []
for each_item in restore_list:
item = "\\".join(each_item.split('/'))
src_item_list.append( "\\" + vm_ids[application_name] + "\\" + disk_name + "\\" + item)
_guest_file_rst_options['paths'] = src_item_list
if volume_level_restore == 6:
if in_place:
restore_pvc_guid = "{}`PersistentVolumeClaim`{}".format(
vm_ids[application_name].split('`')[0], disk_name
)
new_name = disk_name
else:
new_name = restore_pvc_guid.split('`')[-2]
_advanced_restore_options['datacenter'] = "none"
new_guid = restore_pvc_guid
else:
new_guid = "{}`PersistentVolumeClaim`{}".format(
vm_ids[application_name].split('`')[0], disk_name
)
new_name = disk_name
_guest_file_rst_options['in_place'] = in_place
_guest_file_rst_options['volume_level_restore'] = volume_level_restore
_guest_file_rst_options['unconditional_overwrite'] = unconditional_overwrite
_guest_file_rst_options['show_deleted_files'] = show_deleted_files
_advanced_restore_options['new_guid'] = new_guid
_advanced_restore_options['new_name'] = new_name
_advanced_restore_options['name'] = disk_name
_advanced_restore_options['guid'] = vm_ids[application_name]
_advanced_restore_options['end_user_vm_restore'] = True
# set advanced restore options disks
_disk_dict = self._disk_dict_pattern(disk_name, "")
_advanced_restore_options['disks'] = [_disk_dict]
advanced_options_dict = self._json_restore_advancedRestoreOptions(_advanced_restore_options)
self._advanced_restore_option_list.append(advanced_options_dict)
self._set_restore_inputs(
_guest_file_rst_options,
in_place=False,
copy_precedence=copy_precedence,
destination_path=destination_path,
paths=src_item_list
)
request_json = self._prepare_disk_restore_json(_guest_file_rst_options)
# Populate the advancedRestoreOptions section
self._virtualserver_option_restore_json["diskLevelVMRestoreOption"][
"advancedRestoreOptions"] = self._advanced_restore_option_list
self._advanced_restore_option_list = []
return self._process_restore_response(request_json)
def guest_files_browse(
self,
application_path='\\',
show_deleted_files=False,
restore_index=True,
from_date=0,
to_date=0,
copy_precedence=0,
media_agent=""):
"""Browses the Files and Folders inside a Virtual Machine in the time
range specified.
Args:
application_path (str) -- folder path to get the contents
of
default: '\\';
returns the root of the Backup
content
show_deleted_files (bool) -- include deleted files in the
content or not default: False
restore_index (bool) -- restore index if it is not cached
default: True
from_date (int) -- date to get the contents after
format: dd/MM/YYYY
gets contents from 01/01/1970
if not specified
default: 0
to_date (int) -- date to get the contents before
format: dd/MM/YYYY
gets contents till current day
if not specified
default: 0
copy_precedence (int) -- copy precedence to be used
for browsing
media_agent (str) -- Browse MA via with Browse has to happen.
It can be MA different than Storage Policy MA
Returns:
list - list of all folders or files with their full paths
inside the input path
dict - path along with the details like name, file/folder,
size, modification time
Raises:
SDKException:
if from date value is incorrect
if to date value is incorrect
if to date is less than from date
if failed to browse content
if response is empty
if response is not success
"""
return self.browse_in_time(
vm_path, show_deleted_files, restore_index, False, from_date, to_date, copy_precedence,
vm_files_browse=False, media_agent=media_agent)
def _get_apps_in_namespace(self, namespaces):
"""Get the list of applications to be restored with the namespace level restore
Args:
namespaces (list) - List of namespaces
Returns:
list of applictations to be restored with namespaces
"""
apps_to_restore = []
namespace_app_dict = {}
app, app_dict = self.browse()
for app_path, metadata in app_dict.items():
app_name = metadata['name']
app_id = metadata['snap_display_name']
app_ns = app_id.split('`')[0]
app_type = app_id.split('`')[1]
if app_type != 'Namespace' and app_ns in namespaces:
apps_to_restore.append(app_name)
namespace_app_dict[app_name] = app_ns
return apps_to_restore, namespace_app_dict
def namespace_restore_out_of_place(
self,
namespace_to_restore,
target_namespace_name={},
target_cluster_name=None,
storage_class_map=None,
overwrite=True,
copy_precedence=0,
proxy_client=None
):
"""Perform a namespace-level restore out-of-place
Args:
namespace_to_restore (list) -- List of namespaces to restore
target_namespace_name (dict) -- Target namespace name to perform restore at
Eg. {'namespace1': 'namespace1-rst'}
target_cluster_name (str) -- Name of the target cluster to restore at
storage_class_map (dict) -- Mapping of storage classes for transformation
Eg. {'rook-ceph-block' : 'azurefile'}
overwrite (bool) -- Overwrite the existing namespace
Default: true
copy_precedence (int) -- Copy preceedence value
proxy_client (str) -- Name of the proxy client to launch restore
Default : None (Automatic)
Returns:
object - instance of the Job class for this restore job
Raises:
SDKException:
if inputs are not of correct type as per definition
if failed to initialize job
if response is empty
if response is not success
"""
restore_options = {}
# Check mandatory input parameters are correct
if namespace_to_restore and not type(namespace_to_restore) is list:
raise SDKException('Subclient', '101')
# Populating proxy client. It automatically fetches proxy controller from subclient/instance level
# property if not specified
if proxy_client is not None:
restore_options['client'] = proxy_client
restore_new_name = {}
apps_to_restore = []
if not type(target_namespace_name) is dict:
raise SDKException('Subclient', '101')
for ns in namespace_to_restore:
if ns not in target_namespace_name:
target_namespace_name[ns] = ns
restore_new_name.update(target_namespace_name)
namespace_apps, namespace_app_map = self._get_apps_in_namespace(namespace_to_restore)
apps_to_restore.extend(namespace_apps)
for app in apps_to_restore:
restore_new_name[app] = app
apps_to_restore.extend(namespace_to_restore)
restore_options['restore_new_name'] = restore_new_name
restore_options['namespace_app_map'] = namespace_app_map
if not target_cluster_name:
target_cluster_name = self._client_object.client_name
self._set_restore_inputs(
restore_options,
in_place=False,
vcenter_client=target_cluster_name,
esx_host=target_cluster_name,
esx_server=None,
unconditional_overwrite=overwrite,
power_on=True,
vm_to_restore=self._set_vm_to_restore(apps_to_restore),
disk_option=self._disk_option['Original'],
transport_mode=self._transport_mode['Auto'],
copy_precedence=copy_precedence,
volume_level_restore=1,
source_item=[],
source_ip=None,
destination_ip=None,
network=None,
storage_class_map=storage_class_map
)
request_json = self._prepare_kubernetes_restore_json(restore_options)
return self._process_restore_response(request_json)
def namespace_restore_in_place(
self,
namespace_to_restore,
overwrite=True,
copy_precedence=0,
proxy_client=None
):
"""Perform a namespace-level restore in-place
Args:
namespace_to_restore (list) -- List of namespaces to restore
overwrite (bool) -- Overwrite the existing namespace
Default: true
copy_precedence (int) -- Copy preceedence value
proxy_client (str) -- Name of the proxy client to launch restore
Default : None (Automatic)
Returns:
object - instance of the Job class for this restore job
Raises:
SDKException:
if inputs are not of correct type as per definition
if failed to initialize job
if response is empty
if response is not success
"""
restore_options = {}
# Check mandatory input parameters are correct
if namespace_to_restore and not type(namespace_to_restore) is list:
raise SDKException('Subclient', '101')
# Populating proxy client. It automatically fetches proxy controller from subclient/instance level
# property if not specified
if proxy_client is not None:
restore_options['client'] = proxy_client
apps_to_restore = []
namespace_apps, namespace_app_map = self._get_apps_in_namespace(namespace_to_restore)
apps_to_restore.extend(namespace_apps)
apps_to_restore.extend(namespace_to_restore)
client_name = self._client_object.client_name
self._set_restore_inputs(
restore_options,
vm_to_restore=self._set_vm_to_restore(apps_to_restore),
in_place=True,
esx_host=client_name,
esx_server_name="",
volume_level_restore=1,
unconditional_overwrite=overwrite,
disk_option=self._disk_option['Original'],
transport_mode=self._transport_mode['Auto'],
copy_precedence=copy_precedence
)
request_json = self._prepare_kubernetes_inplace_restore_json(restore_options)
return self._process_restore_response(request_json)
class ApplicationGroups(Subclients):
''' Class to create Kubernetes Application groups
Derived from Subclients class
Args:
class_object of Backupset class
'''
def __init__(self, class_object):
super(ApplicationGroups, self).__init__(class_object)
def __do_browse(self, browse_type="Applications", namespace=None, ns_guid=None):
"""Do GET browse request based on the browse type
Args:
browse_type (str) -- Type of browse (mandatory if namespace is not None)
Accepted values - Namespaces, Applications, Volumes, Labels
namespace (str) -- Namespace to browse
ns_guid (str) -- Namespace GUID of namespace to browse
"""
browse_type_dict = {
"Namespaces": "GET_K8S_NS_BROWSE",
"Applications": "GET_K8S_APP_BROWSE",
"Volumes": "GET_K8S_VOLUME_BROWSE",
"Labels": "GET_K8S_LABEL_BROWSE"
}
if not (namespace and ns_guid):
service = browse_type_dict["Namespaces"]
parameters = int(self._client_object.client_id)
else:
service = browse_type_dict[browse_type]
parameters = (namespace, ns_guid, int(self._client_object.client_id))
flag, get_browse = self._cvpysdk_object.make_request(
'GET', self._services[service] % parameters
)
if flag:
if get_browse and get_browse.json():
browse_json = get_browse.json()
if not 'inventoryInfo' in browse_json:
raise SDKException(
'Subclient',
'102',
"Failed to browse cluster content\nContent returned does not have inventoryInfo"
)
else:
return browse_json['inventoryInfo']
else:
raise SDKException(
'Subclient',
'102',
'Failed to browse cluster content\nInvalid response'
)
else:
try:
# If browse fails then append raise HTTP exception to get error reason
get_browse.raise_for_status()
except Exception as exp:
raise SDKException(
'Subclient',
'102',
f'Failed to browse cluster content\nError: "{exp}"'
)
def __get_children_json(self, app_name, app_type, browse_type, browse_response=None, selector=False):
"""Private method to return the json object for the application
Args:
app_name (str) -- Name of the application
app_type (str) -- Application type (FOLDER/VM/Selector)
browse_type (str) -- Browse type of application
browse_response (str) -- Browse response from discovery
selector (str) -- If content is a label selector
"""
# JSON format is different in case of applications and selectors
if selector:
if browse_type == "Applications":
# Casting Applications to Application since `Applications` is not recognized for selectors
browse_type = "Application"
if browse_type not in ['Application', 'Namespaces', 'Volumes']:
raise SDKException(
'Subclient',
'102',
'Invalid browse type for Selector.'
)
return {
"equalsOrNotEquals": True,
"displayName": f"{browse_type}:{app_name}",
"value": f"{browse_type}:{app_name}",
"allOrAnyChildren": True,
"type": app_type,
"name": app_type
}
else:
for app_item in browse_response:
# Iterate over each application in browse response to get strGUID of selected app
if app_item['name'] == app_name:
if browse_type == "Volumes" and app_type == "FOLDER":
app_item['strGUID'].replace('Namespace', 'Volumes')
return {
"equalsOrNotEquals": True,
"displayName": app_item['name'],
"allOrAnyChildren": True,
"type": app_type,
"name": app_item['strGUID']
}
else:
# If for loop completes without returning, then app does not exist in browse
raise SDKException(
'Subclient',
'102',
f'Searched element [{app_name}] not found in browse.'
)
def get_children_node(self, content):
"""Construct and return the json object for content
Args:
content (list) -- Content to parse and construct json object
Check create_application_group for usage.
"""
# List of accepted browse types for application and selector
app_browse_list = ['Applications', 'Labels', 'Volumes']
selector_browse_list = ['Applications', 'Application', 'Namespaces', 'Volumes']
if not type(content) is list:
raise SDKException('Subclient', '101', 'Invalid data type for content.')
children = []
for item in content:
if not type(item) is str:
raise SDKException('Subclient', '101', 'Invalid data type for content.')
# Split first `:` to get content type (Application or Selector).
if item.find(':') < 0:
item = 'Application:' + item
content_type, content_item = item.split(':', 1)
# Split second `:` to get Browse type and app value
if content_item.find(':') < 0:
content_item = 'Applications:' + content_item
browse_type, app = content_item.split(':')
# Format check
if (content_type == 'Selector' and browse_type not in selector_browse_list) or \
(content_type == 'Application' and browse_type not in app_browse_list):
raise SDKException('Subclient', '101', 'Invalid string format for content.')
browse_response = ""
app_type = ""
selector = False
if content_type == 'Selector':
app_type = "Selector"
browse_response=None
app_name = app
selector = True
elif content_type == 'Application':
app_split = app.split('/')
app_name = app_split[-1]
if len(app_split) > 1:
# If split length is > 1, then fetch browse response of application
browse_response = self.browse(browse_type=browse_type, namespace=app_split[0])
app_type = "VM"
else:
# If split length = 1, then fetch browse response of namespace
browse_response = self.browse(browse_type="Namespaces")
app_type = "FOLDER"
else:
raise SDKException('Subclient', '101', 'Invalid content type.')
children.append(self.__get_children_json(
app_name=app_name,
app_type=app_type,
browse_type=browse_type,
browse_response=browse_response,
selector=selector
)
)
return children
def browse(self, browse_type='Namespaces', namespace=None):
"""Browse cluster content
Args:
browse_type (str) -- Browse type to perform
Accepted values - Namespaces, Appilcations, Volumes, Labels
namespace (str) -- Namespace to browse in
"""
if browse_type not in ["Namespaces", "Applications", "Volumes", "Labels"]:
raise SDKException(
'Subclient',
'101',
f'Invalid value passed for browse_type [{browse_type}]'
)
all_namespaces = self.__do_browse(browse_type="Namespaces")
# If namespace is not passed, or browse_type is Namespaces, return browse response from namespaces
if browse_type == "Namespaces" or not namespace:
return all_namespaces
ns_guid = ""
for ns in all_namespaces:
if ns['name'] == namespace:
ns_guid = ns['strGUID']
break
else:
raise SDKException(
'Subclient',
'102',
f"Could not fetch namespace GUID for namespace [{namespace}]"
)
# Encoding ` characters for URL
ns_guid = ns_guid.replace('`', '%60')
return self.__do_browse(browse_type=browse_type, namespace=namespace, ns_guid=ns_guid)
def create_application_group(self,
content,
plan_name=None,
filter=None,
subclient_name="automation"):
"""Create application / Kubernetes Subclient.
Args:
client_id (str) -- Client id
content (list) -- Subclient content. Format 'ContentType:BrowseType:namespace/app'
Should be a list of strings with above format.
Valid ContentType -
Application, Selector.
If not specified, default is 'Application'
Valid BrowseType for Application ContentType -
Applications, Volumes, Labels
If not specified, default is 'Applications'
Valid BrowseType for Selector ContentType -
Application, Applications, Volumes, Namespaces
If not specified, default is 'Namespaces'
Examples -
1. ns001 -- Format : namespace
2. ns001/app001 -- Format : namespace/app
3. Volumes:ns001/pvc001 -- Format : BrowseType:namespace/app
4. Selector:Namespaces:app=demo -n ns004 -- Format : ContentType:BrowseType:namespace
5. ['Application:Volumes:nsvol/vol001', 'nsvol02/app1']
...
plan_name (str) -- Plan name
filter (list) -- filter for subclient content.
See 'content' for format and examples
subclient_name (str) -- Subclient name you want to create Subclient
"""
content_children = []
filter_children = []
# Get the json objects for content and filters
content_children.extend(self.get_children_node(content))
if filter:
filter_children.extend(self.get_children_node(filter))
plan_id = int(self._commcell_object.plans[str(plan_name.lower())])
app_create_json = {
"subClientProperties": {
"vmContentOperationType": 'ADD',
"vmContent": {
"children": content_children
},
"subClientEntity": {
"clientId": int(self._client_object.client_id),
"appName": "Virtual Server",
"applicationId": 106,
"subclientName": subclient_name
},
"planEntity": {
"planId": plan_id
},
"commonProperties": {
"enableBackup": True,
"numberOfBackupStreams": 5,
"isSnapbackupEnabled": True,
"snapCopyInfo": {
"transportModeForVMWare": 0,
"isSnapBackupEnabled": False
}
},
"vsaSubclientProp": {
"autoDetectVMOwner": False,
"quiesceGuestFileSystemAndApplications": True
}
}
}
if filter:
# If filter is passed for subclient, additional flags are added to create subclient json
app_create_json['subClientProperties']['vmFilterOperationType'] = 'ADD'
app_create_json['subClientProperties']['vmDiskFilterOperationType'] = 'ADD'
app_create_json['subClientProperties']['vmFilter'] = { 'children' : filter_children}
flag, response = self._cvpysdk_object.make_request('POST', self._services['ADD_SUBCLIENT'],
app_create_json)
if flag == False:
raise SDKException('Response', '101', self._update_response_(response.text))
Classes
class ApplicationGroups (class_object)
-
Class to create Kubernetes Application groups Derived from Subclients class
Args
class_object of Backupset class
Initialize the Subclients object for the given backupset.
Args
class_object (object) – instance of the Agent / Instance / Backupset class
Returns
object - instance of the Subclients class
Raises
SDKException: if class object is not an instance of Agent / Instance / Backupset
Expand source code Browse git
class ApplicationGroups(Subclients): ''' Class to create Kubernetes Application groups Derived from Subclients class Args: class_object of Backupset class ''' def __init__(self, class_object): super(ApplicationGroups, self).__init__(class_object) def __do_browse(self, browse_type="Applications", namespace=None, ns_guid=None): """Do GET browse request based on the browse type Args: browse_type (str) -- Type of browse (mandatory if namespace is not None) Accepted values - Namespaces, Applications, Volumes, Labels namespace (str) -- Namespace to browse ns_guid (str) -- Namespace GUID of namespace to browse """ browse_type_dict = { "Namespaces": "GET_K8S_NS_BROWSE", "Applications": "GET_K8S_APP_BROWSE", "Volumes": "GET_K8S_VOLUME_BROWSE", "Labels": "GET_K8S_LABEL_BROWSE" } if not (namespace and ns_guid): service = browse_type_dict["Namespaces"] parameters = int(self._client_object.client_id) else: service = browse_type_dict[browse_type] parameters = (namespace, ns_guid, int(self._client_object.client_id)) flag, get_browse = self._cvpysdk_object.make_request( 'GET', self._services[service] % parameters ) if flag: if get_browse and get_browse.json(): browse_json = get_browse.json() if not 'inventoryInfo' in browse_json: raise SDKException( 'Subclient', '102', "Failed to browse cluster content\nContent returned does not have inventoryInfo" ) else: return browse_json['inventoryInfo'] else: raise SDKException( 'Subclient', '102', 'Failed to browse cluster content\nInvalid response' ) else: try: # If browse fails then append raise HTTP exception to get error reason get_browse.raise_for_status() except Exception as exp: raise SDKException( 'Subclient', '102', f'Failed to browse cluster content\nError: "{exp}"' ) def __get_children_json(self, app_name, app_type, browse_type, browse_response=None, selector=False): """Private method to return the json object for the application Args: app_name (str) -- Name of the application app_type (str) -- Application type (FOLDER/VM/Selector) browse_type (str) -- Browse type of application browse_response (str) -- Browse response from discovery selector (str) -- If content is a label selector """ # JSON format is different in case of applications and selectors if selector: if browse_type == "Applications": # Casting Applications to Application since `Applications` is not recognized for selectors browse_type = "Application" if browse_type not in ['Application', 'Namespaces', 'Volumes']: raise SDKException( 'Subclient', '102', 'Invalid browse type for Selector.' ) return { "equalsOrNotEquals": True, "displayName": f"{browse_type}:{app_name}", "value": f"{browse_type}:{app_name}", "allOrAnyChildren": True, "type": app_type, "name": app_type } else: for app_item in browse_response: # Iterate over each application in browse response to get strGUID of selected app if app_item['name'] == app_name: if browse_type == "Volumes" and app_type == "FOLDER": app_item['strGUID'].replace('Namespace', 'Volumes') return { "equalsOrNotEquals": True, "displayName": app_item['name'], "allOrAnyChildren": True, "type": app_type, "name": app_item['strGUID'] } else: # If for loop completes without returning, then app does not exist in browse raise SDKException( 'Subclient', '102', f'Searched element [{app_name}] not found in browse.' ) def get_children_node(self, content): """Construct and return the json object for content Args: content (list) -- Content to parse and construct json object Check create_application_group for usage. """ # List of accepted browse types for application and selector app_browse_list = ['Applications', 'Labels', 'Volumes'] selector_browse_list = ['Applications', 'Application', 'Namespaces', 'Volumes'] if not type(content) is list: raise SDKException('Subclient', '101', 'Invalid data type for content.') children = [] for item in content: if not type(item) is str: raise SDKException('Subclient', '101', 'Invalid data type for content.') # Split first `:` to get content type (Application or Selector). if item.find(':') < 0: item = 'Application:' + item content_type, content_item = item.split(':', 1) # Split second `:` to get Browse type and app value if content_item.find(':') < 0: content_item = 'Applications:' + content_item browse_type, app = content_item.split(':') # Format check if (content_type == 'Selector' and browse_type not in selector_browse_list) or \ (content_type == 'Application' and browse_type not in app_browse_list): raise SDKException('Subclient', '101', 'Invalid string format for content.') browse_response = "" app_type = "" selector = False if content_type == 'Selector': app_type = "Selector" browse_response=None app_name = app selector = True elif content_type == 'Application': app_split = app.split('/') app_name = app_split[-1] if len(app_split) > 1: # If split length is > 1, then fetch browse response of application browse_response = self.browse(browse_type=browse_type, namespace=app_split[0]) app_type = "VM" else: # If split length = 1, then fetch browse response of namespace browse_response = self.browse(browse_type="Namespaces") app_type = "FOLDER" else: raise SDKException('Subclient', '101', 'Invalid content type.') children.append(self.__get_children_json( app_name=app_name, app_type=app_type, browse_type=browse_type, browse_response=browse_response, selector=selector ) ) return children def browse(self, browse_type='Namespaces', namespace=None): """Browse cluster content Args: browse_type (str) -- Browse type to perform Accepted values - Namespaces, Appilcations, Volumes, Labels namespace (str) -- Namespace to browse in """ if browse_type not in ["Namespaces", "Applications", "Volumes", "Labels"]: raise SDKException( 'Subclient', '101', f'Invalid value passed for browse_type [{browse_type}]' ) all_namespaces = self.__do_browse(browse_type="Namespaces") # If namespace is not passed, or browse_type is Namespaces, return browse response from namespaces if browse_type == "Namespaces" or not namespace: return all_namespaces ns_guid = "" for ns in all_namespaces: if ns['name'] == namespace: ns_guid = ns['strGUID'] break else: raise SDKException( 'Subclient', '102', f"Could not fetch namespace GUID for namespace [{namespace}]" ) # Encoding ` characters for URL ns_guid = ns_guid.replace('`', '%60') return self.__do_browse(browse_type=browse_type, namespace=namespace, ns_guid=ns_guid) def create_application_group(self, content, plan_name=None, filter=None, subclient_name="automation"): """Create application / Kubernetes Subclient. Args: client_id (str) -- Client id content (list) -- Subclient content. Format 'ContentType:BrowseType:namespace/app' Should be a list of strings with above format. Valid ContentType - Application, Selector. If not specified, default is 'Application' Valid BrowseType for Application ContentType - Applications, Volumes, Labels If not specified, default is 'Applications' Valid BrowseType for Selector ContentType - Application, Applications, Volumes, Namespaces If not specified, default is 'Namespaces' Examples - 1. ns001 -- Format : namespace 2. ns001/app001 -- Format : namespace/app 3. Volumes:ns001/pvc001 -- Format : BrowseType:namespace/app 4. Selector:Namespaces:app=demo -n ns004 -- Format : ContentType:BrowseType:namespace 5. ['Application:Volumes:nsvol/vol001', 'nsvol02/app1'] ... plan_name (str) -- Plan name filter (list) -- filter for subclient content. See 'content' for format and examples subclient_name (str) -- Subclient name you want to create Subclient """ content_children = [] filter_children = [] # Get the json objects for content and filters content_children.extend(self.get_children_node(content)) if filter: filter_children.extend(self.get_children_node(filter)) plan_id = int(self._commcell_object.plans[str(plan_name.lower())]) app_create_json = { "subClientProperties": { "vmContentOperationType": 'ADD', "vmContent": { "children": content_children }, "subClientEntity": { "clientId": int(self._client_object.client_id), "appName": "Virtual Server", "applicationId": 106, "subclientName": subclient_name }, "planEntity": { "planId": plan_id }, "commonProperties": { "enableBackup": True, "numberOfBackupStreams": 5, "isSnapbackupEnabled": True, "snapCopyInfo": { "transportModeForVMWare": 0, "isSnapBackupEnabled": False } }, "vsaSubclientProp": { "autoDetectVMOwner": False, "quiesceGuestFileSystemAndApplications": True } } } if filter: # If filter is passed for subclient, additional flags are added to create subclient json app_create_json['subClientProperties']['vmFilterOperationType'] = 'ADD' app_create_json['subClientProperties']['vmDiskFilterOperationType'] = 'ADD' app_create_json['subClientProperties']['vmFilter'] = { 'children' : filter_children} flag, response = self._cvpysdk_object.make_request('POST', self._services['ADD_SUBCLIENT'], app_create_json) if flag == False: raise SDKException('Response', '101', self._update_response_(response.text))
Ancestors
Methods
def browse(self, browse_type='Namespaces', namespace=None)
-
Browse cluster content
Args
browse_type (str) – Browse type to perform Accepted values - Namespaces, Appilcations, Volumes, Labels namespace (str) – Namespace to browse in
Expand source code Browse git
def browse(self, browse_type='Namespaces', namespace=None): """Browse cluster content Args: browse_type (str) -- Browse type to perform Accepted values - Namespaces, Appilcations, Volumes, Labels namespace (str) -- Namespace to browse in """ if browse_type not in ["Namespaces", "Applications", "Volumes", "Labels"]: raise SDKException( 'Subclient', '101', f'Invalid value passed for browse_type [{browse_type}]' ) all_namespaces = self.__do_browse(browse_type="Namespaces") # If namespace is not passed, or browse_type is Namespaces, return browse response from namespaces if browse_type == "Namespaces" or not namespace: return all_namespaces ns_guid = "" for ns in all_namespaces: if ns['name'] == namespace: ns_guid = ns['strGUID'] break else: raise SDKException( 'Subclient', '102', f"Could not fetch namespace GUID for namespace [{namespace}]" ) # Encoding ` characters for URL ns_guid = ns_guid.replace('`', '%60') return self.__do_browse(browse_type=browse_type, namespace=namespace, ns_guid=ns_guid)
def create_application_group(self, content, plan_name=None, filter=None, subclient_name='automation')
-
Create application / Kubernetes Subclient.
Args
client_id (str) – Client id
content (list) – Subclient content. Format 'ContentType:BrowseType:namespace/app' Should be a list of strings with above format.
Valid ContentType - Application, Selector. If not specified, default is 'Application' Valid BrowseType for Application ContentType - Applications, Volumes, Labels If not specified, default is 'Applications' Valid BrowseType for Selector ContentType - Application, Applications, Volumes, Namespaces If not specified, default is 'Namespaces' Examples - 1. ns001 -- Format : namespace 2. ns001/app001 -- Format : namespace/app 3. Volumes:ns001/pvc001 -- Format : BrowseType:namespace/app 4. Selector:Namespaces:app=demo -n ns004 -- Format : ContentType:BrowseType:namespace 5. ['Application:Volumes:nsvol/vol001', 'nsvol02/app1'] ...
plan_name (str) – Plan name
filter (list) – filter for subclient content. See 'content' for format and examples
subclient_name (str) – Subclient name you want to create Subclient
Expand source code Browse git
def create_application_group(self, content, plan_name=None, filter=None, subclient_name="automation"): """Create application / Kubernetes Subclient. Args: client_id (str) -- Client id content (list) -- Subclient content. Format 'ContentType:BrowseType:namespace/app' Should be a list of strings with above format. Valid ContentType - Application, Selector. If not specified, default is 'Application' Valid BrowseType for Application ContentType - Applications, Volumes, Labels If not specified, default is 'Applications' Valid BrowseType for Selector ContentType - Application, Applications, Volumes, Namespaces If not specified, default is 'Namespaces' Examples - 1. ns001 -- Format : namespace 2. ns001/app001 -- Format : namespace/app 3. Volumes:ns001/pvc001 -- Format : BrowseType:namespace/app 4. Selector:Namespaces:app=demo -n ns004 -- Format : ContentType:BrowseType:namespace 5. ['Application:Volumes:nsvol/vol001', 'nsvol02/app1'] ... plan_name (str) -- Plan name filter (list) -- filter for subclient content. See 'content' for format and examples subclient_name (str) -- Subclient name you want to create Subclient """ content_children = [] filter_children = [] # Get the json objects for content and filters content_children.extend(self.get_children_node(content)) if filter: filter_children.extend(self.get_children_node(filter)) plan_id = int(self._commcell_object.plans[str(plan_name.lower())]) app_create_json = { "subClientProperties": { "vmContentOperationType": 'ADD', "vmContent": { "children": content_children }, "subClientEntity": { "clientId": int(self._client_object.client_id), "appName": "Virtual Server", "applicationId": 106, "subclientName": subclient_name }, "planEntity": { "planId": plan_id }, "commonProperties": { "enableBackup": True, "numberOfBackupStreams": 5, "isSnapbackupEnabled": True, "snapCopyInfo": { "transportModeForVMWare": 0, "isSnapBackupEnabled": False } }, "vsaSubclientProp": { "autoDetectVMOwner": False, "quiesceGuestFileSystemAndApplications": True } } } if filter: # If filter is passed for subclient, additional flags are added to create subclient json app_create_json['subClientProperties']['vmFilterOperationType'] = 'ADD' app_create_json['subClientProperties']['vmDiskFilterOperationType'] = 'ADD' app_create_json['subClientProperties']['vmFilter'] = { 'children' : filter_children} flag, response = self._cvpysdk_object.make_request('POST', self._services['ADD_SUBCLIENT'], app_create_json) if flag == False: raise SDKException('Response', '101', self._update_response_(response.text))
def get_children_node(self, content)
-
Construct and return the json object for content
Args
content (list) – Content to parse and construct json object Check create_application_group for usage.
Expand source code Browse git
def get_children_node(self, content): """Construct and return the json object for content Args: content (list) -- Content to parse and construct json object Check create_application_group for usage. """ # List of accepted browse types for application and selector app_browse_list = ['Applications', 'Labels', 'Volumes'] selector_browse_list = ['Applications', 'Application', 'Namespaces', 'Volumes'] if not type(content) is list: raise SDKException('Subclient', '101', 'Invalid data type for content.') children = [] for item in content: if not type(item) is str: raise SDKException('Subclient', '101', 'Invalid data type for content.') # Split first `:` to get content type (Application or Selector). if item.find(':') < 0: item = 'Application:' + item content_type, content_item = item.split(':', 1) # Split second `:` to get Browse type and app value if content_item.find(':') < 0: content_item = 'Applications:' + content_item browse_type, app = content_item.split(':') # Format check if (content_type == 'Selector' and browse_type not in selector_browse_list) or \ (content_type == 'Application' and browse_type not in app_browse_list): raise SDKException('Subclient', '101', 'Invalid string format for content.') browse_response = "" app_type = "" selector = False if content_type == 'Selector': app_type = "Selector" browse_response=None app_name = app selector = True elif content_type == 'Application': app_split = app.split('/') app_name = app_split[-1] if len(app_split) > 1: # If split length is > 1, then fetch browse response of application browse_response = self.browse(browse_type=browse_type, namespace=app_split[0]) app_type = "VM" else: # If split length = 1, then fetch browse response of namespace browse_response = self.browse(browse_type="Namespaces") app_type = "FOLDER" else: raise SDKException('Subclient', '101', 'Invalid content type.') children.append(self.__get_children_json( app_name=app_name, app_type=app_type, browse_type=browse_type, browse_response=browse_response, selector=selector ) ) return children
Inherited members
class KubernetesVirtualServerSubclient (backupset_object, subclient_name, subclient_id=None)
-
Derived class from VirtualServerSubclient Base class. This represents a Kubernetes virtual server subclient, and can perform restore operations on only that subclient.
Initialize the Instance object for the given Virtual Server instance. Args class_object (backupset_object, subclient_name, subclient_id) – instance of the backupset class, subclient name, subclient id
Expand source code Browse git
class KubernetesVirtualServerSubclient(VirtualServerSubclient): """Derived class from VirtualServerSubclient Base class. This represents a Kubernetes virtual server subclient, and can perform restore operations on only that subclient. """ def __init__(self, backupset_object, subclient_name, subclient_id=None): """Initialize the Instance object for the given Virtual Server instance. Args class_object (backupset_object, subclient_name, subclient_id) -- instance of the backupset class, subclient name, subclient id """ super(KubernetesVirtualServerSubclient, self).__init__( backupset_object, subclient_name, subclient_id) self.diskExtension = [".yaml"] self._disk_option = { 'Original': 0, 'Thick Lazy Zero': 1, 'Thin': 2, 'Thick Eager Zero': 3 } self._transport_mode = { 'Auto': 0, 'SAN': 1, 'Hot Add': 2, 'NBD': 5, 'NBD SSL': 4 } def full_app_restore_out_of_place( self, apps_to_restore, restore_namespace, restored_app_name=None, kubernetes_client=None, storage_class=None, overwrite=True, copy_precedence=0, proxy_client=None, ): """Restores the FULL Application specified in the input list to the provided Kubernetes client at the specified namespace with storage class. If the provided client name is none then it restores the Full Application to the source Kubernetes client and corresponding namespace and storage class. Args: apps_to_restore (list) -- List of Applications that is to be restored restored_app_name (dict) -- Dictionary mapping new name of Applications kubernetes_client (str) -- Name of the Kubernetes client where the Application should be restored Restores to the source Kubernetes client if this value is not specified storage_class (str) -- Storage class for the PVC to be restored with. Uses source storage class if not specified. restore_namespace (str) -- Target namespace where Applications are to be restored overwrite (bool) -- overwrite the existing Applications if exists default: True copy_precedence (int) -- copy precedence value default: 0 proxy_client (str) -- destination proxy client Returns: object - instance of the Job class for this restore job Raises: SDKException: if inputs are not of correct type as per definition if failed to initialize job if response is empty if response is not success """ restore_option = {} # check mandatory input parameters are correct if apps_to_restore and not isinstance(apps_to_restore, list): raise SDKException('Subclient', '101') # populating proxy client. It assumes the proxy controller added in instance # properties if not specified if proxy_client is not None: restore_option['client'] = proxy_client if restored_app_name: if not(isinstance(apps_to_restore, list) or isinstance(restored_app_name, dict)): raise SDKException('Subclient', '101') restore_option['restore_new_name'] = restored_app_name if not kubernetes_client: kubernetes_client = self._client_object.client_name restore_option_copy = restore_option.copy() self._set_restore_inputs( restore_option, in_place=False, vcenter_client=kubernetes_client, datastore=storage_class, esx_host=kubernetes_client, datacenter=restore_namespace, unconditional_overwrite=overwrite, vm_to_restore=self._set_vm_to_restore(apps_to_restore), copy_precedence=copy_precedence, volume_level_restore=1, source_item=[] ) request_json = self._prepare_kubernetes_restore_json(restore_option) return self._process_restore_response(request_json) def _prepare_kubernetes_restore_json(self, restore_option): """ Prepare Full Application restore Json with all getters Args: restore_option - dictionary with all Application restore options value: restore_option: preserve_level (bool) - set the preserve level in restore unconditional_overwrite (bool) - unconditionally overwrite the disk in the restore path destination_path (str)- path where the disk needs to be restored client_name (str) - client where the disk needs to be restored destination_vendor (str) - vendor id of the Hypervisor destination_disktype (str) - type of disk needs to be restored like VHDX,VHD,VMDK source_item (str)- GUID of Application from which disk needs to be restored eg: \\5F9FA60C-0A89-4BD9-9D02-C5ACB42745EA copy_precedence_applicable (bool)- True if needs copy_precedence to be honoured else False copy_precedence (int) - the copy id from which browse and restore needs to be performed datastore (str) - Storage class which the Application PVC needs to be restored with disks (list of dict) - list with dict for each disk in Application eg: [{ name:"pvc-1" datastore:"storageclass-1" } { name:"pvc-2" datastore:"storageclass-2" } ] guid (str) - GUID of the Application needs to be restored new_name (str) - New name for the Application to be restored esx_host (str) - client name where Application need to be restored name (str) - name of the Application to be restored returns: request_json -complete json for perfomring Full Application Restore options """ if restore_option is None: restore_option = {} restore_option['paths'] = [] if "destination_vendor" not in restore_option: restore_option["destination_vendor"] = \ self._backupset_object._instance_object._vendor_id if restore_option['copy_precedence']: restore_option['copy_precedence_applicable'] = True # set all the restore defaults self._set_restore_defaults(restore_option) # set the setters self._backupset_object._instance_object._restore_association = self._subClientEntity self._json_restore_virtualServerRstOption(restore_option) self._json_restore_diskLevelVMRestoreOption(restore_option) self._json_vcenter_instance(restore_option) _new_name_dict = restore_option.get('restore_new_name', {}) for _each_vm_to_restore in restore_option['vm_to_restore']: restore_option["new_name"] = _new_name_dict.get(_each_vm_to_restore, _each_vm_to_restore) namespace_app_map = restore_option.get('namespace_app_map', {}) datacenter = restore_option.get('datacenter', None) if not namespace_app_map: # FOR : Full Application Restores Restores # If namespace_app_map is not passed the it is a full application restore # so 'datacenter' should be passed. Nothing to do here pass elif _each_vm_to_restore in namespace_app_map: # FOR : Namespace Level Restore # If _each_vm_to_restore is in namespace_app_map which means it's an application # and not a namespace, so we need to pass 'datacenter' to advanced restore options app_ns = namespace_app_map.get(_each_vm_to_restore) # Getting the target namespace if restoring to a new namespace name target_ns = _new_name_dict[app_ns] restore_option['datacenter'] = target_ns else: # FOR : Namespace Level Restore # If it's a namespace, then there is no 'datacenter' needed so pop it if 'datacenter' in restore_option: restore_option.pop('datacenter') self.set_advanced_vm_restore_options(_each_vm_to_restore, restore_option) # prepare json request_json = self._restore_json(restore_option=restore_option) self._virtualserver_option_restore_json["diskLevelVMRestoreOption"][ "advancedRestoreOptions"] = self._advanced_restore_option_list self._advanced_restore_option_list = [] request_json["taskInfo"]["subTasks"][0]["options"]["restoreOptions"][ "virtualServerRstOption"] = self._virtualserver_option_restore_json request_json["taskInfo"]["subTasks"][0]["options"]["restoreOptions"][ "volumeRstOption"] = self._json_restore_volumeRstOption( restore_option) return request_json def _json_restore_volumeRstOption(self, value): """setter for the Volume restore option for in restore json""" if not isinstance(value, dict): raise SDKException('Subclient', '101') return{ "volumeLeveRestore": False, "volumeLevelRestoreType": value.get("volume_level_restore", 0) } def _json_restore_virtualServerRstOption(self, value): """ setter for the Virtual server restore option in restore json """ if not isinstance(value, dict): raise SDKException('Subclient', '101') self._virtualserver_option_restore_json = { "isDiskBrowse": value.get("disk_browse", True), "isFileBrowse": value.get("file_browse", False), "isVolumeBrowse": False, "isVirtualLab": value.get("virtual_lab", False), "esxServer": value.get("esx_server", ""), "isAttachToNewVM": value.get("attach_to_new_vm", False), "viewType": "DEFAULT", "isBlockLevelReplication": value.get("block_level", False), "datacenter": value.get("datacenter", "") } if value.get('replication_guid'): self._virtualserver_option_restore_json['replicationGuid'] = value['replication_guid'] def _json_restore_advancedRestoreOptions(self, value): """setter for the Virtual server restore option in restore json""" if not isinstance(value, dict): raise SDKException('Subclient', '101') self._advanced_option_restore_json = { "disks": value.get("disks", []), "guid": value.get("guid", ""), "newGuid": value.get("new_guid", ""), "newName": value.get("new_name", ""), "esxHost": value.get("esx_host", ""), "projectId": value.get("project_id", ""), "cluster": value.get("cluster", ""), "name": value.get("name", ""), "nics": value.get("nics", []), "vmIPAddressOptions": value.get("vm_ip_address_options", []), "FolderPath": value.get("FolderPath", ""), "resourcePoolPath": value.get("ResourcePool", ""), "volumeType": value.get("volumeType", "Auto"), "endUserVMRestore": value.get("end_user_vm_restore", False) # Required by Kubernetes Disk Level Restore } value_dict = { "createPublicIP": ["createPublicIP", ["createPublicIP", ""]], "restoreAsManagedVM": ["restoreAsManagedVM", ["restoreAsManagedVM", ""]], "destination_os_name": ["osName", ["destination_os_name", "AUTO"]], "resourcePoolPath": ["resourcePoolPath", ["resourcePoolPath", ""]], "datacenter": ["datacenter", ["datacenter", ""]], "terminationProtected": ["terminationProtected", ["terminationProtected", False]], "securityGroups": ["securityGroups", ["securityGroups", ""]], "keyPairList": ["keyPairList", ["keyPairList", ""]] } for key in value_dict: if key in value: inner_key = value_dict[key][0] val1, val2 = value_dict[key][1][0], value_dict[key][1][1] self._advanced_option_restore_json[inner_key] = value.get(val1, val2) if "vmSize" in value: val1, val2 = ("instanceSize", "") if not value["vmSize"] else ("vmSize", "vmSize") self._advanced_option_restore_json["vmSize"] = value.get(val1, val2) if "ami" in value and value["ami"] is not None: self._advanced_option_restore_json["templateId"] = value["ami"]["templateId"] self._advanced_option_restore_json["templateName"] = value["ami"]["templateName"] if "iamRole" in value and value["iamRole"] is not None: self._advanced_option_restore_json["roleInfo"] = { "name": value["iamRole"] } if self._instance_object.instance_name == 'openstack': if "securityGroups" in value and value["securityGroups"] is not None: self._advanced_option_restore_json["securityGroups"] = [{"groupName": value["securityGroups"]}] if "destComputerName" in value and value["destComputerName"] is not None: self._advanced_option_restore_json["destComputerName"] = value["destComputerName"] if "destComputerUserName" in value and value["destComputerUserName"] is not None: self._advanced_option_restore_json["destComputerUserName"] = value["destComputerUserName"] if "instanceAdminPassword" in value and value["instanceAdminPassword"] is not None: self._advanced_option_restore_json["instanceAdminPassword"] = value["instanceAdminPassword"] if self.disk_pattern.datastore.value == "DestinationPath": self._advanced_option_restore_json["DestinationPath"] = value.get("datastore", "") else: self._advanced_option_restore_json["Datastore"] = value.get("datastore", "") if "datacenter" in value: # Required for Kubernetes Disk Level Restore self._advanced_option_restore_json['datacenter'] = value["datacenter"] if value.get('block_level'): self._advanced_option_restore_json["blrRecoveryOpts"] = \ self._json_restore_blrRecoveryOpts(value) temp_dict = copy.deepcopy(self._advanced_option_restore_json) return temp_dict def _get_app_pvc(self, application_id): """Get the dictionary of PVCs in the applications with storage class info Args: application_id (str) -- Application GUID to get PVC Returns: List of dicts with PVC information """ app_disks, disk_metadata = self.browse('\\' + application_id) pvc_path_list = [disk for disk in app_disks if disk.split('.')[-1] != 'yaml'] pvc_list = [] for pvc in pvc_path_list: temp_dict = {} pvc_metadata = disk_metadata[pvc] vs_metadata = pvc_metadata['advanced_data']['browseMetaData']['virtualServerMetaData'] temp_dict['name'] = pvc_metadata['name'] temp_dict['storageclass'] = vs_metadata['datastore'] pvc_list.append(temp_dict) return pvc_list def set_advanced_vm_restore_options(self, vm_to_restore, restore_option): """ set the advanced restore options for all vm in restore param vm_to_restore - Name of the Application to restore restore_option - restore options that need to be set for advanced restore option power_on - power on the Application after restore add_to_failover - Register the Application to Failover Cluster datastore - Datastore where the Application needs to be restored disks (list of dict) - list with dict for each disk in Application eg: [{ name:"pvc-1" datastore:"storageclass-1" } { name:"pvc-2" datastore:"storageclass-2" } ] guid - GUID of the Application needs to be restored new_name - New name for the Application to be restored esx_host - client name where it need to be restored name - name of the Application to be restored """ # Set the new name for the restored Application. # If new_name is not given, it restores the Application with same name # with suffix Delete. vm_names, vm_ids = self._get_vm_ids_and_names_dict_from_browse() browse_result = self.vm_files_browse() application_id = vm_ids[vm_to_restore] # vs metadata from browse result _metadata = browse_result[1][('\\' + vm_to_restore)] vs_metadata = _metadata["advanced_data"]["browseMetaData"]["virtualServerMetaData"] if restore_option['in_place']: folder_path = vs_metadata.get("inventoryPath", '') instance_size = vs_metadata.get("instanceSize", '') else: folder_path = '' instance_size = '' if restore_option.get('resourcePoolPath'): restore_option['resourcePoolPath'] = vs_metadata['resourcePoolPath'] if restore_option.get('datacenter'): restore_option['datacenter'] = restore_option.get('datacenter') if restore_option.get('terminationProtected'): restore_option['terminationProtected'] = vs_metadata.get('terminationProtected', '') if restore_option.get('iamRole'): restore_option['iamRole'] = vs_metadata.get('role', '') if restore_option.get('securityGroups'): _security_groups = self._find_security_groups(vs_metadata['networkSecurityGroups']) restore_option['securityGroups'] = _security_groups if restore_option.get('keyPairList'): _keypair_list = self._find_keypair_list(vs_metadata['loginKeyPairs']) restore_option['keyPairList'] = _keypair_list # populate restore source item restore_option['paths'].append("\\" + application_id) restore_option['name'] = vm_to_restore restore_option['guid'] = application_id restore_option["FolderPath"] = folder_path restore_option["ResourcePool"] = "/" # populate restore disk and datastore vm_disks = [] new_name = vm_to_restore storage_class_map = restore_option.get('storage_class_map', None) # To populate disk list for each app in case of namespace restore if storage_class_map: pvc_list = self._get_app_pvc(application_id) for pvc in pvc_list: storageclass_name = pvc['storageclass'] pvc_name = pvc['name'] # If 'datastore' is passed then it's full app restore, else # it is namespace level restore. # Namespace level restore can be passed with storage class mapping if storageclass_name in storage_class_map: storageclass_name = storage_class_map[storageclass_name] _disk_dict = self._disk_dict_pattern(pvc_name, storageclass_name, pvc_name) vm_disks.append(_disk_dict) restore_option["disks"] = vm_disks self._set_restore_inputs( restore_option, esx_host=restore_option.get('esx_host') or vs_metadata['esxHost'], instance_size=restore_option.get('instanceSize', instance_size), new_name=restore_option.get('new_name', "Delete" + vm_to_restore) ) temp_dict = self._json_restore_advancedRestoreOptions(restore_option) self._advanced_restore_option_list.append(temp_dict) def full_app_restore_in_place( self, apps_to_restore=None, overwrite=True, copy_precedence=0, proxy_client=None): """Restores the FULL Application specified in the input list to the location same as the actual location of the Application in Kubernetes cluster. Args: apps_to_restore (list) -- List of applications to restore overwrite (bool) -- overwrite the existing Applications if exists default: True copy_precedence (int) -- copy precedence value default: 0 proxy_client (str) -- proxy client to be used for restore default: proxy added in application group/cluster Returns: object - instance of the Job class for this restore job Raises: SDKException: if inputs are not of correct type as per definition if failed to initialize job if response is empty if response is not success """ restore_option = {} # check input parameters are correct if apps_to_restore and not isinstance(apps_to_restore, list): raise SDKException('Subclient', '101') if copy_precedence: restore_option['copy_precedence_applicable'] = True if proxy_client is not None: restore_option['client'] = proxy_client kubernetes_host = self._client_object.client_name # set attr for all the option in restore xml from user inputs self._set_restore_inputs( restore_option, vm_to_restore=self._set_vm_to_restore(apps_to_restore), in_place=True, esx_host=kubernetes_host, volume_level_restore=1, unconditional_overwrite=overwrite, copy_precedence=copy_precedence ) request_json = self._prepare_kubernetes_inplace_restore_json(restore_option) return self._process_restore_response(request_json) def _prepare_kubernetes_inplace_restore_json(self, restore_option): """ Prepare Full Application restore in-place Json with all getters Args: restore_option - dictionary with all Application restore options value: restore_option: preserve_level (bool) - set the preserve level in restore unconditional_overwrite (bool) - unconditionally overwrite the disk in the restore path destination_path (str)- path where the disk needs to be restored client_name (str) - client where the disk needs to be restored destination_vendor (str) - vendor id of the Hypervisor destination_disktype (str) - type of disk needs to be restored like VHDX,VHD,VMDK source_item (str)- GUID of Application from which disk needs to be restored eg: \\5F9FA60C-0A89-4BD9-9D02-C5ACB42745EA copy_precedence_applicable (bool)- True if needs copy_precedence to be honoured else False copy_precedence (int) - the copy id from which browse and restore needs to be performed datastore (str) - Storage class which the Application PVC needs to be restored with disks (list of dict) - list with dict for each disk in Application eg: [{ name:"pvc-1" datastore:"storageclass-1" } { name:"pvc-2" datastore:"storageclass-2" } ] guid (str) - GUID of the Application needs to be restored new_name (str) - New name for the Application to be restored esx_host (str) - client name where Application need to be restored name (str) - name of the Application to be restored returns: request_json -complete json for perfomring Full VM Restore options """ if restore_option is None: restore_option = {} restore_option['paths'] = [] if "destination_vendor" not in restore_option: restore_option["destination_vendor"] = \ self._backupset_object._instance_object._vendor_id if restore_option.get('copy_precedence'): restore_option['copy_precedence_applicable'] = True # set all the restore defaults self._set_restore_defaults(restore_option) # set the setters self._backupset_object._instance_object._restore_association = self._subClientEntity self._json_restore_virtualServerRstOption(restore_option) self._json_restore_diskLevelVMRestoreOption(restore_option) self._json_vcenter_instance(restore_option) for _each_vm_to_restore in restore_option['vm_to_restore']: if not restore_option["in_place"]: if 'disk_type' in restore_option: restore_option['restoreAsManagedVM'] = restore_option['disk_type'][_each_vm_to_restore] if ("restore_new_name" in restore_option and restore_option["restore_new_name"] is not None): restore_option["new_name"] = restore_option["restore_new_name"] + _each_vm_to_restore else: restore_option["new_name"] = "del" + _each_vm_to_restore else: restore_option["new_name"] = _each_vm_to_restore self.set_advanced_vm_restore_options(_each_vm_to_restore, restore_option) # prepare json request_json = self._restore_json(restore_option=restore_option) self._virtualserver_option_restore_json["diskLevelVMRestoreOption"][ "advancedRestoreOptions"] = self._advanced_restore_option_list self._advanced_restore_option_list = [] request_json["taskInfo"]["subTasks"][0]["options"]["restoreOptions"][ "virtualServerRstOption"] = self._virtualserver_option_restore_json request_json["taskInfo"]["subTasks"][0]["options"]["restoreOptions"][ "volumeRstOption"] = self._json_restore_volumeRstOption( restore_option) return request_json def disk_restore(self, application_name, destination_path, disk_name=None, proxy_client=None, **kwargs): """Restores the disk specified in the input paths list to the same location Args: application_name (str) -- Name of the Application added in subclient content whose disk is selected for restore destination_path (str) -- Staging (destination) path to restore the disk. disk_name (list) -- name of the disk which has to be restored (only yaml files permitted - enter full name of the disk) default: None proxy_client (str) -- Destination proxy client to be used default: None Kwargs: Allows parameters to modify disk restore -- copy_precedence (int) -- SP copy precedence from which browse has to media_agent (str) -- MA needs to use for disk browse default :Storage policy MA snap_proxy (str) -- proxy need to be used for disk restores from snap default :proxy in instance or subclient disk_extension (str) -- Extension of disk file (Default: '.yaml') Returns: object - instance of the Job class for this restore job Raises: SDKException: if inputs are not passed in proper expected format if response is empty if response is not success """ vm_names, vm_ids = self._get_vm_ids_and_names_dict_from_browse() _disk_restore_option = {} copy_precedence = kwargs.get("copy_precedence", 0) disk_extn = kwargs.get("disk_extension", '.yaml') unconditional_overwrite = kwargs.get('unconditional_overwrite', False) show_deleted_files = kwargs.get('show_deleted_files', False) # Volume level restore values - # 4 - Manifest Restore volume_level_restore = kwargs.get("volume_level_restore", 4) if not disk_name: disk_name = [] else: disk_extn = self._get_disk_extension(disk_name) # check if inputs are correct if not (isinstance(application_name, str) and isinstance(destination_path, str) and isinstance(disk_name, list)): raise SDKException('Subclient', '101') if copy_precedence: _disk_restore_option['copy_precedence_applicable'] = True # fetching all disks from the vm disk_list, disk_info_dict = self.disk_level_browse( "\\" + vm_ids[application_name]) # Filter out disks with specified extension from disk list disk_list = list(filter(lambda name: self._get_disk_extension([name]) == disk_extn, disk_list)) disk_info_dict = { disk : disk_info_dict[disk] for disk in disk_list } if not disk_name: # if disk names are not provided, restore all disks for each_disk_path in disk_list: disk_name.append(each_disk_path.split('\\')[-1]) else: # else, check if the given application has a disk with the list of disks in disk_name. for each_disk in disk_name: # disk path has GUID in case of files, and application name in case of manifests each_disk_path = "\\" + \ (vm_ids[application_name] if volume_level_restore != 4 else application_name) + \ "\\" + each_disk.split("\\")[-1] if each_disk_path not in disk_list: raise SDKException('Subclient', '111') _disk_restore_option["destination_vendor"] = \ self._backupset_object._instance_object._vendor_id if proxy_client is not None: _disk_restore_option['client'] = proxy_client else: _disk_restore_option['client'] = self._backupset_object._instance_object.co_ordinator # set Source item List src_item_list = [] for each_disk in disk_name: src_item_list.append("\\" + vm_ids[application_name] + "\\" + each_disk.split("\\")[-1]) _disk_restore_option['paths'] = src_item_list _disk_restore_option['unconditional_overwrite'] = unconditional_overwrite _disk_restore_option['show_deleted_files'] = show_deleted_files # Populate volume level restore options _disk_restore_option['volume_level_restore'] = volume_level_restore self._set_restore_inputs( _disk_restore_option, in_place=False, copy_precedence=copy_precedence, destination_path=destination_path, paths=src_item_list ) request_json = self._prepare_disk_restore_json(_disk_restore_option) return self._process_restore_response(request_json) def enable_intelli_snap(self, snap_engine_name=None, proxy_options=None, snapshot_engine_id =None): """Enables Intelli Snap for the subclient. Args: snap_engine_name (str) -- Snap Engine Name proxy_options (str) -- to set proxy for Kubernetes snapshot_engine_id (int) -- Snapshot engine id Raises: SDKException: if failed to enable intelli snap for subclient """ if snapshot_engine_id is None: snapshot_engine_id = 82 properties_dict = { "isSnapBackupEnabled": True, "snapToTapeSelectedEngine": { "snapShotEngineId": snapshot_engine_id, "snapShotEngineName": snap_engine_name } } if proxy_options is not None: if "snap_proxy" in proxy_options: properties_dict["snapToTapeProxyToUse"] = { "clientName": proxy_options["snap_proxy"] } if "backupcopy_proxy" in proxy_options: properties_dict["useSeparateProxyForSnapToTape"] = True properties_dict["separateProxyForSnapToTape"] = { "clientName": proxy_options["backupcopy_proxy"] } if "use_source_if_proxy_unreachable" in proxy_options: properties_dict["snapToTapeProxyToUseSource"] = True self._set_subclient_properties( "_commonProperties['snapCopyInfo']", properties_dict) def guest_file_restore(self, application_name, destination_path, volume_level_restore, disk_name=None, proxy_client=None, restore_list=None, restore_pvc_guid=None, **kwargs): """perform Guest file restore of the provided path Args: application_name_name (str) -- Name of the source application destination_path (str) -- Path at the destination to restore at volume_level_restore (str) -- Flag to denote volume_level_restore Accepted values - 6 for restore to PVC 7 for FS Destination restore disk_name (str) -- Name of the source PVC proxy_client (str) -- Access node for restore restore_list (str) -- List of files or folders to restore. Contains Full path of files or folders relative to PVC mount point. Eg. if /tmp is the mount point with files or folder /tmp/folder1/file1, restore list should have format 'folder1/file1' restore_pvc_guid (str) -- strGUID of the target PVC Kwargs: copy_precedence (int) -- To set copy precedence for restore disk_extension (str) -- Extention of the disk unconditional_overwrite (int) -- To set unconditional overwrite for restore show_deleted_files (bool) -- Whether to show deleted files in browse in_place (bool) -- If restore job is inplace Raises: SDK Exception if -inputs are not of correct type as per definition -invalid volume_level_restore passed """ vm_names, vm_ids = self._get_vm_ids_and_names_dict_from_browse() _guest_file_rst_options = {} _advanced_restore_options = {} copy_precedence = kwargs.get("copy_precedence", 0) disk_extn = kwargs.get("disk_extension", '') overwrite = kwargs.get("unconditional_overwrite", 1) unconditional_overwrite = kwargs.get('unconditional_overwrite', False) show_deleted_files = kwargs.get('show_deleted_files', False) in_place = kwargs.get('in_place', False) # check if inputs are correct if not (isinstance(application_name, str) and isinstance(destination_path, str) and isinstance(disk_name, str)): raise SDKException('Subclient', '101') if volume_level_restore not in [6, 7]: raise SDKException("Subclient", "102", "Invalid volume level restore type passed") if copy_precedence: _guest_file_rst_options['copy_precedence_applicable'] = True # fetching all disks from the application disk_list, disk_info_dict = self.disk_level_browse( "\\" + vm_ids[application_name]) # Filter out disks with specified extension from disk list disk_list = list(filter(lambda name: self._get_disk_extension([name]) == disk_extn, disk_list)) disk_info_dict = {disk: disk_info_dict[disk] for disk in disk_list} _guest_file_rst_options["destination_vendor"] = \ self._backupset_object._instance_object._vendor_id if proxy_client is not None: _guest_file_rst_options['client'] = proxy_client else: _guest_file_rst_options['client'] = self._backupset_object._instance_object.co_ordinator # set Source item List src_item_list = [] for each_item in restore_list: item = "\\".join(each_item.split('/')) src_item_list.append( "\\" + vm_ids[application_name] + "\\" + disk_name + "\\" + item) _guest_file_rst_options['paths'] = src_item_list if volume_level_restore == 6: if in_place: restore_pvc_guid = "{}`PersistentVolumeClaim`{}".format( vm_ids[application_name].split('`')[0], disk_name ) new_name = disk_name else: new_name = restore_pvc_guid.split('`')[-2] _advanced_restore_options['datacenter'] = "none" new_guid = restore_pvc_guid else: new_guid = "{}`PersistentVolumeClaim`{}".format( vm_ids[application_name].split('`')[0], disk_name ) new_name = disk_name _guest_file_rst_options['in_place'] = in_place _guest_file_rst_options['volume_level_restore'] = volume_level_restore _guest_file_rst_options['unconditional_overwrite'] = unconditional_overwrite _guest_file_rst_options['show_deleted_files'] = show_deleted_files _advanced_restore_options['new_guid'] = new_guid _advanced_restore_options['new_name'] = new_name _advanced_restore_options['name'] = disk_name _advanced_restore_options['guid'] = vm_ids[application_name] _advanced_restore_options['end_user_vm_restore'] = True # set advanced restore options disks _disk_dict = self._disk_dict_pattern(disk_name, "") _advanced_restore_options['disks'] = [_disk_dict] advanced_options_dict = self._json_restore_advancedRestoreOptions(_advanced_restore_options) self._advanced_restore_option_list.append(advanced_options_dict) self._set_restore_inputs( _guest_file_rst_options, in_place=False, copy_precedence=copy_precedence, destination_path=destination_path, paths=src_item_list ) request_json = self._prepare_disk_restore_json(_guest_file_rst_options) # Populate the advancedRestoreOptions section self._virtualserver_option_restore_json["diskLevelVMRestoreOption"][ "advancedRestoreOptions"] = self._advanced_restore_option_list self._advanced_restore_option_list = [] return self._process_restore_response(request_json) def guest_files_browse( self, application_path='\\', show_deleted_files=False, restore_index=True, from_date=0, to_date=0, copy_precedence=0, media_agent=""): """Browses the Files and Folders inside a Virtual Machine in the time range specified. Args: application_path (str) -- folder path to get the contents of default: '\\'; returns the root of the Backup content show_deleted_files (bool) -- include deleted files in the content or not default: False restore_index (bool) -- restore index if it is not cached default: True from_date (int) -- date to get the contents after format: dd/MM/YYYY gets contents from 01/01/1970 if not specified default: 0 to_date (int) -- date to get the contents before format: dd/MM/YYYY gets contents till current day if not specified default: 0 copy_precedence (int) -- copy precedence to be used for browsing media_agent (str) -- Browse MA via with Browse has to happen. It can be MA different than Storage Policy MA Returns: list - list of all folders or files with their full paths inside the input path dict - path along with the details like name, file/folder, size, modification time Raises: SDKException: if from date value is incorrect if to date value is incorrect if to date is less than from date if failed to browse content if response is empty if response is not success """ return self.browse_in_time( vm_path, show_deleted_files, restore_index, False, from_date, to_date, copy_precedence, vm_files_browse=False, media_agent=media_agent) def _get_apps_in_namespace(self, namespaces): """Get the list of applications to be restored with the namespace level restore Args: namespaces (list) - List of namespaces Returns: list of applictations to be restored with namespaces """ apps_to_restore = [] namespace_app_dict = {} app, app_dict = self.browse() for app_path, metadata in app_dict.items(): app_name = metadata['name'] app_id = metadata['snap_display_name'] app_ns = app_id.split('`')[0] app_type = app_id.split('`')[1] if app_type != 'Namespace' and app_ns in namespaces: apps_to_restore.append(app_name) namespace_app_dict[app_name] = app_ns return apps_to_restore, namespace_app_dict def namespace_restore_out_of_place( self, namespace_to_restore, target_namespace_name={}, target_cluster_name=None, storage_class_map=None, overwrite=True, copy_precedence=0, proxy_client=None ): """Perform a namespace-level restore out-of-place Args: namespace_to_restore (list) -- List of namespaces to restore target_namespace_name (dict) -- Target namespace name to perform restore at Eg. {'namespace1': 'namespace1-rst'} target_cluster_name (str) -- Name of the target cluster to restore at storage_class_map (dict) -- Mapping of storage classes for transformation Eg. {'rook-ceph-block' : 'azurefile'} overwrite (bool) -- Overwrite the existing namespace Default: true copy_precedence (int) -- Copy preceedence value proxy_client (str) -- Name of the proxy client to launch restore Default : None (Automatic) Returns: object - instance of the Job class for this restore job Raises: SDKException: if inputs are not of correct type as per definition if failed to initialize job if response is empty if response is not success """ restore_options = {} # Check mandatory input parameters are correct if namespace_to_restore and not type(namespace_to_restore) is list: raise SDKException('Subclient', '101') # Populating proxy client. It automatically fetches proxy controller from subclient/instance level # property if not specified if proxy_client is not None: restore_options['client'] = proxy_client restore_new_name = {} apps_to_restore = [] if not type(target_namespace_name) is dict: raise SDKException('Subclient', '101') for ns in namespace_to_restore: if ns not in target_namespace_name: target_namespace_name[ns] = ns restore_new_name.update(target_namespace_name) namespace_apps, namespace_app_map = self._get_apps_in_namespace(namespace_to_restore) apps_to_restore.extend(namespace_apps) for app in apps_to_restore: restore_new_name[app] = app apps_to_restore.extend(namespace_to_restore) restore_options['restore_new_name'] = restore_new_name restore_options['namespace_app_map'] = namespace_app_map if not target_cluster_name: target_cluster_name = self._client_object.client_name self._set_restore_inputs( restore_options, in_place=False, vcenter_client=target_cluster_name, esx_host=target_cluster_name, esx_server=None, unconditional_overwrite=overwrite, power_on=True, vm_to_restore=self._set_vm_to_restore(apps_to_restore), disk_option=self._disk_option['Original'], transport_mode=self._transport_mode['Auto'], copy_precedence=copy_precedence, volume_level_restore=1, source_item=[], source_ip=None, destination_ip=None, network=None, storage_class_map=storage_class_map ) request_json = self._prepare_kubernetes_restore_json(restore_options) return self._process_restore_response(request_json) def namespace_restore_in_place( self, namespace_to_restore, overwrite=True, copy_precedence=0, proxy_client=None ): """Perform a namespace-level restore in-place Args: namespace_to_restore (list) -- List of namespaces to restore overwrite (bool) -- Overwrite the existing namespace Default: true copy_precedence (int) -- Copy preceedence value proxy_client (str) -- Name of the proxy client to launch restore Default : None (Automatic) Returns: object - instance of the Job class for this restore job Raises: SDKException: if inputs are not of correct type as per definition if failed to initialize job if response is empty if response is not success """ restore_options = {} # Check mandatory input parameters are correct if namespace_to_restore and not type(namespace_to_restore) is list: raise SDKException('Subclient', '101') # Populating proxy client. It automatically fetches proxy controller from subclient/instance level # property if not specified if proxy_client is not None: restore_options['client'] = proxy_client apps_to_restore = [] namespace_apps, namespace_app_map = self._get_apps_in_namespace(namespace_to_restore) apps_to_restore.extend(namespace_apps) apps_to_restore.extend(namespace_to_restore) client_name = self._client_object.client_name self._set_restore_inputs( restore_options, vm_to_restore=self._set_vm_to_restore(apps_to_restore), in_place=True, esx_host=client_name, esx_server_name="", volume_level_restore=1, unconditional_overwrite=overwrite, disk_option=self._disk_option['Original'], transport_mode=self._transport_mode['Auto'], copy_precedence=copy_precedence ) request_json = self._prepare_kubernetes_inplace_restore_json(restore_options) return self._process_restore_response(request_json)
Ancestors
Methods
def disk_restore(self, application_name, destination_path, disk_name=None, proxy_client=None, **kwargs)
-
Restores the disk specified in the input paths list to the same location
Args
application_name (str) – Name of the Application added in subclient content whose disk is selected for restore
destination_path (str) – Staging (destination) path to restore the disk.
disk_name (list) – name of the disk which has to be restored (only yaml files permitted - enter full name of the disk) default: None proxy_client (str) – Destination proxy client to be used default: None
Kwargs
Allows parameters to modify disk restore –
copy_precedence (int) – SP copy precedence from which browse has to
media_agent (str) – MA needs to use for disk browse default :Storage policy MA
snap_proxy (str) – proxy need to be used for disk restores from snap default :proxy in instance or subclient
disk_extension (str) – Extension of disk file (Default: '.yaml')
Returns
object - instance of the Job class for this restore job
Raises
SDKException: if inputs are not passed in proper expected format
if response is empty if response is not success
Expand source code Browse git
def disk_restore(self, application_name, destination_path, disk_name=None, proxy_client=None, **kwargs): """Restores the disk specified in the input paths list to the same location Args: application_name (str) -- Name of the Application added in subclient content whose disk is selected for restore destination_path (str) -- Staging (destination) path to restore the disk. disk_name (list) -- name of the disk which has to be restored (only yaml files permitted - enter full name of the disk) default: None proxy_client (str) -- Destination proxy client to be used default: None Kwargs: Allows parameters to modify disk restore -- copy_precedence (int) -- SP copy precedence from which browse has to media_agent (str) -- MA needs to use for disk browse default :Storage policy MA snap_proxy (str) -- proxy need to be used for disk restores from snap default :proxy in instance or subclient disk_extension (str) -- Extension of disk file (Default: '.yaml') Returns: object - instance of the Job class for this restore job Raises: SDKException: if inputs are not passed in proper expected format if response is empty if response is not success """ vm_names, vm_ids = self._get_vm_ids_and_names_dict_from_browse() _disk_restore_option = {} copy_precedence = kwargs.get("copy_precedence", 0) disk_extn = kwargs.get("disk_extension", '.yaml') unconditional_overwrite = kwargs.get('unconditional_overwrite', False) show_deleted_files = kwargs.get('show_deleted_files', False) # Volume level restore values - # 4 - Manifest Restore volume_level_restore = kwargs.get("volume_level_restore", 4) if not disk_name: disk_name = [] else: disk_extn = self._get_disk_extension(disk_name) # check if inputs are correct if not (isinstance(application_name, str) and isinstance(destination_path, str) and isinstance(disk_name, list)): raise SDKException('Subclient', '101') if copy_precedence: _disk_restore_option['copy_precedence_applicable'] = True # fetching all disks from the vm disk_list, disk_info_dict = self.disk_level_browse( "\\" + vm_ids[application_name]) # Filter out disks with specified extension from disk list disk_list = list(filter(lambda name: self._get_disk_extension([name]) == disk_extn, disk_list)) disk_info_dict = { disk : disk_info_dict[disk] for disk in disk_list } if not disk_name: # if disk names are not provided, restore all disks for each_disk_path in disk_list: disk_name.append(each_disk_path.split('\\')[-1]) else: # else, check if the given application has a disk with the list of disks in disk_name. for each_disk in disk_name: # disk path has GUID in case of files, and application name in case of manifests each_disk_path = "\\" + \ (vm_ids[application_name] if volume_level_restore != 4 else application_name) + \ "\\" + each_disk.split("\\")[-1] if each_disk_path not in disk_list: raise SDKException('Subclient', '111') _disk_restore_option["destination_vendor"] = \ self._backupset_object._instance_object._vendor_id if proxy_client is not None: _disk_restore_option['client'] = proxy_client else: _disk_restore_option['client'] = self._backupset_object._instance_object.co_ordinator # set Source item List src_item_list = [] for each_disk in disk_name: src_item_list.append("\\" + vm_ids[application_name] + "\\" + each_disk.split("\\")[-1]) _disk_restore_option['paths'] = src_item_list _disk_restore_option['unconditional_overwrite'] = unconditional_overwrite _disk_restore_option['show_deleted_files'] = show_deleted_files # Populate volume level restore options _disk_restore_option['volume_level_restore'] = volume_level_restore self._set_restore_inputs( _disk_restore_option, in_place=False, copy_precedence=copy_precedence, destination_path=destination_path, paths=src_item_list ) request_json = self._prepare_disk_restore_json(_disk_restore_option) return self._process_restore_response(request_json)
def enable_intelli_snap(self, snap_engine_name=None, proxy_options=None, snapshot_engine_id=None)
-
Enables Intelli Snap for the subclient.
Args
snap_engine_name (str) – Snap Engine Name
proxy_options (str) – to set proxy for Kubernetes
snapshot_engine_id (int) – Snapshot engine id
Raises
SDKException: if failed to enable intelli snap for subclient
Expand source code Browse git
def enable_intelli_snap(self, snap_engine_name=None, proxy_options=None, snapshot_engine_id =None): """Enables Intelli Snap for the subclient. Args: snap_engine_name (str) -- Snap Engine Name proxy_options (str) -- to set proxy for Kubernetes snapshot_engine_id (int) -- Snapshot engine id Raises: SDKException: if failed to enable intelli snap for subclient """ if snapshot_engine_id is None: snapshot_engine_id = 82 properties_dict = { "isSnapBackupEnabled": True, "snapToTapeSelectedEngine": { "snapShotEngineId": snapshot_engine_id, "snapShotEngineName": snap_engine_name } } if proxy_options is not None: if "snap_proxy" in proxy_options: properties_dict["snapToTapeProxyToUse"] = { "clientName": proxy_options["snap_proxy"] } if "backupcopy_proxy" in proxy_options: properties_dict["useSeparateProxyForSnapToTape"] = True properties_dict["separateProxyForSnapToTape"] = { "clientName": proxy_options["backupcopy_proxy"] } if "use_source_if_proxy_unreachable" in proxy_options: properties_dict["snapToTapeProxyToUseSource"] = True self._set_subclient_properties( "_commonProperties['snapCopyInfo']", properties_dict)
def full_app_restore_in_place(self, apps_to_restore=None, overwrite=True, copy_precedence=0, proxy_client=None)
-
Restores the FULL Application specified in the input list to the location same as the actual location of the Application in Kubernetes cluster.
Args
apps_to_restore (list) – List of applications to restore
overwrite (bool) – overwrite the existing Applications if exists default: True
copy_precedence (int) – copy precedence value default: 0
proxy_client (str) – proxy client to be used for restore default: proxy added in application group/cluster
Returns
object - instance of the Job class for this restore job
Raises
SDKException: if inputs are not of correct type as per definition
if failed to initialize job if response is empty if response is not success
Expand source code Browse git
def full_app_restore_in_place( self, apps_to_restore=None, overwrite=True, copy_precedence=0, proxy_client=None): """Restores the FULL Application specified in the input list to the location same as the actual location of the Application in Kubernetes cluster. Args: apps_to_restore (list) -- List of applications to restore overwrite (bool) -- overwrite the existing Applications if exists default: True copy_precedence (int) -- copy precedence value default: 0 proxy_client (str) -- proxy client to be used for restore default: proxy added in application group/cluster Returns: object - instance of the Job class for this restore job Raises: SDKException: if inputs are not of correct type as per definition if failed to initialize job if response is empty if response is not success """ restore_option = {} # check input parameters are correct if apps_to_restore and not isinstance(apps_to_restore, list): raise SDKException('Subclient', '101') if copy_precedence: restore_option['copy_precedence_applicable'] = True if proxy_client is not None: restore_option['client'] = proxy_client kubernetes_host = self._client_object.client_name # set attr for all the option in restore xml from user inputs self._set_restore_inputs( restore_option, vm_to_restore=self._set_vm_to_restore(apps_to_restore), in_place=True, esx_host=kubernetes_host, volume_level_restore=1, unconditional_overwrite=overwrite, copy_precedence=copy_precedence ) request_json = self._prepare_kubernetes_inplace_restore_json(restore_option) return self._process_restore_response(request_json)
def full_app_restore_out_of_place(self, apps_to_restore, restore_namespace, restored_app_name=None, kubernetes_client=None, storage_class=None, overwrite=True, copy_precedence=0, proxy_client=None)
-
Restores the FULL Application specified in the input list to the provided Kubernetes client at the specified namespace with storage class. If the provided client name is none then it restores the Full Application to the source Kubernetes client and corresponding namespace and storage class.
Args
apps_to_restore (list) – List of Applications that is to be restored
restored_app_name (dict) – Dictionary mapping new name of Applications
kubernetes_client (str) – Name of the Kubernetes client where the Application should be restored Restores to the source Kubernetes client if this value is not specified
storage_class (str) – Storage class for the PVC to be restored with. Uses source storage class if not specified.
restore_namespace (str) – Target namespace where Applications are to be restored
overwrite (bool) – overwrite the existing Applications if exists default: True
copy_precedence (int) – copy precedence value default: 0
proxy_client (str) – destination proxy client
Returns
object - instance of the Job class for this restore job
Raises
SDKException: if inputs are not of correct type as per definition
if failed to initialize job if response is empty if response is not success
Expand source code Browse git
def full_app_restore_out_of_place( self, apps_to_restore, restore_namespace, restored_app_name=None, kubernetes_client=None, storage_class=None, overwrite=True, copy_precedence=0, proxy_client=None, ): """Restores the FULL Application specified in the input list to the provided Kubernetes client at the specified namespace with storage class. If the provided client name is none then it restores the Full Application to the source Kubernetes client and corresponding namespace and storage class. Args: apps_to_restore (list) -- List of Applications that is to be restored restored_app_name (dict) -- Dictionary mapping new name of Applications kubernetes_client (str) -- Name of the Kubernetes client where the Application should be restored Restores to the source Kubernetes client if this value is not specified storage_class (str) -- Storage class for the PVC to be restored with. Uses source storage class if not specified. restore_namespace (str) -- Target namespace where Applications are to be restored overwrite (bool) -- overwrite the existing Applications if exists default: True copy_precedence (int) -- copy precedence value default: 0 proxy_client (str) -- destination proxy client Returns: object - instance of the Job class for this restore job Raises: SDKException: if inputs are not of correct type as per definition if failed to initialize job if response is empty if response is not success """ restore_option = {} # check mandatory input parameters are correct if apps_to_restore and not isinstance(apps_to_restore, list): raise SDKException('Subclient', '101') # populating proxy client. It assumes the proxy controller added in instance # properties if not specified if proxy_client is not None: restore_option['client'] = proxy_client if restored_app_name: if not(isinstance(apps_to_restore, list) or isinstance(restored_app_name, dict)): raise SDKException('Subclient', '101') restore_option['restore_new_name'] = restored_app_name if not kubernetes_client: kubernetes_client = self._client_object.client_name restore_option_copy = restore_option.copy() self._set_restore_inputs( restore_option, in_place=False, vcenter_client=kubernetes_client, datastore=storage_class, esx_host=kubernetes_client, datacenter=restore_namespace, unconditional_overwrite=overwrite, vm_to_restore=self._set_vm_to_restore(apps_to_restore), copy_precedence=copy_precedence, volume_level_restore=1, source_item=[] ) request_json = self._prepare_kubernetes_restore_json(restore_option) return self._process_restore_response(request_json)
def guest_file_restore(self, application_name, destination_path, volume_level_restore, disk_name=None, proxy_client=None, restore_list=None, restore_pvc_guid=None, **kwargs)
-
perform Guest file restore of the provided path
Args
application_name_name (str) – Name of the source application destination_path (str) – Path at the destination to restore at volume_level_restore (str) – Flag to denote volume_level_restore Accepted values - 6 for restore to PVC 7 for FS Destination restore disk_name (str) – Name of the source PVC proxy_client (str) – Access node for restore restore_list (str) – List of files or folders to restore. Contains Full path of files or folders relative to PVC mount point. Eg. if /tmp is the mount point with files or folder /tmp/folder1/file1, restore list should have format 'folder1/file1' restore_pvc_guid (str) – strGUID of the target PVC
Kwargs
copy_precedence (int) – To set copy precedence for restore disk_extension (str) – Extention of the disk unconditional_overwrite (int) – To set unconditional overwrite for restore show_deleted_files (bool) – Whether to show deleted files in browse in_place (bool) – If restore job is inplace
Raises
SDK Exception if -inputs are not of correct type as per definition
-invalid volume_level_restore passed
Expand source code Browse git
def guest_file_restore(self, application_name, destination_path, volume_level_restore, disk_name=None, proxy_client=None, restore_list=None, restore_pvc_guid=None, **kwargs): """perform Guest file restore of the provided path Args: application_name_name (str) -- Name of the source application destination_path (str) -- Path at the destination to restore at volume_level_restore (str) -- Flag to denote volume_level_restore Accepted values - 6 for restore to PVC 7 for FS Destination restore disk_name (str) -- Name of the source PVC proxy_client (str) -- Access node for restore restore_list (str) -- List of files or folders to restore. Contains Full path of files or folders relative to PVC mount point. Eg. if /tmp is the mount point with files or folder /tmp/folder1/file1, restore list should have format 'folder1/file1' restore_pvc_guid (str) -- strGUID of the target PVC Kwargs: copy_precedence (int) -- To set copy precedence for restore disk_extension (str) -- Extention of the disk unconditional_overwrite (int) -- To set unconditional overwrite for restore show_deleted_files (bool) -- Whether to show deleted files in browse in_place (bool) -- If restore job is inplace Raises: SDK Exception if -inputs are not of correct type as per definition -invalid volume_level_restore passed """ vm_names, vm_ids = self._get_vm_ids_and_names_dict_from_browse() _guest_file_rst_options = {} _advanced_restore_options = {} copy_precedence = kwargs.get("copy_precedence", 0) disk_extn = kwargs.get("disk_extension", '') overwrite = kwargs.get("unconditional_overwrite", 1) unconditional_overwrite = kwargs.get('unconditional_overwrite', False) show_deleted_files = kwargs.get('show_deleted_files', False) in_place = kwargs.get('in_place', False) # check if inputs are correct if not (isinstance(application_name, str) and isinstance(destination_path, str) and isinstance(disk_name, str)): raise SDKException('Subclient', '101') if volume_level_restore not in [6, 7]: raise SDKException("Subclient", "102", "Invalid volume level restore type passed") if copy_precedence: _guest_file_rst_options['copy_precedence_applicable'] = True # fetching all disks from the application disk_list, disk_info_dict = self.disk_level_browse( "\\" + vm_ids[application_name]) # Filter out disks with specified extension from disk list disk_list = list(filter(lambda name: self._get_disk_extension([name]) == disk_extn, disk_list)) disk_info_dict = {disk: disk_info_dict[disk] for disk in disk_list} _guest_file_rst_options["destination_vendor"] = \ self._backupset_object._instance_object._vendor_id if proxy_client is not None: _guest_file_rst_options['client'] = proxy_client else: _guest_file_rst_options['client'] = self._backupset_object._instance_object.co_ordinator # set Source item List src_item_list = [] for each_item in restore_list: item = "\\".join(each_item.split('/')) src_item_list.append( "\\" + vm_ids[application_name] + "\\" + disk_name + "\\" + item) _guest_file_rst_options['paths'] = src_item_list if volume_level_restore == 6: if in_place: restore_pvc_guid = "{}`PersistentVolumeClaim`{}".format( vm_ids[application_name].split('`')[0], disk_name ) new_name = disk_name else: new_name = restore_pvc_guid.split('`')[-2] _advanced_restore_options['datacenter'] = "none" new_guid = restore_pvc_guid else: new_guid = "{}`PersistentVolumeClaim`{}".format( vm_ids[application_name].split('`')[0], disk_name ) new_name = disk_name _guest_file_rst_options['in_place'] = in_place _guest_file_rst_options['volume_level_restore'] = volume_level_restore _guest_file_rst_options['unconditional_overwrite'] = unconditional_overwrite _guest_file_rst_options['show_deleted_files'] = show_deleted_files _advanced_restore_options['new_guid'] = new_guid _advanced_restore_options['new_name'] = new_name _advanced_restore_options['name'] = disk_name _advanced_restore_options['guid'] = vm_ids[application_name] _advanced_restore_options['end_user_vm_restore'] = True # set advanced restore options disks _disk_dict = self._disk_dict_pattern(disk_name, "") _advanced_restore_options['disks'] = [_disk_dict] advanced_options_dict = self._json_restore_advancedRestoreOptions(_advanced_restore_options) self._advanced_restore_option_list.append(advanced_options_dict) self._set_restore_inputs( _guest_file_rst_options, in_place=False, copy_precedence=copy_precedence, destination_path=destination_path, paths=src_item_list ) request_json = self._prepare_disk_restore_json(_guest_file_rst_options) # Populate the advancedRestoreOptions section self._virtualserver_option_restore_json["diskLevelVMRestoreOption"][ "advancedRestoreOptions"] = self._advanced_restore_option_list self._advanced_restore_option_list = [] return self._process_restore_response(request_json)
def guest_files_browse(self, application_path='\\', show_deleted_files=False, restore_index=True, from_date=0, to_date=0, copy_precedence=0, media_agent='')
-
Browses the Files and Folders inside a Virtual Machine in the time range specified.
Args: application_path (str) – folder path to get the contents of default: ''; returns the root of the Backup content
show_deleted_files (bool) -- include deleted files in the content or not default: False restore_index (bool) -- restore index if it is not cached default: True from_date (int) -- date to get the contents after format: dd/MM/YYYY gets contents from 01/01/1970 if not specified default: 0 to_date (int) -- date to get the contents before format: dd/MM/YYYY gets contents till current day if not specified default: 0 copy_precedence (int) -- copy precedence to be used for browsing media_agent (str) -- Browse MA via with Browse has to happen. It can be MA different than Storage Policy MA
Returns: list - list of all folders or files with their full paths inside the input path
dict - path along with the details like name, file/folder, size, modification time
Raises: SDKException: if from date value is incorrect
if to date value is incorrect if to date is less than from date if failed to browse content if response is empty if response is not success
Expand source code Browse git
def guest_files_browse( self, application_path='\\', show_deleted_files=False, restore_index=True, from_date=0, to_date=0, copy_precedence=0, media_agent=""): """Browses the Files and Folders inside a Virtual Machine in the time range specified. Args: application_path (str) -- folder path to get the contents of default: '\\'; returns the root of the Backup content show_deleted_files (bool) -- include deleted files in the content or not default: False restore_index (bool) -- restore index if it is not cached default: True from_date (int) -- date to get the contents after format: dd/MM/YYYY gets contents from 01/01/1970 if not specified default: 0 to_date (int) -- date to get the contents before format: dd/MM/YYYY gets contents till current day if not specified default: 0 copy_precedence (int) -- copy precedence to be used for browsing media_agent (str) -- Browse MA via with Browse has to happen. It can be MA different than Storage Policy MA Returns: list - list of all folders or files with their full paths inside the input path dict - path along with the details like name, file/folder, size, modification time Raises: SDKException: if from date value is incorrect if to date value is incorrect if to date is less than from date if failed to browse content if response is empty if response is not success """ return self.browse_in_time( vm_path, show_deleted_files, restore_index, False, from_date, to_date, copy_precedence, vm_files_browse=False, media_agent=media_agent)
def namespace_restore_in_place(self, namespace_to_restore, overwrite=True, copy_precedence=0, proxy_client=None)
-
Perform a namespace-level restore in-place
Args
namespace_to_restore (list) – List of namespaces to restore
overwrite (bool) – Overwrite the existing namespace Default: true
copy_precedence (int) – Copy preceedence value
proxy_client (str) – Name of the proxy client to launch restore Default : None (Automatic)
Returns
object - instance of the Job class for this restore job
Raises
SDKException:
if inputs are not of correct type as per definition if failed to initialize job if response is empty if response is not success
Expand source code Browse git
def namespace_restore_in_place( self, namespace_to_restore, overwrite=True, copy_precedence=0, proxy_client=None ): """Perform a namespace-level restore in-place Args: namespace_to_restore (list) -- List of namespaces to restore overwrite (bool) -- Overwrite the existing namespace Default: true copy_precedence (int) -- Copy preceedence value proxy_client (str) -- Name of the proxy client to launch restore Default : None (Automatic) Returns: object - instance of the Job class for this restore job Raises: SDKException: if inputs are not of correct type as per definition if failed to initialize job if response is empty if response is not success """ restore_options = {} # Check mandatory input parameters are correct if namespace_to_restore and not type(namespace_to_restore) is list: raise SDKException('Subclient', '101') # Populating proxy client. It automatically fetches proxy controller from subclient/instance level # property if not specified if proxy_client is not None: restore_options['client'] = proxy_client apps_to_restore = [] namespace_apps, namespace_app_map = self._get_apps_in_namespace(namespace_to_restore) apps_to_restore.extend(namespace_apps) apps_to_restore.extend(namespace_to_restore) client_name = self._client_object.client_name self._set_restore_inputs( restore_options, vm_to_restore=self._set_vm_to_restore(apps_to_restore), in_place=True, esx_host=client_name, esx_server_name="", volume_level_restore=1, unconditional_overwrite=overwrite, disk_option=self._disk_option['Original'], transport_mode=self._transport_mode['Auto'], copy_precedence=copy_precedence ) request_json = self._prepare_kubernetes_inplace_restore_json(restore_options) return self._process_restore_response(request_json)
def namespace_restore_out_of_place(self, namespace_to_restore, target_namespace_name={}, target_cluster_name=None, storage_class_map=None, overwrite=True, copy_precedence=0, proxy_client=None)
-
Perform a namespace-level restore out-of-place
Args
namespace_to_restore (list) – List of namespaces to restore
target_namespace_name (dict) – Target namespace name to perform restore at Eg. {'namespace1': 'namespace1-rst'}
target_cluster_name (str) – Name of the target cluster to restore at
storage_class_map (dict) – Mapping of storage classes for transformation Eg. {'rook-ceph-block' : 'azurefile'}
overwrite (bool) – Overwrite the existing namespace Default: true
copy_precedence (int) – Copy preceedence value
proxy_client (str) – Name of the proxy client to launch restore Default : None (Automatic)
Returns
object - instance of the Job class for this restore job
Raises
SDKException:
if inputs are not of correct type as per definition if failed to initialize job if response is empty if response is not success
Expand source code Browse git
def namespace_restore_out_of_place( self, namespace_to_restore, target_namespace_name={}, target_cluster_name=None, storage_class_map=None, overwrite=True, copy_precedence=0, proxy_client=None ): """Perform a namespace-level restore out-of-place Args: namespace_to_restore (list) -- List of namespaces to restore target_namespace_name (dict) -- Target namespace name to perform restore at Eg. {'namespace1': 'namespace1-rst'} target_cluster_name (str) -- Name of the target cluster to restore at storage_class_map (dict) -- Mapping of storage classes for transformation Eg. {'rook-ceph-block' : 'azurefile'} overwrite (bool) -- Overwrite the existing namespace Default: true copy_precedence (int) -- Copy preceedence value proxy_client (str) -- Name of the proxy client to launch restore Default : None (Automatic) Returns: object - instance of the Job class for this restore job Raises: SDKException: if inputs are not of correct type as per definition if failed to initialize job if response is empty if response is not success """ restore_options = {} # Check mandatory input parameters are correct if namespace_to_restore and not type(namespace_to_restore) is list: raise SDKException('Subclient', '101') # Populating proxy client. It automatically fetches proxy controller from subclient/instance level # property if not specified if proxy_client is not None: restore_options['client'] = proxy_client restore_new_name = {} apps_to_restore = [] if not type(target_namespace_name) is dict: raise SDKException('Subclient', '101') for ns in namespace_to_restore: if ns not in target_namespace_name: target_namespace_name[ns] = ns restore_new_name.update(target_namespace_name) namespace_apps, namespace_app_map = self._get_apps_in_namespace(namespace_to_restore) apps_to_restore.extend(namespace_apps) for app in apps_to_restore: restore_new_name[app] = app apps_to_restore.extend(namespace_to_restore) restore_options['restore_new_name'] = restore_new_name restore_options['namespace_app_map'] = namespace_app_map if not target_cluster_name: target_cluster_name = self._client_object.client_name self._set_restore_inputs( restore_options, in_place=False, vcenter_client=target_cluster_name, esx_host=target_cluster_name, esx_server=None, unconditional_overwrite=overwrite, power_on=True, vm_to_restore=self._set_vm_to_restore(apps_to_restore), disk_option=self._disk_option['Original'], transport_mode=self._transport_mode['Auto'], copy_precedence=copy_precedence, volume_level_restore=1, source_item=[], source_ip=None, destination_ip=None, network=None, storage_class_map=storage_class_map ) request_json = self._prepare_kubernetes_restore_json(restore_options) return self._process_restore_response(request_json)
def set_advanced_vm_restore_options(self, vm_to_restore, restore_option)
-
set the advanced restore options for all vm in restore param
vm_to_restore - Name of the Application to restore restore_option - restore options that need to be set for advanced restore option power_on - power on the Application after restore add_to_failover - Register the Application to Failover Cluster datastore - Datastore where the Application needs to be restored disks (list of dict) - list with dict for each disk in Application eg: [{ name:"pvc-1" datastore:"storageclass-1" } { name:"pvc-2" datastore:"storageclass-2" } ] guid - GUID of the Application needs to be restored new_name - New name for the Application to be restored esx_host - client name where it need to be restored name - name of the Application to be restored
Expand source code Browse git
def set_advanced_vm_restore_options(self, vm_to_restore, restore_option): """ set the advanced restore options for all vm in restore param vm_to_restore - Name of the Application to restore restore_option - restore options that need to be set for advanced restore option power_on - power on the Application after restore add_to_failover - Register the Application to Failover Cluster datastore - Datastore where the Application needs to be restored disks (list of dict) - list with dict for each disk in Application eg: [{ name:"pvc-1" datastore:"storageclass-1" } { name:"pvc-2" datastore:"storageclass-2" } ] guid - GUID of the Application needs to be restored new_name - New name for the Application to be restored esx_host - client name where it need to be restored name - name of the Application to be restored """ # Set the new name for the restored Application. # If new_name is not given, it restores the Application with same name # with suffix Delete. vm_names, vm_ids = self._get_vm_ids_and_names_dict_from_browse() browse_result = self.vm_files_browse() application_id = vm_ids[vm_to_restore] # vs metadata from browse result _metadata = browse_result[1][('\\' + vm_to_restore)] vs_metadata = _metadata["advanced_data"]["browseMetaData"]["virtualServerMetaData"] if restore_option['in_place']: folder_path = vs_metadata.get("inventoryPath", '') instance_size = vs_metadata.get("instanceSize", '') else: folder_path = '' instance_size = '' if restore_option.get('resourcePoolPath'): restore_option['resourcePoolPath'] = vs_metadata['resourcePoolPath'] if restore_option.get('datacenter'): restore_option['datacenter'] = restore_option.get('datacenter') if restore_option.get('terminationProtected'): restore_option['terminationProtected'] = vs_metadata.get('terminationProtected', '') if restore_option.get('iamRole'): restore_option['iamRole'] = vs_metadata.get('role', '') if restore_option.get('securityGroups'): _security_groups = self._find_security_groups(vs_metadata['networkSecurityGroups']) restore_option['securityGroups'] = _security_groups if restore_option.get('keyPairList'): _keypair_list = self._find_keypair_list(vs_metadata['loginKeyPairs']) restore_option['keyPairList'] = _keypair_list # populate restore source item restore_option['paths'].append("\\" + application_id) restore_option['name'] = vm_to_restore restore_option['guid'] = application_id restore_option["FolderPath"] = folder_path restore_option["ResourcePool"] = "/" # populate restore disk and datastore vm_disks = [] new_name = vm_to_restore storage_class_map = restore_option.get('storage_class_map', None) # To populate disk list for each app in case of namespace restore if storage_class_map: pvc_list = self._get_app_pvc(application_id) for pvc in pvc_list: storageclass_name = pvc['storageclass'] pvc_name = pvc['name'] # If 'datastore' is passed then it's full app restore, else # it is namespace level restore. # Namespace level restore can be passed with storage class mapping if storageclass_name in storage_class_map: storageclass_name = storage_class_map[storageclass_name] _disk_dict = self._disk_dict_pattern(pvc_name, storageclass_name, pvc_name) vm_disks.append(_disk_dict) restore_option["disks"] = vm_disks self._set_restore_inputs( restore_option, esx_host=restore_option.get('esx_host') or vs_metadata['esxHost'], instance_size=restore_option.get('instanceSize', instance_size), new_name=restore_option.get('new_name', "Delete" + vm_to_restore) ) temp_dict = self._json_restore_advancedRestoreOptions(restore_option) self._advanced_restore_option_list.append(temp_dict)
Inherited members
VirtualServerSubclient
:allow_multiple_readers
amazon_defaults
backup
browse
browse_in_time
cbtvalue
content
data_readers
deduplication_options
description
disable_backup
disable_intelli_snap
disk_level_browse
disk_pattern
display_name
enable_backup
enable_backup_at_time
enable_trueup
enable_trueup_days
encryption_flag
exclude_from_sla
find
find_latest_job
get_ma_associated_storagepolicy
get_nics_from_browse
instance_proxy
is_backup_enabled
is_blocklevel_backup_enabled
is_default_subclient
is_intelli_snap_enabled
is_on_demand_subclient
is_trueup_enabled
last_backup_time
list_media
live_sync
metadata
name
network_agent
next_backup_time
parse_nics_xml
plan
preview_content
properties
quiesce_file_system
read_buffer_size
refresh
restore_in_place
restore_out_of_place
set_advanced_attach_disk_restore_options
set_backup_nodes
set_proxy_for_snap
snapshot_engine_name
software_compression
storage_ma
storage_ma_id
storage_policy
subclient_guid
subclient_id
subclient_name
subclient_proxy
unset_proxy_for_snap
update_properties
vm_diskfilter
vm_files_browse
vm_files_browse_in_time
vm_filter