Skip to content
Snippets Groups Projects
Commit 6e599f13 authored by Alex ubuntu vm's avatar Alex ubuntu vm
Browse files

operator: bug fix

fixed bug where multus was called before the operator, not allowing the pod to receive an ip address assigned
parent 0dce7607
No related branches found
No related tags found
1 merge request!2repo: added new directory where utils scripts will be
apiVersion: l2sm.k8s.local/v1 apiVersion: l2sm.l2sm.k8s.local/v1
kind: L2SMNetwork kind: L2SMNetwork
metadata: metadata:
name: ping-network name: ping-network
......
...@@ -5,7 +5,8 @@ metadata: ...@@ -5,7 +5,8 @@ metadata:
labels: labels:
app: ping-pong app: ping-pong
annotations: annotations:
l2sm/networks: ping-network l2sm/networks: '[{"name": "ping-network", "ips":["192.168.1.6/24"]}]'
spec: spec:
containers: containers:
- name: router - name: router
......
...@@ -5,7 +5,7 @@ metadata: ...@@ -5,7 +5,7 @@ metadata:
labels: labels:
app: ping-pong app: ping-pong
annotations: annotations:
k8s.v1.cni.cncf.io/networks: ping-network l2sm/networks: ping-network
spec: spec:
containers: containers:
- name: router - name: router
......
...@@ -152,7 +152,7 @@ def update_db(body, logger, annotations, **kwargs): ...@@ -152,7 +152,7 @@ def update_db(body, logger, annotations, **kwargs):
#UPDATE DATABASE WHEN NETWORK IS CREATED, I.E: IS A MULTUS CRD WITH OUR L2SM 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', field="spec.config['device']", value='l2sm-vNet')
@kopf.on.create('l2sm.k8s.local', 'v1', 'l2sm-networks') @kopf.on.create('l2sm.l2sm.k8s.local', 'v1', 'l2smnetworks')
def create_vn(spec, name, namespace, logger, **kwargs): def create_vn(spec, name, namespace, logger, **kwargs):
# Database connection setup # Database connection setup
...@@ -183,7 +183,6 @@ def pod_vn(body, name, namespace, logger, annotations, **kwargs): ...@@ -183,7 +183,6 @@ def pod_vn(body, name, namespace, logger, annotations, **kwargs):
"""Assign Pod to a network if a specific annotation is present.""" """Assign Pod to a network if a specific annotation is present."""
# Avoid database overlap by introducing a random sleep time # Avoid database overlap by introducing a random sleep time
time.sleep(random.uniform(0, 0.8))
multus_networks = extract_multus_networks(annotations) multus_networks = extract_multus_networks(annotations)
if not multus_networks: if not multus_networks:
...@@ -219,10 +218,10 @@ def pod_vn(body, name, namespace, logger, annotations, **kwargs): ...@@ -219,10 +218,10 @@ def pod_vn(body, name, namespace, logger, annotations, **kwargs):
def update_network(l2sm_network_name,pod_name,namespace,api): def update_network(l2sm_network_name,pod_name,namespace,api):
l2sm_network = api.get_namespaced_custom_object( l2sm_network = api.get_namespaced_custom_object(
group="l2sm.k8s.local", group="l2sm.l2sm.k8s.local",
version="v1", version="v1",
namespace=namespace, namespace=namespace,
plural="l2sm-networks", plural="l2smnetworks",
name=l2sm_network_name, name=l2sm_network_name,
) )
connected_pods = l2sm_network.get('status', {}).get('connectedPods', []) connected_pods = l2sm_network.get('status', {}).get('connectedPods', [])
...@@ -232,10 +231,10 @@ def update_network(l2sm_network_name,pod_name,namespace,api): ...@@ -232,10 +231,10 @@ def update_network(l2sm_network_name,pod_name,namespace,api):
# Apply the patch to the L2SMNetwork # Apply the patch to the L2SMNetwork
api.patch_namespaced_custom_object( api.patch_namespaced_custom_object(
group="l2sm.k8s.local", group="l2sm.l2sm.k8s.local",
version="v1", version="v1",
namespace=namespace, namespace=namespace,
plural="l2sm-networks", plural="l2smnetworks",
name=l2sm_network_name, name=l2sm_network_name,
body=patch body=patch
) )
...@@ -243,10 +242,10 @@ def update_network(l2sm_network_name,pod_name,namespace,api): ...@@ -243,10 +242,10 @@ def update_network(l2sm_network_name,pod_name,namespace,api):
def remove_from_network(l2sm_network_name,pod_name,namespace,api): def remove_from_network(l2sm_network_name,pod_name,namespace,api):
l2sm_network = api.get_namespaced_custom_object( l2sm_network = api.get_namespaced_custom_object(
group="l2sm.k8s.local", group="l2sm.l2sm.k8s.local",
version="v1", version="v1",
namespace=namespace, namespace=namespace,
plural="l2sm-networks", plural="l2smnetworks",
name=l2sm_network_name, name=l2sm_network_name,
) )
connected_pods = l2sm_network.get('status', {}).get('connectedPods', []) connected_pods = l2sm_network.get('status', {}).get('connectedPods', [])
...@@ -256,10 +255,10 @@ def remove_from_network(l2sm_network_name,pod_name,namespace,api): ...@@ -256,10 +255,10 @@ def remove_from_network(l2sm_network_name,pod_name,namespace,api):
# Apply the patch to the L2SMNetwork # Apply the patch to the L2SMNetwork
api.patch_namespaced_custom_object( api.patch_namespaced_custom_object(
group="l2sm.k8s.local", group="l2sm.l2sm.k8s.local",
version="v1", version="v1",
namespace=namespace, namespace=namespace,
plural="l2sm-networks", plural="l2smnetworks",
name=l2sm_network_name, name=l2sm_network_name,
body=patch body=patch
) )
...@@ -284,7 +283,7 @@ def extract_multus_networks(annotations): ...@@ -284,7 +283,7 @@ def extract_multus_networks(annotations):
def get_existing_networks(namespace): def get_existing_networks(namespace):
"""Return existing networks in the namespace.""" """Return existing networks in the namespace."""
api = client.CustomObjectsApi() api = client.CustomObjectsApi()
networks = api.list_namespaced_custom_object('l2sm.k8s.local', 'v1', namespace, 'l2sm-networks').get('items') networks = api.list_namespaced_custom_object('l2sm.l2sm.k8s.local', 'v1', namespace, 'l2smnetworks').get('items')
return [network['metadata']['name'] for network in networks if "vnet" in network['spec']['type']] return [network['metadata']['name'] for network in networks if "vnet" in network['spec']['type']]
def filter_target_networks(multus_networks, existing_networks): def filter_target_networks(multus_networks, existing_networks):
...@@ -317,19 +316,19 @@ def update_pod_annotation(pod_name, namespace, networks_info): ...@@ -317,19 +316,19 @@ def update_pod_annotation(pod_name, namespace, networks_info):
v1 = client.CoreV1Api() v1 = client.CoreV1Api()
pod = v1.read_namespaced_pod(pod_name, namespace) pod = v1.read_namespaced_pod(pod_name, namespace)
pod_annotations = pod.metadata.annotations or {} pod_annotations = pod.metadata.annotations or {}
print("pod")
print(pod_name)
print("networks")
print(networks_info)
# Format the annotations based on whether IPs are provided # Format the annotations based on whether IPs are provided
formatted_networks = [] formatted_networks = []
for network_info in networks_info: for network_info in networks_info:
if network_info['ips']: # Case B with IP addresses if not network_info.get('ips'):
formatted_networks.append(json.dumps({ network_info['ips'] = [generate_random_ipv6_fe80()] # Ensure this is a list
"name": network_info['name'], formatted_networks.append(json.dumps(network_info)) # Convert dict to a JSON string
"ips": network_info['ips']
}))
else: # Case A without specific IP addresses
formatted_networks.append(network_info['name'])
pod_annotations['k8s.v1.cni.cncf.io/networks'] = '[' + ', '.join(formatted_networks) + ']' pod_annotations['k8s.v1.cni.cncf.io/networks'] = '[' + ', '.join(formatted_networks) + ']'
print(pod_annotations)
v1.patch_namespaced_pod(pod_name, namespace, {'metadata': {'annotations': pod_annotations}}) v1.patch_namespaced_pod(pod_name, namespace, {'metadata': {'annotations': pod_annotations}})
...@@ -344,20 +343,19 @@ def update_network_assignments(pod_name, namespace, node_name, free_interfaces, ...@@ -344,20 +343,19 @@ def update_network_assignments(pod_name, namespace, node_name, free_interfaces,
assigned_interfaces = [] assigned_interfaces = []
with connection.cursor() as cursor: with connection.cursor() as cursor:
for i, interface in enumerate(free_interfaces[:len(target_networks)]): for i, interface in enumerate(free_interfaces[:len(target_networks)]):
update_interface_assignment(cursor, interface['id'], target_networks[i], pod_name, node_name) update_interface_assignment(cursor, interface['id'], target_networks[i]['name'], pod_name, node_name)
assigned_interfaces.append(interface['name']) assigned_interfaces.append({"name":interface['name'], "ips": target_networks[i]['ips']})
# Assuming function get_openflow_id and session.post logic are implemented elsewhere # Assuming function get_openflow_id and session.post logic are implemented elsewhere
if openflow_id: if openflow_id:
port_number = extract_port_number(interface['name']) port_number = extract_port_number(interface['name'])
post_network_assignment(openflow_id, port_number, target_networks[i]) post_network_assignment(openflow_id, port_number, target_networks[i]['name'])
update_pod_annotation(pod_name, namespace, assigned_interfaces) update_pod_annotation(pod_name, namespace, assigned_interfaces)
connection.commit() connection.commit()
finally: finally:
connection.close() connection.close()
logger.info(f"Pod {pod_name} attached to networks {', '.join(target_networks)}") logger.info(f"Pod {pod_name} attached to networks {', '.join(network['name'] for network in target_networks)}")
# Assuming these functions are implemented as per original logic # Assuming these functions are implemented as per original logic
def update_interface_assignment(cursor, interface_id, network_name, pod_name, node_name): def update_interface_assignment(cursor, interface_id, network_name, pod_name, node_name):
...@@ -395,7 +393,7 @@ def post_network_assignment(openflow_id, port_number, network_name): ...@@ -395,7 +393,7 @@ def post_network_assignment(openflow_id, port_number, network_name):
#UPDATE DATABASE WHEN POD IS DELETED #UPDATE DATABASE WHEN POD IS DELETED
@kopf.on.delete('pods.v1', annotations={'l2sm/networks': kopf.PRESENT}) @kopf.on.delete('pods.v1', annotations={'l2sm/networks': kopf.PRESENT})
def dpod_vn(name, logger, **kwargs): def dpod_vn(body, name, namespace, logger, annotations, **kwargs):
connection = pymysql.connect(host=database_ip, connection = pymysql.connect(host=database_ip,
user=database_username, user=database_username,
password=database_password, password=database_password,
...@@ -407,13 +405,31 @@ def dpod_vn(name, logger, **kwargs): ...@@ -407,13 +405,31 @@ def dpod_vn(name, logger, **kwargs):
cursor.execute(sql) cursor.execute(sql)
connection.commit() connection.commit()
update_network() multus_networks = extract_multus_networks(annotations)
if not multus_networks:
logger.info("No Multus networks specified. Exiting.")
return
existing_networks = get_existing_networks(namespace)
# Need to extract just the names from multus_networks for comparison
target_networks_info = filter_target_networks([net['name'] for net in multus_networks], existing_networks)
if not target_networks_info:
logger.info("No target networks found. Letting Multus handle the network assignment.")
return
# Update `target_networks` to include IP information if available
target_networks = [net for net in multus_networks if net['name'] in target_networks_info]
api = CustomObjectsApi()
# Assign pods to each of the target networks
for network in target_networks:
update_network(network['name'], name, namespace, api)
finally: finally:
connection.close() connection.close()
logger.info(f"Pod {name} removed") logger.info(f"Pod {name} removed")
#UPDATE DATABASE WHEN NETWORK IS DELETED #UPDATE DATABASE WHEN NETWORK IS DELETED
@kopf.on.delete('l2sm.k8s.local', 'v1', 'l2sm-networks') @kopf.on.delete('l2sm.l2sm.k8s.local', 'v1', 'l2smnetworks')
def delete_vn(spec, name, logger, **kwargs): def delete_vn(spec, name, logger, **kwargs):
connection = pymysql.connect(host=database_ip, connection = pymysql.connect(host=database_ip,
user=database_username, user=database_username,
...@@ -426,12 +442,12 @@ def delete_vn(spec, name, logger, **kwargs): ...@@ -426,12 +442,12 @@ def delete_vn(spec, name, logger, **kwargs):
update_interfaces_sql = """ update_interfaces_sql = """
UPDATE interfaces UPDATE interfaces
SET network_id = NULL SET network_id = NULL
WHERE network_id = (SELECT id FROM networks WHERE name = %s AND type = '%s'); WHERE network_id = (SELECT id FROM networks WHERE name = %s AND type = %s);
""" """
cursor.execute(update_interfaces_sql, (name,spec['type'])) cursor.execute(update_interfaces_sql, (name,spec['type']))
# Then, delete the network from networks table # Then, delete the network from networks table
delete_network_sql = "DELETE FROM networks WHERE name = %s AND type = '%s';" delete_network_sql = "DELETE FROM networks WHERE name = %s AND type = %s;"
cursor.execute(delete_network_sql, (name,spec['type'])) cursor.execute(delete_network_sql, (name,spec['type']))
...@@ -464,3 +480,21 @@ def remove_node(body, logger, annotations, **kwargs): ...@@ -464,3 +480,21 @@ def remove_node(body, logger, annotations, **kwargs):
connection.close() connection.close()
logger.info(f"Node {body['spec']['nodeName']} has been deleted from the cluster") logger.info(f"Node {body['spec']['nodeName']} has been deleted from the cluster")
def generate_random_ipv6_fe80():
# IPv6 FE80::/64 starts with '1111 1110 10' and 54 bits of 0s
# So we can fix the first 10 bits as '1111 1110 10'
# Then we generate the last 64 bits randomly for the interface ID
# Since IPv6 addresses are represented in hexadecimal, we convert the binary values to hexadecimal
# Generating the interface ID (64 bits)
interface_id = random.getrandbits(64)
# Formatting to a 16 character hexadecimal string
interface_id_hex = format(interface_id, '016x')
# Constructing the full IPv6 address in the fe80::/64 range
ipv6_address = f"fe80::{interface_id_hex[:4]}:{interface_id_hex[4:8]}:{interface_id_hex[8:12]}:{interface_id_hex[12:]}/64"
return ipv6_address
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment