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

added support between operator and controller

parent c4f0f589
No related branches found
No related tags found
1 merge request!2repo: added new directory where utils scripts will be
Showing with 158 additions and 204 deletions
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: l2-ps
name: l2sm-switch
#namespace: kube-system
labels:
l2sm-component: l2-ps
l2sm-component: l2sm-switch
spec:
selector:
matchLabels:
l2sm-component: l2-ps
l2sm-component: l2sm-switch
template:
metadata:
labels:
l2sm-component: l2-ps
l2sm-component: l2sm-switch
annotations:
k8s.v1.cni.cncf.io/networks: vhost1@vhost1, vhost2@vhost2, vhost3@vhost3, vhost4@vhost4, vhost5@vhost5, vhost6@vhost6, vhost7@vhost7, vhost8@vhost8, vhost9@vhost9, vhost10@vhost10
spec:
......@@ -23,7 +23,7 @@ spec:
operator: Exists
effect: NoSchedule
containers:
- name: l2-ps
- name: l2sm-switch
image: alexdecb/l2sm-ovs:test
command: ["sleep","infinity"]
env:
......
apiVersion: apps/v1
kind: Deployment
metadata:
name: l2sm-controller-deployment
name: l2sm-controller
spec:
replicas: 1
selector:
......
apiVersion: apps/v1
kind: Deployment
metadata:
name: l2sm-operator-deployment
name: l2sm-operator
spec:
replicas: 1
strategy:
......@@ -16,8 +16,12 @@ spec:
spec:
serviceAccountName: l2sm-operator
containers:
- image: lewisfelix24/l2sm-operator:latest
- image: alexdecb/l2sm-operator:latest
name: l2sm-opt-pod
env:
- name: CONTROLLER_IP
value: l2sm-controller-service
#command: ["sleep","infinity"]
- image: mysql/mysql-server:5.7
name: mysql
env:
......
FROM onosproject/onos:2.7.0
FROM onosproject/onos:2.7-latest
COPY . ./
......
module l2sm_controller_api
go 1.18
require (
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/helloyi/go-sshclient v1.2.0 // indirect
github.com/kr/fs v0.1.0 // indirect
github.com/pkg/sftp v1.13.5 // indirect
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d // indirect
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 // indirect
)
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/helloyi/go-sshclient v1.2.0 h1:36YOcHjtb3QhtZPTFthb0kvDlfQqVHErwfObVq6omck=
github.com/helloyi/go-sshclient v1.2.0/go.mod h1:L2+lPFL4TshqEu5fl5FHqtojNDzUtPFIjHXgaZYMX0Q=
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/pkg/sftp v1.13.5 h1:a3RLUqkyjYRtBTZJZ1VRrKbN3zhuPLlUc3sphVz81go=
github.com/pkg/sftp v1.13.5/go.mod h1:wHDZ0IZX6JcBYRK1TH9bcVq8G7TLpVHYIGJRFnmPfxg=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d h1:3qF+Z8Hkrw9sOhrFHti9TlB1Hkac1x+DNRkv0XQiFjo=
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM=
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
package main
import (
"bytes"
"encoding/json"
"fmt"
"log"
"net/http"
"golang.org/x/crypto/ssh"
)
type Network struct {
Name string `json:"name"`
Ports []string `json:"ports"`
}
func main() {
//var hostKey ssh.PublicKey
// An SSH client is represented with a ClientConn.
//
// To authenticate with the remote server you must pass at least one
// implementation of AuthMethod via the Auth field in ClientConfig,
// and provide a HostKeyCallback.
config := &ssh.ClientConfig{
User: "root",
Auth: []ssh.AuthMethod{
ssh.Password("root123"),
},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
client, err := ssh.Dial("tcp", "172.17.0.2:22", config)
if err != nil {
log.Fatal("Failed to dial: ", err)
}
defer client.Close()
// Each ClientConn can support multiple interactive sessions,
// represented by a Session.
session, err := client.NewSession()
if err != nil {
log.Fatal("Failed to create session: ", err)
}
defer session.Close()
// Once a Session is created, you can execute a single command on
// the remote side using the Run method.
var b bytes.Buffer
session.Stdout = &b
if err := session.Run("/usr/bin/whoami"); err != nil {
log.Fatal("Failed to run: " + err.Error())
}
fmt.Println(b.String())
http.HandleFunc("/network", func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
getNetwork(w, r)
case "POST":
createNetwork(w, r)
case "PUT":
updateNetwork(w, r)
default:
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
}
})
fmt.Println("Server is running on port 8080...")
http.ListenAndServe(":8080", nil)
}
func getNetwork(w http.ResponseWriter, r *http.Request) {
//var network Network
queryValues := r.URL.Query()
name := queryValues.Get("name")
//network = l2sm-get-network Name
//json.NewEncoder(w).Encode(&network)
fmt.Printf("GET %s", name)
}
func createNetwork(w http.ResponseWriter, r *http.Request) {
var network Network
if err := json.NewDecoder(r.Body).Decode(&network); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
//l2sm-create-network network.Name
w.WriteHeader(http.StatusCreated)
fmt.Printf("POST")
}
func updateNetwork(w http.ResponseWriter, r *http.Request) {
var network Network
// l2sm-add-port network.Port
// if err := json.NewDecoder(r.Body).Decode(&network); err != nil {
// http.Error(w, err.Error(), http.StatusBadRequest)
// return
// }
// params := r.URL.Query()
// name := params.Get("name")
// port := params.Get("port")
// for i, net := range networks {
// if net.Name == name {
// networks[i].Ports = append(networks[i].Ports, port)
// w.WriteHeader(http.StatusOK)
// return
// }
// }
// http.Error(w, "Network not found", http.StatusNotFound)
fmt.Printf("UPDATE")
}
File deleted
File added
......@@ -22,12 +22,12 @@ done
# Start the configuration
./bin/onos-app localhost install idco-app-1.0.oar
./bin/onos-app localhost activate org.idco.app
./bin/onos-app localhost activate org.onosproject.drivers
./bin/onos-app localhost activate org.onosproject.lldpprovider
./bin/onos-app localhost activate org.onosproject.openflow-base
./bin/onos-app localhost activate org.onosproject.optical-model
./bin/onos-app localhost install! l2sm-controller-app-1.0.oar
......
FROM python:3.7
RUN pip install kopf kubernetes PyMySQL cryptography
RUN pip install kopf kubernetes PyMySQL cryptography requests
COPY l2sm-operator.py /l2sm-operator.py
CMD kopf run --standalone --all-namespaces /l2sm-operator.py
......@@ -11,42 +11,138 @@ from kubernetes import client, config
import pymysql
import random
import time
import requests
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):
# Create a session with basic authentication
auth = HTTPBasicAuth(username, password)
session = requests.Session()
session.auth = auth
#Check if connection is possible
response = session.get(baseControllerUrl + '/l2sm/networks/status')
if response.status_code == 200:
# Successful request
print("Initialized session between operator and controller.")
return session
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()
ip = "127.0.0.1"
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
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': 'l2-ps'})
@kopf.on.create('pods.v1', labels={'l2sm-component': 'l2sm-switch'})
def build_db(body, logger, annotations, **kwargs):
db = pymysql.connect(host=ip,user="l2sm",password="l2sm;",db="L2SM")
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()
values = []
#MODIFY THE END VALUE TO ADD MORE INTERFACES
values = []
for i in range(1,11):
values.append(['vpod'+str(i), body['spec']['nodeName'], '-1', ''])
sql = "INSERT INTO interfaces (interface, node, network, pod) VALUES (%s, %s, %s, %s)"
cur.executemany(sql, values)
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")
@kopf.on.update('pods.v1', labels={'l2sm-component': 'l2sm-switch'})
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' WHERE node = '%s'" % (body['status']['podIP'], body['spec']['nodeName'])
cur.execute(updateQuery)
db.commit()
db.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
#@kopf.on.create('NetworkAttachmentDefinition', field="spec.config['device']", value='l2sm-vNet')
@kopf.on.create('NetworkAttachmentDefinition', when=lambda spec, **_: '"device": "l2sm-vNet"' in spec['config'])
def create_vn(spec, name, namespace, logger, **kwargs):
db = pymysql.connect(host=ip,user="l2sm",password="l2sm;",db="L2SM")
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()
logger.info(f"Network has been created")
# 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")
#ASSIGN POD TO NETWORK (TRIGGERS ONLY IF ANNOTATION IS PRESENT)
......@@ -83,7 +179,7 @@ def pod_vn(body, name, namespace, logger, annotations, **kwargs):
ret = v1.read_namespaced_pod(name, namespace)
node = body['spec']['nodeName']
db = pymysql.connect(host=ip,user="l2sm",password="l2sm;",db="L2SM")
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)
......@@ -116,20 +212,50 @@ def pod_vn(body, name, namespace, logger, annotations, **kwargs):
# networkN = retrieve[0].strip()
# break
switchId = getSwitchId(cur, node)
if switchId is None:
logger.info(f"The l2sm switch is not connected to controller. Not connecting the pod")
return
vpodPattern = re.compile(r'\d+$')
portNumbers = [int(vpodPattern.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])]
}
response = session.post(baseControllerUrl + '/l2sm/networks/port', json=payload)
db.commit()
db.close()
#HERE GOES SDN, THIS IS WHERE THE FUN BEGINS
# 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}")
#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=ip,user="l2sm",password="l2sm;",db="L2SM")
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)
......@@ -140,7 +266,7 @@ def dpod_vn(name, logger, **kwargs):
#UPDATE DATABASE WHEN NETWORK IS DELETED
@kopf.on.delete('NetworkAttachmentDefinition', when=lambda spec, **_: '"device": "l2sm-vNet"' in spec['config'])
def delete_vn(spec, name, logger, **kwargs):
db = pymysql.connect(host=ip,user="l2sm",password="l2sm;",db="L2SM")
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)
......@@ -149,9 +275,9 @@ def delete_vn(spec, name, logger, **kwargs):
logger.info(f"Network has been deleted")
#DELETE DATABASE ENTRIES WHEN A NEW L2SM POD IS DELETED (A NEW NODE GETS OUT OF THE CLUSTER)
@kopf.on.delete('pods.v1', labels={'l2sm-component': 'l2-ps'})
@kopf.on.delete('pods.v1', labels={'l2sm-component': 'l2sm-switch'})
def remove_node(body, logger, annotations, **kwargs):
db = pymysql.connect(host=ip,user="l2sm",password="l2sm;",db="L2SM")
db = pymysql.connect(host=databaseIP,user="l2sm",password="l2sm;",db="L2SM")
cur = db.cursor()
sql = "DELETE FROM interfaces WHERE node = '%s'" % (body['spec']['nodeName'])
cur.execute(sql)
......
[
{
"name": "l2sm1",
"nodeIP": "10.1.14.45",
"nodeIP": "10.1.14.50",
"neighborNodes": ["l2sm2"]
},
{
"name": "l2sm2",
"nodeIP": "10.1.72.79",
"nodeIP": "10.1.72.69",
"neighborNodes": ["l2sm1"]
}
]
\ No newline at end of file
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