added some code to keep track of config services separately within core nodes, added function for starting config services during session instantiation

This commit is contained in:
Blake Harnden 2020-01-17 16:57:49 -08:00
parent dbc77d81f6
commit 191a9e9909
8 changed files with 87 additions and 26 deletions

View file

@ -8,6 +8,7 @@ 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 mako.template import Template
from core.config import Configuration from core.config import Configuration
from core.errors import CoreCommandError, CoreError from core.errors import CoreCommandError, CoreError
@ -39,6 +40,12 @@ class ConfigService(abc.ABC):
configs = self.default_configs[:] configs = self.default_configs[:]
self._define_config(configs) self._define_config(configs)
def __hash__(self) -> int:
return hash(self.name)
def __eq__(self, other: "ConfigService") -> bool:
return self.name == other.name
@property @property
@abc.abstractmethod @abc.abstractmethod
def name(self) -> str: def name(self) -> str:
@ -156,27 +163,44 @@ class ConfigService(abc.ABC):
f"failed to validate" f"failed to validate"
) )
def render(self, name: str, data: Dict[str, Any] = None) -> None: def _render(
self, name: str, template: Template, data: Dict[str, Any] = None
) -> None:
if data is None: if data is None:
data = {} data = {}
rendered = template.render_unicode(
node=self.node, config=self.render_config(), **data
)
logging.info(
"node(%s) service(%s) template(%s): \n%s",
self.node.name,
self.name,
name,
rendered,
)
self.node.nodefile(name, rendered)
def render_text(self, name: str, text: str, data: Dict[str, Any] = None) -> None:
try: try:
template = self.templates.get_template(name) text = inspect.cleandoc(text)
rendered = template.render_unicode( template = Template(text)
node=self.node, config=self.render_config(), **data self._render(name, template, data)
)
logging.info(
"node(%s) service(%s) template(%s): \n%s",
self.node.name,
self.name,
name,
rendered,
)
self.node.nodefile(name, rendered)
except Exception: except Exception:
raise CoreError( raise CoreError(
f"node({self.node.name}) service({self.name}) " f"node({self.node.name}) service({self.name}) "
f"error rendering template({name}): " f"error rendering template({name}): "
f"{exceptions.text_error_template().render()}" f"{exceptions.text_error_template().render_unicode()}"
)
def render_template(self, name: str, data: Dict[str, Any] = None) -> None:
try:
template = self.templates.get_template(name)
self._render(name, template, data)
except Exception:
raise CoreError(
f"node({self.node.name}) service({self.name}) "
f"error rendering template({name}): "
f"{exceptions.text_error_template().render_template()}"
) )
def _define_config(self, configs: List[Configuration]) -> None: def _define_config(self, configs: List[Configuration]) -> None:

View file

@ -5,12 +5,20 @@ from typing import List
from core import utils from core import utils
from core.configservice.base import ConfigService from core.configservice.base import ConfigService
from core.errors import CoreError from core.errors import CoreError
from core.nodes.base import CoreNode
class ConfigServiceManager: class ConfigServiceManager:
def __init__(self): def __init__(self):
self.services = {} self.services = {}
def set_service(self, node: CoreNode, name: str) -> None:
service_class = self.services.get(name)
if service_class in node.config_services:
raise CoreError(f"node already has service {name}")
service = service_class(node)
node.config_services.add(service)
def add(self, service: ConfigService) -> None: def add(self, service: ConfigService) -> None:
name = service.name name = service.name
logging.debug("loading service: class(%s) name(%s)", service.__class__, name) logging.debug("loading service: class(%s) name(%s)", service.__class__, name)
@ -34,6 +42,7 @@ class ConfigServiceManager:
def load(self, path: str) -> List[str]: def load(self, path: str) -> List[str]:
path = pathlib.Path(path) path = pathlib.Path(path)
subdirs = [x for x in path.iterdir() if x.is_dir()] subdirs = [x for x in path.iterdir() if x.is_dir()]
subdirs.append(path)
service_errors = [] service_errors = []
for subdir in subdirs: for subdir in subdirs:
logging.info("loading config services from: %s", subdir) logging.info("loading config services from: %s", subdir)

