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/src/operator/l2sm-operator.py b/src/operator/l2sm-operator.py index 9ecdb96cac5f56acc2f1a013ef0f8db160deb2dd..e5374161083e113f951554890b6c1835b39025ac 100644 --- a/src/operator/l2sm-operator.py +++ b/src/operator/l2sm-operator.py @@ -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,11 +178,9 @@ 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)) @@ -206,36 +191,32 @@ 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", @@ -288,8 +269,17 @@ 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.""" @@ -322,12 +312,24 @@ 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 network_info['ips']: # Case B with IP addresses + formatted_networks.append(json.dumps({ + "name": network_info['name'], + "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) + ']' v1.patch_namespaced_pod(pod_name, namespace, {'metadata': {'annotations': pod_annotations}}) @@ -434,15 +436,8 @@ def delete_vn(spec, name, logger, **kwargs): - 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")