daemon: updated core.services to avoid using deprecated type hinting, also updated string formatting to f strings

This commit is contained in:
Blake Harnden 2023-04-13 15:39:40 -07:00
parent 7f58224f43
commit 921bfdf527
11 changed files with 435 additions and 500 deletions

View file

@ -1,7 +1,7 @@
""" """
bird.py: defines routing services provided by the BIRD Internet Routing Daemon. bird.py: defines routing services provided by the BIRD Internet Routing Daemon.
""" """
from typing import Optional, Tuple from typing import Optional
from core.nodes.base import CoreNode from core.nodes.base import CoreNode
from core.services.coreservices import CoreService from core.services.coreservices import CoreService
@ -14,12 +14,12 @@ class Bird(CoreService):
name: str = "bird" name: str = "bird"
group: str = "BIRD" group: str = "BIRD"
executables: Tuple[str, ...] = ("bird",) executables: tuple[str, ...] = ("bird",)
dirs: Tuple[str, ...] = ("/etc/bird",) dirs: tuple[str, ...] = ("/etc/bird",)
configs: Tuple[str, ...] = ("/etc/bird/bird.conf",) configs: tuple[str, ...] = ("/etc/bird/bird.conf",)
startup: Tuple[str, ...] = ("bird -c %s" % (configs[0]),) startup: tuple[str, ...] = (f"bird -c {configs[0]}",)
shutdown: Tuple[str, ...] = ("killall bird",) shutdown: tuple[str, ...] = ("killall bird",)
validate: Tuple[str, ...] = ("pidof bird",) validate: tuple[str, ...] = ("pidof bird",)
@classmethod @classmethod
def generate_config(cls, node: CoreNode, filename: str) -> str: def generate_config(cls, node: CoreNode, filename: str) -> str:
@ -48,33 +48,30 @@ class Bird(CoreService):
Returns configuration file text. Other services that depend on bird Returns configuration file text. Other services that depend on bird
will have hooks that are invoked here. will have hooks that are invoked here.
""" """
cfg = """\ cfg = f"""\
/* Main configuration file for BIRD. This is ony a template, /* Main configuration file for BIRD. This is ony a template,
* you will *need* to customize it according to your needs * you will *need* to customize it according to your needs
* Beware that only double quotes \'"\' are valid. No singles. */ * Beware that only double quotes \'"\' are valid. No singles. */
log "/var/log/%s.log" all; log "/var/log/{cls.name}.log" all;
#debug protocols all; #debug protocols all;
#debug commands 2; #debug commands 2;
router id %s; # Mandatory for IPv6, may be automatic for IPv4 router id {cls.router_id(node)}; # Mandatory for IPv6, may be automatic for IPv4
protocol kernel { protocol kernel {{
persist; # Don\'t remove routes on BIRD shutdown persist; # Don\'t remove routes on BIRD shutdown
scan time 200; # Scan kernel routing table every 200 seconds scan time 200; # Scan kernel routing table every 200 seconds
export all; export all;
import all; import all;
} }}
protocol device { protocol device {{
scan time 10; # Scan interfaces every 10 seconds scan time 10; # Scan interfaces every 10 seconds
} }}
""" % ( """
cls.name,
cls.router_id(node),
)
# generate protocol specific configurations # generate protocol specific configurations
for s in node.services: for s in node.services:
@ -94,8 +91,8 @@ class BirdService(CoreService):
name: Optional[str] = None name: Optional[str] = None
group: str = "BIRD" group: str = "BIRD"
executables: Tuple[str, ...] = ("bird",) executables: tuple[str, ...] = ("bird",)
dependencies: Tuple[str, ...] = ("bird",) dependencies: tuple[str, ...] = ("bird",)
meta: str = "The config file for this service can be found in the bird service." meta: str = "The config file for this service can be found in the bird service."
@classmethod @classmethod
@ -111,7 +108,7 @@ class BirdService(CoreService):
""" """
cfg = "" cfg = ""
for iface in node.get_ifaces(control=False): for iface in node.get_ifaces(control=False):
cfg += ' interface "%s";\n' % iface.name cfg += f' interface "{iface.name}";\n'
return cfg return cfg

View file

