-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfunc.py
More file actions
128 lines (105 loc) · 5.24 KB
/
func.py
File metadata and controls
128 lines (105 loc) · 5.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import io
import json
import logging
import oci
from fdk import response
# Configure logging
logging.basicConfig(level=logging.INFO)
def stop_all_compute_instances(signer, compartment_id):
"""
Scans a compartment AND ALL ITS SUB-COMPARTMENTS for RUNNING VM instances and stops them.
Parameters:
signer (oci.signer.Signer): The OCI signer object for authentication.
compartment_id (str): The OCID of the PARENT compartment to scan.
Returns:
list: A list of messages detailing the actions taken.
"""
logging.info(f"--- Starting recursive instance shutdown process for parent compartment: {compartment_id} ---")
action_log = []
try:
# Initialize the Compute client with the resource principal signer
compute_client = oci.core.ComputeClient(config={}, signer=signer)
# List all instances in the specified compartment AND its children
list_instances_response = compute_client.list_instances(
compartment_id=compartment_id,
compartment_id_in_subtree=True # <-- THIS IS THE CRITICAL CHANGE
)
instances = list_instances_response.data
logging.info(f"Found {len(instances)} total instances in the compartment and its children.")
if not instances:
msg = "No instances found in the compartment tree. No action taken."
logging.info(msg)
action_log.append(msg)
return action_log
# Iterate through the instances and stop any that are running
for instance in instances:
if instance.lifecycle_state == 'RUNNING':
instance_name = instance.display_name
instance_id = instance.id
logging.info(f"Attempting to stop instance: {instance_name} (ID: {instance_id})")
try:
# The action to perform on the instance is 'STOP'
compute_client.instance_action(instance_id=instance_id, action="STOP")
msg = f"Successfully initiated STOP action for instance: {instance_name}"
logging.info(msg)
action_log.append(msg)
except oci.exceptions.ServiceError as e:
msg = f"ERROR stopping instance {instance_name}: {e.message}"
logging.error(msg)
action_log.append(msg)
else:
logging.info(f"Instance '{instance.display_name}' is not RUNNING (State: {instance.lifecycle_state}). Skipping.")
logging.info("--- Finished instance shutdown process ---")
return action_log
except oci.exceptions.ServiceError as e:
error_msg = f"A top-level OCI service error occurred: {e.message}"
logging.error(error_msg)
action_log.append(error_msg)
return action_log
except Exception as e:
error_msg = f"An unexpected error occurred: {str(e)}"
logging.error(error_msg)
action_log.append(error_msg)
return action_log
def handler(ctx, data: io.BytesIO = None):
"""
Main handler function triggered by the OCI Notification Service.
"""
logging.info("Function invoked.")
try:
# OCI Functions use a Resource Principal Signer for authentication.
signer = oci.auth.signers.get_resource_principals_signer()
# The body of the notification is in the data object.
body = json.loads(data.getvalue())
# The budget alert message is nested within the notification body.
# It's good practice to check if the 'message' key exists.
if "message" not in body:
logging.error("Notification body does not contain a 'message' key.")
return response.Response(
ctx, response_data=json.dumps({"status": "Failed", "error": "Invalid payload format"}),
headers={"Content-Type": "application/json"}
)
# The message itself is a JSON string, so we need to parse it again.
budget_alert_message = json.loads(body.get("message"))
# Extract the compartment ID from the budget's scope.
compartment_id = budget_alert_message.get("resourceId")
if not compartment_id:
logging.error("Could not find 'resourceId' (compartmentId) in the budget alert message.")
return response.Response(
ctx, response_data=json.dumps({"status": "Failed", "error": "Missing compartmentId"}),
headers={"Content-Type": "application/json"}
)
logging.info(f"Budget alert received for compartment: {compartment_id}")
# Call the function to stop the instances
action_log = stop_all_compute_instances(signer, compartment_id)
# Return a success response
return response.Response(
ctx, response_data=json.dumps({"status": "Success", "actions": action_log}),
headers={"Content-Type": "application/json"}
)
except (Exception, ValueError) as ex:
logging.error(f"Error in handler: {str(ex)}")
return response.Response(
ctx, response_data=json.dumps({"status": "Failed", "error": str(ex)}),
headers={"Content-Type": "application/json"}
)