initial commit
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
# 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.
|
||||
|
||||
|
||||
"""
|
||||
Zerto Alert Management Example Script
|
||||
|
||||
This script demonstrates basic alert management using the Zerto Virtual Manager (ZVM) API.
|
||||
It shows how to list alerts and perform basic alert operations (dismiss/undismiss).
|
||||
|
||||
Key Features:
|
||||
1. Alert Monitoring:
|
||||
- List all current alerts
|
||||
- Display alert details
|
||||
- Manage alert states (dismiss/undismiss)
|
||||
|
||||
Required Arguments:
|
||||
--zvm_address: ZVM server address
|
||||
--client_id: Keycloak client ID
|
||||
--client_secret: Keycloak client secret
|
||||
--ignore_ssl: Ignore SSL certificate verification (optional)
|
||||
|
||||
Example Usage:
|
||||
python examples/alerts_example.py \
|
||||
--zvm_address "192.168.111.20" \
|
||||
--client_id "zerto-api" \
|
||||
--client_secret "your-secret-here" \
|
||||
--ignore_ssl
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import urllib3
|
||||
import json
|
||||
from zvml import ZVMLClient
|
||||
|
||||
# Disable SSL warnings
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Zerto Alerts Example")
|
||||
parser.add_argument("--zvm_address", required=True, help="ZVM address")
|
||||
parser.add_argument('--client_id', required=True, help='Keycloak client ID')
|
||||
parser.add_argument('--client_secret', required=True, help='Keycloak client secret')
|
||||
parser.add_argument("--ignore_ssl", action="store_true", help="Ignore SSL certificate verification")
|
||||
args = parser.parse_args()
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
try:
|
||||
# Connect to ZVM
|
||||
client = ZVMLClient(
|
||||
zvm_address=args.zvm_address,
|
||||
client_id=args.client_id,
|
||||
client_secret=args.client_secret,
|
||||
verify_certificate=not args.ignore_ssl
|
||||
)
|
||||
|
||||
# Get current alerts
|
||||
alerts = client.alerts.get_alerts()
|
||||
if not alerts:
|
||||
logging.info("No alerts found in the system")
|
||||
return
|
||||
|
||||
# Display alerts
|
||||
logging.info(f"Found {len(alerts)} alerts:")
|
||||
for alert in alerts:
|
||||
logging.info(f"\nAlert Details:")
|
||||
logging.info(f" Description: {alert.get('Description')}")
|
||||
logging.info(f" Status: {alert.get('Status')}")
|
||||
logging.info(f" Level: {alert.get('Level')}")
|
||||
logging.info(f" Turn off Time: {alert.get('TurnedOffTime')}")
|
||||
logging.info(f" Entity: {alert.get('Entity')}")
|
||||
logging.info(f" Help Identifier: {alert.get('HelpIdentifier')}")
|
||||
|
||||
# Get alert identifier
|
||||
alert_id = alert.get('Link', {}).get('identifier')
|
||||
if alert_id:
|
||||
# Dismiss alert
|
||||
input(f"\nPress Enter to dismiss alert {alert_id}...")
|
||||
client.alerts.dismiss_alert(alert_identifier=alert_id)
|
||||
logging.info(f"Alert {alert_id} dismissed")
|
||||
|
||||
# Undismiss alert
|
||||
input(f"Press Enter to undismiss alert {alert_id}...")
|
||||
client.alerts.undismiss_alert(alert_identifier=alert_id)
|
||||
logging.info(f"Alert {alert_id} undismissed")
|
||||
|
||||
except Exception as e:
|
||||
logging.exception("Error:")
|
||||
logging.error(f"Error: {e}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,100 @@
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Zerto Datastores Example Script
|
||||
|
||||
This script demonstrates how to retrieve and list datastores from a Zerto environment.
|
||||
|
||||
The script performs the following steps:
|
||||
1. Connects to Zerto Virtual Manager (ZVM)
|
||||
2. Gets the local site identifier
|
||||
3. Lists all datastores in the site
|
||||
4. Gets detailed information about a specific datastore
|
||||
|
||||
Required Arguments:
|
||||
--zvm_address: ZVM address
|
||||
--client_id: Keycloak client ID
|
||||
--client_secret: Keycloak client secret
|
||||
|
||||
Optional Arguments:
|
||||
--ignore_ssl: Ignore SSL certificate verification
|
||||
|
||||
Example Usage:
|
||||
python examples/datastore_example.py \
|
||||
--zvm_address <zvm_address> \
|
||||
--client_id <client_id> \
|
||||
--client_secret <client_secret> \
|
||||
--ignore_ssl
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import urllib3
|
||||
import json
|
||||
from zvml import ZVMLClient
|
||||
|
||||
# Disable SSL warnings
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Zerto Datastores Example")
|
||||
parser.add_argument("--zvm_address", required=True, help="ZVM address")
|
||||
parser.add_argument('--client_id', required=True, help='Keycloak client ID')
|
||||
parser.add_argument('--client_secret', required=True, help='Keycloak client secret')
|
||||
parser.add_argument("--ignore_ssl", action="store_true", help="Ignore SSL certificate verification")
|
||||
args = parser.parse_args()
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
|
||||
try:
|
||||
# Connect to ZVM
|
||||
logging.info(f"Connecting to ZVM at {args.zvm_address}")
|
||||
client = ZVMLClient(
|
||||
zvm_address=args.zvm_address,
|
||||
client_id=args.client_id,
|
||||
client_secret=args.client_secret,
|
||||
verify_certificate=not args.ignore_ssl
|
||||
)
|
||||
|
||||
# Get local site identifier
|
||||
local_site = client.localsite.get_local_site()
|
||||
site_identifier = local_site.get('SiteIdentifier')
|
||||
logging.info(f"Local site identifier: {site_identifier}")
|
||||
|
||||
# Get datastores using VirtualizationSites API
|
||||
datastores = client.virtualization_sites.get_virtualization_site_datastores(site_identifier)
|
||||
logging.info("\nDatastores in site:")
|
||||
logging.info(json.dumps(datastores, indent=2))
|
||||
|
||||
# Get specific datastore details if any exist
|
||||
if datastores:
|
||||
first_ds_id = datastores[0].get('DatastoreIdentifier')
|
||||
logging.info(f"\nGetting details for specific datastore: {first_ds_id}")
|
||||
|
||||
ds_details = client.datastores.list_datastores(first_ds_id)
|
||||
logging.info("Datastore details:")
|
||||
logging.info(json.dumps(ds_details, indent=2))
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error occurred: {e}")
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,117 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# 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.
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import paramiko
|
||||
import logging
|
||||
import urllib3
|
||||
from zvml.client import Client
|
||||
from zvml.encryptiondetection import EncryptionDetection
|
||||
|
||||
# Disable SSL warnings
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
|
||||
def setup_client(args):
|
||||
"""Initialize and return Zerto client"""
|
||||
client = Client(
|
||||
zvm_address=args.zvm_address,
|
||||
client_id=args.client_id,
|
||||
client_secret=args.client_secret,
|
||||
verify_certificate=not args.ignore_ssl
|
||||
)
|
||||
return client
|
||||
|
||||
def setup_encrypted_volume(ssh_host: str, ssh_user: str, ssh_password: str):
|
||||
"""Create and encrypt a volume on the Linux VM."""
|
||||
try:
|
||||
# Connect to the Linux VM
|
||||
ssh = paramiko.SSHClient()
|
||||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
ssh.connect(ssh_host, username=ssh_user, password=ssh_password)
|
||||
logging.info(f"Successfully connected to {ssh_host}")
|
||||
|
||||
# Create a test file system
|
||||
commands = [
|
||||
"sudo dd if=/dev/zero of=/root/container.img bs=1M count=100", # Create 100MB file
|
||||
"sudo losetup /dev/loop0 /root/container.img", # Set up loop device
|
||||
"sudo cryptsetup -y luksFormat /dev/loop0", # Encrypt with LUKS
|
||||
"echo 'YES' | sudo cryptsetup luksOpen /dev/loop0 encrypted_volume", # Open encrypted volume
|
||||
"sudo mkfs.ext4 /dev/mapper/encrypted_volume", # Create filesystem
|
||||
"sudo mkdir -p /mnt/encrypted", # Create mount point
|
||||
"sudo mount /dev/mapper/encrypted_volume /mnt/encrypted", # Mount encrypted volume
|
||||
"sudo dd if=/dev/urandom of=/mnt/encrypted/testfile bs=1M count=50" # Create test file with random data
|
||||
]
|
||||
|
||||
for cmd in commands:
|
||||
logging.info(f"Executing: {cmd}")
|
||||
stdin, stdout, stderr = ssh.exec_command(cmd)
|
||||
exit_status = stdout.channel.recv_exit_status()
|
||||
if exit_status != 0:
|
||||
error = stderr.read().decode()
|
||||
logging.error(f"Command failed with status {exit_status}: {error}")
|
||||
raise Exception(f"Command failed: {cmd}")
|
||||
|
||||
logging.info("Successfully created and encrypted test volume")
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to setup encrypted volume: {str(e)}")
|
||||
raise
|
||||
finally:
|
||||
ssh.close()
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Encryption Detection Example")
|
||||
parser.add_argument("--zvm_address", required=True, help="ZVM address")
|
||||
parser.add_argument('--client_id', required=True, help='Keycloak client ID')
|
||||
parser.add_argument('--client_secret', required=True, help='Keycloak client secret')
|
||||
parser.add_argument("--ignore_ssl", action="store_true", help="Ignore SSL certificate verification")
|
||||
parser.add_argument("--vm_address", required=True, help="Linux VM address")
|
||||
parser.add_argument("--vm_user", required=True, help="Linux VM username")
|
||||
parser.add_argument("--vm_password", required=True, help="Linux VM password")
|
||||
args = parser.parse_args()
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
|
||||
try:
|
||||
# Setup client
|
||||
client = setup_client(args)
|
||||
encryption_detection = EncryptionDetection(client)
|
||||
logging.info("Successfully connected to ZVM")
|
||||
|
||||
# Setup encrypted volume on Linux VM
|
||||
setup_encrypted_volume(args.vm_address, args.vm_user, args.vm_password)
|
||||
|
||||
# Wait for encryption detection to process
|
||||
logging.info("Waiting for encryption detection to process (30 seconds)...")
|
||||
time.sleep(30)
|
||||
|
||||
# Check for suspected encrypted volumes
|
||||
detections = encryption_detection.list_suspected_volumes()
|
||||
|
||||
if detections:
|
||||
logging.info("Suspected encrypted volumes detected:")
|
||||
for detection in detections:
|
||||
logging.info(f"Volume: {detection.get('VolumeName', 'Unknown')}")
|
||||
logging.info(f"Detection Type: {detection.get('DetectionType', 'Unknown')}")
|
||||
logging.info(f"Confidence Level: {detection.get('ConfidenceLevel', 'Unknown')}")
|
||||
logging.info("---")
|
||||
else:
|
||||
logging.info("No suspected encrypted volumes detected")
|
||||
|
||||
except Exception as e:
|
||||
logging.exception("Error occurred:")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,136 @@
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Zerto Events Example Script
|
||||
|
||||
This script demonstrates how to retrieve and manage events in a Zerto environment.
|
||||
|
||||
The script performs the following steps:
|
||||
1. Connects to Zerto Virtual Manager (ZVM)
|
||||
2. Lists available event types
|
||||
3. Lists available event entities
|
||||
4. Lists available event categories
|
||||
5. Retrieves events from the last hour
|
||||
6. Demonstrates filtered event queries
|
||||
7. Gets detailed information about specific events
|
||||
|
||||
Required Arguments:
|
||||
--zvm_address: ZVM address
|
||||
--client_id: Keycloak client ID
|
||||
--client_secret: Keycloak client secret
|
||||
|
||||
Optional Arguments:
|
||||
--ignore_ssl: Ignore SSL certificate verification
|
||||
|
||||
Example Usage:
|
||||
python examples/events_example.py \
|
||||
--zvm_address <zvm_address> \
|
||||
--client_id <client_id> \
|
||||
--client_secret <client_secret> \
|
||||
--ignore_ssl
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import urllib3
|
||||
import json
|
||||
from zvml import ZVMLClient
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
# Disable SSL warnings
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Zerto Events Example")
|
||||
parser.add_argument("--zvm_address", required=True, help="ZVM address")
|
||||
parser.add_argument('--client_id', required=True, help='Keycloak client ID')
|
||||
parser.add_argument('--client_secret', required=True, help='Keycloak client secret')
|
||||
parser.add_argument("--ignore_ssl", action="store_true", help="Ignore SSL certificate verification")
|
||||
args = parser.parse_args()
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
|
||||
try:
|
||||
# Connect to ZVM
|
||||
logging.info(f"Connecting to ZVM at {args.zvm_address}")
|
||||
client = ZVMLClient(
|
||||
zvm_address=args.zvm_address,
|
||||
client_id=args.client_id,
|
||||
client_secret=args.client_secret,
|
||||
verify_certificate=not args.ignore_ssl
|
||||
)
|
||||
|
||||
# Get event types
|
||||
logging.info("\nFetching event types...")
|
||||
event_types = client.events.list_event_types()
|
||||
logging.info(f"Found {len(event_types)} event types:")
|
||||
logging.info(json.dumps(event_types[:3], indent=2)) # Show first 3 for brevity
|
||||
|
||||
# Get event entities
|
||||
logging.info("\nFetching event entities...")
|
||||
event_entities = client.events.list_event_entities()
|
||||
logging.info(f"Found {len(event_entities)} event entities:")
|
||||
logging.info(json.dumps(event_entities[:3], indent=2)) # Show first 3 for brevity
|
||||
|
||||
# Get event categories
|
||||
logging.info("\nFetching event categories...")
|
||||
event_categories = client.events.list_event_categories()
|
||||
logging.info(f"Found {len(event_categories)} event categories:")
|
||||
logging.info(json.dumps(event_categories[:3], indent=2)) # Show first 3 for brevity
|
||||
|
||||
# Get events from the last 1 hour
|
||||
start_date = (datetime.utcnow() - timedelta(hours=1)).isoformat() + 'Z'
|
||||
end_date = datetime.utcnow().isoformat() + 'Z'
|
||||
|
||||
logging.info(f"\nFetching events from {start_date} to {end_date}...")
|
||||
events = client.events.list_events(
|
||||
start_date=start_date,
|
||||
end_date=end_date
|
||||
)
|
||||
logging.info(f"Found {len(events)} events in the last 24 hours:")
|
||||
if events:
|
||||
logging.info(json.dumps(events[:3], indent=2)) # Show first 3 for brevity
|
||||
|
||||
# Get events with filters
|
||||
logging.info("\nFetching filtered events...")
|
||||
filtered_events = client.events.list_events(
|
||||
start_date=start_date,
|
||||
end_date=end_date,
|
||||
event_type=18, # Using numeric event type
|
||||
category="Events" # Using correct category from list_event_categories
|
||||
)
|
||||
logging.info(f"Found {len(filtered_events)} filtered events:")
|
||||
if filtered_events:
|
||||
logging.info(json.dumps(filtered_events[:3], indent=2)) # Show first 3 for brevity
|
||||
|
||||
# If we have any events, get details for a specific event
|
||||
if events:
|
||||
event_id = events[0].get('EventIdentifier')
|
||||
logging.info(f"\nFetching details for event {event_id}...")
|
||||
event_details = client.events.list_events(event_identifier=event_id)
|
||||
logging.info("Event details:")
|
||||
logging.info(json.dumps(event_details, indent=2))
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error occurred: {e}")
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,127 @@
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Zerto License Management Example Script
|
||||
|
||||
This script demonstrates how to manage Zerto licenses through the API.
|
||||
|
||||
The script performs the following steps:
|
||||
1. Connects to Zerto Virtual Manager (ZVM)
|
||||
2. Retrieves current license information
|
||||
3. Updates the license if a new key is provided
|
||||
4. Verifies the updated license details
|
||||
5. Optionally can delete the license (commented out for safety)
|
||||
|
||||
Required Arguments:
|
||||
--zvm_address: ZVM address
|
||||
--client_id: Keycloak client ID
|
||||
--client_secret: Keycloak client secret
|
||||
|
||||
Optional Arguments:
|
||||
--license_key: License key to add/update
|
||||
--ignore_ssl: Ignore SSL certificate verification
|
||||
|
||||
Example Usage:
|
||||
python examples/license_example.py \
|
||||
--zvm_address <zvm_address> \
|
||||
--client_id <client_id> \
|
||||
--client_secret <client_secret> \
|
||||
--license_key <license_key> \
|
||||
--ignore_ssl
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import urllib3
|
||||
import json
|
||||
from zvml import ZVMLClient
|
||||
|
||||
# Disable SSL warnings
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Zerto License Example")
|
||||
parser.add_argument("--zvm_address", required=True, help="ZVM address")
|
||||
parser.add_argument('--client_id', required=True, help='Keycloak client ID')
|
||||
parser.add_argument('--client_secret', required=True, help='Keycloak client secret')
|
||||
parser.add_argument("--license_key", help="License key to add/update")
|
||||
parser.add_argument("--ignore_ssl", action="store_true", help="Ignore SSL certificate verification")
|
||||
args = parser.parse_args()
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
|
||||
try:
|
||||
# Connect to ZVM
|
||||
logging.info(f"Connecting to ZVM at {args.zvm_address}")
|
||||
client = ZVMLClient(
|
||||
zvm_address=args.zvm_address,
|
||||
client_id=args.client_id,
|
||||
client_secret=args.client_secret,
|
||||
verify_certificate=not args.ignore_ssl
|
||||
)
|
||||
|
||||
# Get current license information
|
||||
logging.info("\nFetching current license information...")
|
||||
license_info = client.license.get_license()
|
||||
if license_info:
|
||||
logging.info("Current license details:")
|
||||
logging.info(json.dumps(license_info, indent=2))
|
||||
else:
|
||||
logging.info("No license currently installed")
|
||||
|
||||
# If license key is provided, update the license
|
||||
if args.license_key:
|
||||
logging.info(f"\nUpdating license with new key...")
|
||||
update_result = client.license.put_license(args.license_key)
|
||||
if update_result:
|
||||
logging.info("License update result:")
|
||||
logging.info(json.dumps(update_result, indent=2))
|
||||
else:
|
||||
logging.info("License updated successfully (no content returned)")
|
||||
|
||||
# Get updated license information
|
||||
logging.info("\nFetching updated license information...")
|
||||
updated_license = client.license.get_license()
|
||||
if updated_license:
|
||||
logging.info("Updated license details:")
|
||||
logging.info(json.dumps(updated_license, indent=2))
|
||||
|
||||
# Delete license (commented out for safety - uncomment if needed)
|
||||
"""
|
||||
logging.info("\nDeleting license...")
|
||||
delete_result = client.license.delete_license()
|
||||
if delete_result:
|
||||
logging.info("License deletion result:")
|
||||
logging.info(json.dumps(delete_result, indent=2))
|
||||
else:
|
||||
logging.info("License deleted successfully (no content returned)")
|
||||
|
||||
# Verify license is deleted
|
||||
final_check = client.license.get_license()
|
||||
if not final_check:
|
||||
logging.info("License successfully removed")
|
||||
"""
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error occurred: {e}")
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,138 @@
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Zerto Local Site Management Example Script
|
||||
|
||||
This script demonstrates how to manage and retrieve information about the local Zerto site.
|
||||
|
||||
The script performs the following steps:
|
||||
1. Connects to Zerto Virtual Manager (ZVM)
|
||||
2. Retrieves local site information
|
||||
3. Gets site pairing statuses
|
||||
4. Sends usage data to Zerto
|
||||
5. Manages login banner settings:
|
||||
- Gets current banner configuration
|
||||
- Sets a new test banner
|
||||
- Verifies the updated settings
|
||||
- Disables the banner
|
||||
|
||||
Required Arguments:
|
||||
--zvm_address: ZVM address
|
||||
--client_id: Keycloak client ID
|
||||
--client_secret: Keycloak client secret
|
||||
|
||||
Optional Arguments:
|
||||
--ignore_ssl: Ignore SSL certificate verification
|
||||
|
||||
Example Usage:
|
||||
python examples/localsite_example.py \
|
||||
--zvm_address <zvm_address> \
|
||||
--client_id <client_id> \
|
||||
--client_secret <client_secret> \
|
||||
--ignore_ssl
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import urllib3
|
||||
import json
|
||||
from zvml import ZVMLClient
|
||||
|
||||
# Disable SSL warnings
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Zerto Local Site Example")
|
||||
parser.add_argument("--zvm_address", required=True, help="ZVM address")
|
||||
parser.add_argument('--client_id', required=True, help='Keycloak client ID')
|
||||
parser.add_argument('--client_secret', required=True, help='Keycloak client secret')
|
||||
parser.add_argument("--ignore_ssl", action="store_true", help="Ignore SSL certificate verification")
|
||||
args = parser.parse_args()
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
|
||||
try:
|
||||
# Connect to ZVM
|
||||
logging.info(f"Connecting to ZVM at {args.zvm_address}")
|
||||
client = ZVMLClient(
|
||||
zvm_address=args.zvm_address,
|
||||
client_id=args.client_id,
|
||||
client_secret=args.client_secret,
|
||||
verify_certificate=not args.ignore_ssl
|
||||
)
|
||||
|
||||
# Get local site information
|
||||
logging.info("\nFetching local site information...")
|
||||
local_site = client.localsite.get_local_site()
|
||||
logging.info("Local site details:")
|
||||
logging.info(json.dumps(local_site, indent=2))
|
||||
|
||||
# Get pairing statuses
|
||||
logging.info("\nFetching pairing statuses...")
|
||||
pairing_statuses = client.localsite.get_pairing_statuses()
|
||||
logging.info("Pairing statuses:")
|
||||
logging.info(json.dumps(pairing_statuses, indent=2))
|
||||
|
||||
# Send usage data
|
||||
logging.info("\nSending usage data...")
|
||||
usage_result = client.localsite.send_usage()
|
||||
if usage_result:
|
||||
logging.info("Usage data result:")
|
||||
logging.info(json.dumps(usage_result, indent=2))
|
||||
else:
|
||||
logging.info("Usage data sent successfully (no content returned)")
|
||||
|
||||
# Get current login banner settings
|
||||
logging.info("\nFetching current login banner settings...")
|
||||
current_banner = client.localsite.get_login_banner()
|
||||
logging.info("Current login banner settings:")
|
||||
logging.info(json.dumps(current_banner, indent=2))
|
||||
|
||||
# Set new login banner
|
||||
test_banner = "This is a test login banner.\nAccess restricted to authorized users only."
|
||||
logging.info("\nSetting new login banner...")
|
||||
banner_result = client.localsite.set_login_banner(
|
||||
is_enabled=True,
|
||||
banner_text=test_banner
|
||||
)
|
||||
logging.info("Login banner update result:")
|
||||
logging.info(banner_result)
|
||||
|
||||
# Verify the new banner settings
|
||||
logging.info("\nVerifying updated login banner settings...")
|
||||
updated_banner = client.localsite.get_login_banner()
|
||||
logging.info("Updated login banner settings:")
|
||||
logging.info(json.dumps(updated_banner, indent=2))
|
||||
|
||||
# Disable the login banner
|
||||
logging.info("\nDisabling login banner...")
|
||||
disable_result = client.localsite.set_login_banner(
|
||||
is_enabled=False,
|
||||
banner_text=""
|
||||
)
|
||||
logging.info("Login banner disable result:")
|
||||
logging.info(disable_result)
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error occurred: {e}")
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,147 @@
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Zerto Peer Sites Management Example Script
|
||||
|
||||
This script demonstrates how to manage peer site relationships between Zerto Virtual Managers (ZVMs).
|
||||
|
||||
The script performs the following steps:
|
||||
1. Connects to two Zerto Virtual Managers (ZVMs)
|
||||
2. Lists existing peer sites from site 1
|
||||
3. Checks for and removes any existing pairing with site 2
|
||||
4. Generates a pairing token at site 2
|
||||
5. Pairs site 1 with site 2 using the token
|
||||
6. Verifies the pairing by checking the updated peer sites list
|
||||
|
||||
Required Arguments:
|
||||
--site1_zvm_address: Site 1 ZVM address
|
||||
--site1_client_id: Site 1 Keycloak client ID
|
||||
--site1_client_secret: Site 1 Keycloak client secret
|
||||
--site2_zvm_address: Site 2 ZVM address
|
||||
--site2_client_id: Site 2 Keycloak client ID
|
||||
--site2_client_secret: Site 2 Keycloak client secret
|
||||
|
||||
Optional Arguments:
|
||||
--ignore_ssl: Ignore SSL certificate verification
|
||||
|
||||
Example Usage:
|
||||
python examples/peersites_example.py \
|
||||
--site1_zvm_address <zvm1_address> \
|
||||
--site1_client_id <client_id1> \
|
||||
--site1_client_secret <secret1> \
|
||||
--site2_zvm_address <zvm2_address> \
|
||||
--site2_client_id <client_id2> \
|
||||
--site2_client_secret <secret2> \
|
||||
--ignore_ssl
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import urllib3
|
||||
import json
|
||||
import time
|
||||
from zvml import ZVMLClient
|
||||
|
||||
# Disable SSL warnings
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Zerto Peer Sites Example")
|
||||
parser.add_argument("--site1_zvm_address", required=True, help="site 1 ZVM address")
|
||||
parser.add_argument('--site1_client_id', required=True, help='site 1 Keycloak client ID')
|
||||
parser.add_argument('--site1_client_secret', required=True, help='site 1 Keycloak client secret')
|
||||
parser.add_argument("--site2_zvm_address", required=True, help="site 2 ZVM address")
|
||||
parser.add_argument('--site2_client_id', required=True, help='site 2 Keycloak client ID')
|
||||
parser.add_argument('--site2_client_secret', required=True, help='site 2 Keycloak client secret')
|
||||
parser.add_argument("--ignore_ssl", action="store_true", help="Ignore SSL certificate verification")
|
||||
args = parser.parse_args()
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
|
||||
try:
|
||||
# Initialize the site 1 client
|
||||
site1_client = ZVMLClient(
|
||||
zvm_address=args.site1_zvm_address,
|
||||
client_id=args.site1_client_id,
|
||||
client_secret=args.site1_client_secret,
|
||||
verify_certificate=not args.ignore_ssl
|
||||
)
|
||||
|
||||
# Initialize the site 2 client
|
||||
site2_client = ZVMLClient(
|
||||
zvm_address=args.site2_zvm_address,
|
||||
client_id=args.site2_client_id,
|
||||
client_secret=args.site2_client_secret,
|
||||
verify_certificate=not args.ignore_ssl
|
||||
)
|
||||
|
||||
# Example 1: Get all peer sites from site 1
|
||||
logging.info("\nExample 1: Getting all peer sites from site 1")
|
||||
peer_sites = site1_client.peersites.get_peer_sites()
|
||||
logging.info(f"Found {len(peer_sites)} peer sites:")
|
||||
logging.info(json.dumps(peer_sites, indent=2))
|
||||
|
||||
# Check if site2 is already paired and delete if necessary
|
||||
site2_already_paired = False
|
||||
site2_identifier = None
|
||||
for site in peer_sites:
|
||||
if site['HostName'] == args.site2_zvm_address:
|
||||
site2_already_paired = True
|
||||
site2_identifier = site['SiteIdentifier']
|
||||
break
|
||||
|
||||
if site2_already_paired:
|
||||
logging.info(f"\nFound existing pairing with {args.site2_zvm_address}, deleting...")
|
||||
site1_client.peersites.delete_peer_site(site2_identifier)
|
||||
logging.info("Existing pairing deleted")
|
||||
# Wait for deletion to complete
|
||||
time.sleep(5)
|
||||
|
||||
# Example 2: Generate pairing token at site 2
|
||||
logging.info("\nExample 2: Generating pairing token at site 2")
|
||||
token = site2_client.peersites.generate_token()
|
||||
logging.info("Generated token:")
|
||||
logging.info(json.dumps(token, indent=2))
|
||||
|
||||
# Example 3: Pair site 1 with site 2
|
||||
logging.info(f"\nExample 3: Pairing site 1 with site 2 ({args.site2_zvm_address})")
|
||||
pair_result = site1_client.peersites.pair_site(
|
||||
hostname=args.site2_zvm_address,
|
||||
token=token['Token'],
|
||||
port=9071
|
||||
)
|
||||
logging.info("Pairing result:")
|
||||
logging.info(json.dumps(pair_result, indent=2))
|
||||
|
||||
# Wait for pairing to complete
|
||||
time.sleep(5)
|
||||
|
||||
# Example 4: Verify pairing by getting updated peer sites list
|
||||
logging.info("\nExample 4: Verifying pairing by getting updated peer sites list")
|
||||
updated_peer_sites = site1_client.peersites.get_peer_sites()
|
||||
logging.info(f"Found {len(updated_peer_sites)} peer sites:")
|
||||
logging.info(json.dumps(updated_peer_sites, indent=2))
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error occurred: {e}")
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import argparse
|
||||
import logging
|
||||
import shutil
|
||||
|
||||
def create_dataset(base_dir: str, number_of_files: int):
|
||||
"""Create test files filled with specific content."""
|
||||
try:
|
||||
# Validate input
|
||||
if number_of_files <= 0:
|
||||
raise ValueError("Number of files must be positive")
|
||||
|
||||
# Create directory if it doesn't exist
|
||||
os.makedirs(base_dir, exist_ok=True)
|
||||
|
||||
# Calculate required disk space (approximate)
|
||||
required_space = number_of_files * 1024 * 1024 # 1MB per file
|
||||
free_space = shutil.disk_usage(base_dir).free
|
||||
|
||||
if free_space < required_space:
|
||||
raise ValueError(
|
||||
f"Not enough disk space. Need {required_space / (1024**3):.2f} GB, "
|
||||
f"but only {free_space / (1024**3):.2f} GB available"
|
||||
)
|
||||
|
||||
# Calculate how many lines we need for ~1MB file
|
||||
# Each line is about 6 bytes (5 chars + newline)
|
||||
# 1MB = 1048576 bytes
|
||||
# Actual calculation: 1048576 / 6 = 174762.67
|
||||
lines_per_file = 174763
|
||||
|
||||
# Create files
|
||||
for i in range(number_of_files):
|
||||
file_path = os.path.join(base_dir, f"file{i:04d}.txt")
|
||||
with open(file_path, 'w') as f:
|
||||
for _ in range(lines_per_file):
|
||||
f.write(f'file{i:04d}\n')
|
||||
|
||||
# Log every 100 files
|
||||
if (i + 1) % 100 == 0:
|
||||
logging.info(f"Created {i + 1} files...")
|
||||
|
||||
logging.info(f"Created dataset in {base_dir}")
|
||||
logging.info(f"Total files created: {number_of_files}")
|
||||
|
||||
# Log total size of the dataset
|
||||
total_size = sum(os.path.getsize(os.path.join(base_dir, f))
|
||||
for f in os.listdir(base_dir))
|
||||
logging.info(f"Total dataset size: {total_size / (1024*1024):.2f} MB")
|
||||
logging.info(f"Average file size: {total_size / (number_of_files * 1024*1024):.2f} MB")
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to create dataset: {str(e)}")
|
||||
raise
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Create test dataset")
|
||||
parser.add_argument("--base_dir", default="~/encryption_test",
|
||||
help="Base directory for test files (default: ~/encryption_test)")
|
||||
parser.add_argument("--number_of_files", type=int, required=True,
|
||||
help="Number of files to create")
|
||||
args = parser.parse_args()
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
|
||||
# Expand user path (~/...)
|
||||
base_dir = os.path.expanduser(args.base_dir)
|
||||
|
||||
create_dataset(base_dir, args.number_of_files)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,129 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import argparse
|
||||
import logging
|
||||
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||
from cryptography.hazmat.primitives import padding
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
import base64
|
||||
import sys
|
||||
|
||||
def generate_key(password: str) -> bytes:
|
||||
"""Generate an AES key from a password."""
|
||||
# Using a fixed key for testing (similar to PowerShell script)
|
||||
key = "Q5KyUru6wn82hlY9k8xUjJOPIC9da41jgRkpt21jo2L="
|
||||
return base64.b64decode(key)
|
||||
|
||||
def decrypt_file(file_path: str, key: bytes) -> bool:
|
||||
"""Decrypt a single file using AES."""
|
||||
try:
|
||||
# Read the encrypted file
|
||||
with open(file_path, 'rb') as file:
|
||||
# Read IV (first 16 bytes) and encrypted data
|
||||
iv = file.read(16)
|
||||
encrypted_data = file.read()
|
||||
|
||||
# Create AES cipher
|
||||
cipher = Cipher(
|
||||
algorithms.AES(key),
|
||||
modes.CBC(iv),
|
||||
backend=default_backend()
|
||||
)
|
||||
decryptor = cipher.decryptor()
|
||||
|
||||
# Decrypt the data
|
||||
padded_data = decryptor.update(encrypted_data) + decryptor.finalize()
|
||||
|
||||
# Remove padding
|
||||
unpadder = padding.PKCS7(128).unpadder()
|
||||
decrypted_data = unpadder.update(padded_data) + unpadder.finalize()
|
||||
|
||||
# Write the decrypted data to a new file (remove .encrypted suffix)
|
||||
decrypted_path = file_path.rsplit('.encrypted', 1)[0]
|
||||
with open(decrypted_path, 'wb') as file:
|
||||
file.write(decrypted_data)
|
||||
|
||||
# Remove the encrypted file
|
||||
os.remove(file_path)
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to decrypt {file_path}: {str(e)}")
|
||||
return False
|
||||
|
||||
def decrypt_directory(base_dir: str, password: str):
|
||||
"""Decrypt all encrypted files in the specified directory."""
|
||||
try:
|
||||
# Generate decryption key
|
||||
key = generate_key(password)
|
||||
|
||||
# Get list of encrypted files
|
||||
files = []
|
||||
for root, _, filenames in os.walk(base_dir):
|
||||
for filename in filenames:
|
||||
if filename.endswith('.encrypted'):
|
||||
files.append(os.path.join(root, filename))
|
||||
|
||||
total_files = len(files)
|
||||
|
||||
if total_files == 0:
|
||||
logging.info("No encrypted files found")
|
||||
return
|
||||
|
||||
logging.info(f"Found {total_files} encrypted files")
|
||||
|
||||
# Track progress
|
||||
successful = 0
|
||||
failed = 0
|
||||
|
||||
# Process each file
|
||||
for i, file_path in enumerate(files, 1):
|
||||
logging.info(f"Decrypting {file_path}")
|
||||
|
||||
if decrypt_file(file_path, key):
|
||||
successful += 1
|
||||
else:
|
||||
failed += 1
|
||||
|
||||
# Log progress every 100 files or at the end
|
||||
if i % 100 == 0 or i == total_files:
|
||||
logging.info(f"Processed {i}/{total_files} files...")
|
||||
|
||||
# Log final results
|
||||
logging.info("Decryption complete!")
|
||||
logging.info(f"Successfully decrypted: {successful} files")
|
||||
if failed > 0:
|
||||
logging.warning(f"Failed to decrypt: {failed} files")
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Decryption failed: {str(e)}")
|
||||
raise
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Decrypt files in directory")
|
||||
parser.add_argument("--base_dir", default="~/encryption_test",
|
||||
help="Base directory containing files to decrypt (default: ~/encryption_test)")
|
||||
parser.add_argument("--password", required=True,
|
||||
help="Password for decryption (must match encryption password)")
|
||||
args = parser.parse_args()
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
|
||||
# Expand user path (~/...)
|
||||
base_dir = os.path.expanduser(args.base_dir)
|
||||
|
||||
# Verify directory exists
|
||||
if not os.path.isdir(base_dir):
|
||||
logging.error(f"Directory not found: {base_dir}")
|
||||
sys.exit(1)
|
||||
|
||||
decrypt_directory(base_dir, args.password)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,140 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import argparse
|
||||
import logging
|
||||
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||
from cryptography.hazmat.primitives import padding
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
import base64
|
||||
import sys
|
||||
|
||||
def generate_key(password: str) -> bytes:
|
||||
"""Generate an AES key from a password."""
|
||||
# Using a fixed key for testing (similar to PowerShell script)
|
||||
key = "Q5KyUru6wn82hlY9k8xUjJOPIC9da41jgRkpt21jo2L="
|
||||
return base64.b64decode(key)
|
||||
|
||||
def encrypt_file(file_path: str, key: bytes) -> bool:
|
||||
"""Encrypt a single file using AES."""
|
||||
try:
|
||||
# Read the original file
|
||||
with open(file_path, 'rb') as file:
|
||||
file_data = file.read()
|
||||
|
||||
# Create an initialization vector
|
||||
iv = os.urandom(16)
|
||||
|
||||
# Create AES cipher
|
||||
cipher = Cipher(
|
||||
algorithms.AES(key),
|
||||
modes.CBC(iv),
|
||||
backend=default_backend()
|
||||
)
|
||||
encryptor = cipher.encryptor()
|
||||
|
||||
# Add padding
|
||||
padder = padding.PKCS7(128).padder()
|
||||
padded_data = padder.update(file_data) + padder.finalize()
|
||||
|
||||
# Encrypt the data
|
||||
encrypted_data = encryptor.update(padded_data) + encryptor.finalize()
|
||||
|
||||
# Write the encrypted data to a new file
|
||||
encrypted_path = f"{file_path}.encrypted"
|
||||
with open(encrypted_path, 'wb') as file:
|
||||
# Write IV first, then encrypted data
|
||||
file.write(iv)
|
||||
file.write(encrypted_data)
|
||||
|
||||
# Remove the original file
|
||||
os.remove(file_path)
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to encrypt {file_path}: {str(e)}")
|
||||
return False
|
||||
|
||||
def encrypt_directory(base_dir: str, password: str):
|
||||
"""Encrypt all files in the specified directory."""
|
||||
try:
|
||||
# Generate encryption key
|
||||
key = generate_key(password)
|
||||
|
||||
# Define target file extensions (same as PowerShell script)
|
||||
target_extensions = [
|
||||
'.pdf', '.xls', '.xlsx', '.ppt', '.pptx', '.doc', '.docx',
|
||||
'.rtf', '.txt', '.csv', '.jpg', '.jpeg', '.png', '.gif',
|
||||
'.avi', '.midi', '.mov', '.mp3', '.mp4', '.mpeg', '.mpg', '.ogg'
|
||||
]
|
||||
|
||||
# Get list of files to encrypt
|
||||
files = []
|
||||
for root, _, filenames in os.walk(base_dir):
|
||||
for filename in filenames:
|
||||
if any(filename.lower().endswith(ext) for ext in target_extensions) and \
|
||||
not filename.endswith('.encrypted'):
|
||||
files.append(os.path.join(root, filename))
|
||||
|
||||
total_files = len(files)
|
||||
|
||||
if total_files == 0:
|
||||
logging.info("No files found to encrypt")
|
||||
return
|
||||
|
||||
logging.info(f"Found {total_files} files to encrypt")
|
||||
|
||||
# Track progress
|
||||
successful = 0
|
||||
failed = 0
|
||||
|
||||
# Process each file
|
||||
for i, file_path in enumerate(files, 1):
|
||||
logging.info(f"Encrypting {file_path}")
|
||||
|
||||
if encrypt_file(file_path, key):
|
||||
successful += 1
|
||||
else:
|
||||
failed += 1
|
||||
|
||||
# Log progress every 100 files or at the end
|
||||
if i % 100 == 0 or i == total_files:
|
||||
logging.info(f"Processed {i}/{total_files} files...")
|
||||
|
||||
# Log final results
|
||||
logging.info("Encryption complete!")
|
||||
logging.info(f"Successfully encrypted: {successful} files")
|
||||
if failed > 0:
|
||||
logging.warning(f"Failed to encrypt: {failed} files")
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Encryption failed: {str(e)}")
|
||||
raise
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Encrypt files in directory")
|
||||
parser.add_argument("--base_dir", default="~/encryption_test",
|
||||
help="Base directory containing files to encrypt (default: ~/encryption_test)")
|
||||
parser.add_argument("--password", required=True,
|
||||
help="Password for encryption")
|
||||
args = parser.parse_args()
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
|
||||
# Expand user path (~/...)
|
||||
base_dir = os.path.expanduser(args.base_dir)
|
||||
|
||||
# Verify directory exists
|
||||
if not os.path.isdir(base_dir):
|
||||
logging.error(f"Directory not found: {base_dir}")
|
||||
sys.exit(1)
|
||||
|
||||
encrypt_directory(base_dir, args.password)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,41 @@
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Zerto Reports Example
|
||||
|
||||
Note: The reports functionality is demonstrated in vpg_failover_example.py, which includes:
|
||||
|
||||
1. Recovery Reports:
|
||||
- Getting recovery reports for VPGs
|
||||
- Filtering reports by date range
|
||||
- Getting specific recovery operation details
|
||||
- Getting latest failover test reports
|
||||
|
||||
2. Resource Reports:
|
||||
- Getting resource reports with various filters
|
||||
- Filtering by site, cluster, and organization
|
||||
- Getting detailed resource information
|
||||
|
||||
Please refer to vpg_failover_example.py for practical examples of using the reports functionality.
|
||||
|
||||
You can run vpg_failover_example.py with:
|
||||
python vpg_failover_example.py \
|
||||
--zvm_address <zvm_ip> \
|
||||
--client_id <client_id> \
|
||||
--client_secret <client_secret> \
|
||||
--ignore_ssl
|
||||
|
||||
For more information about the reports API, see the RecoveryReports class in zvml/recovery_reports.py
|
||||
"""
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(__doc__)
|
||||
@@ -0,0 +1,98 @@
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Zerto Server Date-Time Example Script
|
||||
|
||||
This script demonstrates how to retrieve server time information in different formats from a Zerto Virtual Manager (ZVM).
|
||||
|
||||
The script performs the following steps:
|
||||
1. Connects to Zerto Virtual Manager (ZVM)
|
||||
2. Retrieves server time in three different formats:
|
||||
- Local time
|
||||
- UTC time
|
||||
- Argument format (used for API parameters)
|
||||
3. Displays the time information for each format
|
||||
|
||||
Required Arguments:
|
||||
--zvm_address: ZVM address
|
||||
--client_id: Keycloak client ID
|
||||
--client_secret: Keycloak client secret
|
||||
|
||||
Optional Arguments:
|
||||
--ignore_ssl: Ignore SSL certificate verification
|
||||
|
||||
Example Usage:
|
||||
python examples/server_date_time_example.py \
|
||||
--zvm_address <zvm_address> \
|
||||
--client_id <client_id> \
|
||||
--client_secret <client_secret> \
|
||||
--ignore_ssl
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import urllib3
|
||||
from zvml import ZVMLClient
|
||||
from zvml.server_date_time import DateTimeFormat
|
||||
|
||||
# Disable SSL warnings
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Zerto Server Date-Time Example")
|
||||
parser.add_argument("--zvm_address", required=True, help="ZVM address")
|
||||
parser.add_argument('--client_id', required=True, help='Keycloak client ID')
|
||||
parser.add_argument('--client_secret', required=True, help='Keycloak client secret')
|
||||
parser.add_argument("--ignore_ssl", action="store_true", help="Ignore SSL certificate verification")
|
||||
args = parser.parse_args()
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
|
||||
try:
|
||||
# Connect to ZVM
|
||||
logging.info(f"Connecting to ZVM at {args.zvm_address}")
|
||||
client = ZVMLClient(
|
||||
zvm_address=args.zvm_address,
|
||||
client_id=args.client_id,
|
||||
client_secret=args.client_secret,
|
||||
verify_certificate=not args.ignore_ssl
|
||||
)
|
||||
|
||||
# Test all three date-time formats
|
||||
logging.info("\nTesting all server date-time formats:")
|
||||
|
||||
# Get local time
|
||||
local_time = client.server_date_time.get_server_date_time(DateTimeFormat.LOCAL)
|
||||
logging.info(f"\nLocal Time: {local_time}")
|
||||
|
||||
# Get UTC time
|
||||
utc_time = client.server_date_time.get_server_date_time(DateTimeFormat.UTC)
|
||||
logging.info(f"UTC Time: {utc_time}")
|
||||
|
||||
# Get argument format
|
||||
arg_format = client.server_date_time.get_server_date_time(DateTimeFormat.ARGUMENT)
|
||||
logging.info(f"Argument Format: {arg_format}")
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error occurred: {e}")
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,111 @@
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Zerto Service Profiles Example Script
|
||||
|
||||
This script demonstrates how to retrieve and display service profile information from a Zerto Virtual Manager (ZVM).
|
||||
|
||||
The script performs the following steps:
|
||||
1. Connects to Zerto Virtual Manager (ZVM)
|
||||
2. Retrieves service profiles (optionally filtered by site)
|
||||
3. Displays detailed information for each profile:
|
||||
- Profile name
|
||||
- RPO settings
|
||||
- History configuration
|
||||
- Journal size limits
|
||||
- Test intervals
|
||||
- Profile description
|
||||
|
||||
Required Arguments:
|
||||
--zvm_address: ZVM address
|
||||
--client_id: Keycloak client ID
|
||||
--client_secret: Keycloak client secret
|
||||
|
||||
Optional Arguments:
|
||||
--site_identifier: Site identifier to filter profiles
|
||||
--ignore_ssl: Ignore SSL certificate verification
|
||||
|
||||
Example Usage:
|
||||
python examples/service_profiles_example.py \
|
||||
--zvm_address <zvm_address> \
|
||||
--client_id <client_id> \
|
||||
--client_secret <client_secret> \
|
||||
--site_identifier <site_id> \
|
||||
--ignore_ssl
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import urllib3
|
||||
import json
|
||||
from zvml import ZVMLClient
|
||||
|
||||
# Disable SSL warnings
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Zerto Service Profiles Example")
|
||||
parser.add_argument("--zvm_address", required=True, help="ZVM address")
|
||||
parser.add_argument('--client_id', required=True, help='Keycloak client ID')
|
||||
parser.add_argument('--client_secret', required=True, help='Keycloak client secret')
|
||||
parser.add_argument("--site_identifier", help="Optional site identifier to filter profiles")
|
||||
parser.add_argument("--ignore_ssl", action="store_true", help="Ignore SSL certificate verification")
|
||||
args = parser.parse_args()
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
|
||||
try:
|
||||
# Connect to ZVM
|
||||
logging.info(f"Connecting to ZVM at {args.zvm_address}")
|
||||
client = ZVMLClient(
|
||||
zvm_address=args.zvm_address,
|
||||
client_id=args.client_id,
|
||||
client_secret=args.client_secret,
|
||||
verify_certificate=not args.ignore_ssl
|
||||
)
|
||||
|
||||
# Get all service profiles
|
||||
logging.info("\nFetching service profiles...")
|
||||
profiles = client.service_profiles.get_service_profiles(
|
||||
site_identifier=args.site_identifier
|
||||
)
|
||||
|
||||
# Display service profiles information
|
||||
if profiles:
|
||||
logging.info(f"\nFound {len(profiles)} service profiles:")
|
||||
for profile in profiles:
|
||||
logging.info("\nService Profile Details:")
|
||||
logging.info(f"Name: {profile.get('serviceProfileName')}")
|
||||
logging.info(f"RPO: {profile.get('rpo')}")
|
||||
logging.info(f"History: {profile.get('history')}")
|
||||
logging.info(f"Max Journal Size: {profile.get('maxJournalSizeInPercent')}%")
|
||||
logging.info(f"Test Interval: {profile.get('testInterval')}")
|
||||
if profile.get('description'):
|
||||
logging.info(f"Description: {profile.get('description')}")
|
||||
logging.info("-" * 50)
|
||||
else:
|
||||
logging.warning("No service profiles found")
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error occurred: {e}")
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,156 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Zerto Tweaks Management Example Script
|
||||
|
||||
This script demonstrates how to manage Zerto system tweaks, which are advanced configuration settings.
|
||||
|
||||
The script performs the following steps:
|
||||
1. Connects to Zerto Virtual Manager (ZVM)
|
||||
2. Lists all available system tweaks
|
||||
3. Sets a specific tweak value (t_ransomwareEngCuSumThrsDiff)
|
||||
4. Displays the updated tweak details
|
||||
5. Deletes the tweak setting
|
||||
6. Verifies the deletion by listing tweaks again
|
||||
|
||||
Required Arguments:
|
||||
--zvm_address: ZVM address
|
||||
--client_id: Keycloak client ID
|
||||
--client_secret: Keycloak client secret
|
||||
|
||||
Optional Arguments:
|
||||
--ignore_ssl: Ignore SSL certificate verification
|
||||
|
||||
Example Usage:
|
||||
python examples/tweaks_example.py \
|
||||
--zvm_address <zvm_address> \
|
||||
--client_id <client_id> \
|
||||
--client_secret <client_secret> \
|
||||
--ignore_ssl
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import urllib3
|
||||
import json
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from zvml.zvml import ZVMLClient
|
||||
from zvml.tweaks import Tweaks
|
||||
from zvml.common import ZertoTweakType
|
||||
|
||||
# Disable SSL warnings
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
|
||||
def setup_client(args):
|
||||
"""Initialize and return Zerto client"""
|
||||
client = ZVMLClient(
|
||||
zvm_address=args.zvm_address,
|
||||
client_id=args.client_id,
|
||||
client_secret=args.client_secret,
|
||||
verify_certificate=not args.ignore_ssl
|
||||
)
|
||||
return client
|
||||
|
||||
def format_tweaks_table(result):
|
||||
"""Format tweaks into a table"""
|
||||
# Calculate maximum lengths for formatting
|
||||
max_name_len = max(len(str(tweak.get('name', ''))) for tweak in result)
|
||||
max_value_len = max(len(str(tweak.get('value', ''))) for tweak in result)
|
||||
max_type_len = max(len(str(tweak.get('type', ''))) for tweak in result)
|
||||
max_comment_len = max(len(str(tweak.get('comment', ''))) for tweak in result)
|
||||
|
||||
# Print header
|
||||
header = f"{'Name':<{max_name_len}} | {'Value':<{max_value_len}} | {'Type':<{max_type_len}} | Description | {'Comment':<{max_comment_len}}"
|
||||
logging.info(header)
|
||||
logging.info("-" * len(header))
|
||||
|
||||
# Print tweaks in a formatted table
|
||||
for tweak in result:
|
||||
name = str(tweak.get('name', 'N/A'))
|
||||
value = str(tweak.get('value', 'N/A'))
|
||||
tweak_type = str(tweak.get('type', 'N/A'))
|
||||
description = str(tweak.get('description', 'No description available'))
|
||||
comment = str(tweak.get('comment', ''))
|
||||
|
||||
logging.info(f"{name:<{max_name_len}} | {value:<{max_value_len}} | {tweak_type:<{max_type_len}} | {description} | {comment}")
|
||||
|
||||
logging.info("-" * 80)
|
||||
|
||||
def manage_tweaks(client: ZVMLClient):
|
||||
"""List and manage ZVM tweaks"""
|
||||
tweaks = Tweaks(client)
|
||||
|
||||
# Set a specific tweak
|
||||
tweak_name = "t_ransomwareEngCuSumThrsDiff"
|
||||
logging.info(f"\nSetting tweak {tweak_name}:")
|
||||
updated_tweak = tweaks.set_tweak(
|
||||
tweak_name=tweak_name,
|
||||
value="5",
|
||||
tweak_type=ZertoTweakType.ZVM,
|
||||
comment="mycomment"
|
||||
)
|
||||
|
||||
# List all tweaks
|
||||
logging.info("\nListing all tweaks:")
|
||||
result = tweaks.list_tweaks()
|
||||
logging.info(f"Found {len(result)} ZVM tweaks:")
|
||||
logging.info("-" * 80)
|
||||
format_tweaks_table(result)
|
||||
|
||||
# Show the specific tweak
|
||||
logging.info("\nShowing specific tweak details:")
|
||||
specific_result = tweaks.list_tweaks(tweak_name=tweak_name)
|
||||
format_tweaks_table(specific_result)
|
||||
|
||||
# Delete the tweak
|
||||
logging.info(f"\nDeleting tweak {tweak_name}:")
|
||||
tweaks.delete_tweak(tweak_name)
|
||||
|
||||
# Verify deletion by listing all tweaks again
|
||||
logging.info("\nVerifying deletion - listing all tweaks:")
|
||||
result = tweaks.list_tweaks()
|
||||
logging.info(f"Found {len(result)} ZVM tweaks:")
|
||||
logging.info("-" * 80)
|
||||
format_tweaks_table(result)
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="ZVM Tweaks Management Example")
|
||||
parser.add_argument("--zvm_address", required=True, help="ZVM address")
|
||||
parser.add_argument('--client_id', required=True, help='Keycloak client ID')
|
||||
parser.add_argument('--client_secret', required=True, help='Keycloak client secret')
|
||||
parser.add_argument("--ignore_ssl", action="store_true", help="Ignore SSL certificate verification")
|
||||
args = parser.parse_args()
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
|
||||
try:
|
||||
# Setup client
|
||||
client = setup_client(args)
|
||||
|
||||
# Manage tweaks
|
||||
manage_tweaks(client)
|
||||
|
||||
except Exception as e:
|
||||
logging.exception("Error occurred:")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,230 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Zerto VPG Bulk Update Example Script
|
||||
|
||||
This script demonstrates how to update multiple Virtual Protection Groups (VPGs) settings in bulk.
|
||||
|
||||
The script performs the following steps:
|
||||
1. Connects to Zerto Virtual Manager (ZVM)
|
||||
2. Lists peer sites and their resources:
|
||||
- Available datastores
|
||||
- Available networks
|
||||
3. Retrieves current VPG settings for all VPGs
|
||||
4. Prompts for new settings:
|
||||
- Target datastore
|
||||
- Failover network
|
||||
- Test network
|
||||
5. Updates all VPGs with the new settings after confirmation
|
||||
|
||||
Required Arguments:
|
||||
--zvm_address: Site 1 ZVM address
|
||||
--client_id: Site 1 Keycloak client ID
|
||||
--client_secret: Site 1 Keycloak client secret
|
||||
|
||||
Optional Arguments:
|
||||
--ignore_ssl: Ignore SSL certificate verification
|
||||
|
||||
Example Usage:
|
||||
python examples/update_existing_vpgs.py \
|
||||
--zvm_address <zvm_address> \
|
||||
--client_id <client_id> \
|
||||
--client_secret <client_secret> \
|
||||
--ignore_ssl
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
import urllib3
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
from typing import Dict, List
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
from zvml import ZVMLClient
|
||||
|
||||
# Disable SSL warnings
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
|
||||
def setup_client(args):
|
||||
"""Initialize and return Zerto client"""
|
||||
client = ZVMLClient(
|
||||
zvm_address=args.zvm_address,
|
||||
client_id=args.client_id,
|
||||
client_secret=args.client_secret,
|
||||
verify_certificate=not args.ignore_ssl
|
||||
)
|
||||
return client
|
||||
|
||||
def print_site_resources(client, site_identifier: str, site_name: str):
|
||||
"""Print site resources in a visual way"""
|
||||
print(f"\nSite: {site_name}")
|
||||
print(f"Site ID: {site_identifier}")
|
||||
|
||||
# Get and print datastores
|
||||
print("\nDatastores:")
|
||||
datastores = client.virtualization_sites.get_virtualization_site_datastores(site_identifier)
|
||||
datastore_map = {}
|
||||
for idx, ds in enumerate(datastores, 1):
|
||||
datastore_map[idx] = ds['DatastoreIdentifier']
|
||||
name = ds.get('DatastoreName', 'N/A')
|
||||
logical_name = ds.get('LogicalName', 'N/A')
|
||||
print(f" {idx}. ID: {ds['DatastoreIdentifier']}")
|
||||
print(f" Name: {name}")
|
||||
print(f" Logical Name: {logical_name}")
|
||||
|
||||
# Get and print networks
|
||||
print("\nNetworks:")
|
||||
networks = client.virtualization_sites.get_virtualization_site_networks(site_identifier)
|
||||
network_map = {}
|
||||
for idx, net in enumerate(networks, 1):
|
||||
network_map[idx] = net['NetworkIdentifier']
|
||||
name = net.get('VirtualizationNetworkName', 'N/A')
|
||||
print(f" {idx}. ID: {net['NetworkIdentifier']}")
|
||||
print(f" Name: {name}")
|
||||
|
||||
return datastore_map, network_map
|
||||
|
||||
def get_vpg_settings(client, vpgs: List[Dict]):
|
||||
"""Get current VPG settings"""
|
||||
vpg_settings = []
|
||||
for vpg in vpgs:
|
||||
vpg_id = vpg['VpgIdentifier']
|
||||
vpg_name = vpg['VpgName']
|
||||
|
||||
# Create new settings based on existing VPG
|
||||
settings_id = client.vpgs.create_vpg_settings(
|
||||
basic=None,
|
||||
journal=None,
|
||||
recovery=None,
|
||||
networks=None,
|
||||
vpg_identifier=vpg_id
|
||||
)
|
||||
|
||||
# Get the settings details
|
||||
settings = client.vpgs.get_vpg_settings_by_id(vpg_settings_id=settings_id)
|
||||
|
||||
vpg_settings.append({
|
||||
'vpg_name': vpg_name,
|
||||
'vpg_id': vpg_id,
|
||||
'settings_id': settings_id,
|
||||
'current_settings': settings,
|
||||
'default_datastore': settings.get('Recovery', {}).get('DefaultDatastoreIdentifier'),
|
||||
'failover_network': settings.get('Networks', {}).get('Failover', {}).get('Hypervisor', {}).get('DefaultNetworkIdentifier'),
|
||||
'test_network': settings.get('Networks', {}).get('FailoverTest', {}).get('Hypervisor', {}).get('DefaultNetworkIdentifier')
|
||||
})
|
||||
|
||||
print(f"\nVPG: {vpg_name}")
|
||||
print(f" Current Datastore: {settings.get('Recovery', {}).get('DefaultDatastoreIdentifier')}")
|
||||
print(f" Current Failover Network: {settings.get('Networks', {}).get('Failover', {}).get('Hypervisor', {}).get('DefaultNetworkIdentifier')}")
|
||||
print(f" Current Test Network: {settings.get('Networks', {}).get('FailoverTest', {}).get('Hypervisor', {}).get('DefaultNetworkIdentifier')}")
|
||||
|
||||
return vpg_settings
|
||||
|
||||
def update_vpg_settings(client, vpg_settings: List[Dict], new_datastore: str, new_failover_network: str, new_test_network: str):
|
||||
"""Update all VPG settings with new values"""
|
||||
for vpg in vpg_settings:
|
||||
settings = vpg['current_settings']
|
||||
|
||||
# Update datastore
|
||||
settings['Recovery']['DefaultDatastoreIdentifier'] = new_datastore
|
||||
|
||||
# Update networks
|
||||
if 'Networks' not in settings:
|
||||
settings['Networks'] = {'Failover': {'Hypervisor': {}}, 'FailoverTest': {'Hypervisor': {}}}
|
||||
|
||||
|
||||
settings['Networks']['Failover']['Hypervisor']['DefaultNetworkIdentifier'] = new_failover_network
|
||||
settings['Networks']['FailoverTest']['Hypervisor']['DefaultNetworkIdentifier'] = new_test_network
|
||||
|
||||
# Update the settings
|
||||
client.vpgs.update_vpg_settings(vpg_settings_id=vpg['settings_id'], payload=settings)
|
||||
|
||||
# Commit the changes
|
||||
client.vpgs.commit_vpg(vpg['settings_id'], vpg['vpg_name'], sync=False)
|
||||
|
||||
print(f"Updated and committed settings for VPG: {vpg['vpg_name']}")
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Update existing VPGs settings")
|
||||
parser.add_argument("--zvm_address", required=True, help="Site 1 ZVM address")
|
||||
parser.add_argument('--client_id', required=True, help='Site 1 Keycloak client ID')
|
||||
parser.add_argument('--client_secret', required=True, help='Site 1 Keycloak client secret')
|
||||
parser.add_argument("--ignore_ssl", action="store_true", help="Ignore SSL certificate verification")
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
# Setup client
|
||||
client = setup_client(args)
|
||||
|
||||
# Get peer sites
|
||||
peer_sites = client.peersites.get_peer_sites()
|
||||
logging.debug(f"Peer sites: {peer_sites}")
|
||||
if not peer_sites:
|
||||
raise ValueError("No peer sites found")
|
||||
|
||||
# Get the first peer site
|
||||
peer_site = peer_sites[0]
|
||||
peer_site_id = peer_site['SiteIdentifier']
|
||||
peer_site_name = peer_site.get('PeerSiteName')
|
||||
|
||||
# Print resources and get mapping for peer site
|
||||
datastore_map, network_map = print_site_resources(client, peer_site_id, peer_site_name)
|
||||
|
||||
# Get and print current VPG settings
|
||||
vpgs = client.vpgs.list_vpgs()
|
||||
vpg_settings = get_vpg_settings(client, vpgs)
|
||||
logging.debug(f"VPG settings: {json.dumps(vpg_settings, indent=4)}")
|
||||
|
||||
# Get user input
|
||||
print("\nEnter sequential numbers for new settings:")
|
||||
ds_num = int(input("Datastore number: "))
|
||||
fo_net_num = int(input("Failover network number: "))
|
||||
test_net_num = int(input("Failover test network number: "))
|
||||
|
||||
# Validate input
|
||||
if not all(num in datastore_map for num in [ds_num]) or \
|
||||
not all(num in network_map for num in [fo_net_num, test_net_num]):
|
||||
raise ValueError("Invalid sequential number entered")
|
||||
|
||||
# Get actual IDs
|
||||
new_datastore = datastore_map[ds_num]
|
||||
new_failover_network = network_map[fo_net_num]
|
||||
new_test_network = network_map[test_net_num]
|
||||
|
||||
# Confirm with user
|
||||
print(f"\nAbout to update all VPGs with:")
|
||||
print(f"New datastore: {new_datastore}")
|
||||
print(f"New failover network: {new_failover_network}")
|
||||
print(f"New test network: {new_test_network}")
|
||||
|
||||
if input("\nContinue? (y/n): ").lower() != 'y':
|
||||
print("Operation cancelled")
|
||||
return
|
||||
|
||||
# Update all VPGs
|
||||
update_vpg_settings(client, vpg_settings, new_datastore, new_failover_network, new_test_network)
|
||||
|
||||
print("\nAll VPGs have been updated successfully")
|
||||
|
||||
except Exception as e:
|
||||
logging.exception("Error occurred:")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,282 @@
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Zerto Virtualization Sites Example Script
|
||||
|
||||
This script demonstrates how to retrieve and manage virtualization site information from Zerto.
|
||||
|
||||
The script performs the following steps:
|
||||
1. Connects to Zerto Virtual Manager (ZVM)
|
||||
2. Retrieves information about virtualization sites:
|
||||
- Basic site details
|
||||
- Unprotected VMs and vApps
|
||||
- Storage resources (datastores, clusters)
|
||||
- Network configurations
|
||||
- Host information
|
||||
- Cloud resources (networks, subnets, security)
|
||||
3. For each site, retrieves detailed information about:
|
||||
- Organization VDCs
|
||||
- Storage policies
|
||||
- Network configurations
|
||||
- Host devices and clusters
|
||||
|
||||
Required Arguments:
|
||||
--zvm_address: ZVM address
|
||||
--client_id: Keycloak client ID
|
||||
--client_secret: Keycloak client secret
|
||||
|
||||
Optional Arguments:
|
||||
--ignore_ssl: Ignore SSL certificate verification
|
||||
|
||||
Example Usage:
|
||||
python examples/virtualization_sites_example.py \
|
||||
--zvm_address <zvm_address> \
|
||||
--client_id <client_id> \
|
||||
--client_secret <client_secret> \
|
||||
--ignore_ssl
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import urllib3
|
||||
import json
|
||||
from zvml import ZVMLClient
|
||||
|
||||
# Disable SSL warnings
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Zerto Virtualization Sites Example")
|
||||
parser.add_argument("--zvm_address", required=True, help="ZVM IP address")
|
||||
parser.add_argument('--client_id', required=True, help='Keycloak client ID')
|
||||
parser.add_argument('--client_secret', required=True, help='Keycloak client secret')
|
||||
parser.add_argument("--ignore_ssl", action="store_true", help="Ignore SSL certificate verification")
|
||||
args = parser.parse_args()
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
|
||||
try:
|
||||
# Initialize the client
|
||||
client = ZVMLClient(
|
||||
zvm_address=args.zvm_address,
|
||||
client_id=args.client_id,
|
||||
client_secret=args.client_secret,
|
||||
verify_certificate=not args.ignore_ssl
|
||||
)
|
||||
|
||||
# Example 1: Get all virtualization sites
|
||||
logging.info("\nExample 1: Getting all virtualization sites")
|
||||
sites = client.virtualization_sites.get_virtualization_sites()
|
||||
logging.info("All sites:")
|
||||
logging.info(json.dumps(sites, indent=2))
|
||||
|
||||
# Example 2: Get details for each site individually
|
||||
for site in sites:
|
||||
site_id = site['SiteIdentifier']
|
||||
site_name = site['VirtualizationSiteName']
|
||||
|
||||
logging.info(f"\nExample 2: Getting details for site {site_name} (ID: {site_id})")
|
||||
site_details = client.virtualization_sites.get_virtualization_sites(site_identifier=site_id)
|
||||
logging.info("Site Details:")
|
||||
logging.info(json.dumps(site_details, indent=2))
|
||||
|
||||
# Example 3: Get unprotected VMs for each site
|
||||
logging.info(f"\nExample 3: Getting unprotected VMs for site {site_name}")
|
||||
vms = client.virtualization_sites.get_virtualization_site_vms(site_id)
|
||||
logging.info(f"Found {len(vms)} unprotected VMs:")
|
||||
logging.info(json.dumps(vms, indent=2))
|
||||
|
||||
# Example 4: Get unprotected VCD vApps for each site
|
||||
logging.info(f"\nExample 4: Getting unprotected VCD vApps for site {site_name}")
|
||||
vapps = client.virtualization_sites.get_virtualization_site_vcd_vapps(site_id)
|
||||
logging.info(f"Found {len(vapps)} unprotected VCD vApps:")
|
||||
logging.info(json.dumps(vapps, indent=2))
|
||||
|
||||
# Example 5: Get datastores for each site
|
||||
logging.info(f"\nExample 5: Getting datastores for site {site_name}")
|
||||
datastores = client.virtualization_sites.get_virtualization_site_datastores(site_id)
|
||||
logging.info(f"Found {len(datastores)} datastores:")
|
||||
logging.info(json.dumps(datastores, indent=2))
|
||||
|
||||
# Example 6: Get folders for each site
|
||||
logging.info(f"\nExample 6: Getting folders for site {site_name}")
|
||||
folders = client.virtualization_sites.get_virtualization_site_folders(site_id)
|
||||
logging.info(f"Found {len(folders)} folders:")
|
||||
logging.info(json.dumps(folders, indent=2))
|
||||
|
||||
# Example 7: Get datastore clusters for each site
|
||||
logging.info(f"\nExample 7: Getting datastore clusters for site {site_name}")
|
||||
datastore_clusters = client.virtualization_sites.get_virtualization_site_datastore_clusters(site_id)
|
||||
logging.info(f"Found {len(datastore_clusters)} datastore clusters:")
|
||||
logging.info(json.dumps(datastore_clusters, indent=2))
|
||||
|
||||
# Example 8: Get resource pools for each site
|
||||
logging.info(f"\nExample 8: Getting resource pools for site {site_name}")
|
||||
resource_pools = client.virtualization_sites.get_virtualization_site_resource_pools(site_id)
|
||||
logging.info(f"Found {len(resource_pools)} resource pools:")
|
||||
logging.info(json.dumps(resource_pools, indent=2))
|
||||
|
||||
# Example 9: Get organization VDCs for each site
|
||||
logging.info(f"\nExample 9: Getting organization VDCs for site {site_name}")
|
||||
org_vdcs = client.virtualization_sites.get_virtualization_site_org_vdcs(site_id)
|
||||
logging.info(f"Found {len(org_vdcs)} organization VDCs:")
|
||||
logging.info(json.dumps(org_vdcs, indent=2))
|
||||
|
||||
# Example 10: Get networks for each site
|
||||
logging.info(f"\nExample 10: Getting networks for site {site_name}")
|
||||
networks = client.virtualization_sites.get_virtualization_site_networks(site_id)
|
||||
logging.info(f"Found {len(networks)} networks:")
|
||||
logging.info(json.dumps(networks, indent=2))
|
||||
|
||||
# Example 11: Get hosts for each site
|
||||
logging.info(f"\nExample 11: Getting hosts for site {site_name}")
|
||||
hosts = client.virtualization_sites.get_virtualization_site_hosts(site_id)
|
||||
logging.info(f"Found {len(hosts)} hosts:")
|
||||
logging.info(json.dumps(hosts, indent=2))
|
||||
|
||||
# Example 12: Get host clusters for each site
|
||||
logging.info(f"\nExample 12: Getting host clusters for site {site_name}")
|
||||
host_clusters = client.virtualization_sites.get_virtualization_site_host_clusters(site_id)
|
||||
logging.info(f"Found {len(host_clusters)} host clusters:")
|
||||
logging.info(json.dumps(host_clusters, indent=2))
|
||||
|
||||
# Example 13: Get repositories for each site
|
||||
logging.info(f"\nExample 13: Getting repositories for site {site_name}")
|
||||
repositories = client.virtualization_sites.get_virtualization_site_repositories(site_id)
|
||||
logging.info(f"Found {len(repositories)} repositories:")
|
||||
logging.info(json.dumps(repositories, indent=2))
|
||||
|
||||
# Example 14: Get networks for each org VDC
|
||||
logging.info(f"\nExample 14: Getting org VDC networks for site {site_name}")
|
||||
org_vdcs = client.virtualization_sites.get_virtualization_site_org_vdcs(site_id)
|
||||
for org_vdc in org_vdcs:
|
||||
org_vdc_id = org_vdc['OrgVdcIdentifier']
|
||||
org_vdc_name = org_vdc['VcdVdcName']
|
||||
logging.info(f"\nFetching networks for org VDC: {org_vdc_name}")
|
||||
networks = client.virtualization_sites.get_virtualization_site_org_vdc_networks(site_id, org_vdc_id)
|
||||
logging.info(f"Found {len(networks)} networks in org VDC {org_vdc_name}:")
|
||||
logging.info(json.dumps(networks, indent=2))
|
||||
|
||||
# Example 15: Get storage policies for each org VDC
|
||||
logging.info(f"\nExample 15: Getting storage policies for org VDC: {org_vdc_name}")
|
||||
storage_policies = client.virtualization_sites.get_virtualization_site_org_vdc_storage_policies(site_id, org_vdc_id)
|
||||
logging.info(f"Found {len(storage_policies)} storage policies in org VDC {org_vdc_name}:")
|
||||
logging.info(json.dumps(storage_policies, indent=2))
|
||||
|
||||
# Example 16: Get devices for each site
|
||||
logging.info(f"\nExample 16a: Getting all devices for site {site_name}")
|
||||
devices = client.virtualization_sites.get_virtualization_site_devices(site_id)
|
||||
logging.info(f"Found {len(devices)} devices:")
|
||||
logging.info(json.dumps(devices, indent=2))
|
||||
|
||||
# If we have hosts, get devices for the first host
|
||||
if hosts:
|
||||
host_id = hosts[0]['HostIdentifier']
|
||||
logging.info(f"\nExample 16b: Getting devices for host {host_id} in site {site_name}")
|
||||
host_devices = client.virtualization_sites.get_virtualization_site_devices(
|
||||
site_id,
|
||||
host_identifier=host_id
|
||||
)
|
||||
logging.info(f"Found {len(host_devices)} devices for host {host_id}:")
|
||||
logging.info(json.dumps(host_devices, indent=2))
|
||||
|
||||
# If we found any devices, try filtering by the first device name
|
||||
if host_devices:
|
||||
device_name = host_devices[0]['DeviceName']
|
||||
logging.info(f"\nExample 16c: Getting devices with name {device_name} in site {site_name}")
|
||||
filtered_devices = client.virtualization_sites.get_virtualization_site_devices(
|
||||
site_id,
|
||||
device_name=device_name
|
||||
)
|
||||
logging.info(f"Found {len(filtered_devices)} devices with name {device_name}:")
|
||||
logging.info(json.dumps(filtered_devices, indent=2))
|
||||
|
||||
# Example 17: Get public cloud virtual networks for each site
|
||||
logging.info(f"\nExample 17: Getting public cloud virtual networks for site {site_name}")
|
||||
cloud_networks = client.virtualization_sites.get_virtualization_site_public_cloud_networks(site_id)
|
||||
logging.info(f"Found {len(cloud_networks)} public cloud virtual networks:")
|
||||
logging.info(json.dumps(cloud_networks, indent=2))
|
||||
|
||||
# Example 18: Get public cloud subnets for each site
|
||||
logging.info(f"\nExample 18: Getting public cloud subnets for site {site_name}")
|
||||
cloud_subnets = client.virtualization_sites.get_virtualization_site_public_cloud_subnets(site_id)
|
||||
logging.info(f"Found {len(cloud_subnets)} public cloud subnets:")
|
||||
logging.info(json.dumps(cloud_subnets, indent=2))
|
||||
|
||||
# Example 19: Get public cloud security groups for each site
|
||||
logging.info(f"\nExample 19: Getting public cloud security groups for site {site_name}")
|
||||
security_groups = client.virtualization_sites.get_virtualization_site_public_cloud_security_groups(site_id)
|
||||
logging.info(f"Found {len(security_groups)} public cloud security groups:")
|
||||
logging.info(json.dumps(security_groups, indent=2))
|
||||
|
||||
# Example 20: Get public cloud VM instance types for each site
|
||||
logging.info(f"\nExample 20: Getting public cloud VM instance types for site {site_name}")
|
||||
instance_types = client.virtualization_sites.get_virtualization_site_public_cloud_vm_instance_types(site_id)
|
||||
logging.info(f"Found {len(instance_types)} public cloud VM instance types:")
|
||||
logging.info(json.dumps(instance_types, indent=2))
|
||||
|
||||
# Example 21: Get public cloud resource groups for each site
|
||||
# currently this API returns a 500 error
|
||||
# logging.info(f"\nExample 21: Getting public cloud resource groups for site {site_name}")
|
||||
# resource_groups = client.virtualization_sites.get_virtualization_site_public_cloud_resource_groups(site_id)
|
||||
# logging.info(f"Found {len(resource_groups)} public cloud resource groups:")
|
||||
# logging.info(json.dumps(resource_groups, indent=2))
|
||||
|
||||
# Example 22: Get public cloud keys containers for each site
|
||||
# currently this API returns a 500 error
|
||||
# logging.info(f"\nExample 22: Getting public cloud keys containers for site {site_name}")
|
||||
# keys_containers = client.virtualization_sites.get_virtualization_site_public_cloud_keys_containers(site_id)
|
||||
# logging.info(f"Found {len(keys_containers)} public cloud keys containers:")
|
||||
# logging.info(json.dumps(keys_containers, indent=2))
|
||||
|
||||
# Example 23: Get all encryption keys
|
||||
# currently this API returns a 500 error if does not exist
|
||||
# logging.info(f"\nExample 23a: Getting all encryption keys for site {site_name}")
|
||||
# encryption_keys = client.virtualization_sites.get_virtualization_site_public_cloud_encryption_keys(site_id)
|
||||
# logging.info(f"Found {len(encryption_keys)} encryption keys:")
|
||||
# logging.info(json.dumps(encryption_keys, indent=2))
|
||||
|
||||
# Example 23b: Get details of specific encryption keys if any exist
|
||||
# if encryption_keys:
|
||||
# key_id = encryption_keys[0]['Id']
|
||||
# logging.info(f"\nExample 23b: Getting details for encryption key {key_id}")
|
||||
# key_details = client.virtualization_sites.get_virtualization_site_public_cloud_encryption_keys(site_id, key_id)
|
||||
# logging.info("Encryption key details:")
|
||||
# logging.info(json.dumps(key_details, indent=2))
|
||||
|
||||
# Example 24: Get public cloud managed identities for each site
|
||||
# currently this API returns a 500 error if does not exist
|
||||
# logging.info(f"\nExample 24: Getting public cloud managed identities for site {site_name}")
|
||||
# managed_identities = client.virtualization_sites.get_virtualization_site_public_cloud_managed_identities(site_id)
|
||||
# logging.info(f"Found {len(managed_identities)} managed identities:")
|
||||
# logging.info(json.dumps(managed_identities, indent=2))
|
||||
|
||||
# Example 25: Get public cloud disk encryption keys for each site
|
||||
# currently this API returns a 500 error if does not exist
|
||||
# logging.info(f"\nExample 25: Getting public cloud disk encryption keys for site {site_name}")
|
||||
# disk_encryption_keys = client.virtualization_sites.get_virtualization_site_public_cloud_disk_encryption_keys(site_id)
|
||||
# logging.info(f"Found {len(disk_encryption_keys)} disk encryption keys:")
|
||||
# logging.info(json.dumps(disk_encryption_keys, indent=2))
|
||||
except Exception as e:
|
||||
logging.error(f"Error occurred: {e}")
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,243 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Zerto Virtual Machines Example Script
|
||||
|
||||
This script demonstrates how to manage and retrieve information about protected virtual machines in Zerto.
|
||||
|
||||
The script performs the following steps:
|
||||
1. Connects to Zerto Virtual Manager (ZVM)
|
||||
2. Gets site information and resources:
|
||||
- Local and peer site details
|
||||
- Available datastores
|
||||
- Network configurations
|
||||
3. Demonstrates VM operations:
|
||||
- Lists all protected VMs
|
||||
- Gets detailed information for specific VMs
|
||||
- Filters VMs by VPG name
|
||||
- Manages VM restore points:
|
||||
* Lists available checkpoints
|
||||
* Gets points in time
|
||||
* Retrieves recovery statistics
|
||||
4. Shows VM restore capabilities:
|
||||
- Configures restore settings
|
||||
- Handles network and storage mappings
|
||||
- Manages restore operations
|
||||
|
||||
Required Arguments:
|
||||
--zvm_address: ZVM address
|
||||
--client_id: Keycloak client ID
|
||||
--client_secret: Keycloak client secret
|
||||
|
||||
Optional Arguments:
|
||||
--ignore_ssl: Ignore SSL certificate verification
|
||||
|
||||
Example Usage:
|
||||
python examples/vms_example.py \
|
||||
--zvm_address <zvm_address> \
|
||||
--client_id <client_id> \
|
||||
--client_secret <client_secret> \
|
||||
--ignore_ssl
|
||||
|
||||
Note: VM restore functionality is commented out in this example as it may return a 500 error.
|
||||
"""
|
||||
|
||||
# NOTE
|
||||
# this example assumes that at least one VPG exists on the ZVM and protected VMs exist in the VPG
|
||||
# the vm restore is commnted out as it fails with a 500
|
||||
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import urllib3
|
||||
import json
|
||||
from zvml import ZVMLClient
|
||||
|
||||
# Disable SSL warnings
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Zerto Virtual Machines Example")
|
||||
parser.add_argument("--zvm_address", required=True, help="ZVM address")
|
||||
parser.add_argument('--client_id', required=True, help='Keycloak client ID')
|
||||
parser.add_argument('--client_secret', required=True, help='Keycloak client secret')
|
||||
parser.add_argument("--ignore_ssl", action="store_true", help="Ignore SSL certificate verification")
|
||||
args = parser.parse_args()
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
|
||||
try:
|
||||
# Connect to ZVM
|
||||
logging.info(f"Connecting to ZVM at {args.zvm_address}")
|
||||
client = ZVMLClient(
|
||||
zvm_address=args.zvm_address,
|
||||
client_id=args.client_id,
|
||||
client_secret=args.client_secret,
|
||||
verify_certificate=not args.ignore_ssl
|
||||
)
|
||||
|
||||
# Get both sites information
|
||||
sites = client.virtualization_sites.get_virtualization_sites()
|
||||
logging.info(f"Found {len(sites)} sites:")
|
||||
for site in sites:
|
||||
logging.info(f"Site: {site['VirtualizationSiteName']} (ID: {site['SiteIdentifier']})")
|
||||
|
||||
# Get the peer (second) site identifier
|
||||
local_site = client.localsite.get_local_site()
|
||||
peer_site = next(site for site in sites
|
||||
if site['SiteIdentifier'] != local_site['SiteIdentifier'])
|
||||
peer_site_id = peer_site['SiteIdentifier']
|
||||
logging.info(f"Peer site identifier: {peer_site_id}")
|
||||
|
||||
# Get datastores from peer site
|
||||
datastores = client.virtualization_sites.get_virtualization_site_datastores(peer_site_id)
|
||||
if not datastores:
|
||||
raise ValueError("No datastores found in peer site")
|
||||
# logging.info(json.dumps(datastores, indent=2))
|
||||
datastore_id = datastores[0]['DatastoreIdentifier'] # Use first datastore
|
||||
logging.info(f"Selected datastore ID from peer site: {datastore_id}")
|
||||
|
||||
# Get networks from peer site
|
||||
networks = client.virtualization_sites.get_virtualization_site_networks(peer_site_id)
|
||||
if not networks:
|
||||
raise ValueError("No networks found in peer site")
|
||||
network_id = networks[0]['NetworkIdentifier'] # Use first network
|
||||
logging.info(f"Selected network ID from peer site: {network_id}")
|
||||
|
||||
# Example 1: Get all protected VMs
|
||||
logging.info("\nExample 1: Getting all protected VMs")
|
||||
vms = client.vms.list_vms()
|
||||
# logging.info(f'vms: {json.dumps(vms, indent=2)}')
|
||||
if len(vms) == 0:
|
||||
raise ValueError("No protected VMs found")
|
||||
|
||||
# Example 2: If we found any VMs, get details for the first one
|
||||
first_vm = vms[0]
|
||||
vm_id = first_vm['VmIdentifier']
|
||||
vpg_id = first_vm.get('VpgIdentifier') # VpgIdentifier might be optional
|
||||
|
||||
logging.info(f"\nExample 2: Getting details for VM {vm_id}")
|
||||
vm_details = client.vms.list_vms(
|
||||
vm_identifier=vm_id,
|
||||
vpg_identifier=vpg_id
|
||||
)
|
||||
# logging.info("VM details:")
|
||||
# logging.info(json.dumps(vm_details, indent=2))
|
||||
|
||||
# Example 3: Get VMs filtered by VPG name
|
||||
vpg_name = first_vm.get('VpgName')
|
||||
if vpg_name:
|
||||
logging.info(f"\nExample 3: Getting VMs filtered by VPG name: {vpg_name}")
|
||||
filtered_vms = client.vms.list_vms(vpg_name=vpg_name)
|
||||
# logging.info(f"Found {len(filtered_vms)} VMs in VPG {vpg_name}:")
|
||||
# logging.info(json.dumps(filtered_vms, indent=2))
|
||||
|
||||
# Example 4: Restore the VM if it has checkpoints
|
||||
logging.info("\nExample 4: Restoring VM from checkpoint")
|
||||
|
||||
vpg_info = client.vpgs.list_vpgs(vpg_identifier=vpg_id)
|
||||
vpg_name = vpg_info['VpgName']
|
||||
|
||||
checkpoints = client.vpgs.list_checkpoints(vpg_name=vpg_name)
|
||||
# logging.info(f"checkpoints: {json.dumps(checkpoints, indent=2)}")
|
||||
if not checkpoints:
|
||||
logging.warning("No checkpoints found for VPG")
|
||||
raise ValueError("No checkpoints found for VPG")
|
||||
|
||||
checkpoint_id = checkpoints[0]['CheckpointIdentifier']
|
||||
|
||||
# Prepare restore settings using IDs from peer site
|
||||
restore_settings = {
|
||||
"datastoreIdentifier": datastore_id,
|
||||
"nics": [
|
||||
{
|
||||
"hypervisor": {
|
||||
"dnsSuffix": "",
|
||||
"ipConfig": {
|
||||
"gateway": "",
|
||||
"isDhcp": True,
|
||||
"primaryDns": "",
|
||||
"secondaryDns": "",
|
||||
"staticIp": "",
|
||||
"subnetMask": ""
|
||||
},
|
||||
"networkIdentifier": network_id,
|
||||
"shouldReplaceMacAddress": True
|
||||
},
|
||||
"nicIdentifier": first_vm['Nics'][0]['NicIdentifier']
|
||||
}
|
||||
],
|
||||
"volumes": [
|
||||
{
|
||||
"datastore": {
|
||||
"datastoreIdentifier": datastore_id,
|
||||
"isThin": True
|
||||
},
|
||||
"volumeIdentifier": volume['VmVolumeIdentifier']
|
||||
} for volume in first_vm['Volumes']
|
||||
]
|
||||
}
|
||||
|
||||
# Initiate VM restore
|
||||
# temporary commented out as it fails with a 500
|
||||
# logging.info(f"Restoring VM {first_vm['VmName']} from checkpoint {checkpoint_id}")
|
||||
# restore_result = client.vms.restore_vm(
|
||||
# vm_identifier=vm_id,
|
||||
# vpg_identifier=vpg_id,
|
||||
# restored_vm_name=f"{first_vm['VmName']}_restored",
|
||||
# checkpoint_identifier=checkpoint_id,
|
||||
# journal_vm_restore_settings=restore_settings
|
||||
# )
|
||||
# logging.info(f"Restore initiated: {json.dumps(restore_result, indent=2)}")
|
||||
|
||||
# Example 7: Get points in time for a VM
|
||||
logging.info("\nExample 7: Getting points in time for VM")
|
||||
try:
|
||||
# You can optionally specify start_date and end_date in ISO format
|
||||
# e.g., "2024-01-01T00:00:00.000Z"
|
||||
points_in_time = client.vms.list_vm_points_in_time(
|
||||
vm_identifier=vm_id,
|
||||
vpg_identifier=vpg_id
|
||||
)
|
||||
logging.info(f"Found {len(points_in_time)} points in time:")
|
||||
logging.info(json.dumps(points_in_time, indent=2))
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to get VM points in time: {e}")
|
||||
|
||||
# Example 8: Get points in time stats for a VM
|
||||
logging.info("\nExample 8: Getting points in time stats for VM")
|
||||
try:
|
||||
points_in_time_stats = client.vms.list_vm_points_in_time_stats(
|
||||
vm_identifier=vm_id,
|
||||
vpg_identifier=vpg_id # Optional, but may be required if VM is in multiple VPGs
|
||||
)
|
||||
logging.info("Points in time stats:")
|
||||
logging.info(json.dumps(points_in_time_stats, indent=2))
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to get VM points in time stats: {e}")
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error occurred: {e}")
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,138 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Zerto Volumes Example Script
|
||||
|
||||
This script demonstrates how to retrieve volume information from Zerto Virtual Manager (ZVM).
|
||||
It shows how to list and filter volumes based on different criteria, making it useful for
|
||||
volume management and monitoring.
|
||||
|
||||
Key Features:
|
||||
1. List all volumes in the system
|
||||
2. Filter volumes by:
|
||||
- VPG association
|
||||
- Datastore location
|
||||
- Protected VM attachment
|
||||
3. Display volume details:
|
||||
- Volume identifiers
|
||||
- Storage information
|
||||
- Protection status
|
||||
- Resource associations
|
||||
|
||||
Required Arguments:
|
||||
--zvm_address: Site 1 ZVM address
|
||||
client_id: Site 1 Keycloak client ID
|
||||
client_secret: Site 1 Keycloak client secret
|
||||
--ignore_ssl: Ignore SSL certificate verification (optional)
|
||||
|
||||
Example Usage:
|
||||
python examples/volumes_example.py \
|
||||
--zvm_address "192.168.1.100" \
|
||||
client_id "zerto-api" \
|
||||
client_secret "your-secret-here" \
|
||||
--ignore_ssl
|
||||
|
||||
Note: This script focuses on volume operations and requires only Site 1 credentials
|
||||
since it performs read-only operations on the protected site.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
import urllib3
|
||||
import argparse
|
||||
import logging
|
||||
import json
|
||||
from zvml import ZVMLClient
|
||||
|
||||
# Disable SSL warnings
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Zerto Volumes Example")
|
||||
parser.add_argument("--zvm_address", required=True, help="ZVM address")
|
||||
parser.add_argument('--client_id', required=True, help='Keycloak client ID')
|
||||
parser.add_argument('--client_secret', required=True, help='Keycloak client secret')
|
||||
parser.add_argument("--ignore_ssl", action="store_true", help="Ignore SSL certificate verification")
|
||||
args = parser.parse_args()
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||
|
||||
try:
|
||||
# Initialize the client
|
||||
client = ZVMLClient(args.zvm_address, args.client_id, args.client_secret, not args.ignore_ssl)
|
||||
|
||||
# Example 1: List all volumes
|
||||
logging.info("\nExample 1: Listing all volumes")
|
||||
try:
|
||||
volumes = client.volumes.list_volumes()
|
||||
logging.info(f"Found {len(volumes)} volumes:")
|
||||
logging.info(json.dumps(volumes, indent=2))
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to list volumes: {e}")
|
||||
|
||||
# Example 2: List volumes for a specific VPG
|
||||
logging.info("\nExample 2: Listing volumes for a specific VPG")
|
||||
try:
|
||||
# First get a VPG ID
|
||||
vpgs = client.vpgs.list_vpgs()
|
||||
# logging.info(f"vpgs: {json.dumps(vpgs, indent=2)}")
|
||||
if vpgs:
|
||||
vpg_id = vpgs[0]['VpgIdentifier']
|
||||
logging.info(f"vpg_id: {vpg_id}")
|
||||
volumes = client.volumes.list_volumes(vpg_identifier=vpg_id)
|
||||
logging.info(f"Found {len(volumes)} volumes for VPG {vpg_id}:")
|
||||
logging.info(json.dumps(volumes, indent=2))
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to list volumes for VPG: {e}")
|
||||
|
||||
# Example 3: List volumes for a specific datastore
|
||||
logging.info("\nExample 3: Listing volumes for a specific datastore")
|
||||
try:
|
||||
local_site1_identifier = client.localsite.get_local_site().get('SiteIdentifier')
|
||||
logging.info(f"local_site1_identifier: {local_site1_identifier}")
|
||||
# First get a datastore ID
|
||||
datastores = client.virtualization_sites.get_virtualization_site_datastores(
|
||||
site_identifier=local_site1_identifier
|
||||
)
|
||||
|
||||
for datastore in datastores:
|
||||
datastore_id = datastore['DatastoreIdentifier']
|
||||
volumes = client.volumes.list_volumes(datastore_identifier=datastore_id)
|
||||
logging.info(f"Found {len(volumes)} volumes for datastore {datastore_id}:")
|
||||
logging.info(json.dumps(volumes, indent=2))
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to list volumes for datastore: {e}")
|
||||
|
||||
# Example 4: List volumes for a specific protected VM
|
||||
logging.info("\nExample 4: Listing volumes for a specific protected VM")
|
||||
try:
|
||||
# First get a VM ID
|
||||
vms = client.vms.list_vms()
|
||||
if vms:
|
||||
vm_id = vms[0]['VmIdentifier']
|
||||
volumes = client.volumes.list_volumes(protected_vm_identifier=vm_id)
|
||||
logging.info(f"Found {len(volumes)} volumes for protected VM {vm_id}:")
|
||||
logging.info(json.dumps(volumes, indent=2))
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to list volumes for protected VM: {e}")
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error occurred: {e}")
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,422 @@
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Zerto VPG Failover Example Script
|
||||
|
||||
This script demonstrates how to perform parallel failover tests for multiple Virtual Protection Groups (VPGs)
|
||||
using the Zerto Virtual Manager (ZVM) API. It showcases complete VPG lifecycle management from creation
|
||||
to testing and cleanup.
|
||||
|
||||
Key Features:
|
||||
1. Site Management:
|
||||
- Connect to protected site
|
||||
- Retrieve local and peer site identifiers
|
||||
- Manage cross-site replication using peer site information
|
||||
|
||||
2. VPG Operations:
|
||||
- Create multiple VPGs with custom settings
|
||||
- Add VMs to VPGs
|
||||
- Monitor initial synchronization
|
||||
- Create checkpoints
|
||||
- Run parallel failover tests
|
||||
- Generate test reports
|
||||
- Clean up resources
|
||||
|
||||
3. Resource Management:
|
||||
- Identify and select peer site datastores
|
||||
- Configure peer site hosts and networks
|
||||
- Set up peer site resource pools
|
||||
- Manage VM folders
|
||||
- Monitor volume replication
|
||||
|
||||
Required Arguments:
|
||||
--zvm_address: Protected site ZVM address
|
||||
--client_id: Protected site Keycloak client ID
|
||||
--client_secret: Protected site Keycloak client secret
|
||||
--ignore_ssl: Ignore SSL certificate verification (optional)
|
||||
|
||||
Example Usage:
|
||||
python examples/vpg_failover_example.py \
|
||||
--zvm_address "192.168.111.20" \
|
||||
--client_id "zerto-api" \
|
||||
--client_secret "your-secret-here" \
|
||||
--ignore_ssl
|
||||
|
||||
Note: This script requires credentials only for the protected site. All recovery site information
|
||||
is retrieved using the peer site API, eliminating the need for direct access to the recovery site.
|
||||
Resource identifiers and configuration details for both sites are managed through the protected
|
||||
site's ZVM API.
|
||||
|
||||
Script Flow:
|
||||
1. Connects to protected site ZVM
|
||||
2. Gets local and peer site information
|
||||
3. Creates two VPGs in parallel:
|
||||
- VpgTest1 protecting VM 'SmallCentOS'
|
||||
- VpgTest2 protecting VM 'light-vm1'
|
||||
4. Waits for both VPGs to reach MeetingSLA status
|
||||
5. Performs parallel failover tests
|
||||
6. Waits for user confirmation to stop tests
|
||||
7. Stops failover tests in parallel
|
||||
8. Generates test reports
|
||||
9. Cleans up by deleting both VPGs
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import urllib3
|
||||
import json
|
||||
from zvml import ZVMLClient
|
||||
import time
|
||||
from zvml.common import ZertoVPGStatus, ZertoVPGSubstatus
|
||||
from zvml.recovery_reports import RecoveryReports
|
||||
import threading
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
|
||||
# Disable SSL warnings
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
|
||||
#name sure the api client lifespan settion is complete the initial sync, in my case I set it to 3600 seconds
|
||||
|
||||
def setup_clients(args):
|
||||
"""
|
||||
Initialize and return Zerto clients and their local site identifiers for both sites
|
||||
|
||||
Args:
|
||||
args: Parsed command line arguments
|
||||
|
||||
Returns:
|
||||
tuple: (zvm_client, client2, local_site1_id, local_site2_id)
|
||||
"""
|
||||
# Create clients for both sites
|
||||
zvm_client = ZVMLClient(
|
||||
zvm_address=args.zvm_address,
|
||||
client_id=args.client_id,
|
||||
client_secret=args.client_secret,
|
||||
verify_certificate=not args.ignore_ssl
|
||||
)
|
||||
|
||||
#get all virtualization sites
|
||||
virtualization_sites = zvm_client.virtualization_sites.get_virtualization_sites()
|
||||
logging.info(f"Virtualization Sites: {json.dumps(virtualization_sites, indent=4)}")
|
||||
|
||||
# Get local site ids
|
||||
local_site_identifier = zvm_client.localsite.get_local_site().get('SiteIdentifier')
|
||||
logging.info(f"Site 1 Local Site ID: {local_site_identifier}")
|
||||
|
||||
#peer_site_identifier is site in the list that is not the local site
|
||||
peer_site_identifier = next((site['SiteIdentifier'] for site in virtualization_sites if site['SiteIdentifier'] != local_site_identifier), None)
|
||||
logging.info(f"Site 2 Local Site ID: {peer_site_identifier}")
|
||||
|
||||
return zvm_client, local_site_identifier, peer_site_identifier
|
||||
|
||||
def construct_vpg_settings(vpg_name, local_site_identifier, peer_site_identifier, site2_datastore_identifier,
|
||||
site2_host_identifier, resource_pool_identifier, site2_folder_identifier, site2_network_identifier):
|
||||
basic = {
|
||||
"Name": vpg_name,
|
||||
"VpgType": "Remote",
|
||||
"RpoInSeconds": 300,
|
||||
"JournalHistoryInHours": 1,
|
||||
"Priority": "Medium",
|
||||
"UseWanCompression": True,
|
||||
"ProtectedSiteIdentifier": local_site_identifier,
|
||||
"RecoverySiteIdentifier": peer_site_identifier
|
||||
}
|
||||
|
||||
# Fill journal structure
|
||||
journal = {
|
||||
"DatastoreIdentifier": site2_datastore_identifier,
|
||||
"Limitation": {
|
||||
"HardLimitInMB": 153600,
|
||||
"WarningThresholdInMB": 115200
|
||||
}
|
||||
}
|
||||
|
||||
# Fill recovery structure
|
||||
recovery = {
|
||||
"DefaultHostIdentifier": site2_host_identifier,
|
||||
"DefaultDatastoreIdentifier": site2_datastore_identifier,
|
||||
"DefaultResourcePoolIdentifier": resource_pool_identifier,
|
||||
"DefaultFolderIdentifier": site2_folder_identifier
|
||||
}
|
||||
|
||||
# Fill Networks structure
|
||||
networks = {
|
||||
"Failover": {
|
||||
"Hypervisor": {
|
||||
"DefaultNetworkIdentifier": site2_network_identifier
|
||||
}
|
||||
},
|
||||
"FailoverTest": {
|
||||
"Hypervisor": {
|
||||
"DefaultNetworkIdentifier": site2_network_identifier
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return basic, journal, recovery, networks
|
||||
|
||||
def perform_failover_test(client, vpg_name):
|
||||
"""Execute failover test for a single VPG and get its report"""
|
||||
try:
|
||||
task_id = client.vpgs.failover_test(vpg_name)
|
||||
logging.info(f"Failover test started for VPG {vpg_name}, task ID: {task_id}")
|
||||
|
||||
# Wait for task completion
|
||||
client.tasks.wait_for_task_completion(task_id)
|
||||
|
||||
# Get the latest failover test report
|
||||
latest_report = client.recovery_reports.get_latest_failover_test_report(vpg_name)
|
||||
if latest_report:
|
||||
logging.info(f"Failover test completed for VPG {vpg_name}")
|
||||
return vpg_name, latest_report
|
||||
return vpg_name, None
|
||||
except Exception as e:
|
||||
logging.error(f"Error in failover test for VPG {vpg_name}: {e}")
|
||||
raise
|
||||
|
||||
def run_parallel_failover_tests(client, vpg_names):
|
||||
"""Run failover tests in parallel for multiple VPGs"""
|
||||
logging.info(f"Starting parallel failover tests for VPGs: {vpg_names}")
|
||||
|
||||
with ThreadPoolExecutor(max_workers=len(vpg_names)) as executor:
|
||||
# Submit all failover tasks
|
||||
future_to_vpg = {
|
||||
executor.submit(perform_failover_test, client, vpg_name): vpg_name
|
||||
for vpg_name in vpg_names
|
||||
}
|
||||
|
||||
# Wait for all tasks to complete and collect results
|
||||
results = {}
|
||||
for future in as_completed(future_to_vpg):
|
||||
vpg_name = future_to_vpg[future]
|
||||
try:
|
||||
vpg_name, report = future.result()
|
||||
results[vpg_name] = report
|
||||
logging.info(f"Completed failover test for VPG {vpg_name}")
|
||||
except Exception as e:
|
||||
logging.error(f"Failover test failed for VPG {vpg_name}: {e}")
|
||||
results[vpg_name] = None
|
||||
|
||||
return results
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="zvml Client")
|
||||
parser.add_argument("--zvm_address", required=True, help="ZVM address")
|
||||
parser.add_argument('--client_id', required=True, help='Keycloak client ID')
|
||||
parser.add_argument('--client_secret', required=True, help='Keycloak client secret')
|
||||
# parser.add_argument("--site2_address", required=True, help="Site 2 ZVM address")
|
||||
# parser.add_argument('--site2_client_id', required=True, help='Site 2 Keycloak client ID')
|
||||
# parser.add_argument('--site2_client_secret', required=True, help='Site 2 Keycloak client secret')
|
||||
parser.add_argument("--ignore_ssl", action="store_true", help="Ignore SSL certificate verification")
|
||||
args = parser.parse_args()
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
||||
try:
|
||||
vpg_structure = [
|
||||
{
|
||||
'vpg_name': 'VpgTest1',
|
||||
'vm_name': 'SmallCentOS'
|
||||
},
|
||||
{
|
||||
'vpg_name': 'VpgTest2',
|
||||
'vm_name': 'light-vm1'
|
||||
}
|
||||
]
|
||||
|
||||
# Setup clients and get site identifiers
|
||||
zvm_client, local_site_identifier, peer_site_identifier = setup_clients(args)
|
||||
|
||||
# Get datastore identifier from site 2 using ZVM API
|
||||
site2_datastores = zvm_client.virtualization_sites.get_virtualization_site_datastores(
|
||||
site_identifier=peer_site_identifier
|
||||
)
|
||||
selected_datastore = next((ds for ds in site2_datastores if ds.get('DatastoreName') == "DS_VM_Right"), None)
|
||||
site2_datastore_identifier = selected_datastore.get('DatastoreIdentifier')
|
||||
logging.info(f"Site 2 Datastore ID: {site2_datastore_identifier}")
|
||||
|
||||
# Get host identifier from site 2 using ZVM API
|
||||
site2_hosts = zvm_client.virtualization_sites.get_virtualization_site_hosts(
|
||||
site_identifier=peer_site_identifier
|
||||
)
|
||||
# logging.info(f"Site 2 Hosts: {site2_hosts}")
|
||||
# Get the first host from the list
|
||||
selected_host = site2_hosts[0]
|
||||
site2_host_identifier = selected_host.get('HostIdentifier')
|
||||
# logging.info(f"Site 2 Host ID: {site2_host_identifier}")
|
||||
|
||||
# Get resource pools from site 2 using ZVM API
|
||||
resource_pools = zvm_client.virtualization_sites.get_virtualization_site_resource_pools(
|
||||
site_identifier=peer_site_identifier
|
||||
)
|
||||
if resource_pools:
|
||||
resource_pool_identifier = resource_pools[0].get('ResourcePoolIdentifier')
|
||||
logging.info(f"Resource Pool ID: {resource_pool_identifier}")
|
||||
else:
|
||||
logging.error("No resource pools found on site 2.")
|
||||
return
|
||||
|
||||
# Get networks from site 2 using ZVM API
|
||||
networks = zvm_client.virtualization_sites.get_virtualization_site_networks(
|
||||
site_identifier=peer_site_identifier
|
||||
)
|
||||
if networks:
|
||||
site2_network_identifier = networks[0].get('NetworkIdentifier')
|
||||
logging.info(f"Network ID: {site2_network_identifier}")
|
||||
else:
|
||||
logging.error("No networks found on site 2.")
|
||||
return
|
||||
|
||||
# Get folders from site 2 using ZVM API
|
||||
folders = zvm_client.virtualization_sites.get_virtualization_site_folders(
|
||||
site_identifier=peer_site_identifier
|
||||
)
|
||||
for folder in folders:
|
||||
if folder.get('FolderName') == '/':
|
||||
site2_folder_identifier = folder.get('FolderIdentifier')
|
||||
logging.info(f"Folder ID: {site2_folder_identifier}")
|
||||
break
|
||||
|
||||
# Get VMs from site 1 using ZVM API
|
||||
site_1_vms = zvm_client.virtualization_sites.get_virtualization_site_vms(
|
||||
site_identifier=local_site_identifier
|
||||
)
|
||||
logging.info(f"Found {len(site_1_vms)} VMs on site 1")
|
||||
|
||||
def create_vpg_with_vm(vpg_config):
|
||||
"""Create a VPG and add a VM to it"""
|
||||
try:
|
||||
vpg_name = vpg_config['vpg_name']
|
||||
vm_name = vpg_config['vm_name']
|
||||
|
||||
# Create VPG settings
|
||||
basic, journal, recovery, networks = construct_vpg_settings(
|
||||
vpg_name, local_site_identifier, peer_site_identifier,
|
||||
site2_datastore_identifier, site2_host_identifier,
|
||||
resource_pool_identifier, site2_folder_identifier, site2_network_identifier
|
||||
)
|
||||
|
||||
# Create VPG
|
||||
vpg_id = zvm_client.vpgs.create_vpg(
|
||||
basic=basic, journal=journal, recovery=recovery, networks=networks, sync=True
|
||||
)
|
||||
logging.info(f"VPG {vpg_name} created successfully with ID: {vpg_id}")
|
||||
|
||||
# Find and add VM to VPG
|
||||
vm = next((vm for vm in site_1_vms if vm.get('VmName') == vm_name), None)
|
||||
if vm:
|
||||
vm_payload = {
|
||||
"VmIdentifier": vm.get('VmIdentifier'),
|
||||
"Recovery": {
|
||||
"HostIdentifier": site2_host_identifier,
|
||||
"DatastoreIdentifier": site2_datastore_identifier,
|
||||
"FolderIdentifier": site2_folder_identifier
|
||||
}
|
||||
}
|
||||
task_id = zvm_client.vpgs.add_vm_to_vpg(vpg_name, vm_list_payload=vm_payload)
|
||||
logging.info(f"Task ID: {task_id} to add VM {vm_name} to VPG {vpg_name}")
|
||||
return vpg_name, vpg_id
|
||||
else:
|
||||
logging.error(f"VM {vm_name} not found")
|
||||
return vpg_name, None
|
||||
except Exception as e:
|
||||
logging.error(f"Error creating VPG {vpg_name}: {e}")
|
||||
return vpg_name, None
|
||||
|
||||
# Create VPGs in parallel
|
||||
with ThreadPoolExecutor(max_workers=len(vpg_structure)) as executor:
|
||||
# Submit all VPG creation tasks
|
||||
future_to_vpg = {
|
||||
executor.submit(create_vpg_with_vm, vpg_config): vpg_config
|
||||
for vpg_config in vpg_structure
|
||||
}
|
||||
|
||||
# Wait for all tasks to complete
|
||||
created_vpgs = []
|
||||
for future in as_completed(future_to_vpg):
|
||||
vpg_name, vpg_id = future.result()
|
||||
if vpg_id:
|
||||
created_vpgs.append(vpg_name)
|
||||
logging.info(f"Successfully created VPG {vpg_name}")
|
||||
|
||||
# Wait for all VPGs to reach MeetingSLA status
|
||||
if created_vpgs:
|
||||
logging.info("Waiting for VPGs to reach MeetingSLA status...")
|
||||
while True:
|
||||
all_vpgs_ready = True
|
||||
for vpg_name in created_vpgs:
|
||||
vpg_info = zvm_client.vpgs.list_vpgs(vpg_name=vpg_name)
|
||||
status = vpg_info.get('Status')
|
||||
substatus = vpg_info.get('SubStatus')
|
||||
logging.info(f"VPG {vpg_name} - Status: {ZertoVPGStatus.get_name_by_value(status)}, "
|
||||
f"SubStatus: {ZertoVPGSubstatus.get_name_by_value(substatus)}")
|
||||
|
||||
if not ((status == ZertoVPGStatus.MeetingSLA.value and
|
||||
(substatus == ZertoVPGSubstatus.Sync.value or substatus == ZertoVPGSubstatus.NONE.value)) or
|
||||
(status == ZertoVPGStatus.HistoryNotMeetingSLA.value)):
|
||||
all_vpgs_ready = False
|
||||
break
|
||||
|
||||
if all_vpgs_ready:
|
||||
logging.info("All VPGs are now meeting SLA")
|
||||
break
|
||||
|
||||
logging.info("Waiting for VPGs to reach MeetingSLA status...")
|
||||
time.sleep(30) # Wait 30 seconds before checking again
|
||||
|
||||
input("Press Enter to start parallel failover tests for both VPGs...")
|
||||
|
||||
# Run parallel failover tests
|
||||
failover_results = run_parallel_failover_tests(zvm_client, created_vpgs)
|
||||
logging.info(f"Failover results: {json.dumps(failover_results, indent=4)}")
|
||||
|
||||
input("Press Enter to stop failover tests and rollback both VPGs...")
|
||||
|
||||
# Stop failover tests in parallel
|
||||
with ThreadPoolExecutor(max_workers=len(created_vpgs)) as executor:
|
||||
futures = [
|
||||
executor.submit(zvm_client.vpgs.stop_failover_test, vpg_name)
|
||||
for vpg_name in created_vpgs
|
||||
]
|
||||
for future in as_completed(futures):
|
||||
try:
|
||||
task_id = future.result()
|
||||
logging.info(f"Failover test stop initiated, task ID: {task_id}")
|
||||
except Exception as e:
|
||||
logging.error(f"Error stopping failover test: {e}")
|
||||
|
||||
# After running failover tests
|
||||
for vpg_name in created_vpgs:
|
||||
# Get the latest failover test report
|
||||
latest_report = zvm_client.recovery_reports.get_latest_failover_test_report(vpg_name)
|
||||
if latest_report:
|
||||
logging.info(f"Failover test report for {vpg_name} latest_report=:{json.dumps(latest_report, indent=4)}")
|
||||
|
||||
input("Press Enter to delete both VPGs...")
|
||||
|
||||
# Delete both VPGs
|
||||
zvm_client.vpgs.delete_vpg(created_vpgs[0], force=True, keep_recovery_volumes=False)
|
||||
logging.info(f"VPG {created_vpgs[0]} deleted successfully.")
|
||||
|
||||
zvm_client.vpgs.delete_vpg(created_vpgs[1], force=True, keep_recovery_volumes=False)
|
||||
logging.info(f"VPG {created_vpgs[1]} deleted successfully.")
|
||||
|
||||
except Exception as e:
|
||||
logging.exception("Error:")
|
||||
logging.error(f"Error: {e}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,181 @@
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Zerto VPG Settings Export/Import Example Script
|
||||
|
||||
This script demonstrates how to export and import Virtual Protection Group (VPG) settings
|
||||
using the Zerto Virtual Manager (ZVM) API. It allows for backup and restoration of VPG
|
||||
configurations, which is useful for disaster recovery planning and VPG replication.
|
||||
|
||||
Key Features:
|
||||
1. VPG Settings Export:
|
||||
- Export settings for specific VPGs or all VPGs
|
||||
- Save exported settings to a JSON file
|
||||
- Include all VPG configuration parameters
|
||||
- Capture recovery site mappings
|
||||
|
||||
2. Settings Verification:
|
||||
- List all available exported settings
|
||||
- Read and display detailed settings
|
||||
- Show summary of exported VPG configurations
|
||||
- Verify export timestamp and status
|
||||
|
||||
3. VPG Settings Import:
|
||||
- Import settings back to create new VPGs
|
||||
- Restore original VPG configurations
|
||||
- Support for multiple VPGs in single operation
|
||||
- Validate import results
|
||||
|
||||
Required Arguments:
|
||||
--zvm_address: Protected site ZVM address
|
||||
--client_id: Protected site Keycloak client ID
|
||||
--client_secret: Protected site Keycloak client secret
|
||||
--ignore_ssl: Ignore SSL certificate verification (optional)
|
||||
--vpg_names: Comma-separated list of VPG names to export (optional)
|
||||
--output_file: File path to save exported settings (optional)
|
||||
|
||||
Example Usage:
|
||||
python examples/vpg_setting_export_example.py \
|
||||
--zvm_address "192.168.111.20" \
|
||||
--client_id "zerto-api" \
|
||||
--client_secret "your-secret-here" \
|
||||
--vpg_names "VpgTest1,VpgTest2" \
|
||||
--output_file "vpg_settings.json" \
|
||||
--ignore_ssl
|
||||
|
||||
Script Flow:
|
||||
1. Connects to protected site ZVM
|
||||
2. Exports VPG settings:
|
||||
- For specified VPGs if vpg_names provided
|
||||
- For all VPGs if no vpg_names specified
|
||||
3. Saves settings to file if output_file specified
|
||||
4. Verifies export by reading settings
|
||||
5. Displays VPG configuration summaries:
|
||||
- VPG names
|
||||
- Source and target sites
|
||||
- RPO and journal history
|
||||
6. Pauses for manual VPG deletion
|
||||
7. Imports settings to recreate VPGs
|
||||
8. Verifies import success
|
||||
|
||||
Note: This script requires only protected site credentials. It's designed for VPG
|
||||
configuration backup and restore scenarios, allowing you to quickly recreate VPGs
|
||||
with identical settings after changes or in disaster recovery situations.
|
||||
"""
|
||||
import argparse
|
||||
import logging
|
||||
logging.basicConfig(
|
||||
level=logging.DEBUG,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
import urllib3
|
||||
import json
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
from zvml import ZVMLClient
|
||||
|
||||
# Disable SSL warningss
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
|
||||
def setup_client(args):
|
||||
"""Initialize and return Zerto client"""
|
||||
client = ZVMLClient(
|
||||
zvm_address=args.zvm_address,
|
||||
client_id=args.client_id,
|
||||
client_secret=args.client_secret,
|
||||
verify_certificate=not args.ignore_ssl
|
||||
)
|
||||
return client
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Export and Import VPG settings example")
|
||||
parser.add_argument("--zvm_address", required=True, help="Site 1 ZVM address")
|
||||
parser.add_argument('--client_id', required=True, help='Site 1 Keycloak client ID')
|
||||
parser.add_argument('--client_secret', required=True, help='Site 1 Keycloak client secret')
|
||||
parser.add_argument("--ignore_ssl", action="store_true", help="Ignore SSL certificate verification")
|
||||
parser.add_argument("--vpg_names", help="Comma-separated list of VPG names to export settings for")
|
||||
parser.add_argument("--output_file", help="Optional file to save the exported settings")
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
# Setup client
|
||||
client = setup_client(args)
|
||||
|
||||
# If no VPG names provided, get all VPGs
|
||||
if not args.vpg_names:
|
||||
vpgs = client.vpgs.list_vpgs()
|
||||
vpg_names = [vpg['VpgName'] for vpg in vpgs]
|
||||
logging.info(f"No VPG names provided, exporting all {len(vpg_names)} VPGs")
|
||||
else:
|
||||
# Split the comma-separated string and strip whitespace
|
||||
vpg_names = [name.strip() for name in args.vpg_names.split(',')]
|
||||
logging.info(f"Exporting settings for VPGs: {vpg_names}")
|
||||
|
||||
# Step 1: Export VPG settings
|
||||
print("\nStep 1: Exporting VPG settings...")
|
||||
result = client.vpgs.export_vpg_settings(vpg_names)
|
||||
|
||||
print("Export Result:")
|
||||
print(f"Timestamp: {result.get('TimeStamp')}")
|
||||
print(f"Result: {result.get('ExportResult', {}).get('Result')}")
|
||||
print(f"Message: {result.get('ExportResult', {}).get('Message')}")
|
||||
|
||||
# Save to file if specified
|
||||
if args.output_file:
|
||||
with open(args.output_file, 'w') as f:
|
||||
json.dump(result, f, indent=2)
|
||||
print(f"\nExported settings saved to: {args.output_file}")
|
||||
|
||||
# Step 2: Verify export and read settings
|
||||
print("\nStep 2: Reading exported settings...")
|
||||
exported_settings = client.vpgs.list_exported_vpg_settings()
|
||||
export_timestamp = result.get('TimeStamp', '').split('.')[0] + '.000Z'
|
||||
|
||||
if any(setting.get('TimeStamp') == export_timestamp for setting in exported_settings):
|
||||
print(f"Found export with timestamp {export_timestamp}")
|
||||
settings = client.vpgs.read_exported_vpg_settings(export_timestamp)
|
||||
|
||||
# Display summary of exported VPG settings
|
||||
vpg_settings = settings.get('ExportedVpgSettingsApi', [])
|
||||
print(f"\nFound settings for {len(vpg_settings)} VPGs:")
|
||||
for vpg in vpg_settings:
|
||||
basic = vpg.get('Basic', {})
|
||||
print(f"\nVPG Name: {basic.get('Name')}")
|
||||
print(f"Source Site: {vpg.get('SourceSiteName')}")
|
||||
print(f"Target Site: {vpg.get('TargetSiteName')}")
|
||||
print(f"RPO (seconds): {basic.get('RpoInSeconds')}")
|
||||
print(f"Journal History (hours): {basic.get('JournalHistoryInHours')}")
|
||||
|
||||
#pause
|
||||
input("Delte VPG manually and Press Enter to continue...")
|
||||
|
||||
# Step 3: Import the settings back
|
||||
print("\nStep 3: Importing VPG settings...")
|
||||
import_result = client.vpgs.import_vpg_settings(settings)
|
||||
print("\nImport Result:")
|
||||
print(f"Result: {import_result.get('Result')}")
|
||||
print(f"Message: {import_result.get('Message')}")
|
||||
if import_result.get('VpgSettingsIds'):
|
||||
print(f"Created VPG Settings IDs: {', '.join(import_result.get('VpgSettingsIds'))}")
|
||||
|
||||
#pause
|
||||
input("Look at the VPG and verify whether the manual channges are reverted back to the original settings. Press Enter to continue...")
|
||||
else:
|
||||
print(f"\nWarning: Export with timestamp {export_timestamp} not found in exported settings list")
|
||||
|
||||
except Exception as e:
|
||||
logging.exception("Error occurred:")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,304 @@
|
||||
# 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.
|
||||
"""
|
||||
Zerto VPG VM Management Example Script
|
||||
|
||||
This script demonstrates how to manage Virtual Machines (VMs) within Virtual Protection Groups (VPGs)
|
||||
using the Zerto Virtual Manager (ZVM) API. It showcases VPG creation, VM addition/removal, and cleanup.
|
||||
|
||||
Key Features:
|
||||
1. Site Management:
|
||||
- Connect to protected site
|
||||
- Retrieve local and peer site identifiers
|
||||
- Manage cross-site replication using peer site information
|
||||
|
||||
2. VPG Operations:
|
||||
- Create multiple VPGs with custom settings
|
||||
- Add multiple VMs to a VPG
|
||||
- Remove VMs from VPGs
|
||||
- Move VMs between VPGs
|
||||
- Clean up resources
|
||||
|
||||
3. Resource Management:
|
||||
- Identify and select peer site datastores, hosts, networks, folders and resource pools
|
||||
|
||||
Required Arguments:
|
||||
--zvm_address: Protected site ZVM address
|
||||
--client_id: Protected site Keycloak client ID
|
||||
--client_secret: Protected site Keycloak client secret
|
||||
--ignore_ssl: Ignore SSL certificate verification (optional)
|
||||
|
||||
Example Usage:
|
||||
python examples/vpg_vms_example.py \
|
||||
--zvm_address "192.168.111.20" \
|
||||
--client_id "zerto-api" \
|
||||
--client_secret "your-secret-here" \
|
||||
--ignore_ssl
|
||||
|
||||
Note: This script requires credentials only for the protected site. All recovery site information
|
||||
is retrieved using the peer site API, eliminating the need for direct access to the recovery site.
|
||||
|
||||
Script Flow:
|
||||
1. Connects to protected site ZVM
|
||||
2. Gets local and peer site information
|
||||
3. Creates first VPG 'VpgTest1'
|
||||
4. Adds two VMs (vm1 and vm2) to first VPG
|
||||
5. Removes vm1 from first VPG
|
||||
6. Creates second VPG 'VpgTest2'
|
||||
7. Adds removed VM (vm1) to second VPG
|
||||
8. Cleans up by deleting both VPGs
|
||||
"""
|
||||
|
||||
import logging
|
||||
# Configure logging before any other imports or code
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
|
||||
import argparse
|
||||
import urllib3
|
||||
import json
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
from zvml import ZVMLClient
|
||||
|
||||
|
||||
# Disable SSL warnings
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
|
||||
def setup_clients(args):
|
||||
"""
|
||||
Initialize and return Zerto clients and their local site identifiers for both sites
|
||||
|
||||
Args:
|
||||
args: Parsed command line arguments
|
||||
|
||||
Returns:
|
||||
tuple: (client1, local_site1_id, local_peer_id)
|
||||
"""
|
||||
# Create clients for both sites
|
||||
client = ZVMLClient(
|
||||
zvm_address=args.zvm_address,
|
||||
client_id=args.client_id,
|
||||
client_secret=args.client_secret,
|
||||
verify_certificate=not args.ignore_ssl
|
||||
)
|
||||
|
||||
return client
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="zvml Client")
|
||||
parser.add_argument("--zvm_address", required=True, help="Site 1 ZVM address")
|
||||
parser.add_argument('--client_id', required=True, help='Site 1 Keycloak client ID')
|
||||
parser.add_argument('--client_secret', required=True, help='Site 1 Keycloak client secret')
|
||||
parser.add_argument("--ignore_ssl", action="store_true", help="Ignore SSL certificate verification")
|
||||
parser.add_argument("--vm1", required=True, help="Name of first VM to protect")
|
||||
parser.add_argument("--vm2", required=True, help="Name of second VM to protect")
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
# Setup clients and get site identifiers
|
||||
client1 = setup_clients(args)
|
||||
|
||||
virtualization_sites = client1.virtualization_sites.get_virtualization_sites()
|
||||
logging.debug(f"Virtualization Sites: {json.dumps(virtualization_sites, indent=4)}")
|
||||
|
||||
# Get local site ids
|
||||
local_site_identifier = client1.localsite.get_local_site().get('SiteIdentifier')
|
||||
logging.info(f"Site 1 Local Site ID: {local_site_identifier}")
|
||||
|
||||
peer_site_identifier = next((site['SiteIdentifier'] for site in virtualization_sites if site['SiteIdentifier'] != local_site_identifier), None)
|
||||
logging.info(f"Site 2 Local Site ID: {peer_site_identifier}")
|
||||
|
||||
# Get datastore identifier from site 2
|
||||
peer_datastores = client1.virtualization_sites.get_virtualization_site_datastores(
|
||||
site_identifier=peer_site_identifier
|
||||
)
|
||||
logging.debug(f"Site 2 Datastores: {json.dumps(peer_datastores, indent=4)}")
|
||||
|
||||
selected_datastore = next((ds for ds in peer_datastores if ds.get('DatastoreName') == "DS_VM_Right"), None)
|
||||
peer_datastore_identifier = selected_datastore.get('DatastoreIdentifier')
|
||||
logging.info(f"Site 2 Datastore ID: {peer_datastore_identifier}")
|
||||
|
||||
# Fill basic VPG settings info
|
||||
vpg_name = 'VpgTest1'
|
||||
basic = {
|
||||
"Name": vpg_name,
|
||||
"VpgType": "Remote",
|
||||
"RpoInSeconds": 300,
|
||||
"JournalHistoryInHours": 1,
|
||||
"Priority": "Medium",
|
||||
"UseWanCompression": True,
|
||||
"ProtectedSiteIdentifier": local_site_identifier,
|
||||
"RecoverySiteIdentifier": peer_site_identifier
|
||||
}
|
||||
# Fill journal structure
|
||||
journal = {
|
||||
"DatastoreIdentifier": peer_datastore_identifier,
|
||||
"Limitation": {
|
||||
"HardLimitInMB": 153600,
|
||||
"WarningThresholdInMB": 115200
|
||||
}
|
||||
}
|
||||
|
||||
resource_pools = client1.virtualization_sites.get_virtualization_site_resource_pools(
|
||||
site_identifier=peer_site_identifier
|
||||
)
|
||||
logging.debug(f"Resource Pools: {json.dumps(resource_pools, indent=4)}")
|
||||
|
||||
# Extract resource pool identifier from the first resource pool on the list
|
||||
if resource_pools:
|
||||
resource_pool_identifier = resource_pools[0].get('Identifier')
|
||||
logging.info(f"Resource Pool Identifier from the first resource pool: {resource_pool_identifier}")
|
||||
else:
|
||||
logging.error("No resource pools found on site 2.")
|
||||
return
|
||||
|
||||
# List networks from peer site
|
||||
networks_list = client1.virtualization_sites.get_virtualization_site_networks(
|
||||
site_identifier=peer_site_identifier
|
||||
)
|
||||
logging.debug(f"Networks: {json.dumps(networks_list, indent=4)}")
|
||||
|
||||
# Extract network identifier from the first network on the list
|
||||
if networks_list:
|
||||
network_identifier = networks_list[0].get('NetworkIdentifier')
|
||||
logging.info(f"Network Identifier from the first network: {network_identifier}")
|
||||
else:
|
||||
logging.error("No networks found on site 2.")
|
||||
return
|
||||
|
||||
#list folders from peer
|
||||
folders = client1.virtualization_sites.get_virtualization_site_folders(
|
||||
site_identifier=peer_site_identifier
|
||||
)
|
||||
logging.debug(f"Site 2 Folders: {json.dumps(folders, indent=4)}")
|
||||
|
||||
for folder in folders:
|
||||
if folder.get('FolderName') == '/':
|
||||
peer_folder_identifier = folder.get('FolderIdentifier')
|
||||
logging.info(f"Folder Identifier: {peer_folder_identifier}")
|
||||
break
|
||||
|
||||
peer_hosts = client1.virtualization_sites.get_virtualization_site_hosts(
|
||||
site_identifier=peer_site_identifier
|
||||
)
|
||||
logging.debug(f"Site 2 Hosts: {json.dumps(peer_hosts, indent=4)}")
|
||||
|
||||
# get the second host from the list
|
||||
peer_site_host_identifier = peer_hosts[1].get('HostIdentifier')
|
||||
logging.info(f"Host Identifier from the second host: {peer_site_host_identifier}")
|
||||
|
||||
# Fill recovery structure
|
||||
recovery = {
|
||||
"DefaultHostIdentifier": peer_site_host_identifier,
|
||||
"DefaultDatastoreIdentifier": peer_datastore_identifier,
|
||||
"DefaultResourcePoolIdentifier": resource_pool_identifier,
|
||||
"DefaultFolderIdentifier": peer_folder_identifier
|
||||
}
|
||||
|
||||
# Fill Networks structure
|
||||
networks = {
|
||||
"Failover": {
|
||||
"Hypervisor": {
|
||||
"DefaultNetworkIdentifier": network_identifier
|
||||
}
|
||||
},
|
||||
"FailoverTest": {
|
||||
"Hypervisor": {
|
||||
"DefaultNetworkIdentifier": network_identifier
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
input("Press Enter to create the first VPG...")
|
||||
|
||||
vpg_id = client1.vpgs.create_vpg(basic=basic, journal=journal,
|
||||
recovery=recovery, networks=networks, sync=True)
|
||||
logging.info(f"VPG ID: {vpg_id} created successfully.")
|
||||
|
||||
# Add VMs to the first VPG
|
||||
vms = client1.virtualization_sites.get_virtualization_site_vms(
|
||||
site_identifier=local_site_identifier
|
||||
)
|
||||
logging.debug(f"Site 1 VMs: {json.dumps(vms, indent=4)}")
|
||||
|
||||
vms_to_add = [args.vm1, args.vm2]
|
||||
vm_list = []
|
||||
for vm in vms:
|
||||
logging.info(f"VM: Name={vm.get('VmName')}, VM Identifier={vm.get('VmIdentifier')}")
|
||||
if vm.get('VmName') in vms_to_add:
|
||||
logging.info(f"Adding VM {vm.get('VmName')} to VPG...")
|
||||
vm_payload = {
|
||||
"VmIdentifier": vm.get('VmIdentifier'),
|
||||
"Recovery": {
|
||||
"HostIdentifier": peer_site_host_identifier,
|
||||
"DatastoreIdentifier": peer_datastore_identifier,
|
||||
"FolderIdentifier": peer_folder_identifier
|
||||
}
|
||||
}
|
||||
vm_list.append(vm_payload)
|
||||
task_id = client1.vpgs.add_vm_to_vpg(vpg_name, vm_list_payload=vm_payload)
|
||||
logging.info(f"Task ID: {task_id} to add VM {vm.get('VmName')} to VPG.")
|
||||
|
||||
input(f"Press Enter to remove {args.vm1} VM from the first VPG...")
|
||||
|
||||
# Remove first VM from the first VPG
|
||||
vm_to_remove = args.vm1
|
||||
vm_identifier_to_remove = None
|
||||
for vm in vms:
|
||||
if vm.get('VmName') == vm_to_remove:
|
||||
vm_identifier_to_remove = vm.get('VmIdentifier')
|
||||
task_id = client1.vpgs.remove_vm_from_vpg(vpg_name, vm_identifier_to_remove)
|
||||
logging.info(f"Task ID: {task_id} to remove VM {vm_to_remove} from VPG {vpg_name}")
|
||||
break
|
||||
|
||||
input("Press Enter to create second VPG...")
|
||||
|
||||
# Create second VPG
|
||||
vpg_name_2 = 'VpgTest2'
|
||||
basic['Name'] = vpg_name_2 # Update VPG name for second VPG
|
||||
|
||||
vpg_id_2 = client1.vpgs.create_vpg(basic=basic, journal=journal,
|
||||
recovery=recovery, networks=networks, sync=True)
|
||||
logging.info(f"Second VPG ID: {vpg_id_2} created successfully.")
|
||||
|
||||
# Add the removed VM to the second VPG
|
||||
if vm_identifier_to_remove:
|
||||
vm_payload = {
|
||||
"VmIdentifier": vm_identifier_to_remove,
|
||||
"Recovery": {
|
||||
"HostIdentifier": peer_site_host_identifier,
|
||||
"DatastoreIdentifier": peer_datastore_identifier,
|
||||
"FolderIdentifier": peer_folder_identifier
|
||||
}
|
||||
}
|
||||
task_id = client1.vpgs.add_vm_to_vpg(vpg_name_2, vm_list_payload=vm_payload)
|
||||
logging.info(f"Task ID: {task_id} to add VM {vm_to_remove} to VPG {vpg_name_2}")
|
||||
|
||||
except Exception as e:
|
||||
logging.exception("Error:")
|
||||
logging.error(f"Error: {e}")
|
||||
|
||||
|
||||
# wait for user input to continue
|
||||
input("Press Enter to delete the VPGs...")
|
||||
|
||||
# Delete the VPGs
|
||||
client1.vpgs.delete_vpg(vpg_name, force=True, keep_recovery_volumes=False)
|
||||
logging.info(f"VPG {vpg_name} deleted successfully.")
|
||||
client1.vpgs.delete_vpg(vpg_name_2, force=True, keep_recovery_volumes=False)
|
||||
logging.info(f"VPG {vpg_name_2} deleted successfully.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,262 @@
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Zerto Virtual Replication Appliance (VRA) Management Example Script
|
||||
|
||||
This script demonstrates how to manage VRAs using the Zerto Virtual Manager (ZVM) API.
|
||||
It showcases VRA deployment, configuration, and cleanup operations.
|
||||
|
||||
Key Features:
|
||||
1. VRA Management:
|
||||
- List existing VRAs and their details
|
||||
- Delete existing VRAs
|
||||
- Deploy new VRAs with custom configurations
|
||||
- Monitor VRA deployment status
|
||||
|
||||
2. Resource Selection:
|
||||
- Interactive selection of hosts
|
||||
- Interactive selection of datastores
|
||||
- Interactive selection of networks
|
||||
- Custom IP configuration for VRAs
|
||||
|
||||
3. Parallel Operations:
|
||||
- Deploy multiple VRAs simultaneously
|
||||
- Delete multiple VRAs in parallel
|
||||
- Monitor multiple VRA operations
|
||||
|
||||
Required Arguments:
|
||||
--zvm_address: ZVM server address
|
||||
--client_id: Keycloak client ID
|
||||
--client_secret: Keycloak client secret
|
||||
--ignore_ssl: Ignore SSL certificate verification (optional)
|
||||
|
||||
Example Usage:
|
||||
python examples/vras_example.py \
|
||||
--zvm_address "192.168.111.20" \
|
||||
--client_id "zerto-api" \
|
||||
--client_secret "your-secret-here" \
|
||||
--ignore_ssl
|
||||
|
||||
Script Flow:
|
||||
1. Lists all existing VRAs and their details
|
||||
2. Optionally deletes all existing VRAs
|
||||
3. Creates new VRAs with user-selected resources:
|
||||
- First VRA with IP 192.168.111.30
|
||||
- Second VRA with IP 192.168.111.31 (optional)
|
||||
4. Verifies final VRA configuration
|
||||
|
||||
Note: This script includes interactive prompts for resource selection and operation
|
||||
confirmation. It demonstrates proper error handling and logging for VRA management
|
||||
operations.
|
||||
"""
|
||||
|
||||
#!/usr/bin/python3
|
||||
import argparse
|
||||
import logging
|
||||
# logging.basicConfig(level=logging.DEBUG)
|
||||
import urllib3
|
||||
import json
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
from typing import Dict, List
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
from zvml import ZVMLClient
|
||||
|
||||
# Disable SSL warnings
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
|
||||
def setup_client(args):
|
||||
"""Initialize and return Zerto client"""
|
||||
client = ZVMLClient(
|
||||
zvm_address=args.zvm_address,
|
||||
client_id=args.client_id,
|
||||
client_secret=args.client_secret,
|
||||
verify_certificate=not args.ignore_ssl
|
||||
)
|
||||
return client
|
||||
|
||||
def get_user_confirmation(prompt: str) -> bool:
|
||||
"""Get user confirmation for an action"""
|
||||
while True:
|
||||
response = input(f"\n{prompt} (yes/no): ").lower().strip()
|
||||
if response in ['yes', 'y']:
|
||||
return True
|
||||
if response in ['no', 'n']:
|
||||
return False
|
||||
print("Please answer 'yes' or 'no'")
|
||||
|
||||
def select_from_list(items, item_type: str):
|
||||
"""Let user select an item from a list"""
|
||||
logging.info(f"select_from_list: {json.dumps(items, indent=2)}")
|
||||
print(f"\nAvailable {item_type}s:")
|
||||
for idx, item in enumerate(items, 1):
|
||||
if 'VirtualizationHostName' in item: # For hosts
|
||||
print(f"{idx}. {item['VirtualizationHostName']} ({item['HostIdentifier']})")
|
||||
elif 'DatastoreName' in item: # For datastores
|
||||
print(f"{idx}. {item['DatastoreName']} ({item['DatastoreIdentifier']})")
|
||||
elif 'VirtualizationNetworkName' in item: # For networks
|
||||
print(f"{idx}. {item['VirtualizationNetworkName']} ({item['NetworkIdentifier']})")
|
||||
|
||||
while True:
|
||||
try:
|
||||
choice = int(input(f"\nSelect {item_type} (1-{len(items)}): "))
|
||||
if 1 <= choice <= len(items):
|
||||
return items[choice - 1]
|
||||
except ValueError:
|
||||
pass
|
||||
print(f"Please enter a number between 1 and {len(items)}")
|
||||
|
||||
def list_vras(client: ZVMLClient):
|
||||
"""List all VRAs and their details"""
|
||||
vras = client.vras.list_vras()
|
||||
print(f"\nFound {len(vras)} VRAs:")
|
||||
for vra in vras:
|
||||
print(f"\nVRA Details:")
|
||||
print(f" Name: {vra.get('VraName')}")
|
||||
print(f" Status: {vra.get('Status')}")
|
||||
print(f" IP Address: {vra.get('IpAddress')}")
|
||||
print(f" Version: {vra.get('VraVersion')}")
|
||||
print(f" Host: {vra.get('HostName')}")
|
||||
|
||||
def create_vra_with_selection(client: ZVMLClient, vra_number: int) -> Dict:
|
||||
"""Create a VRA with user-selected resources"""
|
||||
# Get local site information
|
||||
local_site = client.localsite.get_local_site()
|
||||
site_id = local_site['SiteIdentifier']
|
||||
|
||||
# Get available resources for the local site
|
||||
print("\nRetrieving available resources...")
|
||||
hosts = client.virtualization_sites.get_virtualization_site_hosts(site_id)
|
||||
if not hosts:
|
||||
raise ValueError("No hosts found in local site")
|
||||
|
||||
datastores = client.virtualization_sites.get_virtualization_site_datastores(site_id)
|
||||
if not datastores:
|
||||
raise ValueError("No datastores found in local site")
|
||||
|
||||
networks = client.virtualization_sites.get_virtualization_site_networks(site_id)
|
||||
if not networks:
|
||||
raise ValueError("No networks found in local site")
|
||||
|
||||
# Let user select resources
|
||||
print("\nSelect resources for VRA deployment:")
|
||||
host = select_from_list(hosts, "Host")
|
||||
datastore = select_from_list(datastores, "Datastore")
|
||||
network = select_from_list(networks, "Network")
|
||||
|
||||
while True:
|
||||
# Let user customize IP address
|
||||
default_ip = f"192.168.111.{30 + vra_number - 1}"
|
||||
custom_ip = input(f"\nEnter VRA IP address (press Enter to use default {default_ip}): ").strip()
|
||||
vra_ip = custom_ip if custom_ip else default_ip
|
||||
|
||||
# Create VRA configuration
|
||||
vra_config = {
|
||||
"hostIdentifier": host['HostIdentifier'],
|
||||
"datastoreIdentifier": datastore['DatastoreIdentifier'],
|
||||
"networkIdentifier": network['NetworkIdentifier'],
|
||||
"hostRootPassword": input("\nEnter host root password: "),
|
||||
"memoryInGb": 3,
|
||||
"groupName": f"VRA_Group{vra_number}",
|
||||
"vraNetworkDataApi": {
|
||||
"vraIPConfigurationTypeApi": "Static",
|
||||
"vraIPAddress": vra_ip,
|
||||
"vraIPAddressRangeEnd": "",
|
||||
"subnetMask": "255.255.255.0",
|
||||
"defaultGateway": "192.168.111.254"
|
||||
},
|
||||
"usePublicKeyInsteadOfCredentials": False,
|
||||
"populatePostInstallation": True,
|
||||
"numOfCpus": 1,
|
||||
"vmInstanceType": ""
|
||||
}
|
||||
|
||||
# Create VRA
|
||||
print(f"\nCreating VRA {vra_number} with configuration:")
|
||||
print(json.dumps(vra_config, indent=2))
|
||||
|
||||
response = input("\nProceed with VRA creation? (yes/no/edit): ").lower().strip()
|
||||
if response in ['yes', 'y']:
|
||||
result = client.vras.create_vra(vra_config)
|
||||
print(f"VRA creation initiated: {json.dumps(result, indent=2)}")
|
||||
return result
|
||||
elif response in ['no', 'n']:
|
||||
return None
|
||||
elif response in ['edit', 'e']:
|
||||
print("\nRestarting VRA configuration...")
|
||||
continue
|
||||
else:
|
||||
print("Please answer 'yes', 'no', or 'edit'")
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="VRA Management Example")
|
||||
parser.add_argument("--zvm_address", required=True, help="ZVM address")
|
||||
parser.add_argument('--client_id', required=True, help='Keycloak client ID')
|
||||
parser.add_argument('--client_secret', required=True, help='Keycloak client secret')
|
||||
parser.add_argument("--ignore_ssl", action="store_true", help="Ignore SSL certificate verification")
|
||||
args = parser.parse_args()
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
|
||||
try:
|
||||
# Setup client
|
||||
client = setup_client(args)
|
||||
|
||||
# Step 1: List existing VRAs
|
||||
print("\nStep 1: Listing existing VRAs...")
|
||||
list_vras(client)
|
||||
|
||||
# Step 2: Ask for deletion confirmation
|
||||
if get_user_confirmation("Would you like to delete all existing VRAs?"):
|
||||
print("\nStep 2: Deleting existing VRAs...")
|
||||
vras = client.vras.list_vras()
|
||||
for vra in vras:
|
||||
vra_id = vra.get('VraIdentifier')
|
||||
print(f"Deleting VRA: {vra.get('VraName')} ({vra_id})")
|
||||
client.vras.delete_vra(vra_id)
|
||||
print("Waiting for deletion to complete...")
|
||||
time.sleep(5) # Give some time for deletion to process
|
||||
|
||||
# Step 3: Create new VRAs
|
||||
if get_user_confirmation("Would you like to create new VRAs?"):
|
||||
print("\nStep 3: Creating new VRAs...")
|
||||
|
||||
# Create first VRA
|
||||
print("\nConfiguring first VRA...")
|
||||
result1 = create_vra_with_selection(client, 1)
|
||||
if result1:
|
||||
print("Waiting for first VRA deployment to complete...")
|
||||
time.sleep(30)
|
||||
|
||||
# Create second VRA
|
||||
if get_user_confirmation("Would you like to create a second VRA?"):
|
||||
print("\nConfiguring second VRA...")
|
||||
result2 = create_vra_with_selection(client, 2)
|
||||
if result2:
|
||||
print("Waiting for second VRA deployment to complete...")
|
||||
time.sleep(30)
|
||||
|
||||
# Step 4: Verify final state
|
||||
print("\nStep 4: Verifying final VRA configuration...")
|
||||
list_vras(client)
|
||||
|
||||
except Exception as e:
|
||||
logging.exception("Error occurred:")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,139 @@
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Zerto Organizations (ZORG) Management Example Script
|
||||
|
||||
This script demonstrates how to manage Zerto Organizations (ZORGs) using the Zerto Virtual Manager (ZVM) API.
|
||||
It showcases ZORG querying and information retrieval operations.
|
||||
|
||||
Key Features:
|
||||
1. ZORG Management:
|
||||
- List all ZORGs in the environment
|
||||
- Query specific ZORG details by ID
|
||||
- Retrieve detailed ZORG information
|
||||
- Demonstrate ZORG filtering capabilities
|
||||
|
||||
2. Information Retrieval:
|
||||
- Get ZORG identifiers
|
||||
- Access ZORG configuration details
|
||||
- View ZORG relationships and permissions
|
||||
- Monitor ZORG status
|
||||
|
||||
3. Error Handling:
|
||||
- Robust error handling for API requests
|
||||
- Detailed logging of operations
|
||||
- Graceful handling of missing ZORGs
|
||||
|
||||
Required Arguments:
|
||||
--zvm_address: ZVM server address
|
||||
--client_id: Keycloak client ID
|
||||
--client_secret: Keycloak client secret
|
||||
--ignore_ssl: Ignore SSL certificate verification (optional)
|
||||
--zorg_id: Optional specific ZORG ID to query
|
||||
|
||||
Example Usage:
|
||||
python examples/zorgs_example.py \
|
||||
--zvm_address "192.168.111.20" \
|
||||
--client_id "zerto-api" \
|
||||
--client_secret "your-secret-here" \
|
||||
--ignore_ssl \
|
||||
--zorg_id "optional-zorg-id"
|
||||
|
||||
Script Flow:
|
||||
1. Connects to ZVM server
|
||||
2. Lists all available ZORGs
|
||||
3. If specific ZORG ID provided:
|
||||
- Retrieves detailed information for that ZORG
|
||||
4. Otherwise:
|
||||
- Gets details of first available ZORG
|
||||
5. Outputs detailed ZORG information
|
||||
|
||||
Note: This script demonstrates basic ZORG management capabilities and can be used
|
||||
as a foundation for more complex ZORG operations and automation.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import urllib3
|
||||
import json
|
||||
from zvml import ZVMLClient
|
||||
|
||||
# Disable SSL warnings
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Zerto Organizations (ZORG) Example")
|
||||
parser.add_argument("--zvm_address", required=True, help="ZVM address")
|
||||
parser.add_argument('--client_id', required=True, help='Keycloak client ID')
|
||||
parser.add_argument('--client_secret', required=True, help='Keycloak client secret')
|
||||
parser.add_argument("--ignore_ssl", action="store_true", help="Ignore SSL certificate verification")
|
||||
parser.add_argument("--zorg_id", help="Optional: Specific ZORG ID to query")
|
||||
args = parser.parse_args()
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
|
||||
try:
|
||||
# Connect to ZVM
|
||||
logging.info(f"Connecting to ZVM at {args.zvm_address}")
|
||||
client = ZVMLClient(
|
||||
zvm_address=args.zvm_address,
|
||||
client_id=args.client_id,
|
||||
client_secret=args.client_secret,
|
||||
verify_certificate=not args.ignore_ssl
|
||||
)
|
||||
|
||||
# Test 1: Get all ZORGs
|
||||
logging.info("\n=== Testing get_zorgs (all) ===")
|
||||
try:
|
||||
zorgs = client.zorgs.get_zorgs()
|
||||
logging.info("All ZORGs:")
|
||||
logging.info(json.dumps(zorgs, indent=2))
|
||||
except Exception as e:
|
||||
logging.error(f"Error getting all ZORGs: {e}")
|
||||
|
||||
# Test 2: Get specific ZORG if ID provided
|
||||
if args.zorg_id:
|
||||
logging.info(f"\n=== Testing get_zorgs with ID: {args.zorg_id} ===")
|
||||
try:
|
||||
zorg_details = client.zorgs.get_zorgs(args.zorg_id)
|
||||
logging.info("ZORG details:")
|
||||
logging.info(json.dumps(zorg_details, indent=2))
|
||||
except Exception as e:
|
||||
logging.error(f"Error getting ZORG {args.zorg_id}: {e}")
|
||||
|
||||
# Test 3: Get first ZORG details if any exist
|
||||
elif zorgs and len(zorgs) > 0:
|
||||
first_zorg = zorgs[0]
|
||||
zorg_identifier = first_zorg.get('ZorgIdentifier')
|
||||
if zorg_identifier:
|
||||
logging.info(f"\n=== Testing get_zorgs with first found ID: {zorg_identifier} ===")
|
||||
try:
|
||||
zorg_details = client.zorgs.get_zorgs(zorg_identifier)
|
||||
logging.info("First ZORG details:")
|
||||
logging.info(json.dumps(zorg_details, indent=2))
|
||||
except Exception as e:
|
||||
logging.error(f"Error getting ZORG {zorg_identifier}: {e}")
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error occurred: {e}")
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user