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.lookup import TemplateLookup
|
||||
|
||||
from core import utils
|
||||
from core.config import Configuration
|
||||
from core.errors import CoreCommandError, CoreError
|
||||
from core.nodes.base import CoreNode
|
||||
|
@ -23,42 +22,6 @@ class ConfigServiceMode(enum.Enum):
|
|||
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):
|
||||
# validation period in seconds, how frequent validation is attempted
|
||||
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
|
||||
|
||||
from core import utils
|
||||
from core.config import Configuration
|
||||
from core.configservice.base import (
|
||||
ConfigService,
|
||||
ConfigServiceManager,
|
||||
ConfigServiceMode,
|
||||
)
|
||||
from core.emulator.coreemu import CoreEmu
|
||||
from core.emulator.emudata import IpPrefixes, NodeOptions
|
||||
from core.emulator.enumerations import ConfigDataTypes, EventTypes, NodeTypes
|
||||
from core.configservice.base import ConfigService, ConfigServiceMode
|
||||
from core.emulator.enumerations import ConfigDataTypes
|
||||
|
||||
GROUP_NAME = "Utility"
|
||||
|
||||
|
@ -21,7 +12,7 @@ class DefaultRoute(ConfigService):
|
|||
name = "DefaultRoute"
|
||||
group = GROUP_NAME
|
||||
directories = []
|
||||
executables = []
|
||||
executables = ["ip"]
|
||||
dependencies = []
|
||||
startup = ["sh defaultroute.sh"]
|
||||
validate = []
|
||||
|
@ -65,45 +56,3 @@ class IpForwardService(ConfigService):
|
|||
devnames.append(devname)
|
||||
data = dict(devnames=devnames)
|
||||
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…
Reference in a new issue