initial commit
This commit is contained in:
+349
@@ -0,0 +1,349 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user