diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000000000000000000000000000000000000..d891394b28682694379e1feecd8193dfc2d10db1 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,18 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Kopf Run: l2sm-operator", + "type": "node-terminal", + "request": "launch", + "command": "kopf run ${workspaceFolder}/src/operator/l2sm-operator.py", + "env": { + "CONTROLLER_IP": "10.152.183.3", + "DATABASE_IP": "10.152.183.132", + "MYSQL_USER": "l2sm", + "MYSQL_PASSWORD": "l2sm", + "MYSQL_DATABASE": "l2sm" + } + } + ] +} diff --git a/deployments/custom-installation/mysql/database-schema.yaml b/deployments/custom-installation/mysql/database-schema.yaml new file mode 100644 index 0000000000000000000000000000000000000000..9acea17574391b05f04fec2b65dfa2d082669893 --- /dev/null +++ b/deployments/custom-installation/mysql/database-schema.yaml @@ -0,0 +1,61 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: mysql-schema +data: + init.sql: | + + CREATE DATABASE IF NOT EXISTS l2sm; + USE l2sm; + + CREATE TABLE networks ( + id INT PRIMARY KEY AUTO_INCREMENT, + name VARCHAR(255) NOT NULL, + type ENUM('vlink', 'vnet', 'ext-vnet') NOT NULL, + UNIQUE KEY unique_network_name (name, type) + ); + + CREATE TABLE switches ( + id INT PRIMARY KEY AUTO_INCREMENT, + node_name VARCHAR(255) NOT NULL, + openflowId TEXT, + ip VARCHAR(15) + ); + + CREATE TABLE neds ( + id INT PRIMARY KEY AUTO_INCREMENT, + node_name VARCHAR(255) NOT NULL, + provider VARCHAR(255) NOT NULL, + openflowId TEXT, + ip VARCHAR(15) + ); + + CREATE TABLE interfaces ( + id INT PRIMARY KEY AUTO_INCREMENT, + name VARCHAR(255), + pod VARCHAR(255), + switch_id INT, + ned_id INT, + network_id INT, + FOREIGN KEY (switch_id) REFERENCES switches(id), + FOREIGN KEY (ned_id) REFERENCES neds(id), + FOREIGN KEY (network_id) REFERENCES networks(id) + ); + + -- Define the one-to-many relationship between switches and interfaces + ALTER TABLE interfaces + ADD CONSTRAINT fk_switch_interface + FOREIGN KEY (switch_id) + REFERENCES switches(id); + + -- Define the one-to-many relationship between neds and interfaces + ALTER TABLE interfaces + ADD CONSTRAINT fk_ned_interface + FOREIGN KEY (ned_id) + REFERENCES neds(id); + + -- Define the many-to-one relationship between networks and interfaces + ALTER TABLE interfaces + ADD CONSTRAINT fk_network_interface + FOREIGN KEY (network_id) + REFERENCES networks(id); diff --git a/deployments/custom-installation/mysql/mysql-development.yaml b/deployments/custom-installation/mysql/mysql-development.yaml new file mode 100644 index 0000000000000000000000000000000000000000..461bf3072c051d173a6d677e9a17380cf7af8898 --- /dev/null +++ b/deployments/custom-installation/mysql/mysql-development.yaml @@ -0,0 +1,58 @@ +apiVersion: v1 +kind: Pod +metadata: + name: mysql-development-pod + labels: + app: mysql +spec: + containers: + - name: mysql + image: mysql:5.7 + envFrom: + - secretRef: + name: mysql-secret + ports: + - containerPort: 3306 + name: mysql + volumeMounts: + - name: mysql-persistent-storage + mountPath: /var/lib/mysql + - name: initdb-volume + mountPath: /docker-entrypoint-initdb.d + volumes: + - name: mysql-persistent-storage + persistentVolumeClaim: + claimName: mysql-pv-claim + - name: initdb-volume + configMap: + name: mysql-schema + items: + - key: init.sql + path: init.sql + nodeName: l2sm1 +--- +apiVersion: v1 +kind: Service +metadata: + name: mysql-development-service +spec: + type: NodePort + ports: + - port: 3306 + targetPort: 3306 + nodePort: 30001 + protocol: TCP + selector: + app: mysql + +--- +apiVersion: v1 +kind: Secret +metadata: + name: mysql-secret +type: Opaque +data: + MYSQL_ROOT_PASSWORD: cGFzc3dvcmQ= # Base64 encoded "password" + MYSQL_USER: bDJzbQ== # Base64 encoded "l2sm" + MYSQL_PASSWORD: bDJzbQ== # Base64 encoded "l2sm" + MYSQL_DATABASE: bDJzbQ== diff --git a/src/operator/l2sm-operator.py b/src/operator/l2sm-operator.py index 782ed0c55cdac57791f1ade1c567eeec813c06ca..20e2b5dfede9e87e4a01435d9764df1ed53d515e 100644 --- a/src/operator/l2sm-operator.py +++ b/src/operator/l2sm-operator.py @@ -16,9 +16,17 @@ import re import sys from requests.auth import HTTPBasicAuth -databaseIP = "127.0.0.1" -baseControllerUrl = 'http://' + os.environ['CONTROLLER_IP'] + ':8181' + '/onos/v1' -def beginSessionController(baseControllerUrl,username,password): +database_ip = os.environ['DATABASE_IP'] +database_username = os.environ['MYSQL_USER'] +database_password = os.environ['MYSQL_PASSWORD'] +database_name = os.environ['MYSQL_DATABASE'] + +base_controller_url = 'http://' + os.environ['CONTROLLER_IP'] + ':8181' + '/onos/v1' + +print(base_controller_url) +print(database_ip) + +def begin_session_controller(base_controller_url,username,password): # Create a session with basic authentication auth = HTTPBasicAuth(username, password) @@ -27,7 +35,7 @@ def beginSessionController(baseControllerUrl,username,password): session.auth = auth #Check if connection is possible - response = session.get(baseControllerUrl + '/l2sm/networks/status') + response = session.get(base_controller_url + '/l2sm/networks/status') if response.status_code == 200: # Successful request print("Initialized session between operator and controller.") @@ -35,264 +43,354 @@ def beginSessionController(baseControllerUrl,username,password): else: print("Could not initialize session with l2sm-controller") sys.exit() - return None -session = beginSessionController(baseControllerUrl,"karaf","karaf") - -def getSwitchId(cur, node): - switchQuery = "SELECT * FROM switches WHERE node = '%s'" % (node) - cur.execute(switchQuery) - switchRecord = cur.fetchone() - - if switchRecord is not None: - switchId = switchRecord[0] - - if switchId is not None: - return switchId # If openflowId is already set, return it - - # If openflowId is not set, make a request to get the information from the API - response = session.get(baseControllerUrl + '/devices') - devices = response.json().get('devices', []) - - for device in devices: - if 'id' in device and 'annotations' in device and 'managementAddress' in device['annotations']: - if device['annotations']['managementAddress'] == switchRecord[1]: - openflowId = device['id'] - switchId = openflowId - - # Save the openflowId in the database - updateQuery = "UPDATE switches SET openflowId = '%s' WHERE node = '%s'" % (switchId, node) - cur.execute(updateQuery) - - return switchId # Return the openflowId +session = begin_session_controller(base_controller_url,"karaf","karaf") + +def get_openflow_id(node_name): + connection = pymysql.connect(host=database_ip, + user=database_username, + password=database_password, + database=database_name, + cursorclass=pymysql.cursors.DictCursor) + try: + with connection.cursor() as cursor: + switch_query = "SELECT id, openflowId, ip FROM switches WHERE node_name = %s" + cursor.execute(switch_query, (node_name,)) + switch_record = cursor.fetchone() + + print(switch_record) + if switch_record is not None: + switchId = switch_record['openflowId'] + + if switchId is not None: + return switchId # If openflowId is already set, return it + + # If openflowId is not set, make a request to get the information from the API + response = session.get(base_controller_url + '/devices') + devices = response.json().get('devices', []) + + for device in devices: + if 'id' in device and 'annotations' in device and 'managementAddress' in device['annotations']: + if device['annotations']['managementAddress'] == switch_record['ip']: + openflowId = device['id'] + switchId = openflowId + + # Save the openflowId in the database + updateQuery = "UPDATE switches SET openflowId = %s WHERE id = %s" + cursor.execute(updateQuery, (openflowId, switch_record['id'])) + connection.commit() + + return switchId # Return the openflowId + connection.commit() + finally: + connection.close() + return None # Return None if no matching device is found #POPULATE DATABASE ENTRIES WHEN A NEW L2SM POD IS CREATED (A NEW NODE APPEARS) @kopf.on.create('pods.v1', labels={'l2sm-component': 'l2sm-switch'}) def build_db(body, logger, annotations, **kwargs): - db = pymysql.connect(host=databaseIP,user="l2sm",password="l2sm;",db="L2SM") - cur = db.cursor() - - #CREATE TABLES IF THEY DO NOT EXIST - table1 = "CREATE TABLE IF NOT EXISTS networks (network TEXT NOT NULL, id TEXT NOT NULL);" - table2 = "CREATE TABLE IF NOT EXISTS interfaces (interface TEXT NOT NULL, node TEXT NOT NULL, network TEXT, pod TEXT);" - table3 = "CREATE TABLE IF NOT EXISTS switches (openflowId TEXT, ip TEXT, node TEXT NOT NULL);" - cur.execute(table1) - cur.execute(table2) - cur.execute(table3) - db.commit() - - #MODIFY THE END VALUE TO ADD MORE INTERFACES - values = [] - for i in range(1,11): - values.append(['veth'+str(i), body['spec']['nodeName'], '-1', '']) - sqlInterfaces = "INSERT INTO interfaces (interface, node, network, pod) VALUES (%s, %s, %s, %s)" - cur.executemany(sqlInterfaces, values) - db.commit() - - #ADD The switch identification to the database, without the of13 id yet, as it may not be connected yet. - sqlSwitch = "INSERT INTO switches (node) VALUES ('" + body['spec']['nodeName'] + "')" - cur.execute(sqlSwitch) - db.commit() - - db.close() - logger.info(f"Node {body['spec']['nodeName']} has been registered in the operator") + connection = pymysql.connect(host=database_ip, + user=database_username, + password=database_password, + database=database_name, + cursorclass=pymysql.cursors.DictCursor) + if 'spec' in body and 'nodeName' in body['spec']: + try: + with connection.cursor() as cursor: + # Step 1: Check if the switch already exists + select_switch_sql = "SELECT id FROM switches WHERE node_name = %s;" + cursor.execute(select_switch_sql, body['spec']['nodeName']) + result = cursor.fetchone() + + if result: + # Switch exists, so update it (if needed) + switch_id = result['id'] + # Example update (modify as needed) + # update_switch_sql = "UPDATE switches SET openflowId = %s, IP = %s WHERE id = %s;" + # cursor.execute(update_switch_sql, (newOpenflowId, newIP, switch_id)) + else: + # Step 2: Insert a switch since it doesn't exist + insert_switch_sql = "INSERT INTO switches (node_name, openflowId, IP) VALUES (%s, NULL, NULL);" + cursor.execute(insert_switch_sql, body['spec']['nodeName']) + switch_id = cursor.lastrowid + # Step 3: Insert interfaces + for i in range(1, 11): + interface_name = f"veth{i}" + # Consider adding a check here to see if the interface already exists for the switch + insert_interface_sql = "INSERT INTO interfaces (name, switch_id) VALUES (%s, %s);" + cursor.execute(insert_interface_sql, (interface_name, switch_id)) + + # Commit the changes + connection.commit() + finally: + connection.close() + logger.info(f"Node {body['spec']['nodeName']} has been registered in the operator") + else: + raise kopf.TemporaryError("The Pod is not yet ready", delay=5) @kopf.on.field('pods.v1', labels={'l2sm-component': 'l2sm-switch'}, field='status.podIP') def update_db(body, logger, annotations, **kwargs): if 'status' in body and 'podIP' in body['status']: - db = pymysql.connect(host=databaseIP,user="l2sm",password="l2sm;",db="L2SM") - cur = db.cursor() - updateQuery = "UPDATE switches SET ip = '%s', OpenFlowId = NULL WHERE node = '%s'" % (body['status']['podIP'], body['spec']['nodeName']) - cur.execute(updateQuery) - db.commit() - db.close() + connection = pymysql.connect(host=database_ip, + user=database_username, + password=database_password, + database=database_name, + cursorclass=pymysql.cursors.DictCursor) + try: + with connection.cursor() as cursor: + updateQuery = "UPDATE switches SET ip = '%s', OpenFlowId = NULL WHERE node_name = '%s'" % (body['status']['podIP'], body['spec']['nodeName']) + cursor.execute(updateQuery) + connection.commit() + finally: + connection.close() logger.info(f"Updated switch ip") -#UPDATE DATABASE WHEN NETWORK IS CREATED, I.E: IS A MULTUS CRD WITH OUR DUMMY INTERFACE PRESENT IN ITS CONFIG +#UPDATE DATABASE WHEN NETWORK IS CREATED, I.E: IS A MULTUS CRD WITH OUR L2SM INTERFACE PRESENT IN ITS CONFIG #@kopf.on.create('NetworkAttachmentDefinition', field="spec.config['device']", value='l2sm-vNet') -@kopf.on.create('NetworkAttachmentDefinition', when=lambda spec, **_: '"device": "l2sm-vNet"' in spec['config']) +@kopf.on.create('NetworkAttachmentDefinition', when=lambda spec, **_: '"type": "l2sm"' in spec['config']) def create_vn(spec, name, namespace, logger, **kwargs): - - db = pymysql.connect(host=databaseIP,user="l2sm",password="l2sm;",db="L2SM") - cur = db.cursor() - id = secrets.token_hex(32) - sql = "INSERT INTO networks (network, id) VALUES ('%s', '%s')" % (name.strip(), id.strip()) - cur.execute(sql) - db.commit() - db.close() - # Create the network in the controller, using a post request - data = { - "networkId": name.strip() - } - # json_payload = { - # "Content-Type": "application/json", - # "data": payload - # } - response = session.post(baseControllerUrl + '/l2sm/networks', json=data) - - # Check the response status - if response.status_code == 204: - logger.info(f"Network has been created") - #print("Response:", response.json()) - else: - # Handle errors - logger.info(f"Network could not be created, check controller status") + # Database connection setup + connection = pymysql.connect(host=database_ip, + user=database_username, + password=database_password, + database=database_name, + cursorclass=pymysql.cursors.DictCursor) + try: + # Start database transaction + with connection.cursor() as cursor: + sql = "INSERT INTO networks (name, type) VALUES (%s, %s) ON DUPLICATE KEY UPDATE name = VALUES(name), type = VALUES(type)" + cursor.execute(sql, (name.strip(), "vnet")) + + # Prepare data for the REST API call + data = {"networkId": name.strip()} + response = session.post(base_controller_url + '/l2sm/networks', json=data) + + # Check the response status + if response.status_code == 204: + # Commit database changes only if the network is successfully created in the controller + connection.commit() + logger.info(f"Network '{name}' has been successfully created in both the database and the L2SM controller.") + else: + # Roll back the database transaction if the network creation in the controller fails + connection.rollback() + logger.error(f"Failed to create network '{name}' in the L2SM controller. Database transaction rolled back.") + + except Exception as e: + # Roll back the database transaction in case of any error + connection.rollback() + logger.error(f"An error occurred: {e}. Rolled back the database transaction.") + finally: + # Ensure the database connection is closed + connection.close() #ASSIGN POD TO NETWORK (TRIGGERS ONLY IF ANNOTATION IS PRESENT) @kopf.on.create('pods.v1', annotations={'k8s.v1.cni.cncf.io/networks': kopf.PRESENT}) def pod_vn(body, name, namespace, logger, annotations, **kwargs): - #GET MULTUS INTERFACES IN THE DESCRIPTOR - #IN QUARANTINE: SLOWER THAN MULTUS!!!!! - time.sleep(random.uniform(0,0.8)) #Make sure the database is not consulted at the same time to avoid overlaping - - multusInt = annotations.get('k8s.v1.cni.cncf.io/networks').split(",") - #VERIFY IF NETWORK IS PRESENT IN THE CLUSTER - api = client.CustomObjectsApi() - items = api.list_namespaced_custom_object('k8s.cni.cncf.io', 'v1', namespace, 'network-attachment-definitions').get('items') - resources = [] - # NETWORK POSITION IN ANNOTATION - network = [] - - #FIND OUR NETWORKS IN MULTUS - for i in items: - if '"device": "l2sm-vNet"' in i['spec']['config']: - resources.append(i['metadata']['name']) - - for k in range(len(multusInt)): - multusInt[k] = multusInt[k].strip() - if multusInt[k] in resources: - network.append(k) - - #IF THERE ARE NO NETWORKS, LET MULTUS HANDLE THIS - if not network: - return - - #CHECK IF NODE HAS FREE VIRTUAL INTERFACES LEFT - v1 = client.CoreV1Api() - ret = v1.read_namespaced_pod(name, namespace) - node = body['spec']['nodeName'] - - db = pymysql.connect(host=databaseIP,user="l2sm",password="l2sm;",db="L2SM") - nsql = "SELECT * FROM interfaces WHERE node = '%s' AND network = '-1'" % (node.strip()) - cur = db.cursor() - cur.execute(nsql) - data = cur.fetchall() - if not data or len(data)<len(network): - db.close() - raise kopf.PermanentError("l2sm could not deploy the pod: Node " + node.strip() + "has no free interfaces left") - - #IF THERE IS ALREADY A MULTUS ANNOTATION, APPEND IT TO THE END. - interface_to_attach = [] - network_array = [] - j = 0 - for interface in data[0:len(network)]: - network_array.append(multusInt[network[j]]) - multusInt[network[j]] = interface[0].strip() - interface_to_attach.append(interface[0].strip()) - j = j + 1 - - ret.metadata.annotations['k8s.v1.cni.cncf.io/networks'] = ', '.join(multusInt) - - - - #GET NETWORK ID'S - #for j in items: - # if network in j['metadata']['name']: - # idsql = "SELECT id FROM networks WHERE network = '%s'" % (network.strip()) - # cur.execute(idsql) - # retrieve = cur.fetchone() - # networkN = retrieve[0].strip() - # break - - switchId = getSwitchId(cur, node) # TODO: diferenciar caso en el que es un veth el conectado y el de que es una red de vdd. - - if switchId is None: - logger.info(f"The l2sm switch is not connected to controller. Not connecting the pod") - return - vethPattern = re.compile(r'\d+$') - portNumbers = [int(vethPattern.search(interface).group()) for interface in interface_to_attach] - - for m in range(len(network)): - sql = "UPDATE interfaces SET network = '%s', pod = '%s' WHERE interface = '%s' AND node = '%s'" % (network_array[m], name, interface_to_attach[m], node) - cur.execute(sql) - - payload = { - "networkId": network_array[m], - "networkEndpoints": [switchId + '/' + str(portNumbers[m])] - } + """Assign Pod to a network if a specific annotation is present.""" - - - response = session.post(baseControllerUrl + '/l2sm/networks/port', json=payload) - - #PATCH NETWORK WITH ANNOTATION - v1.patch_namespaced_pod(name, namespace, ret) - db.commit() - db.close() - + # Avoid database overlap by introducing a random sleep time + time.sleep(random.uniform(0, 0.8)) + + multus_networks = extract_multus_networks(annotations) + if not multus_networks: + logger.info("No Multus networks specified. Exiting.") + return + + existing_networks = get_existing_networks(namespace) + target_networks = filter_target_networks(multus_networks, existing_networks) + if not target_networks: + logger.info("No target networks found. Letting Multus handle the network assignment.") + return + if 'spec' in body and 'nodeName' in body['spec']: + node_name = body['spec']['nodeName'] + + free_interfaces = get_free_interfaces(node_name) + if len(free_interfaces) < len(target_networks): + raise kopf.PermanentError(f"Node {node_name} has no free interfaces left") + + openflow_id = get_openflow_id(node_name) + + update_network_assignments(name, namespace, node_name, free_interfaces, target_networks, logger, openflow_id) + else: + raise kopf.TemporaryError("The Pod is not yet ready", delay=1) - - # Check the response status - if response.status_code == 200: - # Successful request - print("Request successful!") - else: - # Handle errors - print(f"Error: {response.status_code}") - logger.info(f"Pod {name} attached to network {network_array}") +def extract_multus_networks(annotations): + """Extract and return Multus networks from annotations.""" + return [network.strip() for network in annotations.get('k8s.v1.cni.cncf.io/networks').split(",")] + +def get_existing_networks(namespace): + """Return existing networks in the namespace.""" + api = client.CustomObjectsApi() + networks = api.list_namespaced_custom_object('k8s.cni.cncf.io', 'v1', namespace, 'network-attachment-definitions').get('items') + return [network['metadata']['name'] for network in networks if '"type": "l2sm"' in network['spec']['config']] + +def filter_target_networks(multus_networks, existing_networks): + """Filter and return networks that are both requested and exist.""" + return [network for network in multus_networks if network in existing_networks] + +def get_free_interfaces(node_name): + """Query the database for free interfaces on a node.""" + connection = pymysql.connect(host=database_ip, + user=database_username, + password=database_password, + database=database_name, + cursorclass=pymysql.cursors.DictCursor) + try: + with connection.cursor() as cursor: + sql = """ + SELECT i.id, i.name + FROM interfaces i + JOIN switches s ON i.switch_id = s.id + WHERE i.network_id IS NULL AND s.node_name = %s; + """ + cursor.execute(sql, (node_name.strip(),)) + free_interfaces = cursor.fetchall() + finally: + connection.close() + return free_interfaces + +def update_pod_annotation(pod_name, namespace, interfaces): + """Update the Pod's annotation with assigned interfaces.""" + v1 = client.CoreV1Api() + pod = v1.read_namespaced_pod(pod_name, namespace) + pod_annotations = pod.metadata.annotations or {} + pod_annotations['k8s.v1.cni.cncf.io/networks'] = ', '.join(interfaces) + v1.patch_namespaced_pod(pod_name, namespace, {'metadata': {'annotations': pod_annotations}}) + +def update_network_assignments(pod_name, namespace, node_name, free_interfaces, target_networks, logger, openflow_id): + """Update the network assignments in the database and controller.""" + connection = pymysql.connect(host=database_ip, + user=database_username, + password=database_password, + database=database_name, + cursorclass=pymysql.cursors.DictCursor) + try: + assigned_interfaces = [] + with connection.cursor() as cursor: + for i, interface in enumerate(free_interfaces[:len(target_networks)]): + update_interface_assignment(cursor, interface['id'], target_networks[i], pod_name, node_name) + assigned_interfaces.append(interface['name']) + + # Assuming function get_openflow_id and session.post logic are implemented elsewhere + if openflow_id: + port_number = extract_port_number(interface['name']) + post_network_assignment(openflow_id, port_number, target_networks[i]) + + update_pod_annotation(pod_name, namespace, assigned_interfaces) + + connection.commit() + finally: + connection.close() + logger.info(f"Pod {pod_name} attached to networks {', '.join(target_networks)}") + +# Assuming these functions are implemented as per original logic +def update_interface_assignment(cursor, interface_id, network_name, pod_name, node_name): + """Update a single interface's network assignment in the database.""" + # First, find the network_id from the network name + cursor.execute("SELECT id FROM networks WHERE name = %s", (network_name,)) + network = cursor.fetchone() + if network is None: + raise ValueError(f"Network {network_name} does not exist") + network_id = network['id'] + + # Update the interface with the network_id and pod name + sql = """ + UPDATE interfaces + SET pod = %s, network_id = %s + WHERE id = %s; + """ + cursor.execute(sql, (pod_name, network_id, interface_id)) + +def extract_port_number(interface_name): + """Extract and return the port number from an interface name.""" + return int(re.search(r'\d+$', interface_name).group()) + +def post_network_assignment(openflow_id, port_number, network_name): + """Post network assignment to the controller.""" + payload = { + "networkId": network_name, + "networkEndpoints": [f"{openflow_id}/{port_number}"] + } + response = session.post(f"{base_controller_url}/l2sm/networks/port", json=payload) + if response.status_code != 204: + raise Exception(f"Network assignment failed with status code: {response.status_code}") + #UPDATE DATABASE WHEN POD IS DELETED @kopf.on.delete('pods.v1', annotations={'k8s.v1.cni.cncf.io/networks': kopf.PRESENT}) def dpod_vn(name, logger, **kwargs): - db = pymysql.connect(host=databaseIP,user="l2sm",password="l2sm;",db="L2SM") - cur = db.cursor() - sql = "UPDATE interfaces SET network = '-1', pod = '' WHERE pod = '%s'" % (name) - cur.execute(sql) - db.commit() - db.close() - logger.info(f"Pod {name} removed") + connection = pymysql.connect(host=database_ip, + user=database_username, + password=database_password, + database=database_name, + cursorclass=pymysql.cursors.DictCursor) + try: + with connection.cursor() as cursor: + sql = "UPDATE interfaces SET network_id = NULL, pod = NULL WHERE pod = '%s'" % (name) + cursor.execute(sql) + connection.commit() + finally: + connection.close() + logger.info(f"Pod {name} removed") #UPDATE DATABASE WHEN NETWORK IS DELETED -@kopf.on.delete('NetworkAttachmentDefinition', when=lambda spec, **_: '"device": "l2sm-vNet"' in spec['config']) +@kopf.on.delete('NetworkAttachmentDefinition', when=lambda spec, **_: '"type": "l2sm"' in spec['config']) def delete_vn(spec, name, logger, **kwargs): - db = pymysql.connect(host=databaseIP,user="l2sm",password="l2sm;",db="L2SM") - cur = db.cursor() - sql = "DELETE FROM networks WHERE network = '%s'" % (name) - cur.execute(sql) - - - response = session.delete(baseControllerUrl + '/l2sm/networks/' + name) + connection = pymysql.connect(host=database_ip, + user=database_username, + password=database_password, + database=database_name, + cursorclass=pymysql.cursors.DictCursor) + try: + with connection.cursor() as cursor: + sql = "DELETE FROM networks WHERE name = '%s' AND type = 'vnet'" % (name) + cursor.execute(sql) + + - if response.status_code == 204: - # Successful request - logger.info(f"Network has been deleted") - db.commit() - else: - # Handle errors - logger.info(f"Error: {response.status_code}") - db.close() + response = session.delete(base_controller_url + '/l2sm/networks/' + name) + + if response.status_code == 204: + # Successful request + logger.info(f"Network has been deleted") + connection.commit() + else: + # Handle errors + logger.info(f"Error: {response.status_code}") + finally: + connection.close() + logger.info(f"Pod {name} removed") #DELETE DATABASE ENTRIES WHEN A NEW L2SM SWITCH IS DELETED (A NEW NODE GETS OUT OF THE CLUSTER) @kopf.on.delete('pods.v1', labels={'l2sm-component': 'l2sm-switch'}) def remove_node(body, logger, annotations, **kwargs): - db = pymysql.connect(host=databaseIP,user="l2sm",password="l2sm;",db="L2SM") - cur = db.cursor() - sql = "DELETE FROM interfaces WHERE node = '%s'" % (body['spec']['nodeName']) - switchSql = "DELETE FROM switches WHERE node = '%s'" % (body['spec']['nodeName']) - cur.execute(sql) - cur.execute(switchSql) - db.commit() - db.close() + connection = pymysql.connect(host=database_ip, + user=database_username, + password=database_password, + database=database_name, + cursorclass=pymysql.cursors.DictCursor) + try: + with connection.cursor() as cursor: + sql = """ + DELETE FROM interfaces + WHERE switch_id = (SELECT id FROM switches WHERE node_name = '%s'); + """ + switchSql = "DELETE FROM switches WHERE node_name = '%s';" + cursor.execute(sql,body['spec']['nodeName']) + cursor.execute(switchSql,body['spec']['nodeName']) + connection.commit() + finally: + connection.close() logger.info(f"Node {body['spec']['nodeName']} has been deleted from the cluster")