updates naming for built in config services, broke out current example script to examples dir, broke out config service manager to separate file
This commit is contained in:
parent
433327c0ae
commit
dbc77d81f6
7 changed files with 180 additions and 91 deletions
|
@ -9,7 +9,6 @@ from typing import Any, Dict, List
|
||||||
from mako import exceptions
|
from mako import exceptions
|
||||||
from mako.lookup import TemplateLookup
|
from mako.lookup import TemplateLookup
|
||||||
|
|
||||||
from core import utils
|
|
||||||
from core.config import Configuration
|
from core.config import Configuration
|
||||||
from core.errors import CoreCommandError, CoreError
|
from core.errors import CoreCommandError, CoreError
|
||||||
from core.nodes.base import CoreNode
|
from core.nodes.base import CoreNode
|
||||||
|
@ -23,42 +22,6 @@ class ConfigServiceMode(enum.Enum):
|
||||||
TIMER = 2
|
TIMER = 2
|
||||||
|
|
||||||
|
|
||||||
class ConfigServiceManager:
|
|
||||||
def __init__(self):
|
|
||||||
self.services = {}
|
|
||||||
|
|
||||||
def add(self, service: "ConfigService") -> None:
|
|
||||||
name = service.name
|
|
||||||
logging.debug("loading service: class(%s) name(%s)", service.__class__, name)
|
|
||||||
|
|
||||||
# avoid duplicate services
|
|
||||||
if name in self.services:
|
|
||||||
raise CoreError(f"duplicate service being added: {name}")
|
|
||||||
|
|
||||||
# validate dependent executables are present
|
|
||||||
for executable in service.executables:
|
|
||||||
utils.which(executable, required=True)
|
|
||||||
|
|
||||||
# make service available
|
|
||||||
self.services[name] = service
|
|
||||||
|
|
||||||
def load(self, path: str) -> List[str]:
|
|
||||||
path = pathlib.Path(path)
|
|
||||||
subdirs = [x for x in path.iterdir() if x.is_dir()]
|
|
||||||
service_errors = []
|
|
||||||
for subdir in subdirs:
|
|
||||||
logging.info("loading config services from: %s", subdir)
|
|
||||||
services = utils.load_classes(str(subdir), ConfigService)
|
|
||||||
for service in services:
|
|
||||||
logging.info("found service: %s", service)
|
|
||||||
try:
|
|
||||||
self.add(service)
|
|
||||||
except CoreError as e:
|
|
||||||
service_errors.append(service.name)
|
|
||||||
logging.debug("not loading service(%s): %s", service.name, e)
|
|
||||||
return service_errors
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigService(abc.ABC):
|
class ConfigService(abc.ABC):
|
||||||
# validation period in seconds, how frequent validation is attempted
|
# validation period in seconds, how frequent validation is attempted
|
||||||
validation_period = 0.5
|
validation_period = 0.5
|
||||||
|
|
48
daemon/core/configservice/manager.py
Normal file
48
daemon/core/configservice/manager.py
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
import logging
|
||||||
|
import pathlib
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from core import utils
|
||||||
|
from core.configservice.base import ConfigService
|
||||||
|
from core.errors import CoreError
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigServiceManager:
|
||||||
|
def __init__(self):
|
||||||
|
self.services = {}
|
||||||
|
|
||||||
|
def add(self, service: ConfigService) -> None:
|
||||||
|
name = service.name
|
||||||
|
logging.debug("loading service: class(%s) name(%s)", service.__class__, name)
|
||||||
|
|
||||||
|
# avoid duplicate services
|
||||||
|
if name in self.services:
|
||||||
|
raise CoreError(f"duplicate service being added: {name}")
|
||||||
|
|
||||||
|
# validate dependent executables are present
|
||||||
|
for executable in service.executables:
|
||||||
|
try:
|
||||||
|
utils.which(executable, required=True)
|
||||||
|
except ValueError:
|
||||||
|
raise CoreError(
|
||||||
|
f"service({service.name}) missing executable {executable}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# make service available
|
||||||
|
self.services[name] = service
|
||||||
|
|
||||||
|
def load(self, path: str) -> List[str]:
|
||||||
|
path = pathlib.Path(path)
|
||||||
|
subdirs = [x for x in path.iterdir() if x.is_dir()]
|
||||||
|
service_errors = []
|
||||||
|
for subdir in subdirs:
|
||||||
|
logging.info("loading config services from: %s", subdir)
|
||||||
|
services = utils.load_classes(str(subdir), ConfigService)
|
||||||
|
for service in services:
|
||||||
|
logging.info("found service: %s", service)
|
||||||
|
try:
|
||||||
|
self.add(service)
|
||||||
|
except CoreError as e:
|
||||||
|
service_errors.append(service.name)
|
||||||
|
logging.debug("not loading service(%s): %s", service.name, e)
|
||||||
|
return service_errors
|
0
daemon/core/configservices/sercurityservices/__init__.py
Normal file
0
daemon/core/configservices/sercurityservices/__init__.py
Normal file
19
daemon/core/configservices/sercurityservices/services.py
Normal file
19
daemon/core/configservices/sercurityservices/services.py
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
from core.configservice.base import ConfigService, ConfigServiceMode
|
||||||
|
|
||||||
|
GROUP_NAME = "Security"
|
||||||
|
|
||||||
|
|
||||||
|
class VpnClient(ConfigService):
|
||||||
|
name = "VPNClient"
|
||||||
|
group = GROUP_NAME
|
||||||
|
directories = []
|
||||||
|
executables = ["openvpn", "ip", "killall"]
|
||||||
|
dependencies = []
|
||||||
|
startup = ["sh vpnclient.sh"]
|
||||||
|
validate = ["pidof openvpn"]
|
||||||
|
shutdown = ["killall openvpn"]
|
||||||
|
validation_mode = ConfigServiceMode.BLOCKING
|
||||||
|
default_configs = []
|
||||||
|
|
||||||
|
def create_files(self):
|
||||||
|
self.render("vpnclient.sh")
|
|
@ -0,0 +1,61 @@
|
||||||
|
# -------- CUSTOMIZATION REQUIRED --------
|
||||||
|
#
|
||||||
|
# The VPNClient service builds a VPN tunnel to the specified VPN server using
|
||||||
|
# OpenVPN software and a virtual TUN/TAP device.
|
||||||
|
|
||||||
|
# directory containing the certificate and key described below
|
||||||
|
keydir=/etc/core/keys
|
||||||
|
|
||||||
|
# the name used for a "$keyname.crt" certificate and "$keyname.key" private key.
|
||||||
|
keyname=client1
|
||||||
|
|
||||||
|
# the public IP address of the VPN server this client should connect with
|
||||||
|
vpnserver="10.0.2.10"
|
||||||
|
|
||||||
|
# optional next hop for adding a static route to reach the VPN server
|
||||||
|
nexthop="10.0.1.1"
|
||||||
|
|
||||||
|
# --------- END CUSTOMIZATION --------
|
||||||
|
|
||||||
|
# validate addresses
|
||||||
|
if [ "$(dpkg -l | grep " sipcalc ")" = "" ]; then
|
||||||
|
echo "WARNING: ip validation disabled because package sipcalc not installed
|
||||||
|
" > $PWD/vpnclient.log
|
||||||
|
else
|
||||||
|
if [ "$(sipcalc "$vpnserver" "$nexthop" | grep ERR)" != "" ]; then
|
||||||
|
echo "ERROR: invalide address $vpnserver or $nexthop " > $PWD/vpnclient.log
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# validate key and certification files
|
||||||
|
if [ ! -e $keydir\/$keyname.key ] || [ ! -e $keydir\/$keyname.crt ] \
|
||||||
|
|| [ ! -e $keydir\/ca.crt ] || [ ! -e $keydir\/dh1024.pem ]; then
|
||||||
|
echo "ERROR: missing certification or key files under $keydir $keyname.key or $keyname.crt or ca.crt or dh1024.pem" >> $PWD/vpnclient.log
|
||||||
|
fi
|
||||||
|
|
||||||
|
# if necessary, add a static route for reaching the VPN server IP via the IF
|
||||||
|
vpnservernet=${vpnserver%.*}.0/24
|
||||||
|
if [ "$nexthop" != "" ]; then
|
||||||
|
ip route add $vpnservernet via $nexthop
|
||||||
|
fi
|
||||||
|
|
||||||
|
# create openvpn client.conf
|
||||||
|
(
|
||||||
|
cat << EOF
|
||||||
|
client
|
||||||
|
dev tun
|
||||||
|
proto udp
|
||||||
|
remote $vpnserver 1194
|
||||||
|
nobind
|
||||||
|
ca $keydir/ca.crt
|
||||||
|
cert $keydir/$keyname.crt
|
||||||
|
key $keydir/$keyname.key
|
||||||
|
dh $keydir/dh1024.pem
|
||||||
|
cipher AES-256-CBC
|
||||||
|
log $PWD/openvpn-client.log
|
||||||
|
verb 4
|
||||||
|
daemon
|
||||||
|
EOF
|
||||||
|
) > client.conf
|
||||||
|
|
||||||
|
openvpn --config client.conf
|
|
@ -1,18 +1,9 @@
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
|
|
||||||
import netaddr
|
import netaddr
|
||||||
|
|
||||||
from core import utils
|
from core import utils
|
||||||
from core.config import Configuration
|
from core.config import Configuration
|
||||||
from core.configservice.base import (
|
from core.configservice.base import ConfigService, ConfigServiceMode
|
||||||
ConfigService,
|
from core.emulator.enumerations import ConfigDataTypes
|
||||||
ConfigServiceManager,
|
|
||||||
ConfigServiceMode,
|
|
||||||
)
|
|
||||||
from core.emulator.coreemu import CoreEmu
|
|
||||||
from core.emulator.emudata import IpPrefixes, NodeOptions
|
|
||||||
from core.emulator.enumerations import ConfigDataTypes, EventTypes, NodeTypes
|
|
||||||
|
|
||||||
GROUP_NAME = "Utility"
|
GROUP_NAME = "Utility"
|
||||||
|
|
||||||
|
@ -21,7 +12,7 @@ class DefaultRoute(ConfigService):
|
||||||
name = "DefaultRoute"
|
name = "DefaultRoute"
|
||||||
group = GROUP_NAME
|
group = GROUP_NAME
|
||||||
directories = []
|
directories = []
|
||||||
executables = []
|
executables = ["ip"]
|
||||||
dependencies = []
|
dependencies = []
|
||||||
startup = ["sh defaultroute.sh"]
|
startup = ["sh defaultroute.sh"]
|
||||||
validate = []
|
validate = []
|
||||||
|
@ -65,45 +56,3 @@ class IpForwardService(ConfigService):
|
||||||
devnames.append(devname)
|
devnames.append(devname)
|
||||||
data = dict(devnames=devnames)
|
data = dict(devnames=devnames)
|
||||||
self.render("ipforward.sh", data)
|
self.render("ipforward.sh", data)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
logging.basicConfig(level=logging.INFO)
|
|
||||||
|
|
||||||
# setup basic network
|
|
||||||
prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16")
|
|
||||||
options = NodeOptions(model="nothing")
|
|
||||||
# options.services = []
|
|
||||||
coreemu = CoreEmu()
|
|
||||||
session = coreemu.create_session()
|
|
||||||
session.set_state(EventTypes.CONFIGURATION_STATE)
|
|
||||||
switch = session.add_node(_type=NodeTypes.SWITCH)
|
|
||||||
|
|
||||||
# node one
|
|
||||||
node_one = session.add_node(options=options)
|
|
||||||
interface = prefixes.create_interface(node_one)
|
|
||||||
session.add_link(node_one.id, switch.id, interface_one=interface)
|
|
||||||
|
|
||||||
# node two
|
|
||||||
node_two = session.add_node(options=options)
|
|
||||||
interface = prefixes.create_interface(node_two)
|
|
||||||
session.add_link(node_two.id, switch.id, interface_one=interface)
|
|
||||||
|
|
||||||
session.instantiate()
|
|
||||||
|
|
||||||
# manager load config services
|
|
||||||
manager = ConfigServiceManager()
|
|
||||||
path = os.path.dirname(os.path.abspath(os.path.dirname(__file__)))
|
|
||||||
manager.load(path)
|
|
||||||
|
|
||||||
clazz = manager.services["DefaultRoute"]
|
|
||||||
dr_service = clazz(node_one)
|
|
||||||
dr_service.set_config({"value1": "custom"})
|
|
||||||
dr_service.start()
|
|
||||||
|
|
||||||
clazz = manager.services["IPForward"]
|
|
||||||
dr_service = clazz(node_one)
|
|
||||||
dr_service.start()
|
|
||||||
|
|
||||||
input("press enter to exit")
|
|
||||||
session.shutdown()
|
|
49
daemon/examples/configservices/testing.py
Normal file
49
daemon/examples/configservices/testing.py
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
from core import configservices
|
||||||
|
from core.configservice.manager import ConfigServiceManager
|
||||||
|
from core.emulator.coreemu import CoreEmu
|
||||||
|
from core.emulator.emudata import IpPrefixes, NodeOptions
|
||||||
|
from core.emulator.enumerations import EventTypes, NodeTypes
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
|
||||||
|
# setup basic network
|
||||||
|
prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16")
|
||||||
|
options = NodeOptions(model="nothing")
|
||||||
|
# options.services = []
|
||||||
|
coreemu = CoreEmu()
|
||||||
|
session = coreemu.create_session()
|
||||||
|
session.set_state(EventTypes.CONFIGURATION_STATE)
|
||||||
|
switch = session.add_node(_type=NodeTypes.SWITCH)
|
||||||
|
|
||||||
|
# node one
|
||||||
|
node_one = session.add_node(options=options)
|
||||||
|
interface = prefixes.create_interface(node_one)
|
||||||
|
session.add_link(node_one.id, switch.id, interface_one=interface)
|
||||||
|
|
||||||
|
# node two
|
||||||
|
node_two = session.add_node(options=options)
|
||||||
|
interface = prefixes.create_interface(node_two)
|
||||||
|
session.add_link(node_two.id, switch.id, interface_one=interface)
|
||||||
|
|
||||||
|
session.instantiate()
|
||||||
|
|
||||||
|
# manager load config services
|
||||||
|
manager = ConfigServiceManager()
|
||||||
|
path = os.path.dirname(os.path.abspath(configservices.__file__))
|
||||||
|
manager.load(path)
|
||||||
|
|
||||||
|
clazz = manager.services["DefaultRoute"]
|
||||||
|
dr_service = clazz(node_one)
|
||||||
|
dr_service.set_config({"value1": "custom"})
|
||||||
|
dr_service.start()
|
||||||
|
|
||||||
|
clazz = manager.services["IPForward"]
|
||||||
|
dr_service = clazz(node_one)
|
||||||
|
dr_service.start()
|
||||||
|
|
||||||
|
input("press enter to exit")
|
||||||
|
session.shutdown()
|
Loading…
Add table
Reference in a new issue