Skip to content

Commit d97ec51

Browse files
committed
adding oci-ons-compute-shape-increase-python
1 parent 850783a commit d97ec51

File tree

8 files changed

+265
-0
lines changed

8 files changed

+265
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.DS_Store
2+
__pycache__
3+
test.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
# Automatically Resize VMs
2+
Automatically resize VMs that exceed memory consumption. The function code is referenced in the OCI documentation at https://docs.cloud.oracle.com/en-us/iaas/Content/Notification/Tasks/scenarioa.htm.
3+
4+
This use case involves writing a function to resize VMs and creating an alarm that sends a message to that function. When the alarm fires, the Notifications service sends the alarm message to the destination topic, which then fans out to the topic's subscriptions. In this scenario, the topic's subscriptions include the function as well as your email. The function is invoked on receipt of the alarm message.
5+
6+
![ONS to Functions](https://docs.cloud.oracle.com/en-us/iaas/Content/Resources/Images/notifications-scenarioA.png)
7+
8+
As you make your way through this tutorial, look out for this icon ![user input icon](./images/userinput.png).
9+
Whenever you see it, it's time for you to perform an action.
10+
11+
12+
## Prerequisites
13+
Before you deploy this sample function, make sure you have run step A, B and C of the [Oracle Functions Quick Start Guide for Cloud Shell](https://www.oracle.com/webfolder/technetwork/tutorials/infographics/oci_functions_cloudshell_quickview/functions_quickview_top/functions_quickview/index.html)
14+
* A - Set up your tenancy
15+
* B - Create application
16+
* C - Set up your Cloud Shell dev environment
17+
18+
19+
## List Applications
20+
Assuming your have successfully completed the prerequisites, you should see your
21+
application in the list of applications.
22+
```
23+
fn ls apps
24+
```
25+
26+
27+
## Create or Update your Dynamic Group
28+
In order to use other OCI Services, your function must be part of a dynamic group. For information on how to create a dynamic group, refer to the [documentation](https://docs.cloud.oracle.com/iaas/Content/Identity/Tasks/managingdynamicgroups.htm#To).
29+
30+
When specifying the *Matching Rules*, we suggest matching all functions in a compartment with:
31+
```
32+
ALL {resource.type = 'fnfunc', resource.compartment.id = 'ocid1.compartment.oc1..aaaaaxxxxx'}
33+
```
34+
Please check the [Accessing Other Oracle Cloud Infrastructure Resources from Running Functions](https://docs.cloud.oracle.com/en-us/iaas/Content/Functions/Tasks/functionsaccessingociresources.htm) for other *Matching Rules* options.
35+
36+
37+
## Create or Update IAM Policies
38+
Create a new policy that allows the dynamic group to *use instances*.
39+
40+
![user input icon](./images/userinput.png)
41+
42+
Your policy should look something like this:
43+
```
44+
Allow dynamic-group <dynamic-group-name> to use instances in compartment <compartment-name>
45+
```
46+
For more information on how to create policies, check the [documentation](https://docs.cloud.oracle.com/iaas/Content/Identity/Concepts/policysyntax.htm).
47+
48+
49+
## Review and customize your function
50+
Review the following files in the current folder:
51+
* the code of the function, [func.py](./func.py)
52+
* its dependencies, [requirements.txt](./requirements.txt)
53+
* the function metadata, [func.yaml](./func.yaml)
54+
55+
The following piece of code in [func.py](./func.py) should be updated to match your needs:
56+
```
57+
if current_shape == "VM.Standard1.1":
58+
new_shape = "VM.Standard2.1"
59+
elif current_shape == "VM.Standard2.1":
60+
new_shape = "VM.Standard2.2"
61+
```
62+
63+
The code in [list_vm_shapes.py](./list_vm_shapes.py) can be used to improve the logic of VM shape selection.
64+
65+
66+
## Deploy the function
67+
In Cloud Shell, run the fn deploy command to build the function and its dependencies as a Docker image,
68+
push the image to OCIR, and deploy the function to Oracle Functions in your application.
69+
70+
![user input icon](./images/userinput.png)
71+
```
72+
fn -v deploy --app <your app name>
73+
```
74+
e.g.
75+
```
76+
fn -v deploy --app myapp
77+
```
78+
79+
80+
## Configure Oracle Notification Service
81+
This section walks through creating an alarm using the Console and then updating the ONS topic created with the alarm.
82+
83+
![user input icon](./images/userinput.png)
84+
85+
On the OCI console, navigate to *Monitoring* > *Alarm Definitions*. Click *Create Alarm*.
86+
87+
On the Create Alarm page, under Define alarm, set up your threshold:
88+
89+
Metric description:
90+
* Compartment: (select the compartment that contains your VM)
91+
* Metric Namespace: oci_computeagent
92+
* Metric Name: MemoryUtilization
93+
* Interval: 1m
94+
* Statistic: Max
95+
96+
Trigger rule:
97+
* Operator: greater than
98+
* Value: 90 (or lower for testing purposes)
99+
* Trigger Delay Minutes: 1
100+
101+
Select your function under Notifications, Destinations:
102+
* Destination Service: Notifications Service
103+
* Compartment: (select the compartment where you want to create the topic and associated subscriptions)
104+
105+
Topic: Click *Create a topic*
106+
* Topic Name: Alarm Topic
107+
* Subscription Protocol: Function
108+
* Function Compartment: (select the compartment that contains your function)
109+
* Function Application: (select the application that contains your function)
110+
* Function: (select your function)
111+
* Click *Create topic and subscription*
112+
113+
Click Save alarm.
114+
115+
116+
## Test ONS > Fn
117+
First, test the function indivudually.
118+
119+
![user input icon](./images/userinput.png)
120+
121+
Update section "resourceId" in [test-alarm.json](./test-alarm.json) with the OCID of the instance you want to update.
122+
123+
Invoke the function as follows:
124+
```
125+
cat test-alarm.json | fn invoke <your app name> <function name>
126+
```
127+
e.g.:
128+
```
129+
cat test-alarm.json | fn invoke myapp oci-ons-compute-shape-increase-python
130+
```
131+
132+
Now, the whole flow can be tested. Connect to an instance in the compartment where the alarm is active, and stress the memory utilization with the *stress* utility for example.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
#
2+
# oci-ons-compute-shape-increase-python version 1.0.
3+
#
4+
# Copyright (c) 2020 Oracle, Inc.
5+
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
6+
#
7+
8+
import io
9+
import json
10+
import oci
11+
12+
from fdk import response
13+
14+
def increase_compute_shape(instance_id, alarm_msg_shape):
15+
signer = oci.auth.signers.get_resource_principals_signer()
16+
compute_client = oci.core.ComputeClient(config={}, signer=signer)
17+
current_shape = compute_client.get_instance(instance_id).data.shape
18+
print("INFO: current shape for Instance {0}: {1}".format(instance_id,current_shape), flush=True)
19+
if current_shape != alarm_msg_shape:
20+
return "The shape of Instance {} differs from the Alarm message".format(instance_id)
21+
# improve the logic below to handle more scenarios, make sure the shapes you select are available in the region and AD
22+
if current_shape == "VM.Standard1.1":
23+
new_shape = "VM.Standard2.1"
24+
elif current_shape == "VM.Standard2.1":
25+
new_shape = "VM.Standard2.2"
26+
else:
27+
return "Instance {0} cannot get a bigger shape than its current shape {1}".format(instance_id,current_shape)
28+
print("INFO: new shape for Instance {0}: {1}".format(instance_id,new_shape), flush=True)
29+
try:
30+
update_instance_details = oci.core.models.UpdateInstanceDetails(shape=new_shape)
31+
resp = compute_client.update_instance(instance_id=instance_id, update_instance_details=update_instance_details)
32+
print(resp, flush=True)
33+
except Exception as ex:
34+
print('ERROR: cannot update instance {}'.format(instance_id), flush=True)
35+
raise
36+
return "The shape of Instance {} is updated, the instance is rebooting...".format(instance_id)
37+
38+
def handler(ctx, data: io.BytesIO=None):
39+
alarm_msg = {}
40+
message_id = func_response = ""
41+
try:
42+
headers = ctx.Headers()
43+
message_id = headers["x-oci-ns-messageid"]
44+
except Exception as ex:
45+
print('ERROR: Missing Message ID in the header', ex, flush=True)
46+
raise
47+
print("INFO: Message ID = ", message_id, flush=True)
48+
# the Message Id can be stored in a database and be used to check for duplicate messages
49+
try:
50+
alarm_msg = json.loads(data.getvalue())
51+
print("INFO: Alarm message: ")
52+
print(alarm_msg, flush=True)
53+
except (Exception, ValueError) as ex:
54+
print(str(ex), flush=True)
55+
56+
if alarm_msg["type"] == "OK_TO_FIRING":
57+
if alarm_msg["alarmMetaData"][0]["dimensions"]:
58+
alarm_metric_dimension = alarm_msg["alarmMetaData"][0]["dimensions"][0] #assuming the first dimension matches the instance to resize
59+
print("INFO: Instance to resize: ", alarm_metric_dimension["resourceId"], flush=True)
60+
func_response = increase_compute_shape(alarm_metric_dimension["resourceId"], alarm_metric_dimension["shape"])
61+
print("INFO: ", func_response, flush=True)
62+
else:
63+
print('ERROR: There is no metric dimension in this alarm message', flush=True)
64+
func_response = "There is no metric dimension in this alarm message"
65+
else:
66+
print('INFO: Nothing to do, alarm is not FIRING', flush=True)
67+
func_response = "Nothing to do, alarm is not FIRING"
68+
69+
return response.Response(
70+
ctx,
71+
response_data=func_response,
72+
headers={"Content-Type": "application/json"}
73+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
schema_version: 20180708
2+
name: oci-ons-compute-shape-increase-python
3+
version: 0.0.1
4+
runtime: python
5+
entrypoint: /python/bin/fdk /function/func.py handler
6+
memory: 256
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#
2+
# oci-ons-compute-shape-increase-python version 1.0.
3+
#
4+
# Copyright (c) 2020 Oracle, Inc.
5+
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
6+
#
7+
8+
def list_vm_shapes(compute_client, compartment_id, availability_domain):
9+
list_shapes_response = oci.pagination.list_call_get_all_results(
10+
compute_client.list_shapes,
11+
compartment_id,
12+
availability_domain=availability_domain
13+
)
14+
vm_shapes_only = list(filter(lambda shape: shape.shape.startswith("VM"), list_shapes_response.data))
15+
available_shapes = list(map(lambda shape: shape.shape, vm_shapes_only))
16+
return available_shapes
17+
18+
#available_shapes = list_vm_shapes(compute_client, compartment_id, availability_domain)
19+
#print("available shapes: ", available_shapes, flush=True)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
fdk
2+
oci
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"dedupeKey": "501f01d7-20cd-5958-a5ca-61ba0ddebd8d",
3+
"title": "TEST-high-memory",
4+
"body": "High memory usage",
5+
"type": "OK_TO_FIRING",
6+
"severity": "CRITICAL",
7+
"timestampEpochMillis": 1582324860000,
8+
"alarmMetaData": [
9+
{
10+
"id": "ocid1.alarm.oc1.iad.exampleuniqueID",
11+
"status": "FIRING",
12+
"severity": "CRITICAL",
13+
"query": "MemoryUtilization[1m]{resourceId = \"ocid1.instance.oc1.iad.exampleuniqueID\"}.max() > 20",
14+
"totalMetricsFiring": 1,
15+
"dimensions": [
16+
{
17+
"instancePoolId": "Default",
18+
"resourceDisplayName": "myinstance1",
19+
"faultDomain": "FAULT-DOMAIN-2",
20+
"resourceId": "ocid1.instance.oc1.iad.anuwcljsato5mnsdjasdaskgdasujggsmihhzf6ae26mxnxucd7pcwa3rneq",
21+
"availabilityDomain": "xqgA:US-ASHBURN-AD-1",
22+
"imageId": "ocid1.image.oc1.iad.exampleuniqueID",
23+
"region": "us-ashburn-1",
24+
"shape": "VM.Standard.E2.1"
25+
}
26+
]
27+
}
28+
],
29+
"version": 1.0
30+
}

0 commit comments

Comments
 (0)