Skip to content

Commit a2efcd5

Browse files
committed
F OpenNebula/one-aiops#569: refactor one backend with OneGate
F OpenNebula/one-aiops#569: new granularity with 1 Pod x Node and N worker processes B OpenNebula/one-aiops#569: use generic enum for compatibility with older python versions B OpenNebula/one-aiops#569: fix enum typo F OpenNebula/one-aiops#569: get state from nodes instead from roles F OpenNebula/one-aiops#569: add support for the new worker memory and cpu parameters M OpenNebula/one-aiops#569: return worker processes as Integer B OpenNebula/one-aiops#569: fix scale down call B OpenNebula/one-aiops#569: fix scaling operation in OneGate F OpenNebula/one-aiops#569: add timeout for waiting nodes F OpenNebula/one-aiops#569: improve logging
1 parent 5eb427e commit a2efcd5

File tree

4 files changed

+216
-500
lines changed

4 files changed

+216
-500
lines changed
Lines changed: 21 additions & 178 deletions
Original file line numberDiff line numberDiff line change
@@ -1,197 +1,40 @@
1-
import os
2-
import json
1+
import enum
32

4-
5-
from lithops.serverless.backends.k8s.config import (
6-
DEFAULT_CONFIG_KEYS,
7-
load_config as original_load_config
8-
)
3+
from lithops.serverless.backends.k8s.config import DEFAULT_CONFIG_KEYS
4+
from lithops.serverless.backends.k8s.config import load_config as load_k8
95

106

117
class OneConfigError(Exception):
128
pass
139

1410

15-
MANDATORY_CONFIG_KEYS = {
16-
"public_network_id",
17-
"private_network_id"
18-
}
19-
20-
OPTIONAL_CONFIG_KEYS = {
21-
"ONEAPP_VROUTER_ETH0_VIP0": "",
22-
"ONEAPP_VROUTER_ETH1_VIP0": "",
23-
"ONEAPP_RKE2_SUPERVISOR_EP": "ep0.eth0.vr:9345",
24-
"ONEAPP_K8S_CONTROL_PLANE_EP": "ep0.eth0.vr:6443",
25-
"ONEAPP_K8S_EXTRA_SANS": "localhost,127.0.0.1,ep0.eth0.vr,${vnf.TEMPLATE.CONTEXT.ETH0_IP},k8s.yourdomain.it",
26-
"ONEAPP_K8S_MULTUS_ENABLED": "NO",
27-
"ONEAPP_K8S_MULTUS_CONFIG": "",
28-
"ONEAPP_K8S_CNI_PLUGIN": "cilium",
29-
"ONEAPP_K8S_CNI_CONFIG": "",
30-
"ONEAPP_K8S_CILIUM_RANGE": "",
31-
"ONEAPP_K8S_METALLB_ENABLED": "NO",
32-
"ONEAPP_K8S_METALLB_CONFIG": "",
33-
"ONEAPP_K8S_METALLB_RANGE": "",
34-
"ONEAPP_K8S_LONGHORN_ENABLED": "YES",
35-
"ONEAPP_STORAGE_DEVICE": "/dev/vdb",
36-
"ONEAPP_STORAGE_FILESYSTEM": "xfs",
37-
"ONEAPP_K8S_TRAEFIK_ENABLED": "YES",
38-
"ONEAPP_VNF_HAPROXY_INTERFACES": "eth0",
39-
"ONEAPP_VNF_HAPROXY_REFRESH_RATE": "30",
40-
"ONEAPP_VNF_HAPROXY_LB0_PORT": "9345",
41-
"ONEAPP_VNF_HAPROXY_LB1_PORT": "6443",
42-
"ONEAPP_VNF_HAPROXY_LB2_PORT": "443",
43-
"ONEAPP_VNF_HAPROXY_LB3_PORT": "80",
44-
"ONEAPP_VNF_DNS_ENABLED": "YES",
45-
"ONEAPP_VNF_DNS_INTERFACES": "eth1",
46-
"ONEAPP_VNF_DNS_NAMESERVERS": "1.1.1.1,8.8.8.8",
47-
"ONEAPP_VNF_NAT4_ENABLED": "YES",
48-
"ONEAPP_VNF_NAT4_INTERFACES_OUT": "eth0",
49-
"ONEAPP_VNF_ROUTER4_ENABLED": "YES",
50-
"ONEAPP_VNF_ROUTER4_INTERFACES": "eth0,eth1"
51-
}
52-
53-
DEFAULT_PRIVATE_VNET = """
54-
NAME = "private-oneke"
55-
VN_MAD = "bridge"
56-
AUTOMATIC_VLAN_ID = "YES"
57-
AR = [TYPE = "IP4", IP = "192.168.150.0", SIZE = "51"]
58-
"""
59-
60-
STATE = {
61-
0: "INIT",
62-
1: "PENDING",
63-
2: "HOLD",
64-
3: "ACTIVE",
65-
4: "STOPPED",
66-
5: "SUSPENDED",
67-
6: "DONE",
68-
8: "POWEROFF",
69-
9: "UNDEPLOYED",
70-
10: "CLONING",
71-
11: "CLONING_FAILURE"
72-
}
11+
@enum.unique
12+
class ServiceState(enum.Enum):
13+
RUNNING = 2
14+
SCALING = 9
15+
COOLDOWN = 10
7316

