core-extra/daemon/core/services/sdn.py

131 lines
5.3 KiB
Python

"""
sdn.py defines services to start Open vSwitch and the Ryu SDN Controller.
"""
import re
from core.nodes.base import CoreNode
from core.services.coreservices import CoreService
class SdnService(CoreService):
"""
Parent class for SDN services.
"""
group: str = "SDN"
@classmethod
def generate_config(cls, node: CoreNode, filename: str) -> str:
return ""
class OvsService(SdnService):
name: str = "OvsService"
group: str = "SDN"
executables: tuple[str, ...] = ("ovs-ofctl", "ovs-vsctl")
dirs: tuple[str, ...] = (
"/etc/openvswitch",
"/var/run/openvswitch",
"/var/log/openvswitch",
)
configs: tuple[str, ...] = ("OvsService.sh",)
startup: tuple[str, ...] = ("bash OvsService.sh",)
shutdown: tuple[str, ...] = ("killall ovs-vswitchd", "killall ovsdb-server")
@classmethod
def generate_config(cls, node: CoreNode, filename: str) -> str:
# Check whether the node is running zebra
has_zebra = 0
for s in node.services:
if s.name == "zebra":
has_zebra = 1
cfg = "#!/bin/sh\n"
cfg += "# auto-generated by OvsService (OvsService.py)\n"
cfg += "## First make sure that the ovs services are up and running\n"
cfg += "/etc/init.d/openvswitch-switch start < /dev/null\n\n"
cfg += "## create the switch itself, set the fail mode to secure, \n"
cfg += "## this stops it from routing traffic without defined flows.\n"
cfg += "## remove the -- and everything after if you want it to act as a regular switch\n"
cfg += "ovs-vsctl add-br ovsbr0 -- set Bridge ovsbr0 fail-mode=secure\n"
cfg += "\n## Now add all our interfaces as ports to the switch\n"
portnum = 1
for iface in node.get_ifaces(control=False):
ifnumstr = re.findall(r"\d+", iface.name)
ifnum = ifnumstr[0]
# create virtual interfaces
cfg += "## Create a veth pair to send the data to\n"
cfg += f"ip link add rtr{ifnum} type veth peer name sw{ifnum}\n"
# remove ip address of eths because quagga/zebra will assign same IPs to rtr interfaces
# or assign them manually to rtr interfaces if zebra is not running
for ip4 in iface.ip4s:
cfg += f"ip addr del {ip4.ip} dev {iface.name}\n"
if has_zebra == 0:
cfg += f"ip addr add {ip4.ip} dev rtr{ifnum}\n"
for ip6 in iface.ip6s:
cfg += f"ip -6 addr del {ip6.ip} dev {iface.name}\n"
if has_zebra == 0:
cfg += f"ip -6 addr add {ip6.ip} dev rtr{ifnum}\n"
# add interfaces to bridge
# Make port numbers explicit so they're easier to follow in
# reading the script
cfg += "## Add the CORE interface to the switch\n"
cfg += (
f"ovs-vsctl add-port ovsbr0 eth{ifnum} -- "
f"set Interface eth{ifnum} ofport_request={portnum:d}\n"
)
cfg += "## And then add its sibling veth interface\n"
cfg += (
f"ovs-vsctl add-port ovsbr0 sw{ifnum} -- "
f"set Interface sw{ifnum} ofport_request={portnum + 1:d}\n"
)
cfg += "## start them up so we can send/receive data\n"
cfg += f"ovs-ofctl mod-port ovsbr0 eth{ifnum} up\n"
cfg += f"ovs-ofctl mod-port ovsbr0 sw{ifnum} up\n"
cfg += "## Bring up the lower part of the veth pair\n"
cfg += f"ip link set dev rtr{ifnum} up\n"
portnum += 2
# Add rule for default controller if there is one local
# (even if the controller is not local, it finds it)
cfg += "\n## We assume there will be an SDN controller on the other end of this, \n"
cfg += "## but it will still function if there's not\n"
cfg += "ovs-vsctl set-controller ovsbr0 tcp:127.0.0.1:6633\n"
cfg += "\n## Now to create some default flows, \n"
cfg += "## if the above controller will be present then you probably want to delete them\n"
# Setup default flows
portnum = 1
for iface in node.get_ifaces(control=False):
cfg += "## Take the data from the CORE interface and put it on the veth and vice versa\n"
cfg += f"ovs-ofctl add-flow ovsbr0 priority=1000,in_port={portnum:d},action=output:{portnum + 1:d}\n"
cfg += f"ovs-ofctl add-flow ovsbr0 priority=1000,in_port={portnum + 1:d},action=output:{portnum:d}\n"
portnum += 2
return cfg
class RyuService(SdnService):
name: str = "ryuService"
group: str = "SDN"
executables: tuple[str, ...] = ("ryu-manager",)
configs: tuple[str, ...] = ("ryuService.sh",)
startup: tuple[str, ...] = ("bash ryuService.sh",)
shutdown: tuple[str, ...] = ("killall ryu-manager",)
@classmethod
def generate_config(cls, node: CoreNode, filename: str) -> str:
"""
Return a string that will be written to filename, or sent to the
GUI for user customization.
"""
cfg = "#!/bin/sh\n"
cfg += "# auto-generated by ryuService (ryuService.py)\n"
cfg += (
"ryu-manager --observe-links ryu.app.ofctl_rest ryu.app.rest_topology &\n"
)
return cfg