daemon: updates to refactor and clean up SessionConfig to contain its own logic

This commit is contained in:
Blake Harnden 2022-04-04 15:13:31 -07:00
parent 409b6809e6
commit 440c06c040
16 changed files with 128 additions and 119 deletions

View file

@ -220,6 +220,22 @@ def convert_value(value: Any) -> str:
return value return value
def convert_session_options(session: Session) -> Dict[str, common_pb2.ConfigOption]:
config_options = {}
for option in session.options.options:
value = session.options.get(option.id)
config_option = common_pb2.ConfigOption(
label=option.label,
name=option.id,
value=value,
type=option.type.value,
select=option.options,
group="Options",
)
config_options[option.id] = config_option
return config_options
def get_config_options( def get_config_options(
config: Dict[str, str], config: Dict[str, str],
configurable_options: Union[ConfigurableOptions, Type[ConfigurableOptions]], configurable_options: Union[ConfigurableOptions, Type[ConfigurableOptions]],
@ -768,7 +784,7 @@ def convert_session(session: Session) -> wrappers.Session:
) )
hooks = get_hooks(session) hooks = get_hooks(session)
session_file = str(session.file_path) if session.file_path else None session_file = str(session.file_path) if session.file_path else None
options = get_config_options(session.options.get_configs(), session.options) options = convert_session_options(session)
servers = [ servers = [
core_pb2.Server(name=x.name, host=x.host) core_pb2.Server(name=x.name, host=x.host)
for x in session.distributed.servers.values() for x in session.distributed.servers.values()

View file

@ -263,7 +263,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
# session options # session options
for option in request.session.options.values(): for option in request.session.options.values():
session.options.set_config(option.name, option.value) session.options.set(option.name, option.value)
session.metadata = dict(request.session.metadata) session.metadata = dict(request.session.metadata)
# add servers # add servers

View file

@ -85,10 +85,10 @@ class FRRZebra(ConfigService):
def data(self) -> Dict[str, Any]: def data(self) -> Dict[str, Any]:
frr_conf = self.files[0] frr_conf = self.files[0]
frr_bin_search = self.node.session.options.get_config( frr_bin_search = self.node.session.options.get(
"frr_bin_search", default="/usr/local/bin /usr/bin /usr/lib/frr" "frr_bin_search", default="/usr/local/bin /usr/bin /usr/lib/frr"
).strip('"') ).strip('"')
frr_sbin_search = self.node.session.options.get_config( frr_sbin_search = self.node.session.options.get(
"frr_sbin_search", default="/usr/local/sbin /usr/sbin /usr/lib/frr" "frr_sbin_search", default="/usr/local/sbin /usr/sbin /usr/lib/frr"
).strip('"') ).strip('"')

View file

@ -100,10 +100,10 @@ class Zebra(ConfigService):
modes: Dict[str, Dict[str, str]] = {} modes: Dict[str, Dict[str, str]] = {}
def data(self) -> Dict[str, Any]: def data(self) -> Dict[str, Any]:
quagga_bin_search = self.node.session.options.get_config( quagga_bin_search = self.node.session.options.get(
"quagga_bin_search", default="/usr/local/bin /usr/bin /usr/lib/quagga" "quagga_bin_search", default="/usr/local/bin /usr/bin /usr/lib/quagga"
).strip('"') ).strip('"')
quagga_sbin_search = self.node.session.options.get_config( quagga_sbin_search = self.node.session.options.get(
"quagga_sbin_search", default="/usr/local/sbin /usr/sbin /usr/lib/quagga" "quagga_sbin_search", default="/usr/local/sbin /usr/sbin /usr/lib/quagga"
).strip('"') ).strip('"')
quagga_state_dir = QUAGGA_STATE_DIR quagga_state_dir = QUAGGA_STATE_DIR

View file

@ -133,10 +133,10 @@ class EmaneManager:
self._emane_nets: Dict[int, EmaneNet] = {} self._emane_nets: Dict[int, EmaneNet] = {}
self._emane_node_lock: threading.Lock = threading.Lock() self._emane_node_lock: threading.Lock = threading.Lock()
# port numbers are allocated from these counters # port numbers are allocated from these counters
self.platformport: int = self.session.options.get_config_int( self.platformport: int = self.session.options.get_int(
"emane_platform_port", 8100 "emane_platform_port", 8100
) )
self.transformport: int = self.session.options.get_config_int( self.transformport: int = self.session.options.get_int(
"emane_transform_port", 8200 "emane_transform_port", 8200
) )
self.doeventloop: bool = False self.doeventloop: bool = False
@ -153,7 +153,7 @@ class EmaneManager:
self.nem_service: Dict[int, EmaneEventService] = {} self.nem_service: Dict[int, EmaneEventService] = {}
def next_nem_id(self, iface: CoreInterface) -> int: def next_nem_id(self, iface: CoreInterface) -> int:
nem_id = self.session.options.get_config_int("nem_id_start") nem_id = self.session.options.get_int("nem_id_start")
while nem_id in self.nems_to_ifaces: while nem_id in self.nems_to_ifaces:
nem_id += 1 nem_id += 1
self.nems_to_ifaces[nem_id] = iface self.nems_to_ifaces[nem_id] = iface
@ -484,7 +484,7 @@ class EmaneManager:
logger.exception("error writing to emane nem file") logger.exception("error writing to emane nem file")
def links_enabled(self) -> bool: def links_enabled(self) -> bool:
return self.session.options.get_config_int("link_enabled") == 1 return self.session.options.get_int("link_enabled") == 1
def poststartup(self) -> None: def poststartup(self) -> None:
""" """
@ -602,8 +602,8 @@ class EmaneManager:
""" """
node = iface.node node = iface.node
loglevel = str(DEFAULT_LOG_LEVEL) loglevel = str(DEFAULT_LOG_LEVEL)
cfgloglevel = self.session.options.get_config_int("emane_log_level") cfgloglevel = self.session.options.get_int("emane_log_level", 2)
realtime = self.session.options.get_config_bool("emane_realtime", default=True) realtime = self.session.options.get_bool("emane_realtime")
if cfgloglevel: if cfgloglevel:
logger.info("setting user-defined emane log level: %d", cfgloglevel) logger.info("setting user-defined emane log level: %d", cfgloglevel)
loglevel = str(cfgloglevel) loglevel = str(cfgloglevel)
@ -638,7 +638,7 @@ class EmaneManager:
""" """
# this support must be explicitly turned on; by default, CORE will # this support must be explicitly turned on; by default, CORE will
# generate the EMANE events when nodes are moved # generate the EMANE events when nodes are moved
return self.session.options.get_config_bool("emane_event_monitor") return self.session.options.get_bool("emane_event_monitor", False)
def genlocationevents(self) -> bool: def genlocationevents(self) -> bool:
""" """
@ -646,7 +646,7 @@ class EmaneManager:
""" """
# By default, CORE generates EMANE location events when nodes # By default, CORE generates EMANE location events when nodes
# are moved; this can be explicitly disabled in core.conf # are moved; this can be explicitly disabled in core.conf
tmp = self.session.options.get_config_bool("emane_event_generate") tmp = self.session.options.get_bool("emane_event_generate", True)
if tmp is None: if tmp is None:
tmp = not self.doeventmonitor() tmp = not self.doeventmonitor()
return tmp return tmp

View file

@ -190,9 +190,9 @@ class EmaneLinkMonitor:
def start(self) -> None: def start(self) -> None:
options = self.emane_manager.session.options options = self.emane_manager.session.options
self.loss_threshold = options.get_config_int("loss_threshold") self.loss_threshold = options.get_int("loss_threshold")
self.link_interval = options.get_config_int("link_interval") self.link_interval = options.get_int("link_interval")
self.link_timeout = options.get_config_int("link_timeout") self.link_timeout = options.get_int("link_timeout")
self.initialize() self.initialize()
if not self.clients: if not self.clients:
logger.info("no valid emane models to monitor links") logger.info("no valid emane models to monitor links")

View file

@ -125,9 +125,7 @@ class DistributedController:
self.session: "Session" = session self.session: "Session" = session
self.servers: Dict[str, DistributedServer] = OrderedDict() self.servers: Dict[str, DistributedServer] = OrderedDict()
self.tunnels: Dict[int, Tuple[GreTap, GreTap]] = {} self.tunnels: Dict[int, Tuple[GreTap, GreTap]] = {}
self.address: str = self.session.options.get_config( self.address: str = self.session.options.get("distributed_address")
"distributed_address", default=None
)
def add_server(self, name: str, host: str) -> None: def add_server(self, name: str, host: str) -> None:
""" """
@ -188,7 +186,7 @@ class DistributedController:
:return: nothing :return: nothing
""" """
mtu = self.session.options.get_config_int("mtu") mtu = self.session.options.get_int("mtu")
for node_id in self.session.nodes: for node_id in self.session.nodes:
node = self.session.nodes[node_id] node = self.session.nodes[node_id]
if not isinstance(node, CtrlNet) or node.serverintf is not None: if not isinstance(node, CtrlNet) or node.serverintf is not None:
@ -210,7 +208,7 @@ class DistributedController:
raise CoreError( raise CoreError(
"attempted to create gre tunnel for core link without a ptp network" "attempted to create gre tunnel for core link without a ptp network"
) )
mtu = self.session.options.get_config_int("mtu") mtu = self.session.options.get_int("mtu")
for server in self.servers.values(): for server in self.servers.values():
self.create_gre_tunnel(core_link.ptp, server, mtu, True) self.create_gre_tunnel(core_link.ptp, server, mtu, True)

View file

@ -191,7 +191,7 @@ class Session:
return node_type return node_type
def use_ovs(self) -> bool: def use_ovs(self) -> bool:
return self.options.get_config("ovs") == "1" return self.options.get_int("ovs") == 1
def linked( def linked(
self, node1_id: int, node2_id: int, iface1_id: int, iface2_id: int, linked: bool self, node1_id: int, node2_id: int, iface1_id: int, iface2_id: int, linked: bool
@ -255,7 +255,7 @@ class Session:
""" """
options = options if options else LinkOptions() options = options if options else LinkOptions()
# set mtu # set mtu
mtu = self.options.get_config_int("mtu") or DEFAULT_MTU mtu = self.options.get_int("mtu") or DEFAULT_MTU
if iface1_data: if iface1_data:
iface1_data.mtu = mtu iface1_data.mtu = mtu
if iface2_data: if iface2_data:
@ -489,7 +489,7 @@ class Session:
""" """
# set node start based on current session state, override and check when rj45 # set node start based on current session state, override and check when rj45
start = self.state.should_start() start = self.state.should_start()
enable_rj45 = self.options.get_config("enablerj45") == "1" enable_rj45 = self.options.get_int("enablerj45") == 1
if _class == Rj45Node and not enable_rj45: if _class == Rj45Node and not enable_rj45:
start = False start = False
@ -551,7 +551,7 @@ class Session:
node.add_config_service(service_class) node.add_config_service(service_class)
# set network mtu, if configured # set network mtu, if configured
mtu = self.options.get_config_int("mtu") mtu = self.options.get_int("mtu")
if isinstance(node, CoreNetworkBase) and mtu > 0: if isinstance(node, CoreNetworkBase) and mtu > 0:
node.mtu = mtu node.mtu = mtu
@ -714,7 +714,7 @@ class Session:
# shutdown sdt # shutdown sdt
self.sdt.shutdown() self.sdt.shutdown()
# remove this sessions working directory # remove this sessions working directory
preserve = self.options.get_config("preservedir") == "1" preserve = self.options.get_int("preservedir") == 1
if not preserve: if not preserve:
shutil.rmtree(self.directory, ignore_errors=True) shutil.rmtree(self.directory, ignore_errors=True)
@ -1234,11 +1234,11 @@ class Session:
:return: control net prefix list :return: control net prefix list
""" """
p = self.options.get_config("controlnet") p = self.options.get("controlnet")
p0 = self.options.get_config("controlnet0") p0 = self.options.get("controlnet0")
p1 = self.options.get_config("controlnet1") p1 = self.options.get("controlnet1")
p2 = self.options.get_config("controlnet2") p2 = self.options.get("controlnet2")
p3 = self.options.get_config("controlnet3") p3 = self.options.get("controlnet3")
if not p0 and p: if not p0 and p:
p0 = p p0 = p
return [p0, p1, p2, p3] return [p0, p1, p2, p3]
@ -1249,12 +1249,12 @@ class Session:
:return: list of control net server interfaces :return: list of control net server interfaces
""" """
d0 = self.options.get_config("controlnetif0") d0 = self.options.get("controlnetif0")
if d0: if d0:
logger.error("controlnet0 cannot be assigned with a host interface") logger.error("controlnet0 cannot be assigned with a host interface")
d1 = self.options.get_config("controlnetif1") d1 = self.options.get("controlnetif1")
d2 = self.options.get_config("controlnetif2") d2 = self.options.get("controlnetif2")
d3 = self.options.get_config("controlnetif3") d3 = self.options.get("controlnetif3")
return [None, d1, d2, d3] return [None, d1, d2, d3]
def get_control_net_index(self, dev: str) -> int: def get_control_net_index(self, dev: str) -> int:
@ -1331,7 +1331,7 @@ class Session:
updown_script = None updown_script = None
if net_index == 0: if net_index == 0:
updown_script = self.options.get_config("controlnet_updown_script") updown_script = self.options.get("controlnet_updown_script")
if not updown_script: if not updown_script:
logger.debug("controlnet updown script not configured") logger.debug("controlnet updown script not configured")
@ -1424,7 +1424,7 @@ class Session:
:param remove: flag to check if it should be removed :param remove: flag to check if it should be removed
:return: nothing :return: nothing
""" """
if not self.options.get_config_bool("update_etc_hosts", default=False): if not self.options.get_bool("update_etc_hosts", False):
return return
try: try:

View file

@ -1,23 +1,15 @@
from typing import Any, Dict, List from typing import Dict, List, Optional
from core.config import ( from core.config import ConfigBool, ConfigInt, ConfigString, Configuration
ConfigBool, from core.errors import CoreError
ConfigInt,
ConfigString,
ConfigurableManager,
ConfigurableOptions,
Configuration,
)
from core.emulator.enumerations import RegisterTlvs
from core.plugins.sdt import Sdt from core.plugins.sdt import Sdt
class SessionConfig(ConfigurableManager, ConfigurableOptions): class SessionConfig:
""" """
Provides session configuration. Provides session configuration.
""" """
name: str = "session"
options: List[Configuration] = [ options: List[Configuration] = [
ConfigString(id="controlnet", label="Control Network"), ConfigString(id="controlnet", label="Control Network"),
ConfigString(id="controlnet0", label="Control Network 0"), ConfigString(id="controlnet0", label="Control Network 0"),
@ -42,37 +34,54 @@ class SessionConfig(ConfigurableManager, ConfigurableOptions):
ConfigInt(id="link_timeout", default="4", label="EMANE Link Timeout (sec)"), ConfigInt(id="link_timeout", default="4", label="EMANE Link Timeout (sec)"),
ConfigInt(id="mtu", default="0", label="MTU for All Devices"), ConfigInt(id="mtu", default="0", label="MTU for All Devices"),
] ]
config_type: RegisterTlvs = RegisterTlvs.UTILITY
def __init__(self, config: Dict[str, str] = None) -> None: def __init__(self, config: Dict[str, str] = None) -> None:
super().__init__()
self.set_configs(self.default_values())
if config:
for key, value in config.items():
self.set_config(key, value)
def get_config(
self,
_id: str,
node_id: int = ConfigurableManager._default_node,
config_type: str = ConfigurableManager._default_type,
default: Any = None,
) -> str:
""" """
Retrieves a specific configuration for a node and configuration type. Create a SessionConfig instance.
:param _id: specific configuration to retrieve :param config: configuration to initialize with
:param node_id: node id to store configuration for
:param config_type: configuration type to store configuration for
:param default: default value to return when value is not found
:return: configuration value
""" """
value = super().get_config(_id, node_id, config_type, default) self._config: Dict[str, str] = {x.id: x.default for x in self.options}
if value == "": self._config.update(config or {})
value = default
return value
def get_config_bool(self, name: str, default: Any = None) -> bool: def update(self, config: Dict[str, str]) -> None:
"""
Update current configuration with provided values.
:param config: configuration to update with
:return: nothing
"""
self._config.update(config)
def set(self, name: str, value: str) -> None:
"""
Set a configuration value.
:param name: name of configuration to set
:param value: value to set
:return: nothing
"""
self._config[name] = value
def get(self, name: str, default: str = None) -> Optional[str]:
"""
Retrieve configuration value.
:param name: name of configuration to get
:param default: value to return as default
:return: return found configuration value or default
"""
return self._config.get(name, default)
def all(self) -> Dict[str, str]:
"""
Retrieve all configuration options.
:return: configuration value dict
"""
return self._config
def get_bool(self, name: str, default: bool = None) -> bool:
""" """
Get configuration value as a boolean. Get configuration value as a boolean.
@ -80,12 +89,15 @@ class SessionConfig(ConfigurableManager, ConfigurableOptions):
:param default: default value if not found :param default: default value if not found
:return: boolean for configuration value :return: boolean for configuration value
""" """
value = self.get_config(name) value = self._config.get(name)
if value is None and default is None:
raise CoreError(f"missing session options for {name}")
if value is None: if value is None:
return default return default
else:
return value.lower() == "true" return value.lower() == "true"
def get_config_int(self, name: str, default: Any = None) -> int: def get_int(self, name: str, default: int = None) -> int:
""" """
Get configuration value as int. Get configuration value as int.
@ -93,17 +105,10 @@ class SessionConfig(ConfigurableManager, ConfigurableOptions):
:param default: default value if not found :param default: default value if not found
:return: int for configuration value :return: int for configuration value
""" """
value = self.get_config(name, default=default) value = self._config.get(name)
if value is not None: if value is None and default is None:
value = int(value) raise CoreError(f"missing session options for {name}")
return value if value is None:
return default
def config_reset(self, node_id: int = None) -> None: else:
""" return int(value)
Clear prior configuration files and reset to default values.
:param node_id: node id to store configuration for
:return: nothing
"""
super().config_reset(node_id)
self.set_configs(self.default_values())

View file

@ -434,7 +434,7 @@ class CoreNodeBase(NodeBase):
:return: nothing :return: nothing
""" """
preserve = self.session.options.get_config("preservedir") == "1" preserve = self.session.options.get_int("preservedir") == 1
if preserve: if preserve:
return return
if self.tmpnodedir: if self.tmpnodedir:

View file

@ -87,7 +87,7 @@ class Sdt:
:return: True if enabled, False otherwise :return: True if enabled, False otherwise
""" """
return self.session.options.get_config("enablesdt") == "1" return self.session.options.get_int("enablesdt") == 1
def seturl(self) -> None: def seturl(self) -> None:
""" """
@ -96,7 +96,7 @@ class Sdt:
:return: nothing :return: nothing
""" """
url = self.session.options.get_config("stdurl", default=self.DEFAULT_SDT_URL) url = self.session.options.get("stdurl", self.DEFAULT_SDT_URL)
self.url = urlparse(url) self.url = urlparse(url)
self.address = (self.url.hostname, self.url.port) self.address = (self.url.hostname, self.url.port)
self.protocol = self.url.scheme self.protocol = self.url.scheme

View file

@ -138,11 +138,11 @@ class FRRZebra(CoreService):
""" """
Generate a shell script used to boot the FRR daemons. Generate a shell script used to boot the FRR daemons.
""" """
frr_bin_search = node.session.options.get_config( frr_bin_search = node.session.options.get(
"frr_bin_search", default='"/usr/local/bin /usr/bin /usr/lib/frr"' "frr_bin_search", '"/usr/local/bin /usr/bin /usr/lib/frr"'
) )
frr_sbin_search = node.session.options.get_config( frr_sbin_search = node.session.options.get(
"frr_sbin_search", default='"/usr/local/sbin /usr/sbin /usr/lib/frr"' "frr_sbin_search", '"/usr/local/sbin /usr/sbin /usr/lib/frr"'
) )
cfg = """\ cfg = """\
#!/bin/sh #!/bin/sh

View file

@ -135,11 +135,11 @@ class Zebra(CoreService):
""" """
Generate a shell script used to boot the Quagga daemons. Generate a shell script used to boot the Quagga daemons.
""" """
quagga_bin_search = node.session.options.get_config( quagga_bin_search = node.session.options.get(
"quagga_bin_search", default='"/usr/local/bin /usr/bin /usr/lib/quagga"' "quagga_bin_search", '"/usr/local/bin /usr/bin /usr/lib/quagga"'
) )
quagga_sbin_search = node.session.options.get_config( quagga_sbin_search = node.session.options.get(
"quagga_sbin_search", default='"/usr/local/sbin /usr/sbin /usr/lib/quagga"' "quagga_sbin_search", '"/usr/local/sbin /usr/sbin /usr/lib/quagga"'
) )
return """\ return """\
#!/bin/sh #!/bin/sh

View file

@ -44,9 +44,7 @@ class Ucarp(CoreService):
""" """
Returns configuration file text. Returns configuration file text.
""" """
ucarp_bin = node.session.options.get_config( ucarp_bin = node.session.options.get("ucarp_bin", "/usr/sbin/ucarp")
"ucarp_bin", default="/usr/sbin/ucarp"
)
return """\ return """\
#!/bin/sh #!/bin/sh
# Location of UCARP executable # Location of UCARP executable

View file

@ -336,16 +336,9 @@ class CoreXmlWriter:
def write_session_options(self) -> None: def write_session_options(self) -> None:
option_elements = etree.Element("session_options") option_elements = etree.Element("session_options")
options_config = self.session.options.get_configs() for option in self.session.options.options:
if not options_config: value = self.session.options.get(option.id)
return add_configuration(option_elements, option.id, value)
default_options = self.session.options.default_values()
for _id in default_options:
default_value = default_options[_id]
value = options_config.get(_id, default_value)
add_configuration(option_elements, _id, value)
if option_elements.getchildren(): if option_elements.getchildren():
self.scenario.append(option_elements) self.scenario.append(option_elements)
@ -625,8 +618,7 @@ class CoreXmlReader:
value = configuration.get("value") value = configuration.get("value")
xml_config[name] = value xml_config[name] = value
logger.info("reading session options: %s", xml_config) logger.info("reading session options: %s", xml_config)
config = self.session.options.get_configs() self.session.options.update(xml_config)
config.update(xml_config)
def read_session_hooks(self) -> None: def read_session_hooks(self) -> None:
session_hooks = self.scenario.find("session_hooks") session_hooks = self.scenario.find("session_hooks")

View file

@ -161,7 +161,7 @@ class TestGrpc:
real_node1, service_name, service_file real_node1, service_name, service_file
) )
assert service_file.data == service_file_data assert service_file.data == service_file_data
assert option_value == real_session.options.get_config(option_key) assert option_value == real_session.options.get(option_key)
@pytest.mark.parametrize("session_id", [None, 6013]) @pytest.mark.parametrize("session_id", [None, 6013])
def test_create_session( def test_create_session(