74-
LCM_STATE = {
75-
0: "LCM_INIT",
76-
1: "PROLOG",
77-
2: "BOOT",
78-
3: "RUNNING",
79-
4: "MIGRATE",
80-
5: "SAVE_STOP",
81-
6: "SAVE_SUSPEND",
82-
7: "SAVE_MIGRATE",
83-
8: "PROLOG_MIGRATE",
84-
9: "PROLOG_RESUME",
85-
10: "EPILOG_STOP",
86-
11: "EPILOG",
87-
12: "SHUTDOWN",
88-
15: "CLEANUP_RESUBMIT",
89-
16: "UNKNOWN",
90-
17: "HOTPLUG",
91-
18: "SHUTDOWN_POWEROFF",
92-
19: "BOOT_UNKNOWN",
93-
20: "BOOT_POWEROFF",
94-
21: "BOOT_SUSPENDED",
95-
22: "BOOT_STOPPED",
96-
23: "CLEANUP_DELETE",
97-
24: "HOTPLUG_SNAPSHOT",
98-
25: "HOTPLUG_NIC",
99-
26: "HOTPLUG_SAVEAS",
100-
27: "HOTPLUG_SAVEAS_POWEROFF",
101-
28: "HOTPLUG_SAVEAS_SUSPENDED",
102-
29: "SHUTDOWN_UNDEPLOY",
103-
30: "EPILOG_UNDEPLOY",
104-
31: "PROLOG_UNDEPLOY",
105-
32: "BOOT_UNDEPLOY",
106-
33: "HOTPLUG_PROLOG_POWEROFF",
107-
34: "HOTPLUG_EPILOG_POWEROFF",
108-
35: "BOOT_MIGRATE",
109-
36: "BOOT_FAILURE",
110-
37: "BOOT_MIGRATE_FAILURE",
111-
38: "PROLOG_MIGRATE_FAILURE",
112-
39: "PROLOG_FAILURE",
113-
40: "EPILOG_FAILURE",
114-
41: "EPILOG_STOP_FAILURE",
115-
42: "EPILOG_UNDEPLOY_FAILURE",
116-
43: "PROLOG_MIGRATE_POWEROFF",
117-
44: "PROLOG_MIGRATE_POWEROFF_FAILURE",
118-
45: "PROLOG_MIGRATE_SUSPEND",
119-
46: "PROLOG_MIGRATE_SUSPEND_FAILURE",
120-
47: "BOOT_UNDEPLOY_FAILURE",
121-
48: "BOOT_STOPPED_FAILURE",
122-
49: "PROLOG_RESUME_FAILURE",
123-
50: "PROLOG_UNDEPLOY_FAILURE",
124-
51: "DISK_SNAPSHOT_POWEROFF",
125-
52: "DISK_SNAPSHOT_REVERT_POWEROFF",
126-
53: "DISK_SNAPSHOT_DELETE_POWEROFF",
127-
54: "DISK_SNAPSHOT_SUSPENDED",
128-
55: "DISK_SNAPSHOT_REVERT_SUSPENDED",
129-
56: "DISK_SNAPSHOT_DELETE_SUSPENDED",
130-
57: "DISK_SNAPSHOT",
131-
59: "DISK_SNAPSHOT_DELETE",
132-
60: "PROLOG_MIGRATE_UNKNOWN",
133-
61: "PROLOG_MIGRATE_UNKNOWN_FAILURE",
134-
62: "DISK_RESIZE",
135-
63: "DISK_RESIZE_POWEROFF",
136-
64: "DISK_RESIZE_UNDEPLOYED",
137-
65: "HOTPLUG_NIC_POWEROFF",
138-
66: "HOTPLUG_RESIZE",
139-
67: "HOTPLUG_SAVEAS_UNDEPLOYED",
140-
68: "HOTPLUG_SAVEAS_STOPPED",
141-
69: "BACKUP",
142-
70: "BACKUP_POWEROFF"
143-
}
14417

14518
# Add OpenNebula defaults
146-
DEFAULT_CONFIG_KEYS.update({
147-
'timeout': 600,
148-
'kubecfg_path': '/tmp/kube_config',
149-
'oneke_config_path': None,
150-
'delete': False,
151-
'minimum_nodes': 0,
152-
'maximum_nodes': -1,
153-
'average_job_execution': 1,
154-
'auto_scale': 'all',
155-
})
19+
DEFAULT_CONFIG_KEYS.update(
20+
{
21+
"minimum_nodes": 1,
22+
"maximum_nodes": 3,
23+
}
24+
)
15625

