From bd951d0811ece0ecda5df05025d8f5f7451111cd Mon Sep 17 00:00:00 2001 From: Kosta Mushkin Date: Thu, 12 Jun 2025 20:09:35 -0400 Subject: [PATCH] simplified instructions --- README.md | 48 ++- exercises/02_authentication/README.md | 99 ++++-- exercises/02_authentication/working/auth.py | 138 +++++--- exercises/03_site_discovery/working/sites.py | 147 +++++--- .../working/resources.py | 240 +++++++++---- .../05_vpg_operations/working/create_vpg.py | 335 +++++++++++++----- .../06_failover_test/working/failover.py | 305 ++++++++++------ 7 files changed, 916 insertions(+), 396 deletions(-) diff --git a/README.md b/README.md index 32caf67..928af31 100644 --- a/README.md +++ b/README.md @@ -58,21 +58,57 @@ The lab is divided into 7 exercises: ``` 2. Create and activate a virtual environment: + + **On macOS/Linux:** ```bash python3 -m venv venv - source venv/bin/activate # On Windows, use: venv\Scripts\activate + source venv/bin/activate ``` -3. Install required packages: + **On Windows:** + ```cmd + python -m venv venv + venv\Scripts\activate + ``` + + **On Windows PowerShell:** + ```powershell + python -m venv venv + venv\Scripts\Activate.ps1 + ``` + +3. Install the Zerto Python SDK (`zvml` module): + + **Option 1: Standard install** + ```bash + cd ../zvml-python-sdk + pip install -r requirements.txt + pip install . + cd ../Zerto-Python-SDK-Hands-On-Labs + ``` + + **Option 2: Development mode (recommended for contributors)** + ```bash + cd ../zvml-python-sdk + pip install -r requirements.txt + pip install -e . + cd ../Zerto-Python-SDK-Hands-On-Labs + ``` + +4. Install required packages: ```bash pip install -r prerequisites/requirements.txt ``` -4. Set up your environment: +5. Set up your environment: - Copy `prerequisites/config.example.py` to `prerequisites/config.py` - - Update the configuration with your ZVM details + - Update the configuration with your ZVM details: + - **ZVM_HOST**: Your Zerto Virtual Manager IP address or hostname + - **CLIENT_ID**: Your Keycloak client ID (see Keycloak setup in config.example.py) + - **CLIENT_SECRET**: Your Keycloak client secret + - **Important**: Never commit your `config.py` file as it contains sensitive credentials -5. Start with Exercise 1 in the `exercises` directory +6. Start with Exercise 1 in the `exercises` directory **Note:** When you're done working on the project, you can deactivate the virtual environment by typing `deactivate` in your terminal. @@ -95,4 +131,4 @@ If you encounter any issues during the lab: ## 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 +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/02_authentication/README.md b/exercises/02_authentication/README.md index 2fe3f43..81fe554 100644 --- a/exercises/02_authentication/README.md +++ b/exercises/02_authentication/README.md @@ -3,44 +3,93 @@ ## 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 +## What You'll Learn +- How to create a ZVMLClient object (your "remote control" for ZVM) +- How to connect to your Zerto Virtual Manager +- How to test if the connection works +- How to handle errors if something goes wrong ## Time 10 minutes ## Prerequisites -- Completed Exercise 1 -- Valid ZVM credentials -- Client ID and secret +- ✅ Completed Exercise 1 +- ✅ Valid ZVM credentials (IP address, client ID, client secret) +- ✅ Updated `prerequisites/config.py` with your details -## Exercise Steps -1. Set up your configuration -2. Create the Keycloak client -3. Test the connection -4. Handle authentication errors +## Step-by-Step Instructions + +### Step 1: Open the Working File +1. Navigate to `exercises/02_authentication/working/` +2. Open `auth.py` in your code editor +3. Read through the detailed comments - they explain everything! + +### Step 2: Complete the Code +The file has three main sections you need to complete: + +1. **Create ZVMLClient** - Replace `client = None` with actual code +2. **Test Connection** - Add code to test if it works +3. **Success Message** - Add a final success message + +### Step 3: Run Your Code +```bash +cd exercises/02_authentication/working/ +python auth.py +``` + +### Step 4: Check the Results +- ✅ **Success**: You'll see "Connection successful!" and ZVM version info +- ❌ **Error**: Check the error message and review your config.py ## Working Directory The `working` directory contains: -- `auth.py` - Template to complete +- `auth.py` - **Beginner-friendly template** with detailed instructions ## Solution The `solution` directory contains: -- `auth.py` - Complete working example +- `auth.py` - Complete working example (check this if you get stuck!) -## Key Concepts -- Keycloak authentication -- Client credentials flow -- Error handling -- Connection management +## Key Concepts Explained -## Common Issues -- Invalid credentials -- SSL certificate issues -- Network connectivity problems +### What is ZVMLClient? +- Think of it as a "remote control" for your Zerto Virtual Manager +- It handles all the communication between your Python code and ZVM +- You need to give it your ZVM address and login credentials + +### What is Keycloak? +- It's the authentication system that Zerto uses +- You create a "client" (like a username) and get a "secret" (like a password) +- The ZVMLClient uses these to log into your ZVM + +### What is SSL Verification? +- It's a security check to make sure you're connecting to the right server +- Usually set to `False` for Zerto (self-signed certificates) + +## Common Issues & Solutions + +### ❌ "Configuration file not found" +**Solution**: Copy `prerequisites/config.example.py` to `prerequisites/config.py` + +### ❌ "Authentication failed" +**Solutions**: +- Check your ZVM_HOST is correct +- Verify your CLIENT_ID and CLIENT_SECRET +- Make sure your ZVM is running and accessible + +### ❌ "SSL certificate" errors +**Solution**: Make sure `ZVM_SSL_VERIFY = False` in your config.py + +### ❌ "Connection refused" or "Network unreachable" +**Solutions**: +- Check your ZVM IP address is correct +- Make sure you can ping the ZVM from your computer +- Check firewall settings + +## Need Help? +1. Read the detailed comments in the code +2. Check the solution file +3. Review your `config.py` settings +4. Ask your Zerto administrator for help with credentials ## Next Steps -Proceed to Exercise 3: Site Discovery to start working with Zerto sites. \ No newline at end of file +Once you successfully connect, proceed to **Exercise 3: Site Discovery** to start working with Zerto sites. \ No newline at end of file diff --git a/exercises/02_authentication/working/auth.py b/exercises/02_authentication/working/auth.py index 5ef635d..f0f3123 100644 --- a/exercises/02_authentication/working/auth.py +++ b/exercises/02_authentication/working/auth.py @@ -1,20 +1,31 @@ #!/usr/bin/env python3 """ -Exercise 2: Authentication +Exercise 2: Authentication - Beginner-Friendly Instructions 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 +PREREQUISITES (Complete these first): +1. Make sure you have the zvml package installed (see main README) +2. Update prerequisites/config.py with your ZVM details: + - ZVM_HOST: Your Zerto Virtual Manager IP address or hostname + - CLIENT_ID: Your Keycloak client ID + - CLIENT_SECRET: Your Keycloak client secret -Your task: -1. Initialize ZVMLClient with Keycloak credentials -2. Test connection by retrieving local site information -3. Handle authentication and connection errors +WHAT YOU NEED TO DO: +In this exercise, you will: +1. Create a ZVMLClient object to connect to your ZVM +2. Test the connection by getting information about your local site +3. Handle any errors that might occur -If you need help, check the solution in the solution directory. +STEP-BY-STEP INSTRUCTIONS: +1. Look at the TODO comments below - they tell you exactly what to do +2. Replace the placeholder code with the actual code +3. Each step has hints and examples to help you +4. If you get stuck, check the solution file in the solution/ directory + +WHAT IS A ZVMLClient? +- It's like a "remote control" for your Zerto Virtual Manager +- It handles all the communication with your ZVM +- You need to give it your ZVM address and login credentials """ import sys @@ -23,61 +34,106 @@ import logging import json from pathlib import Path -# Add prerequisites to Python path +# Add prerequisites to Python path (this helps Python find your config file) prerequisites_path = Path(__file__).parent.parent.parent.parent / "prerequisites" sys.path.append(str(prerequisites_path)) -# Import the SDK modules +# Import the Zerto SDK - this gives us the ZVMLClient class from zvml import ZVMLClient -# Import configuration +# Import your configuration settings try: from config import ( - ZVM_HOST, - ZVM_PORT, - ZVM_SSL_VERIFY, - CLIENT_ID, - CLIENT_SECRET + ZVM_HOST, # Your ZVM IP address (e.g., "192.168.1.100") + ZVM_PORT, # Usually 443 for HTTPS + ZVM_SSL_VERIFY, # True/False for SSL certificate verification + CLIENT_ID, # Your Keycloak client ID (e.g., "my-api-client") + CLIENT_SECRET # Your Keycloak client secret ) except ImportError: - print("Error: Please copy config.example.py to config.py and update with your values") + print("❌ ERROR: Configuration file not found!") + print("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 + Main function - this is where your code goes! + Follow the step-by-step instructions below. """ - # Set up logging with timestamp + # Set up logging so you can see what's happening logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s' ) + print("🚀 Starting Zerto Authentication Exercise") + print("=" * 50) + 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 1: Create a ZVMLClient instance + # ======================================== + print("\n📝 STEP 1: Creating ZVMLClient...") + print("You need to replace the line 'client = None' with actual code.") + print("Look at the hint below for the correct syntax.") - # 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 + # TODO: Replace this line with actual ZVMLClient creation + # HINT: Use this syntax: + # client = ZVMLClient( + # zvm_address=ZVM_HOST, + # client_id=CLIENT_ID, + # client_secret=CLIENT_SECRET, + # verify_certificate=ZVM_SSL_VERIFY + # ) + # + # EXPLANATION: + # - zvm_address: Where your ZVM is located (from config.py) + # - client_id: Your Keycloak client ID (from config.py) + # - client_secret: Your Keycloak client secret (from config.py) + # - verify_certificate: Whether to check SSL certificates (from config.py) - # Step 3: Print connection status - # TODO: Display whether the connection was successful - # Hint: Use logging.info() to show the status + client = None # ← REPLACE THIS LINE WITH YOUR CODE + + # ======================================== + # STEP 2: Test the connection + # ======================================== + print("\n📝 STEP 2: Testing connection...") + print("You need to test if the connection works by getting local site info.") + print("Look at the hint below for the correct syntax.") + + # TODO: Add code to test the connection + # HINT: Use this syntax: + # local_site = client.localsite.get_local_site() + # version = local_site.get('Version') + # logging.info(f"Successfully connected to ZVM version: {version}") + # + # EXPLANATION: + # - client.localsite.get_local_site() gets information about your local ZVM + # - local_site.get('Version') extracts the ZVM version from the response + # - logging.info() displays a success message + + # ← ADD YOUR CODE HERE + + # ======================================== + # STEP 3: Display success message + # ======================================== + print("\n📝 STEP 3: Displaying success message...") + print("You need to add a final success message.") + + # TODO: Add a success message + # HINT: Use this syntax: + # logging.info("🎉 Connection successful!") + # + # EXPLANATION: + # This confirms that everything worked correctly + + # ← ADD YOUR CODE HERE except Exception as e: - # TODO: Handle any authentication or connection errors - # Hint: Use logging.error() to log the error message + # This catches any errors that might occur + print(f"\n❌ ERROR: Something went wrong!") + print(f"Error details: {str(e)}") logging.error(f"Authentication failed: {str(e)}") sys.exit(1) diff --git a/exercises/03_site_discovery/working/sites.py b/exercises/03_site_discovery/working/sites.py index ca84902..2adcaf7 100644 --- a/exercises/03_site_discovery/working/sites.py +++ b/exercises/03_site_discovery/working/sites.py @@ -1,19 +1,30 @@ #!/usr/bin/env python3 """ -Exercise 3: Site Discovery +Exercise 3: Site Discovery - Beginner-Friendly Instructions 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 +PREREQUISITES (Complete these first): +1. ✅ Completed Exercise 2 (Authentication) +2. ✅ Make sure you have the zvml package installed +3. ✅ Updated prerequisites/config.py with your ZVM details -Your task: -1. List all available virtualization sites -2. Get and display local site information +WHAT YOU NEED TO DO: +In this exercise, you will: +1. Create a ZVMLClient to connect to your ZVM (same as Exercise 2) +2. Get a list of all available virtualization sites +3. Get detailed information about your local site -If you need help, check the solution in the solution directory. +STEP-BY-STEP INSTRUCTIONS: +1. Look at the TODO comments below - they tell you exactly what to do +2. Replace the placeholder code with the actual code +3. Each step has hints and examples to help you +4. If you get stuck, check the solution file in the solution/ directory + +WHAT ARE VIRTUALIZATION SITES? +- A "site" in Zerto is a location where you have virtual machines +- Your "local site" is where your ZVM is running +- "Peer sites" are other locations you can replicate to/from +- Each site has information like name, type, version, etc. """ import sys @@ -22,62 +33,116 @@ import logging import json from pathlib import Path -# Add prerequisites to Python path +# Add prerequisites to Python path (this helps Python find your config file) prerequisites_path = Path(__file__).parent.parent.parent.parent / "prerequisites" sys.path.append(str(prerequisites_path)) -# Import the SDK modules +# Import the Zerto SDK - this gives us the ZVMLClient class from zvml import ZVMLClient -# Import configuration +# Import your configuration settings try: from config import ( - ZVM_HOST, - ZVM_PORT, - ZVM_SSL_VERIFY, - CLIENT_ID, - CLIENT_SECRET + ZVM_HOST, # Your ZVM IP address (e.g., "192.168.1.100") + ZVM_PORT, # Usually 443 for HTTPS + ZVM_SSL_VERIFY, # True/False for SSL certificate verification + CLIENT_ID, # Your Keycloak client ID (e.g., "my-api-client") + CLIENT_SECRET # Your Keycloak client secret ) except ImportError: - print("Error: Please copy config.example.py to config.py and update with your values") + print("❌ ERROR: Configuration file not found!") + print("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 + Main function - this is where your code goes! + Follow the step-by-step instructions below. """ - # Set up logging with timestamp + # Set up logging so you can see what's happening logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s' ) + print("🚀 Starting Zerto Site Discovery Exercise") + print("=" * 50) + 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 1: Create a ZVMLClient instance + # ======================================== + print("\n📝 STEP 1: Creating ZVMLClient...") + print("This is the same as Exercise 2 - you need to create a client to connect to ZVM.") - # 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 + # TODO: Replace this line with actual ZVMLClient creation + # HINT: Use this syntax (same as Exercise 2): + # client = ZVMLClient( + # zvm_address=ZVM_HOST, + # client_id=CLIENT_ID, + # client_secret=CLIENT_SECRET, + # verify_certificate=ZVM_SSL_VERIFY + # ) + # + # EXPLANATION: + # This creates a connection to your ZVM (same as Exercise 2) - # 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 + client = None # ← REPLACE THIS LINE WITH YOUR CODE + + # ======================================== + # STEP 2: List all available sites + # ======================================== + print("\n📝 STEP 2: Getting list of sites...") + print("You need to get a list of all virtualization sites available to your ZVM.") + + # TODO: Add code to get the list of sites + # HINT: Use this syntax: + # sites = client.virtualization_sites.get_virtualization_sites() + # + # EXPLANATION: + # - client.virtualization_sites.get_virtualization_sites() gets all sites + # - This returns a list of site information + + # ← ADD YOUR CODE HERE + + # TODO: Add code to display the sites + # HINT: Use this syntax: + # 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)}') + # + # EXPLANATION: + # - len(sites) counts how many sites were found + # - json.dumps(sites, indent=4) formats the site data nicely + # - logging.info() displays the information + + # ← ADD YOUR CODE HERE + + # ======================================== + # STEP 3: Get local site information + # ======================================== + print("\n📝 STEP 3: Getting local site details...") + print("You need to get detailed information about your local site.") + + # TODO: Add code to get local site information + # HINT: Use this syntax: + # local_site = client.localsite.get_local_site() + # logging.info(f"Local site details: {json.dumps(local_site, indent=4)}") + # + # EXPLANATION: + # - client.localsite.get_local_site() gets info about your local ZVM + # - This includes version, name, type, and other details + # - json.dumps(local_site, indent=4) formats it nicely + + # ← ADD YOUR CODE HERE except Exception as e: - # TODO: Handle any site discovery errors - # Hint: Use logging.error() to log the error message + # This catches any errors that might occur + print(f"\n❌ ERROR: Something went wrong!") + print(f"Error details: {str(e)}") logging.error(f"Site discovery failed: {str(e)}") sys.exit(1) diff --git a/exercises/04_resource_discovery/working/resources.py b/exercises/04_resource_discovery/working/resources.py index f3f7a36..c98e919 100644 --- a/exercises/04_resource_discovery/working/resources.py +++ b/exercises/04_resource_discovery/working/resources.py @@ -1,19 +1,36 @@ #!/usr/bin/env python3 """ -Exercise 4: Resource Discovery +Exercise 4: Resource Discovery - Beginner-Friendly Instructions 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 +PREREQUISITES (Complete these first): +1. ✅ Completed Exercise 3 (Site Discovery) +2. ✅ Make sure you have the zvml package installed +3. ✅ Updated 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) +WHAT YOU NEED TO DO: +In this exercise, you will: +1. Create a ZVMLClient to connect to your ZVM (same as previous exercises) +2. Get a list of all available sites +3. Identify your local site and a peer site +4. Discover different types of resources: + - Local site: Virtual Machines (VMs) + - Peer site: Datastores, Hosts, Folders, Networks -If you need help, check the solution in the solution directory. +STEP-BY-STEP INSTRUCTIONS: +1. Look at the TODO comments below - they tell you exactly what to do +2. Replace the placeholder code with the actual code +3. Each step has hints and examples to help you +4. If you get stuck, check the solution file in the solution/ directory + +WHAT ARE RESOURCES? +- **VMs**: Virtual machines that can be protected/replicated +- **Datastores**: Storage locations where VMs are stored +- **Hosts**: Physical servers that run the VMs +- **Folders**: Organizational containers for VMs +- **Networks**: Network connections for VMs +- **Local Site**: Your current ZVM location +- **Peer Site**: Another location you can replicate to/from """ import sys @@ -22,104 +39,173 @@ import logging import json from pathlib import Path -# Add prerequisites to Python path +# Add prerequisites to Python path (this helps Python find your config file) prerequisites_path = Path(__file__).parent.parent.parent.parent / "prerequisites" sys.path.append(str(prerequisites_path)) -# Import the SDK modules +# Import the Zerto SDK - this gives us the ZVMLClient class from zvml import ZVMLClient -# Import configuration +# Import your configuration settings try: from config import ( - ZVM_HOST, - ZVM_PORT, - ZVM_SSL_VERIFY, - CLIENT_ID, - CLIENT_SECRET + ZVM_HOST, # Your ZVM IP address (e.g., "192.168.1.100") + ZVM_PORT, # Usually 443 for HTTPS + ZVM_SSL_VERIFY, # True/False for SSL certificate verification + CLIENT_ID, # Your Keycloak client ID (e.g., "my-api-client") + CLIENT_SECRET # Your Keycloak client secret ) except ImportError: - print("Error: Please copy config.example.py to config.py and update with your values") + print("❌ ERROR: Configuration file not found!") + print("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) + Main function - this is where your code goes! + Follow the step-by-step instructions below. """ - # Set up logging with timestamp + # Set up logging so you can see what's happening logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s' ) + print("🚀 Starting Zerto Resource Discovery Exercise") + print("=" * 50) + 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 1: Create a ZVMLClient instance + # ======================================== + print("\n📝 STEP 1: Creating ZVMLClient...") + print("This is the same as previous exercises - you need to create a client to connect to ZVM.") - # 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 + # TODO: Replace this line with actual ZVMLClient creation + # HINT: Use this syntax (same as previous exercises): + # client = ZVMLClient( + # zvm_address=ZVM_HOST, + # client_id=CLIENT_ID, + # client_secret=CLIENT_SECRET, + # verify_certificate=ZVM_SSL_VERIFY + # ) - # 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 + client = None # ← REPLACE THIS LINE WITH YOUR CODE - # 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 2: Identify local and peer sites + # ======================================== + print("\n📝 STEP 2.1: Getting list of sites...") + print("You need to get a list of all available sites (same as Exercise 3).") - # 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 + # TODO: Add code to get the list of sites + # HINT: Use this syntax: + # sites = client.virtualization_sites.get_virtualization_sites() + # + # EXPLANATION: + # This gets all sites available to your ZVM - # 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 + # ← ADD YOUR CODE HERE - # 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 + print("\n📝 STEP 2.2: Getting local site identifier...") + print("You need to get the identifier for your local site.") - # 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 + # TODO: Add code to get local site identifier + # HINT: Use this syntax: + # local_site_identifier = client.localsite.get_local_site().get('SiteIdentifier') + # + # EXPLANATION: + # - client.localsite.get_local_site() gets your local site info + # - .get('SiteIdentifier') extracts the unique identifier - # 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 + local_site_identifier = None # ← REPLACE THIS LINE WITH YOUR CODE + + print("\n📝 STEP 2.3: Getting peer site identifier...") + print("You need to find a peer site (any site that's not your local site).") + + # TODO: Add code to get peer site identifier + # HINT: Use this syntax: + # peer_site = next((site for site in sites if site.get('SiteIdentifier') != local_site_identifier), None) + # peer_site_identifier = peer_site.get('SiteIdentifier') + # + # EXPLANATION: + # - This finds the first site that's not your local site + # - next() gets the first matching site from the list + # - .get('SiteIdentifier') extracts the unique identifier + + peer_site_identifier = None # ← REPLACE THIS LINE WITH YOUR CODE + + # ======================================== + # STEP 3: Get local site resources + # ======================================== + print("\n📝 STEP 3.1: Getting local site VMs...") + print("You need to get all virtual machines from your local site.") + + # TODO: Add code to get local site VMs + # HINT: Use this syntax: + # local_vms = client.virtualization_sites.get_virtualization_site_vms(site_identifier=local_site_identifier) + # + # EXPLANATION: + # This gets all VMs that can be protected/replicated from your local site + + # ← ADD YOUR CODE HERE + + # ======================================== + # STEP 4: Get peer site resources + # ======================================== + print("\n📝 STEP 4.1: Getting peer site datastores...") + print("You need to get datastores from the peer site.") + + # TODO: Add code to get peer site datastores + # HINT: Use this syntax: + # peer_datastores = client.virtualization_sites.get_virtualization_site_datastores(site_identifier=peer_site_identifier) + # + # EXPLANATION: + # Datastores are storage locations where VMs can be stored on the peer site + + # ← ADD YOUR CODE HERE + + print("\n📝 STEP 4.2: Getting peer site hosts...") + print("You need to get hosts from the peer site.") + + # TODO: Add code to get peer site hosts + # HINT: Use this syntax: + # peer_hosts = client.virtualization_sites.get_virtualization_site_hosts(site_identifier=peer_site_identifier) + # + # EXPLANATION: + # Hosts are physical servers that can run VMs on the peer site + + # ← ADD YOUR CODE HERE + + print("\n📝 STEP 4.3: Getting peer site folders...") + print("You need to get folders from the peer site.") + + # TODO: Add code to get peer site folders + # HINT: Use this syntax: + # peer_folders = client.virtualization_sites.get_virtualization_site_folders(site_identifier=peer_site_identifier) + # + # EXPLANATION: + # Folders are organizational containers for VMs on the peer site + + # ← ADD YOUR CODE HERE + + print("\n📝 STEP 4.4: Getting peer site networks...") + print("You need to get networks from the peer site.") + + # TODO: Add code to get peer site networks + # HINT: Use this syntax: + # peer_networks = client.virtualization_sites.get_virtualization_site_networks(site_identifier=peer_site_identifier) + # + # EXPLANATION: + # Networks are network connections that VMs can use on the peer site + + # ← ADD YOUR CODE HERE except Exception as e: - # TODO: Handle any resource discovery errors - # Hint: Use logging.error() to log the error message + # This catches any errors that might occur + print(f"\n❌ ERROR: Something went wrong!") + print(f"Error details: {str(e)}") logging.error(f"Resource discovery failed: {str(e)}") sys.exit(1) diff --git a/exercises/05_vpg_operations/working/create_vpg.py b/exercises/05_vpg_operations/working/create_vpg.py index 7a46247..5116e8d 100644 --- a/exercises/05_vpg_operations/working/create_vpg.py +++ b/exercises/05_vpg_operations/working/create_vpg.py @@ -1,28 +1,39 @@ #!/usr/bin/env python3 """ -Exercise 5: VPG Operations - Template +Exercise 5: VPG Operations - Beginner-Friendly Instructions 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 +PREREQUISITES (Complete these first): +1. ✅ Completed Exercise 4 (Resource Discovery) +2. ✅ Make sure you have the zvml package installed +3. ✅ Updated prerequisites/config.py with your ZVM details +4. ✅ Have some unprotected VMs available in your local site -Usage: - python create_vpg.py --vm-names "vm1" "vm2" "vm3" [--vpg-name "My-VPG"] +WHAT YOU NEED TO DO: +In this exercise, you will: +1. Create a ZVMLClient to connect to your ZVM +2. Parse command line arguments (VM names and VPG name) +3. Find VMs by their names in your local site +4. Get peer site resources (datastores, folders, networks, hosts) +5. Create a VPG configuration with all necessary settings +6. Create the VPG +7. Add VMs to the VPG +8. Optionally remove a VM from the VPG -Your task: -1. Implement the find_vms_by_names function to locate VMs by their names -2. Complete the VPG configuration with appropriate settings -3. Add the found VMs to the VPG -4. Implement VM removal functionality +STEP-BY-STEP INSTRUCTIONS: +1. Look at the TODO comments below - they tell you exactly what to do +2. Replace the placeholder code with the actual code +3. Each step has hints and examples to help you +4. If you get stuck, check the solution file in the solution/ directory -The script should: -- Create a new VPG with basic settings -- Configure journal, recovery, and network settings -- Add specified VMs to the VPG -- Allow removing VMs from the VPG +WHAT IS A VPG? +- **VPG** = Virtual Protection Group +- It's a group of VMs that are protected together +- All VMs in a VPG have the same protection settings +- You can replicate VMs from one site to another using VPGs + +USAGE EXAMPLE: +python create_vpg.py --vm-names "vm1" "vm2" "vm3" --vpg-name "My-VPG" """ import sys @@ -36,112 +47,183 @@ import urllib3 # Suppress SSL warnings urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) -# Add prerequisites to Python path +# Add prerequisites to Python path (this helps Python find your config file) prerequisites_path = Path(__file__).parent.parent.parent.parent / "prerequisites" sys.path.append(str(prerequisites_path)) -# Import the SDK modules +# Import the Zerto SDK - this gives us the ZVMLClient class from zvml import ZVMLClient -# Import configuration +# Import your configuration settings try: from config import ( - ZVM_HOST, - ZVM_PORT, - ZVM_SSL_VERIFY, - CLIENT_ID, - CLIENT_SECRET + ZVM_HOST, # Your ZVM IP address (e.g., "192.168.1.100") + ZVM_PORT, # Usually 443 for HTTPS + ZVM_SSL_VERIFY, # True/False for SSL certificate verification + CLIENT_ID, # Your Keycloak client ID (e.g., "my-api-client") + CLIENT_SECRET # Your Keycloak client secret ) except ImportError: - print("Error: Please copy config.example.py to config.py and update with your values") + print("❌ ERROR: Configuration file not found!") + print("Please copy config.example.py to config.py and update with your values") print("Expected path:", prerequisites_path / "config.py") sys.exit(1) def parse_arguments(): - """Parse command line arguments.""" - # TODO: Implement argument parsing - # Required arguments: - # --vm-names: List of VM names to add to the VPG - # Optional arguments: - # --vpg-name: Name of the VPG to create (default: "Test-VPG-Python") - pass + """ + Parse command line arguments. + + TODO: Implement argument parsing + HINT: Use this syntax: + parser = argparse.ArgumentParser(description='Create VPG and add specified VMs') + parser.add_argument('--vm-names', nargs='+', required=True, + help='List of VM names to add to the VPG') + parser.add_argument('--vpg-name', default="Test-VPG-Python", + help='Name of the VPG to create (default: Test-VPG-Python)') + return parser.parse_args() + + EXPLANATION: + - argparse helps you get command line arguments + - --vm-names: List of VM names (required) + - --vpg-name: Name for the VPG (optional, has default) + """ + pass # ← REPLACE WITH YOUR CODE def find_vms_by_names(client, site_identifier, vm_names): """ Find VMs by their names in the specified site. - Args: - client: ZVMLClient instance - site_identifier: Identifier of the site to search in - vm_names: List of VM names to find - - Returns: - tuple: (list of found VMs, list of not found VM names) - TODO: Implement the function to: - 1. Get all unprotected VMs from the site - 2. Create a dictionary for easy lookup - 3. Find requested as an argument VMs - 4. Return found and not found VMs + 1. Get all VMs from the site using client.virtualization_sites.get_virtualization_site_vms() + 2. Create a dictionary for easy lookup: {vm.get('VmName'): vm for vm in vms} + 3. Find requested VMs and return found/not found lists + + HINT: Use this syntax: + vms = client.virtualization_sites.get_virtualization_site_vms(site_identifier=site_identifier) + vm_dict = {vm.get('VmName'): vm for vm in vms} + + found_vms = [] + not_found = [] + + for vm_name in vm_names: + if vm_name in vm_dict: + found_vms.append(vm_dict[vm_name]) + else: + not_found.append(vm_name) + + return found_vms, not_found """ - pass + pass # ← REPLACE WITH YOUR CODE def remove_vm_from_vpg(client, vpg_name, vm): """ Remove a VM from the VPG. - Args: - client: ZVMLClient instance - vpg_name: Name of the VPG - vm: VM object to remove - TODO: Implement the function to: - 1. Get VM identifier - 2. Call the appropriate API to remove the VM - 3. Log the operation + 1. Get VM name from vm object + 2. Call client.vpgs.remove_vm_from_vpg() to remove the VM + + HINT: Use this syntax: + vm_name = vm.get('VmName') + client.vpgs.remove_vm_from_vpg(vpg_name=vpg_name, vm_name=vm_name) """ - pass + pass # ← REPLACE WITH YOUR CODE def main(): """ - Main function to demonstrate VPG creation. - - TODO: Implement the following steps: - 1. Parse command line arguments - 2. Create ZVMLClient instance - 3. Identify local and peer sites - 4. Get peer site resources (datastores, folders, networks, hosts) - 5. Create VPG configuration - 6. Create VPG - 7. Find and add VMs to VPG - 8. Implement interactive VM removal + Main function - this is where your code goes! + Follow the step-by-step instructions below. """ - # Set up logging + # Set up logging so you can see what's happening logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s' ) + print("🚀 Starting Zerto VPG Operations Exercise") + print("=" * 50) + try: - # TODO: Step 1: Parse command line arguments + # ======================================== + # STEP 1: Parse command line arguments + # ======================================== + print("\n📝 STEP 1: Parsing command line arguments...") + print("You need to call the parse_arguments() function you created above.") + + # TODO: Add code to parse arguments + # HINT: Use this syntax: # args = parse_arguments() + # + # EXPLANATION: + # This gets the VM names and VPG name from command line - # TODO: Step 2: Create ZVMLClient instance - # client = ZVMLClient(...) + # ← ADD YOUR CODE HERE - # TODO: Step 3: Identify local and peer sites - # local_site = client.localsite.get_local_site() + # ======================================== + # STEP 2: Create ZVMLClient instance + # ======================================== + print("\n📝 STEP 2: Creating ZVMLClient...") + print("This is the same as previous exercises.") + + # TODO: Add code to create ZVMLClient + # HINT: Use this syntax: + # client = ZVMLClient( + # zvm_address=ZVM_HOST, + # client_id=CLIENT_ID, + # client_secret=CLIENT_SECRET, + # verify_certificate=ZVM_SSL_VERIFY + # ) + + # ← ADD YOUR CODE HERE + + # ======================================== + # STEP 3: Identify local and peer sites + # ======================================== + print("\n📝 STEP 3: Getting site information...") + print("You need to get local and peer site identifiers.") + + # TODO: Add code to get sites and site identifiers + # HINT: Use this syntax: # sites = client.virtualization_sites.get_virtualization_sites() + # local_site = client.localsite.get_local_site() + # local_site_identifier = local_site.get('SiteIdentifier') + # peer_site = next((site for site in sites if site.get('SiteIdentifier') != local_site_identifier), None) + # peer_site_identifier = peer_site.get('SiteIdentifier') - # TODO: Step 4: Get peer site resources - # peer_datastores = client.virtualization_sites.get_virtualization_site_datastores(...) - # peer_folders = client.virtualization_sites.get_virtualization_site_folders(...) - # peer_networks = client.virtualization_sites.get_virtualization_site_networks(...) - # peer_hosts = client.virtualization_sites.get_virtualization_site_hosts(...) + # ← ADD YOUR CODE HERE - # TODO: Step 5: Create VPG configuration + # ======================================== + # STEP 4: Get peer site resources + # ======================================== + print("\n📝 STEP 4: Getting peer site resources...") + print("You need to get datastores, folders, networks, and hosts from the peer site.") + + # TODO: Add code to get peer site resources + # HINT: Use this syntax: + # peer_datastores = client.virtualization_sites.get_virtualization_site_datastores(site_identifier=peer_site_identifier) + # target_datastore = peer_datastores[0] # Use first available + # + # peer_folders = client.virtualization_sites.get_virtualization_site_folders(site_identifier=peer_site_identifier) + # target_folder = peer_folders[0] # Use first available + # + # peer_networks = client.virtualization_sites.get_virtualization_site_networks(site_identifier=peer_site_identifier) + # target_network = peer_networks[0] # Use first available + # + # peer_hosts = client.virtualization_sites.get_virtualization_site_hosts(site_identifier=peer_site_identifier) + # target_host = peer_hosts[0] # Use first available + + # ← ADD YOUR CODE HERE + + # ======================================== + # STEP 5: Create VPG configuration + # ======================================== + print("\n📝 STEP 5: Creating VPG configuration...") + print("You need to create the basic, journal, recovery, and network settings.") + + # TODO: Add code to create VPG configuration + # HINT: Use this syntax: # basic = { - # "Name": "Your VPG Name", + # "Name": args.vpg_name, # "VpgType": "Remote", # "RpoInSeconds": 300, # "JournalHistoryInHours": 24, @@ -150,21 +232,98 @@ def main(): # "ProtectedSiteIdentifier": local_site_identifier, # "RecoverySiteIdentifier": peer_site_identifier # } + # + # journal = {} # Keep default settings + # + # recovery = { + # "DefaultHostIdentifier": target_host.get('HostIdentifier'), + # "DefaultDatastoreIdentifier": target_datastore.get('DatastoreIdentifier'), + # "DefaultFolderIdentifier": target_folder.get('FolderIdentifier') + # } + # + # networks = { + # "Failover": { + # "Hypervisor": { + # "DefaultNetworkIdentifier": target_network.get('NetworkIdentifier') + # } + # }, + # "FailoverTest": { + # "Hypervisor": { + # "DefaultNetworkIdentifier": target_network.get('NetworkIdentifier') + # } + # } + # } - # TODO: Step 6: Create VPG - # vpg_id = client.vpgs.create_vpg(...) + # ← ADD YOUR CODE HERE - # TODO: Step 7: Find and add VMs to VPG - # found_vms, not_found = find_vms_by_names(...) + # ======================================== + # STEP 6: Create VPG + # ======================================== + print("\n📝 STEP 6: Creating the VPG...") + print("You need to call the create_vpg method with your configuration.") + + # TODO: Add code to create VPG + # HINT: Use this syntax: + # vpg_id = client.vpgs.create_vpg(basic=basic, journal=journal, recovery=recovery, networks=networks, sync=True) + # + # EXPLANATION: + # This creates the VPG with all your settings + + # ← ADD YOUR CODE HERE + + # ======================================== + # STEP 7: Find and add VMs to VPG + # ======================================== + print("\n📝 STEP 7: Finding and adding VMs...") + print("You need to find the VMs and add them to the VPG.") + + # TODO: Add code to find VMs + # HINT: Use this syntax: + # found_vms, not_found = find_vms_by_names(client, local_site_identifier, args.vm_names) + # + # EXPLANATION: + # This finds the VMs you specified in the command line + + # ← ADD YOUR CODE HERE + + # TODO: Add code to add VMs to VPG + # HINT: Use this syntax: # for vm in found_vms: - # # Add VM to VPG + # vm_name = vm.get('VmName') + # vm_id = vm.get('VmIdentifier') + # vm_payload = { + # "VmIdentifier": vm_id, + # "Recovery": { + # "HostIdentifier": target_host.get('HostIdentifier'), + # "DatastoreIdentifier": target_datastore.get('DatastoreIdentifier'), + # "FolderIdentifier": target_folder.get('FolderIdentifier') + # } + # } + # task_id = client.vpgs.add_vm_to_vpg(args.vpg_name, vm_list_payload=vm_payload) - # TODO: Step 8: Implement interactive VM removal + # ← ADD YOUR CODE HERE + + # ======================================== + # STEP 8: Interactive VM removal (optional) + # ======================================== + print("\n📝 STEP 8: Interactive VM removal...") + print("You can optionally remove the last VM from the VPG.") + + # TODO: Add code for interactive VM removal + # HINT: Use this syntax: # if found_vms: - # # Ask user if they want to remove the last VM - # # If yes, remove it + # last_vm = found_vms[-1] + # vm_name = last_vm.get('VmName') + # response = input(f"Remove VM {vm_name} from VPG? (yes/no): ").lower() + # if response in ['yes', 'y']: + # remove_vm_from_vpg(client, args.vpg_name, last_vm) + + # ← ADD YOUR CODE HERE except Exception as e: + # This catches any errors that might occur + print(f"\n❌ ERROR: Something went wrong!") + print(f"Error details: {str(e)}") logging.error(f"VPG operation failed: {str(e)}") sys.exit(1) diff --git a/exercises/06_failover_test/working/failover.py b/exercises/06_failover_test/working/failover.py index af3b1eb..49443d1 100644 --- a/exercises/06_failover_test/working/failover.py +++ b/exercises/06_failover_test/working/failover.py @@ -1,28 +1,37 @@ #!/usr/bin/env python3 """ -Exercise 6: Failover Testing - Template +Exercise 6: Failover Testing - Beginner-Friendly Instructions This script demonstrates how to perform a failover test on a VPG. -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 +PREREQUISITES (Complete these first): +1. ✅ Completed Exercise 5 (VPG Operations) +2. ✅ Make sure you have the zvml package installed +3. ✅ Updated prerequisites/config.py with your ZVM details +4. ✅ Have a VPG created and running (from Exercise 5) -Usage: - python failover.py --vpg-name "My-VPG" +WHAT YOU NEED TO DO: +In this exercise, you will: +1. Create a ZVMLClient to connect to your ZVM +2. Parse command line arguments (VPG name) +3. Find a VPG by its name +4. Start a failover test on the VPG +5. Monitor the test progress +6. Optionally stop the test -Your task: -1. Implement VPG lookup by name using get_vpgs() -2. Start a failover test using failover_test() method -3. Monitor test progress using get_vpg_test_status() -4. Stop the test using stop_vpg_test() method when requested +STEP-BY-STEP INSTRUCTIONS: +1. Look at the TODO comments below - they tell you exactly what to do +2. Replace the placeholder code with the actual code +3. Each step has hints and examples to help you +4. If you get stuck, check the solution file in the solution/ directory -The script should: -- Find the VPG by name and verify it exists -- Start a failover test with default settings -- Monitor the test progress and status -- Allow stopping the test when requested +WHAT IS A FAILOVER TEST? +- **Failover Test**: Tests if your VMs can be successfully started at the recovery site +- It creates test VMs at the peer site to verify everything works +- The test VMs are isolated and don't affect production +- You can stop the test at any time to clean up the test VMs + +USAGE EXAMPLE: +python failover.py --vpg-name "My-VPG" """ import sys @@ -37,159 +46,219 @@ import urllib3 # Suppress SSL warnings urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) -# Add prerequisites to Python path +# Add prerequisites to Python path (this helps Python find your config file) prerequisites_path = Path(__file__).parent.parent.parent.parent / "prerequisites" sys.path.append(str(prerequisites_path)) -# Import the SDK modules +# Import the Zerto SDK - this gives us the ZVMLClient class from zvml import ZVMLClient -# Import configuration +# Import your configuration settings try: from config import ( - ZVM_HOST, - ZVM_PORT, - ZVM_SSL_VERIFY, - CLIENT_ID, - CLIENT_SECRET + ZVM_HOST, # Your ZVM IP address (e.g., "192.168.1.100") + ZVM_PORT, # Usually 443 for HTTPS + ZVM_SSL_VERIFY, # True/False for SSL certificate verification + CLIENT_ID, # Your Keycloak client ID (e.g., "my-api-client") + CLIENT_SECRET # Your Keycloak client secret ) except ImportError: - print("Error: Please copy config.example.py to config.py and update with your values") + print("❌ ERROR: Configuration file not found!") + print("Please copy config.example.py to config.py and update with your values") print("Expected path:", prerequisites_path / "config.py") sys.exit(1) def parse_arguments(): - """Parse command line arguments.""" - # TODO: Implement argument parsing - # Required argument: - # --vpg-name: Name of the VPG to test - pass + """ + Parse command line arguments. + + TODO: Implement argument parsing + HINT: Use this syntax: + parser = argparse.ArgumentParser(description='Perform failover test on a VPG') + parser.add_argument('--vpg-name', required=True, + help='Name of the VPG to test') + return parser.parse_args() + + EXPLANATION: + - argparse helps you get command line arguments + - --vpg-name: Name of the VPG to test (required) + """ + pass # ← REPLACE WITH YOUR CODE def find_vpg_by_name(client, vpg_name): """ - Find a VPG by its name using get_vpgs() method. + Find a VPG by its name. - Args: - client: ZVMLClient instance - vpg_name: Name of the VPG to find - - Returns: - dict: VPG object if found, None otherwise - - TODO: Implement the function to: - 1. Call client.vpgs.get_vpgs() to get all VPGs - 2. Find the VPG with matching name - 3. Log the VPG details if found - 4. Return the VPG object or None + TODO: Implement the function to find a VPG by name + HINT: Use this syntax: + vpg = client.vpgs.list_vpgs(vpg_name=vpg_name) + return vpg if vpg else None + + EXPLANATION: + - client.vpgs.list_vpgs() gets VPGs with a specific name + - Returns the VPG if found, None if not found """ - pass + pass # ← REPLACE WITH YOUR CODE def start_failover_test(client, vpg_name): """ - Start a failover test for the specified VPG using failover_test() method. + Start a failover test for the specified VPG. - Args: - client: ZVMLClient instance - vpg_name: Name of the VPG to test - - Returns: - str: Test identifier (task_id) - - TODO: Implement the function to: - 1. Call client.vpgs.failover_test() with sync=True - 2. Log the test initiation - 3. Return the task_id + TODO: Implement the function to start a failover test + HINT: Use this syntax: + response = client.vpgs.failover_test( + vpg_name=vpg_name, + sync=True # Wait for the test to start + ) + return response + + EXPLANATION: + - client.vpgs.failover_test() starts a failover test + - sync=True means wait for the test to start before returning + - Returns the response from the API """ - pass + pass # ← REPLACE WITH YOUR CODE def monitor_test_progress(client, vpg_name, test_id): """ - Monitor the progress of a failover test using get_vpg_test_status(). + Monitor the progress of a failover test. - Args: - client: ZVMLClient instance - vpg_name: Name of the VPG - test_id: Test identifier (task_id) - - Returns: - bool: True if test completed successfully, False otherwise - - TODO: Implement the function to: - 1. Call client.vpgs.get_vpg_test_status() to get test status - 2. Log the status and progress - 3. Return True for 'Succeeded', False for 'Failed' or 'Stopped' - 4. Return False if test is still running + TODO: Implement the function to monitor test progress + HINT: Use this syntax: + test_status = client.vpgs.get_vpg_test_status(vpg_name, test_id) + status = test_status.get('Status') + progress = test_status.get('Progress', 0) + + logging.info(f"Test status: {status} (Progress: {progress}%)") + + if status == 'Succeeded': + return True + elif status in ['Failed', 'Stopped']: + return False + + return False # Test is still running + + EXPLANATION: + - client.vpgs.get_vpg_test_status() gets the current test status + - Returns True if test succeeded, False if failed/stopped/still running """ - pass + pass # ← REPLACE WITH YOUR CODE -def stop_failover_test(client, vpg_name, test_id): +def stop_failover_test(client, vpg_name): """ - Stop a running failover test using stop_vpg_test() method. + Stop a running failover test. - Args: - client: ZVMLClient instance - vpg_name: Name of the VPG - test_id: Test identifier (task_id) - - TODO: Implement the function to: - 1. Call client.vpgs.stop_vpg_test() with sync=True - 2. Wait for the stop operation to complete - 3. Log the stop operation status + TODO: Implement the function to stop a failover test + HINT: Use this syntax: + response = client.vpgs.stop_failover_test(vpg_name=vpg_name) + + EXPLANATION: + - client.vpgs.stop_failover_test() stops the running test + - This cleans up the test VMs at the recovery site """ - pass + pass # ← REPLACE WITH YOUR CODE def main(): """ - Main function to demonstrate failover testing. - - TODO: Implement the following steps: - 1. Parse command line arguments for VPG name - 2. Create ZVMLClient instance - 3. Find the VPG by name using find_vpg_by_name() - 4. Start failover test using start_failover_test() - 5. Monitor test progress and handle stop request: - - Monitor progress using monitor_test_progress() - - If test completes successfully, exit - - If user requests to stop, call stop_failover_test() + Main function - this is where your code goes! + Follow the step-by-step instructions below. """ - # Set up logging + # Set up logging so you can see what's happening logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s' ) + print("🚀 Starting Zerto Failover Testing Exercise") + print("=" * 50) + try: - # TODO: Step 1: Parse command line arguments + # ======================================== + # STEP 1: Parse command line arguments + # ======================================== + print("\n📝 STEP 1: Parsing command line arguments...") + print("You need to call the parse_arguments() function you created above.") + + # TODO: Add code to parse arguments + # HINT: Use this syntax: # args = parse_arguments() + # + # EXPLANATION: + # This gets the VPG name from command line - # TODO: Step 2: Create ZVMLClient instance - # client = ZVMLClient(...) + # ← ADD YOUR CODE HERE - # TODO: Step 3: Find the VPG + # ======================================== + # STEP 2: Create ZVMLClient instance + # ======================================== + print("\n📝 STEP 2: Creating ZVMLClient...") + print("This is the same as previous exercises.") + + # TODO: Add code to create ZVMLClient + # HINT: Use this syntax: + # client = ZVMLClient( + # zvm_address=ZVM_HOST, + # client_id=CLIENT_ID, + # client_secret=CLIENT_SECRET, + # verify_certificate=ZVM_SSL_VERIFY + # ) + + # ← ADD YOUR CODE HERE + + # ======================================== + # STEP 3: Find the VPG + # ======================================== + print("\n📝 STEP 3: Finding the VPG...") + print("You need to find the VPG by its name.") + + # TODO: Add code to find VPG + # HINT: Use this syntax: # vpg = find_vpg_by_name(client, args.vpg_name) # if not vpg: # logging.error(f"VPG '{args.vpg_name}' not found!") # sys.exit(1) + # + # EXPLANATION: + # This checks if the VPG exists before trying to test it - # TODO: Step 4: Start failover test - # test_id = start_failover_test(client, args.vpg_name) + # ← ADD YOUR CODE HERE - # TODO: Step 5: Monitor test progress and handle stop request - # while True: - # success = monitor_test_progress(client, args.vpg_name, test_id) - # if success: - # logging.info("Test completed successfully") - # break - # - # # Check if user wants to stop the test - # response = input("\nWould you like to stop the test? (yes/no): ").lower() - # if response in ['yes', 'y']: - # stop_failover_test(client, args.vpg_name, test_id) - # break - # - # time.sleep(10) # Wait before next status check + # ======================================== + # STEP 4: Start failover test + # ======================================== + print("\n📝 STEP 4: Starting failover test...") + print("You need to start a failover test on the VPG.") + + # TODO: Add code to start failover test + # HINT: Use this syntax: + # response = start_failover_test(client, args.vpg_name) + # + # EXPLANATION: + # This starts the failover test and waits for it to begin + + # ← ADD YOUR CODE HERE + + # ======================================== + # STEP 5: Handle test stop request + # ======================================== + print("\n📝 STEP 5: Handling test stop request...") + print("You can optionally stop the test.") + + # TODO: Add code for interactive test stopping + # HINT: Use this syntax: + # response = input("\nWould you like to stop the test? (yes/no): ").lower() + # if response in ['yes', 'y']: + # stop_failover_test(client, args.vpg_name) + # + # EXPLANATION: + # This asks the user if they want to stop the test and cleans up if yes + + # ← ADD YOUR CODE HERE except Exception as e: + # This catches any errors that might occur + print(f"\n❌ ERROR: Something went wrong!") + print(f"Error details: {str(e)}") logging.error(f"Failover test failed: {str(e)}") sys.exit(1)