Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F3152678
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
41 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/agents/aws_vpc/fence_aws_vpc.py b/agents/aws_vpc/fence_aws_vpc.py
deleted file mode 100644
index 306ab513..00000000
--- a/agents/aws_vpc/fence_aws_vpc.py
+++ /dev/null
@@ -1,886 +0,0 @@
-#!@PYTHON@ -tt
-
-import sys, re
-import json
-import atexit
-import logging
-import time
-import requests
-from requests import HTTPError
-from requests.exceptions import HTTPError
-
-
-try:
- import boto3
- from botocore.exceptions import ConnectionError, ClientError, EndpointConnectionError, NoRegionError
-except ImportError:
- pass
-
-sys.path.append("@FENCEAGENTSLIBDIR@")
-#sys.path.append("/usr/share/fence")
-#sys.path.append("/usr/share/fence")
-
-from fencing import *
-from fencing import (
- all_opt,
- check_input,
- process_input,
- run_delay,
- show_docs,
- fence_action,
- fail,
- fail_usage,
- atexit_handler,
- EC_STATUS,
- SyslogLibHandler
-)
-
-try:
- import boto3
- from botocore.exceptions import ConnectionError, ClientError, EndpointConnectionError, NoRegionError
-except ImportError:
- pass
-
-# Logger configuration
-logger = logging.getLogger()
-logger.propagate = False
-logger.setLevel(logging.INFO)
-logger.addHandler(SyslogLibHandler())
-logging.getLogger('botocore.vendored').propagate = False
-
-status = {
- "running": "on",
- "stopped": "off",
- "pending": "unknown",
- "stopping": "unknown",
- "shutting-down": "unknown",
- "terminated": "unknown"
-}
-def get_power_status(conn, options):
- logger.debug("Starting status operation")
- try:
- instance_id = options["--plug"]
- ec2_client = conn.meta.client
-
- # Get the lastfence tag first
- lastfence_response = ec2_client.describe_tags(
- Filters=[
- {"Name": "resource-id", "Values": [instance_id]},
- {"Name": "key", "Values": ["lastfence"]}
- ]
- )
-
- if not lastfence_response["Tags"]:
- logger.debug("No lastfence tag found for instance %s - instance is not fenced", instance_id)
- return "on"
-
- lastfence_timestamp = lastfence_response["Tags"][0]["Value"]
-
- # Check for backup tags with pattern Original_SG_Backup_{instance_id}_*
- response = ec2_client.describe_tags(
- Filters=[
- {"Name": "resource-id", "Values": [instance_id]},
- {"Name": "key", "Values": [f"Original_SG_Backup_{instance_id}*"]}
- ]
- )
-
- if not response["Tags"]:
- logger.debug("No backup tags found for instance %s - instance is not fenced", instance_id)
- return "on"
-
- # Loop through backup tags to find matching timestamp
- for tag in response["Tags"]:
- try:
- backup_data = json.loads(tag["Value"])
- backup_timestamp = backup_data.get("timestamp")
-
- if not backup_timestamp:
- logger.debug("No timestamp found in backup data for tag %s", tag["Key"])
- continue
-
- # Validate timestamps match
- if str(backup_timestamp) == str(lastfence_timestamp):
- logger.debug("Found matching backup tag %s - instance is fenced", tag["Key"])
- return "off"
-
- except (json.JSONDecodeError, KeyError) as e:
- logger.error(f"Failed to parse backup data for tag {tag['Key']}: {str(e)}")
- continue
-
- logger.debug("No backup tags with matching timestamp found - instance is not fenced")
- return "on"
-
- except ClientError:
- fail_usage("Failed: Incorrect Access Key or Secret Key.")
- except EndpointConnectionError:
- fail_usage("Failed: Incorrect Region.")
- except IndexError:
- fail(EC_STATUS)
- except Exception as e:
- logger.error("Failed to get power status: %s", e)
- fail(EC_STATUS)
-
-# Retrieve instance ID for self-check
-def get_instance_id():
- """Retrieve the instance ID of the current EC2 instance."""
- try:
- token = requests.put(
- "https://169.254.169.254/latest/api/token",
- headers={"X-aws-ec2-metadata-token-ttl-seconds": "21600"},
- ).content.decode("UTF-8")
- instance_id = requests.get(
- "https://169.254.169.254/latest/meta-data/instance-id",
- headers={"X-aws-ec2-metadata-token": token},
- ).content.decode("UTF-8")
- return instance_id
- except Exception as err:
- logger.error("Failed to retrieve instance ID for self-check: %s", err)
- return None
-
-
-# Retrieve instance details
-def get_instance_details(ec2_client, instance_id):
- """Retrieve instance details including state, VPC, interfaces, and attached SGs."""
- try:
- response = ec2_client.describe_instances(InstanceIds=[instance_id])
- instance = response["Reservations"][0]["Instances"][0]
-
- instance_state = instance["State"]["Name"]
- vpc_id = instance["VpcId"]
- network_interfaces = instance["NetworkInterfaces"]
-
- interfaces = []
- for interface in network_interfaces:
- try:
- interfaces.append(
- {
- "NetworkInterfaceId": interface["NetworkInterfaceId"],
- "SecurityGroups": [sg["GroupId"] for sg in interface["Groups"]],
- }
- )
- except KeyError as e:
- logger.error(f"Malformed interface data: {str(e)}")
- continue
-
- return instance_state, vpc_id, interfaces
-
- except ClientError as e:
- logger.error(f"AWS API error while retrieving instance details: {str(e)}")
- raise
- except IndexError as e:
- logger.error(f"Instance {instance_id} not found or no instances returned: {str(e)}")
- raise
- except KeyError as e:
- logger.error(f"Unexpected response format from AWS API: {str(e)}")
- raise
- except Exception as e:
- logger.error(f"Unexpected error while retrieving instance details: {str(e)}")
- raise
-
-# Check if we are the self-fencing node
-
-def get_self_power_status(conn, instance_id):
- try:
- instance = conn.instances.filter(Filters=[{"Name": "instance-id", "Values": [instance_id]}])
- state = list(instance)[0].state["Name"]
- if state == "running":
- logger.debug("Captured my (%s) state and it %s - returning OK - Proceeding with fencing",instance_id,state.upper())
- return "ok"
- else:
- logger.debug("Captured my (%s) state it is %s - returning Alert - Unable to fence other nodes",instance_id,state.upper())
- return "alert"
-
- except ClientError:
- fail_usage("Failed: Incorrect Access Key or Secret Key.")
- except EndpointConnectionError:
- fail_usage("Failed: Incorrect Region.")
- except IndexError:
- return "fail"
-
-# Create backup tags for each network interface
-def create_backup_tag(ec2_client, instance_id, interfaces, timestamp):
- """Create tags on the instance to backup original security groups for each network interface."""
- try:
- # Create a tag for each network interface
- for idx, interface in enumerate(interfaces, 1):
- sg_backup = {
- "NetworkInterface": interface,
- "timestamp": timestamp
- }
- tag_value = json.dumps(sg_backup)
- tag_key = f"Original_SG_Backup_{instance_id}_{timestamp}_{idx}"
-
- # Create the tag
- ec2_client.create_tags(
- Resources=[instance_id],
- Tags=[{"Key": tag_key, "Value": tag_value}],
- )
-
- # Verify the tag was created by describing tags
- response = ec2_client.describe_tags(
- Filters=[
- {"Name": "resource-id", "Values": [instance_id]},
- {"Name": "key", "Values": [tag_key]}
- ]
- )
-
- if not response["Tags"]:
- logger.error(f"Failed to verify creation of backup tag '{tag_key}' for instance {instance_id}")
- raise Exception("Backup tag creation could not be verified")
-
- created_tag_value = response["Tags"][0]["Value"]
- if created_tag_value != tag_value:
- logger.error(f"Created tag value does not match expected value for instance {instance_id}")
- raise Exception("Backup tag value mismatch")
-
- logger.info(f"Backup tag '{tag_key}' created and verified for interface {interface['NetworkInterfaceId']}.")
- except ClientError as e:
- logger.error(f"AWS API error while creating/verifying backup tag: {str(e)}")
- raise
- except Exception as e:
- logger.error(f"Unexpected error while creating/verifying backup tag: {str(e)}")
- raise
-
-
-# Remove specified security groups
-def remove_security_groups(ec2_client, instance_id, sg_list, timestamp):
- """
- Removes all SGs in `sg_list` from each interface, if it doesn't leave zero SGs.
- If no changes are made to any interface, we exit with an error.
-
- Args:
- ec2_client: The boto3 EC2 client
- instance_id: The ID of the EC2 instance
- sg_list: List of security group IDs to remove
- timestamp: Unix timestamp for backup tag
-
- Raises:
- ClientError: If AWS API calls fail
- Exception: For other unexpected errors
- """
- try:
- # Get instance details
- state, _, interfaces = get_instance_details(ec2_client, instance_id)
-
- try:
- # Create a backup tag before making changes
- create_backup_tag(ec2_client, instance_id, interfaces, timestamp)
- except ClientError as e:
- logger.warning(f"Failed to create backup tag: {str(e)}")
- # Continue execution even if backup tag creation fails
-
- changed_any = False
- for interface in interfaces:
- try:
- original_sgs = interface["SecurityGroups"]
- # Exclude any SGs that are in sg_list
- updated_sgs = [sg for sg in original_sgs if sg not in sg_list]
-
- # Only skip if we'd end up with zero SGs
- if not updated_sgs:
- logger.info(
- f"Skipping interface {interface['NetworkInterfaceId']}: "
- f"removal of {sg_list} would leave 0 SGs."
- )
- continue
-
- # Proceed even if there's no change (idempotency)
-
- logger.info(
- f"Updating interface {interface['NetworkInterfaceId']} from {original_sgs} "
- f"to {updated_sgs} (removing {sg_list})"
- )
-
- try:
- ec2_client.modify_network_interface_attribute(
- NetworkInterfaceId=interface["NetworkInterfaceId"],
- Groups=updated_sgs
- )
- changed_any = True
- except ClientError as e:
- logger.error(
- f"Failed to modify security groups for interface "
- f"{interface['NetworkInterfaceId']}: {str(e)}"
- )
- continue
-
- except KeyError as e:
- logger.error(f"Malformed interface data: {str(e)}")
- continue
-
- # If we didn't remove anything, either the SGs weren't found or it left 0 SG
- if not changed_any:
- logger.error(
- f"Security Groups {sg_list} not removed from any interface. "
- f"Either not found, or removal left 0 SGs."
- )
- sys.exit(1)
-
- # Wait a bit for changes to propagate
- time.sleep(5)
-
- except ClientError as e:
- logger.error(f"AWS API error: {str(e)}")
- raise
- except Exception as e:
- logger.error(f"Unexpected error: {str(e)}")
- raise
-
-def keep_only_security_groups(ec2_client, instance_id, sg_to_keep_list, timestamp):
- """
- Removes all security groups from each network interface except for the specified ones.
- If none of the specified security groups are attached to an interface, that interface is skipped.
-
- Args:
- ec2_client: The boto3 EC2 client
- instance_id: The ID of the EC2 instance
- sg_to_keep_list: List containing the IDs of the security groups to keep
- timestamp: Unix timestamp for backup tag
-
- Raises:
- ClientError: If AWS API calls fail
- Exception: For other unexpected errors
- """
- try:
- state, _, interfaces = get_instance_details(ec2_client, instance_id)
-
- try:
- # Create a backup tag before making changes
- create_backup_tag(ec2_client, instance_id, interfaces, timestamp)
- except ClientError as e:
- logger.warning(f"Failed to create backup tag: {str(e)}")
- # Continue execution even if backup tag creation fails
-
- changed_any = False
- for interface in interfaces:
- try:
- original_sgs = interface["SecurityGroups"]
-
- # Set interface to only use the specified security groups that exist
- # This allows the function to work even if some SGs aren't currently attached
- updated_sgs = sg_to_keep_list
-
- if updated_sgs == original_sgs:
- continue
-
- logger.info(
- f"Updating interface {interface['NetworkInterfaceId']} from {original_sgs} "
- f"to {updated_sgs} (keeping only {sg_to_keep_list})"
- )
-
- try:
- ec2_client.modify_network_interface_attribute(
- NetworkInterfaceId=interface["NetworkInterfaceId"],
- Groups=updated_sgs
- )
- changed_any = True
- except ClientError as e:
- logger.error(
- f"Failed to modify security groups for interface "
- f"{interface['NetworkInterfaceId']}: {str(e)}"
- )
- continue
-
- except KeyError as e:
- logger.error(f"Malformed interface data: {str(e)}")
- continue
-
- # If we didn't modify anything, the specified SGs weren't found on any interface
- if not changed_any:
- logger.error(
- f"Security Groups {sg_to_keep_list} not found on any interface. "
- f"No changes made."
- )
- sys.exit(1)
-
- # Wait a bit for changes to propagate
- time.sleep(5)
-
- except ClientError as e:
- logger.error(f"AWS API error: {str(e)}")
- raise
- except Exception as e:
- logger.error(f"Unexpected error: {str(e)}")
- raise
-
-def restore_security_groups(ec2_client, instance_id):
- """
- Restores the original security groups from backup tags to each network interface.
- Each network interface's original security groups are stored in a separate backup tag.
- All backup tags share the same timestamp as the lastfence tag for validation.
-
- The process:
- 1. Get lastfence tag timestamp
- 2. Find all backup tags with matching timestamp
- 3. Create a map of interface IDs to their original security groups
- 4. Restore each interface's security groups from the map
- 5. Clean up matching backup tags and lastfence tag
-
- Args:
- ec2_client: The boto3 EC2 client
- instance_id: The ID of the EC2 instance
-
- Raises:
- ClientError: If AWS API calls fail
- Exception: For other unexpected errors
- SystemExit: If required tags are missing or no changes were made
- """
- try:
- # Get the lastfence tag first
- lastfence_response = ec2_client.describe_tags(
- Filters=[
- {"Name": "resource-id", "Values": [instance_id]},
- {"Name": "key", "Values": ["lastfence"]}
- ]
- )
-
- if not lastfence_response["Tags"]:
- logger.error(f"No lastfence tag found for instance {instance_id}")
- sys.exit(1)
-
- lastfence_timestamp = lastfence_response["Tags"][0]["Value"]
-
- # Get all backup tags for this instance
- backup_response = ec2_client.describe_tags(
- Filters=[
- {"Name": "resource-id", "Values": [instance_id]},
- {"Name": "key", "Values": [f"Original_SG_Backup_{instance_id}*"]}
- ]
- )
-
- if not backup_response["Tags"]:
- logger.error(f"No backup tags found for instance {instance_id}")
- sys.exit(1)
-
- # Find backup tags with matching timestamp
- matching_backups = {}
- for tag in backup_response["Tags"]:
- try:
- backup_data = json.loads(tag["Value"])
- backup_timestamp = backup_data.get("timestamp")
-
- if not backup_timestamp:
- logger.debug(f"No timestamp found in backup data for tag {tag['Key']}")
- continue
-
- if str(backup_timestamp) == str(lastfence_timestamp):
- logger.info(f"Found matching backup tag {tag['Key']}")
- interface_data = backup_data.get("NetworkInterface")
- if interface_data and "NetworkInterfaceId" in interface_data:
- matching_backups[interface_data["NetworkInterfaceId"]] = interface_data["SecurityGroups"]
-
- except (json.JSONDecodeError, KeyError) as e:
- logger.error(f"Failed to parse backup data for tag {tag['Key']}: {str(e)}")
- continue
-
- if not matching_backups:
- logger.error("No backup tags found with matching timestamp")
- sys.exit(1)
-
- # Get current interfaces
- _, _, current_interfaces = get_instance_details(ec2_client, instance_id)
-
- # Use the matching_backups map directly as our backup_sg_map
- backup_sg_map = matching_backups
-
- changed_any = False
- for interface in current_interfaces:
- try:
- interface_id = interface["NetworkInterfaceId"]
- if interface_id not in backup_sg_map:
- logger.warning(
- f"No backup data found for interface {interface_id}. Skipping."
- )
- continue
-
- original_sgs = backup_sg_map[interface_id]
- current_sgs = interface["SecurityGroups"]
-
- if original_sgs == current_sgs:
- logger.info(
- f"Interface {interface_id} already has original security groups. Skipping."
- )
- continue
-
- logger.info(
- f"Restoring interface {interface_id} from {current_sgs} "
- f"to original security groups {original_sgs}"
- )
-
- try:
- ec2_client.modify_network_interface_attribute(
- NetworkInterfaceId=interface_id,
- Groups=original_sgs
- )
- changed_any = True
- except ClientError as e:
- logger.error(
- f"Failed to restore security groups for interface "
- f"{interface_id}: {str(e)}"
- )
- continue
-
- except KeyError as e:
- logger.error(f"Malformed interface data: {str(e)}")
- continue
-
- if not changed_any:
- logger.error("No security groups were restored. All interfaces skipped.")
- sys.exit(1)
-
- # Wait for changes to propagate
- time.sleep(5)
-
- # Clean up only the matching backup tags and lastfence tag after successful restore
- try:
- # Delete all backup tags that match the lastfence timestamp
- tags_to_delete = [{"Key": "lastfence"}]
- deleted_tag_keys = []
- for tag in backup_response["Tags"]:
- try:
- backup_data = json.loads(tag["Value"])
- if str(backup_data.get("timestamp")) == str(lastfence_timestamp):
- tags_to_delete.append({"Key": tag["Key"]})
- deleted_tag_keys.append(tag["Key"])
- except (json.JSONDecodeError, KeyError):
- continue
-
- if len(tags_to_delete) > 1: # More than just the lastfence tag
- ec2_client.delete_tags(
- Resources=[instance_id],
- Tags=tags_to_delete
- )
- logger.info(f"Removed matching backup tags {deleted_tag_keys} and lastfence tag from instance {instance_id}")
- except ClientError as e:
- logger.warning(f"Failed to remove tags: {str(e)}")
- # Continue since the restore operation was successful
-
- except ClientError as e:
- logger.error(f"AWS API error: {str(e)}")
- raise
- except Exception as e:
- logger.error(f"Unexpected error: {str(e)}")
- raise
-
-# Shutdown instance
-def shutdown_instance(ec2_client, instance_id):
- """Shutdown the instance and confirm the state transition."""
- try:
- logger.info(f"Initiating shutdown for instance {instance_id}...")
- ec2_client.stop_instances(InstanceIds=[instance_id], Force=True)
-
- while True:
- try:
- state, _, _ = get_instance_details(ec2_client, instance_id)
- logger.info(f"Current instance state: {state}")
- if state == "stopping":
- logger.info(
- f"Instance {instance_id} is transitioning to 'stopping'. Proceeding without waiting further."
- )
- break
- except ClientError as e:
- logger.error(f"Failed to get instance state during shutdown: {str(e)}")
- fail_usage(f"AWS API error while checking instance state: {str(e)}")
- except Exception as e:
- logger.error(f"Unexpected error while checking instance state: {str(e)}")
- fail_usage(f"Failed to check instance state: {str(e)}")
-
- except ClientError as e:
- logger.error(f"AWS API error during instance shutdown: {str(e)}")
- fail_usage(f"Failed to shutdown instance: {str(e)}")
- except Exception as e:
- logger.error(f"Unexpected error during instance shutdown: {str(e)}")
- fail_usage(f"Failed to shutdown instance due to unexpected error: {str(e)}")
-
-
-# Perform the fencing action
-def get_nodes_list(conn, options):
- """Get list of nodes and their status."""
- logger.debug("Starting monitor operation")
- result = {}
- try:
- if "--filter" in options:
- filter_key = options["--filter"].split("=")[0].strip()
- filter_value = options["--filter"].split("=")[1].strip()
- filter = [{"Name": filter_key, "Values": [filter_value]}]
- logging.debug("Filter: {}".format(filter))
-
- for instance in conn.instances.filter(Filters=filter if 'filter' in vars() else []):
- instance_name = ""
- for tag in instance.tags or []:
- if tag.get("Key") == "Name":
- instance_name = tag["Value"]
- try:
- result[instance.id] = (instance_name, status[instance.state["Name"]])
- except KeyError as e:
- if options.get("--original-action") == "list-status":
- logger.error("Unknown status \"{}\" returned for {} ({})".format(instance.state["Name"], instance.id, instance_name))
- result[instance.id] = (instance_name, "unknown")
- except Exception as e:
- logger.error("Failed to get node list: %s", e)
- return result
-
-def set_lastfence_tag(ec2_client, instance_id, timestamp):
- """Set a lastfence tag on the instance with the timestamp."""
- try:
- ec2_client.create_tags(
- Resources=[instance_id],
- Tags=[{"Key": "lastfence", "Value": str(timestamp)}]
- )
- logger.info(f"Set lastfence tag with timestamp {timestamp} on instance {instance_id}")
- except Exception as e:
- logger.error(f"Failed to set lastfence tag: {str(e)}")
- raise
-
-def set_power_status(conn, options):
- """Set power status of the instance."""
- timestamp = int(time.time()) # Unix timestamp
- ec2_client = conn.meta.client
- instance_id = options["--plug"]
- sg_to_remove = options.get("--secg", "").split(",") if options.get("--secg") else []
-
- # Perform self-check if skip-race not set
- if "--skip-race-check" not in options:
- self_instance_id = get_instance_id()
- if self_instance_id == instance_id:
- fail_usage("Self-fencing detected. Exiting.")
-
- # Verify the instance is running
- instance_state, _, _ = get_instance_details(ec2_client, instance_id)
- if instance_state != "running":
- fail_usage(f"Instance {instance_id} is not running. Exiting.")
-
- try:
- if options["--action"] == "on":
- if not "--unfence-ignore-restore" in options:
- restore_security_groups(ec2_client, instance_id)
- else:
- logger.info("Ignored Restoring security groups as --unfence-ignore-restore is set")
- elif options["--action"] == "off":
- if sg_to_remove:
- if "--invert-sg-removal" not in options:
- remove_security_groups(ec2_client, instance_id, sg_to_remove, timestamp)
- set_lastfence_tag(ec2_client, instance_id, timestamp)
- if "--onfence-poweroff" in options:
- shutdown_instance(ec2_client, instance_id)
- else:
- keep_only_security_groups(ec2_client, instance_id, sg_to_remove, timestamp)
- set_lastfence_tag(ec2_client, instance_id, timestamp)
- if "--onfence-poweroff" in options:
- shutdown_instance(ec2_client, instance_id)
- except Exception as e:
- logger.error("Failed to set power status: %s", e)
- fail(EC_STATUS)
-
-
-# Define fencing agent options
-def define_new_opts():
- #print("in define new opts")
-
- all_opt["region"] = {
- "getopt": "r:",
- "longopt": "region",
- "help": "-r, --region=[region] AWS region (e.g., us-east-1)",
- "shortdesc": "AWS Region.",
- "required": "0",
- "order": 1,
- }
- all_opt["access_key"] = {
- "getopt": "a:",
- "longopt": "access-key",
- "help": "-a, --access-key=[key] AWS access key.",
- "shortdesc": "AWS Access Key.",
- "required": "0",
- "order": 2,
- }
- all_opt["secret_key"] = {
- "getopt": "s:",
- "longopt": "secret-key",
- "help": "-s, --secret-key=[key] AWS secret key.",
- "shortdesc": "AWS Secret Key.",
- "required": "0",
- "order": 3,
- }
- all_opt["secg"] = {
- "getopt": "g:",
- "longopt": "secg",
- "help": "-g --secg=[sg1,sg2,...] Comma-separated list of Security Groups to remove.",
- "shortdesc": "Security Groups to remove.",
- "required": "0",
- "order": 4,
- }
- all_opt["plug"] = {
- "getopt": "n:",
- "longopt": "plug",
- "help": "-n, --plug=[id] Instance ID or target identifier (mandatory).",
- "shortdesc": "Target instance identifier.",
- "required": "1",
- "order": 5,
- }
- all_opt["skip_race_check"] = {
- "getopt": "",
- "longopt": "skip-race-check",
- "help": "--skip-race-check Skip race condition check.",
- "shortdesc": "Skip race condition check.",
- "required": "0",
- "order": 6,
- }
- all_opt["invert-sg-removal"] = {
- "getopt": "",
- "longopt": "invert-sg-removal",
- "help": "--invert-sg-removal Remove all security groups except the specified one.",
- "shortdesc": "Remove all security groups except specified..",
- "required": "0",
- "order": 7,
- }
- all_opt["unfence-ignore-restore"] = {
- "getopt": "",
- "longopt": "unfence-ignore-restore",
- "help": "--unfence-ignore-restore Do not restore security groups from tag when unfencing (off).",
- "shortdesc": "Remove all security groups except specified..",
- "required": "0",
- "order": 8,
-
- }
- all_opt["filter"] = {
- "getopt": ":",
- "longopt": "filter",
- "help": "--filter=[key=value] Filter (e.g. vpc-id=[vpc-XXYYZZAA])",
- "shortdesc": "Filter for list-action",
- "required": "0",
- "order": 9
- }
- all_opt["boto3_debug"] = {
- "getopt": "b:",
- "longopt": "boto3_debug",
- "help": "-b, --boto3_debug=[option] Boto3 and Botocore library debug logging",
- "shortdesc": "Boto Lib debug",
- "required": "0",
- "default": "False",
- "order": 10
- }
- all_opt["onfence-poweroff"] = {
- "getopt": "",
- "longopt": "onfence-poweroff",
- "help": "--onfence-poweroff Power off the machine async upon fence (this is a network fencing agent...)",
- "shortdesc": "Power off the machine async..",
- "required": "0",
- "order": 11
- }
-
-
-
-
-
-def main():
- conn = None
- #print("in main")
-
- device_opt = [
- "no_password",
- "region",
- "access_key",
- "secret_key",
- "secg",
- "plug",
- "skip_race_check",
- "invert-sg-removal",
- "unfence-ignore-restore",
- "filter",
- "boto3_debug",
- "onfence-poweroff"
-]
-
- #device_opt = ["port", "no_password" "filter", "boto3_debug","region", "access_key", "secret_key", "skip_race_check"]
- #device_opt = ["port", "no_password", "region", "access_key", "secret_key", "boto3_debug", "skip_race_check"]
-
-
-
- atexit.register(atexit_handler)
- #print("after atexit")
-
- define_new_opts()
-
- #print("after define new opts")
-
- try:
- #print("before check input")
- processed_input = process_input(device_opt)
- #print("Processed input:", processed_input)
- options = check_input(device_opt, processed_input)
- #print("after check input")
- except Exception as e:
- logger.error(f"Failed to process input options: {str(e)}")
- #print(f"Error details: {str(e)}")
- sys.exit(1)
-
- run_delay(options)
-
- docs = {
- "shortdesc": "Fence agent for AWS VPC",
- "longdesc": (
- "fence_aws_vpc is a Power Fencing agent for AWS VPC that works by "
- "manipulating security groups. It uses the boto3 library to connect to AWS.\n\n"
- "boto3 can be configured with AWS CLI or by creating ~/.aws/credentials.\n"
- "For instructions see: https://boto3.readthedocs.io/en/latest/guide/quickstart.html#configuration"
- ),
- "vendorurl": "http://www.amazon.com"
- }
- show_docs(options, docs)
-
-
- # Configure logging
- if "--debug-file" in options:
- for handler in logger.handlers:
- if isinstance(handler, logging.FileHandler):
- logger.removeHandler(handler)
- lh = logging.FileHandler(options["--debug-file"])
- logger.addHandler(lh)
- lhf = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
- lh.setFormatter(lhf)
- lh.setLevel(logging.DEBUG)
-
- # Configure boto3 logging
- if options.get("--boto3_debug", "").lower() not in ["1", "yes", "on", "true"]:
- boto3.set_stream_logger('boto3', logging.INFO)
- boto3.set_stream_logger('botocore', logging.CRITICAL)
- logging.getLogger('botocore').propagate = False
- logging.getLogger('boto3').propagate = False
- else:
- log_format = logging.Formatter('%(asctime)s %(name)-12s %(levelname)-8s %(message)s')
- logging.getLogger('botocore').propagate = False
- logging.getLogger('boto3').propagate = False
- fdh = logging.FileHandler('/var/log/fence_aws_vpc_boto3.log')
- fdh.setFormatter(log_format)
- logging.getLogger('boto3').addHandler(fdh)
- logging.getLogger('botocore').addHandler(fdh)
- logging.debug("Boto debug level is %s and sending debug info to /var/log/fence_aws_vpc_boto3.log",
- options.get("--boto3_debug"))
-
- # Establish AWS connection
- region = options.get("--region")
- access_key = options.get("--access-key")
- secret_key = options.get("--secret-key")
-
- try:
- conn = boto3.resource(
- "ec2",
- region_name=region,
- aws_access_key_id=access_key,
- aws_secret_access_key=secret_key,
- )
- except Exception as e:
- if not options.get("--action", "") in ["metadata", "manpage", "validate-all"]:
- fail_usage("Failed: Unable to connect to AWS: " + str(e))
- else:
- pass
-
- # Operate the fencing device using the fence library's fence_action
- #print("before fence action")
- result = fence_action(conn, options, set_power_status, get_power_status, get_nodes_list)
- sys.exit(result)
-
-
-if __name__ == "__main__":
- main()
-
diff --git a/tests/data/metadata/fence_aws_vpc.xml b/tests/data/metadata/fence_aws_vpc.xml
deleted file mode 100644
index e9ee6e99..00000000
--- a/tests/data/metadata/fence_aws_vpc.xml
+++ /dev/null
@@ -1,174 +0,0 @@
-<?xml version="1.0" ?>
-<resource-agent name="fence_aws_vpc" shortdesc="Fence agent for AWS VPC" >
-<longdesc>fence_aws_vpc is a Power Fencing agent for AWS VPC that works by manipulating security groups. It uses the boto3 library to connect to AWS.
-
-boto3 can be configured with AWS CLI or by creating ~/.aws/credentials.
-For instructions see: https://boto3.readthedocs.io/en/latest/guide/quickstart.html#configuration</longdesc>
-<vendor-url>http://www.amazon.com</vendor-url>
-<parameters>
- <parameter name="action" unique="0" required="1">
- <getopt mixed="-o, --action=[action]" />
- <content type="string" default="reboot" />
- <shortdesc lang="en">Fencing action</shortdesc>
- </parameter>
- <parameter name="region" unique="0" required="0">
- <getopt mixed="-r, --region=[region]" />
- <content type="string" />
- <shortdesc lang="en">AWS Region.</shortdesc>
- </parameter>
- <parameter name="access_key" unique="0" required="0">
- <getopt mixed="-a, --access-key=[key]" />
- <content type="string" />
- <shortdesc lang="en">AWS Access Key.</shortdesc>
- </parameter>
- <parameter name="secret_key" unique="0" required="0">
- <getopt mixed="-s, --secret-key=[key]" />
- <content type="string" />
- <shortdesc lang="en">AWS Secret Key.</shortdesc>
- </parameter>
- <parameter name="secg" unique="0" required="0">
- <getopt mixed="-g --secg=[sg1,sg2,...]" />
- <content type="string" />
- <shortdesc lang="en">Security Groups to remove.</shortdesc>
- </parameter>
- <parameter name="plug" unique="0" required="1">
- <getopt mixed="-n, --plug=[id]" />
- <content type="string" />
- <shortdesc lang="en">Target instance identifier.</shortdesc>
- </parameter>
- <parameter name="skip_race_check" unique="0" required="0">
- <getopt mixed="--skip-race-check" />
- <content type="boolean" />
- <shortdesc lang="en">Skip race condition check.</shortdesc>
- </parameter>
- <parameter name="invert-sg-removal" unique="0" required="0" deprecated="1">
- <getopt mixed="--invert-sg-removal" />
- <content type="boolean" />
- <shortdesc lang="en">Remove all security groups except specified..</shortdesc>
- </parameter>
- <parameter name="invert_sg_removal" unique="0" required="0" obsoletes="invert-sg-removal">
- <getopt mixed="--invert-sg-removal" />
- <content type="boolean" />
- <shortdesc lang="en">Remove all security groups except specified..</shortdesc>
- </parameter>
- <parameter name="unfence-ignore-restore" unique="0" required="0" deprecated="1">
- <getopt mixed="--unfence-ignore-restore" />
- <content type="boolean" />
- <shortdesc lang="en">Remove all security groups except specified..</shortdesc>
- </parameter>
- <parameter name="unfence_ignore_restore" unique="0" required="0" obsoletes="unfence-ignore-restore">
- <getopt mixed="--unfence-ignore-restore" />
- <content type="boolean" />
- <shortdesc lang="en">Remove all security groups except specified..</shortdesc>
- </parameter>
- <parameter name="filter" unique="0" required="0">
- <getopt mixed="--filter=[key=value]" />
- <content type="string" />
- <shortdesc lang="en">Filter for list-action</shortdesc>
- </parameter>
- <parameter name="boto3_debug" unique="0" required="0">
- <getopt mixed="-b, --boto3_debug=[option]" />
- <content type="string" default="False" />
- <shortdesc lang="en">Boto Lib debug</shortdesc>
- </parameter>
- <parameter name="onfence-poweroff" unique="0" required="0" deprecated="1">
- <getopt mixed="--onfence-poweroff" />
- <content type="boolean" />
- <shortdesc lang="en">Power off the machine async..</shortdesc>
- </parameter>
- <parameter name="onfence_poweroff" unique="0" required="0" obsoletes="onfence-poweroff">
- <getopt mixed="--onfence-poweroff" />
- <content type="boolean" />
- <shortdesc lang="en">Power off the machine async..</shortdesc>
- </parameter>
- <parameter name="quiet" unique="0" required="0">
- <getopt mixed="-q, --quiet" />
- <content type="boolean" />
- <shortdesc lang="en">Disable logging to stderr. Does not affect --verbose or --debug-file or logging to syslog.</shortdesc>
- </parameter>
- <parameter name="verbose" unique="0" required="0">
- <getopt mixed="-v, --verbose" />
- <content type="boolean" />
- <shortdesc lang="en">Verbose mode. Multiple -v flags can be stacked on the command line (e.g., -vvv) to increase verbosity.</shortdesc>
- </parameter>
- <parameter name="verbose_level" unique="0" required="0">
- <getopt mixed="--verbose-level" />
- <content type="integer" />
- <shortdesc lang="en">Level of debugging detail in output. Defaults to the number of --verbose flags specified on the command line, or to 1 if verbose=1 in a stonith device configuration (i.e., on stdin).</shortdesc>
- </parameter>
- <parameter name="debug" unique="0" required="0" deprecated="1">
- <getopt mixed="-D, --debug-file=[debugfile]" />
- <content type="string" />
- <shortdesc lang="en">Write debug information to given file</shortdesc>
- </parameter>
- <parameter name="debug_file" unique="0" required="0" obsoletes="debug">
- <getopt mixed="-D, --debug-file=[debugfile]" />
- <shortdesc lang="en">Write debug information to given file</shortdesc>
- </parameter>
- <parameter name="version" unique="0" required="0">
- <getopt mixed="-V, --version" />
- <content type="boolean" />
- <shortdesc lang="en">Display version information and exit</shortdesc>
- </parameter>
- <parameter name="help" unique="0" required="0">
- <getopt mixed="-h, --help" />
- <content type="boolean" />
- <shortdesc lang="en">Display help and exit</shortdesc>
- </parameter>
- <parameter name="plug_separator" unique="0" required="0">
- <getopt mixed="--plug-separator=[char]" />
- <content type="string" default="," />
- <shortdesc lang="en">Separator for plug parameter when specifying more than 1 plug</shortdesc>
- </parameter>
- <parameter name="delay" unique="0" required="0">
- <getopt mixed="--delay=[seconds]" />
- <content type="second" default="0" />
- <shortdesc lang="en">Wait X seconds before fencing is started</shortdesc>
- </parameter>
- <parameter name="disable_timeout" unique="0" required="0">
- <getopt mixed="--disable-timeout=[true/false]" />
- <content type="string" />
- <shortdesc lang="en">Disable timeout (true/false) (default: true when run from Pacemaker 2.0+)</shortdesc>
- </parameter>
- <parameter name="login_timeout" unique="0" required="0">
- <getopt mixed="--login-timeout=[seconds]" />
- <content type="second" default="5" />
- <shortdesc lang="en">Wait X seconds for cmd prompt after login</shortdesc>
- </parameter>
- <parameter name="power_timeout" unique="0" required="0">
- <getopt mixed="--power-timeout=[seconds]" />
- <content type="second" default="20" />
- <shortdesc lang="en">Test X seconds for status change after ON/OFF</shortdesc>
- </parameter>
- <parameter name="power_wait" unique="0" required="0">
- <getopt mixed="--power-wait=[seconds]" />
- <content type="second" default="0" />
- <shortdesc lang="en">Wait X seconds after issuing ON/OFF</shortdesc>
- </parameter>
- <parameter name="shell_timeout" unique="0" required="0">
- <getopt mixed="--shell-timeout=[seconds]" />
- <content type="second" default="3" />
- <shortdesc lang="en">Wait X seconds for cmd prompt after issuing command</shortdesc>
- </parameter>
- <parameter name="stonith_status_sleep" unique="0" required="0">
- <getopt mixed="--stonith-status-sleep=[seconds]" />
- <content type="second" default="1" />
- <shortdesc lang="en">Sleep X seconds between status calls during a STONITH action</shortdesc>
- </parameter>
- <parameter name="retry_on" unique="0" required="0">
- <getopt mixed="--retry-on=[attempts]" />
- <content type="integer" default="1" />
- <shortdesc lang="en">Count of attempts to retry power on</shortdesc>
- </parameter>
-</parameters>
-<actions>
- <action name="on" automatic="0"/>
- <action name="off" />
- <action name="reboot" />
- <action name="status" />
- <action name="monitor" />
- <action name="metadata" />
- <action name="manpage" />
- <action name="validate-all" />
-</actions>
-</resource-agent>
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Tue, Feb 25, 2:07 AM (1 d, 7 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1463978
Default Alt Text
(41 KB)
Attached To
Mode
rF Fence Agents
Attached
Detach File
Event Timeline
Log In to Comment