Merge pull request #474 from coreemu/cleanup/service-type-hinting

daemon: added type hinting throughout all services and made small twe…
This commit is contained in:
bharnden 2020-06-18 13:08:25 -07:00 committed by GitHub
commit a09910d0bc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 560 additions and 636 deletions

View file

@ -7,7 +7,7 @@ import os
import shutil
import threading
from threading import RLock
from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple, Type
from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple, Type, Union
import netaddr
@ -27,7 +27,7 @@ if TYPE_CHECKING:
from core.configservice.base import ConfigService
from core.services.coreservices import CoreService
CoreServices = List[CoreService]
CoreServices = List[Union[CoreService, Type[CoreService]]]
ConfigServiceType = Type[ConfigService]
_DEFAULT_MTU = 1500

View file

@ -1,8 +1,11 @@
"""
bird.py: defines routing services provided by the BIRD Internet Routing Daemon.
"""
from typing import Optional, Tuple
import netaddr
from core.nodes.base import CoreNode
from core.services.coreservices import CoreService
@ -11,27 +14,27 @@ class Bird(CoreService):
Bird router support
"""
name = "bird"
executables = ("bird",)
group = "BIRD"
dirs = ("/etc/bird",)
configs = ("/etc/bird/bird.conf",)
startup = ("bird -c %s" % (configs[0]),)
shutdown = ("killall bird",)
validate = ("pidof bird",)
name: str = "bird"
group: str = "BIRD"
executables: Tuple[str, ...] = ("bird",)
dirs: Tuple[str, ...] = ("/etc/bird",)
configs: Tuple[str, ...] = ("/etc/bird/bird.conf",)
startup: Tuple[str, ...] = ("bird -c %s" % (configs[0]),)
shutdown: Tuple[str, ...] = ("killall bird",)
validate: Tuple[str, ...] = ("pidof bird",)
@classmethod
def generate_config(cls, node, filename):
def generate_config(cls, node: CoreNode, filename: str) -> str:
"""
Return the bird.conf file contents.
"""
if filename == cls.configs[0]:
return cls.generateBirdConf(node)
return cls.generate_bird_config(node)
else:
raise ValueError
@staticmethod
def routerid(node):
def router_id(node: CoreNode) -> str:
"""
Helper to return the first IPv4 address of a node as its router ID.
"""
@ -40,15 +43,13 @@ class Bird(CoreService):
a = a.split("/")[0]
if netaddr.valid_ipv4(a):
return a
# raise ValueError, "no IPv4 address found for router ID"
return "0.0.0.0"
@classmethod
def generateBirdConf(cls, node):
def generate_bird_config(cls, node: CoreNode) -> str:
"""
Returns configuration file text. Other services that depend on bird
will have generatebirdifcconfig() and generatebirdconfig()
hooks that are invoked here.
will have hooks that are invoked here.
"""
cfg = """\
/* Main configuration file for BIRD. This is ony a template,
@ -75,15 +76,16 @@ protocol device {
""" % (
cls.name,
cls.routerid(node),
cls.router_id(node),
)
# Generate protocol specific configurations
# generate protocol specific configurations
for s in node.services:
if cls.name not in s.dependencies:
continue
if not (isinstance(s, BirdService) or issubclass(s, BirdService)):
continue
cfg += s.generate_bird_config(node)
return cfg
@ -93,32 +95,26 @@ class BirdService(CoreService):
common to Bird's routing daemons.
"""
name = None
executables = ("bird",)
group = "BIRD"
dependencies = ("bird",)
dirs = ()
configs = ()
startup = ()
shutdown = ()
meta = "The config file for this service can be found in the bird service."
name: Optional[str] = None
group: str = "BIRD"
executables: Tuple[str, ...] = ("bird",)
dependencies: Tuple[str, ...] = ("bird",)
meta: str = "The config file for this service can be found in the bird service."
@classmethod
def generate_bird_config(cls, node):
def generate_bird_config(cls, node: CoreNode) -> str:
return ""
@classmethod
def generate_bird_iface_config(cls, node):
def generate_bird_iface_config(cls, node: CoreNode) -> str:
"""
Use only bare interfaces descriptions in generated protocol
configurations. This has the slight advantage of being the same
everywhere.
"""
cfg = ""
for iface in node.get_ifaces(control=False):
cfg += ' interface "%s";\n' % iface.name
return cfg
@ -127,11 +123,11 @@ class BirdBgp(BirdService):
BGP BIRD Service (configuration generation)
"""
name = "BIRD_BGP"
custom_needed = True
name: str = "BIRD_BGP"
custom_needed: bool = True
@classmethod
def generate_bird_config(cls, node):
def generate_bird_config(cls, node: CoreNode) -> str:
return """
/* This is a sample config that should be customized with appropriate AS numbers
* and peers; add one section like this for each neighbor */
@ -158,10 +154,10 @@ class BirdOspf(BirdService):
OSPF BIRD Service (configuration generation)
"""
name = "BIRD_OSPFv2"
name: str = "BIRD_OSPFv2"
@classmethod
def generate_bird_config(cls, node):
def generate_bird_config(cls, node: CoreNode) -> str:
cfg = "protocol ospf {\n"
cfg += " export filter {\n"
cfg += " if source = RTS_BGP then {\n"
@ -174,7 +170,6 @@ class BirdOspf(BirdService):
cfg += cls.generate_bird_iface_config(node)
cfg += " };\n"
cfg += "}\n\n"
return cfg
@ -183,12 +178,11 @@ class BirdRadv(BirdService):
RADV BIRD Service (configuration generation)
"""
name = "BIRD_RADV"
name: str = "BIRD_RADV"
@classmethod
def generate_bird_config(cls, node):
def generate_bird_config(cls, node: CoreNode) -> str:
cfg = "/* This is a sample config that must be customized */\n"
cfg += "protocol radv {\n"
cfg += " # auto configuration on all interfaces\n"
cfg += cls.generate_bird_iface_config(node)
@ -202,7 +196,6 @@ class BirdRadv(BirdService):
cfg += "# ns 2001:0DB8:1234::12;\n"
cfg += " };\n"
cfg += "}\n\n"
return cfg
@ -211,10 +204,10 @@ class BirdRip(BirdService):
RIP BIRD Service (configuration generation)
"""
name = "BIRD_RIP"
name: str = "BIRD_RIP"
@classmethod
def generate_bird_config(cls, node):
def generate_bird_config(cls, node: CoreNode) -> str:
cfg = "protocol rip {\n"
cfg += " period 10;\n"
cfg += " garbage time 60;\n"
@ -224,7 +217,6 @@ class BirdRip(BirdService):
cfg += " import all;\n"
cfg += " export all;\n"
cfg += "}\n\n"
return cfg
@ -233,11 +225,11 @@ class BirdStatic(BirdService):
Static Bird Service (configuration generation)
"""
name = "BIRD_static"
custom_needed = True
name: str = "BIRD_static"
custom_needed: bool = True
@classmethod
def generate_bird_config(cls, node):
def generate_bird_config(cls, node: CoreNode) -> str:
cfg = "/* This is a sample config that must be customized */\n"
cfg += "protocol static {\n"
cfg += "# route 0.0.0.0/0 via 198.51.100.130; # Default route. Do NOT advertise on BGP !\n"

View file

@ -1,23 +1,26 @@
from typing import Tuple
from core.emane.nodes import EmaneNet
from core.errors import CoreError
from core.nodes.base import CoreNode
from core.services.coreservices import CoreService
from core.xml import emanexml
class EmaneTransportService(CoreService):
name = "transportd"
executables = ("emanetransportd", "emanegentransportxml")
group = "EMANE"
dependencies = ()
dirs = ()
configs = ("emanetransport.sh",)
startup = ("sh %s" % configs[0],)
validate = ("pidof %s" % executables[0],)
validation_timer = 0.5
shutdown = ("killall %s" % executables[0],)
name: str = "transportd"
group: str = "EMANE"
executables: Tuple[str, ...] = ("emanetransportd", "emanegentransportxml")
dependencies: Tuple[str, ...] = ()
dirs: Tuple[str, ...] = ()
configs: Tuple[str, ...] = ("emanetransport.sh",)
startup: Tuple[str, ...] = ("sh %s" % configs[0],)
validate: Tuple[str, ...] = ("pidof %s" % executables[0],)
validation_timer: float = 0.5
shutdown: Tuple[str, ...] = ("killall %s" % executables[0],)
@classmethod
def generate_config(cls, node, filename):
def generate_config(cls, node: CoreNode, filename: str) -> str:
if filename == cls.configs[0]:
transport_commands = []
for iface in node.get_ifaces():

View file

