diff --git a/deployments/custom-installation/deploySwitch.yaml b/deployments/custom-installation/deploySwitch.yaml index e509e0818cd94ee2e85d956c5625a5c2bb38bd6b..c93a73bac71f5759e30f2e8a5d4d42e44b2f005b 100644 --- a/deployments/custom-installation/deploySwitch.yaml +++ b/deployments/custom-installation/deploySwitch.yaml @@ -14,7 +14,7 @@ spec: labels: l2sm-component: l2sm-switch annotations: - k8s.v1.cni.cncf.io/networks: veth1, veth2, veth3, veth4, veth5, veth6, veth7, veth8, veth9, veth10 + k8s.v1.cni.cncf.io/networks: '[{ "name": "veth1", "ips": ["fe80::58d0:b8ff:fe42:debf/64"]}, { "name": "veth2", "ips": ["fe80::58d0:b8ff:fe42:debe/64"]}, { "name": "veth3", "ips": ["fe80::58d0:b8ff:fe42:debd/64"]}, { "name": "veth4", "ips": ["fe80::58d0:b8ff:fe42:debc/64"]}, { "name": "veth5", "ips": ["fe80::58d0:b8ff:fe42:debb/64"]}, { "name": "veth6", "ips": ["fe80::58d0:b8ff:fe42:deba/64"]}, { "name": "veth7", "ips": ["fe80::58d0:b8ff:fe42:deb9/64"]}, { "name": "veth8", "ips": ["fe80::58d0:b8ff:fe42:deb8/64"]}, { "name": "veth9", "ips": ["fe80::58d0:b8ff:fe42:deb7/64"]}, { "name": "veth10", "ips": ["fe80::58d0:b8ff:fe42:deb6/64"]}]' spec: tolerations: # this toleration is to have the daemonset runnable on master nodes @@ -22,18 +22,18 @@ spec: - key: node-role.kubernetes.io/master operator: Exists effect: NoSchedule - initContainers: - - name: wait-for-l2sm-operator - image: curlimages/curl - args: - - /bin/sh - - -c - - > - set -x; - while [ $(curl -sw '%{http_code}' "http://l2sm-operator-service:8080/healthz" -o /dev/null) -ne 200 ]; do - sleep 15; - done; - sleep 5; + # initContainers: + # - name: wait-for-l2sm-operator + # image: curlimages/curl + # args: + # - /bin/sh + # - -c + # - > + # set -x; + # while [ $(curl -sw '%{http_code}' "http://l2sm-operator-service:8080/healthz" -o /dev/null) -ne 200 ]; do + # sleep 15; + # done; + # sleep 5; containers: - name: l2sm-switch image: alexdecb/l2sm-switch:2.2 diff --git a/deployments/custom-installation/interfaces_definitions/test/poda.yaml b/deployments/custom-installation/interfaces_definitions/test/poda.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c22dd4d8fe5a133232f428de285183c865bcf609 --- /dev/null +++ b/deployments/custom-installation/interfaces_definitions/test/poda.yaml @@ -0,0 +1,21 @@ +apiVersion: v1 +kind: Pod +metadata: + name: poda + labels: + app: ping-pong + annotations: + k8s.v1.cni.cncf.io/networks: '[ + { "name": "ptp1", + "ips": ["192.168.1.6/16"] + }]' +spec: + containers: + - name: router + command: ["/bin/ash", "-c", "trap : TERM INT; sleep infinity & wait"] + image: alpine:latest + securityContext: + capabilities: + add: ["NET_ADMIN"] + # Use this parameter if you want to place the pod in a specific node + #nodeName: workerk8s diff --git a/deployments/custom-installation/interfaces_definitions/test/podb.yaml b/deployments/custom-installation/interfaces_definitions/test/podb.yaml new file mode 100644 index 0000000000000000000000000000000000000000..e5c9084816ab5f079952fe51f8cc301102f35e3e --- /dev/null +++ b/deployments/custom-installation/interfaces_definitions/test/podb.yaml @@ -0,0 +1,21 @@ +apiVersion: v1 +kind: Pod +metadata: + name: podb + labels: + app: ping-pong + annotations: + k8s.v1.cni.cncf.io/networks: '[ + { "name": "ptp1", + "ips": ["fe80::58d0:b8ff:fe42:debf/64"] + }]' +spec: + containers: + - name: router + command: ["/bin/ash", "-c", "trap : TERM INT; sleep infinity & wait"] + image: alpine:latest + securityContext: + capabilities: + add: ["NET_ADMIN"] + # Use this parameter if you want to place the pod in a specific node + #nodeName: workerk8s diff --git a/deployments/custom-installation/interfaces_definitions/test/ptp1.yaml b/deployments/custom-installation/interfaces_definitions/test/ptp1.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c2b261ac27dbd16ee3462f17b2ea90ec44e0c54e --- /dev/null +++ b/deployments/custom-installation/interfaces_definitions/test/ptp1.yaml @@ -0,0 +1,15 @@ +apiVersion: "k8s.cni.cncf.io/v1" +kind: NetworkAttachmentDefinition +metadata: + name: veth1 +spec: + config: '{ + "cniVersion": "0.3.0", + "type": "bridge", + "name":"veth1", + "mtu": 1400, + "device":"veth1", + "ipam": { + "type":"static" + } + }' \ No newline at end of file diff --git a/deployments/custom-installation/interfaces_definitions/vhost1.yaml b/deployments/custom-installation/interfaces_definitions/vhost1.yaml index 5eed80277d2e17060a7eca49df5c16c5a65f10ea..20e0002aa2114f0207d35f7ef8d20257788d34ab 100644 --- a/deployments/custom-installation/interfaces_definitions/vhost1.yaml +++ b/deployments/custom-installation/interfaces_definitions/vhost1.yaml @@ -9,5 +9,8 @@ spec: "type": "bridge", "bridge": "br1", "mtu": 1400, - "device": "veth1" + "device": "veth1", + "ipam": { + "type":"static" + } }' diff --git a/deployments/custom-installation/interfaces_definitions/vhost10.yaml b/deployments/custom-installation/interfaces_definitions/vhost10.yaml index db850d1e3521c9def537de543f938859863faddb..86e6e192a0654a0fcd4ffbba96e6f5b74667336e 100644 --- a/deployments/custom-installation/interfaces_definitions/vhost10.yaml +++ b/deployments/custom-installation/interfaces_definitions/vhost10.yaml @@ -9,5 +9,8 @@ spec: "type": "bridge", "bridge": "br10", "mtu": 1400, - "device": "veth10" + "device": "veth10", + "ipam": { + "type":"static" + } }' diff --git a/deployments/custom-installation/interfaces_definitions/vhost2.yaml b/deployments/custom-installation/interfaces_definitions/vhost2.yaml index ebee425eab8d2688deb262d400a7c0a8a7534ec4..d16f057e4ed5f3f9e3161225a11dec27acd1b40a 100644 --- a/deployments/custom-installation/interfaces_definitions/vhost2.yaml +++ b/deployments/custom-installation/interfaces_definitions/vhost2.yaml @@ -9,5 +9,8 @@ spec: "type": "bridge", "bridge": "br2", "mtu": 1400, - "device": "veth2" + "device": "veth2", + "ipam": { + "type":"static" + } }' diff --git a/deployments/custom-installation/interfaces_definitions/vhost3.yaml b/deployments/custom-installation/interfaces_definitions/vhost3.yaml index 984a57469a4e688885fe059edd4c8e4e7f170260..c91fad34578616dae147f95d9aada012814b0174 100644 --- a/deployments/custom-installation/interfaces_definitions/vhost3.yaml +++ b/deployments/custom-installation/interfaces_definitions/vhost3.yaml @@ -9,5 +9,8 @@ spec: "type": "bridge", "bridge": "br3", "mtu": 1400, - "device": "veth3" + "device": "veth3", + "ipam": { + "type":"static" + } }' diff --git a/deployments/custom-installation/interfaces_definitions/vhost4.yaml b/deployments/custom-installation/interfaces_definitions/vhost4.yaml index be25ebf2cfabc4eeaa689e05c117997ff883de6f..f42ba14a504268952c58c3c8d0f08d33d700035d 100644 --- a/deployments/custom-installation/interfaces_definitions/vhost4.yaml +++ b/deployments/custom-installation/interfaces_definitions/vhost4.yaml @@ -9,5 +9,8 @@ spec: "type": "bridge", "bridge": "br4", "mtu": 1400, - "device": "veth4" + "device": "veth4", + "ipam": { + "type":"static" + } }' diff --git a/deployments/custom-installation/interfaces_definitions/vhost5.yaml b/deployments/custom-installation/interfaces_definitions/vhost5.yaml index 9742122420bcf635a91e478ccb578b2814bdd3e6..a1cfdd38b490b6d9f5539a480f1ccf7278c1decd 100644 --- a/deployments/custom-installation/interfaces_definitions/vhost5.yaml +++ b/deployments/custom-installation/interfaces_definitions/vhost5.yaml @@ -9,5 +9,8 @@ spec: "type": "bridge", "bridge": "br5", "mtu": 1400, - "device": "veth5" + "device": "veth5", + "ipam": { + "type":"static" + } }' diff --git a/deployments/custom-installation/interfaces_definitions/vhost6.yaml b/deployments/custom-installation/interfaces_definitions/vhost6.yaml index 86012451b57ea4510865b89233c1ae49c2148c82..a000d585786afaf1ea9abe23db754f1f42dd354a 100644 --- a/deployments/custom-installation/interfaces_definitions/vhost6.yaml +++ b/deployments/custom-installation/interfaces_definitions/vhost6.yaml @@ -9,5 +9,8 @@ spec: "type": "bridge", "bridge": "br6", "mtu": 1400, - "device": "veth6" + "device": "veth6", + "ipam": { + "type":"static" + } }' diff --git a/deployments/custom-installation/interfaces_definitions/vhost7.yaml b/deployments/custom-installation/interfaces_definitions/vhost7.yaml index c6cdd78a69e830449a07e477b434f7af4ce75bb6..dfa6b2e234b6c6a4af28491e79f37f7a7c8873ab 100644 --- a/deployments/custom-installation/interfaces_definitions/vhost7.yaml +++ b/deployments/custom-installation/interfaces_definitions/vhost7.yaml @@ -9,5 +9,8 @@ spec: "type": "bridge", "bridge": "br7", "mtu": 1400, - "device": "veth7" + "device": "veth7", + "ipam": { + "type":"static" + } }' diff --git a/deployments/custom-installation/interfaces_definitions/vhost8.yaml b/deployments/custom-installation/interfaces_definitions/vhost8.yaml index e3b478dd85bef2539de7e684143768e5e49a6ecb..f3fbafc964f88a80dcf5e10b6efacc54f9d06a42 100644 --- a/deployments/custom-installation/interfaces_definitions/vhost8.yaml +++ b/deployments/custom-installation/interfaces_definitions/vhost8.yaml @@ -9,5 +9,8 @@ spec: "type": "bridge", "bridge": "br8", "mtu": 1400, - "device": "veth8" + "device": "veth8", + "ipam": { + "type":"static" + } }' diff --git a/deployments/custom-installation/interfaces_definitions/vhost9.yaml b/deployments/custom-installation/interfaces_definitions/vhost9.yaml index 5b68835638e705195e1f528cc00fe8e3bcdce8cf..cd1502ec99ac3244bf9e213a07b340166958da70 100644 --- a/deployments/custom-installation/interfaces_definitions/vhost9.yaml +++ b/deployments/custom-installation/interfaces_definitions/vhost9.yaml @@ -9,5 +9,8 @@ spec: "type": "bridge", "bridge": "br9", "mtu": 1400, - "device": "veth9" + "device": "veth9", + "ipam": { + "type":"static" + } }' diff --git a/deployments/custom-installation/l2sm-network-crd.yaml b/deployments/inter-cluster/l2sm-network-crd.yaml similarity index 100% rename from deployments/custom-installation/l2sm-network-crd.yaml rename to deployments/inter-cluster/l2sm-network-crd.yaml diff --git a/deployments/l2sm-deployment.yaml b/deployments/l2sm-deployment.yaml index 16f26552dde13b88ba1d64fa3a604b49079167ee..5a9c1f10907716857109f4e6eb8b81cb724e18e8 100644 --- a/deployments/l2sm-deployment.yaml +++ b/deployments/l2sm-deployment.yaml @@ -9,7 +9,10 @@ spec: "type": "bridge", "bridge": "br1", "mtu": 1400, - "device": "veth1" + "device": "veth1", + "ipam": { + "type":"static" + } }' --- apiVersion: "k8s.cni.cncf.io/v1" @@ -22,7 +25,10 @@ spec: "type": "bridge", "bridge": "br2", "mtu": 1400, - "device": "veth2" + "device": "veth2", + "ipam": { + "type":"static" + } }' --- apiVersion: "k8s.cni.cncf.io/v1" @@ -35,7 +41,10 @@ spec: "type": "bridge", "bridge": "br3", "mtu": 1400, - "device": "veth3" + "device": "veth3", + "ipam": { + "type":"static" + } }' --- apiVersion: "k8s.cni.cncf.io/v1" @@ -48,7 +57,10 @@ spec: "type": "bridge", "bridge": "br4", "mtu": 1400, - "device": "veth4" + "device": "veth4", + "ipam": { + "type":"static" + } }' --- apiVersion: "k8s.cni.cncf.io/v1" @@ -61,7 +73,10 @@ spec: "type": "bridge", "bridge": "br5", "mtu": 1400, - "device": "veth5" + "device": "veth5", + "ipam": { + "type":"static" + } }' --- apiVersion: "k8s.cni.cncf.io/v1" @@ -74,7 +89,10 @@ spec: "type": "bridge", "bridge": "br6", "mtu": 1400, - "device": "veth6" + "device": "veth6", + "ipam": { + "type":"static" + } }' --- @@ -88,7 +106,10 @@ spec: "type": "bridge", "bridge": "br7", "mtu": 1400, - "device": "veth7" + "device": "veth7", + "ipam": { + "type":"static" + } }' --- apiVersion: "k8s.cni.cncf.io/v1" @@ -101,7 +122,10 @@ spec: "type": "bridge", "bridge": "br8", "mtu": 1400, - "device": "veth8" + "device": "veth8", + "ipam": { + "type":"static" + } }' --- apiVersion: "k8s.cni.cncf.io/v1" @@ -114,7 +138,10 @@ spec: "type": "bridge", "bridge": "br9", "mtu": 1400, - "device": "veth9" + "device": "veth9", + "ipam": { + "type":"static" + } }' --- apiVersion: "k8s.cni.cncf.io/v1" @@ -127,7 +154,10 @@ spec: "type": "bridge", "bridge": "br10", "mtu": 1400, - "device": "veth10" + "device": "veth10", + "ipam": { + "type":"static" + } }' --- apiVersion: v1 diff --git a/examples/ping-pong/network.yaml b/examples/ping-pong/network.yaml index b96a334588db4b08ceb9ecf3c1a317e59922ebb7..900bab719ca0cfc2a8732305fc33f87b48be107c 100644 --- a/examples/ping-pong/network.yaml +++ b/examples/ping-pong/network.yaml @@ -1,4 +1,4 @@ -apiVersion: l2sm.k8s.local/v1 +apiVersion: l2sm.l2sm.k8s.local/v1 kind: L2SMNetwork metadata: name: ping-network diff --git a/examples/ping-pong/ping.yaml b/examples/ping-pong/ping.yaml index 665ec1d0b85a38bc3db4b3233889d4a48d6284ea..fda551568728a3d08dfd3777b958d53f0c32f640 100644 --- a/examples/ping-pong/ping.yaml +++ b/examples/ping-pong/ping.yaml @@ -5,7 +5,8 @@ metadata: labels: app: ping-pong annotations: - l2sm/networks: ping-network + l2sm/networks: '[{"name": "ping-network", "ips":["192.168.1.6/24"]}]' + spec: containers: - name: router diff --git a/examples/ping-pong/pong.yaml b/examples/ping-pong/pong.yaml index e9fa4abe7ca6f279f04c7bda03b4728149f73dff..fb20c996f4a4ee5717fe58d8bf95a549837537be 100644 --- a/examples/ping-pong/pong.yaml +++ b/examples/ping-pong/pong.yaml @@ -5,7 +5,7 @@ metadata: labels: app: ping-pong annotations: - k8s.v1.cni.cncf.io/networks: ping-network + l2sm/networks: ping-network spec: containers: - name: router diff --git a/src/operator/l2sm-operator.py b/src/operator/l2sm-operator.py index 9ecdb96cac5f56acc2f1a013ef0f8db160deb2dd..413287ff0ddb8e55393c51af8b9ddd5266fd96a3 100644 --- a/src/operator/l2sm-operator.py +++ b/src/operator/l2sm-operator.py @@ -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 #@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): # Database connection setup @@ -167,20 +167,7 @@ def create_vn(spec, name, namespace, logger, **kwargs): sql = "INSERT INTO networks (name, type) VALUES (%s, %s) ON DUPLICATE KEY UPDATE name = VALUES(name), type = VALUES(type)" cursor.execute(sql, (name.strip(), spec['type'])) connection.commit() - # # 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() @@ -191,14 +178,11 @@ def create_vn(spec, name, namespace, logger, **kwargs): -#ASSIGN POD TO NETWORK (TRIGGERS ONLY IF ANNOTATION IS PRESENT) @kopf.on.create('pods.v1', annotations={'l2sm/networks': kopf.PRESENT}) def pod_vn(body, name, namespace, logger, annotations, **kwargs): """Assign Pod to a network if a specific annotation is present.""" - - # Avoid database overlap by introducing a random sleep time - time.sleep(random.uniform(0, 0.8)) + #time.sleep(random.uniform(0,0.8)) # Avoid database overlap by introducing a random sleep time multus_networks = extract_multus_networks(annotations) if not multus_networks: @@ -206,42 +190,38 @@ def pod_vn(body, name, namespace, logger, annotations, **kwargs): return existing_networks = get_existing_networks(namespace) - target_networks = filter_target_networks(multus_networks, existing_networks) - if not target_networks: + # 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() - l2sm_network = api.get_namespaced_custom_object( - group="l2sm.k8s.local", - version="v1", - namespace=namespace, - plural="l2sm-networks", - name="ping-network", - ) - print(l2sm_network) - for target_network in target_networks: - update_network(target_network,name,namespace,api) + # Assign pods to each of the target networks, this part remains unchanged + for network in target_networks: + update_network(network['name'], name, namespace, api) 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) - + # Now pass the full network info including IPs if present 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) - + def update_network(l2sm_network_name,pod_name,namespace,api): l2sm_network = api.get_namespaced_custom_object( - group="l2sm.k8s.local", + group="l2sm.l2sm.k8s.local", version="v1", namespace=namespace, - plural="l2sm-networks", + plural="l2smnetworks", name=l2sm_network_name, ) connected_pods = l2sm_network.get('status', {}).get('connectedPods', []) @@ -251,10 +231,10 @@ def update_network(l2sm_network_name,pod_name,namespace,api): # Apply the patch to the L2SMNetwork api.patch_namespaced_custom_object( - group="l2sm.k8s.local", + group="l2sm.l2sm.k8s.local", version="v1", namespace=namespace, - plural="l2sm-networks", + plural="l2smnetworks", name=l2sm_network_name, body=patch ) @@ -262,10 +242,10 @@ def update_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( - group="l2sm.k8s.local", + group="l2sm.l2sm.k8s.local", version="v1", namespace=namespace, - plural="l2sm-networks", + plural="l2smnetworks", name=l2sm_network_name, ) connected_pods = l2sm_network.get('status', {}).get('connectedPods', []) @@ -275,10 +255,10 @@ def remove_from_network(l2sm_network_name,pod_name,namespace,api): # Apply the patch to the L2SMNetwork api.patch_namespaced_custom_object( - group="l2sm.k8s.local", + group="l2sm.l2sm.k8s.local", version="v1", namespace=namespace, - plural="l2sm-networks", + plural="l2smnetworks", name=l2sm_network_name, body=patch ) @@ -288,13 +268,22 @@ def remove_from_network(l2sm_network_name,pod_name,namespace,api): def extract_multus_networks(annotations): - """Extract and return Multus networks from annotations.""" - return [network.strip() for network in annotations.get('l2sm/networks').split(",")] + """Extract and return Multus networks from annotations.""" + annotation = annotations.get('l2sm/networks') + if annotation.startswith('['): # New JSON format + try: + networks = json.loads(annotation) + return [{ 'name': net['name'], 'ips': net.get('ips', []) } for net in networks] + except json.JSONDecodeError: + raise ValueError("Failed to decode JSON from annotations") + else: # Original string format + return [{'name': network.strip(), 'ips': []} for network in annotation.split(",")] + def get_existing_networks(namespace): """Return existing networks in the namespace.""" 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']] def filter_target_networks(multus_networks, existing_networks): @@ -322,12 +311,20 @@ def get_free_interfaces(node_name): connection.close() return free_interfaces -def update_pod_annotation(pod_name, namespace, interfaces): - """Update the Pod's annotation with assigned interfaces.""" +def update_pod_annotation(pod_name, namespace, networks_info): + """Update the Pod's annotation with assigned networks and optional IPs.""" 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) + # Format the annotations based on whether IPs are provided + formatted_networks = [] + for network_info in networks_info: + if not network_info.get('ips'): + network_info['ips'] = [generate_random_ipv6_fe80()] # Ensure this is a list + formatted_networks.append(json.dumps(network_info)) # Convert dict to a JSON string + + 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}}) @@ -342,20 +339,19 @@ def update_network_assignments(pod_name, namespace, node_name, free_interfaces, 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']) - + update_interface_assignment(cursor, interface['id'], target_networks[i]['name'], pod_name, node_name) + assigned_interfaces.append({"name":interface['name'], "ips": target_networks[i]['ips']}) # 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]) + post_network_assignment(openflow_id, port_number, target_networks[i]['name']) 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)}") + 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 def update_interface_assignment(cursor, interface_id, network_name, pod_name, node_name): @@ -393,7 +389,7 @@ def post_network_assignment(openflow_id, port_number, network_name): #UPDATE DATABASE WHEN POD IS DELETED @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, user=database_username, password=database_password, @@ -405,13 +401,31 @@ def dpod_vn(name, logger, **kwargs): cursor.execute(sql) 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: connection.close() logger.info(f"Pod {name} removed") #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): connection = pymysql.connect(host=database_ip, user=database_username, @@ -424,25 +438,18 @@ def delete_vn(spec, name, logger, **kwargs): update_interfaces_sql = """ UPDATE interfaces 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'])) # 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'])) - response = session.delete(base_controller_url + '/l2sm/networks/' + name) - - if response.status_code == 204: - # Successful request - logger.info(f"Network has been deleted in the SDN Controller") - connection.commit() - else: - # Handle errors - logger.info(f"Error: {response.status_code}") + + connection.commit() finally: connection.close() logger.info(f"Network {name} removed") @@ -459,9 +466,9 @@ def remove_node(body, logger, annotations, **kwargs): with connection.cursor() as cursor: sql = """ DELETE FROM interfaces - WHERE switch_id = (SELECT id FROM switches WHERE node_name = '%s'); + WHERE switch_id = (SELECT id FROM switches WHERE node_name = %s); """ - switchSql = "DELETE 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() @@ -469,3 +476,21 @@ def remove_node(body, logger, annotations, **kwargs): connection.close() 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 +