simplified instructions
This commit is contained in:
@@ -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.
|
||||
Your feedback is valuable! Please complete the feedback form after finishing the lab to help us improve the content.
|
||||
@@ -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.
|
||||
Once you successfully connect, proceed to **Exercise 3: Site Discovery** to start working with Zerto sites.
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user