@ -11,18 +11,9 @@ import enum
import logging import logging
import pkgutil import pkgutil
import time import time
from collections.abc import Iterable
from pathlib import Path from pathlib import Path
from typing import ( from typing import TYPE_CHECKING, Optional, Union
TYPE_CHECKING,
Dict,
Iterable,
List,
Optional,
Set,
Tuple,
Type,
Union,
)
from core import services as core_services from core import services as core_services
from core import utils from core import utils
@ -41,7 +32,7 @@ logger = logging.getLogger(__name__)
if TYPE_CHECKING: if TYPE_CHECKING:
from core.emulator.session import Session from core.emulator.session import Session
CoreServiceType = Union["CoreService", Type["CoreService"]] CoreServiceType = Union["CoreService", type["CoreService"]]
class ServiceMode(enum.Enum): class ServiceMode(enum.Enum):
@ -57,25 +48,25 @@ class ServiceDependencies:
provided. provided.
""" """
def __init__(self, services: List["CoreServiceType"]) -> None: def __init__(self, services: list["CoreServiceType"]) -> None:
self.visited: Set[str] = set() self.visited: set[str] = set()
self.services: Dict[str, "CoreServiceType"] = {} self.services: dict[str, "CoreServiceType"] = {}
self.paths: Dict[str, List["CoreServiceType"]] = {} self.paths: dict[str, list["CoreServiceType"]] = {}
self.boot_paths: List[List["CoreServiceType"]] = [] self.boot_paths: list[list["CoreServiceType"]] = []
roots = set([x.name for x in services]) roots = {x.name for x in services}
for service in services: for service in services:
self.services[service.name] = service self.services[service.name] = service
roots -= set(service.dependencies) roots -= set(service.dependencies)
self.roots: List["CoreServiceType"] = [x for x in services if x.name in roots] self.roots: list["CoreServiceType"] = [x for x in services if x.name in roots]
if services and not self.roots: if services and not self.roots:
raise ValueError("circular dependency is present") raise ValueError("circular dependency is present")
def _search( def _search(
self, self,
service: "CoreServiceType", service: "CoreServiceType",
visiting: Set[str] = None, visiting: set[str] = None,
path: List[str] = None, path: list[str] = None,
) -> List["CoreServiceType"]: ) -> list["CoreServiceType"]:
if service.name in self.visited: if service.name in self.visited:
return self.paths[service.name] return self.paths[service.name]
self.visited.add(service.name) self.visited.add(service.name)
@ -103,7 +94,7 @@ class ServiceDependencies:
self.paths[service.name] = path self.paths[service.name] = path
return path return path
def boot_order(self) -> List[List["CoreServiceType"]]: def boot_order(self) -> list[list["CoreServiceType"]]:
for service in self.roots: for service in self.roots:
self._search(service) self._search(service)
return self.boot_paths return self.boot_paths
@ -114,10 +105,10 @@ class ServiceManager:
Manages services available for CORE nodes to use. Manages services available for CORE nodes to use.
""" """
services: Dict[str, Type["CoreService"]] = {} services: dict[str, type["CoreService"]] = {}
@classmethod @classmethod
def add(cls, service: Type["CoreService"]) -> None: def add(cls, service: type["CoreService"]) -> None:
""" """
Add a service to manager. Add a service to manager.
@ -150,7 +141,7 @@ class ServiceManager:
cls.services[name] = service cls.services[name] = service
@classmethod @classmethod
def get(cls, name: str) -> Type["CoreService"]: def get(cls, name: str) -> type["CoreService"]:
""" """
Retrieve a service from the manager. Retrieve a service from the manager.
@ -163,7 +154,7 @@ class ServiceManager:
return service return service
@classmethod @classmethod
def add_services(cls, path: Path) -> List[str]: def add_services(cls, path: Path) -> list[str]:
""" """
Method for retrieving all CoreServices from a given path. Method for retrieving all CoreServices from a given path.
@ -183,7 +174,7 @@ class ServiceManager:
return service_errors return service_errors
@classmethod @classmethod
def load_locals(cls) -> List[str]: def load_locals(cls) -> list[str]:
errors = [] errors = []
for module_info in pkgutil.walk_packages( for module_info in pkgutil.walk_packages(
core_services.__path__, f"{core_services.__name__}." core_services.__path__, f"{core_services.__name__}."
@ -218,7 +209,7 @@ class CoreServices:
""" """
self.session: "Session" = session self.session: "Session" = session
# dict of default services tuples, key is node type # dict of default services tuples, key is node type
self.default_services: Dict[str, List[str]] = { self.default_services: dict[str, list[str]] = {
"mdr": ["zebra", "OSPFv3MDR", "IPForward"], "mdr": ["zebra", "OSPFv3MDR", "IPForward"],
"PC": ["DefaultRoute"], "PC": ["DefaultRoute"],
"prouter": [], "prouter": [],
@ -226,7 +217,7 @@ class CoreServices:
"host": ["DefaultRoute", "SSH"], "host": ["DefaultRoute", "SSH"],
} }
# dict of node ids to dict of custom services by name # dict of node ids to dict of custom services by name
self.custom_services: Dict[int, Dict[str, "CoreService"]] = {} self.custom_services: dict[int, dict[str, "CoreService"]] = {}
def reset(self) -> None: def reset(self) -> None:
""" """
@ -273,7 +264,7 @@ class CoreServices:
node_services[service.name] = service node_services[service.name] = service
def add_services( def add_services(
self, node: CoreNode, model: str, services: List[str] = None self, node: CoreNode, model: str, services: list[str] = None
) -> None: ) -> None:
""" """
Add services to a node. Add services to a node.
@ -298,7 +289,7 @@ class CoreServices:
continue continue
node.services.append(service) node.services.append(service)
def all_configs(self) -> List[Tuple[int, "CoreService"]]: def all_configs(self) -> list[tuple[int, "CoreService"]]:
""" """
Return (node_id, service) tuples for all stored configs. Used when reconnecting Return (node_id, service) tuples for all stored configs. Used when reconnecting
to a session or opening XML. to a session or opening XML.
@ -313,7 +304,7 @@ class CoreServices:
configs.append((node_id, service)) configs.append((node_id, service))
return configs return configs
def all_files(self, service: "CoreService") -> List[Tuple[str, str]]: def all_files(self, service: "CoreService") -> list[tuple[str, str]]:
""" """
Return all customized files stored with a service. Return all customized files stored with a service.
Used when reconnecting to a session or opening XML. Used when reconnecting to a session or opening XML.
@ -349,7 +340,7 @@ class CoreServices:
if exceptions: if exceptions:
raise CoreServiceBootError(*exceptions) raise CoreServiceBootError(*exceptions)
def _boot_service_path(self, node: CoreNode, boot_path: List["CoreServiceType"]): def _boot_service_path(self, node: CoreNode, boot_path: list["CoreServiceType"]):
logger.info( logger.info(
"booting node(%s) services: %s", "booting node(%s) services: %s",
node.name, node.name,
@ -400,7 +391,7 @@ class CoreServices:
status = self.startup_service(node, service, wait) status = self.startup_service(node, service, wait)
if status: if status:
raise CoreServiceBootError( raise CoreServiceBootError(
"node(%s) service(%s) error during startup" % (node.name, service.name) f"node({node.name}) service({service.name}) error during startup"
) )
# blocking mode is finished # blocking mode is finished
@ -425,7 +416,7 @@ class CoreServices:
if status: if status:
raise CoreServiceBootError( raise CoreServiceBootError(
"node(%s) service(%s) failed validation" % (node.name, service.name) f"node({node.name}) service({service.name}) failed validation"
) )
def copy_service_file(self, node: CoreNode, file_path: Path, cfg: str) -> bool: def copy_service_file(self, node: CoreNode, file_path: Path, cfg: str) -> bool:
@ -540,11 +531,11 @@ class CoreServices:
# get the file data # get the file data
data = service.config_data.get(filename) data = service.config_data.get(filename)
if data is None: if data is None:
data = "%s" % service.generate_config(node, filename) data = service.generate_config(node, filename)
else: else:
data = "%s" % data data = data
filetypestr = "service:%s" % service.name filetypestr = f"service:{service.name}"
return FileData( return FileData(
message_type=MessageFlags.ADD, message_type=MessageFlags.ADD,
node=node.id, node=node.id,
@ -636,7 +627,7 @@ class CoreServices:
try: try:
if self.copy_service_file(node, file_path, cfg): if self.copy_service_file(node, file_path, cfg):
continue continue
except IOError: except OSError:
logger.exception("error copying service file: %s", file_name) logger.exception("error copying service file: %s", file_name)
continue continue
else: else:
@ -674,31 +665,31 @@ class CoreService:
name: Optional[str] = None name: Optional[str] = None
# executables that must exist for service to run # executables that must exist for service to run
executables: Tuple[str, ...] = () executables: tuple[str, ...] = ()
# sets service requirements that must be started prior to this service starting # sets service requirements that must be started prior to this service starting
dependencies: Tuple[str, ...] = () dependencies: tuple[str, ...] = ()
# group string allows grouping services together # group string allows grouping services together
group: Optional[str] = None group: Optional[str] = None
# private, per-node directories required by this service # private, per-node directories required by this service
dirs: Tuple[str, ...] = () dirs: tuple[str, ...] = ()
# config files written by this service # config files written by this service
configs: Tuple[str, ...] = () configs: tuple[str, ...] = ()
# config file data # config file data
config_data: Dict[str, str] = {} config_data: dict[str, str] = {}
# list of startup commands # list of startup commands
startup: Tuple[str, ...] = () startup: tuple[str, ...] = ()
# list of shutdown commands # list of shutdown commands
shutdown: Tuple[str, ...] = () shutdown: tuple[str, ...] = ()
# list of validate commands # list of validate commands
validate: Tuple[str, ...] = () validate: tuple[str, ...] = ()
# validation mode, used to determine startup success # validation mode, used to determine startup success
validation_mode: ServiceMode = ServiceMode.NON_BLOCKING validation_mode: ServiceMode = ServiceMode.NON_BLOCKING
@ -723,7 +714,7 @@ class CoreService:
configuration is used to override their default parameters. configuration is used to override their default parameters.
""" """
self.custom: bool = True self.custom: bool = True
self.config_data: Dict[str, str] = self.__class__.config_data.copy() self.config_data: dict[str, str] = self.__class__.config_data.copy()
@classmethod @classmethod
def on_load(cls) -> None: def on_load(cls) -> None:
@ -742,7 +733,7 @@ class CoreService:
return cls.configs return cls.configs
@classmethod @classmethod
def generate_config(cls, node: CoreNode, filename: str) -> None: def generate_config(cls, node: CoreNode, filename: str) -> str:
""" """
Generate configuration file given a node object. The filename is Generate configuration file given a node object. The filename is
provided to allow for multiple config files. provided to allow for multiple config files.
@ -751,7 +742,7 @@ class CoreService:
:param node: node to generate config for :param node: node to generate config for
:param filename: file name to generate config for :param filename: file name to generate config for
:return: nothing :return: generated config
""" """
raise NotImplementedError raise NotImplementedError

View file

@ -1,5 +1,3 @@
from typing import Tuple
from core.emane.nodes import EmaneNet from core.emane.nodes import EmaneNet
from core.nodes.base import CoreNode from core.nodes.base import CoreNode
from core.services.coreservices import CoreService from core.services.coreservices import CoreService
@ -9,14 +7,14 @@ from core.xml import emanexml
class EmaneTransportService(CoreService): class EmaneTransportService(CoreService):
name: str = "transportd" name: str = "transportd"
group: str = "EMANE" group: str = "EMANE"
executables: Tuple[str, ...] = ("emanetransportd", "emanegentransportxml") executables: tuple[str, ...] = ("emanetransportd", "emanegentransportxml")
dependencies: Tuple[str, ...] = () dependencies: tuple[str, ...] = ()
dirs: Tuple[str, ...] = () dirs: tuple[str, ...] = ()
configs: Tuple[str, ...] = ("emanetransport.sh",) configs: tuple[str, ...] = ("emanetransport.sh",)
startup: Tuple[str, ...] = (f"bash {configs[0]}",) startup: tuple[str, ...] = (f"bash {configs[0]}",)
validate: Tuple[str, ...] = (f"pidof {executables[0]}",) validate: tuple[str, ...] = (f"pidof {executables[0]}",)
validation_timer: float = 0.5 validation_timer: float = 0.5
shutdown: Tuple[str, ...] = (f"killall {executables[0]}",) shutdown: tuple[str, ...] = (f"killall {executables[0]}",)
@classmethod @classmethod
def generate_config(cls, node: CoreNode, filename: str) -> str: def generate_config(cls, node: CoreNode, filename: str) -> str:

View file

@ -2,7 +2,7 @@
frr.py: defines routing services provided by FRRouting. frr.py: defines routing services provided by FRRouting.
Assumes installation of FRR via https://deb.frrouting.org/ Assumes installation of FRR via https://deb.frrouting.org/
""" """
from typing import Optional, Tuple from typing import Optional
import netaddr import netaddr
@ -30,16 +30,16 @@ def is_wireless(node: NodeBase) -> bool:
class FRRZebra(CoreService): class FRRZebra(CoreService):
name: str = "FRRzebra" name: str = "FRRzebra"
group: str = "FRR" group: str = "FRR"
dirs: Tuple[str, ...] = ("/usr/local/etc/frr", "/var/run/frr", "/var/log/frr") dirs: tuple[str, ...] = ("/usr/local/etc/frr", "/var/run/frr", "/var/log/frr")
configs: Tuple[str, ...] = ( configs: tuple[str, ...] = (
"/usr/local/etc/frr/frr.conf", "/usr/local/etc/frr/frr.conf",
"frrboot.sh", "frrboot.sh",
"/usr/local/etc/frr/vtysh.conf", "/usr/local/etc/frr/vtysh.conf",
"/usr/local/etc/frr/daemons", "/usr/local/etc/frr/daemons",
) )
startup: Tuple[str, ...] = ("bash frrboot.sh zebra",) startup: tuple[str, ...] = ("bash frrboot.sh zebra",)
shutdown: Tuple[str, ...] = ("killall zebra",) shutdown: tuple[str, ...] = ("killall zebra",)
validate: Tuple[str, ...] = ("pidof zebra",) validate: tuple[str, ...] = ("pidof zebra",)
@classmethod @classmethod
def generate_config(cls, node: CoreNode, filename: str) -> str: def generate_config(cls, node: CoreNode, filename: str) -> str:
@ -75,7 +75,7 @@ class FRRZebra(CoreService):
# we could verify here that filename == frr.conf # we could verify here that filename == frr.conf
cfg = "" cfg = ""
for iface in node.get_ifaces(): for iface in node.get_ifaces():
cfg += "interface %s\n" % iface.name cfg += f"interface {iface.name}\n"
# include control interfaces in addressing but not routing daemons # include control interfaces in addressing but not routing daemons
if iface.control: if iface.control:
cfg += " " cfg += " "
@ -127,11 +127,11 @@ class FRRZebra(CoreService):
""" """
address = str(ip.ip) address = str(ip.ip)
if netaddr.valid_ipv4(address): if netaddr.valid_ipv4(address):
return "ip address %s" % ip return f"ip address {ip}"
elif netaddr.valid_ipv6(address): elif netaddr.valid_ipv6(address):
return "ipv6 address %s" % ip return f"ipv6 address {ip}"
else: else:
raise ValueError("invalid address: %s", ip) raise ValueError(f"invalid address: {ip}")
@classmethod @classmethod
def generate_frr_boot(cls, node: CoreNode) -> str: def generate_frr_boot(cls, node: CoreNode) -> str:
@ -144,16 +144,16 @@ class FRRZebra(CoreService):
frr_sbin_search = node.session.options.get( frr_sbin_search = node.session.options.get(
"frr_sbin_search", '"/usr/local/sbin /usr/sbin /usr/lib/frr"' "frr_sbin_search", '"/usr/local/sbin /usr/sbin /usr/lib/frr"'
) )
cfg = """\ cfg = f"""\
#!/bin/sh #!/bin/sh
# auto-generated by zebra service (frr.py) # auto-generated by zebra service (frr.py)
FRR_CONF=%s FRR_CONF={cls.configs[0]}
FRR_SBIN_SEARCH=%s FRR_SBIN_SEARCH={frr_sbin_search}
FRR_BIN_SEARCH=%s FRR_BIN_SEARCH={frr_bin_search}
FRR_STATE_DIR=%s FRR_STATE_DIR={FRR_STATE_DIR}
searchforprog() searchforprog()
{ {{
prog=$1 prog=$1
searchpath=$@ searchpath=$@
ret= ret=
@ -164,10 +164,10 @@ searchforprog()
fi fi
done done
echo $ret echo $ret
} }}
confcheck() confcheck()
{ {{
CONF_DIR=`dirname $FRR_CONF` CONF_DIR=`dirname $FRR_CONF`
# if /etc/frr exists, point /etc/frr/frr.conf -> CONF_DIR # if /etc/frr exists, point /etc/frr/frr.conf -> CONF_DIR
if [ "$CONF_DIR" != "/etc/frr" ] && [ -d /etc/frr ] && [ ! -e /etc/frr/frr.conf ]; then if [ "$CONF_DIR" != "/etc/frr" ] && [ -d /etc/frr ] && [ ! -e /etc/frr/frr.conf ]; then
@ -177,10 +177,10 @@ confcheck()
if [ "$CONF_DIR" != "/etc/frr" ] && [ -d /etc/frr ] && [ ! -e /etc/frr/vtysh.conf ]; then if [ "$CONF_DIR" != "/etc/frr" ] && [ -d /etc/frr ] && [ ! -e /etc/frr/vtysh.conf ]; then
ln -s $CONF_DIR/vtysh.conf /etc/frr/vtysh.conf ln -s $CONF_DIR/vtysh.conf /etc/frr/vtysh.conf
fi fi
} }}
bootdaemon() bootdaemon()
{ {{
FRR_SBIN_DIR=$(searchforprog $1 $FRR_SBIN_SEARCH) FRR_SBIN_DIR=$(searchforprog $1 $FRR_SBIN_SEARCH)
if [ "z$FRR_SBIN_DIR" = "z" ]; then if [ "z$FRR_SBIN_DIR" = "z" ]; then
echo "ERROR: FRR's '$1' daemon not found in search path:" echo "ERROR: FRR's '$1' daemon not found in search path:"
@ -207,10 +207,10 @@ bootdaemon()
echo "ERROR: FRR's '$1' daemon failed to start!:" echo "ERROR: FRR's '$1' daemon failed to start!:"
return 1 return 1
fi fi
} }}
bootfrr() bootfrr()
{ {{
FRR_BIN_DIR=$(searchforprog 'vtysh' $FRR_BIN_SEARCH) FRR_BIN_DIR=$(searchforprog 'vtysh' $FRR_BIN_SEARCH)
if [ "z$FRR_BIN_DIR" = "z" ]; then if [ "z$FRR_BIN_DIR" = "z" ]; then
echo "ERROR: FRR's 'vtysh' program not found in search path:" echo "ERROR: FRR's 'vtysh' program not found in search path:"
@ -229,8 +229,8 @@ bootfrr()
bootdaemon "staticd" bootdaemon "staticd"
fi fi
for r in rip ripng ospf6 ospf bgp babel isis; do for r in rip ripng ospf6 ospf bgp babel isis; do
if grep -q "^router \\<${r}\\>" $FRR_CONF; then if grep -q "^router \\<${{r}}\\>" $FRR_CONF; then
bootdaemon "${r}d" bootdaemon "${{r}}d"
fi fi
done done
@ -239,7 +239,7 @@ bootfrr()
fi fi
$FRR_BIN_DIR/vtysh -b $FRR_BIN_DIR/vtysh -b
} }}
if [ "$1" != "zebra" ]; then if [ "$1" != "zebra" ]; then
echo "WARNING: '$1': all FRR daemons are launched by the 'zebra' service!" echo "WARNING: '$1': all FRR daemons are launched by the 'zebra' service!"
@ -248,12 +248,7 @@ fi
confcheck confcheck
bootfrr bootfrr
""" % ( """
cls.configs[0],
frr_sbin_search,
frr_bin_search,
FRR_STATE_DIR,
)
for iface in node.get_ifaces(): for iface in node.get_ifaces():
cfg += f"ip link set dev {iface.name} down\n" cfg += f"ip link set dev {iface.name} down\n"
cfg += "sleep 1\n" cfg += "sleep 1\n"
@ -337,7 +332,7 @@ class FrrService(CoreService):
name: Optional[str] = None name: Optional[str] = None
group: str = "FRR" group: str = "FRR"
dependencies: Tuple[str, ...] = ("FRRzebra",) dependencies: tuple[str, ...] = ("FRRzebra",)
meta: str = "The config file for this service can be found in the Zebra service." meta: str = "The config file for this service can be found in the Zebra service."
ipv4_routing: bool = False ipv4_routing: bool = False
ipv6_routing: bool = False ipv6_routing: bool = False
@ -388,8 +383,8 @@ class FRROspfv2(FrrService):
""" """
name: str = "FRROSPFv2" name: str = "FRROSPFv2"
shutdown: Tuple[str, ...] = ("killall ospfd",) shutdown: tuple[str, ...] = ("killall ospfd",)
validate: Tuple[str, ...] = ("pidof ospfd",) validate: tuple[str, ...] = ("pidof ospfd",)
ipv4_routing: bool = True ipv4_routing: bool = True
@staticmethod @staticmethod
@ -424,7 +419,7 @@ class FRROspfv2(FrrService):
def generate_frr_config(cls, node: CoreNode) -> str: def generate_frr_config(cls, node: CoreNode) -> str:
cfg = "router ospf\n" cfg = "router ospf\n"
rtrid = cls.router_id(node) rtrid = cls.router_id(node)
cfg += " router-id %s\n" % rtrid cfg += f" router-id {rtrid}\n"
# network 10.0.0.0/24 area 0 # network 10.0.0.0/24 area 0
for iface in node.get_ifaces(control=False): for iface in node.get_ifaces(control=False):
for ip4 in iface.ip4s: for ip4 in iface.ip4s:
@ -458,8 +453,8 @@ class FRROspfv3(FrrService):
""" """
name: str = "FRROSPFv3" name: str = "FRROSPFv3"
shutdown: Tuple[str, ...] = ("killall ospf6d",) shutdown: tuple[str, ...] = ("killall ospf6d",)
validate: Tuple[str, ...] = ("pidof ospf6d",) validate: tuple[str, ...] = ("pidof ospf6d",)
ipv4_routing: bool = True ipv4_routing: bool = True
ipv6_routing: bool = True ipv6_routing: bool = True
@ -486,7 +481,7 @@ class FRROspfv3(FrrService):
""" """
minmtu = cls.min_mtu(iface) minmtu = cls.min_mtu(iface)
if minmtu < iface.mtu: if minmtu < iface.mtu:
return " ipv6 ospf6 ifmtu %d\n" % minmtu return f" ipv6 ospf6 ifmtu {minmtu:d}\n"
else: else:
return "" return ""
@ -504,9 +499,9 @@ class FRROspfv3(FrrService):
def generate_frr_config(cls, node: CoreNode) -> str: def generate_frr_config(cls, node: CoreNode) -> str:
cfg = "router ospf6\n" cfg = "router ospf6\n"
rtrid = cls.router_id(node) rtrid = cls.router_id(node)
cfg += " router-id %s\n" % rtrid cfg += f" router-id {rtrid}\n"
for iface in node.get_ifaces(control=False): for iface in node.get_ifaces(control=False):
cfg += " interface %s area 0.0.0.0\n" % iface.name cfg += f" interface {iface.name} area 0.0.0.0\n"
cfg += "!\n" cfg += "!\n"
return cfg return cfg
@ -523,8 +518,8 @@ class FRRBgp(FrrService):
""" """
name: str = "FRRBGP" name: str = "FRRBGP"
shutdown: Tuple[str, ...] = ("killall bgpd",) shutdown: tuple[str, ...] = ("killall bgpd",)
validate: Tuple[str, ...] = ("pidof bgpd",) validate: tuple[str, ...] = ("pidof bgpd",)
custom_needed: bool = True custom_needed: bool = True
ipv4_routing: bool = True ipv4_routing: bool = True
ipv6_routing: bool = True ipv6_routing: bool = True
@ -534,9 +529,9 @@ class FRRBgp(FrrService):
cfg = "!\n! BGP configuration\n!\n" cfg = "!\n! BGP configuration\n!\n"
cfg += "! You should configure the AS number below,\n" cfg += "! You should configure the AS number below,\n"
cfg += "! along with this router's peers.\n!\n" cfg += "! along with this router's peers.\n!\n"
cfg += "router bgp %s\n" % node.id cfg += f"router bgp {node.id}\n"
rtrid = cls.router_id(node) rtrid = cls.router_id(node)
cfg += " bgp router-id %s\n" % rtrid cfg += f" bgp router-id {rtrid}\n"
cfg += " redistribute connected\n" cfg += " redistribute connected\n"
cfg += "! neighbor 1.2.3.4 remote-as 555\n!\n" cfg += "! neighbor 1.2.3.4 remote-as 555\n!\n"
return cfg return cfg
@ -548,8 +543,8 @@ class FRRRip(FrrService):
""" """
name: str = "FRRRIP" name: str = "FRRRIP"
shutdown: Tuple[str, ...] = ("killall ripd",) shutdown: tuple[str, ...] = ("killall ripd",)
validate: Tuple[str, ...] = ("pidof ripd",) validate: tuple[str, ...] = ("pidof ripd",)
ipv4_routing: bool = True ipv4_routing: bool = True
@classmethod @classmethod
@ -571,8 +566,8 @@ class FRRRipng(FrrService):
""" """
name: str = "FRRRIPNG" name: str = "FRRRIPNG"
shutdown: Tuple[str, ...] = ("killall ripngd",) shutdown: tuple[str, ...] = ("killall ripngd",)
validate: Tuple[str, ...] = ("pidof ripngd",) validate: tuple[str, ...] = ("pidof ripngd",)
ipv6_routing: bool = True ipv6_routing: bool = True
@classmethod @classmethod
@ -595,15 +590,15 @@ class FRRBabel(FrrService):
""" """
name: str = "FRRBabel" name: str = "FRRBabel"
shutdown: Tuple[str, ...] = ("killall babeld",) shutdown: tuple[str, ...] = ("killall babeld",)
validate: Tuple[str, ...] = ("pidof babeld",) validate: tuple[str, ...] = ("pidof babeld",)
ipv6_routing: bool = True ipv6_routing: bool = True
@classmethod @classmethod
def generate_frr_config(cls, node: CoreNode) -> str: def generate_frr_config(cls, node: CoreNode) -> str:
cfg = "router babel\n" cfg = "router babel\n"
for iface in node.get_ifaces(control=False): for iface in node.get_ifaces(control=False):
cfg += " network %s\n" % iface.name cfg += f" network {iface.name}\n"
cfg += " redistribute static\n redistribute ipv4 connected\n" cfg += " redistribute static\n redistribute ipv4 connected\n"
return cfg return cfg
@ -621,8 +616,8 @@ class FRRpimd(FrrService):
""" """
name: str = "FRRpimd" name: str = "FRRpimd"
shutdown: Tuple[str, ...] = ("killall pimd",) shutdown: tuple[str, ...] = ("killall pimd",)
validate: Tuple[str, ...] = ("pidof pimd",) validate: tuple[str, ...] = ("pidof pimd",)
ipv4_routing: bool = True ipv4_routing: bool = True
@classmethod @classmethod
@ -636,8 +631,8 @@ class FRRpimd(FrrService):
cfg += "router igmp\n!\n" cfg += "router igmp\n!\n"
cfg += "router pim\n" cfg += "router pim\n"
cfg += " !ip pim rp-address 10.0.0.1\n" cfg += " !ip pim rp-address 10.0.0.1\n"
cfg += " ip pim bsr-candidate %s\n" % ifname cfg += f" ip pim bsr-candidate {ifname}\n"
cfg += " ip pim rp-candidate %s\n" % ifname cfg += f" ip pim rp-candidate {ifname}\n"
cfg += " !ip pim spt-threshold interval 10 bytes 80000\n" cfg += " !ip pim spt-threshold interval 10 bytes 80000\n"
return cfg return cfg
@ -654,8 +649,8 @@ class FRRIsis(FrrService):
""" """
name: str = "FRRISIS" name: str = "FRRISIS"
shutdown: Tuple[str, ...] = ("killall isisd",) shutdown: tuple[str, ...] = ("killall isisd",)
validate: Tuple[str, ...] = ("pidof isisd",) validate: tuple[str, ...] = ("pidof isisd",)
ipv4_routing: bool = True ipv4_routing: bool = True
ipv6_routing: bool = True ipv6_routing: bool = True
@ -672,7 +667,7 @@ class FRRIsis(FrrService):
@classmethod @classmethod
def generate_frr_config(cls, node: CoreNode) -> str: def generate_frr_config(cls, node: CoreNode) -> str:
cfg = "router isis DEFAULT\n" cfg = "router isis DEFAULT\n"
cfg += " net 47.0001.0000.1900.%04x.00\n" % node.id cfg += f" net 47.0001.0000.1900.{node.id:04x}.00\n"
cfg += " metric-style wide\n" cfg += " metric-style wide\n"
cfg += " is-type level-2-only\n" cfg += " is-type level-2-only\n"
cfg += "!\n" cfg += "!\n"

View file

@ -2,7 +2,7 @@
nrl.py: defines services provided by NRL protolib tools hosted here: nrl.py: defines services provided by NRL protolib tools hosted here:
http://www.nrl.navy.mil/itd/ncs/products http://www.nrl.navy.mil/itd/ncs/products
""" """
from typing import Optional, Tuple from typing import Optional
from core import utils from core import utils
from core.nodes.base import CoreNode from core.nodes.base import CoreNode
@ -33,29 +33,29 @@ class NrlService(CoreService):
ip4 = iface.get_ip4() ip4 = iface.get_ip4()
if ip4: if ip4:
return f"{ip4.ip}/{prefixlen}" return f"{ip4.ip}/{prefixlen}"
return "0.0.0.0/%s" % prefixlen return f"0.0.0.0/{prefixlen}"
class MgenSinkService(NrlService): class MgenSinkService(NrlService):
name: str = "MGEN_Sink" name: str = "MGEN_Sink"
executables: Tuple[str, ...] = ("mgen",) executables: tuple[str, ...] = ("mgen",)
configs: Tuple[str, ...] = ("sink.mgen",) configs: tuple[str, ...] = ("sink.mgen",)
startup: Tuple[str, ...] = ("mgen input sink.mgen",) startup: tuple[str, ...] = ("mgen input sink.mgen",)
validate: Tuple[str, ...] = ("pidof mgen",) validate: tuple[str, ...] = ("pidof mgen",)
shutdown: Tuple[str, ...] = ("killall mgen",) shutdown: tuple[str, ...] = ("killall mgen",)
@classmethod @classmethod
def generate_config(cls, node: CoreNode, filename: str) -> str: def generate_config(cls, node: CoreNode, filename: str) -> str:
cfg = "0.0 LISTEN UDP 5000\n" cfg = "0.0 LISTEN UDP 5000\n"
for iface in node.get_ifaces(): for iface in node.get_ifaces():
name = utils.sysctl_devname(iface.name) name = utils.sysctl_devname(iface.name)
cfg += "0.0 Join 224.225.1.2 INTERFACE %s\n" % name cfg += f"0.0 Join 224.225.1.2 INTERFACE {name}\n"
return cfg return cfg
@classmethod @classmethod
def get_startup(cls, node: CoreNode) -> Tuple[str, ...]: def get_startup(cls, node: CoreNode) -> tuple[str, ...]:
cmd = cls.startup[0] cmd = cls.startup[0]
cmd += " output /tmp/mgen_%s.log" % node.name cmd += f" output /tmp/mgen_{node.name}.log"
return (cmd,) return (cmd,)
@ -65,23 +65,23 @@ class NrlNhdp(NrlService):
""" """
name: str = "NHDP" name: str = "NHDP"
executables: Tuple[str, ...] = ("nrlnhdp",) executables: tuple[str, ...] = ("nrlnhdp",)
startup: Tuple[str, ...] = ("nrlnhdp",) startup: tuple[str, ...] = ("nrlnhdp",)
shutdown: Tuple[str, ...] = ("killall nrlnhdp",) shutdown: tuple[str, ...] = ("killall nrlnhdp",)
validate: Tuple[str, ...] = ("pidof nrlnhdp",) validate: tuple[str, ...] = ("pidof nrlnhdp",)
@classmethod @classmethod
def get_startup(cls, node: CoreNode) -> Tuple[str, ...]: def get_startup(cls, node: CoreNode) -> tuple[str, ...]:
""" """
Generate the appropriate command-line based on node interfaces. Generate the appropriate command-line based on node interfaces.
""" """
cmd = cls.startup[0] cmd = cls.startup[0]
cmd += " -l /var/log/nrlnhdp.log" cmd += " -l /var/log/nrlnhdp.log"
cmd += " -rpipe %s_nhdp" % node.name cmd += f" -rpipe {node.name}_nhdp"
servicenames = map(lambda x: x.name, node.services) servicenames = map(lambda x: x.name, node.services)
if "SMF" in servicenames: if "SMF" in servicenames:
cmd += " -flooding ecds" cmd += " -flooding ecds"
cmd += " -smfClient %s_smf" % node.name cmd += f" -smfClient {node.name}_smf"
ifaces = node.get_ifaces(control=False) ifaces = node.get_ifaces(control=False)
if len(ifaces) > 0: if len(ifaces) > 0:
iface_names = map(lambda x: x.name, ifaces) iface_names = map(lambda x: x.name, ifaces)
@ -96,11 +96,11 @@ class NrlSmf(NrlService):
""" """
name: str = "SMF" name: str = "SMF"
executables: Tuple[str, ...] = ("nrlsmf",) executables: tuple[str, ...] = ("nrlsmf",)
startup: Tuple[str, ...] = ("bash startsmf.sh",) startup: tuple[str, ...] = ("bash startsmf.sh",)
shutdown: Tuple[str, ...] = ("killall nrlsmf",) shutdown: tuple[str, ...] = ("killall nrlsmf",)
validate: Tuple[str, ...] = ("pidof nrlsmf",) validate: tuple[str, ...] = ("pidof nrlsmf",)
configs: Tuple[str, ...] = ("startsmf.sh",) configs: tuple[str, ...] = ("startsmf.sh",)
@classmethod @classmethod
def generate_config(cls, node: CoreNode, filename: str) -> str: def generate_config(cls, node: CoreNode, filename: str) -> str:
@ -112,7 +112,7 @@ class NrlSmf(NrlService):
cfg = "#!/bin/sh\n" cfg = "#!/bin/sh\n"
cfg += "# auto-generated by nrl.py:NrlSmf.generateconfig()\n" cfg += "# auto-generated by nrl.py:NrlSmf.generateconfig()\n"
comments = "" comments = ""
cmd = "nrlsmf instance %s_smf" % node.name cmd = f"nrlsmf instance {node.name}_smf"
servicenames = map(lambda x: x.name, node.services) servicenames = map(lambda x: x.name, node.services)
ifaces = node.get_ifaces(control=False) ifaces = node.get_ifaces(control=False)
@ -142,13 +142,13 @@ class NrlOlsr(NrlService):
""" """
name: str = "OLSR" name: str = "OLSR"
executables: Tuple[str, ...] = ("nrlolsrd",) executables: tuple[str, ...] = ("nrlolsrd",)
startup: Tuple[str, ...] = ("nrlolsrd",) startup: tuple[str, ...] = ("nrlolsrd",)
shutdown: Tuple[str, ...] = ("killall nrlolsrd",) shutdown: tuple[str, ...] = ("killall nrlolsrd",)
validate: Tuple[str, ...] = ("pidof nrlolsrd",) validate: tuple[str, ...] = ("pidof nrlolsrd",)
@classmethod @classmethod
def get_startup(cls, node: CoreNode) -> Tuple[str, ...]: def get_startup(cls, node: CoreNode) -> tuple[str, ...]:
""" """
Generate the appropriate command-line based on node interfaces. Generate the appropriate command-line based on node interfaces.
""" """
@ -157,13 +157,13 @@ class NrlOlsr(NrlService):
ifaces = node.get_ifaces() ifaces = node.get_ifaces()
if len(ifaces) > 0: if len(ifaces) > 0:
iface = ifaces[0] iface = ifaces[0]
cmd += " -i %s" % iface.name cmd += f" -i {iface.name}"
cmd += " -l /var/log/nrlolsrd.log" cmd += " -l /var/log/nrlolsrd.log"
cmd += " -rpipe %s_olsr" % node.name cmd += f" -rpipe {node.name}_olsr"
servicenames = map(lambda x: x.name, node.services) servicenames = map(lambda x: x.name, node.services)
if "SMF" in servicenames and "NHDP" not in servicenames: if "SMF" in servicenames and "NHDP" not in servicenames:
cmd += " -flooding s-mpr" cmd += " -flooding s-mpr"
cmd += " -smfClient %s_smf" % node.name cmd += f" -smfClient {node.name}_smf"
if "zebra" in servicenames: if "zebra" in servicenames:
cmd += " -z" cmd += " -z"
return (cmd,) return (cmd,)
@ -175,23 +175,23 @@ class NrlOlsrv2(NrlService):
""" """
name: str = "OLSRv2" name: str = "OLSRv2"
executables: Tuple[str, ...] = ("nrlolsrv2",) executables: tuple[str, ...] = ("nrlolsrv2",)
startup: Tuple[str, ...] = ("nrlolsrv2",) startup: tuple[str, ...] = ("nrlolsrv2",)
shutdown: Tuple[str, ...] = ("killall nrlolsrv2",) shutdown: tuple[str, ...] = ("killall nrlolsrv2",)
validate: Tuple[str, ...] = ("pidof nrlolsrv2",) validate: tuple[str, ...] = ("pidof nrlolsrv2",)
@classmethod @classmethod
def get_startup(cls, node: CoreNode) -> Tuple[str, ...]: def get_startup(cls, node: CoreNode) -> tuple[str, ...]:
""" """
Generate the appropriate command-line based on node interfaces. Generate the appropriate command-line based on node interfaces.
""" """
cmd = cls.startup[0] cmd = cls.startup[0]
cmd += " -l /var/log/nrlolsrv2.log" cmd += " -l /var/log/nrlolsrv2.log"
cmd += " -rpipe %s_olsrv2" % node.name cmd += f" -rpipe {node.name}_olsrv2"
servicenames = map(lambda x: x.name, node.services) servicenames = map(lambda x: x.name, node.services)
if "SMF" in servicenames: if "SMF" in servicenames:
cmd += " -flooding ecds" cmd += " -flooding ecds"
cmd += " -smfClient %s_smf" % node.name cmd += f" -smfClient {node.name}_smf"
cmd += " -p olsr" cmd += " -p olsr"
ifaces = node.get_ifaces(control=False) ifaces = node.get_ifaces(control=False)
if len(ifaces) > 0: if len(ifaces) > 0:
@ -207,15 +207,15 @@ class OlsrOrg(NrlService):
""" """
name: str = "OLSRORG" name: str = "OLSRORG"
executables: Tuple[str, ...] = ("olsrd",) executables: tuple[str, ...] = ("olsrd",)
configs: Tuple[str, ...] = ("/etc/olsrd/olsrd.conf",) configs: tuple[str, ...] = ("/etc/olsrd/olsrd.conf",)
dirs: Tuple[str, ...] = ("/etc/olsrd",) dirs: tuple[str, ...] = ("/etc/olsrd",)
startup: Tuple[str, ...] = ("olsrd",) startup: tuple[str, ...] = ("olsrd",)
shutdown: Tuple[str, ...] = ("killall olsrd",) shutdown: tuple[str, ...] = ("killall olsrd",)
validate: Tuple[str, ...] = ("pidof olsrd",) validate: tuple[str, ...] = ("pidof olsrd",)
@classmethod @classmethod
def get_startup(cls, node: CoreNode) -> Tuple[str, ...]: def get_startup(cls, node: CoreNode) -> tuple[str, ...]:
""" """
Generate the appropriate command-line based on node interfaces. Generate the appropriate command-line based on node interfaces.
""" """
@ -558,11 +558,11 @@ class MgenActor(NrlService):
# a unique name is required, without spaces # a unique name is required, without spaces
name: str = "MgenActor" name: str = "MgenActor"
group: str = "ProtoSvc" group: str = "ProtoSvc"
executables: Tuple[str, ...] = ("mgen",) executables: tuple[str, ...] = ("mgen",)
configs: Tuple[str, ...] = ("start_mgen_actor.sh",) configs: tuple[str, ...] = ("start_mgen_actor.sh",)
startup: Tuple[str, ...] = ("bash start_mgen_actor.sh",) startup: tuple[str, ...] = ("bash start_mgen_actor.sh",)
validate: Tuple[str, ...] = ("pidof mgen",) validate: tuple[str, ...] = ("pidof mgen",)
shutdown: Tuple[str, ...] = ("killall mgen",) shutdown: tuple[str, ...] = ("killall mgen",)
@classmethod @classmethod
def generate_config(cls, node: CoreNode, filename: str) -> str: def generate_config(cls, node: CoreNode, filename: str) -> str:
@ -574,7 +574,7 @@ class MgenActor(NrlService):
cfg = "#!/bin/sh\n" cfg = "#!/bin/sh\n"
cfg += "# auto-generated by nrl.py:MgenActor.generateconfig()\n" cfg += "# auto-generated by nrl.py:MgenActor.generateconfig()\n"
comments = "" comments = ""
cmd = "mgenBasicActor.py -n %s -a 0.0.0.0" % node.name cmd = f"mgenBasicActor.py -n {node.name} -a 0.0.0.0"
ifaces = node.get_ifaces(control=False) ifaces = node.get_ifaces(control=False)
if len(ifaces) == 0: if len(ifaces) == 0:
return "" return ""

View file

@ -1,7 +1,7 @@
""" """
quagga.py: defines routing services provided by Quagga. quagga.py: defines routing services provided by Quagga.
""" """
from typing import Optional, Tuple from typing import Optional
import netaddr import netaddr
@ -29,15 +29,15 @@ def is_wireless(node: NodeBase) -> bool:
class Zebra(CoreService): class Zebra(CoreService):
name: str = "zebra" name: str = "zebra"
group: str = "Quagga" group: str = "Quagga"
dirs: Tuple[str, ...] = ("/usr/local/etc/quagga", "/var/run/quagga") dirs: tuple[str, ...] = ("/usr/local/etc/quagga", "/var/run/quagga")
configs: Tuple[str, ...] = ( configs: tuple[str, ...] = (
"/usr/local/etc/quagga/Quagga.conf", "/usr/local/etc/quagga/Quagga.conf",
"quaggaboot.sh", "quaggaboot.sh",
"/usr/local/etc/quagga/vtysh.conf", "/usr/local/etc/quagga/vtysh.conf",
) )
startup: Tuple[str, ...] = ("bash quaggaboot.sh zebra",) startup: tuple[str, ...] = ("bash quaggaboot.sh zebra",)
shutdown: Tuple[str, ...] = ("killall zebra",) shutdown: tuple[str, ...] = ("killall zebra",)
validate: Tuple[str, ...] = ("pidof zebra",) validate: tuple[str, ...] = ("pidof zebra",)
@classmethod @classmethod
def generate_config(cls, node: CoreNode, filename: str) -> str: def generate_config(cls, node: CoreNode, filename: str) -> str:
@ -71,7 +71,7 @@ class Zebra(CoreService):
# we could verify here that filename == Quagga.conf # we could verify here that filename == Quagga.conf
cfg = "" cfg = ""
for iface in node.get_ifaces(): for iface in node.get_ifaces():
cfg += "interface %s\n" % iface.name cfg += f"interface {iface.name}\n"
# include control interfaces in addressing but not routing daemons # include control interfaces in addressing but not routing daemons
if iface.control: if iface.control:
cfg += " " cfg += " "
@ -123,11 +123,11 @@ class Zebra(CoreService):
""" """
address = str(ip.ip) address = str(ip.ip)
if netaddr.valid_ipv4(address): if netaddr.valid_ipv4(address):
return "ip address %s" % ip return f"ip address {ip}"
elif netaddr.valid_ipv6(address): elif netaddr.valid_ipv6(address):
return "ipv6 address %s" % ip return f"ipv6 address {ip}"
else: else:
raise ValueError("invalid address: %s", ip) raise ValueError(f"invalid address: {ip}")
@classmethod @classmethod
def generate_quagga_boot(cls, node: CoreNode) -> str: def generate_quagga_boot(cls, node: CoreNode) -> str:
@ -140,16 +140,16 @@ class Zebra(CoreService):
quagga_sbin_search = node.session.options.get( quagga_sbin_search = node.session.options.get(
"quagga_sbin_search", '"/usr/local/sbin /usr/sbin /usr/lib/quagga"' "quagga_sbin_search", '"/usr/local/sbin /usr/sbin /usr/lib/quagga"'
) )
return """\ return f"""\
#!/bin/sh #!/bin/sh
# auto-generated by zebra service (quagga.py) # auto-generated by zebra service (quagga.py)
QUAGGA_CONF=%s QUAGGA_CONF={cls.configs[0]}
QUAGGA_SBIN_SEARCH=%s QUAGGA_SBIN_SEARCH={quagga_sbin_search}
QUAGGA_BIN_SEARCH=%s QUAGGA_BIN_SEARCH={quagga_bin_search}
QUAGGA_STATE_DIR=%s QUAGGA_STATE_DIR={QUAGGA_STATE_DIR}
searchforprog() searchforprog()
{ {{
prog=$1 prog=$1
searchpath=$@ searchpath=$@
ret= ret=
@ -160,10 +160,10 @@ searchforprog()
fi fi
done done
echo $ret echo $ret
} }}
confcheck() confcheck()
{ {{
CONF_DIR=`dirname $QUAGGA_CONF` CONF_DIR=`dirname $QUAGGA_CONF`
# if /etc/quagga exists, point /etc/quagga/Quagga.conf -> CONF_DIR # if /etc/quagga exists, point /etc/quagga/Quagga.conf -> CONF_DIR
if [ "$CONF_DIR" != "/etc/quagga" ] && [ -d /etc/quagga ] && [ ! -e /etc/quagga/Quagga.conf ]; then if [ "$CONF_DIR" != "/etc/quagga" ] && [ -d /etc/quagga ] && [ ! -e /etc/quagga/Quagga.conf ]; then
@ -173,10 +173,10 @@ confcheck()
if [ "$CONF_DIR" != "/etc/quagga" ] && [ -d /etc/quagga ] && [ ! -e /etc/quagga/vtysh.conf ]; then if [ "$CONF_DIR" != "/etc/quagga" ] && [ -d /etc/quagga ] && [ ! -e /etc/quagga/vtysh.conf ]; then
ln -s $CONF_DIR/vtysh.conf /etc/quagga/vtysh.conf ln -s $CONF_DIR/vtysh.conf /etc/quagga/vtysh.conf
fi fi
} }}
bootdaemon() bootdaemon()
{ {{
QUAGGA_SBIN_DIR=$(searchforprog $1 $QUAGGA_SBIN_SEARCH) QUAGGA_SBIN_DIR=$(searchforprog $1 $QUAGGA_SBIN_SEARCH)
if [ "z$QUAGGA_SBIN_DIR" = "z" ]; then if [ "z$QUAGGA_SBIN_DIR" = "z" ]; then
echo "ERROR: Quagga's '$1' daemon not found in search path:" echo "ERROR: Quagga's '$1' daemon not found in search path:"
@ -196,10 +196,10 @@ bootdaemon()
echo "ERROR: Quagga's '$1' daemon failed to start!:" echo "ERROR: Quagga's '$1' daemon failed to start!:"
return 1 return 1
fi fi
} }}
bootquagga() bootquagga()
{ {{
QUAGGA_BIN_DIR=$(searchforprog 'vtysh' $QUAGGA_BIN_SEARCH) QUAGGA_BIN_DIR=$(searchforprog 'vtysh' $QUAGGA_BIN_SEARCH)
if [ "z$QUAGGA_BIN_DIR" = "z" ]; then if [ "z$QUAGGA_BIN_DIR" = "z" ]; then
echo "ERROR: Quagga's 'vtysh' program not found in search path:" echo "ERROR: Quagga's 'vtysh' program not found in search path:"
@ -215,8 +215,8 @@ bootquagga()
bootdaemon "zebra" bootdaemon "zebra"
for r in rip ripng ospf6 ospf bgp babel; do for r in rip ripng ospf6 ospf bgp babel; do
if grep -q "^router \\<${r}\\>" $QUAGGA_CONF; then if grep -q "^router \\<${{r}}\\>" $QUAGGA_CONF; then
bootdaemon "${r}d" bootdaemon "${{r}}d"
fi fi
done done
@ -225,7 +225,7 @@ bootquagga()
fi fi
$QUAGGA_BIN_DIR/vtysh -b $QUAGGA_BIN_DIR/vtysh -b
} }}
if [ "$1" != "zebra" ]; then if [ "$1" != "zebra" ]; then
echo "WARNING: '$1': all Quagga daemons are launched by the 'zebra' service!" echo "WARNING: '$1': all Quagga daemons are launched by the 'zebra' service!"
@ -233,12 +233,7 @@ if [ "$1" != "zebra" ]; then
fi fi
confcheck confcheck
bootquagga bootquagga
""" % ( """
cls.configs[0],
quagga_sbin_search,
quagga_bin_search,
QUAGGA_STATE_DIR,
)
class QuaggaService(CoreService): class QuaggaService(CoreService):
@ -249,7 +244,7 @@ class QuaggaService(CoreService):
name: Optional[str] = None name: Optional[str] = None
group: str = "Quagga" group: str = "Quagga"
dependencies: Tuple[str, ...] = (Zebra.name,) dependencies: tuple[str, ...] = (Zebra.name,)
meta: str = "The config file for this service can be found in the Zebra service." meta: str = "The config file for this service can be found in the Zebra service."
ipv4_routing: bool = False ipv4_routing: bool = False
ipv6_routing: bool = False ipv6_routing: bool = False
@ -300,8 +295,8 @@ class Ospfv2(QuaggaService):
""" """
name: str = "OSPFv2" name: str = "OSPFv2"
shutdown: Tuple[str, ...] = ("killall ospfd",) shutdown: tuple[str, ...] = ("killall ospfd",)
validate: Tuple[str, ...] = ("pidof ospfd",) validate: tuple[str, ...] = ("pidof ospfd",)
ipv4_routing: bool = True ipv4_routing: bool = True
@staticmethod @staticmethod
@ -336,7 +331,7 @@ class Ospfv2(QuaggaService):
def generate_quagga_config(cls, node: CoreNode) -> str: def generate_quagga_config(cls, node: CoreNode) -> str:
cfg = "router ospf\n" cfg = "router ospf\n"
rtrid = cls.router_id(node) rtrid = cls.router_id(node)
cfg += " router-id %s\n" % rtrid cfg += f" router-id {rtrid}\n"
# network 10.0.0.0/24 area 0 # network 10.0.0.0/24 area 0
for iface in node.get_ifaces(control=False): for iface in node.get_ifaces(control=False):
for ip4 in iface.ip4s: for ip4 in iface.ip4s:
@ -369,8 +364,8 @@ class Ospfv3(QuaggaService):
""" """
name: str = "OSPFv3" name: str = "OSPFv3"
shutdown: Tuple[str, ...] = ("killall ospf6d",) shutdown: tuple[str, ...] = ("killall ospf6d",)
validate: Tuple[str, ...] = ("pidof ospf6d",) validate: tuple[str, ...] = ("pidof ospf6d",)
ipv4_routing: bool = True ipv4_routing: bool = True
ipv6_routing: bool = True ipv6_routing: bool = True
@ -397,7 +392,7 @@ class Ospfv3(QuaggaService):
""" """
minmtu = cls.min_mtu(iface) minmtu = cls.min_mtu(iface)
if minmtu < iface.mtu: if minmtu < iface.mtu:
return " ipv6 ospf6 ifmtu %d\n" % minmtu return f" ipv6 ospf6 ifmtu {minmtu:d}\n"
else: else:
return "" return ""
@ -416,9 +411,9 @@ class Ospfv3(QuaggaService):
cfg = "router ospf6\n" cfg = "router ospf6\n"
rtrid = cls.router_id(node) rtrid = cls.router_id(node)
cfg += " instance-id 65\n" cfg += " instance-id 65\n"
cfg += " router-id %s\n" % rtrid cfg += f" router-id {rtrid}\n"
for iface in node.get_ifaces(control=False): for iface in node.get_ifaces(control=False):
cfg += " interface %s area 0.0.0.0\n" % iface.name cfg += f" interface {iface.name} area 0.0.0.0\n"
cfg += "!\n" cfg += "!\n"
return cfg return cfg
@ -466,8 +461,8 @@ class Bgp(QuaggaService):
""" """
name: str = "BGP" name: str = "BGP"
shutdown: Tuple[str, ...] = ("killall bgpd",) shutdown: tuple[str, ...] = ("killall bgpd",)
validate: Tuple[str, ...] = ("pidof bgpd",) validate: tuple[str, ...] = ("pidof bgpd",)
custom_needed: bool = True custom_needed: bool = True
ipv4_routing: bool = True ipv4_routing: bool = True
ipv6_routing: bool = True ipv6_routing: bool = True
@ -477,9 +472,9 @@ class Bgp(QuaggaService):
cfg = "!\n! BGP configuration\n!\n" cfg = "!\n! BGP configuration\n!\n"
cfg += "! You should configure the AS number below,\n" cfg += "! You should configure the AS number below,\n"
cfg += "! along with this router's peers.\n!\n" cfg += "! along with this router's peers.\n!\n"
cfg += "router bgp %s\n" % node.id cfg += f"router bgp {node.id}\n"
rtrid = cls.router_id(node) rtrid = cls.router_id(node)
cfg += " bgp router-id %s\n" % rtrid cfg += f" bgp router-id {rtrid}\n"
cfg += " redistribute connected\n" cfg += " redistribute connected\n"
cfg += "! neighbor 1.2.3.4 remote-as 555\n!\n" cfg += "! neighbor 1.2.3.4 remote-as 555\n!\n"
return cfg return cfg
@ -491,8 +486,8 @@ class Rip(QuaggaService):
""" """
name: str = "RIP" name: str = "RIP"
shutdown: Tuple[str, ...] = ("killall ripd",) shutdown: tuple[str, ...] = ("killall ripd",)
validate: Tuple[str, ...] = ("pidof ripd",) validate: tuple[str, ...] = ("pidof ripd",)
ipv4_routing: bool = True ipv4_routing: bool = True
@classmethod @classmethod
@ -514,8 +509,8 @@ class Ripng(QuaggaService):
""" """
name: str = "RIPNG" name: str = "RIPNG"
shutdown: Tuple[str, ...] = ("killall ripngd",) shutdown: tuple[str, ...] = ("killall ripngd",)
validate: Tuple[str, ...] = ("pidof ripngd",) validate: tuple[str, ...] = ("pidof ripngd",)
ipv6_routing: bool = True ipv6_routing: bool = True
@classmethod @classmethod
@ -538,15 +533,15 @@ class Babel(QuaggaService):
""" """
name: str = "Babel" name: str = "Babel"
shutdown: Tuple[str, ...] = ("killall babeld",) shutdown: tuple[str, ...] = ("killall babeld",)
validate: Tuple[str, ...] = ("pidof babeld",) validate: tuple[str, ...] = ("pidof babeld",)
ipv6_routing: bool = True ipv6_routing: bool = True
@classmethod @classmethod
def generate_quagga_config(cls, node: CoreNode) -> str: def generate_quagga_config(cls, node: CoreNode) -> str:
cfg = "router babel\n" cfg = "router babel\n"
for iface in node.get_ifaces(control=False): for iface in node.get_ifaces(control=False):
cfg += " network %s\n" % iface.name cfg += f" network {iface.name}\n"
cfg += " redistribute static\n redistribute connected\n" cfg += " redistribute static\n redistribute connected\n"
return cfg return cfg
@ -564,8 +559,8 @@ class Xpimd(QuaggaService):
""" """
name: str = "Xpimd" name: str = "Xpimd"
shutdown: Tuple[str, ...] = ("killall xpimd",) shutdown: tuple[str, ...] = ("killall xpimd",)
validate: Tuple[str, ...] = ("pidof xpimd",) validate: tuple[str, ...] = ("pidof xpimd",)
ipv4_routing: bool = True ipv4_routing: bool = True
@classmethod @classmethod
@ -579,8 +574,8 @@ class Xpimd(QuaggaService):
cfg += "router igmp\n!\n" cfg += "router igmp\n!\n"
cfg += "router pim\n" cfg += "router pim\n"
cfg += " !ip pim rp-address 10.0.0.1\n" cfg += " !ip pim rp-address 10.0.0.1\n"
cfg += " ip pim bsr-candidate %s\n" % ifname cfg += f" ip pim bsr-candidate {ifname}\n"
cfg += " ip pim rp-candidate %s\n" % ifname cfg += f" ip pim rp-candidate {ifname}\n"
cfg += " !ip pim spt-threshold interval 10 bytes 80000\n" cfg += " !ip pim spt-threshold interval 10 bytes 80000\n"
return cfg return cfg

View file

@ -3,7 +3,6 @@ sdn.py defines services to start Open vSwitch and the Ryu SDN Controller.
""" """
import re import re
from typing import Tuple
from core.nodes.base import CoreNode from core.nodes.base import CoreNode
from core.services.coreservices import CoreService from core.services.coreservices import CoreService
@ -24,15 +23,15 @@ class SdnService(CoreService):
class OvsService(SdnService): class OvsService(SdnService):
name: str = "OvsService" name: str = "OvsService"
group: str = "SDN" group: str = "SDN"
executables: Tuple[str, ...] = ("ovs-ofctl", "ovs-vsctl") executables: tuple[str, ...] = ("ovs-ofctl", "ovs-vsctl")
dirs: Tuple[str, ...] = ( dirs: tuple[str, ...] = (
"/etc/openvswitch", "/etc/openvswitch",
"/var/run/openvswitch", "/var/run/openvswitch",
"/var/log/openvswitch", "/var/log/openvswitch",
) )
configs: Tuple[str, ...] = ("OvsService.sh",) configs: tuple[str, ...] = ("OvsService.sh",)
startup: Tuple[str, ...] = ("bash OvsService.sh",) startup: tuple[str, ...] = ("bash OvsService.sh",)
shutdown: Tuple[str, ...] = ("killall ovs-vswitchd", "killall ovsdb-server") shutdown: tuple[str, ...] = ("killall ovs-vswitchd", "killall ovsdb-server")
@classmethod @classmethod
def generate_config(cls, node: CoreNode, filename: str) -> str: def generate_config(cls, node: CoreNode, filename: str) -> str:
@ -59,39 +58,41 @@ class OvsService(SdnService):
# create virtual interfaces # create virtual interfaces
cfg += "## Create a veth pair to send the data to\n" cfg += "## Create a veth pair to send the data to\n"
cfg += "ip link add rtr%s type veth peer name sw%s\n" % (ifnum, ifnum) 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 # 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 # or assign them manually to rtr interfaces if zebra is not running
for ip4 in iface.ip4s: for ip4 in iface.ip4s:
cfg += "ip addr del %s dev %s\n" % (ip4.ip, iface.name) cfg += f"ip addr del {ip4.ip} dev {iface.name}\n"
if has_zebra == 0: if has_zebra == 0:
cfg += "ip addr add %s dev rtr%s\n" % (ip4.ip, ifnum) cfg += f"ip addr add {ip4.ip} dev rtr{ifnum}\n"
for ip6 in iface.ip6s: for ip6 in iface.ip6s:
cfg += "ip -6 addr del %s dev %s\n" % (ip6.ip, iface.name) cfg += f"ip -6 addr del {ip6.ip} dev {iface.name}\n"
if has_zebra == 0: if has_zebra == 0:
cfg += "ip -6 addr add %s dev rtr%s\n" % (ip6.ip, ifnum) cfg += f"ip -6 addr add {ip6.ip} dev rtr{ifnum}\n"
# add interfaces to bridge # add interfaces to bridge
# Make port numbers explicit so they're easier to follow in reading the script # Make port numbers explicit so they're easier to follow in
# reading the script
cfg += "## Add the CORE interface to the switch\n" cfg += "## Add the CORE interface to the switch\n"
cfg += ( cfg += (
"ovs-vsctl add-port ovsbr0 eth%s -- set Interface eth%s ofport_request=%d\n" f"ovs-vsctl add-port ovsbr0 eth{ifnum} -- "
% (ifnum, ifnum, portnum) f"set Interface eth{ifnum} ofport_request={portnum:d}\n"
) )
cfg += "## And then add its sibling veth interface\n" cfg += "## And then add its sibling veth interface\n"
cfg += ( cfg += (
"ovs-vsctl add-port ovsbr0 sw%s -- set Interface sw%s ofport_request=%d\n" f"ovs-vsctl add-port ovsbr0 sw{ifnum} -- "
% (ifnum, ifnum, portnum + 1) f"set Interface sw{ifnum} ofport_request={portnum + 1:d}\n"
) )
cfg += "## start them up so we can send/receive data\n" cfg += "## start them up so we can send/receive data\n"
cfg += "ovs-ofctl mod-port ovsbr0 eth%s up\n" % ifnum cfg += f"ovs-ofctl mod-port ovsbr0 eth{ifnum} up\n"
cfg += "ovs-ofctl mod-port ovsbr0 sw%s up\n" % ifnum cfg += f"ovs-ofctl mod-port ovsbr0 sw{ifnum} up\n"
cfg += "## Bring up the lower part of the veth pair\n" cfg += "## Bring up the lower part of the veth pair\n"
cfg += "ip link set dev rtr%s up\n" % ifnum cfg += f"ip link set dev rtr{ifnum} up\n"
portnum += 2 portnum += 2
# Add rule for default controller if there is one local (even if the controller is not local, it finds it) # 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 += "\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 += "## but it will still function if there's not\n"
cfg += "ovs-vsctl set-controller ovsbr0 tcp:127.0.0.1:6633\n" cfg += "ovs-vsctl set-controller ovsbr0 tcp:127.0.0.1:6633\n"
@ -102,14 +103,8 @@ class OvsService(SdnService):
portnum = 1 portnum = 1
for iface in node.get_ifaces(control=False): 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 += "## Take the data from the CORE interface and put it on the veth and vice versa\n"
cfg += ( cfg += f"ovs-ofctl add-flow ovsbr0 priority=1000,in_port={portnum:d},action=output:{portnum + 1:d}\n"
"ovs-ofctl add-flow ovsbr0 priority=1000,in_port=%d,action=output:%d\n" cfg += f"ovs-ofctl add-flow ovsbr0 priority=1000,in_port={portnum + 1:d},action=output:{portnum:d}\n"
% (portnum, portnum + 1)
)
cfg += (
"ovs-ofctl add-flow ovsbr0 priority=1000,in_port=%d,action=output:%d\n"
% (portnum + 1, portnum)
)
portnum += 2 portnum += 2
return cfg return cfg
@ -117,10 +112,10 @@ class OvsService(SdnService):
class RyuService(SdnService): class RyuService(SdnService):
name: str = "ryuService" name: str = "ryuService"
group: str = "SDN" group: str = "SDN"
executables: Tuple[str, ...] = ("ryu-manager",) executables: tuple[str, ...] = ("ryu-manager",)
configs: Tuple[str, ...] = ("ryuService.sh",) configs: tuple[str, ...] = ("ryuService.sh",)
startup: Tuple[str, ...] = ("bash ryuService.sh",) startup: tuple[str, ...] = ("bash ryuService.sh",)
shutdown: Tuple[str, ...] = ("killall ryu-manager",) shutdown: tuple[str, ...] = ("killall ryu-manager",)
@classmethod @classmethod
def generate_config(cls, node: CoreNode, filename: str) -> str: def generate_config(cls, node: CoreNode, filename: str) -> str:

View file

@ -4,7 +4,6 @@ firewall)
""" """
import logging import logging
from typing import Tuple
from core import constants from core import constants
from core.nodes.base import CoreNode from core.nodes.base import CoreNode
@ -17,10 +16,10 @@ logger = logging.getLogger(__name__)
class VPNClient(CoreService): class VPNClient(CoreService):
name: str = "VPNClient" name: str = "VPNClient"
group: str = "Security" group: str = "Security"
configs: Tuple[str, ...] = ("vpnclient.sh",) configs: tuple[str, ...] = ("vpnclient.sh",)
startup: Tuple[str, ...] = ("bash vpnclient.sh",) startup: tuple[str, ...] = ("bash vpnclient.sh",)
shutdown: Tuple[str, ...] = ("killall openvpn",) shutdown: tuple[str, ...] = ("killall openvpn",)
validate: Tuple[str, ...] = ("pidof openvpn",) validate: tuple[str, ...] = ("pidof openvpn",)
custom_needed: bool = True custom_needed: bool = True
@classmethod @classmethod
@ -32,9 +31,9 @@ class VPNClient(CoreService):
cfg += "# custom VPN Client configuration for service (security.py)\n" cfg += "# custom VPN Client configuration for service (security.py)\n"
fname = f"{constants.CORE_DATA_DIR}/examples/services/sampleVPNClient" fname = f"{constants.CORE_DATA_DIR}/examples/services/sampleVPNClient"
try: try:
with open(fname, "r") as f: with open(fname) as f:
cfg += f.read() cfg += f.read()
except IOError: except OSError:
logger.exception( logger.exception(
"error opening VPN client configuration template (%s)", fname "error opening VPN client configuration template (%s)", fname
) )
@ -44,10 +43,10 @@ class VPNClient(CoreService):
class VPNServer(CoreService): class VPNServer(CoreService):
name: str = "VPNServer" name: str = "VPNServer"
group: str = "Security" group: str = "Security"
configs: Tuple[str, ...] = ("vpnserver.sh",) configs: tuple[str, ...] = ("vpnserver.sh",)
startup: Tuple[str, ...] = ("bash vpnserver.sh",) startup: tuple[str, ...] = ("bash vpnserver.sh",)
shutdown: Tuple[str, ...] = ("killall openvpn",) shutdown: tuple[str, ...] = ("killall openvpn",)
validate: Tuple[str, ...] = ("pidof openvpn",) validate: tuple[str, ...] = ("pidof openvpn",)
custom_needed: bool = True custom_needed: bool = True
@classmethod @classmethod
@ -60,9 +59,9 @@ class VPNServer(CoreService):
cfg += "# custom VPN Server Configuration for service (security.py)\n" cfg += "# custom VPN Server Configuration for service (security.py)\n"
fname = f"{constants.CORE_DATA_DIR}/examples/services/sampleVPNServer" fname = f"{constants.CORE_DATA_DIR}/examples/services/sampleVPNServer"
try: try:
with open(fname, "r") as f: with open(fname) as f:
cfg += f.read() cfg += f.read()
except IOError: except OSError:
logger.exception( logger.exception(
"Error opening VPN server configuration template (%s)", fname "Error opening VPN server configuration template (%s)", fname
) )
@ -72,9 +71,9 @@ class VPNServer(CoreService):
class IPsec(CoreService): class IPsec(CoreService):
name: str = "IPsec" name: str = "IPsec"
group: str = "Security" group: str = "Security"
configs: Tuple[str, ...] = ("ipsec.sh",) configs: tuple[str, ...] = ("ipsec.sh",)
startup: Tuple[str, ...] = ("bash ipsec.sh",) startup: tuple[str, ...] = ("bash ipsec.sh",)
shutdown: Tuple[str, ...] = ("killall racoon",) shutdown: tuple[str, ...] = ("killall racoon",)
custom_needed: bool = True custom_needed: bool = True
@classmethod @classmethod
@ -88,9 +87,9 @@ class IPsec(CoreService):
cfg += "(security.py)\n" cfg += "(security.py)\n"
fname = f"{constants.CORE_DATA_DIR}/examples/services/sampleIPsec" fname = f"{constants.CORE_DATA_DIR}/examples/services/sampleIPsec"
try: try:
with open(fname, "r") as f: with open(fname) as f:
cfg += f.read() cfg += f.read()
except IOError: except OSError:
logger.exception("Error opening IPsec configuration template (%s)", fname) logger.exception("Error opening IPsec configuration template (%s)", fname)
return cfg return cfg
@ -98,8 +97,8 @@ class IPsec(CoreService):
class Firewall(CoreService): class Firewall(CoreService):
name: str = "Firewall" name: str = "Firewall"
group: str = "Security" group: str = "Security"
configs: Tuple[str, ...] = ("firewall.sh",) configs: tuple[str, ...] = ("firewall.sh",)
startup: Tuple[str, ...] = ("bash firewall.sh",) startup: tuple[str, ...] = ("bash firewall.sh",)
custom_needed: bool = True custom_needed: bool = True
@classmethod @classmethod
@ -111,9 +110,9 @@ class Firewall(CoreService):
cfg += "# custom node firewall rules for service (security.py)\n" cfg += "# custom node firewall rules for service (security.py)\n"
fname = f"{constants.CORE_DATA_DIR}/examples/services/sampleFirewall" fname = f"{constants.CORE_DATA_DIR}/examples/services/sampleFirewall"
try: try:
with open(fname, "r") as f: with open(fname) as f:
cfg += f.read() cfg += f.read()
except IOError: except OSError:
logger.exception( logger.exception(
"Error opening Firewall configuration template (%s)", fname "Error opening Firewall configuration template (%s)", fname
) )
@ -127,9 +126,9 @@ class Nat(CoreService):
name: str = "NAT" name: str = "NAT"
group: str = "Security" group: str = "Security"
executables: Tuple[str, ...] = ("iptables",) executables: tuple[str, ...] = ("iptables",)
configs: Tuple[str, ...] = ("nat.sh",) configs: tuple[str, ...] = ("nat.sh",)
startup: Tuple[str, ...] = ("bash nat.sh",) startup: tuple[str, ...] = ("bash nat.sh",)
custom_needed: bool = False custom_needed: bool = False
@classmethod @classmethod

View file

@ -1,7 +1,6 @@
""" """
ucarp.py: defines high-availability IP address controlled by ucarp ucarp.py: defines high-availability IP address controlled by ucarp
""" """
from typing import Tuple
from core.nodes.base import CoreNode from core.nodes.base import CoreNode
from core.services.coreservices import CoreService from core.services.coreservices import CoreService
@ -12,16 +11,16 @@ UCARP_ETC = "/usr/local/etc/ucarp"
class Ucarp(CoreService): class Ucarp(CoreService):
name: str = "ucarp" name: str = "ucarp"
group: str = "Utility" group: str = "Utility"
dirs: Tuple[str, ...] = (UCARP_ETC,) dirs: tuple[str, ...] = (UCARP_ETC,)
configs: Tuple[str, ...] = ( configs: tuple[str, ...] = (
UCARP_ETC + "/default.sh", UCARP_ETC + "/default.sh",
UCARP_ETC + "/default-up.sh", UCARP_ETC + "/default-up.sh",
UCARP_ETC + "/default-down.sh", UCARP_ETC + "/default-down.sh",
"ucarpboot.sh", "ucarpboot.sh",
) )
startup: Tuple[str, ...] = ("bash ucarpboot.sh",) startup: tuple[str, ...] = ("bash ucarpboot.sh",)
shutdown: Tuple[str, ...] = ("killall ucarp",) shutdown: tuple[str, ...] = ("killall ucarp",)
validate: Tuple[str, ...] = ("pidof ucarp",) validate: tuple[str, ...] = ("pidof ucarp",)
@classmethod @classmethod
def generate_config(cls, node: CoreNode, filename: str) -> str: def generate_config(cls, node: CoreNode, filename: str) -> str:
@ -45,13 +44,13 @@ class Ucarp(CoreService):
Returns configuration file text. Returns configuration file text.
""" """
ucarp_bin = node.session.options.get("ucarp_bin", "/usr/sbin/ucarp") ucarp_bin = node.session.options.get("ucarp_bin", "/usr/sbin/ucarp")
return """\ return f"""\
#!/bin/sh #!/bin/sh
# Location of UCARP executable # Location of UCARP executable
UCARP_EXEC=%s UCARP_EXEC={ucarp_bin}
# Location of the UCARP config directory # Location of the UCARP config directory
UCARP_CFGDIR=%s UCARP_CFGDIR={UCARP_ETC}
# Logging Facility # Logging Facility
FACILITY=daemon FACILITY=daemon
@ -92,40 +91,34 @@ OPTIONS="-z -n -M"
# Send extra parameter to down and up scripts # Send extra parameter to down and up scripts
#XPARAM="-x <enter param here>" #XPARAM="-x <enter param here>"
XPARAM="-x ${VIRTUAL_NET}" XPARAM="-x ${{VIRTUAL_NET}}"
# The start and stop scripts # The start and stop scripts
START_SCRIPT=${UCARP_CFGDIR}/default-up.sh START_SCRIPT=${{UCARP_CFGDIR}}/default-up.sh
STOP_SCRIPT=${UCARP_CFGDIR}/default-down.sh STOP_SCRIPT=${{UCARP_CFGDIR}}/default-down.sh
# These line should not need to be touched # These line should not need to be touched
UCARP_OPTS="$OPTIONS -b $UCARP_BASE -k $SKEW -i $INTERFACE -v $INSTANCE_ID -p $PASSWORD -u $START_SCRIPT -d $STOP_SCRIPT -a $VIRTUAL_ADDRESS -s $SOURCE_ADDRESS -f $FACILITY $XPARAM" UCARP_OPTS="$OPTIONS -b $UCARP_BASE -k $SKEW -i $INTERFACE -v $INSTANCE_ID -p $PASSWORD -u $START_SCRIPT -d $STOP_SCRIPT -a $VIRTUAL_ADDRESS -s $SOURCE_ADDRESS -f $FACILITY $XPARAM"
${UCARP_EXEC} -B ${UCARP_OPTS} ${{UCARP_EXEC}} -B ${{UCARP_OPTS}}
""" % ( """
ucarp_bin,
UCARP_ETC,
)
@classmethod @classmethod
def generate_ucarp_boot(cls, node: CoreNode) -> str: def generate_ucarp_boot(cls, node: CoreNode) -> str:
""" """
Generate a shell script used to boot the Ucarp daemons. Generate a shell script used to boot the Ucarp daemons.
""" """
return ( return f"""\
"""\
#!/bin/sh #!/bin/sh
# Location of the UCARP config directory # Location of the UCARP config directory
UCARP_CFGDIR=%s UCARP_CFGDIR={UCARP_ETC}
chmod a+x ${UCARP_CFGDIR}/*.sh chmod a+x ${{UCARP_CFGDIR}}/*.sh
# Start the default ucarp daemon configuration # Start the default ucarp daemon configuration
${UCARP_CFGDIR}/default.sh ${{UCARP_CFGDIR}}/default.sh
""" """
% UCARP_ETC
)
@classmethod @classmethod
def generate_vip_up(cls, node: CoreNode) -> str: def generate_vip_up(cls, node: CoreNode) -> str:

View file

@ -1,7 +1,7 @@
""" """
utility.py: defines miscellaneous utility services. utility.py: defines miscellaneous utility services.
""" """
from typing import Optional, Tuple from typing import Optional
import netaddr import netaddr
@ -27,8 +27,8 @@ class UtilService(CoreService):
class IPForwardService(UtilService): class IPForwardService(UtilService):
name: str = "IPForward" name: str = "IPForward"
configs: Tuple[str, ...] = ("ipforward.sh",) configs: tuple[str, ...] = ("ipforward.sh",)
startup: Tuple[str, ...] = ("bash ipforward.sh",) startup: tuple[str, ...] = ("bash ipforward.sh",)
@classmethod @classmethod
def generate_config(cls, node: CoreNode, filename: str) -> str: def generate_config(cls, node: CoreNode, filename: str) -> str:
@ -36,32 +36,30 @@ class IPForwardService(UtilService):
@classmethod @classmethod
def generateconfiglinux(cls, node: CoreNode, filename: str) -> str: def generateconfiglinux(cls, node: CoreNode, filename: str) -> str:
cfg = """\ cfg = f"""\
#!/bin/sh #!/bin/sh
# auto-generated by IPForward service (utility.py) # auto-generated by IPForward service (utility.py)
%(sysctl)s -w net.ipv4.conf.all.forwarding=1 {SYSCTL} -w net.ipv4.conf.all.forwarding=1
%(sysctl)s -w net.ipv4.conf.default.forwarding=1 {SYSCTL} -w net.ipv4.conf.default.forwarding=1
%(sysctl)s -w net.ipv6.conf.all.forwarding=1 {SYSCTL} -w net.ipv6.conf.all.forwarding=1
%(sysctl)s -w net.ipv6.conf.default.forwarding=1 {SYSCTL} -w net.ipv6.conf.default.forwarding=1
%(sysctl)s -w net.ipv4.conf.all.send_redirects=0 {SYSCTL} -w net.ipv4.conf.all.send_redirects=0
%(sysctl)s -w net.ipv4.conf.default.send_redirects=0 {SYSCTL} -w net.ipv4.conf.default.send_redirects=0
%(sysctl)s -w net.ipv4.conf.all.rp_filter=0 {SYSCTL} -w net.ipv4.conf.all.rp_filter=0
%(sysctl)s -w net.ipv4.conf.default.rp_filter=0 {SYSCTL} -w net.ipv4.conf.default.rp_filter=0
""" % { """
"sysctl": SYSCTL
}
for iface in node.get_ifaces(): for iface in node.get_ifaces():
name = utils.sysctl_devname(iface.name) name = utils.sysctl_devname(iface.name)
cfg += "%s -w net.ipv4.conf.%s.forwarding=1\n" % (SYSCTL, name) cfg += f"{SYSCTL} -w net.ipv4.conf.{name}.forwarding=1\n"
cfg += "%s -w net.ipv4.conf.%s.send_redirects=0\n" % (SYSCTL, name) cfg += f"{SYSCTL} -w net.ipv4.conf.{name}.send_redirects=0\n"
cfg += "%s -w net.ipv4.conf.%s.rp_filter=0\n" % (SYSCTL, name) cfg += f"{SYSCTL} -w net.ipv4.conf.{name}.rp_filter=0\n"
return cfg return cfg
class DefaultRouteService(UtilService): class DefaultRouteService(UtilService):
name: str = "DefaultRoute" name: str = "DefaultRoute"
configs: Tuple[str, ...] = ("defaultroute.sh",) configs: tuple[str, ...] = ("defaultroute.sh",)
startup: Tuple[str, ...] = ("bash defaultroute.sh",) startup: tuple[str, ...] = ("bash defaultroute.sh",)
@classmethod @classmethod
def generate_config(cls, node: CoreNode, filename: str) -> str: def generate_config(cls, node: CoreNode, filename: str) -> str:
@ -83,8 +81,8 @@ class DefaultRouteService(UtilService):
class DefaultMulticastRouteService(UtilService): class DefaultMulticastRouteService(UtilService):
name: str = "DefaultMulticastRoute" name: str = "DefaultMulticastRoute"
configs: Tuple[str, ...] = ("defaultmroute.sh",) configs: tuple[str, ...] = ("defaultmroute.sh",)
startup: Tuple[str, ...] = ("bash defaultmroute.sh",) startup: tuple[str, ...] = ("bash defaultmroute.sh",)
@classmethod @classmethod
def generate_config(cls, node: CoreNode, filename: str) -> str: def generate_config(cls, node: CoreNode, filename: str) -> str:
@ -94,7 +92,7 @@ class DefaultMulticastRouteService(UtilService):
cfg += "as needed\n" cfg += "as needed\n"
for iface in node.get_ifaces(control=False): for iface in node.get_ifaces(control=False):
rtcmd = "ip route add 224.0.0.0/4 dev" rtcmd = "ip route add 224.0.0.0/4 dev"
cfg += "%s %s\n" % (rtcmd, iface.name) cfg += f"{rtcmd} {iface.name}\n"
cfg += "\n" cfg += "\n"
break break
return cfg return cfg
@ -102,8 +100,8 @@ class DefaultMulticastRouteService(UtilService):
class StaticRouteService(UtilService): class StaticRouteService(UtilService):
name: str = "StaticRoute" name: str = "StaticRoute"
configs: Tuple[str, ...] = ("staticroute.sh",) configs: tuple[str, ...] = ("staticroute.sh",)
startup: Tuple[str, ...] = ("bash staticroute.sh",) startup: tuple[str, ...] = ("bash staticroute.sh",)
custom_needed: bool = True custom_needed: bool = True
@classmethod @classmethod
@ -127,16 +125,16 @@ class StaticRouteService(UtilService):
if ip[-2] == ip[1]: if ip[-2] == ip[1]:
return "" return ""
else: else:
rtcmd = "#/sbin/ip route add %s via" % dst rtcmd = f"#/sbin/ip route add {dst} via"
return "%s %s" % (rtcmd, ip[1]) return f"{rtcmd} {ip[1]}"
class SshService(UtilService): class SshService(UtilService):
name: str = "SSH" name: str = "SSH"
configs: Tuple[str, ...] = ("startsshd.sh", "/etc/ssh/sshd_config") configs: tuple[str, ...] = ("startsshd.sh", "/etc/ssh/sshd_config")
dirs: Tuple[str, ...] = ("/etc/ssh", "/var/run/sshd") dirs: tuple[str, ...] = ("/etc/ssh", "/var/run/sshd")
startup: Tuple[str, ...] = ("bash startsshd.sh",) startup: tuple[str, ...] = ("bash startsshd.sh",)
shutdown: Tuple[str, ...] = ("killall sshd",) shutdown: tuple[str, ...] = ("killall sshd",)
validation_mode: ServiceMode = ServiceMode.BLOCKING validation_mode: ServiceMode = ServiceMode.BLOCKING
@classmethod @classmethod
@ -149,26 +147,22 @@ class SshService(UtilService):
sshstatedir = cls.dirs[1] sshstatedir = cls.dirs[1]
sshlibdir = "/usr/lib/openssh" sshlibdir = "/usr/lib/openssh"
if filename == "startsshd.sh": if filename == "startsshd.sh":
return """\ return f"""\
#!/bin/sh #!/bin/sh
# auto-generated by SSH service (utility.py) # auto-generated by SSH service (utility.py)
ssh-keygen -q -t rsa -N "" -f %s/ssh_host_rsa_key ssh-keygen -q -t rsa -N "" -f {sshcfgdir}/ssh_host_rsa_key
chmod 655 %s chmod 655 {sshstatedir}
# wait until RSA host key has been generated to launch sshd # wait until RSA host key has been generated to launch sshd
/usr/sbin/sshd -f %s/sshd_config /usr/sbin/sshd -f {sshcfgdir}/sshd_config
""" % ( """
sshcfgdir,
sshstatedir,
sshcfgdir,
)
else: else:
return """\ return f"""\
# auto-generated by SSH service (utility.py) # auto-generated by SSH service (utility.py)
Port 22 Port 22
Protocol 2 Protocol 2
HostKey %s/ssh_host_rsa_key HostKey {sshcfgdir}/ssh_host_rsa_key
UsePrivilegeSeparation yes UsePrivilegeSeparation yes
PidFile %s/sshd.pid PidFile {sshstatedir}/sshd.pid
KeyRegenerationInterval 3600 KeyRegenerationInterval 3600
ServerKeyBits 768 ServerKeyBits 768
@ -197,23 +191,19 @@ PrintLastLog yes
TCPKeepAlive yes TCPKeepAlive yes
AcceptEnv LANG LC_* AcceptEnv LANG LC_*
Subsystem sftp %s/sftp-server Subsystem sftp {sshlibdir}/sftp-server
UsePAM yes UsePAM yes
UseDNS no UseDNS no
""" % ( """
sshcfgdir,
sshstatedir,
sshlibdir,
)
class DhcpService(UtilService): class DhcpService(UtilService):
name: str = "DHCP" name: str = "DHCP"
configs: Tuple[str, ...] = ("/etc/dhcp/dhcpd.conf",) configs: tuple[str, ...] = ("/etc/dhcp/dhcpd.conf",)
dirs: Tuple[str, ...] = ("/etc/dhcp", "/var/lib/dhcp") dirs: tuple[str, ...] = ("/etc/dhcp", "/var/lib/dhcp")
startup: Tuple[str, ...] = ("touch /var/lib/dhcp/dhcpd.leases", "dhcpd") startup: tuple[str, ...] = ("touch /var/lib/dhcp/dhcpd.leases", "dhcpd")
shutdown: Tuple[str, ...] = ("killall dhcpd",) shutdown: tuple[str, ...] = ("killall dhcpd",)
validate: Tuple[str, ...] = ("pidof dhcpd",) validate: tuple[str, ...] = ("pidof dhcpd",)
@classmethod @classmethod
def generate_config(cls, node: CoreNode, filename: str) -> str: def generate_config(cls, node: CoreNode, filename: str) -> str:
@ -252,21 +242,15 @@ ddns-update-style none;
index = (ip.size - 2) / 2 index = (ip.size - 2) / 2
rangelow = ip[index] rangelow = ip[index]
rangehigh = ip[-2] rangehigh = ip[-2]
return """ return f"""
subnet %s netmask %s { subnet {ip.cidr.ip} netmask {ip.netmask} {{
pool { pool {{
range %s %s; range {rangelow} {rangehigh};
default-lease-time 600; default-lease-time 600;
option routers %s; option routers {ip.ip};
} }}
} }}
""" % ( """
ip.cidr.ip,
ip.netmask,
rangelow,
rangehigh,
ip.ip,
)
class DhcpClientService(UtilService): class DhcpClientService(UtilService):
@ -275,10 +259,10 @@ class DhcpClientService(UtilService):
""" """
name: str = "DHCPClient" name: str = "DHCPClient"
configs: Tuple[str, ...] = ("startdhcpclient.sh",) configs: tuple[str, ...] = ("startdhcpclient.sh",)
startup: Tuple[str, ...] = ("bash startdhcpclient.sh",) startup: tuple[str, ...] = ("bash startdhcpclient.sh",)
shutdown: Tuple[str, ...] = ("killall dhclient",) shutdown: tuple[str, ...] = ("killall dhclient",)
validate: Tuple[str, ...] = ("pidof dhclient",) validate: tuple[str, ...] = ("pidof dhclient",)
@classmethod @classmethod
def generate_config(cls, node: CoreNode, filename: str) -> str: def generate_config(cls, node: CoreNode, filename: str) -> str:
@ -291,10 +275,10 @@ class DhcpClientService(UtilService):
cfg += "side DNS\n# resolution based on the DHCP server response.\n" cfg += "side DNS\n# resolution based on the DHCP server response.\n"
cfg += "#mkdir -p /var/run/resolvconf/interface\n" cfg += "#mkdir -p /var/run/resolvconf/interface\n"
for iface in node.get_ifaces(control=False): for iface in node.get_ifaces(control=False):
cfg += "#ln -s /var/run/resolvconf/interface/%s.dhclient" % iface.name cfg += f"#ln -s /var/run/resolvconf/interface/{iface.name}.dhclient"
cfg += " /var/run/resolvconf/resolv.conf\n" cfg += " /var/run/resolvconf/resolv.conf\n"
cfg += "/sbin/dhclient -nw -pf /var/run/dhclient-%s.pid" % iface.name cfg += f"/sbin/dhclient -nw -pf /var/run/dhclient-{iface.name}.pid"
cfg += " -lf /var/run/dhclient-%s.lease %s\n" % (iface.name, iface.name) cfg += f" -lf /var/run/dhclient-{iface.name}.lease {iface.name}\n"
return cfg return cfg
@ -304,11 +288,11 @@ class FtpService(UtilService):
""" """
name: str = "FTP" name: str = "FTP"
configs: Tuple[str, ...] = ("vsftpd.conf",) configs: tuple[str, ...] = ("vsftpd.conf",)
dirs: Tuple[str, ...] = ("/var/run/vsftpd/empty", "/var/ftp") dirs: tuple[str, ...] = ("/var/run/vsftpd/empty", "/var/ftp")
startup: Tuple[str, ...] = ("vsftpd ./vsftpd.conf",) startup: tuple[str, ...] = ("vsftpd ./vsftpd.conf",)
shutdown: Tuple[str, ...] = ("killall vsftpd",) shutdown: tuple[str, ...] = ("killall vsftpd",)
validate: Tuple[str, ...] = ("pidof vsftpd",) validate: tuple[str, ...] = ("pidof vsftpd",)
@classmethod @classmethod
def generate_config(cls, node: CoreNode, filename: str) -> str: def generate_config(cls, node: CoreNode, filename: str) -> str:
@ -337,12 +321,12 @@ class HttpService(UtilService):
""" """
name: str = "HTTP" name: str = "HTTP"
configs: Tuple[str, ...] = ( configs: tuple[str, ...] = (
"/etc/apache2/apache2.conf", "/etc/apache2/apache2.conf",
"/etc/apache2/envvars", "/etc/apache2/envvars",
"/var/www/index.html", "/var/www/index.html",
) )
dirs: Tuple[str, ...] = ( dirs: tuple[str, ...] = (
"/etc/apache2", "/etc/apache2",
"/var/run/apache2", "/var/run/apache2",
"/var/log/apache2", "/var/log/apache2",
@ -350,9 +334,9 @@ class HttpService(UtilService):
"/var/lock/apache2", "/var/lock/apache2",
"/var/www", "/var/www",
) )
startup: Tuple[str, ...] = ("chown www-data /var/lock/apache2", "apache2ctl start") startup: tuple[str, ...] = ("chown www-data /var/lock/apache2", "apache2ctl start")
shutdown: Tuple[str, ...] = ("apache2ctl stop",) shutdown: tuple[str, ...] = ("apache2ctl stop",)
validate: Tuple[str, ...] = ("pidof apache2",) validate: tuple[str, ...] = ("pidof apache2",)
APACHEVER22: int = 22 APACHEVER22: int = 22
APACHEVER24: int = 24 APACHEVER24: int = 24
@ -538,18 +522,15 @@ export LANG
@classmethod @classmethod
def generatehtml(cls, node: CoreNode, filename: str) -> str: def generatehtml(cls, node: CoreNode, filename: str) -> str:
body = ( body = f"""\
"""\
<!-- generated by utility.py:HttpService --> <!-- generated by utility.py:HttpService -->
<h1>%s web server</h1> <h1>{node.name} web server</h1>
<p>This is the default web page for this server.</p> <p>This is the default web page for this server.</p>
<p>The web server software is running but no content has been added, yet.</p> <p>The web server software is running but no content has been added, yet.</p>
""" """
% node.name
)
for iface in node.get_ifaces(control=False): for iface in node.get_ifaces(control=False):
body += "<li>%s - %s</li>\n" % (iface.name, [str(x) for x in iface.ips()]) body += f"<li>{iface.name} - {[str(x) for x in iface.ips()]}</li>\n"
return "<html><body>%s</body></html>" % body return f"<html><body>{body}</body></html>"
class PcapService(UtilService): class PcapService(UtilService):
@ -558,10 +539,10 @@ class PcapService(UtilService):
""" """
name: str = "pcap" name: str = "pcap"
configs: Tuple[str, ...] = ("pcap.sh",) configs: tuple[str, ...] = ("pcap.sh",)
startup: Tuple[str, ...] = ("bash pcap.sh start",) startup: tuple[str, ...] = ("bash pcap.sh start",)
shutdown: Tuple[str, ...] = ("bash pcap.sh stop",) shutdown: tuple[str, ...] = ("bash pcap.sh stop",)
validate: Tuple[str, ...] = ("pidof tcpdump",) validate: tuple[str, ...] = ("pidof tcpdump",)
meta: str = "logs network traffic to pcap packet capture files" meta: str = "logs network traffic to pcap packet capture files"
@classmethod @classmethod
@ -582,11 +563,9 @@ if [ "x$1" = "xstart" ]; then
if iface.control: if iface.control:
cfg += "# " cfg += "# "
redir = "< /dev/null" redir = "< /dev/null"
cfg += "tcpdump ${DUMPOPTS} -w %s.%s.pcap -i %s %s &\n" % ( cfg += (
node.name, f"tcpdump ${{DUMPOPTS}} -w {node.name}.{iface.name}.pcap "
iface.name, f"-i {iface.name} {redir} &\n"
iface.name,
redir,
) )
cfg += """ cfg += """
@ -600,13 +579,13 @@ fi;
class RadvdService(UtilService): class RadvdService(UtilService):
name: str = "radvd" name: str = "radvd"
configs: Tuple[str, ...] = ("/etc/radvd/radvd.conf",) configs: tuple[str, ...] = ("/etc/radvd/radvd.conf",)
dirs: Tuple[str, ...] = ("/etc/radvd", "/var/run/radvd") dirs: tuple[str, ...] = ("/etc/radvd", "/var/run/radvd")
startup: Tuple[str, ...] = ( startup: tuple[str, ...] = (
"radvd -C /etc/radvd/radvd.conf -m logfile -l /var/log/radvd.log", "radvd -C /etc/radvd/radvd.conf -m logfile -l /var/log/radvd.log",
) )
shutdown: Tuple[str, ...] = ("pkill radvd",) shutdown: tuple[str, ...] = ("pkill radvd",)
validate: Tuple[str, ...] = ("pidof radvd",) validate: tuple[str, ...] = ("pidof radvd",)
@classmethod @classmethod
def generate_config(cls, node: CoreNode, filename: str) -> str: def generate_config(cls, node: CoreNode, filename: str) -> str:
@ -619,32 +598,26 @@ class RadvdService(UtilService):
prefixes = list(map(cls.subnetentry, iface.ips())) prefixes = list(map(cls.subnetentry, iface.ips()))
if len(prefixes) < 1: if len(prefixes) < 1:
continue continue
cfg += ( cfg += f"""\
"""\ interface {iface.name}
interface %s {{
{
AdvSendAdvert on; AdvSendAdvert on;
MinRtrAdvInterval 3; MinRtrAdvInterval 3;
MaxRtrAdvInterval 10; MaxRtrAdvInterval 10;
AdvDefaultPreference low; AdvDefaultPreference low;
AdvHomeAgentFlag off; AdvHomeAgentFlag off;
""" """
% iface.name
)
for prefix in prefixes: for prefix in prefixes:
if prefix == "": if prefix == "":
continue continue
cfg += ( cfg += f"""\
"""\ prefix {prefix}
prefix %s {{
{
AdvOnLink on; AdvOnLink on;
AdvAutonomous on; AdvAutonomous on;
AdvRouterAddr on; AdvRouterAddr on;
}; }};
""" """
% prefix
)
cfg += "};\n" cfg += "};\n"
return cfg return cfg
@ -667,10 +640,10 @@ class AtdService(UtilService):
""" """
name: str = "atd" name: str = "atd"
configs: Tuple[str, ...] = ("startatd.sh",) configs: tuple[str, ...] = ("startatd.sh",)
dirs: Tuple[str, ...] = ("/var/spool/cron/atjobs", "/var/spool/cron/atspool") dirs: tuple[str, ...] = ("/var/spool/cron/atjobs", "/var/spool/cron/atspool")
startup: Tuple[str, ...] = ("bash startatd.sh",) startup: tuple[str, ...] = ("bash startatd.sh",)
shutdown: Tuple[str, ...] = ("pkill atd",) shutdown: tuple[str, ...] = ("pkill atd",)
@classmethod @classmethod
def generate_config(cls, node: CoreNode, filename: str) -> str: def generate_config(cls, node: CoreNode, filename: str) -> str:

View file

@ -2,7 +2,7 @@
xorp.py: defines routing services provided by the XORP routing suite. xorp.py: defines routing services provided by the XORP routing suite.
""" """
from typing import Optional, Tuple from typing import Optional
import netaddr import netaddr
@ -19,15 +19,14 @@ class XorpRtrmgr(CoreService):
name: str = "xorp_rtrmgr" name: str = "xorp_rtrmgr"
group: str = "XORP" group: str = "XORP"
executables: Tuple[str, ...] = ("xorp_rtrmgr",) executables: tuple[str, ...] = ("xorp_rtrmgr",)
dirs: Tuple[str, ...] = ("/etc/xorp",) dirs: tuple[str, ...] = ("/etc/xorp",)
configs: Tuple[str, ...] = ("/etc/xorp/config.boot",) configs: tuple[str, ...] = ("/etc/xorp/config.boot",)
startup: Tuple[str, ...] = ( startup: tuple[
"xorp_rtrmgr -d -b %s -l /var/log/%s.log -P /var/run/%s.pid" str, ...
% (configs[0], name, name), ] = f"xorp_rtrmgr -d -b {configs[0]} -l /var/log/{name}.log -P /var/run/{name}.pid"
) shutdown: tuple[str, ...] = ("killall xorp_rtrmgr",)
shutdown: Tuple[str, ...] = ("killall xorp_rtrmgr",) validate: tuple[str, ...] = ("pidof xorp_rtrmgr",)
validate: Tuple[str, ...] = ("pidof xorp_rtrmgr",)
@classmethod @classmethod
def generate_config(cls, node: CoreNode, filename: str) -> str: def generate_config(cls, node: CoreNode, filename: str) -> str:
@ -38,8 +37,8 @@ class XorpRtrmgr(CoreService):
""" """
cfg = "interfaces {\n" cfg = "interfaces {\n"
for iface in node.get_ifaces(): for iface in node.get_ifaces():
cfg += " interface %s {\n" % iface.name cfg += f" interface {iface.name} {{\n"
cfg += "\tvif %s {\n" % iface.name cfg += f"\tvif {iface.name} {{\n"
cfg += "".join(map(cls.addrstr, iface.ips())) cfg += "".join(map(cls.addrstr, iface.ips()))
cfg += cls.lladdrstr(iface) cfg += cls.lladdrstr(iface)
cfg += "\t}\n" cfg += "\t}\n"
@ -59,8 +58,8 @@ class XorpRtrmgr(CoreService):
""" """
helper for mapping IP addresses to XORP config statements helper for mapping IP addresses to XORP config statements
""" """
cfg = "\t address %s {\n" % ip.ip cfg = f"\t address {ip.ip} {{\n"
cfg += "\t\tprefix-length: %s\n" % ip.prefixlen cfg += f"\t\tprefix-length: {ip.prefixlen}\n"
cfg += "\t }\n" cfg += "\t }\n"
return cfg return cfg
@ -69,7 +68,7 @@ class XorpRtrmgr(CoreService):
""" """
helper for adding link-local address entries (required by OSPFv3) helper for adding link-local address entries (required by OSPFv3)
""" """
cfg = "\t address %s {\n" % iface.mac.eui64() cfg = f"\t address {iface.mac.eui64()} {{\n"
cfg += "\t\tprefix-length: 64\n" cfg += "\t\tprefix-length: 64\n"
cfg += "\t }\n" cfg += "\t }\n"
return cfg return cfg
@ -83,8 +82,8 @@ class XorpService(CoreService):
name: Optional[str] = None name: Optional[str] = None
group: str = "XORP" group: str = "XORP"
executables: Tuple[str, ...] = ("xorp_rtrmgr",) executables: tuple[str, ...] = ("xorp_rtrmgr",)
dependencies: Tuple[str, ...] = ("xorp_rtrmgr",) dependencies: tuple[str, ...] = ("xorp_rtrmgr",)
meta: str = ( meta: str = (
"The config file for this service can be found in the xorp_rtrmgr service." "The config file for this service can be found in the xorp_rtrmgr service."
) )
@ -95,7 +94,7 @@ class XorpService(CoreService):
Helper to add a forwarding engine entry to the config file. Helper to add a forwarding engine entry to the config file.
""" """
cfg = "fea {\n" cfg = "fea {\n"
cfg += " %s {\n" % forwarding cfg += f" {forwarding} {{\n"
cfg += "\tdisable:false\n" cfg += "\tdisable:false\n"
cfg += " }\n" cfg += " }\n"
cfg += "}\n" cfg += "}\n"
@ -111,10 +110,10 @@ class XorpService(CoreService):
names.append(iface.name) names.append(iface.name)
names.append("register_vif") names.append("register_vif")
cfg = "plumbing {\n" cfg = "plumbing {\n"
cfg += " %s {\n" % forwarding cfg += f" {forwarding} {{\n"
for name in names: for name in names:
cfg += "\tinterface %s {\n" % name cfg += f"\tinterface {name} {{\n"
cfg += "\t vif %s {\n" % name cfg += f"\t vif {name} {{\n"
cfg += "\t\tdisable: false\n" cfg += "\t\tdisable: false\n"
cfg += "\t }\n" cfg += "\t }\n"
cfg += "\t}\n" cfg += "\t}\n"
@ -173,13 +172,13 @@ class XorpOspfv2(XorpService):
rtrid = cls.router_id(node) rtrid = cls.router_id(node)
cfg += "\nprotocols {\n" cfg += "\nprotocols {\n"
cfg += " ospf4 {\n" cfg += " ospf4 {\n"
cfg += "\trouter-id: %s\n" % rtrid cfg += f"\trouter-id: {rtrid}\n"
cfg += "\tarea 0.0.0.0 {\n" cfg += "\tarea 0.0.0.0 {\n"
for iface in node.get_ifaces(control=False): for iface in node.get_ifaces(control=False):
cfg += "\t interface %s {\n" % iface.name cfg += f"\t interface {iface.name} {{\n"
cfg += "\t\tvif %s {\n" % iface.name cfg += f"\t\tvif {iface.name} {{\n"
for ip4 in iface.ip4s: for ip4 in iface.ip4s:
cfg += "\t\t address %s {\n" % ip4.ip cfg += f"\t\t address {ip4.ip} {{\n"
cfg += "\t\t }\n" cfg += "\t\t }\n"
cfg += "\t\t}\n" cfg += "\t\t}\n"
cfg += "\t }\n" cfg += "\t }\n"
@ -204,11 +203,11 @@ class XorpOspfv3(XorpService):
rtrid = cls.router_id(node) rtrid = cls.router_id(node)
cfg += "\nprotocols {\n" cfg += "\nprotocols {\n"
cfg += " ospf6 0 { /* Instance ID 0 */\n" cfg += " ospf6 0 { /* Instance ID 0 */\n"
cfg += "\trouter-id: %s\n" % rtrid cfg += f"\trouter-id: {rtrid}\n"
cfg += "\tarea 0.0.0.0 {\n" cfg += "\tarea 0.0.0.0 {\n"
for iface in node.get_ifaces(control=False): for iface in node.get_ifaces(control=False):
cfg += "\t interface %s {\n" % iface.name cfg += f"\t interface {iface.name} {{\n"
cfg += "\t\tvif %s {\n" % iface.name cfg += f"\t\tvif {iface.name} {{\n"
cfg += "\t\t}\n" cfg += "\t\t}\n"
cfg += "\t }\n" cfg += "\t }\n"
cfg += "\t}\n" cfg += "\t}\n"
@ -234,7 +233,7 @@ class XorpBgp(XorpService):
rtrid = cls.router_id(node) rtrid = cls.router_id(node)
cfg += "\nprotocols {\n" cfg += "\nprotocols {\n"
cfg += " bgp {\n" cfg += " bgp {\n"
cfg += "\tbgp-id: %s\n" % rtrid cfg += f"\tbgp-id: {rtrid}\n"
cfg += "\tlocal-as: 65001 /* change this */\n" cfg += "\tlocal-as: 65001 /* change this */\n"
cfg += '\texport: "export-connected"\n' cfg += '\texport: "export-connected"\n'
cfg += "\tpeer 10.0.1.1 { /* change this */\n" cfg += "\tpeer 10.0.1.1 { /* change this */\n"
@ -262,10 +261,10 @@ class XorpRip(XorpService):
cfg += " rip {\n" cfg += " rip {\n"
cfg += '\texport: "export-connected"\n' cfg += '\texport: "export-connected"\n'
for iface in node.get_ifaces(control=False): for iface in node.get_ifaces(control=False):
cfg += "\tinterface %s {\n" % iface.name cfg += f"\tinterface {iface.name} {{\n"
cfg += "\t vif %s {\n" % iface.name cfg += f"\t vif {iface.name} {{\n"
for ip4 in iface.ip4s: for ip4 in iface.ip4s:
cfg += "\t\taddress %s {\n" % ip4.ip cfg += f"\t\taddress {ip4.ip} {{\n"
cfg += "\t\t disable: false\n" cfg += "\t\t disable: false\n"
cfg += "\t\t}\n" cfg += "\t\t}\n"
cfg += "\t }\n" cfg += "\t }\n"
@ -290,9 +289,9 @@ class XorpRipng(XorpService):
cfg += " ripng {\n" cfg += " ripng {\n"
cfg += '\texport: "export-connected"\n' cfg += '\texport: "export-connected"\n'
for iface in node.get_ifaces(control=False): for iface in node.get_ifaces(control=False):
cfg += "\tinterface %s {\n" % iface.name cfg += f"\tinterface {iface.name} {{\n"
cfg += "\t vif %s {\n" % iface.name cfg += f"\t vif {iface.name} {{\n"
cfg += "\t\taddress %s {\n" % iface.mac.eui64() cfg += f"\t\taddress {iface.mac.eui64()} {{\n"
cfg += "\t\t disable: false\n" cfg += "\t\t disable: false\n"
cfg += "\t\t}\n" cfg += "\t\t}\n"
cfg += "\t }\n" cfg += "\t }\n"
@ -317,8 +316,8 @@ class XorpPimSm4(XorpService):
names = [] names = []
for iface in node.get_ifaces(control=False): for iface in node.get_ifaces(control=False):
names.append(iface.name) names.append(iface.name)
cfg += "\tinterface %s {\n" % iface.name cfg += f"\tinterface {iface.name} {{\n"
cfg += "\t vif %s {\n" % iface.name cfg += f"\t vif {iface.name} {{\n"
cfg += "\t\tdisable: false\n" cfg += "\t\tdisable: false\n"
cfg += "\t }\n" cfg += "\t }\n"
cfg += "\t}\n" cfg += "\t}\n"
@ -329,20 +328,20 @@ class XorpPimSm4(XorpService):
names.append("register_vif") names.append("register_vif")
for name in names: for name in names:
cfg += "\tinterface %s {\n" % name cfg += f"\tinterface {name} {{\n"
cfg += "\t vif %s {\n" % name cfg += f"\t vif {name} {{\n"
cfg += "\t\tdr-priority: 1\n" cfg += "\t\tdr-priority: 1\n"
cfg += "\t }\n" cfg += "\t }\n"
cfg += "\t}\n" cfg += "\t}\n"
cfg += "\tbootstrap {\n" cfg += "\tbootstrap {\n"
cfg += "\t cand-bsr {\n" cfg += "\t cand-bsr {\n"
cfg += "\t\tscope-zone 224.0.0.0/4 {\n" cfg += "\t\tscope-zone 224.0.0.0/4 {\n"
cfg += '\t\t cand-bsr-by-vif-name: "%s"\n' % names[0] cfg += f'\t\t cand-bsr-by-vif-name: "{names[0]}"\n'
cfg += "\t\t}\n" cfg += "\t\t}\n"
cfg += "\t }\n" cfg += "\t }\n"
cfg += "\t cand-rp {\n" cfg += "\t cand-rp {\n"
cfg += "\t\tgroup-prefix 224.0.0.0/4 {\n" cfg += "\t\tgroup-prefix 224.0.0.0/4 {\n"
cfg += '\t\t cand-rp-by-vif-name: "%s"\n' % names[0] cfg += f'\t\t cand-rp-by-vif-name: "{names[0]}"\n'
cfg += "\t\t}\n" cfg += "\t\t}\n"
cfg += "\t }\n" cfg += "\t }\n"
cfg += "\t}\n" cfg += "\t}\n"
@ -371,8 +370,8 @@ class XorpPimSm6(XorpService):
names = [] names = []
for iface in node.get_ifaces(control=False): for iface in node.get_ifaces(control=False):
names.append(iface.name) names.append(iface.name)
cfg += "\tinterface %s {\n" % iface.name cfg += f"\tinterface {iface.name} {{\n"
cfg += "\t vif %s {\n" % iface.name cfg += f"\t vif {iface.name} {{\n"
cfg += "\t\tdisable: false\n" cfg += "\t\tdisable: false\n"
cfg += "\t }\n" cfg += "\t }\n"
cfg += "\t}\n" cfg += "\t}\n"
@ -383,20 +382,20 @@ class XorpPimSm6(XorpService):
names.append("register_vif") names.append("register_vif")
for name in names: for name in names:
cfg += "\tinterface %s {\n" % name cfg += f"\tinterface {name} {{\n"
cfg += "\t vif %s {\n" % name cfg += f"\t vif {name} {{\n"
cfg += "\t\tdr-priority: 1\n" cfg += "\t\tdr-priority: 1\n"
cfg += "\t }\n" cfg += "\t }\n"
cfg += "\t}\n" cfg += "\t}\n"
cfg += "\tbootstrap {\n" cfg += "\tbootstrap {\n"
cfg += "\t cand-bsr {\n" cfg += "\t cand-bsr {\n"
cfg += "\t\tscope-zone ff00::/8 {\n" cfg += "\t\tscope-zone ff00::/8 {\n"
cfg += '\t\t cand-bsr-by-vif-name: "%s"\n' % names[0] cfg += f'\t\t cand-bsr-by-vif-name: "{names[0]}"\n'
cfg += "\t\t}\n" cfg += "\t\t}\n"
cfg += "\t }\n" cfg += "\t }\n"
cfg += "\t cand-rp {\n" cfg += "\t cand-rp {\n"
cfg += "\t\tgroup-prefix ff00::/8 {\n" cfg += "\t\tgroup-prefix ff00::/8 {\n"
cfg += '\t\t cand-rp-by-vif-name: "%s"\n' % names[0] cfg += f'\t\t cand-rp-by-vif-name: "{names[0]}"\n'
cfg += "\t\t}\n" cfg += "\t\t}\n"
cfg += "\t }\n" cfg += "\t }\n"
cfg += "\t}\n" cfg += "\t}\n"
@ -423,12 +422,12 @@ class XorpOlsr(XorpService):
rtrid = cls.router_id(node) rtrid = cls.router_id(node)
cfg += "\nprotocols {\n" cfg += "\nprotocols {\n"
cfg += " olsr4 {\n" cfg += " olsr4 {\n"
cfg += "\tmain-address: %s\n" % rtrid cfg += f"\tmain-address: {rtrid}\n"
for iface in node.get_ifaces(control=False): for iface in node.get_ifaces(control=False):
cfg += "\tinterface %s {\n" % iface.name cfg += f"\tinterface {iface.name} {{\n"
cfg += "\t vif %s {\n" % iface.name cfg += f"\t vif {iface.name} {{\n"
for ip4 in iface.ip4s: for ip4 in iface.ip4s:
cfg += "\t\taddress %s {\n" % ip4.ip cfg += f"\t\taddress {ip4.ip} {{\n"
cfg += "\t\t}\n" cfg += "\t\t}\n"
cfg += "\t }\n" cfg += "\t }\n"
cfg += "\t}\n" cfg += "\t}\n"