15726

15827
def load_config(config_data):
159-
if 'oneke_config' in config_data['one']:
160-
oneke_config = config_data['one']['oneke_config']
161-
162-
# Validate mandatory params
163-
for key in MANDATORY_CONFIG_KEYS:
164-
if key not in oneke_config:
165-
raise OneConfigError(f"'{key}' is missing in 'oneke_config'")
166-
public_network_id = oneke_config['public_network_id']
167-
private_network_id = oneke_config['private_network_id']
168-
169-
# Optional params
170-
name = oneke_config.get('name', 'OneKE for lithops')
171-
custom_attrs_values = {key: oneke_config.get(key, default_value)
172-
for key, default_value in OPTIONAL_CONFIG_KEYS.items()}
173-
174-
oneke_update = {
175-
"name": name,
176-
"networks_values": [
177-
{"Public": {"id": str(public_network_id)}},
178-
{"Private": {"id": str(private_network_id)}}
179-
],
180-
"custom_attrs_values": custom_attrs_values
181-
}
182-
# Override oneke_config with a valid JSON to update the service
183-
config_data['one']['oneke_config'] = json.dumps(oneke_update)
184-
18528
# Load default config
18629
for key in DEFAULT_CONFIG_KEYS:
187-
if key not in config_data['one']:
188-
config_data['one'][key] = DEFAULT_CONFIG_KEYS[key]
30+
if key not in config_data["one"]:
31+
config_data["one"][key] = DEFAULT_CONFIG_KEYS[key]
18932

19033
# Ensure 'k8s' key exists and is a dictionary
191-
if 'k8s' not in config_data or config_data['k8s'] is None:
192-
config_data['k8s'] = {}
193-
config_data['k8s']['docker_user'] = config_data['one']['docker_user']
194-
config_data['k8s']['docker_password'] = config_data['one']['docker_password']
34+
if "k8s" not in config_data or config_data["k8s"] is None:
35+
config_data["k8s"] = {}
36+
config_data["k8s"]["docker_user"] = config_data["one"]["docker_user"]
37+
config_data["k8s"]["docker_password"] = config_data["one"]["docker_password"]
19538

19639
# Load k8s default config
197-
original_load_config(config_data)
40+
load_k8(config_data)
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import os
2+
3+
import requests
4+
5+
6+
class OneGateError(Exception):
7+
"""General exception for OneGate-related errors."""
8+
9+
def __init__(self, message, status_code=None):
10+
super().__init__(message)
11+
self.status_code = status_code
12+
13+
14+
class OneGateClient:
15+
def __init__(self):
16+
self.endpoint = os.getenv(
17+
"ONEGATE_ENDPOINT", self.get_config("ONEGATE_ENDPOINT")
18+
)
19+
self.token = self.read_file("/mnt/context/token.txt")
20+
self.vm_id = self.get_config("VMID")
21+
22+
@staticmethod
23+
def read_file(filepath):
24+
with open(filepath, "r") as file:
25+
return file.read().strip()
26+
27+
@staticmethod
28+
def get_config(param, filepath="/mnt/context/context.sh"):
29+
with open(filepath, "r") as file:
30+
for line in file:
31+
if line.startswith(f"{param}="):
32+
return line.split("=", 1)[1].strip().strip("'\"")
33+
return None
34+
35+
def get(self, path):
36+
"""
37+
Make a GET request to OneGate API and return the JSON response.
38+
"""
39+
url = f"{self.endpoint}/{path}"
40+
headers = {"X-ONEGATE-TOKEN": self.token, "X-ONEGATE-VMID": self.vm_id}
41+
42+
try:
43+
response = requests.get(url, headers=headers)
44+
response.raise_for_status()
45+
return response.json()
46+
except requests.exceptions.RequestException as e:
47+
status_code = e.response.status_code if e.response else None
48+
raise OneGateError(f"GET request to {url} failed: {e}", status_code)
49+
except ValueError as e:
50+
raise OneGateError(f"Failed to parse JSON response: {e}")
51+
52+
def scale(self, cardinality, role="worker"):
53+
"""
54+
Make a PUT request to OneGate API.
55+
"""
56+
url = f"{self.endpoint}/service/role/{role}"
57+
headers = {
58+
"X-ONEGATE-TOKEN": self.token,
59+
"X-ONEGATE-VMID": self.vm_id,
60+
"Content-Type": "application/json",
61+
}
62+
data = {"cardinality": cardinality}
63+
try:
64+
response = requests.put(url, headers=headers, json=data)
65+
response.raise_for_status()
66+
except requests.exceptions.RequestException as e:
67+
status_code = e.response.status_code if e.response else None
68+
raise OneGateError(f"PUT request to {url} failed: {e}", status_code)

0 commit comments

Comments
 (0)