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

pods deployment: added ipam plugin

Following discussions with the SWM team, static ip address implementation was required. This is done now by specifying with the pod an optional ip address field. If no ip is defined, a default ipv6 will be used
parent cf83402c
No related branches found
No related tags found
1 merge request!2repo: added new directory where utils scripts will be
Showing
with 190 additions and 78 deletions
...@@ -14,7 +14,7 @@ spec: ...@@ -14,7 +14,7 @@ spec:
labels: labels:
l2sm-component: l2sm-switch l2sm-component: l2sm-switch
annotations: 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: spec:
tolerations: tolerations:
# this toleration is to have the daemonset runnable on master nodes # this toleration is to have the daemonset runnable on master nodes
...@@ -22,18 +22,18 @@ spec: ...@@ -22,18 +22,18 @@ spec:
- key: node-role.kubernetes.io/master - key: node-role.kubernetes.io/master
operator: Exists operator: Exists
effect: NoSchedule effect: NoSchedule
initContainers: # initContainers:
- name: wait-for-l2sm-operator # - name: wait-for-l2sm-operator
image: curlimages/curl # image: curlimages/curl
args: # args:
- /bin/sh # - /bin/sh
- -c # - -c
- > # - >
set -x; # set -x;
while [ $(curl -sw '%{http_code}' "http://l2sm-operator-service:8080/healthz" -o /dev/null) -ne 200 ]; do # while [ $(curl -sw '%{http_code}' "http://l2sm-operator-service:8080/healthz" -o /dev/null) -ne 200 ]; do
sleep 15; # sleep 15;
done; # done;
sleep 5; # sleep 5;
containers: containers:
- name: l2sm-switch - name: l2sm-switch
image: alexdecb/l2sm-switch:2.2 image: alexdecb/l2sm-switch:2.2
......
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
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
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
...@@ -9,5 +9,8 @@ spec: ...@@ -9,5 +9,8 @@ spec:
"type": "bridge", "type": "bridge",
"bridge": "br1", "bridge": "br1",
"mtu": 1400, "mtu": 1400,
"device": "veth1" "device": "veth1",
"ipam": {
"type":"static"
}
}' }'
...@@ -9,5 +9,8 @@ spec: ...@@ -9,5 +9,8 @@ spec:
"type": "bridge", "type": "bridge",
"bridge": "br10", "bridge": "br10",
"mtu": 1400, "mtu": 1400,
"device": "veth10" "device": "veth10",
"ipam": {
"type":"static"
}
}' }'
...@@ -9,5 +9,8 @@ spec: ...@@ -9,5 +9,8 @@ spec:
"type": "bridge", "type": "bridge",
"bridge": "br2", "bridge": "br2",
"mtu": 1400, "mtu": 1400,
"device": "veth2" "device": "veth2",
"ipam": {
"type":"static"
}
}' }'
...@@ -9,5 +9,8 @@ spec: ...@@ -9,5 +9,8 @@ spec:
"type": "bridge", "type": "bridge",
"bridge": "br3", "bridge": "br3",
"mtu": 1400, "mtu": 1400,
"device": "veth3" "device": "veth3",
"ipam": {
"type":"static"
}
}' }'
...@@ -9,5 +9,8 @@ spec: ...@@ -9,5 +9,8 @@ spec:
"type": "bridge", "type": "bridge",
"bridge": "br4", "bridge": "br4",
"mtu": 1400, "mtu": 1400,
"device": "veth4" "device": "veth4",
"ipam": {
"type":"static"
}
}' }'
...@@ -9,5 +9,8 @@ spec: ...@@ -9,5 +9,8 @@ spec:
"type": "bridge", "type": "bridge",
"bridge": "br5", "bridge": "br5",
"mtu": 1400, "mtu": 1400,
"device": "veth5" "device": "veth5",
"ipam": {
"type":"static"
}
}' }'
...@@ -9,5 +9,8 @@ spec: ...@@ -9,5 +9,8 @@ spec:
"type": "bridge", "type": "bridge",
"bridge": "br6", "bridge": "br6",
"mtu": 1400, "mtu": 1400,
"device": "veth6" "device": "veth6",
"ipam": {
"type":"static"
}
}' }'
...@@ -9,5 +9,8 @@ spec: ...@@ -9,5 +9,8 @@ spec:
"type": "bridge", "type": "bridge",
"bridge": "br7", "bridge": "br7",
"mtu": 1400, "mtu": 1400,
"device": "veth7" "device": "veth7",
"ipam": {
"type":"static"
}
}' }'
...@@ -9,5 +9,8 @@ spec: ...@@ -9,5 +9,8 @@ spec:
"type": "bridge", "type": "bridge",
"bridge": "br8", "bridge": "br8",
"mtu": 1400, "mtu": 1400,
"device": "veth8" "device": "veth8",
"ipam": {
"type":"static"
}
}' }'
...@@ -9,5 +9,8 @@ spec: ...@@ -9,5 +9,8 @@ spec:
"type": "bridge", "type": "bridge",
"bridge": "br9", "bridge": "br9",
"mtu": 1400, "mtu": 1400,
"device": "veth9" "device": "veth9",
"ipam": {
"type":"static"
}
}' }'
...@@ -9,7 +9,10 @@ spec: ...@@ -9,7 +9,10 @@ spec:
"type": "bridge", "type": "bridge",
"bridge": "br1", "bridge": "br1",
"mtu": 1400, "mtu": 1400,
"device": "veth1" "device": "veth1",
"ipam": {
"type":"static"
}
}' }'
--- ---
apiVersion: "k8s.cni.cncf.io/v1" apiVersion: "k8s.cni.cncf.io/v1"
...@@ -22,7 +25,10 @@ spec: ...@@ -22,7 +25,10 @@ spec:
"type": "bridge", "type": "bridge",
"bridge": "br2", "bridge": "br2",
"mtu": 1400, "mtu": 1400,
"device": "veth2" "device": "veth2",
"ipam": {
"type":"static"
}
}' }'
--- ---
apiVersion: "k8s.cni.cncf.io/v1" apiVersion: "k8s.cni.cncf.io/v1"
...@@ -35,7 +41,10 @@ spec: ...@@ -35,7 +41,10 @@ spec:
"type": "bridge", "type": "bridge",
"bridge": "br3", "bridge": "br3",
"mtu": 1400, "mtu": 1400,
"device": "veth3" "device": "veth3",
"ipam": {
"type":"static"
}
}' }'
--- ---
apiVersion: "k8s.cni.cncf.io/v1" apiVersion: "k8s.cni.cncf.io/v1"
...@@ -48,7 +57,10 @@ spec: ...@@ -48,7 +57,10 @@ spec:
"type": "bridge", "type": "bridge",
"bridge": "br4", "bridge": "br4",
"mtu": 1400, "mtu": 1400,
"device": "veth4" "device": "veth4",
"ipam": {
"type":"static"
}
}' }'
--- ---
apiVersion: "k8s.cni.cncf.io/v1" apiVersion: "k8s.cni.cncf.io/v1"
...@@ -61,7 +73,10 @@ spec: ...@@ -61,7 +73,10 @@ spec:
"type": "bridge", "type": "bridge",
"bridge": "br5", "bridge": "br5",
"mtu": 1400, "mtu": 1400,
"device": "veth5" "device": "veth5",
"ipam": {
"type":"static"
}
}' }'
--- ---
apiVersion: "k8s.cni.cncf.io/v1" apiVersion: "k8s.cni.cncf.io/v1"
...@@ -74,7 +89,10 @@ spec: ...@@ -74,7 +89,10 @@ spec:
"type": "bridge", "type": "bridge",
"bridge": "br6", "bridge": "br6",
"mtu": 1400, "mtu": 1400,
"device": "veth6" "device": "veth6",
"ipam": {
"type":"static"
}
}' }'
--- ---
...@@ -88,7 +106,10 @@ spec: ...@@ -88,7 +106,10 @@ spec:
"type": "bridge", "type": "bridge",
"bridge": "br7", "bridge": "br7",
"mtu": 1400, "mtu": 1400,
"device": "veth7" "device": "veth7",
"ipam": {
"type":"static"
}
}' }'
--- ---
apiVersion: "k8s.cni.cncf.io/v1" apiVersion: "k8s.cni.cncf.io/v1"
...@@ -101,7 +122,10 @@ spec: ...@@ -101,7 +122,10 @@ spec:
"type": "bridge", "type": "bridge",
"bridge": "br8", "bridge": "br8",
"mtu": 1400, "mtu": 1400,
"device": "veth8" "device": "veth8",
"ipam": {
"type":"static"
}
}' }'
--- ---
apiVersion: "k8s.cni.cncf.io/v1" apiVersion: "k8s.cni.cncf.io/v1"
...@@ -114,7 +138,10 @@ spec: ...@@ -114,7 +138,10 @@ spec:
"type": "bridge", "type": "bridge",
"bridge": "br9", "bridge": "br9",
"mtu": 1400, "mtu": 1400,
"device": "veth9" "device": "veth9",
"ipam": {
"type":"static"
}
}' }'
--- ---
apiVersion: "k8s.cni.cncf.io/v1" apiVersion: "k8s.cni.cncf.io/v1"
...@@ -127,7 +154,10 @@ spec: ...@@ -127,7 +154,10 @@ spec:
"type": "bridge", "type": "bridge",
"bridge": "br10", "bridge": "br10",
"mtu": 1400, "mtu": 1400,
"device": "veth10" "device": "veth10",
"ipam": {
"type":"static"
}
}' }'
--- ---
apiVersion: v1 apiVersion: v1
......
...@@ -167,20 +167,7 @@ def create_vn(spec, name, namespace, logger, **kwargs): ...@@ -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)" 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'])) cursor.execute(sql, (name.strip(), spec['type']))
connection.commit() 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: except Exception as e:
# Roll back the database transaction in case of any error # Roll back the database transaction in case of any error
connection.rollback() connection.rollback()
...@@ -191,11 +178,9 @@ def create_vn(spec, name, namespace, logger, **kwargs): ...@@ -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}) @kopf.on.create('pods.v1', annotations={'l2sm/networks': kopf.PRESENT})
def pod_vn(body, name, namespace, logger, annotations, **kwargs): 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)) time.sleep(random.uniform(0, 0.8))
...@@ -206,36 +191,32 @@ def pod_vn(body, name, namespace, logger, annotations, **kwargs): ...@@ -206,36 +191,32 @@ def pod_vn(body, name, namespace, logger, annotations, **kwargs):
return return
existing_networks = get_existing_networks(namespace) existing_networks = get_existing_networks(namespace)
target_networks = filter_target_networks(multus_networks, existing_networks) # Need to extract just the names from multus_networks for comparison
if not target_networks: 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.") logger.info("No target networks found. Letting Multus handle the network assignment.")
return 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() api = CustomObjectsApi()
l2sm_network = api.get_namespaced_custom_object( # Assign pods to each of the target networks, this part remains unchanged
group="l2sm.k8s.local", for network in target_networks:
version="v1", update_network(network['name'], name, namespace, api)
namespace=namespace,
plural="l2sm-networks",
name="ping-network",
)
print(l2sm_network)
for target_network in target_networks:
update_network(target_network,name,namespace,api)
if 'spec' in body and 'nodeName' in body['spec']: if 'spec' in body and 'nodeName' in body['spec']:
node_name = body['spec']['nodeName'] node_name = body['spec']['nodeName']
free_interfaces = get_free_interfaces(node_name) free_interfaces = get_free_interfaces(node_name)
if len(free_interfaces) < len(target_networks): if len(free_interfaces) < len(target_networks):
raise kopf.PermanentError(f"Node {node_name} has no free interfaces left") raise kopf.PermanentError(f"Node {node_name} has no free interfaces left")
openflow_id = get_openflow_id(node_name) 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) update_network_assignments(name, namespace, node_name, free_interfaces, target_networks, logger, openflow_id)
else: else:
raise kopf.TemporaryError("The Pod is not yet ready", delay=1) raise kopf.TemporaryError("The Pod is not yet ready", delay=1)
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.k8s.local",
...@@ -288,8 +269,17 @@ def remove_from_network(l2sm_network_name,pod_name,namespace,api): ...@@ -288,8 +269,17 @@ def remove_from_network(l2sm_network_name,pod_name,namespace,api):
def extract_multus_networks(annotations): def extract_multus_networks(annotations):
"""Extract and return Multus networks from annotations.""" """Extract and return Multus networks from annotations."""
return [network.strip() for network in annotations.get('l2sm/networks').split(",")] 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): def get_existing_networks(namespace):
"""Return existing networks in the namespace.""" """Return existing networks in the namespace."""
...@@ -322,12 +312,24 @@ def get_free_interfaces(node_name): ...@@ -322,12 +312,24 @@ def get_free_interfaces(node_name):
connection.close() connection.close()
return free_interfaces return free_interfaces
def update_pod_annotation(pod_name, namespace, interfaces): def update_pod_annotation(pod_name, namespace, networks_info):
"""Update the Pod's annotation with assigned interfaces.""" """Update the Pod's annotation with assigned networks and optional IPs."""
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 {}
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}}) v1.patch_namespaced_pod(pod_name, namespace, {'metadata': {'annotations': pod_annotations}})
...@@ -434,15 +436,8 @@ def delete_vn(spec, name, logger, **kwargs): ...@@ -434,15 +436,8 @@ def delete_vn(spec, name, logger, **kwargs):
response = session.delete(base_controller_url + '/l2sm/networks/' + name)
connection.commit()
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}")
finally: finally:
connection.close() connection.close()
logger.info(f"Network {name} removed") logger.info(f"Network {name} removed")
......
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