@ -2,60 +2,63 @@
frr.py: defines routing services provided by FRRouting.
Assumes installation of FRR via https://deb.frrouting.org/
"""
from typing import Optional, Tuple
import netaddr
from core import constants
from core.emane.nodes import EmaneNet
from core.nodes.base import CoreNode
from core.nodes.interface import CoreInterface
from core.nodes.network import PtpNet, WlanNode
from core.nodes.physical import Rj45Node
from core.services.coreservices import CoreService
class FRRZebra(CoreService):
name = "FRRzebra"
group = "FRR"
dirs = ("/usr/local/etc/frr", "/var/run/frr", "/var/log/frr")
configs = (
name: str = "FRRzebra"
group: str = "FRR"
dirs: Tuple[str, ...] = ("/usr/local/etc/frr", "/var/run/frr", "/var/log/frr")
configs: Tuple[str, ...] = (
"/usr/local/etc/frr/frr.conf",
"frrboot.sh",
"/usr/local/etc/frr/vtysh.conf",
"/usr/local/etc/frr/daemons",
)
startup = ("sh frrboot.sh zebra",)
shutdown = ("killall zebra",)
validate = ("pidof zebra",)
startup: Tuple[str, ...] = ("sh frrboot.sh zebra",)
shutdown: Tuple[str, ...] = ("killall zebra",)
validate: Tuple[str, ...] = ("pidof zebra",)
@classmethod
def generate_config(cls, node, filename):
def generate_config(cls, node: CoreNode, filename: str) -> str:
"""
Return the frr.conf or frrboot.sh file contents.
"""
if filename == cls.configs[0]:
return cls.generateFrrConf(node)
return cls.generate_frr_conf(node)
elif filename == cls.configs[1]:
return cls.generateFrrBoot(node)
return cls.generate_frr_boot(node)
elif filename == cls.configs[2]:
return cls.generateVtyshConf(node)
return cls.generate_vtysh_conf(node)
elif filename == cls.configs[3]:
return cls.generateFrrDaemons(node)
return cls.generate_frr_daemons(node)
else:
raise ValueError(
"file name (%s) is not a known configuration: %s", filename, cls.configs
)
@classmethod
def generateVtyshConf(cls, node):
def generate_vtysh_conf(cls, node: CoreNode) -> str:
"""
Returns configuration file text.
"""
return "service integrated-vtysh-config\n"
@classmethod
def generateFrrConf(cls, node):
def generate_frr_conf(cls, node: CoreNode) -> str:
"""
Returns configuration file text. Other services that depend on zebra
will have generatefrrifcconfig() and generatefrrconfig()
hooks that are invoked here.
will have hooks that are invoked here.
"""
# we could verify here that filename == frr.conf
cfg = ""
@ -108,7 +111,7 @@ class FRRZebra(CoreService):
return cfg
@staticmethod
def addrstr(x):
def addrstr(x: str) -> str:
"""
helper for mapping IP addresses to zebra config statements
"""
@ -121,7 +124,7 @@ class FRRZebra(CoreService):
raise ValueError("invalid address: %s", x)
@classmethod
def generateFrrBoot(cls, node):
def generate_frr_boot(cls, node: CoreNode) -> str:
"""
Generate a shell script used to boot the FRR daemons.
"""
@ -244,7 +247,7 @@ bootfrr
return cfg
@classmethod
def generateFrrDaemons(cls, node):
def generate_frr_daemons(cls, node: CoreNode) -> str:
"""
Returns configuration file text.
"""
@ -317,20 +320,15 @@ class FrrService(CoreService):
common to FRR's routing daemons.
"""
name = None
group = "FRR"
dependencies = ("FRRzebra",)
dirs = ()
configs = ()
startup = ()
shutdown = ()
meta = "The config file for this service can be found in the Zebra service."
ipv4_routing = False
ipv6_routing = False
name: Optional[str] = None
group: str = "FRR"
dependencies: Tuple[str, ...] = ("FRRzebra",)
meta: str = "The config file for this service can be found in the Zebra service."
ipv4_routing: bool = False
ipv6_routing: bool = False
@staticmethod
def routerid(node):
def router_id(node: CoreNode) -> str:
"""
Helper to return the first IPv4 address of a node as its router ID.
"""
@ -339,11 +337,10 @@ class FrrService(CoreService):
a = a.split("/")[0]
if netaddr.valid_ipv4(a):
return a
# raise ValueError, "no IPv4 address found for router ID"
return "0.0.0.0"
@staticmethod
def rj45check(iface):
def rj45check(iface: CoreInterface) -> bool:
"""
Helper to detect whether interface is connected an external RJ45
link.
@ -357,15 +354,15 @@ class FrrService(CoreService):
return False
@classmethod
def generate_config(cls, node, filename):
def generate_config(cls, node: CoreNode, filename: str) -> str:
return ""
@classmethod
def generate_frr_iface_config(cls, node, iface):
def generate_frr_iface_config(cls, node: CoreNode, iface: CoreInterface) -> str:
return ""
@classmethod
def generate_frr_config(cls, node):
def generate_frr_config(cls, node: CoreNode) -> str:
return ""
@ -376,14 +373,13 @@ class FRROspfv2(FrrService):
unified frr.conf file.
"""
name = "FRROSPFv2"
startup = ()
shutdown = ("killall ospfd",)
validate = ("pidof ospfd",)
ipv4_routing = True
name: str = "FRROSPFv2"
shutdown: Tuple[str, ...] = ("killall ospfd",)
validate: Tuple[str, ...] = ("pidof ospfd",)
ipv4_routing: bool = True
@staticmethod
def mtucheck(iface):
def mtu_check(iface: CoreInterface) -> str:
"""
Helper to detect MTU mismatch and add the appropriate OSPF
mtu-ignore command. This is needed when e.g. a node is linked via a
@ -401,7 +397,7 @@ class FRROspfv2(FrrService):
return ""
@staticmethod
def ptpcheck(iface):
def ptp_check(iface: CoreInterface) -> str:
"""
Helper to detect whether interface is connected to a notional
point-to-point link.
@ -411,9 +407,9 @@ class FRROspfv2(FrrService):
return ""
@classmethod
def generate_frr_config(cls, node):
def generate_frr_config(cls, node: CoreNode) -> str:
cfg = "router ospf\n"
rtrid = cls.routerid(node)
rtrid = cls.router_id(node)
cfg += " router-id %s\n" % rtrid
# network 10.0.0.0/24 area 0
for iface in node.get_ifaces(control=False):
@ -426,8 +422,8 @@ class FRROspfv2(FrrService):
return cfg
@classmethod
def generate_frr_iface_config(cls, node, iface):
return cls.mtucheck(iface)
def generate_frr_iface_config(cls, node: CoreNode, iface: CoreInterface) -> str:
return cls.mtu_check(iface)
class FRROspfv3(FrrService):
@ -437,15 +433,14 @@ class FRROspfv3(FrrService):
unified frr.conf file.
"""
name = "FRROSPFv3"
startup = ()
shutdown = ("killall ospf6d",)
validate = ("pidof ospf6d",)
ipv4_routing = True
ipv6_routing = True
name: str = "FRROSPFv3"
shutdown: Tuple[str, ...] = ("killall ospf6d",)
validate: Tuple[str, ...] = ("pidof ospf6d",)
ipv4_routing: bool = True
ipv6_routing: bool = True
@staticmethod
def minmtu(iface):
def min_mtu(iface: CoreInterface) -> int:
"""
Helper to discover the minimum MTU of interfaces linked with the
given interface.
@ -459,20 +454,20 @@ class FRROspfv3(FrrService):
return mtu
@classmethod
def mtucheck(cls, iface):
def mtu_check(cls, iface: CoreInterface) -> str:
"""
Helper to detect MTU mismatch and add the appropriate OSPFv3
ifmtu command. This is needed when e.g. a node is linked via a
GreTap device.
"""
minmtu = cls.minmtu(iface)
minmtu = cls.min_mtu(iface)
if minmtu < iface.mtu:
return " ipv6 ospf6 ifmtu %d\n" % minmtu
else:
return ""
@staticmethod
def ptpcheck(iface):
def ptp_check(iface: CoreInterface) -> str:
"""
Helper to detect whether interface is connected to a notional
point-to-point link.
@ -482,9 +477,9 @@ class FRROspfv3(FrrService):
return ""
@classmethod
def generate_frr_config(cls, node):
def generate_frr_config(cls, node: CoreNode) -> str:
cfg = "router ospf6\n"
rtrid = cls.routerid(node)
rtrid = cls.router_id(node)
cfg += " router-id %s\n" % rtrid
for iface in node.get_ifaces(control=False):
cfg += " interface %s area 0.0.0.0\n" % iface.name
@ -492,14 +487,13 @@ class FRROspfv3(FrrService):
return cfg
@classmethod
def generate_frr_iface_config(cls, node, iface):
return cls.mtucheck(iface)
def generate_frr_iface_config(cls, node: CoreNode, iface: CoreInterface) -> str:
return cls.mtu_check(iface)
# cfg = cls.mtucheck(ifc)
# external RJ45 connections will use default OSPF timers
# if cls.rj45check(ifc):
# return cfg
# cfg += cls.ptpcheck(ifc)
# return cfg + """\
@ -516,21 +510,20 @@ class FRRBgp(FrrService):
having the same AS number.
"""
name = "FRRBGP"
startup = ()
shutdown = ("killall bgpd",)
validate = ("pidof bgpd",)
custom_needed = True
ipv4_routing = True
ipv6_routing = True
name: str = "FRRBGP"
shutdown: Tuple[str, ...] = ("killall bgpd",)
validate: Tuple[str, ...] = ("pidof bgpd",)
custom_needed: bool = True
ipv4_routing: bool = True
ipv6_routing: bool = True
@classmethod
def generate_frr_config(cls, node):
def generate_frr_config(cls, node: CoreNode) -> str:
cfg = "!\n! BGP configuration\n!\n"
cfg += "! You should configure the AS number below,\n"
cfg += "! along with this router's peers.\n!\n"
cfg += "router bgp %s\n" % node.id
rtrid = cls.routerid(node)
rtrid = cls.router_id(node)
cfg += " bgp router-id %s\n" % rtrid
cfg += " redistribute connected\n"
cfg += "! neighbor 1.2.3.4 remote-as 555\n!\n"
@ -542,14 +535,13 @@ class FRRRip(FrrService):
The RIP service provides IPv4 routing for wired networks.
"""
name = "FRRRIP"
startup = ()
shutdown = ("killall ripd",)
validate = ("pidof ripd",)
ipv4_routing = True
name: str = "FRRRIP"
shutdown: Tuple[str, ...] = ("killall ripd",)
validate: Tuple[str, ...] = ("pidof ripd",)
ipv4_routing: bool = True
@classmethod
def generate_frr_config(cls, node):
def generate_frr_config(cls, node: CoreNode) -> str:
cfg = """\
router rip
redistribute static
@ -566,14 +558,13 @@ class FRRRipng(FrrService):
The RIP NG service provides IPv6 routing for wired networks.
"""
name = "FRRRIPNG"
startup = ()
shutdown = ("killall ripngd",)
validate = ("pidof ripngd",)
ipv6_routing = True
name: str = "FRRRIPNG"
shutdown: Tuple[str, ...] = ("killall ripngd",)
validate: Tuple[str, ...] = ("pidof ripngd",)
ipv6_routing: bool = True
@classmethod
def generate_frr_config(cls, node):
def generate_frr_config(cls, node: CoreNode) -> str:
cfg = """\
router ripng
redistribute static
@ -591,14 +582,13 @@ class FRRBabel(FrrService):
protocol for IPv6 and IPv4 with fast convergence properties.
"""
name = "FRRBabel"
startup = ()
shutdown = ("killall babeld",)
validate = ("pidof babeld",)
ipv6_routing = True
name: str = "FRRBabel"
shutdown: Tuple[str, ...] = ("killall babeld",)
validate: Tuple[str, ...] = ("pidof babeld",)
ipv6_routing: bool = True
@classmethod
def generate_frr_config(cls, node):
def generate_frr_config(cls, node: CoreNode) -> str:
cfg = "router babel\n"
for iface in node.get_ifaces(control=False):
cfg += " network %s\n" % iface.name
@ -606,7 +596,7 @@ class FRRBabel(FrrService):
return cfg
@classmethod
def generate_frr_iface_config(cls, node, iface):
def generate_frr_iface_config(cls, node: CoreNode, iface: CoreInterface) -> str:
if iface.net and isinstance(iface.net, (EmaneNet, WlanNode)):
return " babel wireless\n no babel split-horizon\n"
else:
@ -618,14 +608,13 @@ class FRRpimd(FrrService):
PIM multicast routing based on XORP.
"""
name = "FRRpimd"
startup = ()
shutdown = ("killall pimd",)
validate = ("pidof pimd",)
ipv4_routing = True
name: str = "FRRpimd"
shutdown: Tuple[str, ...] = ("killall pimd",)
validate: Tuple[str, ...] = ("pidof pimd",)
ipv4_routing: bool = True
@classmethod
def generate_frr_config(cls, node):
def generate_frr_config(cls, node: CoreNode) -> str:
ifname = "eth0"
for iface in node.get_ifaces():
if iface.name != "lo":
@ -641,7 +630,7 @@ class FRRpimd(FrrService):
return cfg
@classmethod
def generate_frr_iface_config(cls, node, iface):
def generate_frr_iface_config(cls, node: CoreNode, iface: CoreInterface) -> str:
return " ip mfea\n ip igmp\n ip pim\n"
@ -652,15 +641,14 @@ class FRRIsis(FrrService):
unified frr.conf file.
"""
name = "FRRISIS"
startup = ()
shutdown = ("killall isisd",)
validate = ("pidof isisd",)
ipv4_routing = True
ipv6_routing = True
name: str = "FRRISIS"
shutdown: Tuple[str, ...] = ("killall isisd",)
validate: Tuple[str, ...] = ("pidof isisd",)
ipv4_routing: bool = True
ipv6_routing: bool = True
@staticmethod
def ptpcheck(iface):
def ptp_check(iface: CoreInterface) -> str:
"""
Helper to detect whether interface is connected to a notional
point-to-point link.
@ -670,7 +658,7 @@ class FRRIsis(FrrService):
return ""
@classmethod
def generate_frr_config(cls, node):
def generate_frr_config(cls, node: CoreNode) -> str:
cfg = "router isis DEFAULT\n"
cfg += " net 47.0001.0000.1900.%04x.00\n" % node.id
cfg += " metric-style wide\n"
@ -679,9 +667,9 @@ class FRRIsis(FrrService):
return cfg
@classmethod
def generate_frr_iface_config(cls, node, iface):
def generate_frr_iface_config(cls, node: CoreNode, iface: CoreInterface) -> str:
cfg = " ip router isis DEFAULT\n"
cfg += " ipv6 router isis DEFAULT\n"
cfg += " isis circuit-type level-2-only\n"
cfg += cls.ptpcheck(iface)
cfg += cls.ptp_check(iface)
return cfg

View file

@ -2,9 +2,12 @@
nrl.py: defines services provided by NRL protolib tools hosted here:
http://www.nrl.navy.mil/itd/ncs/products
"""
from typing import Optional, Tuple
import netaddr
from core import utils
from core.nodes.base import CoreNode
from core.services.coreservices import CoreService
@ -14,19 +17,15 @@ class NrlService(CoreService):
common to NRL's routing daemons.
"""
name = None
group = "ProtoSvc"
dirs = ()
configs = ()
startup = ()
shutdown = ()
name: Optional[str] = None
group: str = "ProtoSvc"
@classmethod
def generate_config(cls, node, filename):
def generate_config(cls, node: CoreNode, filename: str) -> str:
return ""
@staticmethod
def firstipv4prefix(node, prefixlen=24):
def firstipv4prefix(node: CoreNode, prefixlen: int = 24) -> str:
"""
Similar to QuaggaService.routerid(). Helper to return the first IPv4
prefix of a node, using the supplied prefix length. This ignores the
@ -37,20 +36,19 @@ class NrlService(CoreService):
a = a.split("/")[0]
if netaddr.valid_ipv4(a):
return f"{a}/{prefixlen}"
# raise ValueError, "no IPv4 address found"
return "0.0.0.0/%s" % prefixlen
class MgenSinkService(NrlService):
name = "MGEN_Sink"
executables = ("mgen",)
configs = ("sink.mgen",)
startup = ("mgen input sink.mgen",)
validate = ("pidof mgen",)
shutdown = ("killall mgen",)
name: str = "MGEN_Sink"
executables: Tuple[str, ...] = ("mgen",)
configs: Tuple[str, ...] = ("sink.mgen",)
startup: Tuple[str, ...] = ("mgen input sink.mgen",)
validate: Tuple[str, ...] = ("pidof mgen",)
shutdown: Tuple[str, ...] = ("killall mgen",)
@classmethod
def generate_config(cls, node, filename):
def generate_config(cls, node: CoreNode, filename: str) -> str:
cfg = "0.0 LISTEN UDP 5000\n"
for iface in node.get_ifaces():
name = utils.sysctl_devname(iface.name)
@ -58,7 +56,7 @@ class MgenSinkService(NrlService):
return cfg
@classmethod
def get_startup(cls, node):
def get_startup(cls, node: CoreNode) -> Tuple[str, ...]:
cmd = cls.startup[0]
cmd += " output /tmp/mgen_%s.log" % node.name
return (cmd,)
@ -69,32 +67,29 @@ class NrlNhdp(NrlService):
NeighborHood Discovery Protocol for MANET networks.
"""
name = "NHDP"
executables = ("nrlnhdp",)
startup = ("nrlnhdp",)
shutdown = ("killall nrlnhdp",)
validate = ("pidof nrlnhdp",)
name: str = "NHDP"
executables: Tuple[str, ...] = ("nrlnhdp",)
startup: Tuple[str, ...] = ("nrlnhdp",)
shutdown: Tuple[str, ...] = ("killall nrlnhdp",)
validate: Tuple[str, ...] = ("pidof nrlnhdp",)
@classmethod
def get_startup(cls, node):
def get_startup(cls, node: CoreNode) -> Tuple[str, ...]:
"""
Generate the appropriate command-line based on node interfaces.
"""
cmd = cls.startup[0]
cmd += " -l /var/log/nrlnhdp.log"
cmd += " -rpipe %s_nhdp" % node.name
servicenames = map(lambda x: x.name, node.services)
if "SMF" in servicenames:
cmd += " -flooding ecds"
cmd += " -smfClient %s_smf" % node.name
ifaces = node.get_ifaces(control=False)
if len(ifaces) > 0:
iface_names = map(lambda x: x.name, ifaces)
cmd += " -i "
cmd += " -i ".join(iface_names)
return (cmd,)
@ -103,15 +98,15 @@ class NrlSmf(NrlService):
Simplified Multicast Forwarding for MANET networks.
"""
name = "SMF"
executables = ("nrlsmf",)
startup = ("sh startsmf.sh",)
shutdown = ("killall nrlsmf",)
validate = ("pidof nrlsmf",)
configs = ("startsmf.sh",)
name: str = "SMF"
executables: Tuple[str, ...] = ("nrlsmf",)
startup: Tuple[str, ...] = ("sh startsmf.sh",)
shutdown: Tuple[str, ...] = ("killall nrlsmf",)
validate: Tuple[str, ...] = ("pidof nrlsmf",)
configs: Tuple[str, ...] = ("startsmf.sh",)
@classmethod
def generate_config(cls, node, filename):
def generate_config(cls, node: CoreNode, filename: str) -> str:
"""
Generate a startup script for SMF. Because nrlsmf does not
daemonize, it can cause problems in some situations when launched
@ -146,7 +141,6 @@ class NrlSmf(NrlService):
cmd += " hash MD5"
cmd += " log /var/log/nrlsmf.log"
cfg += comments + cmd + " < /dev/null > /dev/null 2>&1 &\n\n"
return cfg
@ -156,14 +150,14 @@ class NrlOlsr(NrlService):
Optimized Link State Routing protocol for MANET networks.
"""
name = "OLSR"
executables = ("nrlolsrd",)
startup = ("nrlolsrd",)
shutdown = ("killall nrlolsrd",)
validate = ("pidof nrlolsrd",)
name: str = "OLSR"
executables: Tuple[str, ...] = ("nrlolsrd",)
startup: Tuple[str, ...] = ("nrlolsrd",)
shutdown: Tuple[str, ...] = ("killall nrlolsrd",)
validate: Tuple[str, ...] = ("pidof nrlolsrd",)
@classmethod
def get_startup(cls, node):
def get_startup(cls, node: CoreNode) -> Tuple[str, ...]:
"""
Generate the appropriate command-line based on node interfaces.
"""
@ -175,14 +169,12 @@ class NrlOlsr(NrlService):
cmd += " -i %s" % iface.name
cmd += " -l /var/log/nrlolsrd.log"
cmd += " -rpipe %s_olsr" % node.name
servicenames = map(lambda x: x.name, node.services)
if "SMF" in servicenames and "NHDP" not in servicenames:
cmd += " -flooding s-mpr"
cmd += " -smfClient %s_smf" % node.name
if "zebra" in servicenames:
cmd += " -z"
return (cmd,)
@ -191,34 +183,30 @@ class NrlOlsrv2(NrlService):
Optimized Link State Routing protocol version 2 for MANET networks.
"""
name = "OLSRv2"
executables = ("nrlolsrv2",)
startup = ("nrlolsrv2",)
shutdown = ("killall nrlolsrv2",)
validate = ("pidof nrlolsrv2",)
name: str = "OLSRv2"
executables: Tuple[str, ...] = ("nrlolsrv2",)
startup: Tuple[str, ...] = ("nrlolsrv2",)
shutdown: Tuple[str, ...] = ("killall nrlolsrv2",)
validate: Tuple[str, ...] = ("pidof nrlolsrv2",)
@classmethod
def get_startup(cls, node):
def get_startup(cls, node: CoreNode) -> Tuple[str, ...]:
"""
Generate the appropriate command-line based on node interfaces.
"""
cmd = cls.startup[0]
cmd += " -l /var/log/nrlolsrv2.log"
cmd += " -rpipe %s_olsrv2" % node.name
servicenames = map(lambda x: x.name, node.services)
if "SMF" in servicenames:
cmd += " -flooding ecds"
cmd += " -smfClient %s_smf" % node.name
cmd += " -p olsr"
ifaces = node.get_ifaces(control=False)
if len(ifaces) > 0:
iface_names = map(lambda x: x.name, ifaces)
cmd += " -i "
cmd += " -i ".join(iface_names)
return (cmd,)
@ -227,16 +215,16 @@ class OlsrOrg(NrlService):
Optimized Link State Routing protocol from olsr.org for MANET networks.
"""
name = "OLSRORG"
executables = ("olsrd",)
configs = ("/etc/olsrd/olsrd.conf",)
dirs = ("/etc/olsrd",)
startup = ("olsrd",)
shutdown = ("killall olsrd",)
validate = ("pidof olsrd",)
name: str = "OLSRORG"
executables: Tuple[str, ...] = ("olsrd",)
configs: Tuple[str, ...] = ("/etc/olsrd/olsrd.conf",)
dirs: Tuple[str, ...] = ("/etc/olsrd",)
startup: Tuple[str, ...] = ("olsrd",)
shutdown: Tuple[str, ...] = ("killall olsrd",)
validate: Tuple[str, ...] = ("pidof olsrd",)
@classmethod
def get_startup(cls, node):
def get_startup(cls, node: CoreNode) -> Tuple[str, ...]:
"""
Generate the appropriate command-line based on node interfaces.
"""
@ -246,13 +234,13 @@ class OlsrOrg(NrlService):
iface_names = map(lambda x: x.name, ifaces)
cmd += " -i "
cmd += " -i ".join(iface_names)
return (cmd,)
@classmethod
def generate_config(cls, node, filename):
def generate_config(cls, node: CoreNode, filename: str) -> str:
"""
Generate a default olsrd config file to use the broadcast address of 255.255.255.255.
Generate a default olsrd config file to use the broadcast address of
255.255.255.255.
"""
cfg = """\
#
@ -577,24 +565,16 @@ class MgenActor(NrlService):
"""
# a unique name is required, without spaces
name = "MgenActor"
executables = ("mgen",)
# you can create your own group here
group = "ProtoSvc"
# per-node directories
dirs = ()
# generated files (without a full path this file goes in the node's dir,
# e.g. /tmp/pycore.12345/n1.conf/)
configs = ("start_mgen_actor.sh",)
# list of startup commands, also may be generated during startup
startup = ("sh start_mgen_actor.sh",)
# list of validation commands
validate = ("pidof mgen",)
# list of shutdown commands
shutdown = ("killall mgen",)
name: str = "MgenActor"
group: str = "ProtoSvc"
executables: Tuple[str, ...] = ("mgen",)
configs: Tuple[str, ...] = ("start_mgen_actor.sh",)
startup: Tuple[str, ...] = ("sh start_mgen_actor.sh",)
validate: Tuple[str, ...] = ("pidof mgen",)
shutdown: Tuple[str, ...] = ("killall mgen",)
@classmethod
def generate_config(cls, node, filename):
def generate_config(cls, node: CoreNode, filename: str) -> str:
"""
Generate a startup script for MgenActor. Because mgenActor does not
daemonize, it can cause problems in some situations when launched
@ -604,11 +584,9 @@ class MgenActor(NrlService):
cfg += "# auto-generated by nrl.py:MgenActor.generateconfig()\n"
comments = ""
cmd = "mgenBasicActor.py -n %s -a 0.0.0.0" % node.name
ifaces = node.get_ifaces(control=False)
if len(ifaces) == 0:
return ""
cfg += comments + cmd + " < /dev/null > /dev/null 2>&1 &\n\n"
return cfg
@ -618,15 +596,15 @@ class Arouted(NrlService):
Adaptive Routing
"""
name = "arouted"
executables = ("arouted",)
configs = ("startarouted.sh",)
startup = ("sh startarouted.sh",)
shutdown = ("pkill arouted",)
validate = ("pidof arouted",)
name: str = "arouted"
executables: Tuple[str, ...] = ("arouted",)
configs: Tuple[str, ...] = ("startarouted.sh",)
startup: Tuple[str, ...] = ("sh startarouted.sh",)
shutdown: Tuple[str, ...] = ("pkill arouted",)
validate: Tuple[str, ...] = ("pidof arouted",)
@classmethod
def generate_config(cls, node, filename):
def generate_config(cls, node: CoreNode, filename: str) -> str:
"""
Return the Quagga.conf or quaggaboot.sh file contents.
"""

