initial commit

This commit is contained in:
Kosta Mushkin
2025-04-12 14:33:32 -04:00
parent 955ec79014
commit ba16ef9a08
83 changed files with 10290 additions and 2 deletions
+1
View File
@@ -0,0 +1 @@
from .client import ZVMLClient
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+324
View File
@@ -0,0 +1,324 @@
# 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
class Alerts:
def __init__(self, client):
self.client = client
# Manage ZVM Alerts
def get_alerts(self, start_date=None, end_date=None, vpg_name=None, zorg_identifier=None,
site_identifier=None, level=None, entity=None, help_identifier=None, is_dismissed=None,
alert_identifier=None):
"""
Fetches alerts from the Zerto API with optional filters or a specific alert if `alert_identifier` is provided.
:param start_date: The filter interval start date-time (string in date-time format).
:param end_date: The filter interval end date-time (string in date-time format).
:param vpg_name: The name of the VPG to filter alerts for.
:param zorg_identifier: The identifier of the ZORG.
:param site_identifier: The internal ZVM site identifier.
:param level: The alert level.
:param entity: The alert entity type.
:param help_identifier: The alert help identifier associated with the alert.
:param is_dismissed: True if alert was dismissed.
:param alert_identifier: The specific alert identifier to retrieve a single alert.
:return: List of alerts or a specific alert based on the provided filters.
"""
if alert_identifier:
alerts_uri = f"https://{self.client.zvm_address}/v1/alerts/{alert_identifier}"
else:
alerts_uri = f"https://{self.client.zvm_address}/v1/alerts"
logging.info(f'Alerts.get_alerts(alert_identifier={alert_identifier}, start_date={start_date}, end_date={end_date}, '
f'vpg_name={vpg_name}, zorg_identifier={zorg_identifier}, site_identifier={site_identifier}, '
f'level={level}, entity={entity}, help_identifier={help_identifier}, is_dismissed={is_dismissed})')
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
# Building query parameters for general alerts retrieval
params = {}
if not alert_identifier:
if start_date:
params['startDate'] = start_date
if end_date:
params['endDate'] = end_date
if vpg_name:
# Get VPG identifier from name
vpg = self.client.vpgs.get_vpg_by_name(vpg_name)
if vpg:
params['vpgIdentifier'] = vpg.get('VpgIdentifier')
logging.info(f"Found VPG identifier {params['vpgIdentifier']} for VPG name {vpg_name}")
else:
logging.warning(f"VPG with name {vpg_name} not found")
return []
if zorg_identifier:
params['zorgIdentifier'] = zorg_identifier
if site_identifier:
params['siteIdentifier'] = site_identifier
if level:
params['level'] = level
if entity:
params['entity'] = entity
if help_identifier:
params['helpIdentifier'] = help_identifier
if is_dismissed is not None:
params['isDismissed'] = str(is_dismissed).lower()
try:
logging.info("Fetching alerts...")
response = requests.get(alerts_uri, headers=headers, params=params, verify=self.client.verify_certificate)
response.raise_for_status()
alerts = response.json()
if not alerts:
logging.warning("No alerts found.")
return []
return alerts
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
except Exception as e:
logging.error(f"Unexpected error: {e}")
raise
def dismiss_alert(self, alert_identifier):
"""
Dismisses a specific alert by its identifier.
:param alert_identifier: The identifier of the alert to be dismissed.
:return: Success message if the alert was dismissed, else an error message.
"""
logging.info(f'Alerts.dismiss_alert(alert_identifier={alert_identifier})')
# Construct the URL for dismissing the alert
dismiss_uri = f"https://{self.client.zvm_address}/v1/alerts/{alert_identifier}/dismiss"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
logging.info(f"Attempting to dismiss alert with ID: {alert_identifier}")
response = requests.post(dismiss_uri, headers=headers, verify=self.client.verify_certificate)
response.raise_for_status()
if response.status_code == 200:
logging.info(f"Alert {alert_identifier} successfully dismissed.")
return f"Alert {alert_identifier} dismissed successfully."
else:
logging.warning(f"Unexpected response code: {response.status_code}")
return f"Alert {alert_identifier} dismissal returned an unexpected status."
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
except Exception as e:
logging.error(f"Unexpected error: {e}")
raise
def undismiss_alert(self, alert_identifier):
"""
Undismisses a specific alert by its identifier.
:param alert_identifier: The identifier of the alert to be dismissed.
:return: Success message if the alert was dismissed, else an error message.
"""
logging.info(f'Alerts.undismiss_alert(alert_identifier={alert_identifier})')
# Construct the URL for dismissing the alert
undismiss_uri = f"https://{self.client.zvm_address}/v1/alerts/{alert_identifier}/undismiss"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
logging.info(f"Attempting to undismiss alert with ID: {alert_identifier}")
response = requests.post(undismiss_uri, headers=headers, verify=self.client.verify_certificate)
response.raise_for_status()
if response.status_code == 200:
logging.info(f"Alert {alert_identifier} successfully undismissed.")
return f"Alert {alert_identifier} undismissed successfully."
else:
logging.warning(f"Unexpected response code: {response.status_code}")
return f"Alert {alert_identifier} undismissal returned an unexpected status."
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
except Exception as e:
logging.error(f"Unexpected error: {e}")
raise
def get_alert_levels(self):
"""
Fetches the available alert levels from the Zerto API.
:return: List of alert levels or an error message if the request fails.
"""
logging.info('Alerts.get_alert_levels()')
# Construct the URL for fetching alert levels
alert_levels_uri = f"https://{self.client.zvm_address}/v1/alerts/levels"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
logging.info("Fetching available alert levels...")
response = requests.get(alert_levels_uri, headers=headers, verify=self.client.verify_certificate)
response.raise_for_status()
alert_levels = response.json()
if not alert_levels:
logging.warning("No alert levels found.")
return []
return alert_levels
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
except Exception as e:
logging.error(f"Unexpected error: {e}")
raise
def get_alert_entities(self):
"""
Fetches the available alert entities from the Zerto API.
:return: List of alert entities or an error message if the request fails.
"""
logging.info('Alerts.get_alert_entities()')
# Construct the URL for fetching alert entities
alert_entities_uri = f"https://{self.client.zvm_address}/v1/alerts/entities"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
logging.info("Fetching available alert entities...")
response = requests.get(alert_entities_uri, headers=headers, verify=self.client.verify_certificate)
response.raise_for_status()
alert_entities = response.json()
if not alert_entities:
logging.warning("No alert entities found.")
return []
return alert_entities
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
except Exception as e:
logging.error(f"Unexpected error: {e}")
raise
def get_alert_help_identifiers(self):
"""
Fetches the available alert help identifiers from the Zerto API.
:return: List of alert help identifiers or an error message if the request fails.
"""
logging.info('Alerts.get_alert_help_identifiers()')
# Construct the URL for fetching alert help identifiers
help_identifiers_uri = f"https://{self.client.zvm_address}/v1/alerts/helpidentifiers"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
logging.info("Fetching available alert help identifiers...")
response = requests.get(help_identifiers_uri, headers=headers, verify=self.client.verify_certificate)
response.raise_for_status()
help_identifiers = response.json()
if not help_identifiers:
logging.warning("No alert help identifiers found.")
return []
return help_identifiers
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
except Exception as e:
logging.error(f"Unexpected error: {e}")
raise
+109
View File
@@ -0,0 +1,109 @@
# 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 ssl
# Configure logging with timestamp format
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
# Import all necessary classes
from .tasks import Tasks
from .vpgs import VPGs
from .vms import VMs
from .failover import Failover
from .alerts import Alerts
from .peersites import PeerSites
from .events import Events
from .repositories import Repositories
from .sessions import Sessions
from .recoveryscripts import RecoveryScripts
from .encryptiondetection import EncryptionDetection
from .zorgs import Zorgs
from .localsite import LocalSite
from .datastores import Datastores
from .vras import VRA
from .recovery_reports import RecoveryReports
from .license import License
from .service_profiles import ServiceProfiles
from .server_date_time import ServerDateTime
from .virtualization_sites import VirtualizationSites
from .volumes import Volumes
from .tweaks import Tweaks
# Disable SSL warnings for self-signed certificates
context = ssl._create_unverified_context()
class ZVMLClient:
def __init__(self, zvm_address, client_id, client_secret, verify_certificate=True):
self.zvm_address = zvm_address
self.client_id = client_id
self.client_secret = client_secret
self.verify_certificate = verify_certificate
self.token = None
self.token_expiry = None
self.__get_keycloak_token()
self.tasks = Tasks(self)
self.vpgs = VPGs(self)
self.vms = VMs(self)
self.failover = Failover(self)
self.alerts = Alerts(self)
self.peersites = PeerSites(self)
self.events = Events(self)
self.repositories = Repositories(self)
self.sessions = Sessions(self)
self.recoveryscripts = RecoveryScripts(self)
self.zorgs = Zorgs(self)
self.encryptiondetection = EncryptionDetection(self)
self.localsite = LocalSite(self.zvm_address, self.token)
self.datastores = Datastores(self)
self.vras = VRA(self)
self.recovery_reports = RecoveryReports(self)
self.license = License(self)
self.service_profiles = ServiceProfiles(self)
self.server_date_time = ServerDateTime(self)
self.virtualization_sites = VirtualizationSites(self)
self.volumes = Volumes(self)
self.tweaks = Tweaks(self)
def __get_keycloak_token(self):
logging.debug(f'__get_keycloak_token(zvm_address={self.zvm_address})')
keycloak_uri = f"https://{self.zvm_address}/auth/realms/zerto/protocol/openid-connect/token"
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
body = {
'client_id': self.client_id,
'client_secret': self.client_secret,
'grant_type': 'client_credentials',
'expires_in': 3600 # Request token expiration in seconds (e.g., 1 hour)
}
try:
logging.info("Connecting to Keycloak to get token...")
response = requests.post(keycloak_uri, headers=headers, data=body, verify=self.verify_certificate)
response.raise_for_status()
token_data = response.json()
self.token = token_data.get('access_token')
self.token_expiry = token_data.get('expires_in') # Store expiration time
logging.info(f"Successfully retrieved token.")
logging.info(f"Token expiration details:")
logging.info(f"- Expires in: {self.token_expiry} seconds")
logging.info(f"- Requested expiration: {body['expires_in']} seconds")
if self.token_expiry != body['expires_in']:
logging.warning(f"Server provided different expiration time than requested!")
return self.token
except requests.exceptions.RequestException as e:
logging.error(f"Error retrieving token: {e}")
raise
+599
View File
@@ -0,0 +1,599 @@
# 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.
from enum import Enum
class ZertoTaskTypes(Enum):
CreateProtectionGroup = 0
RemoveProtectionGroup = 1
FailOver = 2
FailOverTest = 3
StopFailOverTest = 4
Move = 5
GetCheckpointList = 6
ProtectVM = 7
UnprotectVM = 8
AddVMToProtectionGroup = 9
RemoveVMFromProtectionGroup = 10
InstallVra = 11
UninstallVra = 12
GetVMSettings = 13
UpdateProtectionGroup = 14
InsertTaggedCP = 15
WaitForCP = 16
HandleMirrorPromotion = 17
ActivateAllMirrors = 18
LogCollection = 19
ClearCheckpoints = 20
ForceReconfigurationOfNewVM = 21
ClearSite = 22
ForceRemoveProtectionGroup = 23
ForceUpdateProtectionGroup = 24
ForceKillProtectionGroup = 25
PrePostScript = 26
InitFullSync = 27
Pair = 28
Unpair = 29
AddPeerVraInfo = 30
RemovePeerVraInfo = 31
InstallCloudConnector = 32
UninstallCloudConnector = 33
HandleFirstSyncDone = 34
Clone = 35
MoveBeforeCommit = 36
MoveRollback = 37
MoveCommit = 38
UpgradeVRA = 39
MaintainHost = 40
NotSupportedInThisVersion = 41
MoveProtectionGroupToManualOperationNeeded = 42
FailoverBeforeCommit = 43
FailoverCommit = 44
@classmethod
def get_name_by_value(cls, value):
for member in cls:
if member.value == value:
return member.name
return None
@classmethod
def get_value_by_name(cls, name):
for member in cls:
if member.name == name:
return member.value
return None
class ZertoTaskStates(Enum):
FirstUnusedValue = 0
InProgress = 1
WaitingForUserInput = 2
Paused = 3
Failed = 4
Stopped = 5
Completed = 6
Cancelling = 7
@classmethod
def get_name_by_value(cls, value):
for member in cls:
if member.value == value:
return member.name
return None
@classmethod
def get_value_by_name(cls, name):
for member in cls:
if member.name == name:
return member.value
return None
class ZertoVPGStatus(Enum):
Initializing = 0
MeetingSLA = 1
NotMeetingSLA = 2
HistoryNotMeetingSLA = 3
RpoNotMeetingSLA = 4
FailingOver = 5
Moving = 6
Deleting = 7
Recovered = 8
@classmethod
def get_name_by_value(cls, value):
for member in cls:
if member.value == value:
return member.name
return None
@classmethod
def get_value_by_name(cls, name):
for member in cls:
if member.name == name:
return member.value
return None
class ZertoVPGSubstatus(Enum):
NONE = 0 # Using NONE instead of None as None is a Python keyword
InitialSync = 1
Creating = 2
VolumeInitialSync = 3
Sync = 4
RecoveryPossible = 5
DeltaSync = 6
NeedsConfiguration = 7
Error = 8
EmptyProtectionGroup = 9
DisconnectedFromPeerNoRecoveryPoints = 10
FullSync = 11
VolumeDeltaSync = 12
VolumeFullSync = 13
FailingOverCommitting = 14
FailingOverBeforeCommit = 15
FailingOverRollingBack = 16
Promoting = 17
MovingCommitting = 18
MovingBeforeCommit = 19
MovingRollingBack = 20
Deleting = 21
PendingRemove = 22
BitmapSync = 23
DisconnectedFromPeer = 24
ReplicationPausedUserInitiated = 25
ReplicationPausedSystemInitiated = 26
RecoveryStorageProfileError = 27
Backup = 28
RollingBack = 29
RecoveryStorageError = 30
JournalStorageError = 31
VmNotProtectedError = 32
@classmethod
def get_name_by_value(cls, value):
for member in cls:
if member.value == value:
return member.name
return None
@classmethod
def get_value_by_name(cls, name):
for member in cls:
if member.name == name:
return member.value
return None
class ZertoProtectedSiteType(Enum):
VCVpg = 0
VCvApp = 1
VCDvApp = 2
AWS = 3
HyperV = 4
@classmethod
def get_name_by_value(cls, value):
for member in cls:
if member.value == value:
return member.name
return None
@classmethod
def get_value_by_name(cls, name):
for member in cls:
if member.name == name:
return member.value
return None
class ZertoRecoverySiteType(Enum):
VCVpg = 0
VCvApp = 1
VCDvApp = 2
AWS = 3
HyperV = 4
@classmethod
def get_name_by_value(cls, value):
for member in cls:
if member.value == value:
return member.name
return None
@classmethod
def get_value_by_name(cls, name):
for member in cls:
if member.name == name:
return member.value
return None
class ZertoVPGPriority(Enum):
Low = 0
Medium = 1
High = 2
@classmethod
def get_name_by_value(cls, value):
for member in cls:
if member.value == value:
return member.name
return None
class ZertoVRAStatus(Enum):
Installed = 0
UnsupportedEsxVersion = 1
NotInstalled = 2
Installing = 3
Removing = 4
InstallationError = 5
HostPasswordChanged = 6
UpdatingIpSettings = 7
DuringChangeHost = 8
@classmethod
def get_name_by_value(cls, value):
for member in cls:
if member.value == value:
return member.name
return None
class ZertoPairingStatus(Enum):
Paired = 0
Pairing = 1
Unpaired = 2
@classmethod
def get_name_by_value(cls, value):
for member in cls:
if member.value == value:
return member.name
return None
class ZertoAlertLevel(Enum):
Warning = 0
Error = 1
@classmethod
def get_name_by_value(cls, value):
for member in cls:
if member.value == value:
return member.name
return None
class ZertoAlertEntity(Enum):
Zvm = 0
Vra = 1
Vpg = 2
CloudConnector = 3
Storage = 4
License = 5
Zcm = 6
FileRecoveryComponent = 7
@classmethod
def get_name_by_value(cls, value):
for member in cls:
if member.value == value:
return member.name
return None
class ZertoAlertHelpIdentifier(Enum):
AWS0001 = 0
BCK0001 = 1
BCK0002 = 2
BCK0005 = 3
BCK0006 = 4
BCK0007 = 5
LIC0001 = 6
LIC0002 = 7
LIC0003 = 8
LIC0004 = 9
LIC0005 = 10
LIC0006 = 11
LIC0007 = 12
LIC0008 = 13
STR0001 = 14
STR0002 = 15
STR0004 = 16
VCD0001 = 17
VCD0002 = 18
VCD0003 = 19
VCD0004 = 20
VCD0005 = 21
VCD0006 = 22
VCD0007 = 23
VCD0010 = 24
VCD0014 = 25
VCD0015 = 26
VCD0016 = 27
VCD0017 = 28
VCD0018 = 29
VCD0020 = 30
VCD0021 = 31
VPG0003 = 32
VPG0004 = 33
VPG0005 = 34
VPG0006 = 35
VPG0007 = 36
VPG0008 = 37
VPG0009 = 38
VPG0010 = 39
VPG0011 = 40
VPG0012 = 41
VPG0014 = 42
VPG0015 = 43
VPG0016 = 44
VPG0017 = 45
VPG0018 = 46
VPG0019 = 47
VPG0020 = 48
VPG0021 = 49
VPG0022 = 50
VPG0023 = 51
VPG0024 = 52
VPG0025 = 53
VPG0026 = 54
VPG0027 = 55
VPG0028 = 56
VPG0035 = 57
VPG0036 = 58
VPG0037 = 59
VPG0038 = 60
VPG0039 = 61
VPG0040 = 62
VPG0041 = 63
VPG0042 = 64
VPG0043 = 65
VPG0044 = 66
VPG0045 = 67
VPG0046 = 68
VPG0047 = 69
VPG0048 = 70
VRA0001 = 71
VRA0002 = 72
VRA0003 = 73
VRA0004 = 74
VRA0005 = 75
VRA0006 = 76
VRA0007 = 77
VRA0008 = 78
VRA0009 = 79
VRA0010 = 80
VRA0011 = 81
VRA0012 = 82
VRA0013 = 83
VRA0014 = 84
VRA0015 = 85
VRA0016 = 86
VRA0017 = 87
VRA0018 = 88
VRA0019 = 89
VRA0020 = 90
VRA0021 = 91
VRA0022 = 92
VRA0023 = 93
VRA0024 = 94
VRA0025 = 95
VRA0026 = 96
VRA0027 = 97
VRA0028 = 98
VRA0029 = 99
VRA0030 = 100
VRA0032 = 101
VRA0035 = 102
VRA0036 = 103
VRA0037 = 104
VRA0038 = 105
VRA0039 = 106
VRA0040 = 107
VRA0049 = 108
VRA0050 = 109
VRA0051 = 110
VRA0052 = 111
VRA0053 = 112
VRA0054 = 113
VRA0055 = 114
ZCC0001 = 115
ZCC0002 = 116
ZCC0003 = 117
ZCM0001 = 118
ZVM0001 = 119
ZVM0002 = 120
ZVM0003 = 121
ZVM0004 = 122
ZVM0005 = 123
ZVM0006 = 124
ZVM0007 = 125
ZVM0008 = 126
ZVM0009 = 127
ZVM0010 = 128
ZVM0011 = 129
ZVM0012 = 130
ZVM0013 = 131
ZVM0014 = 132
ZVM0015 = 133
FLR0001 = 134
Unknown = 135
@classmethod
def get_name_by_value(cls, value):
for member in cls:
if member.value == value:
return member.name
return None
class ZertoEventType(Enum):
Unknown = 0
CreateProtectionGroup = 1
RemoveProtectionGroup = 2
FailOver = 3
FailOverTest = 4
StopFailOverTest = 5
Move = 6
ProtectVM = 7
UnprotectVM = 8
InstallVra = 9
UninstallVra = 10
UpdateProtectionGroup = 11
InsertTaggedCP = 12
HandleMirrorPromotion = 13
ActivateAllMirrors = 14
LogCollection = 15
ForceReconfigurationOfNewVM = 16
ClearSite = 17
ForceRemoveProtectionGroup = 18
ForceUpdateProtectionGroup = 19
ForceKillProtectionGroup = 20
PrePostScript = 21
InitFullSync = 22
Pair = 23
Unpair = 24
InstallCloudConnector = 25
UninstallCloudConnector = 26
RedeployCloudConnector = 27
ScriptExecutionFailure = 28
SetAdvancedSiteSettings = 29
Clone = 30
KeepDisk = 31
FailoverBeforeCommit = 32
FailoverCommit = 33
FailoverRollback = 34
MoveBeforeCommit = 35
MoveRollback = 36
MoveCommit = 37
MaintainHost = 38
UpgradeVra = 39
MoveProtectionGroupToManualOperationNeeded = 40
ChangeVraIpSettings = 41
PauseProtectionGroup = 42
ResumeProtectionGroup = 43
UpgradeZVM = 44
BulkUpgradeVras = 45
BulkUninstallVras = 46
AlertTurnedOn = 47
AlertTurnedOff = 48
ChangeVraPassword = 49
ChangeRecoveryHost = 50
BackupProtectionGroup = 51
CleanupProtectionGroupVipDiskbox = 52
RestoreProtectionGroup = 53
PreScript = 54
PostScript = 55
RemoveVmFromVc = 56
ChangeVraPasswordIpSettings = 57
FlrJournalMount = 58
FlrJournalUnmount = 59
Login = 60
@classmethod
def get_name_by_value(cls, value):
for member in cls:
if member.value == value:
return member.name
return None
class ZertoEventCategory(Enum):
All = 0
Events = 1
Alerts = 2
@classmethod
def get_name_by_value(cls, value):
for member in cls:
if member.value == value:
return member.name
return None
class ZertoCommitPolicy(Enum):
Rollback = 0
Commit = 1
NONE = 2 # Using NONE instead of None as None is a Python keyword
@classmethod
def get_name_by_value(cls, value):
for member in cls:
if member.value == value:
return member.name
return None
class ZertoShutdownPolicy(Enum):
NONE = 0 # Using NONE instead of None as None is a Python keyword
Shutdown = 1
ForceShutdown = 2
@classmethod
def get_name_by_value(cls, value):
for member in cls:
if member.value == value:
return member.name
return None
class ZertoVRAIPConfigType(Enum):
Dhcp = 0
Static = 1
@classmethod
def get_name_by_value(cls, value):
for member in cls:
if member.value == value:
return member.name
return None
class ZertoVPGSettingsBackupRetentionPeriod(Enum):
OneWeek = 0
OneMonth = 1
ThreeMonths = 2
SixMonths = 3
NineMonths = 4
OneYear = 5
@classmethod
def get_name_by_value(cls, value):
for member in cls:
if member.value == value:
return member.name
return None
class ZertoVPGSettingsBackupSchedulerDOW(Enum):
Sunday = 0
Monday = 1
Tuesday = 2
Wednesday = 3
Thursday = 4
Friday = 5
Saturday = 6
@classmethod
def get_name_by_value(cls, value):
for member in cls:
if member.value == value:
return member.name
return None
class ZertoVPGSettingsBackupSchedulerPeriod(Enum):
Daily = 0
Weekly = 1
@classmethod
def get_name_by_value(cls, value):
for member in cls:
if member.value == value:
return member.name
return None
class ZertoTweakType(Enum):
ZVM = "zvm-tweak"
VRA = "vra-tweak"
Frontend = "frontend-tweak"
@classmethod
def get_name_by_value(cls, value):
for member in cls:
if member.value == value:
return member.name
return None
+45
View File
@@ -0,0 +1,45 @@
# 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
class Datastores:
def __init__(self, client):
self.client = client
def list_datastores(self, datastore_identifier=None):
if datastore_identifier:
logging.info(f"Datastores.list_datastores: Fetching datastore information for identifier: {datastore_identifier}...")
url = f"https://{self.client.zvm_address}/v1/datastores/{datastore_identifier}"
else:
logging.info("Datastores.list_datastores: Fetching all datastores information...")
url = f"https://{self.client.zvm_address}/v1/datastores"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.get(url, headers=headers, verify=self.client.verify_certificate)
response.raise_for_status()
if datastore_identifier:
logging.info(f"Datastores.list_datastores: Successfully retrieved datastore information for identifier: {datastore_identifier}.")
else:
logging.info("Datastores.list_datastores: Successfully retrieved all datastores information.")
return response.json()
except requests.exceptions.RequestException as e:
if datastore_identifier:
logging.error(f"Datastores.list_datastores: Failed to get datastore information for identifier {datastore_identifier}: {e}")
else:
logging.error(f"Datastores.list_datastores: Failed to get all datastores information: {e}")
raise
+124
View File
@@ -0,0 +1,124 @@
# 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
from typing import List, Dict
class EncryptionDetection:
def __init__(self, client):
self.client = client
def get_encryption_detections(self):
url = f"https://{self.client.zvm_address}/v1/encryptiondetection"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
logging.info(f"EncryptionDetection.get_encryption_detections(zvm_address={self.client.zvm_address})")
try:
response = requests.get(url, headers=headers, 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 get_encryption_detection(self, detection_identifier):
url = f"https://{self.client.zvm_address}/v1/encryptiondetection/{detection_identifier}"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
logging.info(f"EncryptionDetection.get_encryption_detection(zvm_address={self.client.zvm_address}, detection_identifier={detection_identifier})")
try:
response = requests.get(url, headers=headers, 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 get_encryption_detection_types(self):
url = f"https://{self.client.zvm_address}/v1/encryptiondetection/types"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
logging.info(f"EncryptionDetection.get_encryption_detection_types(zvm_address={self.client.zvm_address})")
try:
response = requests.get(url, headers=headers, 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_suspected_volumes(self) -> List[Dict]:
"""List all suspected encrypted volumes.
Returns:
List[Dict]: List of suspected encrypted volumes with their details
Raises:
requests.exceptions.RequestException: If the API request fails
"""
logging.info(f"EncryptionDetection.list_suspected_volumes(zvm_address={self.client.zvm_address})")
url = f"https://{self.client.zvm_address}/v1/encryptiondetection/suspected/volumes"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.get(url, headers=headers, verify=self.client.verify_certificate)
response.raise_for_status()
result = response.json()
logging.info(f"Successfully retrieved {len(result)} suspected encrypted volumes")
logging.debug(f"EncryptionDetection.list_suspected_volumes result: {json.dumps(result, indent=4)}")
return result
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
+241
View File
@@ -0,0 +1,241 @@
# 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
class Events:
def __init__(self, client):
self.client = client
def list_events(self, event_identifier=None, start_date=None, end_date=None, vpg_identifier=None,
site_name=None, site_identifier=None, zorg_identifier=None, event_type=None,
entity_type=None, category=None, user_name=None, alert_identifier=None):
"""
Fetches a list of events or a specific event from the Zerto API with optional filters.
:param event_identifier: The identifier of the specific event (if fetching a specific event).
:param start_date: The filter interval start date-time (string in date-time format).
:param end_date: The filter interval end date-time (string in date-time format).
:param vpg_identifier: The identifier of the VPG.
:param site_name: The name of the site.
:param site_identifier: The internal ZVM site identifier.
:param zorg_identifier: The identifier of the ZORG.
:param event_type: The event type.
:param entity_type: The entity type to return.
:param category: The event category to return.
:param user_name: The username for which the event occurred.
:param alert_identifier: The alert identifier.
:return: List of events or a specific event based on provided filters.
"""
logging.info(f'Events.list_events(event_identifier={event_identifier}, start_date={start_date}, end_date={end_date}, '
f'vpg_identifier={vpg_identifier}, site_name={site_name}, site_identifier={site_identifier}, '
f'zorg_identifier={zorg_identifier}, event_type={event_type}, entity_type={entity_type}, '
f'category={category}, user_name={user_name}, alert_identifier={alert_identifier})')
# Determine endpoint based on whether event_identifier is provided
if event_identifier:
events_uri = f"https://{self.client.zvm_address}/v1/events/{event_identifier}"
else:
events_uri = f"https://{self.client.zvm_address}/v1/events"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
# Building query parameters
params = {}
if start_date:
params['startDate'] = start_date
if end_date:
params['endDate'] = end_date
if vpg_identifier:
params['vpgIdentifier'] = vpg_identifier
if site_name:
params['siteName'] = site_name
if site_identifier:
params['siteIdentifier'] = site_identifier
if zorg_identifier:
params['zorgIdentifier'] = zorg_identifier
if event_type:
params['eventType'] = event_type
if entity_type:
params['entityType'] = entity_type
if category:
params['category'] = category
if user_name:
params['userName'] = user_name
if alert_identifier:
params['alertIdentifier'] = alert_identifier
try:
logging.info("Fetching events with specified filters...")
response = requests.get(events_uri, headers=headers, params=params, verify=self.client.verify_certificate)
response.raise_for_status()
events = response.json()
if not events:
logging.warning("No events found.")
return []
return events
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
except Exception as e:
logging.error(f"Unexpected error: {e}")
raise
def list_event_types(self):
"""
Fetches a list of event types from the Zerto API.
:return: List of event types.
"""
logging.info(f'Events.list_event_types(zvm_address={self.client.zvm_address})')
event_types_uri = f"https://{self.client.zvm_address}/v1/events/types"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
logging.info("Fetching event types...")
response = requests.get(event_types_uri, headers=headers, verify=self.client.verify_certificate)
response.raise_for_status()
event_types = response.json()
if not event_types:
logging.warning("No event types found.")
return []
return event_types
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
except Exception as e:
logging.error(f"Unexpected error: {e}")
raise
def list_event_entities(self):
"""
Fetches a list of event entities from the Zerto API.
:return: List of event entities.
"""
logging.info(f'Events.list_event_entities(zvm_address={self.client.zvm_address})')
event_entities_uri = f"https://{self.client.zvm_address}/v1/events/entities"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
logging.info("Fetching event entities...")
response = requests.get(event_entities_uri, headers=headers, verify=self.client.verify_certificate)
response.raise_for_status()
event_entities = response.json()
if not event_entities:
logging.warning("No event entities found.")
return []
return event_entities
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
except Exception as e:
logging.error(f"Unexpected error: {e}")
raise
def list_event_categories(self):
"""
Fetches a list of event categories from the Zerto API.
:return: List of event categories.
"""
logging.info(f'Events.list_event_categories(zvm_address={self.client.zvm_address})')
event_categories_uri = f"https://{self.client.zvm_address}/v1/events/categories"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.get(event_categories_uri, headers=headers, verify=self.client.verify_certificate)
response.raise_for_status()
event_categories = response.json()
if not event_categories:
logging.warning("No event categories found.")
return []
return event_categories
except requests.exceptions.RequestException as e:
logging.error(f"Error fetching event categories: {e}")
return None
url = f"https://{self.client.zvm_address}/v1/events/types"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.get(url, headers=headers, 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
except Exception as e:
logging.error(f"Unexpected error: {e}")
raise
+21
View File
@@ -0,0 +1,21 @@
# 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
class Failover:
def __init__(self, client):
self.client = client
def failover(self, vpg_name, checkpoint_identifier=None, vm_name_list=None, commit_policy=0, time_to_wait_before_shutdown_sec=3600, shutdown_policy=0, is_reverse_protection=False, sync=None):
# Implementation of failover method
pass
+164
View File
@@ -0,0 +1,164 @@
# 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
class License:
def __init__(self, client):
self.client = client
def get_license(self):
"""
Fetch license information from the Zerto server.
Returns:
dict: The license information from the Zerto server, or an empty dictionary if no content is returned.
"""
logging.info(f'License.get_license(zvm_address={self.client.zvm_address})')
url = f"https://{self.client.zvm_address}/v1/license"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
logging.info("Fetching license information...")
response = requests.get(url, headers=headers, verify=self.client.verify_certificate)
# Handle 204 No Content
if response.status_code == 204:
logging.info("No license information available.")
return {}
# Raise an error for other non-successful HTTP status codes
response.raise_for_status()
# Parse the response JSON
license_info = response.json()
logging.info("Successfully fetched license information.")
return license_info
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 details: {json.dumps(error_details, indent=2)}")
except ValueError:
logging.error(f"Response content: {e.response.text}")
else:
logging.error("HTTPError occurred with no response attached.")
raise
except Exception as e:
logging.error(f"Unexpected error while fetching license information: {e}")
raise
def put_license(self, license_key):
"""
Add a new license or update an existing one on the Zerto server.
Args:
license_key (str): The license key to add or update.
Returns:
dict: The response from the Zerto server, or an empty dictionary if no content is returned.
"""
logging.info(f'License.put_license(zvm_address={self.client.zvm_address}, license_key={license_key})')
url = f"https://{self.client.zvm_address}/v1/license"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
payload = {
"licenseKey": license_key
}
try:
logging.info("Adding or updating license...")
response = requests.put(url, json=payload, headers=headers, verify=self.client.verify_certificate)
# Handle empty response with 200 status code
if response.status_code == 200 and not response.content:
logging.info("License successfully added or updated with no content returned.")
return {}
# Raise an error for other non-successful HTTP status codes
response.raise_for_status()
# Parse the response JSON
response_data = response.json()
logging.info("Successfully added or updated license.")
return response_data
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 details: {json.dumps(error_details, indent=2)}")
except ValueError:
logging.error(f"Response content: {e.response.text}")
else:
logging.error("HTTPError occurred with no response attached.")
raise
except Exception as e:
logging.error(f"Unexpected error while adding or updating license: {e}")
raise
def delete_license(self):
"""
Delete the current license from the Zerto server.
Returns:
dict: The response from the Zerto server.
"""
logging.info(f'License.delete_license(zvm_address={self.client.zvm_address})')
url = f"https://{self.client.zvm_address}/v1/license"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
logging.info("Deleting license...")
response = requests.delete(url, headers=headers, verify=self.client.verify_certificate)
# Raise an error for non-successful HTTP status codes
response.raise_for_status()
# Parse the response JSON if available
if response.content:
response_data = response.json()
logging.info("License successfully deleted.")
return response_data
else:
logging.info("License successfully deleted with no content returned.")
return {}
except requests.exceptions.RequestException as e:
logging.error(f"HTTPError: {e.response.status_code} - {e.response.reason}")
try:
error_details = e.response.json()
logging.error(f"Error details: {json.dumps(error_details, indent=2)}")
except ValueError:
logging.error(f"Response content: {e.response.text}")
raise
except Exception as e:
logging.error(f"Unexpected error while deleting license: {e}")
raise
+151
View File
@@ -0,0 +1,151 @@
# 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
class LocalSite:
def __init__(self, zvm_address, token):
self.zvm_address = zvm_address
self.token = token
self.headers = {
"Authorization": f"Bearer {self.token}",
"Accept": "application/json",
"Content-Type": "application/json"
}
def get_local_site(self):
logging.info("LocalSite.get_local_site: Fetching local site information...")
url = f"https://{self.zvm_address}/v1/localsite"
try:
response = requests.get(url, headers=self.headers, verify=False)
response.raise_for_status()
logging.info("LocalSite.get_local_site: Successfully retrieved local site information.")
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
except Exception as e:
logging.error(f"Unexpected error: {e}")
raise
def get_pairing_statuses(self):
logging.info("LocalSite.get_pairing_statuses: Fetching pairing statuses...")
url = f"https://{self.zvm_address}/v1/localsite/pairingstatuses"
try:
response = requests.get(url, headers=self.headers, verify=False)
response.raise_for_status()
logging.info("LocalSite.get_pairing_statuses: Successfully retrieved pairing statuses.")
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
except Exception as e:
logging.error(f"Unexpected error: {e}")
raise
def send_usage(self):
logging.info("LocalSite.send_usage: Sending local site billing usage...")
url = f"https://{self.zvm_address}/v1/localsite/billing/sendUsage"
try:
response = requests.post(url, headers=self.headers, verify=False)
response.raise_for_status()
if response.content.strip():
logging.info("LocalSite.send_usage: Successfully sent billing usage data.")
return response.json()
else:
logging.info("LocalSite.send_usage: Successfully sent billing usage data. No content returned.")
return 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
except Exception as e:
logging.error(f"Unexpected error: {e}")
raise
def get_login_banner(self):
logging.info("LocalSite.get_login_banner: Fetching login banner settings...")
url = f"https://{self.zvm_address}/v1/localsite/settings/loginBanner"
try:
response = requests.get(url, headers=self.headers, verify=False)
response.raise_for_status()
logging.info("LocalSite.get_login_banner: Successfully retrieved login banner settings.")
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
except Exception as e:
logging.error(f"Unexpected error: {e}")
raise
def set_login_banner(self, is_enabled, banner_text):
logging.info("LocalSite.set_login_banner: Setting login banner settings...")
url = f"https://{self.zvm_address}/v1/localsite/settings/loginBanner"
payload = {
"isLoginBannerEnabled": is_enabled,
"loginBanner": banner_text
}
try:
response = requests.put(url, headers=self.headers, json=payload, verify=False)
response.raise_for_status()
logging.info("LocalSite.set_login_banner: Successfully set login banner settings.")
return response
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
except Exception as e:
logging.error(f"Unexpected error: {e}")
raise
+34
View File
@@ -0,0 +1,34 @@
# 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 argparse
import logging
from zvml import ZVMLClient
def main():
parser = argparse.ArgumentParser(description="zvml Client")
parser.add_argument("--zvm_address", required=True, help="ZVM address")
parser.add_argument("--username", required=True, help="Username")
parser.add_argument("--password", required=True, help="Password")
args = parser.parse_args()
logging.basicConfig(level=logging.INFO)
try:
client = ZVMLClient(zvm_address=args.zvm_address, username=args.username, password=args.password)
# Example usage
vpgs = client.vpgs.list_vpgs()
logging.info(f"VPGs: {vpgs}")
except Exception as e:
logging.error(f"Error: {e}")
if __name__ == "__main__":
main()
+275
View File
@@ -0,0 +1,275 @@
# 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 time
from .tasks import Tasks
class PeerSites:
def __init__(self, client):
self.client = client
self.tasks = Tasks(client)
def get_peer_sites(self):
"""
Get details of all peer sites paired with this site. (Auth)
Returns:
list: List of peer sites
"""
url = f"https://{self.client.zvm_address}/v1/peersites"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
logging.info("PeerSites.get_peer_sites: Fetching all peer sites...")
try:
response = requests.get(url, headers=headers, 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
except Exception as e:
logging.error(f"Unexpected error: {e}")
raise
def pair_site(self, hostname, token, port=9071, sync=True):
"""
Pairs this site with another site. (Auth)
Args:
hostname (str): The IP or DNS name for the peer site
token (str): The pairing token generated from the peer site
port (int, optional): The port used to access the peer site. Defaults to 9071.
sync (bool, optional): Wait for the pairing task to complete. Defaults to True.
Returns:
dict: Pairing result if sync=False, or final task status if sync=True
"""
url = f"https://{self.client.zvm_address}/v1/peersites"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
pairing_data = {
"hostName": hostname,
"port": port,
"token": token
}
logging.info(f"PeerSites.pair_site: Pairing with site {hostname} at port {port}...")
try:
response = requests.post(url, headers=headers, json=pairing_data, verify=self.client.verify_certificate)
response.raise_for_status()
if not sync:
return response.json() if response.content else None
# Get the task identifier from the response
task_id = response.json()
logging.info(f"PeerSites.pair_site pairing submitted, task_id={task_id}")
if sync:
# Wait for task completion
self.tasks.wait_for_task_completion(task_id, timeout=30, interval=5)
return task_id
return task_id
except requests.exceptions.RequestException as e:
logging.error(f"Error pairing site: {str(e)}")
if hasattr(e, 'response') and e.response is not None:
logging.error(f"Response content: {e.response.text}")
raise
def delete_peer_site(self, site_identifier, sync=True):
"""
Unpairs this site with another site. (Auth)
Args:
site_identifier (str): The identifier of the peer site to delete
sync (bool, optional): Wait for the pairing task to complete. Defaults to True.
"""
url = f"https://{self.client.zvm_address}/v1/peersites/{site_identifier}"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
logging.info(f"PeerSites.delete_peer_site: Deleting peer site {site_identifier}...")
try:
response = requests.delete(url, headers=headers, verify=self.client.verify_certificate)
response.raise_for_status()
if not sync:
return response.json() if response.content else None
# Get the task identifier from the response
task_id = response.json()
logging.info(f"PeerSites.delete_peer_site unpairing submitted, task_id={task_id}")
if sync:
# Wait for task completion
self.tasks.wait_for_task_completion(task_id, timeout=30, interval=5)
return task_id
return task_id
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
except Exception as e:
logging.error(f"Unexpected error: {e}")
raise
def get_pairing_statuses(self):
"""
Get the list of possible statuses for peer sites pairing. (Auth)
Returns:
list: List of possible pairing statuses
"""
url = f"https://{self.client.zvm_address}/v1/peersites/pairingstatuses"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
logging.info("PeerSites.get_pairing_statuses: Fetching pairing statuses...")
try:
response = requests.get(url, headers=headers, 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
except Exception as e:
logging.error(f"Unexpected error: {e}")
raise
def generate_token(self):
"""
Generate a token to pair with this site. (Auth)
Returns:
str: Generated pairing token
"""
url = f"https://{self.client.zvm_address}/v1/peersites/generatetoken"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
logging.info("PeerSites.generate_token: Generating pairing token...")
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
except Exception as e:
logging.error(f"Unexpected error: {e}")
raise
def get_peer_site(self, site_identifier):
logging.info(f"PeerSites.get_peer_site: Fetching peer site information for site identifier: {site_identifier}...")
url = f"https://{self.client.zvm_address}/v1/peersites/{site_identifier}"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.get(url, headers=headers, verify=self.client.verify_certificate)
response.raise_for_status()
logging.info(f"PeerSites.get_peer_site: Successfully retrieved peer site information for site identifier: {site_identifier}.")
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
except Exception as e:
logging.error(f"Unexpected error: {e}")
raise
def get_peer_site_types(self):
logging.info("PeerSites.get_peer_site_types: Fetching peer site information for site types...")
url = f"https://{self.client.zvm_address}/v1/peersites/types"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.get(url, headers=headers, verify=self.client.verify_certificate)
response.raise_for_status()
logging.info("PeerSites.get_peer_site_types: Successfully retrieved peer site types information.")
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
except Exception as e:
logging.error(f"Unexpected error: {e}")
raise
+255
View File
@@ -0,0 +1,255 @@
# 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 RecoveryReports:
def __init__(self, client):
self.client = client
def get_recovery_reports(self, recovery_operation_identifier=None, page_number=1, page_size=1000,
vpg_name=None, recovery_type=None, state=None, start_time=None, end_time=None):
"""
Generate a recovery report and view information about recovery operations.
Args:
recovery_operation_identifier (str): The identifier of a specific recovery operation. If provided, no other parameters are used.
start_time (str): The filtering interval start date-time.
end_time (str): The filtering interval end date-time.
page_number (int): The page number to retrieve. Default is 1.
page_size (int): The number of reports to display in a single page. Max 1000. Default is 1000.
vpg_name (str): The name of the VPG(s) to filter by. Separate multiple VPGs with commas.
recovery_type (str): The type of recovery operation. Possible values: Failover, FailoverTest, Move.
state (str): The recovery operation state. Possible values: Success, Fail.
Returns:
dict: The response from the ZVM API containing recovery report details.
"""
logging.info(f'RecoveryReports.get_recovery_reports recovery_operation_identifier: {recovery_operation_identifier}, \
start_time: {start_time}, end_time: {end_time}, page_number: {page_number}, \
page_size: {page_size}, vpg_name: {vpg_name}, recovery_type: {recovery_type}, state: {state}')
# Determine the URL based on whether recoveryOperationIdentifier is provided
if recovery_operation_identifier:
base_url = f"https://{self.client.zvm_address}/v1/reports/recovery/{recovery_operation_identifier}"
params = None # No query parameters for this endpoint
else:
base_url = f"https://{self.client.zvm_address}/v1/reports/recovery"
# Parameters for the request
params = {
"startTime": start_time,
"endTime": end_time,
"pageNumber": page_number,
"pageSize": page_size,
}
# Optional parameters
if vpg_name:
params["vpgName"] = vpg_name
if recovery_type:
params["recoveryType"] = recovery_type
if state:
params["state"] = state
# Headers for the request
headers = {
"Authorization": f"Bearer {self.client.token}",
"Accept": "application/json",
}
try:
response = requests.get(base_url, headers=headers, params=params, verify=self.client.verify_certificate)
if response.status_code == 200:
# logging.info(f"Successfully retrieved recovery reports = {json.dumps(response.json(), indent=4)}")
return response.json()
else:
logging.error(f"Failed to fetch recovery reports. Status Code: {response.status_code}, Response: {response.text}")
response.raise_for_status()
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
except Exception as e:
logging.error(f"Unexpected error: {e}")
raise
def list_resource_reports(self, start_time=None, end_time=None, page_number=None, page_size=None,
zorg_name=None, vpg_name=None, vm_name=None, protected_site_name=None,
protected_cluster_name=None, protected_host_name=None, protected_org_vdc=None,
protected_vcd_org=None, recovery_site_name=None, recovery_cluster_name=None,
recovery_host_name=None, recovery_org_vdc=None, recovery_vcd_org=None):
"""
Fetch resource reports with optional filters.
Args:
start_time (str): The filtering interval start date-time.
end_time (str): The filtering interval end date-time.
page_number (int): The page number to retrieve.
page_size (int): The number of reports per page (max 1000).
zorg_name (str): The name of the ZORG in the Zerto Cloud Manager.
vpg_name (str): The name of the VPG.
vm_name (str): The name of the virtual machine.
protected_site_name (str): The name of the protected site.
protected_cluster_name (str): The name of the protected cluster.
protected_host_name (str): The name of the protected host.
protected_org_vdc (str): The name of the protected VDC organization.
protected_vcd_org (str): The name of the protected VCD organization.
recovery_site_name (str): The name of the recovery site.
recovery_cluster_name (str): The name of the recovery cluster.
recovery_host_name (str): The name of the recovery host.
recovery_org_vdc (str): The name of the recovery VDC organization.
recovery_vcd_org (str): The name of the recovery VCD organization.
Returns:
list: A list of resource reports based on the provided filters.
"""
logging.info(f"list_resource_reports(start_time={start_time}, end_time={end_time}, page_number={page_number}, "
f"page_size={page_size}, zorg_name={zorg_name}, vpg_name={vpg_name}, vm_name={vm_name}, "
f"protected_site_name={protected_site_name}, protected_cluster_name={protected_cluster_name}, "
f"protected_host_name={protected_host_name}, protected_org_vdc={protected_org_vdc}, "
f"protected_vcd_org={protected_vcd_org}, recovery_site_name={recovery_site_name}, "
f"recovery_cluster_name={recovery_cluster_name}, recovery_host_name={recovery_host_name}, "
f"recovery_org_vdc={recovery_org_vdc}, recovery_vcd_org={recovery_vcd_org})")
uri = f"https://{self.client.zvm_address}/v1/reports/resources"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
# Building query parameters
params = {}
if start_time:
params['startTime'] = start_time
if end_time:
params['endTime'] = end_time
if page_number is not None:
params['pageNumber'] = page_number
if page_size is not None:
params['pageSize'] = page_size
if zorg_name:
params['zorgName'] = zorg_name
if vpg_name:
params['vpgName'] = vpg_name
if vm_name:
params['vmName'] = vm_name
if protected_site_name:
params['protectedSiteName'] = protected_site_name
if protected_cluster_name:
params['protectedClusterName'] = protected_cluster_name
if protected_host_name:
params['protectedHostName'] = protected_host_name
if protected_org_vdc:
params['protectedOrgVdc'] = protected_org_vdc
if protected_vcd_org:
params['protectedVcdOrg'] = protected_vcd_org
if recovery_site_name:
params['recoverySiteName'] = recovery_site_name
if recovery_cluster_name:
params['recoveryClusterName'] = recovery_cluster_name
if recovery_host_name:
params['recoveryHostName'] = recovery_host_name
if recovery_org_vdc:
params['recoveryOrgVdc'] = recovery_org_vdc
if recovery_vcd_org:
params['recoveryVcdOrg'] = recovery_vcd_org
try:
response = requests.get(uri, headers=headers, params=params, verify=self.client.verify_certificate)
response.raise_for_status()
reports = response.json()
if not reports:
logging.warning("No resource reports found.")
return []
return reports
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
except Exception as e:
logging.error(f"Unexpected error: {e}")
raise
def get_latest_failover_test_report(self, vpg_name):
"""
Get the most recent failover test report for a specific VPG.
Args:
vpg_name (str): The name of the VPG to get the report for.
Returns:
dict: The most recent failover test report for the VPG, or None if no reports found.
"""
logging.info(f"RecoveryReports.get_latest_failover_test_report VPG: {vpg_name}")
try:
# Get all failover test reports for this VPG
reports = self.get_recovery_reports(
vpg_name=vpg_name,
recovery_type="FailoverTest",
page_size=1000 # Adjust if you need more reports
)
if not reports:
logging.warning(f"No failover test reports found for VPG: {vpg_name}")
return None
# Sort reports by StartTime in descending order and get the first one
sorted_reports = sorted(
reports,
key=lambda x: x["General"].get("EndTime", ""),
reverse=True
)
if sorted_reports:
return sorted_reports[0]
return 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
except Exception as e:
logging.error(f"Unexpected error: {e}")
raise
+93
View File
@@ -0,0 +1,93 @@
# 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
class RecoveryScripts:
def __init__(self, client):
self.client = client
def get_recovery_scripts(self):
url = f"https://{self.client.zvm_address}/v1/recoveryscripts"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.get(url, headers=headers, 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
except Exception as e:
logging.error(f"Unexpected error: {e}")
raise
def get_recovery_script(self, script_identifier):
url = f"https://{self.client.zvm_address}/v1/recoveryscripts/{script_identifier}"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.get(url, headers=headers, 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
except Exception as e:
logging.error(f"Unexpected error: {e}")
raise
def get_recovery_script_types(self):
url = f"https://{self.client.zvm_address}/v1/recoveryscripts/types"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.get(url, headers=headers, 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
except Exception as e:
logging.error(f"Unexpected error: {e}")
raise
+95
View File
@@ -0,0 +1,95 @@
# 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
class Repositories:
def __init__(self, client):
self.client = client
def get_repositories(self):
url = f"https://{self.client.zvm_address}/v1/repositories"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.get(url, headers=headers, 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
except Exception as e:
logging.error(f"Unexpected error: {e}")
raise
def get_repository(self, repository_identifier):
url = f"https://{self.client.zvm_address}/v1/repositories/{repository_identifier}"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.get(url, headers=headers, 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
except Exception as e:
logging.error(f"Unexpected error: {e}")
raise
def get_repository_types(self):
url = f"https://{self.client.zvm_address}/v1/repositories/types"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.get(url, headers=headers, 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
except Exception as e:
logging.error(f"Unexpected error: {e}")
raise
+72
View File
@@ -0,0 +1,72 @@
# 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
from enum import Enum
class DateTimeFormat(Enum):
"""Enum for date time format options"""
DEFAULT = "" # Returns full server time info
LOCAL = "serverDateTimeLocal" # Returns local time
UTC = "serverDateTimeUtc" # Returns UTC time
ARGUMENT = "dateTimeArgument" # Returns date time argument format
class ServerDateTime:
def __init__(self, client):
self.client = client
def get_server_date_time(self, format=DateTimeFormat.DEFAULT):
"""
Get the server date and time in the specified format.
Args:
format (DateTimeFormat): The format to return the date-time in.
- DEFAULT: Returns full server time info (timezone, UTC time, local time, offset)
- LOCAL: Returns local time
- UTC: Returns UTC time
- ARGUMENT: Returns date time argument format
Returns:
dict/str: Server date-time information. Format depends on the format parameter:
- DEFAULT: {
'TimeZone': str,
'ServerTimeUtc': str,
'LocalTime': str,
'TimeOffset': str
}
- LOCAL: Local time string
- UTC: UTC time string
- ARGUMENT: Date time argument format string
"""
logging.info(f"ServerDateTime.get_server_date_time: Fetching server date and time in {format.name} format...")
# Build the URL based on the format
base_url = f"https://{self.client.zvm_address}/v1/serverDateTime"
url = base_url if format == DateTimeFormat.DEFAULT else f"{base_url}/{format.value}"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.get(url, headers=headers, verify=self.client.verify_certificate)
response.raise_for_status()
server_time = response.json()
logging.info(f"Successfully retrieved server date and time in {format.name} format")
return server_time
except requests.exceptions.RequestException as e:
logging.error(f"Failed to get server date and time: {e}")
if hasattr(e.response, 'text'):
logging.error(f"Error response: {e.response.text}")
raise
+61
View File
@@ -0,0 +1,61 @@
# 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
class ServiceProfiles:
def __init__(self, client):
self.client = client
def get_service_profiles(self, site_identifier=None):
"""
Get the list of all service profiles for the site.
Args:
site_identifier (str, optional): The identifier of the site for which service profiles
should be returned.
Returns:
list: List of service profiles with their details including:
- serviceProfileName: Name of the service profile
- rpo: Recovery Point Objective
- history: Journal history length
- maxJournalSizeInPercent: Maximum journal size as percentage
- testInterval: Test interval period
- description: Service profile description
"""
logging.info(f"ServiceProfiles.get_service_profiles: Fetching service profiles...")
url = f"https://{self.client.zvm_address}/v1/serviceprofiles"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
params = {}
if site_identifier:
params['siteIdentifier'] = site_identifier
logging.info(f"Filtering service profiles for site: {site_identifier}")
try:
response = requests.get(url, headers=headers, params=params, verify=self.client.verify_certificate)
response.raise_for_status()
profiles = response.json()
logging.info(f"Successfully retrieved {len(profiles)} service profiles")
return profiles
except requests.exceptions.RequestException as e:
logging.error(f"Failed to get service profiles: {e}")
if hasattr(e.response, 'text'):
logging.error(f"Error response: {e.response.text}")
raise
+59
View File
@@ -0,0 +1,59 @@
# 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
class Sessions:
def __init__(self, client):
self.client = client
def get_sessions(self):
url = f"https://{self.client.zvm_address}/v1/sessions"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.get(url, headers=headers, verify=self.client.verify_certificate)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
logging.error(f"Failed to get sessions: {e}")
raise
def get_session(self, session_identifier):
url = f"https://{self.client.zvm_address}/v1/sessions/{session_identifier}"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.get(url, headers=headers, verify=self.client.verify_certificate)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
logging.error(f"Failed to get session: {e}")
raise
def get_session_types(self):
url = f"https://{self.client.zvm_address}/v1/sessions/types"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.get(url, headers=headers, verify=self.client.verify_certificate)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
logging.error(f"Failed to get session types: {e}")
raise
+57
View File
@@ -0,0 +1,57 @@
# 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 time
from .common import ZertoTaskStates
class Tasks:
def __init__(self, client):
self.client = client
def wait_for_task_completion(self, task_identifier, timeout=600, interval=5, expected_task_state: ZertoTaskStates = ZertoTaskStates.Completed):
logging.debug(f'wait_for_task_completion(zvm_address={self.client.zvm_address}, task_identifier={task_identifier}, timeout={timeout}, interval={interval})')
start_time = time.time()
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
while True:
# Check if we've exceeded the timeout
if time.time() - start_time > timeout:
logging.error(f'Task ID={task_identifier} timed out after {timeout} seconds')
raise TimeoutError(f"Task did not complete within {timeout} seconds")
url = f"https://{self.client.zvm_address}/v1/tasks/{task_identifier}"
try:
response = requests.get(url, headers=headers, verify=self.client.verify_certificate)
response.raise_for_status()
task_info = response.json()
state = task_info.get("Status", {}).get("State", -1)
progress = task_info.get("Status", {}).get("Progress", 0)
logging.debug(f'Task response: status={ZertoTaskStates.get_name_by_value(state)}, progress={progress}')
if state == expected_task_state.value and progress == 100:
logging.info("Task completed successfully.")
time.sleep(interval)
return task_info
elif state == ZertoTaskStates.InProgress.value:
time.sleep(interval)
continue
else:
logging.error(f'Task ID={task_identifier} failed. task state={ZertoTaskStates.get_name_by_value(state)}')
raise Exception(f"Task failed: {task_info.get('CompleteReason', 'No reason provided')}")
except requests.exceptions.RequestException as e:
logging.error(f"Request failed: {e}")
raise
+179
View File
@@ -0,0 +1,179 @@
# 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
from typing import Dict, List, Optional, Any
from zvml.common import ZertoTweakType
class Tweaks:
def __init__(self, client):
self.client = client
def list_tweaks(self, tweak_name: Optional[str] = None) -> List[Dict]:
"""List ZVM tweaks.
Args:
tweak_name: Optional name of specific tweak to retrieve
Returns:
List[Dict]: List of ZVM tweaks and their current settings, or a single tweak if name provided
Raises:
requests.exceptions.RequestException: If the API request fails
"""
logging.info(f"Tweaks.list_tweaks(zvm_address={self.client.zvm_address}, tweak_name={tweak_name})")
# Build URL based on whether a specific tweak is requested
base_url = f"https://{self.client.zvm_address}/management/api/tweaks/v1.0/zvmTweaks"
url = f"{base_url}/{tweak_name}" if tweak_name else base_url
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.get(url, headers=headers, verify=self.client.verify_certificate)
response.raise_for_status()
result = response.json()
# If a specific tweak was requested, wrap the result in a list for consistent return type
if tweak_name:
result = [result]
logging.info(f"Successfully retrieved {len(result)} ZVM tweak(s)")
logging.debug(f"Tweaks.list_tweaks result: {json.dumps(result, indent=4)}")
return result
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 set_tweak(self, tweak_name: str, value: Any, tweak_type: ZertoTweakType = ZertoTweakType.ZVM, comment: str = "Changed from API") -> Dict:
"""Set a ZVM tweak value.
Args:
tweak_name: Name of the tweak to update
value: New value for the tweak
tweak_type: Type of tweak (ZVM, VRA, or Frontend)
comment: Optional comment for the change
Returns:
Dict: Updated tweak information
Raises:
requests.exceptions.RequestException: If the API request fails
"""
logging.info(f"Tweaks.set_tweak(zvm_address={self.client.zvm_address}, tweak_name={tweak_name}, value={value}, type={tweak_type.value})")
url = f"https://{self.client.zvm_address}/management/api/tweaks/v1/zvmTweaks"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
payload = {
"name": tweak_name,
"type": tweak_type.value,
"value": str(value),
"comment": comment
}
logging.info(f"Tweaks.set_tweak payload: {payload}")
logging.info(f"Tweaks.set_tweak url: {url}")
try:
response = requests.post(url, headers=headers, json=payload, verify=self.client.verify_certificate)
# Log the raw response for debugging
logging.debug(f"Raw response status: {response.status_code}")
logging.debug(f"Raw response headers: {dict(response.headers)}")
logging.debug(f"Raw response content: {response.text}")
response.raise_for_status()
try:
result = response.json()
logging.info(f"Successfully updated tweak {tweak_name}")
logging.debug(f"Tweaks.set_tweak result: {json.dumps(result, indent=4)}")
return result
except ValueError:
# If response is not JSON but request was successful
logging.info(f"Successfully updated tweak {tweak_name} (no JSON response)")
return {"status": "success", "name": tweak_name}
except requests.exceptions.RequestException as e:
logging.error(f"Error setting tweak {tweak_name}")
if hasattr(e, 'response') and e.response is not None:
logging.error(f"Status code: {e.response.status_code}")
logging.error(f"Response text: {e.response.text}")
try:
error_json = e.response.json()
logging.error(f"Error details: {json.dumps(error_json, indent=2)}")
except ValueError:
logging.error("Could not parse error response as JSON")
else:
logging.error(f"Request failed: {str(e)}")
raise
def delete_tweak(self, tweak_name: str) -> None:
"""Delete a ZVM tweak.
Args:
tweak_name: Name of the tweak to delete
Raises:
requests.exceptions.RequestException: If the API request fails
"""
logging.info(f"Tweaks.delete_tweak(zvm_address={self.client.zvm_address}, tweak_name={tweak_name})")
url = f"https://{self.client.zvm_address}/management/api/tweaks/v1/zvmTweaks/{tweak_name}"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
logging.info(f"Tweaks.delete_tweak url: {url}")
try:
response = requests.delete(url, headers=headers, verify=self.client.verify_certificate)
# Log the raw response for debugging
logging.debug(f"Raw response status: {response.status_code}")
logging.debug(f"Raw response headers: {dict(response.headers)}")
logging.debug(f"Raw response content: {response.text}")
response.raise_for_status()
logging.info(f"Successfully deleted tweak {tweak_name}")
except requests.exceptions.RequestException as e:
logging.error(f"Error deleting tweak {tweak_name}")
if hasattr(e, 'response') and e.response is not None:
logging.error(f"Status code: {e.response.status_code}")
logging.error(f"Response text: {e.response.text}")
try:
error_json = e.response.json()
logging.error(f"Error details: {json.dumps(error_json, indent=2)}")
except ValueError:
logging.error("Could not parse error response as JSON")
else:
logging.error(f"Request failed: {str(e)}")
raise
+989
View File
@@ -0,0 +1,989 @@
# 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
class VirtualizationSites:
def __init__(self, client):
self.client = client
def get_virtualization_sites(self, site_identifier=None):
"""
Get virtualization sites information. (Auth)
Args:
site_identifier (str, optional): The identifier of the site to get details for.
If not provided, returns all sites.
Endpoints:
- /v1/virtualizationsites (when site_identifier is None)
- /v1/virtualizationsites/{siteIdentifier} (when site_identifier is provided)
Returns:
dict or list: Site details if site_identifier is provided, otherwise array of all sites
"""
url = f"https://{self.client.zvm_address}/v1/virtualizationsites"
if site_identifier:
url = f"{url}/{site_identifier}"
logging.info(f"VirtualizationSites.get_virtualization_sites: Fetching site {site_identifier}...")
else:
logging.info("VirtualizationSites.get_virtualization_sites: Fetching all virtualization sites...")
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.get(url, headers=headers, 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 get_virtualization_site_vms(self, site_identifier):
"""
Get a list of unprotected VMs from the specified site. (Auth)
Args:
site_identifier (str): The identifier of the site to get VMs from.
Endpoint:
/v1/virtualizationsites/{siteIdentifier}/vms
Returns:
list: Array of unprotected VMs in the specified site
Raises:
requests.exceptions.RequestException: If the API request fails
"""
url = f"https://{self.client.zvm_address}/v1/virtualizationsites/{site_identifier}/vms"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
logging.info(f"VirtualizationSites.get_virtualization_site_vms: Fetching VMs for site {site_identifier}...")
try:
response = requests.get(url, headers=headers, 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 get_virtualization_site_vcd_vapps(self, site_identifier):
"""
Get a list of unprotected VCD vApps from the specified site. (Auth)
Args:
site_identifier (str): The identifier of the site to get VCD vApps from.
Endpoint:
/v1/virtualizationsites/{siteIdentifier}/vcdvapps
Returns:
list: Array of unprotected VCD vApps in the specified site
Raises:
requests.exceptions.RequestException: If the API request fails
"""
url = f"https://{self.client.zvm_address}/v1/virtualizationsites/{site_identifier}/vcdvapps"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
logging.info(f"VirtualizationSites.get_virtualization_site_vcd_vapps: Fetching VCD vApps for site {site_identifier}...")
try:
response = requests.get(url, headers=headers, 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 get_virtualization_site_datastores(self, site_identifier):
"""
Get a list of datastores from the specified site. (Auth)
Args:
site_identifier (str): The identifier of the site to get datastores from.
Endpoint:
/v1/virtualizationsites/{siteIdentifier}/datastores
Returns:
list: Array of datastores in the specified site
Raises:
requests.exceptions.RequestException: If the API request fails
"""
url = f"https://{self.client.zvm_address}/v1/virtualizationsites/{site_identifier}/datastores"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
logging.info(f"VirtualizationSites.get_virtualization_site_datastores: Fetching datastores for site {site_identifier}...")
try:
response = requests.get(url, headers=headers, 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 get_virtualization_site_folders(self, site_identifier):
"""
Get a list of folders from the specified site. (Auth)
Args:
site_identifier (str): The identifier of the site to get folders from.
Endpoint:
/v1/virtualizationsites/{siteIdentifier}/folders
Returns:
list: Array of folders in the specified site
Raises:
requests.exceptions.RequestException: If the API request fails
"""
url = f"https://{self.client.zvm_address}/v1/virtualizationsites/{site_identifier}/folders"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
logging.info(f"VirtualizationSites.get_virtualization_site_folders: Fetching folders for site {site_identifier}...")
try:
response = requests.get(url, headers=headers, 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 get_virtualization_site_datastore_clusters(self, site_identifier):
"""
Get a list of datastore clusters from the specified site. (Auth)
Args:
site_identifier (str): The identifier of the site to get datastore clusters from.
Endpoint:
/v1/virtualizationsites/{siteIdentifier}/datastoreclusters
Returns:
list: Array of datastore clusters in the specified site
Raises:
requests.exceptions.RequestException: If the API request fails
"""
url = f"https://{self.client.zvm_address}/v1/virtualizationsites/{site_identifier}/datastoreclusters"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
logging.info(f"VirtualizationSites.get_virtualization_site_datastore_clusters: Fetching datastore clusters for site {site_identifier}...")
try:
response = requests.get(url, headers=headers, 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 get_virtualization_site_resource_pools(self, site_identifier):
"""
Get a list of resource pools from the specified site. (Auth)
Args:
site_identifier (str): The identifier of the site to get resource pools from.
Endpoint:
/v1/virtualizationsites/{siteIdentifier}/resourcepools
Returns:
list: Array of resource pools in the specified site
Raises:
requests.exceptions.RequestException: If the API request fails
"""
url = f"https://{self.client.zvm_address}/v1/virtualizationsites/{site_identifier}/resourcepools"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
logging.info(f"VirtualizationSites.get_virtualization_site_resource_pools: Fetching resource pools for site {site_identifier}...")
try:
response = requests.get(url, headers=headers, 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 get_virtualization_site_org_vdcs(self, site_identifier):
"""
Get a list of organization VDCs (Virtual Data Centers) from the specified site. (Auth)
Args:
site_identifier (str): The identifier of the site to get org VDCs from.
Endpoint:
/v1/virtualizationsites/{siteIdentifier}/orgvdcs
Returns:
list: Array of organization VDCs in the specified site
Raises:
requests.exceptions.RequestException: If the API request fails
"""
url = f"https://{self.client.zvm_address}/v1/virtualizationsites/{site_identifier}/orgvdcs"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
logging.info(f"VirtualizationSites.get_virtualization_site_org_vdcs: Fetching org VDCs for site {site_identifier}...")
try:
response = requests.get(url, headers=headers, 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 get_virtualization_site_networks(self, site_identifier):
"""
Get a list of networks from the specified site. (Auth)
Args:
site_identifier (str): The identifier of the site to get networks from.
Endpoint:
/v1/virtualizationsites/{siteIdentifier}/networks
Returns:
list: Array of networks in the specified site
Raises:
requests.exceptions.RequestException: If the API request fails
"""
url = f"https://{self.client.zvm_address}/v1/virtualizationsites/{site_identifier}/networks"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
logging.info(f"VirtualizationSites.get_virtualization_site_networks: Fetching networks for site {site_identifier}...")
try:
response = requests.get(url, headers=headers, 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 get_virtualization_site_hosts(self, site_identifier, host_identifier=None):
"""
Get hosts information from the specified site. (Auth)
Args:
site_identifier (str): The identifier of the site to get hosts from.
host_identifier (str, optional): The identifier of a specific host to get details for.
If not provided, returns all hosts.
Endpoints:
- /v1/virtualizationsites/{siteIdentifier}/hosts (when host_identifier is None)
- /v1/virtualizationsites/{siteIdentifier}/hosts/{hostIdentifier} (when host_identifier is provided)
Returns:
list or dict: Array of hosts if host_identifier is None, otherwise details of specific host
Raises:
requests.exceptions.RequestException: If the API request fails
"""
url = f"https://{self.client.zvm_address}/v1/virtualizationsites/{site_identifier}/hosts"
if host_identifier:
url = f"{url}/{host_identifier}"
logging.info(f"VirtualizationSites.get_virtualization_site_hosts: Fetching host {host_identifier} from site {site_identifier}...")
else:
logging.info(f"VirtualizationSites.get_virtualization_site_hosts: Fetching all hosts for site {site_identifier}...")
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.get(url, headers=headers, 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 get_virtualization_site_repositories(self, site_identifier):
"""
Get a list of repositories from the specified site. (Auth)
Args:
site_identifier (str): The identifier of the site to get repositories from.
Endpoint:
/v1/virtualizationsites/{siteIdentifier}/repositories
Returns:
list: Array of repositories in the specified site
Raises:
requests.exceptions.RequestException: If the API request fails
"""
url = f"https://{self.client.zvm_address}/v1/virtualizationsites/{site_identifier}/repositories"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
logging.info(f"VirtualizationSites.get_virtualization_site_repositories: Fetching repositories for site {site_identifier}...")
try:
response = requests.get(url, headers=headers, 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 get_virtualization_site_host_clusters(self, site_identifier):
"""
Get a list of host clusters from the specified site. (Auth)
Args:
site_identifier (str): The identifier of the site to get host clusters from.
Endpoint:
/v1/virtualizationsites/{siteIdentifier}/hostclusters
Returns:
list: Array of host clusters in the specified site
Raises:
requests.exceptions.RequestException: If the API request fails
"""
url = f"https://{self.client.zvm_address}/v1/virtualizationsites/{site_identifier}/hostclusters"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
logging.info(f"VirtualizationSites.get_virtualization_site_host_clusters: Fetching host clusters for site {site_identifier}...")
try:
response = requests.get(url, headers=headers, 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 get_virtualization_site_org_vdc_networks(self, site_identifier, org_vdc_identifier):
"""
Get a list of networks from the specified organization VDC. (Auth)
Args:
site_identifier (str): The identifier of the site.
org_vdc_identifier (str): The identifier of the organization VDC to get networks from.
Endpoint:
/v1/virtualizationsites/{siteIdentifier}/orgvdcs/{orgVdcIdentifier}/networks
Returns:
list: Array of networks in the specified organization VDC
Raises:
requests.exceptions.RequestException: If the API request fails
"""
url = f"https://{self.client.zvm_address}/v1/virtualizationsites/{site_identifier}/orgvdcs/{org_vdc_identifier}/networks"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
logging.info(f"VirtualizationSites.get_virtualization_site_org_vdc_networks: Fetching networks for org VDC {org_vdc_identifier} in site {site_identifier}...")
try:
response = requests.get(url, headers=headers, 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 get_virtualization_site_org_vdc_storage_policies(self, site_identifier, org_vdc_identifier):
"""
Get a list of storage policies from the specified organization VDC. (Auth)
Args:
site_identifier (str): The identifier of the site.
org_vdc_identifier (str): The identifier of the organization VDC to get storage policies from.
Endpoint:
/v1/virtualizationsites/{siteIdentifier}/orgvdcs/{orgVdcIdentifier}/storagepolicies
Returns:
list: Array of storage policies in the specified organization VDC
Raises:
requests.exceptions.RequestException: If the API request fails
"""
url = f"https://{self.client.zvm_address}/v1/virtualizationsites/{site_identifier}/orgvdcs/{org_vdc_identifier}/storagepolicies"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
logging.info(f"VirtualizationSites.get_virtualization_site_org_vdc_storage_policies: Fetching storage policies for org VDC {org_vdc_identifier} in site {site_identifier}...")
try:
response = requests.get(url, headers=headers, 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 get_virtualization_site_devices(self, site_identifier, host_identifier=None, device_name=None):
"""
Get a list of devices from the specified site. (Auth)
Args:
site_identifier (str): The identifier of the site to get devices from.
host_identifier (str, optional): Filter devices by host identifier.
device_name (str, optional): Filter devices by device name.
Endpoint:
/v1/virtualizationsites/{siteIdentifier}/devices
Query Parameters:
- hostIdentifier (optional)
- deviceName (optional)
Returns:
list: Array of devices in the specified site
Raises:
requests.exceptions.RequestException: If the API request fails
"""
url = f"https://{self.client.zvm_address}/v1/virtualizationsites/{site_identifier}/devices"
# Add query parameters if provided
params = {}
if host_identifier:
params['hostIdentifier'] = host_identifier
if device_name:
params['deviceName'] = device_name
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
logging.info(f"VirtualizationSites.get_virtualization_site_devices: Fetching devices for site {site_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 get_virtualization_site_public_cloud_networks(self, site_identifier):
"""
Get a list of public cloud virtual networks from the specified site. (Auth)
Args:
site_identifier (str): The identifier of the site to get public cloud virtual networks from.
Endpoint:
/v1/virtualizationsites/{siteIdentifier}/publiccloud/virtualNetworks
Returns:
list: Array of public cloud virtual networks in the specified site
Raises:
requests.exceptions.RequestException: If the API request fails
"""
url = f"https://{self.client.zvm_address}/v1/virtualizationsites/{site_identifier}/publiccloud/virtualNetworks"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
logging.info(f"VirtualizationSites.get_virtualization_site_public_cloud_networks: Fetching public cloud virtual networks for site {site_identifier}...")
try:
response = requests.get(url, headers=headers, 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 get_virtualization_site_public_cloud_subnets(self, site_identifier):
"""
Get a list of public cloud subnets from the specified site. (Auth)
Args:
site_identifier (str): The identifier of the site to get public cloud subnets from.
Endpoint:
/v1/virtualizationsites/{siteIdentifier}/publiccloud/subnets
Returns:
list: Array of public cloud subnets in the specified site
Raises:
requests.exceptions.RequestException: If the API request fails
"""
url = f"https://{self.client.zvm_address}/v1/virtualizationsites/{site_identifier}/publiccloud/subnets"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
logging.info(f"VirtualizationSites.get_virtualization_site_public_cloud_subnets: Fetching public cloud subnets for site {site_identifier}...")
try:
response = requests.get(url, headers=headers, 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 get_virtualization_site_public_cloud_security_groups(self, site_identifier):
"""
Get a list of public cloud security groups from the specified site. (Auth)
Args:
site_identifier (str): The identifier of the site to get public cloud security groups from.
Endpoint:
/v1/virtualizationsites/{siteIdentifier}/publiccloud/securityGroups
Returns:
list: Array of public cloud security groups in the specified site
Raises:
requests.exceptions.RequestException: If the API request fails
"""
url = f"https://{self.client.zvm_address}/v1/virtualizationsites/{site_identifier}/publiccloud/securityGroups"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
logging.info(f"VirtualizationSites.get_virtualization_site_public_cloud_security_groups: Fetching public cloud security groups for site {site_identifier}...")
try:
response = requests.get(url, headers=headers, 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 get_virtualization_site_public_cloud_vm_instance_types(self, site_identifier):
"""
Get a list of public cloud VM instance types from the specified site. (Auth)
Args:
site_identifier (str): The identifier of the site to get VM instance types from.
Endpoint:
/v1/virtualizationsites/{siteIdentifier}/publiccloud/vmInstanceTypes
Returns:
list: Array of public cloud VM instance types in the specified site
Raises:
requests.exceptions.RequestException: If the API request fails
"""
url = f"https://{self.client.zvm_address}/v1/virtualizationsites/{site_identifier}/publiccloud/vmInstanceTypes"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
logging.info(f"VirtualizationSites.get_virtualization_site_public_cloud_vm_instance_types: Fetching VM instance types for site {site_identifier}...")
try:
response = requests.get(url, headers=headers, 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 get_virtualization_site_public_cloud_resource_groups(self, site_identifier):
"""
Get a list of public cloud resource groups from the specified site. (Auth)
Args:
site_identifier (str): The identifier of the site to get resource groups from.
Endpoint:
/v1/virtualizationsites/{siteIdentifier}/publiccloud/resourceGroups
Returns:
list: Array of public cloud resource groups in the specified site
Raises:
requests.exceptions.RequestException: If the API request fails
"""
url = f"https://{self.client.zvm_address}/v1/virtualizationsites/{site_identifier}/publiccloud/resourceGroups"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
logging.info(f"VirtualizationSites.get_virtualization_site_public_cloud_resource_groups: Fetching resource groups for site {site_identifier}...")
try:
response = requests.get(url, headers=headers, 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 get_virtualization_site_public_cloud_keys_containers(self, site_identifier):
"""
Get a list of public cloud keys containers from the specified site. (Auth)
Args:
site_identifier (str): The identifier of the site to get keys containers from.
Endpoint:
/v1/virtualizationsites/{siteIdentifier}/publiccloud/keyscontainers
Returns:
list: Array of public cloud keys containers in the specified site
Raises:
requests.exceptions.RequestException: If the API request fails
"""
url = f"https://{self.client.zvm_address}/v1/virtualizationsites/{site_identifier}/publiccloud/keyscontainers"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
logging.info(f"VirtualizationSites.get_virtualization_site_public_cloud_keys_containers: Fetching keys containers for site {site_identifier}...")
try:
response = requests.get(url, headers=headers, 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 get_virtualization_site_public_cloud_encryption_keys(self, site_identifier, encryption_key_id=None):
"""
Get public cloud encryption keys from the specified site. (Auth)
Args:
site_identifier (str): The identifier of the site.
encryption_key_id (str, optional): The identifier of a specific encryption key to retrieve.
If not provided, returns all encryption keys.
Endpoints:
- /v1/virtualizationsites/{siteIdentifier}/publiccloud/encryptionkeys (when encryption_key_id is None)
- /v1/virtualizationsites/{siteIdentifier}/publiccloud/encryptionkeys/{encryptionKeyId} (when encryption_key_id is provided)
Returns:
list or dict: Array of encryption keys if encryption_key_id is None, otherwise details of specific key
Raises:
requests.exceptions.RequestException: If the API request fails
"""
url = f"https://{self.client.zvm_address}/v1/virtualizationsites/{site_identifier}/publiccloud/encryptionkeys"
if encryption_key_id:
url = f"{url}/{encryption_key_id}"
logging.info(f"VirtualizationSites.get_virtualization_site_public_cloud_encryption_keys: Fetching encryption key {encryption_key_id} for site {site_identifier}...")
else:
logging.info(f"VirtualizationSites.get_virtualization_site_public_cloud_encryption_keys: Fetching all encryption keys for site {site_identifier}...")
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.get(url, headers=headers, 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 get_virtualization_site_public_cloud_managed_identities(self, site_identifier):
"""
Get a list of public cloud managed identities from the specified site. (Auth)
Args:
site_identifier (str): The identifier of the site to get managed identities from.
Endpoint:
/v1/virtualizationsites/{siteIdentifier}/publiccloud/managedidentities
Returns:
list: Array of public cloud managed identities in the specified site
Raises:
requests.exceptions.RequestException: If the API request fails
"""
url = f"https://{self.client.zvm_address}/v1/virtualizationsites/{site_identifier}/publiccloud/managedidentities"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
logging.info(f"VirtualizationSites.get_virtualization_site_public_cloud_managed_identities: Fetching managed identities for site {site_identifier}...")
try:
response = requests.get(url, headers=headers, 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 get_virtualization_site_public_cloud_disk_encryption_keys(self, site_identifier):
"""
Get a list of public cloud disk encryption keys from the specified site. (Auth)
Args:
site_identifier (str): The identifier of the site to get disk encryption keys from.
Endpoint:
/v1/virtualizationsites/{siteIdentifier}/publiccloud/diskencryptionkeys
Returns:
list: Array of public cloud disk encryption keys in the specified site
Raises:
requests.exceptions.RequestException: If the API request fails
"""
url = f"https://{self.client.zvm_address}/v1/virtualizationsites/{site_identifier}/publiccloud/diskencryptionkeys"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
logging.info(f"VirtualizationSites.get_virtualization_site_public_cloud_disk_encryption_keys: Fetching disk encryption keys for site {site_identifier}...")
try:
response = requests.get(url, headers=headers, 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
+349
View File
@@ -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
+59
View File
@@ -0,0 +1,59 @@
import requests
import logging
import json
class Volumes:
def __init__(self, client):
self.client = client
def list_volumes(self, volume_type=None, vpg_identifier=None, datastore_identifier=None,
protected_vm_identifier=None, owning_vm_identifier=None):
"""
Get a list of volumes info in the current site. For ZSSP users, the information
retrieved is for Protected entities only. (Auth)
Args:
volume_type (str, optional): The volume type
vpg_identifier (str, optional): The identifier of the VPG
datastore_identifier (str, optional): The identifier of the datastore
protected_vm_identifier (str, optional): The identifier of the protected virtual machine
owning_vm_identifier (str, optional): The identifier of the owning virtual machine
Returns:
list: Array of volume information objects
Raises:
requests.exceptions.RequestException: If the API request fails
"""
url = f"https://{self.client.zvm_address}/v1/volumes"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
params = {
'volumeType': volume_type,
'vpgIdentifier': vpg_identifier,
'datastoreIdentifier': datastore_identifier,
'protectedVmIdentifier': protected_vm_identifier,
'owningVmIdentifier': owning_vm_identifier
}
# Remove None values from params
params = {k: v for k, v in params.items() if v is not None}
logging.info("Volumes.list_volumes: Fetching volumes information")
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
+995
View File
@@ -0,0 +1,995 @@
# 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 time
import json
from .tasks import Tasks
from .common import ZertoVPGStatus, ZertoVPGSubstatus, ZertoProtectedSiteType, ZertoRecoverySiteType, ZertoVPGPriority
from typing import Optional, Union, Dict, List
class VPGs:
def __init__(self, client):
self.client = client
self.tasks = Tasks(client)
def list_vpgs(self,
vpg_name: str = None,
vpg_identifier: str = None,
status: ZertoVPGStatus = None,
sub_status: ZertoVPGSubstatus = None,
protected_site_type: ZertoProtectedSiteType = None,
recovery_site_type: ZertoRecoverySiteType = None,
protected_site_identifier: str = None,
recovery_site_identifier: str = None,
organization_name: str = None,
zorg_identifier: str = None,
priority: ZertoVPGPriority = None,
service_profile_identifier: str = None,
backup_enabled: bool = None) -> Dict | List[Dict]:
"""
Get information about VPGs. If vpg_identifier or vpg_name is provided, returns a single VPG.
Otherwise, returns a list of VPGs that match the filter criteria.
Args:
vpg_name: Get a specific VPG by name
vpg_identifier: Get a specific VPG by identifier
status: Filter by VPG status
sub_status: Filter by VPG sub-status
protected_site_type: The protected site type
recovery_site_type: The recovery site type
protected_site_identifier: The identifier of the protected site
recovery_site_identifier: The identifier of the recovery site
organization_name: Filter by ZORG name
zorg_identifier: Filter by ZORG identifier
priority: Filter by VPG priority
service_profile_identifier: Filter by service profile ID
backup_enabled: Deprecated parameter
Returns:
Dict: When vpg_identifier or vpg_name is provided
List[Dict]: When filtering VPGs without specific identifier
"""
# Construct the base URL
if vpg_identifier:
url = f"https://{self.client.zvm_address}/v1/vpgs/{vpg_identifier}"
else:
url = f"https://{self.client.zvm_address}/v1/vpgs"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
# Only include query parameters if we're not getting a specific VPG
params = {}
if not vpg_identifier:
params = {
'name': vpg_name,
'status': status.get_name_by_value(status.value) if status else None,
'subStatus': sub_status.get_name_by_value(sub_status.value) if sub_status else None,
'protectedSiteType': protected_site_type.get_name_by_value(protected_site_type.value) if protected_site_type else None,
'recoverySiteType': recovery_site_type.get_name_by_value(recovery_site_type.value) if recovery_site_type else None,
'protectedSiteIdentifier': protected_site_identifier,
'recoverySiteIdentifier': recovery_site_identifier,
'organizationName': organization_name,
'zorgIdentifier': zorg_identifier,
'priority': priority.get_name_by_value(priority.value) if priority else None,
'serviceProfileIdentifier': service_profile_identifier,
'backupEnabled': backup_enabled
}
# Remove None values from params
params = {k: v for k, v in params.items() if v is not None}
logging.info(f"VPGs.list_vpgs: Fetching VPGs with parameters:")
if vpg_identifier:
logging.info(f" vpg_identifier: {vpg_identifier}")
for key, value in params.items():
logging.info(f" {key}: {value}")
try:
response = requests.get(
url,
headers=headers,
params=params,
verify=self.client.verify_certificate,
timeout=30
)
response.raise_for_status()
result = response.json()
# If we're querying by name, return the first matching VPG
if vpg_name and isinstance(result, list):
matching_vpg = next((vpg for vpg in result if vpg.get("VpgName") == vpg_name), None)
if matching_vpg:
logging.info(f"Successfully retrieved VPG details for {vpg_name}")
return matching_vpg
logging.warning(f"No VPG found with name {vpg_name}")
return {}
if vpg_identifier:
logging.info(f"Successfully retrieved VPG details for {vpg_identifier}")
else:
logging.info(f"Successfully retrieved {len(result)} VPGs")
return result
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 commit_vpg(self, vpg_settings_id, vpg_name, sync=False, expected_status=ZertoVPGStatus.Initializing, timeout=30, interval=5):
logging.info(f'VPGs.commit_vpg(zvm_address={self.client.zvm_address}, vpg_settings_id={vpg_settings_id}, vpg_name={vpg_name}, sync={sync})')
commit_uri = f"https://{self.client.zvm_address}/v1/vpgSettings/{vpg_settings_id}/commit"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.post(commit_uri, headers=headers, verify=self.client.verify_certificate)
response.raise_for_status()
task_id = response.json()
logging.info(f"VPGSettings {vpg_settings_id} successfully committed, {vpg_name} is created, task_id={task_id}")
if sync:
# Wait for task completion
self.tasks.wait_for_task_completion(task_id, timeout=timeout, interval=interval)
logging.debug('sleeping 5 seconds ...')
self.wait_for_vpg_ready(vpg_name=vpg_name, timeout=30, interval=5, expected_status=expected_status)
return task_id
return task_id
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
except Exception as e:
logging.error(f"Unexpected error: {e}")
raise
def create_vpg(self, basic, journal, recovery, networks, sync=True, status: ZertoVPGStatus = ZertoVPGStatus.Initializing, timeout=30, interval=5):
vpg_name = basic.get("Name")
logging.info(f'VPGs.create_vpg(zvm_address={self.client.zvm_address}, vpg_name={vpg_name}, sync={sync})')
vpg_settings_id = self.create_vpg_settings(basic, journal, recovery, networks, vpg_identifier=None)
return self.commit_vpg(vpg_settings_id, vpg_name, sync, expected_status=status, timeout=timeout, interval=interval)
def wait_for_vpg_ready(self, vpg_name, timeout=180, interval=5, expected_status=ZertoVPGStatus.Initializing):
logging.debug(f'VPGs.wait_for_vpg_ready(zvm_address={self.client.zvm_address}, vpg_name={vpg_name}, timeout={timeout}, interval={interval}, expected_status={ZertoVPGStatus.get_name_by_value(expected_status.value)})')
start_time = time.time()
while True:
time.sleep(interval)
vpg_info = self.list_vpgs(vpg_name=vpg_name)
# get status and convert string into enum
logging.debug(f"VPG status: {vpg_info.get('Status')}")
vpg_status: ZertoVPGStatus = ZertoVPGStatus(vpg_info.get("Status"))
logging.debug(f"Checking VPG status for {vpg_name}: Expected status = {ZertoVPGStatus.get_name_by_value(expected_status.value)}, Current status = {ZertoVPGStatus.get_name_by_value(vpg_status.value)}")
# If VPG is in the expected status or passed the Initializing status too quickly and is in another status
if vpg_status == expected_status or (expected_status == ZertoVPGStatus.Initializing and vpg_status.value > ZertoVPGStatus.Initializing.value):
logging.info(f"VPG {vpg_name} is now in the expected state: {ZertoVPGStatus.get_name_by_value(vpg_status.value)}")
return vpg_info
# Check if the timeout has been reached
elapsed_time = time.time() - start_time
if elapsed_time > timeout:
raise TimeoutError(f"VPG {vpg_name} did not reach the {ZertoVPGStatus.get_name_by_value(expected_status.value)} state within the allotted time. Current status: {ZertoVPGStatus.get_name_by_value(vpg_status.value)}")
def add_vm_to_vpg(self, vpg_name, vm_list_payload):
logging.info(f'VPGs.add_vm_to_vpg(zvm_address={self.client.zvm_address}, vpg_name={vpg_name})')
vpg = self.list_vpgs(vpg_name=vpg_name)
if not vpg:
logging.error(f"VPG with name '{vpg_name}' not found.")
return
vpg_identifier = vpg['VpgIdentifier']
logging.info(f"Found VPG '{vpg_name}' with Identifier: {vpg_identifier}")
new_vpg_settings_id = self.create_vpg_settings(basic=None, journal=None, recovery=None, networks=None, vpg_identifier=vpg_identifier)
logging.info(f"Adding VMs to VPGSettings ID: {new_vpg_settings_id}")
logging.debug(f"VM List Payload: {json.dumps(vm_list_payload, indent=4)}")
vms_uri = f"https://{self.client.zvm_address}/v1/vpgSettings/{new_vpg_settings_id}/vms"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.post(vms_uri, headers=headers, json=vm_list_payload, verify=self.client.verify_certificate)
response.raise_for_status()
logging.info(f"Successfully added VMs to VPG {new_vpg_settings_id}.")
self.commit_vpg(new_vpg_settings_id, vpg_name, sync=True, expected_status=ZertoVPGStatus.Initializing)
return
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
except Exception as e:
logging.error(f"Unexpected error: {e}")
raise
def remove_vm_from_vpg(self, vpg_name, vm_identifier):
logging.info(f'VPGs.remove_vm_from_vpg(zvm_address={self.client.zvm_address}, vpg_name={vpg_name}, vm_identifier={vm_identifier})')
vpg = self.list_vpgs(vpg_name=vpg_name)
if not vpg:
logging.error(f"VPG with name '{vpg_name}' not found.")
return
vpg_id = vpg['VpgIdentifier']
logging.info(f"Found VPG '{vpg_name}' with Identifier: {vpg_id}")
new_vpg_settings_id = self.create_vpg_settings(basic=None, journal=None, recovery=None, networks=None, vpg_identifier=vpg_id)
remove_vm_uri = f"https://{self.client.zvm_address}/v1/vpgSettings/{new_vpg_settings_id}/vms/{vm_identifier}"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.delete(remove_vm_uri, headers=headers, verify=self.client.verify_certificate)
response.raise_for_status()
logging.info(f"VM {vm_identifier} successfully removed from VPG '{vpg_name}' (ID: {new_vpg_settings_id}).")
self.commit_vpg(new_vpg_settings_id, vpg_name, sync=True, expected_status=ZertoVPGStatus.Initializing)
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
except Exception as e:
logging.error(f"Unexpected error: {e}")
raise
def failover_test(self, vpg_name, checkpoint_identifier=None, vm_name_list=None, sync=True):
"""
Initiate a failover test for a given VPG by its name.
:param vpg_name: The name of the VPG.
:param checkpoint_identifier: checkpoint_identifier can be recived by list_checkpoint, if not provided uses the latest checkpoint.
:param vm_name_list: List of
:param options: Optional parameters for the failover test.
:return: Response from the Zerto API.
"""
logging.info(f'VPGs.failover_test(zvm_address={self.client.zvm_address}, vpg_name={vpg_name}, checkpoint_identifier={checkpoint_identifier}, vm_name_list={vm_name_list}, sync={sync})')
# Retrieve the VPG identifier using the VPG name
vpg_info = self.list_vpgs(vpg_name=vpg_name)
vpg_identifier = vpg_info['VpgIdentifier']
logging.debug(f"Found VPG '{vpg_name}' with Identifier: {vpg_identifier}")
url = f"https://{self.client.zvm_address}/v1/vpgs/{vpg_identifier}/FailoverTest"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
payload = {}
if checkpoint_identifier: payload['CheckpointIdentifier'] = checkpoint_identifier
vm_identifier_list = []
if vm_name_list:
for vm in vm_name_list:
vm_info = self.list_vms(vm_name=vm)
if not vm_info:
logging.error (f'failover_test vm={vm} not found')
return
vm_identifier_list.append(vm_info[0]['VmIdentifier'])
payload['VmIdentifiers'] = vm_identifier_list
try:
logging.info(f"Initiating failover test for VPG '{vpg_name}', payload={payload}")
response = requests.post(url, headers=headers, json=payload, verify=self.client.verify_certificate)
response.raise_for_status()
task_id = response.json()
logging.info(f"Failover test initiated for VPG {vpg_name}, task_id = {task_id}")
if sync:
# Wait for task completion
self.tasks.wait_for_task_completion(task_id, timeout=30, interval=5)
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
except Exception as e:
logging.error(f"Unexpected error: {e}")
raise
def stop_failover_test(self, vpg_name, failoverTestSuccess=True, failoverTestSummary=None, sync=True):
"""
Stop a failover test for a given VPG by its name.
:param vpg_name: The name of the VPG.
:param sync: wait until task is completed.
"""
logging.info(f'VPGs.stop_failover_test(zvm_address={self.client.zvm_address}, vpg_name={vpg_name}, sync={sync})')
# Retrieve the VPG identifier using the VPG name
vpg_info = self.list_vpgs(vpg_name=vpg_name)
vpg_identifier = vpg_info['VpgIdentifier']
logging.info(f"Found VPG '{vpg_name}' with Identifier: {vpg_identifier}")
url = f"https://{self.client.zvm_address}/v1/vpgs/{vpg_identifier}/FailoverTestStop"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
body = {
"FailoverTestSuccess": failoverTestSuccess,
"FailoverTestSummary": failoverTestSummary
}
try:
logging.info(f"Stopping failover test for VPG '{vpg_name}'...")
response = requests.post(url, headers=headers, json=body, verify=self.client.verify_certificate)
response.raise_for_status()
task_id = response.json()
logging.info(f"Failover test stopping for VPG {vpg_name}, task_id = {task_id}")
if sync:
# Wait for task completion
self.tasks.wait_for_task_completion(task_id, timeout=30, interval=5)
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
except Exception as e:
logging.error(f"Unexpected error: {e}")
raise
def rollback_failover(self, vpg_name, sync=True):
"""
Rollback failover for a given VPG by its name.
:param vpg_name: The name of the VPG.
:param sync: wait until task is completed.
"""
logging.info(f'VPGs.rollback_failover(zvm_address={self.client.zvm_address}, vpg_name={vpg_name}, sync={sync})')
# Retrieve the VPG identifier using the VPG name
vpg_info = self.list_vpgs(vpg_name=vpg_name)
vpg_identifier = vpg_info['VpgIdentifier']
logging.info(f"Found VPG '{vpg_name}' with Identifier: {vpg_identifier}")
url = f"https://{self.client.zvm_address}/v1/vpgs/{vpg_identifier}/FailoverRollback"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
logging.info(f"Rollback failover for VPG '{vpg_name}'...")
response = requests.post(url, headers=headers, verify=self.client.verify_certificate)
response.raise_for_status()
task_id = response.json()
logging.info(f"Rollback faolover for VPG {vpg_name}, task_id = {task_id}")
if sync:
# Wait for task completion
self.tasks.wait_for_task_completion(task_id, timeout=30, interval=5)
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
except Exception as e:
logging.error(f"Unexpected error: {e}")
raise
def delete_vpg(self, vpg_name, force=False, keep_recovery_volumes=True):
"""
Deletes a VPG by its name.
:param vpg_name: The name of the VPG to delete.
:return: Success message if deleted, else an error message.
"""
logging.info(f"VPGs.delete_vpg(zvm_address={self.client.zvm_address}, vpg_name={vpg_name}, force={force}, keep_recovery_volumes={keep_recovery_volumes})")
# Step 1: Retrieve the VPG details using the VPG name
vpg = self.list_vpgs(vpg_name=vpg_name)
if not vpg:
logging.error(f"No VPG found with the name '{vpg_name}'.")
return
# Get the VPG Identifier
vpg_identifier = vpg.get("VpgIdentifier")
if not vpg_identifier:
logging.error(f"Could not retrieve Identifier for VPG '{vpg_name}'.")
return
# Step 2: Construct the DELETE request URL
delete_vpg_uri = f"https://{self.client.zvm_address}/v1/vpgs/{vpg_identifier}"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
payload = {
"keepRecoveryVolumes": force,
"force": keep_recovery_volumes
}
try:
# Step 3: Send DELETE request
response = requests.delete(delete_vpg_uri, headers=headers, json=payload, verify=self.client.verify_certificate)
response.raise_for_status() # Ensure the request was successful
logging.info(f"Successfully deleted VPG '{vpg_name}' (ID: {vpg_identifier}).")
return f"VPG '{vpg_name}' deleted successfully."
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
except Exception as e:
logging.error(f"Unexpected error: {e}")
raise
# Added methods from VPGSettings
def list_vpg_settings(self):
url = f"https://{self.client.zvm_address}/v1/vpgs/settings"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.get(url, headers=headers, verify=self.client.verify_certificate)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
logging.error(f"Failed to list VPG settings: {e}")
raise
def get_vpg_settings_by_id(self, vpg_settings_id):
url = f"https://{self.client.zvm_address}/v1/vpgSettings/{vpg_settings_id}"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.get(url, headers=headers, verify=self.client.verify_certificate)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
logging.error(f"Failed to get VPG settings by ID: {e}")
raise
def update_vpg_settings(self, vpg_settings_id, payload):
url = f"https://{self.client.zvm_address}/v1/vpgSettings/{vpg_settings_id}"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
logging.info(f"VPGs.update_vpg_settings: Updating VPG settings for ID: {vpg_settings_id}")
logging.debug(f"VPGs.update_vpg_settings: Payload: {json.dumps(payload, indent=4)}")
try:
response = requests.put(url, json=payload, headers=headers, verify=self.client.verify_certificate)
response.raise_for_status()
return response
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 delete_vpg_settings(self, vpg_settings_id):
url = f"https://{self.client.zvm_address}/v1/vpgs/settings/{vpg_settings_id}"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.delete(url, headers=headers, 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 create_vpg_settings(self, basic, journal, recovery, networks, vpg_identifier=None):
logging.info(f'VPGs.create_vpg_settings(zvm_address={self.client.zvm_address}, vpg_identifier={vpg_identifier})')
vpg_settings_uri = f"https://{self.client.zvm_address}/v1/vpgSettings"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
payload = {}
if vpg_identifier:
payload["vpgIdentifier"] = vpg_identifier
if basic:
payload["Basic"] = basic
if journal:
payload["Journal"] = journal
if recovery:
payload["Recovery"] = recovery
if networks:
payload["Networks"] = networks
logging.debug(f"VPGs.create_vpg_settings: Payload: {json.dumps(payload, indent=4)}")
try:
response = requests.post(vpg_settings_uri, headers=headers, json=payload, verify=self.client.verify_certificate)
response.raise_for_status()
vpg_settings_id = response.json()
logging.info(f"VPG Settings ID: {vpg_settings_id} created")
return vpg_settings_id
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
except Exception as e:
logging.error(f"Unexpected error: {e}")
raise
def list_checkpoints(self, vpg_name, start_date=None, endd_date=None, checkpoint_date_str=None, latest=None):
"""
Fetches a list of checkpoints for a specified Virtual Protection Group (VPG).
Parameters:
vpg_name (str): The name of the Virtual Protection Group (VPG) to fetch checkpoints for.
start_date (str): The start date for filtering checkpoints, in ISO 8601 format (e.g., '2024-11-13T00:00:00Z').
endd_date (str): The end date for filtering checkpoints, in ISO 8601 format (e.g., '2024-11-14T00:00:00Z').
checkpoint_date_str (str): A specific date string in the format 'Month Day, Year HH:MM:SS AM/PM'
(e.g., 'November 13, 2024 1:43:02 PM') to search for an exact checkpoint.
latest (bool): If True, returns the checkpoint with the most recent timestamp.
Returns:
dict: A single checkpoint that matches `checkpoint_date_str` or the latest checkpoint if `latest=True`.
list: The full list of checkpoints if neither `checkpoint_date_str` nor `latest` is specified.
None: If no matching checkpoint is found or an error occurs.
Raises:
SystemExit: If a request exception occurs during the API call.
"""
logging.info(f'VPGs.list_checkpoints(vpg_name={vpg_name}, start_date={start_date}, endd_date={endd_date}, checkpoint_date_str={checkpoint_date_str}, latest={latest})')
vpgid = (self.list_vpgs(vpg_name=vpg_name))['VpgIdentifier']
vpgs_uri = f"https://{self.client.zvm_address}/v1/vpgs/{vpgid}/checkpoints"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
params = {
"startDate": start_date,
"endDate": endd_date
}
try:
response = requests.get(vpgs_uri, headers=headers, params=params, verify=self.client.verify_certificate)
response.raise_for_status()
checkpoints = response.json()
if not checkpoints:
logging.warning("No checkpoints found.")
return []
if checkpoint_date_str:
check_point_timestamp = self.__convert_datetime_to_timestamp(date_str = checkpoint_date_str)
matching_checkpoints = next((checkpoint for checkpoint in checkpoints if checkpoint.get("TimeStamp") == check_point_timestamp), None)
if not check_point_timestamp:
logging.warning(f"No checkpoint {checkpoint_date_str} found")
return {}
return matching_checkpoints
if latest:
# Find the checkpoint with the most recent timestamp
latest_checkpoint = max(checkpoints, key=lambda x: x.get("TimeStamp"))
logging.debug(f"Latest checkpoint found: {latest_checkpoint}")
return latest_checkpoint
return checkpoints
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 create_checkpoint(self, checkpoint_name: str, vpg_identifier: str = None, vpg_name: str = None) -> str:
"""
Create a tagged checkpoint for the VPG.
Args:
checkpoint_name: The name/tag to assign to the checkpoint
vpg_identifier: The identifier of the VPG
vpg_name: The name of the VPG (alternative to vpg_identifier)
Returns:
str: The task identifier that can be used to monitor the operation
Raises:
requests.exceptions.RequestException: If the API request fails
ValueError: If neither vpg_identifier nor vpg_name is provided, or if VPG name is not found
"""
if not vpg_identifier and not vpg_name:
raise ValueError("Either vpg_identifier or vpg_name must be provided")
# If vpg_name is provided, get the vpg_identifier
if vpg_name and not vpg_identifier:
vpg = self.list_vpgs(vpg_name=vpg_name)
if not vpg:
raise ValueError(f"VPG with name '{vpg_name}' not found")
vpg_identifier = vpg.get('VpgIdentifier')
logging.info(f"Found VPG identifier '{vpg_identifier}' for VPG name '{vpg_name}'")
url = f"https://{self.client.zvm_address}/v1/vpgs/{vpg_identifier}/checkpoints"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
data = {
"CheckpointName": checkpoint_name
}
logging.info(f"VPGs.create_checkpoint: Creating checkpoint '{checkpoint_name}' for VPG {vpg_identifier}")
try:
response = requests.post(
url,
headers=headers,
json=data,
verify=self.client.verify_certificate,
timeout=30
)
response.raise_for_status()
task_id = response.json()
logging.info(f"Successfully initiated checkpoint creation, task_id={task_id}")
return task_id
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 export_vpg_settings(self, vpg_names: List[str]) -> dict:
"""
Export settings for specified VPGs.
Args:
vpg_names: List of VPG names to export settings for
Returns:
dict: The exported VPG settings in the format:
{
"timeStamp": "2025-02-08T21:50:46.574Z",
"exportResult": {
"result": str,
"message": str
}
}
Raises:
requests.exceptions.RequestException: If the API request fails
"""
url = f"https://{self.client.zvm_address}/v1/vpgSettings/exportSettings"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
payload = {
"vpgNames": vpg_names
}
logging.info(f"VPGs.export_vpg_settings: Exporting settings for VPGs: {vpg_names}")
try:
response = requests.post(url, headers=headers, json=payload, verify=self.client.verify_certificate)
response.raise_for_status()
result = response.json()
logging.info(f"Successfully exported settings for {len(vpg_names)} VPGs at {result.get('timeStamp')}")
logging.debug(f"Export result: {json.dumps(result, indent=2)}")
return result
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_exported_vpg_settings(self) -> List[Dict]:
"""
Get all available exported settings files.
Returns:
List[Dict]: List of exported settings files in the format:
[
{
"timeStamp": "2025-02-08T22:02:18.685Z"
}
]
Raises:
requests.exceptions.RequestException: If the API request fails
"""
url = f"https://{self.client.zvm_address}/v1/vpgSettings/exportedSettings"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
logging.debug("Fetching list of exported VPG settings")
try:
response = requests.get(url, headers=headers, verify=self.client.verify_certificate)
response.raise_for_status()
result = response.json()
logging.info(f"Found {len(result)} exported settings files")
logging.debug(f"Exported settings list: {json.dumps(result, indent=2)}")
return result
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 read_exported_vpg_settings(self, timestamp: str, vpg_names: List[str] = None) -> dict:
"""
Read exported settings from a file of given timestamp.
Args:
timestamp: The timestamp of the exported settings file (format: YYYY-MM-DDThh:mm:ss.SSSZ)
vpg_names: Optional list of VPG names to filter the exported settings
Returns:
dict: The exported VPG settings containing:
- ExportedVpgSettingsApi: List[dict] - List of VPG settings
- ErrorMessage: str - Error message if any
Raises:
requests.exceptions.RequestException: If the API request fails
"""
url = f"https://{self.client.zvm_address}/v1/vpgSettings/exportedSettings/{timestamp}"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
payload = {}
if vpg_names:
payload['vpgNames'] = vpg_names
logging.info(f"VPGs.read_exported_vpg_settings: Reading exported VPG settings for timestamp: {timestamp}")
if vpg_names:
logging.debug(f"Filtering for VPGs: {vpg_names}")
try:
response = requests.post(url, headers=headers, json=payload, verify=self.client.verify_certificate)
response.raise_for_status()
result = response.json()
logging.debug(f"VPGs.read_exported_vpg_settings: result: {json.dumps(result, indent=4)}")
return result
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 import_vpg_settings(self, settings: Dict) -> dict:
"""
Import VPG settings.
Args:
settings: Dictionary containing the VPG settings to import. Must include:
- ExportedVpgSettingsApi: List of VPG settings with detailed configuration
Returns:
dict: The import result containing:
- validationFailedResults: List[dict] - VPGs that failed validation
- vpgName: str - Name of the VPG
- errorMessages: List[str] - List of validation error messages
- importFailedResults: List[dict] - VPGs that failed to import
- vpgName: str - Name of the VPG
- errorMessage: str - Import error message
- importTaskIdentifiers: List[dict] - Successfully initiated imports
- vpgName: str - Name of the VPG
- taskIdentifier: str - Task ID for tracking the import
Raises:
requests.exceptions.RequestException: If the API request fails
ValueError: If settings dictionary is missing required fields
"""
url = f"https://{self.client.zvm_address}/v1/vpgSettings/import"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
# Validate input settings
if not isinstance(settings, dict):
raise ValueError("Settings must be a dictionary")
if 'ExportedVpgSettingsApi' not in settings:
raise ValueError("Settings must contain 'ExportedVpgSettingsApi' key")
# Prepare payload
payload = {
"ExportedVpgSettingsApi": settings['ExportedVpgSettingsApi']
}
logging.info(f"VPGs.import_vpg_settings: Importing settings for {len(settings['ExportedVpgSettingsApi'])} VPGs")
try:
response = requests.post(url, headers=headers, json=payload, verify=self.client.verify_certificate)
response.raise_for_status()
result = response.json()
logging.debug(f"VPGs.import_vpg_settings: result: {json.dumps(result, indent=4)}")
# Log validation failures
if result.get('validationFailedResults'):
for failure in result['validationFailedResults']:
logging.error(f"Validation failed for VPG '{failure['vpgName']}': {', '.join(failure['errorMessages'])}")
# Log import failures
if result.get('importFailedResults'):
for failure in result['importFailedResults']:
logging.error(f"Import failed for VPG '{failure['vpgName']}': {failure['errorMessage']}")
# Log successful imports
if result.get('importTaskIdentifiers'):
for task in result['importTaskIdentifiers']:
logging.info(f"Import initiated for VPG '{task['vpgName']}' with task ID: {task['taskIdentifier']}")
logging.debug(f"Import result: {json.dumps(result, indent=2)}")
return result
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
+668
View File
@@ -0,0 +1,668 @@
# 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
from typing import Dict, List, Optional
class VRA:
def __init__(self, client):
self.client = client
def list_vras(self) -> List[Dict]:
"""List all VRAs."""
logging.info(f"VRA.list_vras(zvm_address={self.client.zvm_address})")
url = f"https://{self.client.zvm_address}/v1/vras"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.get(url, headers=headers, verify=self.client.verify_certificate)
response.raise_for_status()
result = response.json()
logging.info(f"Successfully retrieved {len(result)} VRAs")
logging.debug(f"VRA.list_vras result: {json.dumps(result, indent=4)}")
return result
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 create_vra(self, payload: Dict, sync: bool = True) -> Dict:
"""Create a new VRA.
Args:
payload: The VRA configuration
sync: If True, wait for task completion (default: True)
Returns:
Dict: The creation result
Raises:
requests.exceptions.RequestException: If the API request fails
"""
logging.info(f"VRA.create_vra(zvm_address={self.client.zvm_address}, sync={sync})")
logging.debug(f"VRA.create_vra payload: {json.dumps(payload, indent=4)}")
url = f"https://{self.client.zvm_address}/v1/vras"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.post(url, headers=headers, json=payload, verify=self.client.verify_certificate)
response.raise_for_status()
task_id = response.json()
logging.info("Successfully initiated VRA creation")
logging.debug(f"VRA.create_vra task_id: {task_id}")
if sync:
# Wait for task completion
self.client.tasks.wait_for_task_completion(task_id, timeout=300, interval=5)
return task_id
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 get_vra(self, vra_identifier: str) -> Dict:
"""
Get information about a specific VRA.
Args:
vra_identifier: The identifier of the VRA to retrieve
Returns:
Dict: The VRA information
Raises:
requests.exceptions.RequestException: If the API request fails
"""
logging.info(f"VRA.get_vra(zvm_address={self.client.zvm_address}, vra_identifier={vra_identifier})")
url = f"https://{self.client.zvm_address}/v1/vras/{vra_identifier}"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.get(url, headers=headers, verify=self.client.verify_certificate)
response.raise_for_status()
result = response.json()
logging.info(f"Successfully retrieved VRA information for identifier: {vra_identifier}")
logging.debug(f"VRA.get_vra result: {json.dumps(result, indent=4)}")
return result
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 delete_vra(self, vra_identifier: str, sync: bool = True) -> Dict:
"""
Delete a specific VRA.
Args:
vra_identifier: The identifier of the VRA to delete
sync: If True, wait for task completion (default: True)
Returns:
Dict: The deletion result
Raises:
requests.exceptions.RequestException: If the API request fails
"""
logging.info(f"VRA.delete_vra(zvm_address={self.client.zvm_address}, vra_identifier={vra_identifier})")
url = f"https://{self.client.zvm_address}/v1/vras/{vra_identifier}"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.delete(url, headers=headers, verify=self.client.verify_certificate)
response.raise_for_status()
task_id = response.json()
logging.info(f"Successfully initiated deletion of VRA with identifier: {vra_identifier}")
logging.debug(f"VRA.delete_vra task_id: {task_id}")
if sync:
# Wait for task completion
self.client.tasks.wait_for_task_completion(task_id, timeout=300, interval=5)
return task_id
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 update_vra(self, vra_identifier: str, payload: Dict, sync: bool = True) -> Dict:
"""
Update a specific VRA's configuration.
Args:
vra_identifier: The identifier of the VRA to update
payload: The update configuration
sync: If True, wait for task completion (default: True)
Returns:
Dict: The update result
Raises:
requests.exceptions.RequestException: If the API request fails
"""
logging.info(f"VRA.update_vra(zvm_address={self.client.zvm_address}, vra_identifier={vra_identifier})")
logging.debug(f"VRA.update_vra payload: {json.dumps(payload, indent=4)}")
url = f"https://{self.client.zvm_address}/v1/vras/{vra_identifier}"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.put(url, headers=headers, json=payload, verify=self.client.verify_certificate)
response.raise_for_status()
task_id = response.json()
logging.info(f"Successfully initiated update for VRA with identifier: {vra_identifier}")
logging.debug(f"VRA.update_vra task_id: {task_id}")
if sync:
# Wait for task completion
self.client.tasks.wait_for_task_completion(task_id, timeout=300, interval=5)
return task_id
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 create_vra_cluster(self, payload: Dict, sync: bool = True) -> Dict:
"""
Create a new VRA cluster.
Args:
payload: The cluster configuration
sync: If True, wait for task completion (default: True)
Returns:
Dict: The creation result
Raises:
requests.exceptions.RequestException: If the API request fails
"""
logging.info(f"VRA.create_vra_cluster(zvm_address={self.client.zvm_address})")
logging.debug(f"VRA.create_vra_cluster payload: {json.dumps(payload, indent=4)}")
url = f"https://{self.client.zvm_address}/v1/vras/clusters"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.post(url, headers=headers, json=payload, verify=self.client.verify_certificate)
response.raise_for_status()
task_id = response.json()
logging.info("Successfully initiated VRA cluster creation")
logging.debug(f"VRA.create_vra_cluster task_id: {task_id}")
if sync:
# Wait for task completion
self.client.tasks.wait_for_task_completion(task_id, timeout=300, interval=5)
return task_id
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 delete_vra_cluster(self, cluster_identifier: str) -> Dict:
"""
Delete a VRA cluster.
Args:
cluster_identifier: The identifier of the cluster to delete
Returns:
Dict: The deletion result
Raises:
requests.exceptions.RequestException: If the API request fails
"""
logging.info(f"VRA.delete_vra_cluster(zvm_address={self.client.zvm_address}, cluster_identifier={cluster_identifier})")
url = f"https://{self.client.zvm_address}/v1/vras/clusters/{cluster_identifier}"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.delete(url, headers=headers, verify=self.client.verify_certificate)
response.raise_for_status()
result = response.json()
logging.info(f"Successfully deleted VRA cluster with identifier: {cluster_identifier}")
logging.debug(f"VRA.delete_vra_cluster result: {json.dumps(result, indent=4)}")
return result
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 update_vra_cluster(self, cluster_identifier: str, payload: Dict) -> Dict:
"""
Update a VRA cluster configuration.
Args:
cluster_identifier: The identifier of the cluster to update
payload: The update configuration
Returns:
Dict: The update result
Raises:
requests.exceptions.RequestException: If the API request fails
"""
logging.info(f"VRA.update_vra_cluster(zvm_address={self.client.zvm_address}, cluster_identifier={cluster_identifier})")
logging.debug(f"VRA.update_vra_cluster payload: {json.dumps(payload, indent=4)}")
url = f"https://{self.client.zvm_address}/v1/vras/clusters/{cluster_identifier}"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.put(url, headers=headers, json=payload, verify=self.client.verify_certificate)
response.raise_for_status()
result = response.json()
logging.info(f"Successfully updated VRA cluster with identifier: {cluster_identifier}")
logging.debug(f"VRA.update_vra_cluster result: {json.dumps(result, indent=4)}")
return result
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 cleanup_vras(self) -> Dict:
"""
Clean up VRAs.
Returns:
Dict: The cleanup result
Raises:
requests.exceptions.RequestException: If the API request fails
"""
logging.info(f"VRA.cleanup_vras(zvm_address={self.client.zvm_address})")
url = f"https://{self.client.zvm_address}/v1/vras/cleanup"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.delete(url, headers=headers, verify=self.client.verify_certificate)
response.raise_for_status()
result = response.json()
logging.info("Successfully cleaned up VRAs")
logging.debug(f"VRA.cleanup_vras result: {json.dumps(result, indent=4)}")
return result
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 upgrade_vra(self, vra_identifier: str) -> Dict:
"""
Upgrade a specific VRA.
Args:
vra_identifier: The identifier of the VRA to upgrade
Returns:
Dict: The upgrade result
Raises:
requests.exceptions.RequestException: If the API request fails
"""
logging.info(f"VRA.upgrade_vra(zvm_address={self.client.zvm_address}, vra_identifier={vra_identifier})")
url = f"https://{self.client.zvm_address}/v1/vras/{vra_identifier}/upgrade"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.post(url, headers=headers, verify=self.client.verify_certificate)
response.raise_for_status()
result = response.json()
logging.info(f"Successfully initiated upgrade for VRA with identifier: {vra_identifier}")
logging.debug(f"VRA.upgrade_vra result: {json.dumps(result, indent=4)}")
return result
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 get_vra_cluster_settings(self, cluster_identifier: str) -> Dict:
"""
Get settings for a specific VRA cluster.
Args:
cluster_identifier: The identifier of the cluster to get settings for
Returns:
Dict: The cluster settings
Raises:
requests.exceptions.RequestException: If the API request fails
"""
logging.info(f"VRA.get_vra_cluster_settings(zvm_address={self.client.zvm_address}, cluster_identifier={cluster_identifier})")
url = f"https://{self.client.zvm_address}/v1/vras/clusters/{cluster_identifier}/settings"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.get(url, headers=headers, verify=self.client.verify_certificate)
response.raise_for_status()
result = response.json()
logging.info(f"Successfully retrieved VRA cluster settings for identifier: {cluster_identifier}")
logging.debug(f"VRA.get_vra_cluster_settings result: {json.dumps(result, indent=4)}")
return result
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 create_vra_cluster_settings(self, cluster_identifier: str, payload: Dict) -> Dict:
"""
Create settings for a VRA cluster.
Args:
cluster_identifier: The identifier of the cluster to create settings for
payload: The cluster settings configuration
Returns:
Dict: The creation result
Raises:
requests.exceptions.RequestException: If the API request fails
"""
logging.info(f"VRA.create_vra_cluster_settings(zvm_address={self.client.zvm_address}, cluster_identifier={cluster_identifier})")
logging.debug(f"VRA.create_vra_cluster_settings payload: {json.dumps(payload, indent=4)}")
url = f"https://{self.client.zvm_address}/v1/vras/clusters/{cluster_identifier}/settings"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.post(url, headers=headers, json=payload, verify=self.client.verify_certificate)
response.raise_for_status()
result = response.json()
logging.info(f"Successfully created VRA cluster settings for identifier: {cluster_identifier}")
logging.debug(f"VRA.create_vra_cluster_settings result: {json.dumps(result, indent=4)}")
return result
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_vra_statuses(self) -> List[Dict]:
"""
List all VRA statuses.
Returns:
List[Dict]: List of VRA statuses
Raises:
requests.exceptions.RequestException: If the API request fails
"""
logging.info(f"VRA.list_vra_statuses(zvm_address={self.client.zvm_address})")
url = f"https://{self.client.zvm_address}/v1/vras/statuses"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.get(url, headers=headers, verify=self.client.verify_certificate)
response.raise_for_status()
result = response.json()
logging.info(f"Successfully retrieved VRA statuses")
logging.debug(f"VRA.list_vra_statuses result: {json.dumps(result, indent=4)}")
return result
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_ip_configuration_types(self) -> List[Dict]:
"""
List all IP configuration types.
Returns:
List[Dict]: List of IP configuration types
Raises:
requests.exceptions.RequestException: If the API request fails
"""
logging.info(f"VRA.list_ip_configuration_types(zvm_address={self.client.zvm_address})")
url = f"https://{self.client.zvm_address}/v1/vras/ipconfigurationtypes"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.get(url, headers=headers, verify=self.client.verify_certificate)
response.raise_for_status()
result = response.json()
logging.info("Successfully retrieved IP configuration types")
logging.debug(f"VRA.list_ip_configuration_types result: {json.dumps(result, indent=4)}")
return result
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_potential_recovery_vras(self, vra_identifier: str) -> List[Dict]:
"""
List potential recovery VRAs for a specific VRA.
Args:
vra_identifier: The identifier of the VRA to get potential recovery VRAs for
Returns:
List[Dict]: List of potential recovery VRAs
Raises:
requests.exceptions.RequestException: If the API request fails
"""
logging.info(f"VRA.list_potential_recovery_vras(zvm_address={self.client.zvm_address}, vra_identifier={vra_identifier})")
url = f"https://{self.client.zvm_address}/v1/vras/{vra_identifier}/changerecoveryvra/potentials"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.get(url, headers=headers, verify=self.client.verify_certificate)
response.raise_for_status()
result = response.json()
logging.info(f"Successfully retrieved potential recovery VRAs for identifier: {vra_identifier}")
logging.debug(f"VRA.list_potential_recovery_vras result: {json.dumps(result, indent=4)}")
return result
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 execute_recovery_vra_change(self, vra_identifier: str, payload: Dict) -> Dict:
"""
Execute a recovery VRA change for a specific VRA.
Args:
vra_identifier: The identifier of the VRA to change recovery VRA for
payload: The change configuration
Returns:
Dict: The execution result
Raises:
requests.exceptions.RequestException: If the API request fails
"""
logging.info(f"VRA.execute_recovery_vra_change(zvm_address={self.client.zvm_address}, vra_identifier={vra_identifier})")
logging.debug(f"VRA.execute_recovery_vra_change payload: {json.dumps(payload, indent=4)}")
url = f"https://{self.client.zvm_address}/v1/vras/{vra_identifier}/changerecoveryvra/execute"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.post(url, headers=headers, json=payload, verify=self.client.verify_certificate)
response.raise_for_status()
result = response.json()
logging.info(f"Successfully executed recovery VRA change for identifier: {vra_identifier}")
logging.debug(f"VRA.execute_recovery_vra_change result: {json.dumps(result, indent=4)}")
return result
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 validate_recovery_vra_change(self, vra_identifier, payload):
logging.info(f"VRA.validate_recovery_vra_change: Validating recovery VRA change for identifier: {vra_identifier}...")
url = f"https://{self.client.zvm_address}/v1/vras/{vra_identifier}/changerecoveryvra/validate"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.post(url, headers=headers, json=payload, verify=self.client.verify_certificate)
response.raise_for_status()
logging.info(f"VRA.validate_recovery_vra_change: Successfully validated recovery VRA change for identifier: {vra_identifier}.")
return response.json()
except requests.exceptions.RequestException as e:
logging.error(f"VRA.validate_recovery_vra_change: Failed to validate recovery VRA change for identifier {vra_identifier}: {e}")
raise
def recommend_recovery_vra_change(self, vra_identifier, payload):
logging.info(f"VRA.recommend_recovery_vra_change: Recommending recovery VRA change for identifier: {vra_identifier}...")
url = f"https://{self.client.zvm_address}/v1/vras/{vra_identifier}/changerecoveryvra/recommendation"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.post(url, headers=headers, json=payload, verify=self.client.verify_certificate)
response.raise_for_status()
logging.info(f"VRA.recommend_recovery_vra_change: Successfully recommended recovery VRA change for identifier: {vra_identifier}.")
return response.json()
except requests.exceptions.RequestException as e:
logging.error(f"VRA.recommend_recovery_vra_change: Failed to recommend recovery VRA change for identifier {vra_identifier}: {e}")
raise
+59
View File
@@ -0,0 +1,59 @@
# 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
class Zorgs:
def __init__(self, client):
self.client = client
def get_zorgs(self, zorg_identifier=None):
"""
Get ZORG information. If zorg_identifier is provided, returns details for that specific ZORG.
Args:
zorg_identifier (str, optional): The identifier of a specific ZORG
Returns:
dict/list: ZORG information. Returns a list of all ZORGs if no identifier is provided,
or details of a specific ZORG if identifier is provided.
"""
url = f"https://{self.client.zvm_address}/v1/zorgs"
if zorg_identifier:
url = f"{url}/{zorg_identifier}"
logging.info(f"Zorgs.get_zorgs: Fetching ZORG {zorg_identifier}...")
else:
logging.info("Zorgs.get_zorgs: Fetching all ZORGs...")
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.client.token}'
}
try:
response = requests.get(url, headers=headers, 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
except Exception as e:
logging.error(f"Unexpected error: {e}")
raise
+109
View File
@@ -0,0 +1,109 @@
# 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 ssl
# Configure logging with timestamp format
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
# Import all necessary classes
from .tasks import Tasks
from .vpgs import VPGs
# from .vpg_settings import VPGSettings
from .vms import VMs
from .failover import Failover
from .alerts import Alerts
from .peersites import PeerSites
from .events import Events
from .repositories import Repositories
from .sessions import Sessions
from .recoveryscripts import RecoveryScripts
from .encryptiondetection import EncryptionDetection
from .zorgs import Zorgs
from .localsite import LocalSite
from .datastores import Datastores
from .vras import VRA
from .recovery_reports import RecoveryReports
from .license import License
from .service_profiles import ServiceProfiles
from .server_date_time import ServerDateTime
from .virtualization_sites import VirtualizationSites
from .volumes import Volumes
from .tweaks import Tweaks
# Disable SSL warnings for self-signed certificates
context = ssl._create_unverified_context()
class ZVMLClient:
def __init__(self, zvm_address, client_id, client_secret, verify_certificate=True):
self.zvm_address = zvm_address
self.client_id = client_id
self.client_secret = client_secret
self.verify_certificate = verify_certificate
self.token = None
self.token_expiry = None
self.__get_keycloak_token()
self.tasks = Tasks(self)
self.vpgs = VPGs(self)
# self.vpg_settings = VPGSettings(self)
self.vms = VMs(self)
self.failover = Failover(self)
self.alerts = Alerts(self)
self.peersites = PeerSites(self)
self.events = Events(self)
self.repositories = Repositories(self)
self.sessions = Sessions(self)
self.recoveryscripts = RecoveryScripts(self)
self.zorgs = Zorgs(self)
self.encryptiondetection = EncryptionDetection(self)
self.localsite = LocalSite(self.zvm_address, self.token)
self.datastores = Datastores(self)
self.vras = VRA(self)
self.recovery_reports = RecoveryReports(self)
self.license = License(self)
self.service_profiles = ServiceProfiles(self)
self.server_date_time = ServerDateTime(self)
self.virtualization_sites = VirtualizationSites(self)
self.volumes = Volumes(self)
self.tweaks = Tweaks(self)
def __get_keycloak_token(self):
logging.debug(f'__get_keycloak_token(zvm_address={self.zvm_address})')
keycloak_uri = f"https://{self.zvm_address}/auth/realms/zerto/protocol/openid-connect/token"
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
body = {
'client_id': self.client_id,
'client_secret': self.client_secret,
'grant_type': 'client_credentials',
'expires_in': 3600 # Request token expiration in seconds (e.g., 1 hour)
}
try:
logging.info("Connecting to Keycloak to get token...")
response = requests.post(keycloak_uri, headers=headers, data=body, verify=self.verify_certificate)
response.raise_for_status()
token_data = response.json()
self.token = token_data.get('access_token')
self.token_expiry = token_data.get('expires_in') # Store expiration time
logging.info(f"Successfully retrieved token.")
logging.info(f"Token expiration details:")
logging.info(f"- Expires in: {self.token_expiry} seconds")
logging.info(f"- Requested expiration: {body['expires_in']} seconds")
if self.token_expiry != body['expires_in']:
logging.warning(f"Server provided different expiration time than requested!")
return self.token
except requests.exceptions.RequestException as e:
logging.error(f"Error retrieving token: {e}")
raise