Files
zvml-python-sdk/examples/vpg_vms_example.py
T

312 lines
12 KiB
Python

# Legal Disclaimer
# This script is an example script and is not supported under any Zerto support program or service.
# The author and Zerto further disclaim all implied warranties including, without limitation,
# any implied warranties of merchantability or of fitness for a particular purpose.
# In no event shall Zerto, its authors or anyone else involved in the creation,
# production or delivery of the scripts be liable for any damages whatsoever (including,
# without limitation, damages for loss of business profits, business interruption, loss of business
# information, or other pecuniary loss) arising out of the use of or the inability to use the sample
# scripts or documentation, even if the author or Zerto has been advised of the possibility of such damages.
# The entire risk arising out of the use or performance of the sample scripts and documentation remains with you.
"""
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
}
}
}
scratch = {
"Limitation": {
"HardLimitInMB": 407200,
"HardLimitInPercent": 0,
"WarningThresholdInMB": 400000,
"WarningThresholdInPercent": 0
}
}
input("Press Enter to create the first VPG...")
vpg_id = client1.vpgs.create_vpg(basic=basic, journal=journal,
recovery=recovery, networks=networks, scratch=scratch, 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()