View file

@ -1,65 +1,68 @@
"""
quagga.py: defines routing services provided by Quagga.
"""
from typing import Optional, Tuple
import netaddr
from core import constants
from core.emane.nodes import EmaneNet
from core.emulator.enumerations import LinkTypes
from core.nodes.base import CoreNode
from core.nodes.interface import CoreInterface
from core.nodes.network import PtpNet, WlanNode
from core.nodes.physical import Rj45Node
from core.services.coreservices import CoreService
class Zebra(CoreService):
name = "zebra"
group = "Quagga"
dirs = ("/usr/local/etc/quagga", "/var/run/quagga")
configs = (
name: str = "zebra"
group: str = "Quagga"
dirs: Tuple[str, ...] = ("/usr/local/etc/quagga", "/var/run/quagga")
configs: Tuple[str, ...] = (
"/usr/local/etc/quagga/Quagga.conf",
"quaggaboot.sh",
"/usr/local/etc/quagga/vtysh.conf",
)
startup = ("sh quaggaboot.sh zebra",)
shutdown = ("killall zebra",)
validate = ("pidof zebra",)
startup: Tuple[str, ...] = ("sh quaggaboot.sh zebra",)
shutdown: Tuple[str, ...] = ("killall zebra",)
validate: Tuple[str, ...] = ("pidof zebra",)
@classmethod
def generate_config(cls, node, filename):
def generate_config(cls, node: CoreNode, filename: str) -> str:
"""
Return the Quagga.conf or quaggaboot.sh file contents.
"""
if filename == cls.configs[0]:
return cls.generateQuaggaConf(node)
return cls.generate_quagga_conf(node)
elif filename == cls.configs[1]:
return cls.generateQuaggaBoot(node)
return cls.generate_quagga_boot(node)
elif filename == cls.configs[2]:
return cls.generateVtyshConf(node)
return cls.generate_vtysh_conf(node)
else:
raise ValueError(
"file name (%s) is not a known configuration: %s", filename, cls.configs
)
@classmethod
def generateVtyshConf(cls, node):
def generate_vtysh_conf(cls, node: CoreNode) -> str:
"""
Returns configuration file text.
"""
return "service integrated-vtysh-config\n"
@classmethod
def generateQuaggaConf(cls, node):
def generate_quagga_conf(cls, node: CoreNode) -> str:
"""
Returns configuration file text. Other services that depend on zebra
will have generatequaggaifcconfig() and generatequaggaconfig()
hooks that are invoked here.
will have hooks that are invoked here.
"""
# we could verify here that filename == Quagga.conf
cfg = ""
for iface in node.get_ifaces():
cfg += "interface %s\n" % iface.name
# include control interfaces in addressing but not routing daemons
if hasattr(iface, "control") and iface.control is True:
if getattr(iface, "control", False):
cfg += " "
cfg += "\n ".join(map(cls.addrstr, iface.addrlist))
cfg += "\n"
@ -71,6 +74,8 @@ class Zebra(CoreService):
for s in node.services:
if cls.name not in s.dependencies:
continue
if not (isinstance(s, QuaggaService) or issubclass(s, QuaggaService)):
continue
iface_config = s.generate_quagga_iface_config(node, iface)
if s.ipv4_routing:
want_ipv4 = True
@ -101,11 +106,13 @@ class Zebra(CoreService):
for s in node.services:
if cls.name not in s.dependencies:
continue
if not (isinstance(s, QuaggaService) or issubclass(s, QuaggaService)):
continue
cfg += s.generate_quagga_config(node)
return cfg
@staticmethod
def addrstr(x):
def addrstr(x: str) -> str:
"""
helper for mapping IP addresses to zebra config statements
"""
@ -118,7 +125,7 @@ class Zebra(CoreService):
raise ValueError("invalid address: %s", x)
@classmethod
def generateQuaggaBoot(cls, node):
def generate_quagga_boot(cls, node: CoreNode) -> str:
"""
Generate a shell script used to boot the Quagga daemons.
"""
@ -235,20 +242,15 @@ class QuaggaService(CoreService):
common to Quagga's routing daemons.
"""
name = None
group = "Quagga"
dependencies = ("zebra",)
dirs = ()
configs = ()
startup = ()
shutdown = ()
meta = "The config file for this service can be found in the Zebra service."
ipv4_routing = False
ipv6_routing = False
name: Optional[str] = None
group: str = "Quagga"
dependencies: Tuple[str, ...] = (Zebra.name,)
meta: str = "The config file for this service can be found in the Zebra service."
ipv4_routing: bool = False
ipv6_routing: bool = False
@staticmethod
def routerid(node):
def router_id(node: CoreNode) -> str:
"""
Helper to return the first IPv4 address of a node as its router ID.
"""
@ -257,11 +259,10 @@ class QuaggaService(CoreService):
a = a.split("/")[0]
if netaddr.valid_ipv4(a):
return a
# raise ValueError, "no IPv4 address found for router ID"
return "0.0.0.%d" % node.id
return f"0.0.0.{node.id:d}"
@staticmethod
def rj45check(iface):
def rj45check(iface: CoreInterface) -> bool:
"""
Helper to detect whether interface is connected an external RJ45
link.
@ -275,15 +276,15 @@ class QuaggaService(CoreService):
return False
@classmethod
def generate_config(cls, node, filename):
def generate_config(cls, node: CoreNode, filename: str) -> str:
return ""
@classmethod
def generate_quagga_iface_config(cls, node, iface):
def generate_quagga_iface_config(cls, node: CoreNode, iface: CoreInterface) -> str:
return ""
@classmethod
def generate_quagga_config(cls, node):
def generate_quagga_config(cls, node: CoreNode) -> str:
return ""
@ -294,14 +295,13 @@ class Ospfv2(QuaggaService):
unified Quagga.conf file.
"""
name = "OSPFv2"
startup = ()
shutdown = ("killall ospfd",)
validate = ("pidof ospfd",)
ipv4_routing = True
name: str = "OSPFv2"
shutdown: Tuple[str, ...] = ("killall ospfd",)
validate: Tuple[str, ...] = ("pidof ospfd",)
ipv4_routing: bool = True
@staticmethod
def mtucheck(iface):
def mtu_check(iface: CoreInterface) -> str:
"""
Helper to detect MTU mismatch and add the appropriate OSPF
mtu-ignore command. This is needed when e.g. a node is linked via a
@ -319,7 +319,7 @@ class Ospfv2(QuaggaService):
return ""
@staticmethod
def ptpcheck(iface):
def ptp_check(iface: CoreInterface) -> str:
"""
Helper to detect whether interface is connected to a notional
point-to-point link.
@ -329,9 +329,9 @@ class Ospfv2(QuaggaService):
return ""
@classmethod
def generate_quagga_config(cls, node):
def generate_quagga_config(cls, node: CoreNode) -> str:
cfg = "router ospf\n"
rtrid = cls.routerid(node)
rtrid = cls.router_id(node)
cfg += " router-id %s\n" % rtrid
# network 10.0.0.0/24 area 0
for iface in node.get_ifaces(control=False):
@ -343,12 +343,12 @@ class Ospfv2(QuaggaService):
return cfg
@classmethod
def generate_quagga_iface_config(cls, node, iface):
cfg = cls.mtucheck(iface)
def generate_quagga_iface_config(cls, node: CoreNode, iface: CoreInterface) -> str:
cfg = cls.mtu_check(iface)
# external RJ45 connections will use default OSPF timers
if cls.rj45check(iface):
return cfg
cfg += cls.ptpcheck(iface)
cfg += cls.ptp_check(iface)
return (
cfg
+ """\
@ -366,15 +366,14 @@ class Ospfv3(QuaggaService):
unified Quagga.conf file.
"""
name = "OSPFv3"
startup = ()
shutdown = ("killall ospf6d",)
validate = ("pidof ospf6d",)
ipv4_routing = True
ipv6_routing = True
name: str = "OSPFv3"
shutdown: Tuple[str, ...] = ("killall ospf6d",)
validate: Tuple[str, ...] = ("pidof ospf6d",)
ipv4_routing: bool = True
ipv6_routing: bool = True
@staticmethod
def minmtu(iface):
def min_mtu(iface: CoreInterface) -> int:
"""
Helper to discover the minimum MTU of interfaces linked with the
given interface.
@ -388,20 +387,20 @@ class Ospfv3(QuaggaService):
return mtu
@classmethod
def mtucheck(cls, iface):
def mtu_check(cls, iface: CoreInterface) -> str:
"""
Helper to detect MTU mismatch and add the appropriate OSPFv3
ifmtu command. This is needed when e.g. a node is linked via a
GreTap device.
"""
minmtu = cls.minmtu(iface)
minmtu = cls.min_mtu(iface)
if minmtu < iface.mtu:
return " ipv6 ospf6 ifmtu %d\n" % minmtu
else:
return ""
@staticmethod
def ptpcheck(iface):
def ptp_check(iface: CoreInterface) -> str:
"""
Helper to detect whether interface is connected to a notional
point-to-point link.
@ -411,9 +410,9 @@ class Ospfv3(QuaggaService):
return ""
@classmethod
def generate_quagga_config(cls, node):
def generate_quagga_config(cls, node: CoreNode) -> str:
cfg = "router ospf6\n"
rtrid = cls.routerid(node)
rtrid = cls.router_id(node)
cfg += " instance-id 65\n"
cfg += " router-id %s\n" % rtrid
for iface in node.get_ifaces(control=False):
@ -422,8 +421,8 @@ class Ospfv3(QuaggaService):
return cfg
@classmethod
def generate_quagga_iface_config(cls, node, iface):
return cls.mtucheck(iface)
def generate_quagga_iface_config(cls, node: CoreNode, iface: CoreInterface) -> str:
return cls.mtu_check(iface)
class Ospfv3mdr(Ospfv3):
@ -434,12 +433,12 @@ class Ospfv3mdr(Ospfv3):
unified Quagga.conf file.
"""
name = "OSPFv3MDR"
ipv4_routing = True
name: str = "OSPFv3MDR"
ipv4_routing: bool = True
@classmethod
def generate_quagga_iface_config(cls, node, iface):
cfg = cls.mtucheck(iface)
def generate_quagga_iface_config(cls, node: CoreNode, iface: CoreInterface) -> str:
cfg = cls.mtu_check(iface)
if iface.net is not None and isinstance(iface.net, (WlanNode, EmaneNet)):
return (
cfg
@ -464,21 +463,20 @@ class Bgp(QuaggaService):
having the same AS number.
"""
name = "BGP"
startup = ()
shutdown = ("killall bgpd",)
validate = ("pidof bgpd",)
custom_needed = True
ipv4_routing = True
ipv6_routing = True
name: str = "BGP"
shutdown: Tuple[str, ...] = ("killall bgpd",)
validate: Tuple[str, ...] = ("pidof bgpd",)
custom_needed: bool = True
ipv4_routing: bool = True
ipv6_routing: bool = True
@classmethod
def generate_quagga_config(cls, node):
def generate_quagga_config(cls, node: CoreNode) -> str:
cfg = "!\n! BGP configuration\n!\n"
cfg += "! You should configure the AS number below,\n"
cfg += "! along with this router's peers.\n!\n"
cfg += "router bgp %s\n" % node.id
rtrid = cls.routerid(node)
rtrid = cls.router_id(node)
cfg += " bgp router-id %s\n" % rtrid
cfg += " redistribute connected\n"
cfg += "! neighbor 1.2.3.4 remote-as 555\n!\n"
@ -490,14 +488,13 @@ class Rip(QuaggaService):
The RIP service provides IPv4 routing for wired networks.
"""
name = "RIP"
startup = ()
shutdown = ("killall ripd",)
validate = ("pidof ripd",)
ipv4_routing = True
name: str = "RIP"
shutdown: Tuple[str, ...] = ("killall ripd",)
validate: Tuple[str, ...] = ("pidof ripd",)
ipv4_routing: bool = True
@classmethod
def generate_quagga_config(cls, node):
def generate_quagga_config(cls, node: CoreNode) -> str:
cfg = """\
router rip
redistribute static
@ -514,14 +511,13 @@ class Ripng(QuaggaService):
The RIP NG service provides IPv6 routing for wired networks.
"""
name = "RIPNG"
startup = ()
shutdown = ("killall ripngd",)
validate = ("pidof ripngd",)
ipv6_routing = True
name: str = "RIPNG"
shutdown: Tuple[str, ...] = ("killall ripngd",)
validate: Tuple[str, ...] = ("pidof ripngd",)
ipv6_routing: bool = True
@classmethod
def generate_quagga_config(cls, node):
def generate_quagga_config(cls, node: CoreNode) -> str:
cfg = """\
router ripng
redistribute static
@ -539,14 +535,13 @@ class Babel(QuaggaService):
protocol for IPv6 and IPv4 with fast convergence properties.
"""
name = "Babel"
startup = ()
shutdown = ("killall babeld",)
validate = ("pidof babeld",)
ipv6_routing = True
name: str = "Babel"
shutdown: Tuple[str, ...] = ("killall babeld",)
validate: Tuple[str, ...] = ("pidof babeld",)
ipv6_routing: bool = True
@classmethod
def generate_quagga_config(cls, node):
def generate_quagga_config(cls, node: CoreNode) -> str:
cfg = "router babel\n"
for iface in node.get_ifaces(control=False):
cfg += " network %s\n" % iface.name
@ -554,7 +549,7 @@ class Babel(QuaggaService):
return cfg
@classmethod
def generate_quagga_iface_config(cls, node, iface):
def generate_quagga_iface_config(cls, node: CoreNode, iface: CoreInterface) -> str:
if iface.net and iface.net.linktype == LinkTypes.WIRELESS:
return " babel wireless\n no babel split-horizon\n"
else:
@ -566,14 +561,13 @@ class Xpimd(QuaggaService):
PIM multicast routing based on XORP.
"""
name = "Xpimd"
startup = ()
shutdown = ("killall xpimd",)
validate = ("pidof xpimd",)
ipv4_routing = True
name: str = "Xpimd"
shutdown: Tuple[str, ...] = ("killall xpimd",)
validate: Tuple[str, ...] = ("pidof xpimd",)
ipv4_routing: bool = True
@classmethod
def generate_quagga_config(cls, node):
def generate_quagga_config(cls, node: CoreNode) -> str:
ifname = "eth0"
for iface in node.get_ifaces():
if iface.name != "lo":
@ -589,5 +583,5 @@ class Xpimd(QuaggaService):
return cfg
@classmethod
def generate_quagga_iface_config(cls, node, iface):
def generate_quagga_iface_config(cls, node: CoreNode, iface: CoreInterface) -> str:
return " ip mfea\n ip igmp\n ip pim\n"

View file

@ -3,9 +3,11 @@ sdn.py defines services to start Open vSwitch and the Ryu SDN Controller.
"""
import re
from typing import Tuple
import netaddr
from core.nodes.base import CoreNode
from core.services.coreservices import CoreService
@ -14,24 +16,28 @@ class SdnService(CoreService):
Parent class for SDN services.
"""
group = "SDN"
group: str = "SDN"
@classmethod
def generate_config(cls, node, filename):
def generate_config(cls, node: CoreNode, filename: str) -> str:
return ""
class OvsService(SdnService):
name = "OvsService"
executables = ("ovs-ofctl", "ovs-vsctl")
group = "SDN"
dirs = ("/etc/openvswitch", "/var/run/openvswitch", "/var/log/openvswitch")
configs = ("OvsService.sh",)
startup = ("sh OvsService.sh",)
shutdown = ("killall ovs-vswitchd", "killall ovsdb-server")
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, ...] = ("sh OvsService.sh",)
shutdown: Tuple[str, ...] = ("killall ovs-vswitchd", "killall ovsdb-server")
@classmethod
def generate_config(cls, node, filename):
def generate_config(cls, node: CoreNode, filename: str) -> str:
# Check whether the node is running zebra
has_zebra = 0
for s in node.services:
@ -46,8 +52,8 @@ class OvsService(SdnService):
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)
@ -111,21 +117,19 @@ class OvsService(SdnService):
% (portnum + 1, portnum)
)
portnum += 2
return cfg
class RyuService(SdnService):
name = "ryuService"
executables = ("ryu-manager",)
group = "SDN"
dirs = ()
configs = ("ryuService.sh",)
startup = ("sh ryuService.sh",)
shutdown = ("killall ryu-manager",)
name: str = "ryuService"
group: str = "SDN"
executables: Tuple[str, ...] = ("ryu-manager",)
configs: Tuple[str, ...] = ("ryuService.sh",)
startup: Tuple[str, ...] = ("sh ryuService.sh",)
shutdown: Tuple[str, ...] = ("killall ryu-manager",)
@classmethod
def generate_config(cls, node, filename):
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.

View file

@ -4,78 +4,79 @@ firewall)
"""
import logging
from typing import Tuple
from core import constants
from core.nodes.base import CoreNode
from core.nodes.interface import CoreInterface
from core.services.coreservices import CoreService
class VPNClient(CoreService):
name = "VPNClient"
group = "Security"
configs = ("vpnclient.sh",)
startup = ("sh vpnclient.sh",)
shutdown = ("killall openvpn",)
validate = ("pidof openvpn",)
custom_needed = True
name: str = "VPNClient"
group: str = "Security"
configs: Tuple[str, ...] = ("vpnclient.sh",)
startup: Tuple[str, ...] = ("sh vpnclient.sh",)
shutdown: Tuple[str, ...] = ("killall openvpn",)
validate: Tuple[str, ...] = ("pidof openvpn",)
custom_needed: bool = True
@classmethod
def generate_config(cls, node, filename):
def generate_config(cls, node: CoreNode, filename: str) -> str:
"""
Return the client.conf and vpnclient.sh file contents to
"""
cfg = "#!/bin/sh\n"
cfg += "# custom VPN Client configuration for service (security.py)\n"
fname = "%s/examples/services/sampleVPNClient" % constants.CORE_DATA_DIR
fname = f"{constants.CORE_DATA_DIR}/examples/services/sampleVPNClient"
try:
cfg += open(fname, "rb").read()
with open(fname, "r") as f:
cfg += f.read()
except IOError:
logging.exception(
"Error opening VPN client configuration template (%s)", fname
"error opening VPN client configuration template (%s)", fname
)
return cfg
class VPNServer(CoreService):
name = "VPNServer"
group = "Security"
configs = ("vpnserver.sh",)
startup = ("sh vpnserver.sh",)
shutdown = ("killall openvpn",)
validate = ("pidof openvpn",)
custom_needed = True
name: str = "VPNServer"
group: str = "Security"
configs: Tuple[str, ...] = ("vpnserver.sh",)
startup: Tuple[str, ...] = ("sh vpnserver.sh",)
shutdown: Tuple[str, ...] = ("killall openvpn",)
validate: Tuple[str, ...] = ("pidof openvpn",)
custom_needed: bool = True
@classmethod
def generate_config(cls, node, filename):
def generate_config(cls, node: CoreNode, filename: str) -> str:
"""
Return the sample server.conf and vpnserver.sh file contents to
GUI for user customization.
"""
cfg = "#!/bin/sh\n"
cfg += "# custom VPN Server Configuration for service (security.py)\n"
fname = "%s/examples/services/sampleVPNServer" % constants.CORE_DATA_DIR
fname = f"{constants.CORE_DATA_DIR}/examples/services/sampleVPNServer"
try:
cfg += open(fname, "rb").read()
with open(fname, "r") as f:
cfg += f.read()
except IOError:
logging.exception(
"Error opening VPN server configuration template (%s)", fname
)
return cfg
class IPsec(CoreService):
name = "IPsec"
group = "Security"
configs = ("ipsec.sh",)
startup = ("sh ipsec.sh",)
shutdown = ("killall racoon",)
custom_needed = True
name: str = "IPsec"
group: str = "Security"
configs: Tuple[str, ...] = ("ipsec.sh",)
startup: Tuple[str, ...] = ("sh ipsec.sh",)
shutdown: Tuple[str, ...] = ("killall racoon",)
custom_needed: bool = True
@classmethod
def generate_config(cls, node, filename):
def generate_config(cls, node: CoreNode, filename: str) -> str:
"""
Return the ipsec.conf and racoon.conf file contents to
GUI for user customization.
@ -83,7 +84,7 @@ class IPsec(CoreService):
cfg = "#!/bin/sh\n"
cfg += "# set up static tunnel mode security assocation for service "
cfg += "(security.py)\n"
fname = "%s/examples/services/sampleIPsec" % constants.CORE_DATA_DIR
fname = f"{constants.CORE_DATA_DIR}/examples/services/sampleIPsec"
try:
with open(fname, "r") as f:
cfg += f.read()
@ -93,28 +94,27 @@ class IPsec(CoreService):
class Firewall(CoreService):
name = "Firewall"
group = "Security"
configs = ("firewall.sh",)
startup = ("sh firewall.sh",)
custom_needed = True
name: str = "Firewall"
group: str = "Security"
configs: Tuple[str, ...] = ("firewall.sh",)
startup: Tuple[str, ...] = ("sh firewall.sh",)
custom_needed: bool = True
@classmethod
def generate_config(cls, node, filename):
def generate_config(cls, node: CoreNode, filename: str) -> str:
"""
Return the firewall rule examples to GUI for user customization.
"""
cfg = "#!/bin/sh\n"
cfg += "# custom node firewall rules for service (security.py)\n"
fname = "%s/examples/services/sampleFirewall" % constants.CORE_DATA_DIR
fname = f"{constants.CORE_DATA_DIR}/examples/services/sampleFirewall"
try:
cfg += open(fname, "rb").read()
with open(fname, "r") as f:
cfg += f.read()
except IOError:
logging.exception(
"Error opening Firewall configuration template (%s)", fname
)
return cfg
@ -123,30 +123,28 @@ class Nat(CoreService):
IPv4 source NAT service.
"""
name = "NAT"
executables = ("iptables",)
group = "Security"
configs = ("nat.sh",)
startup = ("sh nat.sh",)
custom_needed = False
name: str = "NAT"
group: str = "Security"
executables: Tuple[str, ...] = ("iptables",)
configs: Tuple[str, ...] = ("nat.sh",)
startup: Tuple[str, ...] = ("sh nat.sh",)
custom_needed: bool = False
@classmethod
def generate_iface_nat_rule(cls, iface, line_prefix=""):
def generate_iface_nat_rule(cls, iface: CoreInterface, prefix: str = "") -> str:
"""
Generate a NAT line for one interface.
"""
cfg = line_prefix + "iptables -t nat -A POSTROUTING -o "
cfg = prefix + "iptables -t nat -A POSTROUTING -o "
cfg += iface.name + " -j MASQUERADE\n"
cfg += line_prefix + "iptables -A FORWARD -i " + iface.name
cfg += prefix + "iptables -A FORWARD -i " + iface.name
cfg += " -m state --state RELATED,ESTABLISHED -j ACCEPT\n"
cfg += line_prefix + "iptables -A FORWARD -i "
cfg += prefix + "iptables -A FORWARD -i "
cfg += iface.name + " -j DROP\n"
return cfg
@classmethod
def generate_config(cls, node, filename):
def generate_config(cls, node: CoreNode, filename: str) -> str:
"""
NAT out the first interface
"""
@ -156,7 +154,7 @@ class Nat(CoreService):
have_nat = False
for iface in node.get_ifaces(control=False):
if have_nat:
cfg += cls.generate_iface_nat_rule(iface, line_prefix="#")
cfg += cls.generate_iface_nat_rule(iface, prefix="#")
else:
have_nat = True
cfg += "# NAT out the " + iface.name + " interface\n"

View file

@ -1,52 +1,52 @@
"""
ucarp.py: defines high-availability IP address controlled by ucarp
"""
from typing import Tuple
from core.nodes.base import CoreNode
from core.services.coreservices import CoreService
UCARP_ETC = "/usr/local/etc/ucarp"
class Ucarp(CoreService):
name = "ucarp"
group = "Utility"
dirs = (UCARP_ETC,)
configs = (
name: str = "ucarp"
group: str = "Utility"
dirs: Tuple[str, ...] = (UCARP_ETC,)
configs: Tuple[str, ...] = (
UCARP_ETC + "/default.sh",
UCARP_ETC + "/default-up.sh",
UCARP_ETC + "/default-down.sh",
"ucarpboot.sh",
)
startup = ("sh ucarpboot.sh",)
shutdown = ("killall ucarp",)
validate = ("pidof ucarp",)
startup: Tuple[str, ...] = ("sh ucarpboot.sh",)
shutdown: Tuple[str, ...] = ("killall ucarp",)
validate: Tuple[str, ...] = ("pidof ucarp",)
@classmethod
def generate_config(cls, node, filename):
def generate_config(cls, node: CoreNode, filename: str) -> str:
"""
Return the default file contents
"""
if filename == cls.configs[0]:
return cls.generateUcarpConf(node)
return cls.generate_ucarp_conf(node)
elif filename == cls.configs[1]:
return cls.generateVipUp(node)
return cls.generate_vip_up(node)
elif filename == cls.configs[2]:
return cls.generateVipDown(node)
return cls.generate_vip_down(node)
elif filename == cls.configs[3]:
return cls.generateUcarpBoot(node)
return cls.generate_ucarp_boot(node)
else:
raise ValueError
@classmethod
def generateUcarpConf(cls, node):
def generate_ucarp_conf(cls, node: CoreNode) -> str:
"""
Returns configuration file text.
"""
try:
ucarp_bin = node.session.cfg["ucarp_bin"]
except KeyError:
ucarp_bin = "/usr/sbin/ucarp"
ucarp_bin = node.session.options.get_config(
"ucarp_bin", default="/usr/sbin/ucarp"
)
return """\
#!/bin/sh
# Location of UCARP executable
@ -110,7 +110,7 @@ ${UCARP_EXEC} -B ${UCARP_OPTS}
)
@classmethod
def generateUcarpBoot(cls, node):
def generate_ucarp_boot(cls, node: CoreNode) -> str:
"""
Generate a shell script used to boot the Ucarp daemons.
"""
@ -130,7 +130,7 @@ ${UCARP_CFGDIR}/default.sh
)
@classmethod
def generateVipUp(cls, node):
def generate_vip_up(cls, node: CoreNode) -> str:
"""
Generate a shell script used to start the virtual ip
"""
@ -152,7 +152,7 @@ fi
"""
@classmethod
def generateVipDown(cls, node):
def generate_vip_down(cls, node: CoreNode) -> str:
"""
Generate a shell script used to stop the virtual ip
"""

View file

@ -1,12 +1,13 @@
"""
utility.py: defines miscellaneous utility services.
"""
import os
from typing import Optional, Tuple
import netaddr
from core import constants, utils
from core.errors import CoreCommandError
from core.nodes.base import CoreNode
from core.services.coreservices import CoreService, ServiceMode
@ -15,32 +16,25 @@ class UtilService(CoreService):
Parent class for utility services.
"""
name = None
group = "Utility"
dirs = ()
configs = ()
startup = ()
shutdown = ()
name: Optional[str] = None
group: str = "Utility"
@classmethod
def generate_config(cls, node, filename):
def generate_config(cls, node: CoreNode, filename: str) -> str:
return ""
class IPForwardService(UtilService):
name = "IPForward"
configs = ("ipforward.sh",)
startup = ("sh ipforward.sh",)
name: str = "IPForward"
configs: Tuple[str, ...] = ("ipforward.sh",)
startup: Tuple[str, ...] = ("sh ipforward.sh",)
@classmethod
def generate_config(cls, node, filename):
if os.uname()[0] == "Linux":
return cls.generateconfiglinux(node, filename)
else:
raise Exception("unknown platform")
def generate_config(cls, node: CoreNode, filename: str) -> str:
return cls.generateconfiglinux(node, filename)
@classmethod
def generateconfiglinux(cls, node, filename):
def generateconfiglinux(cls, node: CoreNode, filename: str) -> str:
cfg = """\
#!/bin/sh
# auto-generated by IPForward service (utility.py)
@ -70,12 +64,12 @@ class IPForwardService(UtilService):
class DefaultRouteService(UtilService):
name = "DefaultRoute"
configs = ("defaultroute.sh",)
startup = ("sh defaultroute.sh",)
name: str = "DefaultRoute"
configs: Tuple[str, ...] = ("defaultroute.sh",)
startup: Tuple[str, ...] = ("sh defaultroute.sh",)
@classmethod
def generate_config(cls, node, filename):
def generate_config(cls, node: CoreNode, filename: str) -> str:
routes = []
ifaces = node.get_ifaces()
if ifaces:
@ -93,22 +87,18 @@ class DefaultRouteService(UtilService):
class DefaultMulticastRouteService(UtilService):
name = "DefaultMulticastRoute"
configs = ("defaultmroute.sh",)
startup = ("sh defaultmroute.sh",)
name: str = "DefaultMulticastRoute"
configs: Tuple[str, ...] = ("defaultmroute.sh",)
startup: Tuple[str, ...] = ("sh defaultmroute.sh",)
@classmethod
def generate_config(cls, node, filename):
def generate_config(cls, node: CoreNode, filename: str) -> str:
cfg = "#!/bin/sh\n"
cfg += "# auto-generated by DefaultMulticastRoute service (utility.py)\n"
cfg += "# the first interface is chosen below; please change it "
cfg += "as needed\n"
for iface in node.get_ifaces(control=False):
if os.uname()[0] == "Linux":
rtcmd = "ip route add 224.0.0.0/4 dev"
else:
raise Exception("unknown platform")
rtcmd = "ip route add 224.0.0.0/4 dev"
cfg += "%s %s\n" % (rtcmd, iface.name)
cfg += "\n"
break
@ -116,13 +106,13 @@ class DefaultMulticastRouteService(UtilService):
class StaticRouteService(UtilService):
name = "StaticRoute"
configs = ("staticroute.sh",)
startup = ("sh staticroute.sh",)
custom_needed = True
name: str = "StaticRoute"
configs: Tuple[str, ...] = ("staticroute.sh",)
startup: Tuple[str, ...] = ("sh staticroute.sh",)
custom_needed: bool = True
@classmethod
def generate_config(cls, node, filename):
def generate_config(cls, node: CoreNode, filename: str) -> str:
cfg = "#!/bin/sh\n"
cfg += "# auto-generated by StaticRoute service (utility.py)\n#\n"
cfg += "# NOTE: this service must be customized to be of any use\n"
@ -133,7 +123,7 @@ class StaticRouteService(UtilService):
return cfg
@staticmethod
def routestr(x):
def routestr(x: str) -> str:
addr = x.split("/")[0]
if netaddr.valid_ipv6(addr):
dst = "3ffe:4::/64"
@ -143,24 +133,20 @@ class StaticRouteService(UtilService):
if net[-2] == net[1]:
return ""
else:
if os.uname()[0] == "Linux":
rtcmd = "#/sbin/ip route add %s via" % dst
else:
raise Exception("unknown platform")
rtcmd = "#/sbin/ip route add %s via" % dst
return "%s %s" % (rtcmd, net[1])
class SshService(UtilService):
name = "SSH"
configs = ("startsshd.sh", "/etc/ssh/sshd_config")
dirs = ("/etc/ssh", "/var/run/sshd")
startup = ("sh startsshd.sh",)
shutdown = ("killall sshd",)
validate = ()
validation_mode = ServiceMode.BLOCKING
name: str = "SSH"
configs: Tuple[str, ...] = ("startsshd.sh", "/etc/ssh/sshd_config")
dirs: Tuple[str, ...] = ("/etc/ssh", "/var/run/sshd")
startup: Tuple[str, ...] = ("sh startsshd.sh",)
shutdown: Tuple[str, ...] = ("killall sshd",)
validation_mode: ServiceMode = ServiceMode.BLOCKING
@classmethod
def generate_config(cls, node, filename):
def generate_config(cls, node: CoreNode, filename: str) -> str:
"""
Use a startup script for launching sshd in order to wait for host
key generation.
@ -228,15 +214,15 @@ UseDNS no
class DhcpService(UtilService):
name = "DHCP"
configs = ("/etc/dhcp/dhcpd.conf",)
dirs = ("/etc/dhcp", "/var/lib/dhcp")
startup = ("touch /var/lib/dhcp/dhcpd.leases", "dhcpd")
shutdown = ("killall dhcpd",)
validate = ("pidof dhcpd",)
name: str = "DHCP"
configs: Tuple[str, ...] = ("/etc/dhcp/dhcpd.conf",)
dirs: Tuple[str, ...] = ("/etc/dhcp", "/var/lib/dhcp")
startup: Tuple[str, ...] = ("touch /var/lib/dhcp/dhcpd.leases", "dhcpd")
shutdown: Tuple[str, ...] = ("killall dhcpd",)
validate: Tuple[str, ...] = ("pidof dhcpd",)
@classmethod
def generate_config(cls, node, filename):
def generate_config(cls, node: CoreNode, filename: str) -> str:
"""
Generate a dhcpd config file using the network address of
each interface.
@ -261,7 +247,7 @@ ddns-update-style none;
return cfg
@staticmethod
def subnetentry(x):
def subnetentry(x: str) -> str:
"""
Generate a subnet declaration block given an IPv4 prefix string
for inclusion in the dhcpd3 config file.
@ -297,14 +283,14 @@ class DhcpClientService(UtilService):
Use a DHCP client for all interfaces for addressing.
"""
name = "DHCPClient"
configs = ("startdhcpclient.sh",)
startup = ("sh startdhcpclient.sh",)
shutdown = ("killall dhclient",)
validate = ("pidof dhclient",)
name: str = "DHCPClient"
configs: Tuple[str, ...] = ("startdhcpclient.sh",)
startup: Tuple[str, ...] = ("sh startdhcpclient.sh",)
shutdown: Tuple[str, ...] = ("killall dhclient",)
validate: Tuple[str, ...] = ("pidof dhclient",)
@classmethod
def generate_config(cls, node, filename):
def generate_config(cls, node: CoreNode, filename: str) -> str:
"""
Generate a script to invoke dhclient on all interfaces.
"""
@ -313,7 +299,6 @@ class DhcpClientService(UtilService):
cfg += "# uncomment this mkdir line and symlink line to enable client-"
cfg += "side DNS\n# resolution based on the DHCP server response.\n"
cfg += "#mkdir -p /var/run/resolvconf/interface\n"
for iface in node.get_ifaces(control=False):
cfg += "#ln -s /var/run/resolvconf/interface/%s.dhclient" % iface.name
cfg += " /var/run/resolvconf/resolv.conf\n"
@ -327,15 +312,15 @@ class FtpService(UtilService):
Start a vsftpd server.
"""
name = "FTP"
configs = ("vsftpd.conf",)
dirs = ("/var/run/vsftpd/empty", "/var/ftp")
startup = ("vsftpd ./vsftpd.conf",)
shutdown = ("killall vsftpd",)
validate = ("pidof vsftpd",)
name: str = "FTP"
configs: Tuple[str, ...] = ("vsftpd.conf",)
dirs: Tuple[str, ...] = ("/var/run/vsftpd/empty", "/var/ftp")
startup: Tuple[str, ...] = ("vsftpd ./vsftpd.conf",)
shutdown: Tuple[str, ...] = ("killall vsftpd",)
validate: Tuple[str, ...] = ("pidof vsftpd",)
@classmethod
def generate_config(cls, node, filename):
def generate_config(cls, node: CoreNode, filename: str) -> str:
"""
Generate a vsftpd.conf configuration file.
"""
@ -360,13 +345,13 @@ class HttpService(UtilService):
Start an apache server.
"""
name = "HTTP"
configs = (
name: str = "HTTP"
configs: Tuple[str, ...] = (
"/etc/apache2/apache2.conf",
"/etc/apache2/envvars",
"/var/www/index.html",
)
dirs = (
dirs: Tuple[str, ...] = (
"/etc/apache2",
"/var/run/apache2",
"/var/log/apache2",
@ -374,14 +359,14 @@ class HttpService(UtilService):
"/var/lock/apache2",
"/var/www",
)
startup = ("chown www-data /var/lock/apache2", "apache2ctl start")
shutdown = ("apache2ctl stop",)
validate = ("pidof apache2",)
APACHEVER22, APACHEVER24 = (22, 24)
startup: Tuple[str, ...] = ("chown www-data /var/lock/apache2", "apache2ctl start")
shutdown: Tuple[str, ...] = ("apache2ctl stop",)
validate: Tuple[str, ...] = ("pidof apache2",)
APACHEVER22: int = 22
APACHEVER24: int = 24
@classmethod
def generate_config(cls, node, filename):
def generate_config(cls, node: CoreNode, filename: str) -> str:
"""
Generate an apache2.conf configuration file.
"""
@ -395,7 +380,7 @@ class HttpService(UtilService):
return ""
@classmethod
def detectversionfromcmd(cls):
def detectversionfromcmd(cls) -> int:
"""
Detect the apache2 version using the 'a2query' command.
"""
@ -405,14 +390,12 @@ class HttpService(UtilService):
except CoreCommandError as e:
status = e.returncode
result = e.stderr
if status == 0 and result[:3] == "2.4":
return cls.APACHEVER24
return cls.APACHEVER22
@classmethod
def generateapache2conf(cls, node, filename):
def generateapache2conf(cls, node: CoreNode, filename: str) -> str:
lockstr = {
cls.APACHEVER22: "LockFile ${APACHE_LOCK_DIR}/accept.lock\n",
cls.APACHEVER24: "Mutex file:${APACHE_LOCK_DIR} default\n",
@ -421,22 +404,18 @@ class HttpService(UtilService):
cls.APACHEVER22: "",
cls.APACHEVER24: "LoadModule mpm_worker_module /usr/lib/apache2/modules/mod_mpm_worker.so\n",
}
permstr = {
cls.APACHEVER22: " Order allow,deny\n Deny from all\n Satisfy all\n",
cls.APACHEVER24: " Require all denied\n",
}
authstr = {
cls.APACHEVER22: "LoadModule authz_default_module /usr/lib/apache2/modules/mod_authz_default.so\n",
cls.APACHEVER24: "LoadModule authz_core_module /usr/lib/apache2/modules/mod_authz_core.so\n",
}
permstr2 = {
cls.APACHEVER22: "\t\tOrder allow,deny\n\t\tallow from all\n",
cls.APACHEVER24: "\t\tRequire all granted\n",
}
version = cls.detectversionfromcmd()
cfg = "# apache2.conf generated by utility.py:HttpService\n"
cfg += lockstr[version]
@ -552,7 +531,7 @@ TraceEnable Off
return cfg
@classmethod
def generateenvvars(cls, node, filename):
def generateenvvars(cls, node: CoreNode, filename: str) -> str:
return """\
# this file is used by apache2ctl - generated by utility.py:HttpService
# these settings come from a default Ubuntu apache2 installation
@ -567,7 +546,7 @@ export LANG
"""
@classmethod
def generatehtml(cls, node, filename):
def generatehtml(cls, node: CoreNode, filename: str) -> str:
body = (
"""\
<!-- generated by utility.py:HttpService -->
@ -587,16 +566,15 @@ class PcapService(UtilService):
Pcap service for logging packets.
"""
name = "pcap"
configs = ("pcap.sh",)
dirs = ()
startup = ("sh pcap.sh start",)
shutdown = ("sh pcap.sh stop",)
validate = ("pidof tcpdump",)
meta = "logs network traffic to pcap packet capture files"
name: str = "pcap"
configs: Tuple[str, ...] = ("pcap.sh",)
startup: Tuple[str, ...] = ("sh pcap.sh start",)
shutdown: Tuple[str, ...] = ("sh pcap.sh stop",)
validate: Tuple[str, ...] = ("pidof tcpdump",)
meta: str = "logs network traffic to pcap packet capture files"
@classmethod
def generate_config(cls, node, filename):
def generate_config(cls, node: CoreNode, filename: str) -> str:
"""
Generate a startpcap.sh traffic logging script.
"""
@ -630,15 +608,17 @@ fi;
class RadvdService(UtilService):
name = "radvd"
configs = ("/etc/radvd/radvd.conf",)
dirs = ("/etc/radvd",)
startup = ("radvd -C /etc/radvd/radvd.conf -m logfile -l /var/log/radvd.log",)
shutdown = ("pkill radvd",)
validate = ("pidof radvd",)
name: str = "radvd"
configs: Tuple[str, ...] = ("/etc/radvd/radvd.conf",)
dirs: Tuple[str, ...] = ("/etc/radvd",)
startup: Tuple[str, ...] = (
"radvd -C /etc/radvd/radvd.conf -m logfile -l /var/log/radvd.log",
)
shutdown: Tuple[str, ...] = ("pkill radvd",)
validate: Tuple[str, ...] = ("pidof radvd",)
@classmethod
def generate_config(cls, node, filename):
def generate_config(cls, node: CoreNode, filename: str) -> str:
"""
Generate a RADVD router advertisement daemon config file
using the network address of each interface.
@ -678,7 +658,7 @@ interface %s
return cfg
@staticmethod
def subnetentry(x):
def subnetentry(x: str) -> str:
"""
Generate a subnet declaration block given an IPv6 prefix string
for inclusion in the RADVD config file.
@ -695,14 +675,14 @@ class AtdService(UtilService):
Atd service for scheduling at jobs
"""
name = "atd"
configs = ("startatd.sh",)
dirs = ("/var/spool/cron/atjobs", "/var/spool/cron/atspool")
startup = ("sh startatd.sh",)
shutdown = ("pkill atd",)
name: str = "atd"
configs: Tuple[str, ...] = ("startatd.sh",)
dirs: Tuple[str, ...] = ("/var/spool/cron/atjobs", "/var/spool/cron/atspool")
startup: Tuple[str, ...] = ("sh startatd.sh",)
shutdown: Tuple[str, ...] = ("pkill atd",)
@classmethod
def generate_config(cls, node, filename):
def generate_config(cls, node: CoreNode, filename: str) -> str:
return """
#!/bin/sh
echo 00001 > /var/spool/cron/atjobs/.SEQ
@ -717,5 +697,5 @@ class UserDefinedService(UtilService):
Dummy service allowing customization of anything.
"""
name = "UserDefined"
meta = "Customize this service to do anything upon startup."
name: str = "UserDefined"
meta: str = "Customize this service to do anything upon startup."

View file

@ -2,10 +2,12 @@
xorp.py: defines routing services provided by the XORP routing suite.
"""
import logging
from typing import Optional, Tuple
import netaddr
from core.nodes.base import CoreNode
from core.nodes.interface import CoreInterface
from core.services.coreservices import CoreService
@ -15,20 +17,20 @@ class XorpRtrmgr(CoreService):
enabled XORP services, and launches necessary daemons upon startup.
"""
name = "xorp_rtrmgr"
executables = ("xorp_rtrmgr",)
group = "XORP"
dirs = ("/etc/xorp",)
configs = ("/etc/xorp/config.boot",)
startup = (
name: str = "xorp_rtrmgr"
group: str = "XORP"
executables: Tuple[str, ...] = ("xorp_rtrmgr",)
dirs: Tuple[str, ...] = ("/etc/xorp",)
configs: Tuple[str, ...] = ("/etc/xorp/config.boot",)
startup: Tuple[str, ...] = (
"xorp_rtrmgr -d -b %s -l /var/log/%s.log -P /var/run/%s.pid"
% (configs[0], name, name),
)
shutdown = ("killall xorp_rtrmgr",)
validate = ("pidof xorp_rtrmgr",)
shutdown: Tuple[str, ...] = ("killall xorp_rtrmgr",)
validate: Tuple[str, ...] = ("pidof xorp_rtrmgr",)
@classmethod
def generate_config(cls, node, filename):
def generate_config(cls, node: CoreNode, filename: str) -> str:
"""
Returns config.boot configuration file text. Other services that
depend on this will have generatexorpconfig() hooks that are
@ -45,16 +47,15 @@ class XorpRtrmgr(CoreService):
cfg += "}\n\n"
for s in node.services:
try:
s.dependencies.index(cls.name)
cfg += s.generatexorpconfig(node)
except ValueError:
logging.exception("error getting value from service: %s", cls.name)
if cls.name not in s.dependencies:
continue
if not (isinstance(s, XorpService) or issubclass(s, XorpService)):
continue
cfg += s.generate_xorp_config(node)
return cfg
@staticmethod
def addrstr(x):
def addrstr(x: str) -> str:
"""
helper for mapping IP addresses to XORP config statements
"""
@ -65,7 +66,7 @@ class XorpRtrmgr(CoreService):
return cfg
@staticmethod
def lladdrstr(iface):
def lladdrstr(iface: CoreInterface) -> str:
"""
helper for adding link-local address entries (required by OSPFv3)
"""
@ -81,18 +82,16 @@ class XorpService(CoreService):
common to XORP's routing daemons.
"""
name = None
executables = ("xorp_rtrmgr",)
group = "XORP"
dependencies = ("xorp_rtrmgr",)
dirs = ()
configs = ()
startup = ()
shutdown = ()
meta = "The config file for this service can be found in the xorp_rtrmgr service."
name: Optional[str] = None
group: str = "XORP"
executables: Tuple[str, ...] = ("xorp_rtrmgr",)
dependencies: Tuple[str, ...] = ("xorp_rtrmgr",)
meta: str = (
"The config file for this service can be found in the xorp_rtrmgr service."
)
@staticmethod
def fea(forwarding):
def fea(forwarding: str) -> str:
"""
Helper to add a forwarding engine entry to the config file.
"""
@ -104,17 +103,14 @@ class XorpService(CoreService):
return cfg
@staticmethod
def mfea(forwarding, ifaces):
def mfea(forwarding, node: CoreNode) -> str:
"""
Helper to add a multicast forwarding engine entry to the config file.
"""
names = []
for iface in ifaces:
if hasattr(iface, "control") and iface.control is True:
continue
for iface in node.get_ifaces(control=False):
names.append(iface.name)
names.append("register_vif")
cfg = "plumbing {\n"
cfg += " %s {\n" % forwarding
for name in names:
@ -128,7 +124,7 @@ class XorpService(CoreService):
return cfg
@staticmethod
def policyexportconnected():
def policyexportconnected() -> str:
"""
Helper to add a policy statement for exporting connected routes.
"""
@ -144,7 +140,7 @@ class XorpService(CoreService):
return cfg
@staticmethod
def routerid(node):
def router_id(node: CoreNode) -> str:
"""
Helper to return the first IPv4 address of a node as its router ID.
"""
@ -153,15 +149,14 @@ class XorpService(CoreService):
a = a.split("/")[0]
if netaddr.valid_ipv4(a):
return a
# raise ValueError, "no IPv4 address found for router ID"
return "0.0.0.0"
@classmethod
def generate_config(cls, node, filename):
def generate_config(cls, node: CoreNode, filename: str) -> str:
return ""
@classmethod
def generatexorpconfig(cls, node):
def generate_xorp_config(cls, node: CoreNode) -> str:
return ""
@ -172,12 +167,12 @@ class XorpOspfv2(XorpService):
unified XORP configuration file.
"""
name = "XORP_OSPFv2"
name: str = "XORP_OSPFv2"
@classmethod
def generatexorpconfig(cls, node):
def generate_xorp_config(cls, node: CoreNode) -> str:
cfg = cls.fea("unicast-forwarding4")
rtrid = cls.routerid(node)
rtrid = cls.router_id(node)
cfg += "\nprotocols {\n"
cfg += " ospf4 {\n"
cfg += "\trouter-id: %s\n" % rtrid
@ -206,12 +201,12 @@ class XorpOspfv3(XorpService):
unified XORP configuration file.
"""
name = "XORP_OSPFv3"
name: str = "XORP_OSPFv3"
@classmethod
def generatexorpconfig(cls, node):
def generate_xorp_config(cls, node: CoreNode) -> str:
cfg = cls.fea("unicast-forwarding6")
rtrid = cls.routerid(node)
rtrid = cls.router_id(node)
cfg += "\nprotocols {\n"
cfg += " ospf6 0 { /* Instance ID 0 */\n"
cfg += "\trouter-id: %s\n" % rtrid
@ -232,16 +227,16 @@ class XorpBgp(XorpService):
IPv4 inter-domain routing. AS numbers and peers must be customized.
"""
name = "XORP_BGP"
custom_needed = True
name: str = "XORP_BGP"
custom_needed: bool = True
@classmethod
def generatexorpconfig(cls, node):
def generate_xorp_config(cls, node: CoreNode) -> str:
cfg = "/* This is a sample config that should be customized with\n"
cfg += " appropriate AS numbers and peers */\n"
cfg += cls.fea("unicast-forwarding4")
cfg += cls.policyexportconnected()
rtrid = cls.routerid(node)
rtrid = cls.router_id(node)
cfg += "\nprotocols {\n"
cfg += " bgp {\n"
cfg += "\tbgp-id: %s\n" % rtrid
@ -262,10 +257,10 @@ class XorpRip(XorpService):
RIP IPv4 unicast routing.
"""
name = "XORP_RIP"
name: str = "XORP_RIP"
@classmethod
def generatexorpconfig(cls, node):
def generate_xorp_config(cls, node: CoreNode) -> str:
cfg = cls.fea("unicast-forwarding4")
cfg += cls.policyexportconnected()
cfg += "\nprotocols {\n"
@ -293,10 +288,10 @@ class XorpRipng(XorpService):
RIP NG IPv6 unicast routing.
"""
name = "XORP_RIPNG"
name: str = "XORP_RIPNG"
@classmethod
def generatexorpconfig(cls, node):
def generate_xorp_config(cls, node: CoreNode) -> str:
cfg = cls.fea("unicast-forwarding6")
cfg += cls.policyexportconnected()
cfg += "\nprotocols {\n"
@ -320,12 +315,11 @@ class XorpPimSm4(XorpService):
PIM Sparse Mode IPv4 multicast routing.
"""
name = "XORP_PIMSM4"
name: str = "XORP_PIMSM4"
@classmethod
def generatexorpconfig(cls, node):
cfg = cls.mfea("mfea4", node.get_ifaces())
def generate_xorp_config(cls, node: CoreNode) -> str:
cfg = cls.mfea("mfea4", node)
cfg += "\nprotocols {\n"
cfg += " igmp {\n"
names = []
@ -338,7 +332,6 @@ class XorpPimSm4(XorpService):
cfg += "\t}\n"
cfg += " }\n"
cfg += "}\n"
cfg += "\nprotocols {\n"
cfg += " pimsm4 {\n"
@ -361,10 +354,8 @@ class XorpPimSm4(XorpService):
cfg += "\t\t}\n"
cfg += "\t }\n"
cfg += "\t}\n"
cfg += " }\n"
cfg += "}\n"
cfg += "\nprotocols {\n"
cfg += " fib2mrib {\n"
cfg += "\tdisable: false\n"
@ -378,12 +369,11 @@ class XorpPimSm6(XorpService):
PIM Sparse Mode IPv6 multicast routing.
"""
name = "XORP_PIMSM6"
name: str = "XORP_PIMSM6"
@classmethod
def generatexorpconfig(cls, node):
cfg = cls.mfea("mfea6", node.get_ifaces())
def generate_xorp_config(cls, node: CoreNode) -> str:
cfg = cls.mfea("mfea6", node)
cfg += "\nprotocols {\n"
cfg += " mld {\n"
names = []
@ -396,7 +386,6 @@ class XorpPimSm6(XorpService):
cfg += "\t}\n"
cfg += " }\n"
cfg += "}\n"
cfg += "\nprotocols {\n"
cfg += " pimsm6 {\n"
@ -419,10 +408,8 @@ class XorpPimSm6(XorpService):
cfg += "\t\t}\n"
cfg += "\t }\n"
cfg += "\t}\n"
cfg += " }\n"
cfg += "}\n"
cfg += "\nprotocols {\n"
cfg += " fib2mrib {\n"
cfg += "\tdisable: false\n"
@ -436,12 +423,12 @@ class XorpOlsr(XorpService):
OLSR IPv4 unicast MANET routing.
"""
name = "XORP_OLSR"
name: str = "XORP_OLSR"
@classmethod
def generatexorpconfig(cls, node):
def generate_xorp_config(cls, node: CoreNode) -> str:
cfg = cls.fea("unicast-forwarding4")
rtrid = cls.routerid(node)
rtrid = cls.router_id(node)
cfg += "\nprotocols {\n"
cfg += " olsr4 {\n"
cfg += "\tmain-address: %s\n" % rtrid