commit 3a075e5ecf1a56660285241b103f454fc560e3fe Author: Kosta Mushkin Date: Thu May 22 10:10:15 2025 -0400 initial commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..e27a594 --- /dev/null +++ b/README.md @@ -0,0 +1,90 @@ +# Zerto Python SDK Hands-On Lab + +This hands-on lab provides practical experience with the Zerto Python SDK. The lab is designed to take approximately 60 minutes to complete and covers key aspects of the SDK including authentication, site discovery, VPG operations, and more. + +## Prerequisites + +- Python 3.8 or higher +- Access to a Zerto Virtual Manager (ZVM) +- API credentials (client ID and secret) +- Basic understanding of Python programming +- Basic understanding of Zerto concepts + +## Lab Structure + +The lab is divided into 7 exercises: + +1. **Introduction to Zerto APIs** (5 min) + - Understanding Zerto's REST API + - API documentation overview + - Keycloak authentication basics + +2. **Authentication** (10 min) + - Creating a Keycloak client + - Establishing connection to ZVM + - Testing authentication + +3. **Site Discovery** (5 min) + - Listing virtualization sites + - Understanding site information + - Basic site operations + +4. **Resource Discovery** (10 min) + - Discovering local site resources + - Working with peer sites + - Understanding resource identifiers + +5. **VPG Operations** (15 min) + - Creating VPGs + - Adding VMs to VPGs + - Managing VPG settings + - VPG validation + +6. **Failover Testing** (10 min) + - Initiating failover tests + - Monitoring test status + - Stopping tests + +7. **Bulk Operations** (5 min) + - Performing bulk IP modifications + - Managing multiple VMs + +## Getting Started + +1. Clone this repository: + ```bash + git clone + cd Zerto-Python-SDK-Hands-On-Labs + ``` + +2. Install required packages: + ```bash + pip install -r prerequisites/requirements.txt + ``` + +3. Set up your environment: + - Copy `prerequisites/config.example.py` to `prerequisites/config.py` + - Update the configuration with your ZVM details + +4. Start with Exercise 1 in the `exercises` directory + +## Lab Completion + +Each exercise includes: +- Step-by-step instructions +- Working directory for your code +- Solution directory for reference +- Common issues and troubleshooting + +Complete all exercises to gain a comprehensive understanding of the Zerto Python SDK. + +## Support + +If you encounter any issues during the lab: +1. Check the troubleshooting section in each exercise +2. Review the solution code +3. Consult the Zerto API documentation + +## Feedback + +Your feedback is valuable! Please complete the feedback form after finishing the lab to help us improve the content. \ No newline at end of file diff --git a/exercises/01_introduction/README.md b/exercises/01_introduction/README.md new file mode 100644 index 0000000..db4f83c --- /dev/null +++ b/exercises/01_introduction/README.md @@ -0,0 +1,32 @@ +# Exercise 1: Introduction to Zerto APIs + +## Overview +This exercise introduces you to Zerto's REST API and the Python SDK. You'll learn about the API structure, authentication methods, and basic concepts. + +## Objectives +- Understand Zerto's REST API architecture +- Learn about Keycloak authentication +- Review API documentation +- Set up your development environment + +## Time +5 minutes + +## Prerequisites +- Python 3.8 or higher installed +- Access to Zerto API documentation +- Basic understanding of REST APIs + +## Exercise Steps +1. Review the API documentation +2. Understand the authentication flow +3. Explore the SDK structure + +## Key Concepts +- REST API basics +- Keycloak authentication +- API endpoints +- SDK architecture + +## Next Steps +Proceed to Exercise 2: Authentication to start working with the SDK. \ No newline at end of file diff --git a/exercises/01_introduction/solution/.gitkeep b/exercises/01_introduction/solution/.gitkeep new file mode 100644 index 0000000..0519ecb --- /dev/null +++ b/exercises/01_introduction/solution/.gitkeep @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exercises/01_introduction/working/.gitkeep b/exercises/01_introduction/working/.gitkeep new file mode 100644 index 0000000..0519ecb --- /dev/null +++ b/exercises/01_introduction/working/.gitkeep @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/exercises/02_authentication/README.md b/exercises/02_authentication/README.md new file mode 100644 index 0000000..2fe3f43 --- /dev/null +++ b/exercises/02_authentication/README.md @@ -0,0 +1,46 @@ +# Exercise 2: Authentication + +## Overview +In this exercise, you'll learn how to authenticate with the Zerto API using Keycloak. You'll create a client and establish a connection to your ZVM. + +## Objectives +- Create a Keycloak client +- Configure authentication parameters +- Test the connection to ZVM +- Handle authentication errors + +## Time +10 minutes + +## Prerequisites +- Completed Exercise 1 +- Valid ZVM credentials +- Client ID and secret + +## Exercise Steps +1. Set up your configuration +2. Create the Keycloak client +3. Test the connection +4. Handle authentication errors + +## Working Directory +The `working` directory contains: +- `auth.py` - Template to complete + +## Solution +The `solution` directory contains: +- `auth.py` - Complete working example + +## Key Concepts +- Keycloak authentication +- Client credentials flow +- Error handling +- Connection management + +## Common Issues +- Invalid credentials +- SSL certificate issues +- Network connectivity problems + +## Next Steps +Proceed to Exercise 3: Site Discovery to start working with Zerto sites. \ No newline at end of file diff --git a/exercises/02_authentication/solution/auth.py b/exercises/02_authentication/solution/auth.py new file mode 100644 index 0000000..b33aeb3 --- /dev/null +++ b/exercises/02_authentication/solution/auth.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 +""" +Exercise 2: Authentication - Solution +This script demonstrates how to authenticate with Zerto API using Keycloak. + +Prerequisites: +1. Install the zvml package in development mode: + cd /path/to/zvml-python-sdk + pip install -e . +2. Update prerequisites/config.py with your ZVM details + +This solution demonstrates: +- ZVMLClient initialization with Keycloak authentication +- Connection testing using local site information +- Proper error handling and logging +""" + +import sys +import os +import logging +import json +from pathlib import Path + +# Add prerequisites to Python path +prerequisites_path = Path(__file__).parent.parent.parent.parent / "prerequisites" +sys.path.append(str(prerequisites_path)) + +# Import the SDK modules +from zvml import ZVMLClient + +# Import configuration +try: + from config import ( + ZVM_HOST, + ZVM_PORT, + ZVM_SSL_VERIFY, + CLIENT_ID, + CLIENT_SECRET + ) +except ImportError: + print("Error: Please copy config.example.py to config.py and update with your values") + print("Expected path:", prerequisites_path / "config.py") + sys.exit(1) + +def main(): + """ + Main function to demonstrate Zerto authentication. + Shows how to: + 1. Initialize ZVMLClient with Keycloak credentials + 2. Test connection by retrieving local site info + 3. Handle authentication and connection errors + """ + # Set up logging with timestamp + logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(levelname)s - %(message)s' + ) + + try: + # Step 1: Create a ZVMLClient instance + logging.info(f"Initializing ZVMLClient for ZVM at {ZVM_HOST}") + client = ZVMLClient( + zvm_address=ZVM_HOST, + client_id=CLIENT_ID, + client_secret=CLIENT_SECRET, + verify_certificate=ZVM_SSL_VERIFY + ) + + # Step 2: Test the connection by getting local site info + logging.info("Testing connection by retrieving local site information...") + local_site = client.localsite.get_local_site() + + # Extract and log version information + version = local_site.get('Version') + logging.info(f"Successfully connected to ZVM version: {version}") + + # Optional: Log additional site details if needed + # logging.debug(f"Full site details: {json.dumps(local_site, indent=2)}") + + # Step 3: Print final connection status + logging.info("Connection successful!") + + except Exception as e: + logging.error(f"Authentication failed: {str(e)}") + sys.exit(1) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/exercises/02_authentication/working/auth.py b/exercises/02_authentication/working/auth.py new file mode 100644 index 0000000..5ef635d --- /dev/null +++ b/exercises/02_authentication/working/auth.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 +""" +Exercise 2: Authentication +This script demonstrates how to authenticate with Zerto API using Keycloak. + +Prerequisites: +1. Install the zvml package in development mode: + cd /path/to/zvml-python-sdk + pip install -e . +2. Update prerequisites/config.py with your ZVM details + +Your task: +1. Initialize ZVMLClient with Keycloak credentials +2. Test connection by retrieving local site information +3. Handle authentication and connection errors + +If you need help, check the solution in the solution directory. +""" + +import sys +import os +import logging +import json +from pathlib import Path + +# Add prerequisites to Python path +prerequisites_path = Path(__file__).parent.parent.parent.parent / "prerequisites" +sys.path.append(str(prerequisites_path)) + +# Import the SDK modules +from zvml import ZVMLClient + +# Import configuration +try: + from config import ( + ZVM_HOST, + ZVM_PORT, + ZVM_SSL_VERIFY, + CLIENT_ID, + CLIENT_SECRET + ) +except ImportError: + print("Error: Please copy config.example.py to config.py and update with your values") + print("Expected path:", prerequisites_path / "config.py") + sys.exit(1) + +def main(): + """ + Main function to demonstrate Zerto authentication. + Complete the following steps: + 1. Initialize ZVMLClient with Keycloak credentials + 2. Test connection by retrieving local site info + 3. Handle authentication and connection errors + """ + # Set up logging with timestamp + logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(levelname)s - %(message)s' + ) + + try: + # Step 1: Create a ZVMLClient instance + # TODO: Initialize the ZVMLClient with your ZVM host and credentials + # Hint: Use ZVMLClient(zvm_address=ZVM_HOST, client_id=CLIENT_ID, + # client_secret=CLIENT_SECRET, verify_certificate=ZVM_SSL_VERIFY) + client = None # Replace with actual client initialization + + # Step 2: Test the connection + # TODO: Try to get local site information to verify the connection + # Hint: Use client.localsite.get_local_site() and extract the Version + # Hint: Log the version using logging.info() + pass # Replace with actual connection test + + # Step 3: Print connection status + # TODO: Display whether the connection was successful + # Hint: Use logging.info() to show the status + + except Exception as e: + # TODO: Handle any authentication or connection errors + # Hint: Use logging.error() to log the error message + logging.error(f"Authentication failed: {str(e)}") + sys.exit(1) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/exercises/03_site_discovery/README.md b/exercises/03_site_discovery/README.md new file mode 100644 index 0000000..9bcc198 --- /dev/null +++ b/exercises/03_site_discovery/README.md @@ -0,0 +1,44 @@ +# Exercise 3: Site Discovery + +## Overview +In this exercise, you'll learn how to discover and work with Zerto virtualization sites. You'll explore how to list available sites and retrieve local site information. + +## Objectives +- List all available virtualization sites +- Get and display local site information +- Work with site data using JSON + +## Time +5 minutes + +## Prerequisites +- Completed Exercise 2 (Authentication) +- Working authentication +- Access to ZVM + +## Exercise Steps +1. Connect to ZVM using ZVMLClient +2. List all available virtualization sites +3. Get and display local site information + +## Working Directory +The `working` directory contains: +- `sites.py` - Template to complete + +## Solution +The `solution` directory contains: +- `sites.py` - Complete working example + +## Key Concepts +- Virtualization site discovery +- Local site information +- JSON data handling +- Logging with timestamps + +## Common Issues +- No sites found +- Authentication issues +- Connection problems + +## Next Steps +Proceed to Exercise 4: Resource Discovery to learn about site resources. \ No newline at end of file diff --git a/exercises/03_site_discovery/solution/sites.py b/exercises/03_site_discovery/solution/sites.py new file mode 100644 index 0000000..d1388c4 --- /dev/null +++ b/exercises/03_site_discovery/solution/sites.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 +""" +Exercise 3: Site Discovery - Solution +This script demonstrates how to discover and work with Zerto virtualization sites. + +Prerequisites: +1. Install the zvml package in development mode: + cd /path/to/zvml-python-sdk + pip install -e . +2. Update prerequisites/config.py with your ZVM details + +This solution demonstrates: +- Listing all available virtualization sites +- Retrieving and displaying local site information +- Proper error handling and logging +""" + +import sys +import os +import logging +import json +from pathlib import Path + +# Add prerequisites to Python path +prerequisites_path = Path(__file__).parent.parent.parent.parent / "prerequisites" +sys.path.append(str(prerequisites_path)) + +# Import the SDK modules +from zvml import ZVMLClient + +# Import configuration +try: + from config import ( + ZVM_HOST, + ZVM_PORT, + ZVM_SSL_VERIFY, + CLIENT_ID, + CLIENT_SECRET + ) +except ImportError: + print("Error: Please copy config.example.py to config.py and update with your values") + print("Expected path:", prerequisites_path / "config.py") + sys.exit(1) + +def main(): + """ + Main function to demonstrate site discovery. + Shows how to: + 1. List all available virtualization sites + 2. Get and display local site information + """ + # Set up logging with timestamp + logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(levelname)s - %(message)s' + ) + + try: + # Step 1: Create a ZVMLClient instance + logging.info(f"Initializing ZVMLClient for ZVM at {ZVM_HOST}") + client = ZVMLClient( + zvm_address=ZVM_HOST, + client_id=CLIENT_ID, + client_secret=CLIENT_SECRET, + verify_certificate=ZVM_SSL_VERIFY + ) + + # Step 2: List all available sites + logging.info("Retrieving list of available sites...") + sites = client.virtualization_sites.get_virtualization_sites() + + if not sites: + logging.warning("No sites found!") + else: + logging.info(f"Found {len(sites)} site(s):") + logging.info(f'Sites Info: {json.dumps(sites, indent=4)}') + + # Step 3: Get and display local site information + logging.info("\nRetrieving local site information...") + local_site = client.localsite.get_local_site() + logging.info(f"Local site details: {json.dumps(local_site, indent=4)}") + + except Exception as e: + logging.error(f"Site discovery failed: {str(e)}") + sys.exit(1) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/exercises/03_site_discovery/working/sites.py b/exercises/03_site_discovery/working/sites.py new file mode 100644 index 0000000..ca84902 --- /dev/null +++ b/exercises/03_site_discovery/working/sites.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 +""" +Exercise 3: Site Discovery +This script demonstrates how to discover and work with Zerto virtualization sites. + +Prerequisites: +1. Install the zvml package in development mode: + cd /path/to/zvml-python-sdk + pip install -e . +2. Update prerequisites/config.py with your ZVM details + +Your task: +1. List all available virtualization sites +2. Get and display local site information + +If you need help, check the solution in the solution directory. +""" + +import sys +import os +import logging +import json +from pathlib import Path + +# Add prerequisites to Python path +prerequisites_path = Path(__file__).parent.parent.parent.parent / "prerequisites" +sys.path.append(str(prerequisites_path)) + +# Import the SDK modules +from zvml import ZVMLClient + +# Import configuration +try: + from config import ( + ZVM_HOST, + ZVM_PORT, + ZVM_SSL_VERIFY, + CLIENT_ID, + CLIENT_SECRET + ) +except ImportError: + print("Error: Please copy config.example.py to config.py and update with your values") + print("Expected path:", prerequisites_path / "config.py") + sys.exit(1) + +def main(): + """ + Main function to demonstrate site discovery. + Complete the following steps: + 1. List all available virtualization sites + 2. Get and display local site information + """ + # Set up logging with timestamp + logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(levelname)s - %(message)s' + ) + + try: + # Step 1: Create a ZVMLClient instance + # TODO: Initialize the ZVMLClient with your ZVM host and credentials + # Hint: Use ZVMLClient(zvm_address=ZVM_HOST, client_id=CLIENT_ID, + # client_secret=CLIENT_SECRET, verify_certificate=ZVM_SSL_VERIFY) + client = None # Replace with actual client initialization + + # Step 2: List all available sites + # TODO: Get the list of virtualization sites + # Hint: Use client.virtualization_sites.get_virtualization_sites() + # Hint: Log the number of sites found and their details using json.dumps() + pass # Replace with actual site listing + + # Step 3: Get and display local site information + # TODO: Get and display local site information + # Hint: Use client.localsite.get_local_site() + # Hint: Log the local site details using json.dumps() + pass # Replace with actual local site retrieval + + except Exception as e: + # TODO: Handle any site discovery errors + # Hint: Use logging.error() to log the error message + logging.error(f"Site discovery failed: {str(e)}") + sys.exit(1) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/exercises/04_resource_discovery/README.md b/exercises/04_resource_discovery/README.md new file mode 100644 index 0000000..b8c11c4 --- /dev/null +++ b/exercises/04_resource_discovery/README.md @@ -0,0 +1,57 @@ +# Exercise 4: Resource Discovery + +## Overview +This exercise focuses on discovering and working with resources in your Zerto environment. You'll learn how to find VMs in your local site and discover various resources (datastores, hosts, folders, networks) in peer sites. + +## Objectives +- Discover local site VMs +- Work with peer site resources: + - Datastores + - Hosts + - Folders + - Networks +- Handle site identifiers properly +- Use JSON for data display + +## Time +15 minutes + +## Prerequisites +- Completed Exercise 3 (Site Discovery) +- Working site discovery +- Access to site resources + +## Exercise Steps +1. Connect to ZVM +2. Identify local and peer sites +3. Discover local site VMs +4. Get peer site resources: + - Datastores + - Hosts + - Folders + - Networks + +## Working Directory +The `working` directory contains: +- `resources.py` - Template to complete + +## Solution +The `solution` directory contains: +- `resources.py` - Complete working example + +## Key Concepts +- Site resource discovery +- Local vs peer site resources +- Resource types (VMs, datastores, hosts, folders, networks) +- JSON data handling +- Error handling + +## Common Issues +- No sites found +- Invalid site identifiers +- Authentication issues +- Connection problems +- Resource access issues + +## Next Steps +Proceed to Exercise 5: VPG Management to learn about protecting VMs. \ No newline at end of file diff --git a/exercises/04_resource_discovery/solution/resources.py b/exercises/04_resource_discovery/solution/resources.py new file mode 100644 index 0000000..bb88385 --- /dev/null +++ b/exercises/04_resource_discovery/solution/resources.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python3 +""" +Exercise 4: Resource Discovery - Solution +This script demonstrates how to discover and work with resources in your Zerto environment. + +Prerequisites: +1. Install the zvml package in development mode: + cd /path/to/zvml-python-sdk + pip install -e . +2. Update prerequisites/config.py with your ZVM details + +This solution demonstrates: +- Discovering local site resources (clusters, hosts, datastores) +- Working with peer site resources +- Proper error handling and logging +""" + +import sys +import os +import logging +import json +from pathlib import Path + +# Add prerequisites to Python path +prerequisites_path = Path(__file__).parent.parent.parent.parent / "prerequisites" +sys.path.append(str(prerequisites_path)) + +# Import the SDK modules +from zvml import ZVMLClient + +# Import configuration +try: + from config import ( + ZVM_HOST, + ZVM_PORT, + ZVM_SSL_VERIFY, + CLIENT_ID, + CLIENT_SECRET + ) +except ImportError: + print("Error: Please copy config.example.py to config.py and update with your values") + print("Expected path:", prerequisites_path / "config.py") + sys.exit(1) + +def main(): + """ + Main function to demonstrate resource discovery. + Shows how to: + 1. Discover local site resources + 2. Work with peer site resources + """ + # Set up logging with timestamp + logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(levelname)s - %(message)s' + ) + + try: + # Step 1: Create a ZVMLClient instance + logging.info(f"Initializing ZVMLClient for ZVM at {ZVM_HOST}") + client = ZVMLClient( + zvm_address=ZVM_HOST, + client_id=CLIENT_ID, + client_secret=CLIENT_SECRET, + verify_certificate=ZVM_SSL_VERIFY + ) + + # Step 2: Identify local and peer sites + # Step 2.1: List all available sites + logging.info("Retrieving list of available sites...") + sites = client.virtualization_sites.get_virtualization_sites() + + if not sites: + logging.warning("No sites found!") + else: + logging.info(f"Found {len(sites)} site(s):") + logging.info(f'Sites Info: {json.dumps(sites, indent=4)}') + + # Step 2.2: Get local and peer site Identifiers + local_site_identifier = client.localsite.get_local_site().get('SiteIdentifier') + logging.info(f"Local site identifier: {local_site_identifier}") + + # Get peer site identifier (first non-local site) + peer_site = next((site for site in sites if site.get('SiteIdentifier') != local_site_identifier), None) + if not peer_site: + logging.warning("No peer site found!") + sys.exit(1) + + peer_site_identifier = peer_site.get('SiteIdentifier') + logging.info(f"Peer site identifier: {peer_site_identifier}") + + + # Step 3: Get local site resources + # Step 3.1: Get local site vms + logging.info("\nRetrieving local site vms...") + local_vms = client.virtualization_sites.get_virtualization_site_vms(site_identifier=local_site_identifier) + logging.info(f"Local Vms Info: {json.dumps(local_vms, indent=4)}") + + # in order to create a DR solution, we need to get information about the local site vms and peer site datastores, hosts, folders and networks + # Step 3.2: Get peer site datastores + logging.info("\nRetrieving peer site datastores...") + peer_datastores = client.virtualization_sites.get_virtualization_site_datastores(site_identifier=peer_site_identifier) + logging.info(f"Peer Datastores Info: {json.dumps(peer_datastores, indent=4)}") + + # Step 3.3: Get peer site hosts + logging.info("\nRetrieving peer site hosts...") + peer_hosts = client.virtualization_sites.get_virtualization_site_hosts(site_identifier=peer_site_identifier) + logging.info(f"Peer Hosts Info: {json.dumps(peer_hosts, indent=4)}") + + # Step 3.4: Get peer site folders + logging.info("\nRetrieving peer site folders...") + peer_folders = client.virtualization_sites.get_virtualization_site_folders(site_identifier=peer_site_identifier) + logging.info(f"Peer Folders Info: {json.dumps(peer_folders, indent=4)}") + + # Step 3.5: Get peer site networks + logging.info("\nRetrieving peer site networks...") + peer_networks = client.virtualization_sites.get_virtualization_site_networks(site_identifier=peer_site_identifier) + logging.info(f"Peer Networks Info: {json.dumps(peer_networks, indent=4)}") + + except Exception as e: + logging.error(f"Resource discovery failed: {str(e)}") + sys.exit(1) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/exercises/04_resource_discovery/working/resources.py b/exercises/04_resource_discovery/working/resources.py new file mode 100644 index 0000000..f3f7a36 --- /dev/null +++ b/exercises/04_resource_discovery/working/resources.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python3 +""" +Exercise 4: Resource Discovery +This script demonstrates how to discover and work with resources in your Zerto environment. + +Prerequisites: +1. Install the zvml package in development mode: + cd /path/to/zvml-python-sdk + pip install -e . +2. Update prerequisites/config.py with your ZVM details + +Your task: +1. Discover local site resources (VMs) +2. Work with peer site resources (datastores, hosts, folders, networks) + +If you need help, check the solution in the solution directory. +""" + +import sys +import os +import logging +import json +from pathlib import Path + +# Add prerequisites to Python path +prerequisites_path = Path(__file__).parent.parent.parent.parent / "prerequisites" +sys.path.append(str(prerequisites_path)) + +# Import the SDK modules +from zvml import ZVMLClient + +# Import configuration +try: + from config import ( + ZVM_HOST, + ZVM_PORT, + ZVM_SSL_VERIFY, + CLIENT_ID, + CLIENT_SECRET + ) +except ImportError: + print("Error: Please copy config.example.py to config.py and update with your values") + print("Expected path:", prerequisites_path / "config.py") + sys.exit(1) + +def main(): + """ + Main function to demonstrate resource discovery. + Complete the following steps: + 1. Discover local site resources (VMs) + 2. Work with peer site resources (datastores, hosts, folders, networks) + """ + # Set up logging with timestamp + logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(levelname)s - %(message)s' + ) + + try: + # Step 1: Create a ZVMLClient instance + # TODO: Initialize the ZVMLClient with your ZVM host and credentials + # Hint: Use ZVMLClient(zvm_address=ZVM_HOST, client_id=CLIENT_ID, + # client_secret=CLIENT_SECRET, verify_certificate=ZVM_SSL_VERIFY) + client = None # Replace with actual client initialization + + # Step 2: Identify local and peer sites + # Step 2.1: List all available sites + # TODO: Get the list of available sites + # Hint: Use client.virtualization_sites.get_virtualization_sites() + # Hint: Log the number of sites found and their details using json.dumps() + pass # Replace with actual site listing + + # Step 2.2: Get local and peer site identifiers + # TODO: Get local site identifier + # Hint: Use client.localsite.get_local_site() + # Hint: Extract the SiteIdentifier from the response + local_site_identifier = None # Replace with actual local site identifier + + # TODO: Get peer site identifier + # Hint: Find the first site that is not the local site + # Hint: Extract the SiteIdentifier from that site + peer_site_identifier = None # Replace with actual peer site identifier + + # Step 3: Get local site resources + # Step 3.1: Get local site VMs + # TODO: Get VMs for the local site + # Hint: Use client.virtualization_sites.get_virtualization_site_vms() + # Hint: Pass the local_site_identifier as site_identifier parameter + # Hint: Log the VMs information using json.dumps() + pass # Replace with actual VM retrieval + + # Step 3.2: Get peer site datastores + # TODO: Get datastores for the peer site + # Hint: Use client.virtualization_sites.get_virtualization_site_datastores() + # Hint: Pass the peer_site_identifier as site_identifier parameter + # Hint: Log the datastores information using json.dumps() + pass # Replace with actual datastore retrieval + + # Step 3.3: Get peer site hosts + # TODO: Get hosts for the peer site + # Hint: Use client.virtualization_sites.get_virtualization_site_hosts() + # Hint: Pass the peer_site_identifier as site_identifier parameter + # Hint: Log the hosts information using json.dumps() + pass # Replace with actual host retrieval + + # Step 3.4: Get peer site folders + # TODO: Get folders for the peer site + # Hint: Use client.virtualization_sites.get_virtualization_site_folders() + # Hint: Pass the peer_site_identifier as site_identifier parameter + # Hint: Log the folders information using json.dumps() + pass # Replace with actual folder retrieval + + # Step 3.5: Get peer site networks + # TODO: Get networks for the peer site + # Hint: Use client.virtualization_sites.get_virtualization_site_networks() + # Hint: Pass the peer_site_identifier as site_identifier parameter + # Hint: Log the networks information using json.dumps() + pass # Replace with actual network retrieval + + except Exception as e: + # TODO: Handle any resource discovery errors + # Hint: Use logging.error() to log the error message + logging.error(f"Resource discovery failed: {str(e)}") + sys.exit(1) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/exercises/05_vpg_operations/README.md b/exercises/05_vpg_operations/README.md new file mode 100644 index 0000000..4f95907 --- /dev/null +++ b/exercises/05_vpg_operations/README.md @@ -0,0 +1,52 @@ +# Exercise 5: VPG Operations + +## Overview +This exercise covers the core functionality of VPG management. You'll learn how to create VPGs, manage VMs within them, and handle VPG settings. + +## Objectives +- Create new VPGs +- Add VMs to VPGs +- Remove VMs from VPGs +- Manage VPG settings +- Validate VPG configurations + +## Time +15 minutes + +## Prerequisites +- Completed Exercise 4 +- Working resource discovery +- Access to VMs for protection + +## Exercise Steps +1. Create a new VPG +2. Add VMs to the VPG +3. Configure VPG settings +4. Validate the VPG +5. Remove VMs from VPG + +## Working Directory +The `working` directory contains: +- `create_vpg.py` - Template for VPG creation +- `manage_vms.py` - Template for VM management + +## Solution +The `solution` directory contains: +- `create_vpg.py` - Complete VPG creation example +- `manage_vms.py` - Complete VM management example + +## Key Concepts +- VPG creation +- VM management +- VPG settings +- VPG validation +- Resource allocation + +## Common Issues +- Invalid VPG settings +- VM compatibility issues +- Resource constraints +- Validation failures + +## Next Steps +Proceed to Exercise 6: Failover Testing to learn about VPG testing. \ No newline at end of file diff --git a/exercises/05_vpg_operations/solution/create_vpg.py b/exercises/05_vpg_operations/solution/create_vpg.py new file mode 100644 index 0000000..3f0092e --- /dev/null +++ b/exercises/05_vpg_operations/solution/create_vpg.py @@ -0,0 +1,214 @@ +#!/usr/bin/env python3 +""" +Exercise 5: VPG Operations - Solution (Part 1: VPG Creation) +This script demonstrates how to create and configure VPGs in your Zerto environment. + +Prerequisites: +1. Install the zvml package in development mode: + cd /path/to/zvml-python-sdk + pip install -e . +2. Update prerequisites/config.py with your ZVM details + +This solution demonstrates: +- Creating a new VPG with basic settings +- Configuring journal, recovery, and network settings +- Adding VMs to the VPG +- Proper error handling and logging +""" + +import sys +import os +import logging +import json +from pathlib import Path + +# Add prerequisites to Python path +prerequisites_path = Path(__file__).parent.parent.parent.parent / "prerequisites" +sys.path.append(str(prerequisites_path)) + +# Import the SDK modules +from zvml import ZVMLClient + +# Import configuration +try: + from config import ( + ZVM_HOST, + ZVM_PORT, + ZVM_SSL_VERIFY, + CLIENT_ID, + CLIENT_SECRET + ) +except ImportError: + print("Error: Please copy config.example.py to config.py and update with your values") + print("Expected path:", prerequisites_path / "config.py") + sys.exit(1) + +def main(): + """ + Main function to demonstrate VPG creation. + Shows how to: + 1. Create a new VPG with basic settings + 2. Configure journal, recovery, and network settings + 3. Add VMs to the VPG + """ + # Set up logging with timestamp + logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(levelname)s - %(message)s' + ) + + try: + # Step 1: Create a ZVMLClient instance + logging.info(f"Initializing ZVMLClient for ZVM at {ZVM_HOST}") + client = ZVMLClient( + zvm_address=ZVM_HOST, + client_id=CLIENT_ID, + client_secret=CLIENT_SECRET, + verify_certificate=ZVM_SSL_VERIFY + ) + + # Step 2: Identify local and peer sites + logging.info("Retrieving list of available sites...") + sites = client.virtualization_sites.get_virtualization_sites() + + if not sites: + logging.warning("No sites found!") + sys.exit(1) + + logging.info(f"Found {len(sites)} site(s):") + logging.info(f'Sites Info: {json.dumps(sites, indent=4)}') + + # Get local and peer site identifiers + local_site = client.localsite.get_local_site() + local_site_identifier = local_site.get('SiteIdentifier') + logging.info(f"Local site identifier: {local_site_identifier}") + + peer_site = next((site for site in sites if site.get('SiteIdentifier') != local_site_identifier), None) + if not peer_site: + logging.warning("No peer site found!") + sys.exit(1) + + peer_site_identifier = peer_site.get('SiteIdentifier') + logging.info(f"Peer site identifier: {peer_site_identifier}") + + # Step 3: Get peer site resources for VPG configuration + logging.info("\nRetrieving peer site resources for VPG configuration...") + + # Get peer datastores + peer_datastores = client.virtualization_sites.get_virtualization_site_datastores(site_identifier=peer_site_identifier) + if not peer_datastores: + logging.warning("No datastores found in peer site!") + sys.exit(1) + target_datastore = peer_datastores[0] # Use first available datastore + + # Get peer folders + peer_folders = client.virtualization_sites.get_virtualization_site_folders(site_identifier=peer_site_identifier) + if not peer_folders: + logging.warning("No folders found in peer site!") + sys.exit(1) + target_folder = peer_folders[0] # Use first available folder + + # Get peer networks + peer_networks = client.virtualization_sites.get_virtualization_site_networks(site_identifier=peer_site_identifier) + if not peer_networks: + logging.warning("No networks found in peer site!") + sys.exit(1) + target_network = peer_networks[0] # Use first available network + + # Get peer hosts + peer_hosts = client.virtualization_sites.get_virtualization_site_hosts(site_identifier=peer_site_identifier) + if not peer_hosts: + logging.warning("No hosts found in peer site!") + sys.exit(1) + target_host = peer_hosts[0] # Use first available host + + # Step 4: Create VPG configuration + logging.info("\nCreating VPG configuration...") + vpg_name = "Test-VPG-Python" + + # Basic VPG settings + basic = { + "Name": vpg_name, + "VpgType": "Remote", + "RpoInSeconds": 300, + "JournalHistoryInHours": 24, + "Priority": "Medium", + "UseWanCompression": True, + "ProtectedSiteIdentifier": local_site_identifier, + "RecoverySiteIdentifier": peer_site_identifier + } + + # Journal settings + journal = { + "DatastoreIdentifier": target_datastore.get('DatastoreIdentifier'), + "Limitation": { + "HardLimitInMB": 153600, + "WarningThresholdInMB": 115200 + } + } + + # Recovery settings + recovery = { + "DefaultHostIdentifier": target_host.get('HostIdentifier'), + "DefaultDatastoreIdentifier": target_datastore.get('DatastoreIdentifier'), + "DefaultFolderIdentifier": target_folder.get('FolderIdentifier') + } + + # Network settings + networks = { + "Failover": { + "Hypervisor": { + "DefaultNetworkIdentifier": target_network.get('NetworkIdentifier') + } + }, + "FailoverTest": { + "Hypervisor": { + "DefaultNetworkIdentifier": target_network.get('NetworkIdentifier') + } + } + } + + # Step 5: Create VPG + logging.info(f"\nCreating VPG '{vpg_name}'...") + logging.info("VPG Settings:") + logging.info(f"Basic: {json.dumps(basic, indent=4)}") + logging.info(f"Journal: {json.dumps(journal, indent=4)}") + logging.info(f"Recovery: {json.dumps(recovery, indent=4)}") + logging.info(f"Networks: {json.dumps(networks, indent=4)}") + + # Create VPG + vpg_id = client.vpgs.create_vpg(basic=basic, journal=journal, recovery=recovery, networks=networks, sync=True) + logging.info(f"VPG created successfully with ID: {vpg_id}") + + # Step 6: Get available VMs for protection + logging.info("\nRetrieving available VMs for protection...") + vms = client.virtualization_sites.get_virtualization_site_vms(site_identifier=local_site_identifier) + + if not vms: + logging.warning("No VMs found for protection!") + sys.exit(1) + + # Filter out VMs that are already protected + available_vms = [vm for vm in vms if not vm.get('IsProtected')] + logging.info(f"Found {len(available_vms)} available VM(s) for protection") + + # Step 7: Add VMs to VPG + for vm in available_vms[:2]: # Add first two available VMs + logging.info(f"\nAdding VM {vm.get('VmName')} to VPG...") + vm_payload = { + "VmIdentifier": vm.get('VmIdentifier'), + "Recovery": { + "HostIdentifier": target_host.get('HostIdentifier'), + "DatastoreIdentifier": target_datastore.get('DatastoreIdentifier'), + "FolderIdentifier": target_folder.get('FolderIdentifier') + } + } + task_id = client.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") + + except Exception as e: + logging.error(f"VPG creation failed: {str(e)}") + sys.exit(1) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/exercises/05_vpg_operations/solution/manage_vms.py b/exercises/05_vpg_operations/solution/manage_vms.py new file mode 100644 index 0000000..f226093 --- /dev/null +++ b/exercises/05_vpg_operations/solution/manage_vms.py @@ -0,0 +1,162 @@ +#!/usr/bin/env python3 +""" +Exercise 5: VPG Operations - Solution (Part 2: VM Management) +This script demonstrates how to manage VMs within VPGs in your Zerto environment. + +Prerequisites: +1. Install the zvml package in development mode: + cd /path/to/zvml-python-sdk + pip install -e . +2. Update prerequisites/config.py with your ZVM details +3. Run create_vpg.py first to create a VPG + +This solution demonstrates: +- Adding VMs to existing VPGs using add_vm_to_vpg +- Removing VMs from VPGs using remove_vm_from_vpg +- Proper error handling and logging +""" + +import sys +import os +import logging +import json +from pathlib import Path + +# Add prerequisites to Python path +prerequisites_path = Path(__file__).parent.parent.parent.parent / "prerequisites" +sys.path.append(str(prerequisites_path)) + +# Import the SDK modules +from zvml import ZVMLClient + +# Import configuration +try: + from config import ( + ZVM_HOST, + ZVM_PORT, + ZVM_SSL_VERIFY, + CLIENT_ID, + CLIENT_SECRET + ) +except ImportError: + print("Error: Please copy config.example.py to config.py and update with your values") + print("Expected path:", prerequisites_path / "config.py") + sys.exit(1) + +def main(): + """ + Main function to demonstrate VM management in VPGs. + Shows how to: + 1. Add VMs to an existing VPG using add_vm_to_vpg + 2. Remove VMs from a VPG using remove_vm_from_vpg + """ + # Set up logging with timestamp + logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(levelname)s - %(message)s' + ) + + try: + # Step 1: Create a ZVMLClient instance + logging.info(f"Initializing ZVMLClient for ZVM at {ZVM_HOST}") + client = ZVMLClient( + zvm_address=ZVM_HOST, + client_id=CLIENT_ID, + client_secret=CLIENT_SECRET, + verify_certificate=ZVM_SSL_VERIFY + ) + + # Step 2: Find the VPG we created earlier + logging.info("\nRetrieving VPGs...") + vpgs = client.vpgs.get_vpgs() + + if not vpgs: + logging.warning("No VPGs found!") + sys.exit(1) + + # Find our test VPG + test_vpg = next((vpg for vpg in vpgs if vpg.get('VpgName') == "Test-VPG-Python"), None) + if not test_vpg: + logging.warning("Test VPG not found! Please run create_vpg.py first.") + sys.exit(1) + + vpg_name = test_vpg.get('VpgName') + logging.info(f"Found VPG: {json.dumps(test_vpg, indent=4)}") + + # Step 3: Get available VMs for adding to VPG + logging.info("\nRetrieving available VMs...") + vms = client.virtualization_sites.get_virtualization_site_vms(site_identifier=test_vpg.get('SourceSiteIdentifier')) + + if not vms: + logging.warning("No VMs found!") + sys.exit(1) + + # Filter out VMs that are already protected + available_vms = [vm for vm in vms if not vm.get('IsProtected')] + logging.info(f"Found {len(available_vms)} available VM(s)") + + if not available_vms: + logging.warning("No available VMs to add!") + sys.exit(1) + + # Step 4: Get peer site resources for VM recovery settings + logging.info("\nRetrieving peer site resources...") + peer_site_identifier = test_vpg.get('TargetSiteIdentifier') + + # Get peer datastores + peer_datastores = client.virtualization_sites.get_virtualization_site_datastores(site_identifier=peer_site_identifier) + if not peer_datastores: + logging.warning("No datastores found in peer site!") + sys.exit(1) + target_datastore = peer_datastores[0] # Use first available datastore + + # Get peer folders + peer_folders = client.virtualization_sites.get_virtualization_site_folders(site_identifier=peer_site_identifier) + if not peer_folders: + logging.warning("No folders found in peer site!") + sys.exit(1) + target_folder = peer_folders[0] # Use first available folder + + # Get peer hosts + peer_hosts = client.virtualization_sites.get_virtualization_site_hosts(site_identifier=peer_site_identifier) + if not peer_hosts: + logging.warning("No hosts found in peer site!") + sys.exit(1) + target_host = peer_hosts[0] # Use first available host + + # Step 5: Add a new VM to the VPG + logging.info("\nAdding a new VM to the VPG...") + vm_to_add = available_vms[0] # Use first available VM + + vm_payload = { + "VmIdentifier": vm_to_add.get('VmIdentifier'), + "Recovery": { + "HostIdentifier": target_host.get('HostIdentifier'), + "DatastoreIdentifier": target_datastore.get('DatastoreIdentifier'), + "FolderIdentifier": target_folder.get('FolderIdentifier') + } + } + + task_id = client.vpgs.add_vm_to_vpg(vpg_name, vm_list_payload=vm_payload) + logging.info(f"Task ID: {task_id} to add VM {vm_to_add.get('VmName')} to VPG") + + # Step 6: Remove a VM from the VPG + logging.info("\nRemoving a VM from the VPG...") + + # Get VPG VMs + vpg_vms = client.vpgs.get_vpg_vms(vpg_name) + if len(vpg_vms) > 1: # Keep at least one VM + vm_to_remove = vpg_vms[-1] # Remove the last VM + vm_identifier = vm_to_remove.get('VmIdentifier') + + task_id = client.vpgs.remove_vm_from_vpg(vpg_name, vm_identifier) + logging.info(f"Task ID: {task_id} to remove VM {vm_to_remove.get('VmName')} from VPG") + else: + logging.info("Skipping VM removal to maintain at least one VM in the VPG") + + except Exception as e: + logging.error(f"VM management failed: {str(e)}") + sys.exit(1) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/exercises/05_vpg_operations/working/create_vpg.py b/exercises/05_vpg_operations/working/create_vpg.py new file mode 100644 index 0000000..3b2bd01 --- /dev/null +++ b/exercises/05_vpg_operations/working/create_vpg.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +""" +Exercise 5: VPG Operations - VPG Creation +This script demonstrates how to create and configure a VPG. +""" + +import sys +from pathlib import Path + +# Add the parent directory to the Python path to import the SDK +sys.path.append(str(Path(__file__).parent.parent.parent.parent)) + +# Import the SDK modules +from zvml import ZertoClient +from zvml.vpgs import VPG +from zvml.common import ZertoVPGError + +# Import configuration +try: + from prerequisites.config import ( + ZVM_HOST, + ZVM_PORT, + ZVM_SSL_VERIFY, + KEYCLOAK_SERVER_URL, + KEYCLOAK_REALM, + CLIENT_ID, + CLIENT_SECRET + ) +except ImportError: + print("Error: Please copy config.example.py to config.py and update with your values") + sys.exit(1) + +def main(): + """ + Main function to demonstrate VPG creation. + """ + # Step 1: Create and authenticate ZertoClient + # TODO: Initialize ZertoClient and authenticate + # Hint: Reuse the authentication code from previous exercises + + # Step 2: Get source and target sites + # TODO: Get local site and a peer site + # Hint: Use client.sites.get_local() and client.sites.list() + + # Step 3: Configure VPG settings + # TODO: Create VPG settings with required parameters + # Required settings: + # - VPG name + # - Source site + # - Target site + # - Journal history + # - RPO + # - Test network + # - Recovery network + + # Step 4: Create the VPG + # TODO: Create a new VPG with the configured settings + # Hint: Use client.vpgs.create() method + + # Step 5: Validate the VPG + # TODO: Validate the VPG configuration + # Hint: Use vpg.validate() method + + # Step 6: Handle errors + # TODO: Add error handling for VPG operations + # Hint: Use try/except blocks for ZertoVPGError + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/exercises/05_vpg_operations/working/manage_vms.py b/exercises/05_vpg_operations/working/manage_vms.py new file mode 100644 index 0000000..7cc2e07 --- /dev/null +++ b/exercises/05_vpg_operations/working/manage_vms.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +""" +Exercise 5: VPG Operations - VM Management +This script demonstrates how to manage VMs within a VPG. +""" + +import sys +from pathlib import Path + +# Add the parent directory to the Python path to import the SDK +sys.path.append(str(Path(__file__).parent.parent.parent.parent)) + +# Import the SDK modules +from zvml import ZertoClient +from zvml.vpgs import VPG +from zvml.common import ZertoVPGError + +# Import configuration +try: + from prerequisites.config import ( + ZVM_HOST, + ZVM_PORT, + ZVM_SSL_VERIFY, + KEYCLOAK_SERVER_URL, + KEYCLOAK_REALM, + CLIENT_ID, + CLIENT_SECRET + ) +except ImportError: + print("Error: Please copy config.example.py to config.py and update with your values") + sys.exit(1) + +def main(): + """ + Main function to demonstrate VM management in VPGs. + """ + # Step 1: Create and authenticate ZertoClient + # TODO: Initialize ZertoClient and authenticate + # Hint: Reuse the authentication code from previous exercises + + # Step 2: Get the VPG + # TODO: Find and get the VPG you want to manage + # Hint: Use client.vpgs.list() and client.vpgs.get() + + # Step 3: List current VMs in the VPG + # TODO: Get a list of VMs currently in the VPG + # Hint: Use vpg.get_vms() method + + # Step 4: Add VMs to the VPG + # TODO: Add one or more VMs to the VPG + # Required steps: + # - Find eligible VMs + # - Configure VM settings + # - Add VMs to VPG + # Hint: Use vpg.add_vms() method + + # Step 5: Remove VMs from the VPG + # TODO: Remove one or more VMs from the VPG + # Hint: Use vpg.remove_vms() method + + # Step 6: Validate VPG after changes + # TODO: Validate the VPG after VM changes + # Hint: Use vpg.validate() method + + # Step 7: Handle errors + # TODO: Add error handling for VM operations + # Hint: Use try/except blocks for ZertoVPGError + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/exercises/06_failover_test/README.md b/exercises/06_failover_test/README.md new file mode 100644 index 0000000..9947ec7 --- /dev/null +++ b/exercises/06_failover_test/README.md @@ -0,0 +1,48 @@ +# Exercise 6: Failover Testing + +## Overview +In this exercise, you'll learn how to perform and manage failover tests for your VPGs. You'll understand the testing process and how to monitor test status. + +## Objectives +- Initiate failover tests +- Monitor test status +- Stop running tests +- Handle test results + +## Time +10 minutes + +## Prerequisites +- Completed Exercise 5 +- Working VPG +- Access to test resources + +## Exercise Steps +1. Select a VPG for testing +2. Initiate a failover test +3. Monitor test progress +4. Stop the test +5. Review test results + +## Working Directory +The `working` directory contains: +- `failover.py` - Template to complete + +## Solution +The `solution` directory contains: +- `failover.py` - Complete working example + +## Key Concepts +- Failover testing +- Test monitoring +- Test management +- Status tracking + +## Common Issues +- Test initiation failures +- Resource conflicts +- Test timeout +- Cleanup issues + +## Next Steps +Proceed to Exercise 7: Bulk Operations to learn about managing multiple VMs. \ No newline at end of file diff --git a/exercises/06_failover_test/working/failover.py b/exercises/06_failover_test/working/failover.py new file mode 100644 index 0000000..72f6870 --- /dev/null +++ b/exercises/06_failover_test/working/failover.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 +""" +Exercise 6: Failover Testing +This script demonstrates how to perform and manage failover tests. +""" + +import sys +import time +from pathlib import Path + +# Add the parent directory to the Python path to import the SDK +sys.path.append(str(Path(__file__).parent.parent.parent.parent)) + +# Import the SDK modules +from zvml import ZertoClient +from zvml.vpgs import VPG +from zvml.common import ZertoVPGError + +# Import configuration +try: + from prerequisites.config import ( + ZVM_HOST, + ZVM_PORT, + ZVM_SSL_VERIFY, + KEYCLOAK_SERVER_URL, + KEYCLOAK_REALM, + CLIENT_ID, + CLIENT_SECRET + ) +except ImportError: + print("Error: Please copy config.example.py to config.py and update with your values") + sys.exit(1) + +def main(): + """ + Main function to demonstrate failover testing. + """ + # Step 1: Create and authenticate ZertoClient + # TODO: Initialize ZertoClient and authenticate + # Hint: Reuse the authentication code from previous exercises + + # Step 2: Get the VPG + # TODO: Find and get the VPG you want to test + # Hint: Use client.vpgs.list() and client.vpgs.get() + + # Step 3: Initiate failover test + # TODO: Start a failover test for the VPG + # Required steps: + # - Configure test settings + # - Start the test + # Hint: Use vpg.start_test() method + + # Step 4: Monitor test progress + # TODO: Monitor the test status until completion + # Required steps: + # - Get test status + # - Check for completion + # - Handle any errors + # Hint: Use vpg.get_test_status() method + + # Step 5: Stop the test + # TODO: Stop the running test + # Hint: Use vpg.stop_test() method + + # Step 6: Clean up + # TODO: Ensure proper cleanup after the test + # Hint: Check if any cleanup is needed + + # Step 7: Handle errors + # TODO: Add error handling for test operations + # Hint: Use try/except blocks for ZertoVPGError + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/exercises/07_bulk_operations/README.md b/exercises/07_bulk_operations/README.md new file mode 100644 index 0000000..14709c2 --- /dev/null +++ b/exercises/07_bulk_operations/README.md @@ -0,0 +1,57 @@ +# Exercise 7: Bulk Operations + +## Overview +This final exercise covers bulk operations, focusing on managing multiple VMs efficiently. You'll learn how to perform operations on multiple VMs simultaneously. + +## Objectives +- Perform bulk IP modifications +- Manage multiple VMs +- Handle bulk operations efficiently +- Monitor bulk task progress + +## Time +5 minutes + +## Prerequisites +- Completed Exercise 6 +- Working VPGs with multiple VMs +- Access to VM management + +## Exercise Steps +1. Prepare VM list +2. Configure bulk operations +3. Execute bulk IP changes +4. Monitor operation progress +5. Verify changes + +## Working Directory +The `working` directory contains: +- `bulk_ip.py` - Template to complete +- `vm_list.csv` - Sample VM list + +## Solution +The `solution` directory contains: +- `bulk_ip.py` - Complete working example +- `vm_list.csv` - Example VM list + +## Key Concepts +- Bulk operations +- IP management +- Task monitoring +- Error handling + +## Common Issues +- Invalid IP configurations +- Operation timeouts +- Partial failures +- Resource constraints + +## Lab Completion +Congratulations! You have completed all exercises in the Zerto Python SDK Hands-On Lab. You should now have a good understanding of: +- Zerto API basics +- Authentication and connection +- Site and resource management +- VPG operations +- Testing and bulk operations + +Please complete the feedback form to help us improve the lab content. \ No newline at end of file diff --git a/exercises/07_bulk_operations/working/bulk_ip.py b/exercises/07_bulk_operations/working/bulk_ip.py new file mode 100644 index 0000000..199975d --- /dev/null +++ b/exercises/07_bulk_operations/working/bulk_ip.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 +""" +Exercise 7: Bulk Operations +This script demonstrates how to perform bulk IP modifications on multiple VMs. +""" + +import sys +import csv +from pathlib import Path + +# Add the parent directory to the Python path to import the SDK +sys.path.append(str(Path(__file__).parent.parent.parent.parent)) + +# Import the SDK modules +from zvml import ZertoClient +from zvml.vpgs import VPG +from zvml.common import ZertoVPGError + +# Import configuration +try: + from prerequisites.config import ( + ZVM_HOST, + ZVM_PORT, + ZVM_SSL_VERIFY, + KEYCLOAK_SERVER_URL, + KEYCLOAK_REALM, + CLIENT_ID, + CLIENT_SECRET + ) +except ImportError: + print("Error: Please copy config.example.py to config.py and update with your values") + sys.exit(1) + +def read_vm_list(csv_file): + """ + Read VM list from CSV file. + Expected format: vm_name,ip_address,subnet_mask,gateway + """ + # TODO: Implement CSV reading + # Hint: Use csv.DictReader + pass + +def main(): + """ + Main function to demonstrate bulk IP operations. + """ + # Step 1: Create and authenticate ZertoClient + # TODO: Initialize ZertoClient and authenticate + # Hint: Reuse the authentication code from previous exercises + + # Step 2: Read VM list + # TODO: Read the VM list from CSV file + # Hint: Use the read_vm_list function + + # Step 3: Get VPG + # TODO: Find and get the VPG containing the VMs + # Hint: Use client.vpgs.list() and client.vpgs.get() + + # Step 4: Prepare IP changes + # TODO: Prepare the IP modification data + # Required for each VM: + # - VM identifier + # - New IP settings + # - Network information + + # Step 5: Apply IP changes + # TODO: Apply the IP changes to all VMs + # Hint: Use vpg.modify_vm_ips() method + + # Step 6: Monitor progress + # TODO: Monitor the bulk operation progress + # Required steps: + # - Track operation status + # - Handle any failures + # - Report results + + # Step 7: Handle errors + # TODO: Add error handling for bulk operations + # Hint: Use try/except blocks for ZertoVPGError + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/exercises/07_bulk_operations/working/vm_list.csv b/exercises/07_bulk_operations/working/vm_list.csv new file mode 100644 index 0000000..ffe9f7a --- /dev/null +++ b/exercises/07_bulk_operations/working/vm_list.csv @@ -0,0 +1,6 @@ +vm_name,ip_address,subnet_mask,gateway +vm-001,192.168.1.101,255.255.255.0,192.168.1.1 +vm-002,192.168.1.102,255.255.255.0,192.168.1.1 +vm-003,192.168.1.103,255.255.255.0,192.168.1.1 +vm-004,192.168.1.104,255.255.255.0,192.168.1.1 +vm-005,192.168.1.105,255.255.255.0,192.168.1.1 \ No newline at end of file diff --git a/prerequisites/__init__.py b/prerequisites/__init__.py new file mode 100644 index 0000000..3cf90cc --- /dev/null +++ b/prerequisites/__init__.py @@ -0,0 +1,4 @@ +""" +Prerequisites package for Zerto Python SDK Hands-On Lab. +Contains configuration and common utilities. +""" \ No newline at end of file diff --git a/prerequisites/__pycache__/config.cpython-313.pyc b/prerequisites/__pycache__/config.cpython-313.pyc new file mode 100644 index 0000000..4b452f1 Binary files /dev/null and b/prerequisites/__pycache__/config.cpython-313.pyc differ diff --git a/prerequisites/config.py b/prerequisites/config.py new file mode 100644 index 0000000..853a670 --- /dev/null +++ b/prerequisites/config.py @@ -0,0 +1,17 @@ +# Zerto API Configuration +# Update these values with your ZVM details + +# ZVM Connection Details +ZVM_HOST = "192.168.111.20" #"zvm.example.com" +ZVM_PORT = 443 # Default HTTPS port +ZVM_SSL_VERIFY = False # Set to False for self-signed certificates + +# Keycloak Authentication +CLIENT_ID = "zerto-api" #"your-client-id" +CLIENT_SECRET = "q0GUcbK1olq2Op27cpjvTHT5scKeQWBy" # "your-client-secret" + +# Optional: Proxy settings if needed +# PROXY = { +# "http": "http://proxy.example.com:8080", +# "https": "https://proxy.example.com:8080" +# } \ No newline at end of file diff --git a/prerequisites/py.typed b/prerequisites/py.typed new file mode 100644 index 0000000..0519ecb --- /dev/null +++ b/prerequisites/py.typed @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/prerequisites/requirements.txt b/prerequisites/requirements.txt new file mode 100644 index 0000000..d5bc8ca --- /dev/null +++ b/prerequisites/requirements.txt @@ -0,0 +1,4 @@ +requests>=2.31.0 +python-keycloak>=2.8.0 +urllib3>=2.0.0 +python-dotenv>=1.0.0 \ No newline at end of file