349 lines
16 KiB
Python
349 lines
16 KiB
Python
# Legal Disclaimer
|
|
# This script is an example script and is not supported under any Zerto support program or service.
|
|
# The author and Zerto further disclaim all implied warranties including, without limitation,
|
|
# any implied warranties of merchantability or of fitness for a particular purpose.
|
|
# In no event shall Zerto, its authors or anyone else involved in the creation,
|
|
# production or delivery of the scripts be liable for any damages whatsoever (including,
|
|
# without limitation, damages for loss of business profits, business interruption, loss of business
|
|
# information, or other pecuniary loss) arising out of the use of or the inability to use the sample
|
|
# scripts or documentation, even if the author or Zerto has been advised of the possibility of such damages.
|
|
# The entire risk arising out of the use or performance of the sample scripts and documentation remains with you.
|
|
|
|
import requests
|
|
import logging
|
|
import json
|
|
|
|
class VMs:
|
|
def __init__(self, client):
|
|
self.client = client
|
|
|
|
def list_vms(self, vm_identifier=None, vpg_name=None, vm_name=None, status=None, sub_status=None,
|
|
|
|
protected_site_type=None, recovery_site_type=None, protected_site_identifier=None,
|
|
recovery_site_identifier=None, organization_name=None, priority=None,
|
|
vpg_identifier=None, include_backuped_vms=None, include_mounted_vms=True):
|
|
"""
|
|
Get information about protected virtual machines. If vm_identifier is provided,
|
|
returns details about a specific VM, otherwise returns a filtered list of VMs. (Auth)
|
|
|
|
Args:
|
|
vm_identifier (str, optional): The identifier of a specific VM to get information about
|
|
vpg_name (str, optional): The name of the VPG
|
|
vm_name (str, optional): The name of the VM
|
|
status (str, optional): The status of the VPG
|
|
sub_status (str, optional): The sub-status of the VPG
|
|
protected_site_type (str, optional): The protected site type
|
|
recovery_site_type (str, optional): The recovery site type
|
|
protected_site_identifier (str, optional): The identifier of the protected site
|
|
recovery_site_identifier (str, optional): The identifier of the recovery site
|
|
organization_name (str, optional): The ZORG name
|
|
priority (str, optional): The VPG priority
|
|
vpg_identifier (str, optional): The identifier of the VPG (used with vm_identifier)
|
|
include_backuped_vms (bool, optional): Include VMs in backup targets
|
|
include_mounted_vms (bool, optional): Include mounted VMs in the response
|
|
|
|
Returns:
|
|
dict or list: Details of a specific VM if vm_identifier is provided,
|
|
otherwise an array of protected VMs matching the filter criteria
|
|
|
|
Raises:
|
|
requests.exceptions.RequestException: If the API request fails
|
|
"""
|
|
# Build the URL based on whether we're getting a specific VM or listing VMs
|
|
base_url = f"https://{self.client.zvm_address}/v1/vms"
|
|
url = f"{base_url}/{vm_identifier}" if vm_identifier else base_url
|
|
|
|
headers = {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': f'Bearer {self.client.token}'
|
|
}
|
|
|
|
# Build params based on whether we're getting a specific VM or listing VMs
|
|
if vm_identifier:
|
|
params = {
|
|
'vpgIdentifier': vpg_identifier,
|
|
'includeBackupedVms': include_backuped_vms,
|
|
'includeMountedVms': include_mounted_vms
|
|
}
|
|
log_msg = f"VMs.list_vms: Fetching VM {vm_identifier}"
|
|
else:
|
|
params = {
|
|
'vpgName': vpg_name,
|
|
'vmName': vm_name,
|
|
'status': status,
|
|
'subStatus': sub_status,
|
|
'protectedSiteType': protected_site_type,
|
|
'recoverySiteType': recovery_site_type,
|
|
'protectedSiteIdentifier': protected_site_identifier,
|
|
'recoverySiteIdentifier': recovery_site_identifier,
|
|
'organizationName': organization_name,
|
|
'priority': priority,
|
|
'vmIdentifier': vm_identifier,
|
|
'includeBackupedVms': include_backuped_vms,
|
|
'includeMountedVms': include_mounted_vms
|
|
}
|
|
log_msg = "VMs.list_vms: Fetching VMs"
|
|
|
|
# Remove None values from params
|
|
params = {k: v for k, v in params.items() if v is not None}
|
|
|
|
logging.info(f"{log_msg} with params: {params}")
|
|
try:
|
|
response = requests.get(url, headers=headers, params=params, verify=self.client.verify_certificate)
|
|
response.raise_for_status()
|
|
return response.json()
|
|
except requests.exceptions.RequestException as e:
|
|
if e.response is not None:
|
|
logging.error(f"HTTPError: {e.response.status_code} - {e.response.reason}")
|
|
try:
|
|
error_details = e.response.json()
|
|
logging.error(f"Error Message: {error_details.get('Message', 'No detailed error message available')}")
|
|
except ValueError:
|
|
logging.error(f"Response content: {e.response.text}")
|
|
else:
|
|
logging.error("HTTPError occurred with no response attached.")
|
|
raise
|
|
|
|
def restore_vm(self, vm_identifier, vpg_identifier, restored_vm_name, checkpoint_identifier,
|
|
journal_vm_restore_settings, commit_policy=0, shutdown_policy=0,
|
|
time_to_wait_before_continue_in_seconds=0):
|
|
"""
|
|
Restore a VM from a specific checkpoint. (Auth)
|
|
|
|
Args:
|
|
vm_identifier (str): The identifier of the VM to restore
|
|
vpg_identifier (str): The identifier of the VPG
|
|
restored_vm_name (str): The name for the restored VM
|
|
checkpoint_identifier (str): The identifier of the checkpoint to restore from
|
|
journal_vm_restore_settings (dict): Settings for the restored VM with structure:
|
|
{
|
|
"datastoreIdentifier": str,
|
|
"nics": [
|
|
{
|
|
"hypervisor": {
|
|
"dnsSuffix": str,
|
|
"ipConfig": {
|
|
"gateway": str,
|
|
"isDhcp": bool,
|
|
"primaryDns": str,
|
|
"secondaryDns": str,
|
|
"staticIp": str,
|
|
"subnetMask": str
|
|
},
|
|
"networkIdentifier": str,
|
|
"shouldReplaceMacAddress": bool
|
|
},
|
|
"nicIdentifier": str
|
|
}
|
|
],
|
|
"volumes": [
|
|
{
|
|
"datastore": {
|
|
"datastoreIdentifier": str,
|
|
"isThin": bool
|
|
},
|
|
"volumeIdentifier": str
|
|
}
|
|
]
|
|
}
|
|
commit_policy (int, optional): The commit policy. Defaults to 0
|
|
shutdown_policy (int, optional): The shutdown policy. Defaults to 0
|
|
time_to_wait_before_continue_in_seconds (int, optional): Time to wait before continuing. Defaults to 0
|
|
|
|
Returns:
|
|
dict: Response from the server
|
|
|
|
Raises:
|
|
requests.exceptions.RequestException: If the API request fails
|
|
"""
|
|
url = f"https://{self.client.zvm_address}/v1/vms/{vm_identifier}/Restore"
|
|
headers = {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': f'Bearer {self.client.token}'
|
|
}
|
|
|
|
data = {
|
|
"vpgIdentifier": vpg_identifier,
|
|
"restoredVmName": restored_vm_name,
|
|
"checkpointIdentifier": checkpoint_identifier,
|
|
"commitPolicy": commit_policy,
|
|
"shutdownPolicy": shutdown_policy,
|
|
"timeToWaitBeforeContinueInSeconds": time_to_wait_before_continue_in_seconds,
|
|
"journalVMRestoreSettings": journal_vm_restore_settings
|
|
}
|
|
logging.info(f"VMs.restore_vm: Restoring VM {vm_identifier} from checkpoint {checkpoint_identifier}")
|
|
logging.info(f"VMs.restore_vm: Data: {json.dumps(data, indent=2)}")
|
|
try:
|
|
response = requests.post(url, headers=headers, json=data, verify=self.client.verify_certificate)
|
|
response.raise_for_status()
|
|
return response.json() if response.content else None
|
|
except requests.exceptions.RequestException as e:
|
|
if e.response is not None:
|
|
logging.error(f"HTTPError: {e.response.status_code} - {e.response.reason}")
|
|
try:
|
|
error_details = e.response.json()
|
|
logging.error(f"Error Message: {error_details.get('Message', 'No detailed error message available')}")
|
|
except ValueError:
|
|
logging.error(f"Response content: {e.response.text}")
|
|
else:
|
|
logging.error("HTTPError occurred with no response attached.")
|
|
raise
|
|
|
|
def restore_vm_commit(self, vm_identifier):
|
|
"""
|
|
Commit a restored VM. (Auth)
|
|
|
|
Args:
|
|
vm_identifier (str): The identifier of the VM to commit
|
|
|
|
Returns:
|
|
dict: Response from the server if any
|
|
|
|
Raises:
|
|
requests.exceptions.RequestException: If the API request fails
|
|
"""
|
|
url = f"https://{self.client.zvm_address}/v1/vms/{vm_identifier}/RestoreCommit"
|
|
headers = {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': f'Bearer {self.client.token}'
|
|
}
|
|
|
|
logging.info(f"VMs.restore_vm_commit: Committing restored VM {vm_identifier}")
|
|
try:
|
|
response = requests.post(url, headers=headers, verify=self.client.verify_certificate)
|
|
response.raise_for_status()
|
|
return response.json() if response.content else None
|
|
except requests.exceptions.RequestException as e:
|
|
if e.response is not None:
|
|
logging.error(f"HTTPError: {e.response.status_code} - {e.response.reason}")
|
|
try:
|
|
error_details = e.response.json()
|
|
logging.error(f"Error Message: {error_details.get('Message', 'No detailed error message available')}")
|
|
except ValueError:
|
|
logging.error(f"Response content: {e.response.text}")
|
|
else:
|
|
logging.error("HTTPError occurred with no response attached.")
|
|
raise
|
|
|
|
def restore_vm_rollback(self, vm_identifier):
|
|
"""
|
|
Rollback a restored VM. (Auth)
|
|
|
|
Args:
|
|
vm_identifier (str): The identifier of the VM to rollback
|
|
|
|
Returns:
|
|
dict: Response from the server if any
|
|
|
|
Raises:
|
|
requests.exceptions.RequestException: If the API request fails
|
|
"""
|
|
url = f"https://{self.client.zvm_address}/v1/vms/{vm_identifier}/RestoreRollback"
|
|
headers = {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': f'Bearer {self.client.token}'
|
|
}
|
|
|
|
logging.info(f"VMs.restore_vm_rollback: Rolling back restored VM {vm_identifier}")
|
|
try:
|
|
response = requests.post(url, headers=headers, verify=self.client.verify_certificate)
|
|
response.raise_for_status()
|
|
return response.json() if response.content else None
|
|
except requests.exceptions.RequestException as e:
|
|
if e.response is not None:
|
|
logging.error(f"HTTPError: {e.response.status_code} - {e.response.reason}")
|
|
try:
|
|
error_details = e.response.json()
|
|
logging.error(f"Error Message: {error_details.get('Message', 'No detailed error message available')}")
|
|
except ValueError:
|
|
logging.error(f"Response content: {e.response.text}")
|
|
else:
|
|
logging.error("HTTPError occurred with no response attached.")
|
|
raise
|
|
|
|
def list_vm_points_in_time(self, vm_identifier, vpg_identifier=None, start_date=None, end_date=None):
|
|
"""
|
|
Get points in time for a specific VM. (Auth)
|
|
|
|
Args:
|
|
vm_identifier (str): The identifier of the VM
|
|
vpg_identifier (str, optional): The identifier of the VPG
|
|
start_date (str, optional): The filter interval start date-time
|
|
end_date (str, optional): The filter interval end date-time
|
|
|
|
Returns:
|
|
list: Array of points in time for the specified VM
|
|
|
|
Raises:
|
|
requests.exceptions.RequestException: If the API request fails
|
|
"""
|
|
url = f"https://{self.client.zvm_address}/v1/vms/{vm_identifier}/pointsInTime"
|
|
headers = {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': f'Bearer {self.client.token}'
|
|
}
|
|
|
|
params = {
|
|
'vpgIdentifier': vpg_identifier,
|
|
'startDate': start_date,
|
|
'endDate': end_date
|
|
}
|
|
# Remove None values from params
|
|
params = {k: v for k, v in params.items() if v is not None}
|
|
|
|
logging.info(f"VMs.list_vm_points_in_time: Fetching points in time for VM {vm_identifier}")
|
|
try:
|
|
response = requests.get(url, headers=headers, params=params, verify=self.client.verify_certificate)
|
|
response.raise_for_status()
|
|
return response.json()
|
|
except requests.exceptions.RequestException as e:
|
|
if e.response is not None:
|
|
logging.error(f"HTTPError: {e.response.status_code} - {e.response.reason}")
|
|
try:
|
|
error_details = e.response.json()
|
|
logging.error(f"Error Message: {error_details.get('Message', 'No detailed error message available')}")
|
|
except ValueError:
|
|
logging.error(f"Response content: {e.response.text}")
|
|
else:
|
|
logging.error("HTTPError occurred with no response attached.")
|
|
raise
|
|
|
|
def list_vm_points_in_time_stats(self, vm_identifier, vpg_identifier=None):
|
|
"""
|
|
Get the earliest and latest points in time for the VM. (Auth)
|
|
VpgId may be required if the VM is protected by more than one VPG.
|
|
|
|
Args:
|
|
vm_identifier (str): The identifier of the VM
|
|
vpg_identifier (str, optional): The identifier of the VPG which protects the VM
|
|
|
|
Returns:
|
|
dict: Statistics about the earliest and latest points in time for the VM
|
|
|
|
Raises:
|
|
requests.exceptions.RequestException: If the API request fails
|
|
"""
|
|
url = f"https://{self.client.zvm_address}/v1/vms/{vm_identifier}/pointsInTime/stats"
|
|
headers = {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': f'Bearer {self.client.token}'
|
|
}
|
|
|
|
params = {'vpgIdentifier': vpg_identifier} if vpg_identifier else {}
|
|
|
|
logging.info(f"VMs.list_vm_points_in_time_stats: Fetching points in time stats for VM {vm_identifier}")
|
|
try:
|
|
response = requests.get(url, headers=headers, params=params, verify=self.client.verify_certificate)
|
|
response.raise_for_status()
|
|
return response.json()
|
|
except requests.exceptions.RequestException as e:
|
|
if e.response is not None:
|
|
logging.error(f"HTTPError: {e.response.status_code} - {e.response.reason}")
|
|
try:
|
|
error_details = e.response.json()
|
|
logging.error(f"Error Message: {error_details.get('Message', 'No detailed error message available')}")
|
|
except ValueError:
|
|
logging.error(f"Response content: {e.response.text}")
|
|
else:
|
|
logging.error("HTTPError occurred with no response attached.")
|
|
raise |