View file

@ -16,4 +16,4 @@ class VpnClient(ConfigService):
default_configs = [] default_configs = []
def create_files(self): def create_files(self):
self.render("vpnclient.sh") self.render_template("vpnclient.sh")

View file

@ -34,7 +34,7 @@ class DefaultRoute(ConfigService):
if net[1] != net[-2]: if net[1] != net[-2]:
addresses.append(net[1]) addresses.append(net[1])
data = dict(addresses=addresses) data = dict(addresses=addresses)
self.render("defaultroute.sh", data) self.render_template("defaultroute.sh", data)
class IpForwardService(ConfigService): class IpForwardService(ConfigService):
@ -55,4 +55,4 @@ class IpForwardService(ConfigService):
devname = utils.sysctl_devname(ifc.name) devname = utils.sysctl_devname(ifc.name)
devnames.append(devname) devnames.append(devname)
data = dict(devnames=devnames) data = dict(devnames=devnames)
self.render("ipforward.sh", data) self.render_template("ipforward.sh", data)

View file

@ -0,0 +1,22 @@
from core.configservice.base import ConfigService, ConfigServiceMode
class SimpleService(ConfigService):
name = "Simple"
group = "SimpleGroup"
directories = []
executables = []
dependencies = []
startup = []
validate = []
shutdown = []
validation_mode = ConfigServiceMode.BLOCKING
default_configs = []
def create_files(self):
text = """
# sample script
# node id(${node.id}) name(${node.name})
echo hello
"""
self.render_text("test1.sh", text)

View file

@ -1602,6 +1602,7 @@ class Session:
logging.info("booting node(%s): %s", node.name, [x.name for x in node.services]) logging.info("booting node(%s): %s", node.name, [x.name for x in node.services])
self.add_remove_control_interface(node=node, remove=False) self.add_remove_control_interface(node=node, remove=False)
self.services.boot_services(node) self.services.boot_services(node)
node.start_config_services()
def boot_nodes(self) -> List[Exception]: def boot_nodes(self) -> List[Exception]:
""" """

View file

@ -277,9 +277,19 @@ class CoreNodeBase(NodeBase):
""" """
super().__init__(session, _id, name, start, server) super().__init__(session, _id, name, start, server)
self.services = [] self.services = []
self.config_services = set()
self.nodedir = None self.nodedir = None
self.tmpnodedir = False self.tmpnodedir = False
def start_config_services(self) -> None:
"""
Start configuration services for this node.
:return: nothing
"""
for service in self.config_services:
service.start()
def makenodedir(self) -> None: def makenodedir(self) -> None:
""" """
Create the node directory. Create the node directory.

View file

@ -29,21 +29,16 @@ if __name__ == "__main__":
interface = prefixes.create_interface(node_two) interface = prefixes.create_interface(node_two)
session.add_link(node_two.id, switch.id, interface_one=interface) session.add_link(node_two.id, switch.id, interface_one=interface)
session.instantiate()
# manager load config services # manager load config services
manager = ConfigServiceManager() manager = ConfigServiceManager()
path = os.path.dirname(os.path.abspath(configservices.__file__)) path = os.path.dirname(os.path.abspath(configservices.__file__))
manager.load(path) manager.load(path)
clazz = manager.services["DefaultRoute"] manager.set_service(node_one, "DefaultRoute")
dr_service = clazz(node_one) manager.set_service(node_one, "IPForward")
dr_service.set_config({"value1": "custom"})
dr_service.start()
clazz = manager.services["IPForward"] # start session and run services
dr_service = clazz(node_one) session.instantiate()
dr_service.start()
input("press enter to exit") input("press enter to exit")
session.shutdown() session.shutdown()