initial commit
This commit is contained in:
@@ -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.
Binary file not shown.
+324
@@ -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
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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()
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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
@@ -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
@@ -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
|
||||
@@ -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
@@ -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
|
||||
Reference in New Issue
Block a user