import io import json import oci import re from datetime import datetime, timedelta, timezone import pytz import logging from fdk import response def getInstancePoolFromBackendSetV2(compute_management_client, compartment_id, lb_ocid, backendSetName, app_context_variables): # Identify which instance pool has at least one instance that belongs to the LB and BackendSetName returned_instance_pool_id = "notfound" actual_size = 0 instance_pools = compute_management_client.list_instance_pools(compartment_id).data for instance_pool in instance_pools: detailed_instance_pool = compute_management_client.get_instance_pool(instance_pool.id).data conditions = [detailed_instance_pool.load_balancers[0].backend_set_name == backendSetName, detailed_instance_pool.load_balancers[0].load_balancer_id == lb_ocid] if all(conditions): if detailed_instance_pool.lifecycle_state in ('RUNNING'): returned_instance_pool_id = detailed_instance_pool.id actual_size = detailed_instance_pool.size break else: logging.info("Current instance pool is not in RUNNING state, lifecycle_state is: "+detailed_instance_pool.lifecycle_state) break return returned_instance_pool_id, actual_size def UpdateInstancePoolSize(compute_management_client, instance_pool_id, new_size): try: update_instance_pool_response = compute_management_client.update_instance_pool(instance_pool_id=instance_pool_id, update_instance_pool_details=oci.core.models.UpdateInstancePoolDetails(size=new_size)) logging.info(update_instance_pool_response.data) except oci.exceptions.ServiceError as e: logging.ERROR("Failed to resize the pool:"+e.message) def scale_to_target_size(lb_ocid, backendSetName, app_context_variables, alarm_ocid): signer = oci.auth.signers.get_resource_principals_signer() compute_management_client = oci.core.ComputeManagementClient(config={}, signer=signer) load_balancer_client = oci.load_balancer.LoadBalancerClient(config={}, signer=signer) monitoring_client = oci.monitoring.MonitoringClient(config={}, signer=signer) target_size = 0 desired_size = 0 try: target_size = int(app_context_variables['INSTANCE_POOL_TARGET_SIZE']) desired_size = int(app_context_variables['INSTANCE_POOL_DESIRED_SIZE']) except Exception as ex: logging.error('Getting INSTANCE_POOL_TARGET_SIZE from configuration : - '+str(ex)) raise #Get the alarm freeform TAG to identify what type of scaling this alarm is for : IN or OUT alarm_detail = monitoring_client.get_alarm(alarm_ocid).data autoscaling_type = alarm_detail.freeform_tags["autoscaling_type"] logging.info("identified the freeformTag for autoscaling_type ["+ autoscaling_type+"]") if str(autoscaling_type) not in ["in", "out"]: logging.error("freeformTAG autoscaling_type should be defined as in/out ") raise new_pool_size = target_size # If autoscaling = IN, reduce the number of instances back to the desired value if autoscaling_type == "in": new_pool_size = desired_size #Get the compartment from the Load Balancer lb = load_balancer_client.get_load_balancer(load_balancer_id=lb_ocid).data compartment_id = lb.compartment_id action_taken = "NO-ACTION-TAKEN" instance_pool_id,actual_size = getInstancePoolFromBackendSetV2(compute_management_client, compartment_id,lb_ocid,backendSetName, app_context_variables) if instance_pool_id != "notfound": logging.info("Instance Pool ID: "+instance_pool_id) logging.info("Actual Size: "+str(actual_size)) logging.info("New Size: "+str(new_pool_size)) if actual_size != new_pool_size: logging.info("Calling update instance...") action_taken = "INSTANCE_POOL_RESIZED_TO_"+str(new_pool_size) UpdateInstancePoolSize(compute_management_client, instance_pool_id,new_pool_size) else: logging.info("Current Instance Pool size is the same as the new requested size, ignoring...") return action_taken def handler(ctx, data: io.BytesIO=None): alarm_msg = {} logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') func_response = "" try: app_context_variables = dict(ctx.Config()) except Exception as ex: logging.ERROR('Getting ctx config - '+str(ex)) raise try: alarm_msg = json.loads(data.getvalue()) logging.info("Alarm full message: ") print(alarm_msg, flush=True) except (Exception, ValueError) as ex: logging.error("Error parsing the JSON: " + str(ex)) if alarm_msg["type"] in ("REPEAT","OK_TO_FIRING"): msg_type = alarm_msg["type"] logging.info("Identified a message with type :"+msg_type) if alarm_msg["alarmMetaData"][0]["dimensions"]: alarm_metric_dimension = alarm_msg["alarmMetaData"][0]["dimensions"][0] alarm_resource_id = alarm_metric_dimension["resourceId"] alarm_backendsetName = alarm_metric_dimension["backendSetName"] alarm_ocid = alarm_msg["alarmMetaData"][0]["id"] logging.info("Resource ID: " + alarm_resource_id) logging.info("backendSetName: " + alarm_backendsetName) func_response = scale_to_target_size(alarm_resource_id, alarm_backendsetName, app_context_variables,alarm_ocid) logging.info(func_response) else: func_response = 'There is no metric dimension in this alarm message, ignoring alarm...' logging.info(func_response) else: logging.info('Nothing to do, alarm is not FIRING') func_response = "Nothing to do, alarm is not FIRING" return response.Response( ctx, response_data=func_response, headers={"Content-Type": "application/json"} )