daemon: updated config.py to use dataclasses for config classes, updated naming and referencing. updated configurable options to self validate default values align with the config type. updated the example emane model to better align with the current state of things

This commit is contained in:
Blake Harnden 2021-03-31 11:13:40 -07:00
parent bb3590fbde
commit 6086d1229b
14 changed files with 171 additions and 133 deletions

View file

@ -4,10 +4,12 @@ Common support for configurable CORE objects.
import logging import logging
from collections import OrderedDict from collections import OrderedDict
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Type, Union from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Set, Tuple, Type, Union
from core.emane.nodes import EmaneNet from core.emane.nodes import EmaneNet
from core.emulator.enumerations import ConfigDataTypes from core.emulator.enumerations import ConfigDataTypes
from core.errors import CoreConfigError
from core.nodes.network import WlanNode from core.nodes.network import WlanNode
if TYPE_CHECKING: if TYPE_CHECKING:
@ -15,62 +17,56 @@ if TYPE_CHECKING:
WirelessModelType = Type[WirelessModel] WirelessModelType = Type[WirelessModel]
_BOOL_OPTIONS: Set[str] = {"0", "1"}
@dataclass
class ConfigGroup: class ConfigGroup:
""" """
Defines configuration group tabs used for display by ConfigurationOptions. Defines configuration group tabs used for display by ConfigurationOptions.
""" """
def __init__(self, name: str, start: int, stop: int) -> None: name: str
""" start: int
Creates a ConfigGroup object. stop: int
:param name: configuration group display name
:param start: configurations start index for this group
:param stop: configurations stop index for this group
"""
self.name: str = name
self.start: int = start
self.stop: int = stop
@dataclass
class Configuration: class Configuration:
""" """
Represents a configuration options. Represents a configuration options.
""" """
def __init__( id: str
self, type: ConfigDataTypes
_id: str, label: str = None
_type: ConfigDataTypes, default: str = ""
label: str = None, options: List[str] = field(default_factory=list)
default: str = "",
options: List[str] = None,
) -> None:
"""
Creates a Configuration object.
:param _id: unique name for configuration def __post_init__(self) -> None:
:param _type: configuration data type self.label = self.label if self.label else self.id
:param label: configuration label for display if self.type == ConfigDataTypes.BOOL:
:param default: default value for configuration if self.default and self.default not in _BOOL_OPTIONS:
:param options: list options if this is a configuration with a combobox raise CoreConfigError(
""" f"{self.id} bool value must be one of: {_BOOL_OPTIONS}: "
self.id: str = _id f"{self.default}"
self.type: ConfigDataTypes = _type )
self.default: str = default elif self.type == ConfigDataTypes.FLOAT:
if not options: if self.default:
options = [] try:
self.options: List[str] = options float(self.default)
if not label: except ValueError:
label = _id raise CoreConfigError(
self.label: str = label f"{self.id} is not a valid float: {self.default}"
)
def __str__(self): elif self.type != ConfigDataTypes.STRING:
return ( if self.default:
f"{self.__class__.__name__}(id={self.id}, type={self.type}, " try:
f"default={self.default}, options={self.options})" int(self.default)
) except ValueError:
raise CoreConfigError(
f"{self.id} is not a valid int: {self.default}"
)
class ConfigurableOptions: class ConfigurableOptions:

View file

@ -20,20 +20,20 @@ class VpnClient(ConfigService):
validation_mode: ConfigServiceMode = ConfigServiceMode.BLOCKING validation_mode: ConfigServiceMode = ConfigServiceMode.BLOCKING
default_configs: List[Configuration] = [ default_configs: List[Configuration] = [
Configuration( Configuration(
_id="keydir", id="keydir",
_type=ConfigDataTypes.STRING, type=ConfigDataTypes.STRING,
label="Key Dir", label="Key Dir",
default="/etc/core/keys", default="/etc/core/keys",
), ),
Configuration( Configuration(
_id="keyname", id="keyname",
_type=ConfigDataTypes.STRING, type=ConfigDataTypes.STRING,
label="Key Name", label="Key Name",
default="client1", default="client1",
), ),
Configuration( Configuration(
_id="server", id="server",
_type=ConfigDataTypes.STRING, type=ConfigDataTypes.STRING,
label="Server", label="Server",
default="10.0.2.10", default="10.0.2.10",
), ),
@ -54,20 +54,20 @@ class VpnServer(ConfigService):
validation_mode: ConfigServiceMode = ConfigServiceMode.BLOCKING validation_mode: ConfigServiceMode = ConfigServiceMode.BLOCKING
default_configs: List[Configuration] = [ default_configs: List[Configuration] = [
Configuration( Configuration(
_id="keydir", id="keydir",
_type=ConfigDataTypes.STRING, type=ConfigDataTypes.STRING,
label="Key Dir", label="Key Dir",
default="/etc/core/keys", default="/etc/core/keys",
), ),
Configuration( Configuration(
_id="keyname", id="keyname",
_type=ConfigDataTypes.STRING, type=ConfigDataTypes.STRING,
label="Key Name", label="Key Name",
default="server", default="server",
), ),
Configuration( Configuration(
_id="subnet", id="subnet",
_type=ConfigDataTypes.STRING, type=ConfigDataTypes.STRING,
label="Subnet", label="Subnet",
default="10.0.200.0", default="10.0.200.0",
), ),

View file

@ -17,11 +17,11 @@ class SimpleService(ConfigService):
shutdown: List[str] = [] shutdown: List[str] = []
validation_mode: ConfigServiceMode = ConfigServiceMode.BLOCKING validation_mode: ConfigServiceMode = ConfigServiceMode.BLOCKING
default_configs: List[Configuration] = [ default_configs: List[Configuration] = [
Configuration(_id="value1", _type=ConfigDataTypes.STRING, label="Text"), Configuration(id="value1", type=ConfigDataTypes.STRING, label="Text"),
Configuration(_id="value2", _type=ConfigDataTypes.BOOL, label="Boolean"), Configuration(id="value2", type=ConfigDataTypes.BOOL, label="Boolean"),
Configuration( Configuration(
_id="value3", id="value3",
_type=ConfigDataTypes.STRING, type=ConfigDataTypes.STRING,
label="Multiple Choice", label="Multiple Choice",
options=["value1", "value2", "value3"], options=["value1", "value2", "value3"],
), ),

View file

@ -18,8 +18,8 @@ class EmaneBypassModel(emanemodel.EmaneModel):
mac_library: str = "bypassmaclayer" mac_library: str = "bypassmaclayer"
mac_config: List[Configuration] = [ mac_config: List[Configuration] = [
Configuration( Configuration(
_id="none", id="none",
_type=ConfigDataTypes.BOOL, type=ConfigDataTypes.BOOL,
default="0", default="0",
label="There are no parameters for the bypass model.", label="There are no parameters for the bypass model.",
) )

View file

@ -825,38 +825,38 @@ class EmaneGlobalModel:
self.session: "Session" = session self.session: "Session" = session
self.core_config: List[Configuration] = [ self.core_config: List[Configuration] = [
Configuration( Configuration(
_id="platform_id_start", id="platform_id_start",
_type=ConfigDataTypes.INT32, type=ConfigDataTypes.INT32,
default="1", default="1",
label="Starting Platform ID", label="Starting Platform ID",
), ),
Configuration( Configuration(
_id="nem_id_start", id="nem_id_start",
_type=ConfigDataTypes.INT32, type=ConfigDataTypes.INT32,
default="1", default="1",
label="Starting NEM ID", label="Starting NEM ID",
), ),
Configuration( Configuration(
_id="link_enabled", id="link_enabled",
_type=ConfigDataTypes.BOOL, type=ConfigDataTypes.BOOL,
default="1", default="1",
label="Enable Links?", label="Enable Links?",
), ),
Configuration( Configuration(
_id="loss_threshold", id="loss_threshold",
_type=ConfigDataTypes.INT32, type=ConfigDataTypes.INT32,
default="30", default="30",
label="Link Loss Threshold (%)", label="Link Loss Threshold (%)",
), ),
Configuration( Configuration(
_id="link_interval", id="link_interval",
_type=ConfigDataTypes.INT32, type=ConfigDataTypes.INT32,
default="1", default="1",
label="Link Check Interval (sec)", label="Link Check Interval (sec)",
), ),
Configuration( Configuration(
_id="link_timeout", id="link_timeout",
_type=ConfigDataTypes.INT32, type=ConfigDataTypes.INT32,
default="4", default="4",
label="Link Timeout (sec)", label="Link Timeout (sec)",
), ),

View file

@ -118,8 +118,8 @@ def parse(manifest_path: Path, defaults: Dict[str, str]) -> List[Configuration]:
config_descriptions = f"{config_descriptions} file" config_descriptions = f"{config_descriptions} file"
configuration = Configuration( configuration = Configuration(
_id=config_name, id=config_name,
_type=config_type_value, type=config_type_value,
default=config_default, default=config_default,
options=possible, options=possible,
label=config_descriptions, label=config_descriptions,

View file

@ -35,8 +35,8 @@ class EmaneTdmaModel(emanemodel.EmaneModel):
) )
super().load(emane_prefix) super().load(emane_prefix)
config_item = Configuration( config_item = Configuration(
_id=cls.schedule_name, id=cls.schedule_name,
_type=ConfigDataTypes.STRING, type=ConfigDataTypes.STRING,
default=str(cls.default_schedule), default=str(cls.default_schedule),
label="TDMA schedule file (core)", label="TDMA schedule file (core)",
) )

View file

@ -13,51 +13,51 @@ class SessionConfig(ConfigurableManager, ConfigurableOptions):
name: str = "session" name: str = "session"
options: List[Configuration] = [ options: List[Configuration] = [
Configuration( Configuration(
_id="controlnet", _type=ConfigDataTypes.STRING, label="Control Network" id="controlnet", type=ConfigDataTypes.STRING, label="Control Network"
), ),
Configuration( Configuration(
_id="controlnet0", _type=ConfigDataTypes.STRING, label="Control Network 0" id="controlnet0", type=ConfigDataTypes.STRING, label="Control Network 0"
), ),
Configuration( Configuration(
_id="controlnet1", _type=ConfigDataTypes.STRING, label="Control Network 1" id="controlnet1", type=ConfigDataTypes.STRING, label="Control Network 1"
), ),
Configuration( Configuration(
_id="controlnet2", _type=ConfigDataTypes.STRING, label="Control Network 2" id="controlnet2", type=ConfigDataTypes.STRING, label="Control Network 2"
), ),
Configuration( Configuration(
_id="controlnet3", _type=ConfigDataTypes.STRING, label="Control Network 3" id="controlnet3", type=ConfigDataTypes.STRING, label="Control Network 3"
), ),
Configuration( Configuration(
_id="controlnet_updown_script", id="controlnet_updown_script",
_type=ConfigDataTypes.STRING, type=ConfigDataTypes.STRING,
label="Control Network Script", label="Control Network Script",
), ),
Configuration( Configuration(
_id="enablerj45", id="enablerj45",
_type=ConfigDataTypes.BOOL, type=ConfigDataTypes.BOOL,
default="1", default="1",
label="Enable RJ45s", label="Enable RJ45s",
), ),
Configuration( Configuration(
_id="preservedir", id="preservedir",
_type=ConfigDataTypes.BOOL, type=ConfigDataTypes.BOOL,
default="0", default="0",
label="Preserve session dir", label="Preserve session dir",
), ),
Configuration( Configuration(
_id="enablesdt", id="enablesdt",
_type=ConfigDataTypes.BOOL, type=ConfigDataTypes.BOOL,
default="0", default="0",
label="Enable SDT3D output", label="Enable SDT3D output",
), ),
Configuration( Configuration(
_id="sdturl", id="sdturl",
_type=ConfigDataTypes.STRING, type=ConfigDataTypes.STRING,
default=Sdt.DEFAULT_SDT_URL, default=Sdt.DEFAULT_SDT_URL,
label="SDT3D URL", label="SDT3D URL",
), ),
Configuration( Configuration(
_id="ovs", _type=ConfigDataTypes.BOOL, default="0", label="Enable OVS" id="ovs", type=ConfigDataTypes.BOOL, default="0", label="Enable OVS"
), ),
] ]
config_type: RegisterTlvs = RegisterTlvs.UTILITY config_type: RegisterTlvs = RegisterTlvs.UTILITY

View file

@ -46,3 +46,11 @@ class CoreServiceBootError(Exception):
""" """
pass pass
class CoreConfigError(Exception):
"""
Used when there is an error defining a configurable option.
"""
pass

View file

@ -236,35 +236,35 @@ class BasicRangeModel(WirelessModel):
name: str = "basic_range" name: str = "basic_range"
options: List[Configuration] = [ options: List[Configuration] = [
Configuration( Configuration(
_id="range", id="range",
_type=ConfigDataTypes.UINT32, type=ConfigDataTypes.UINT32,
default="275", default="275",
label="wireless range (pixels)", label="wireless range (pixels)",
), ),
Configuration( Configuration(
_id="bandwidth", id="bandwidth",
_type=ConfigDataTypes.UINT64, type=ConfigDataTypes.UINT64,
default="54000000", default="54000000",
label="bandwidth (bps)", label="bandwidth (bps)",
), ),
Configuration( Configuration(
_id="jitter", id="jitter",
_type=ConfigDataTypes.UINT64, type=ConfigDataTypes.UINT64,
default="0", default="0",
label="transmission jitter (usec)", label="transmission jitter (usec)",
), ),
Configuration( Configuration(
_id="delay", id="delay",
_type=ConfigDataTypes.UINT64, type=ConfigDataTypes.UINT64,
default="5000", default="5000",
label="transmission delay (usec)", label="transmission delay (usec)",
), ),
Configuration( Configuration(
_id="error", _type=ConfigDataTypes.STRING, default="0", label="loss (%)" id="error", type=ConfigDataTypes.STRING, default="0", label="loss (%)"
), ),
Configuration( Configuration(
_id="promiscuous", id="promiscuous",
_type=ConfigDataTypes.BOOL, type=ConfigDataTypes.BOOL,
default="0", default="0",
label="promiscuous mode", label="promiscuous mode",
), ),
@ -868,40 +868,38 @@ class Ns2ScriptedMobility(WayPointMobility):
name: str = "ns2script" name: str = "ns2script"
options: List[Configuration] = [ options: List[Configuration] = [
Configuration( Configuration(
_id="file", _type=ConfigDataTypes.STRING, label="mobility script file" id="file", type=ConfigDataTypes.STRING, label="mobility script file"
), ),
Configuration( Configuration(
_id="refresh_ms", id="refresh_ms",
_type=ConfigDataTypes.UINT32, type=ConfigDataTypes.UINT32,
default="50", default="50",
label="refresh time (ms)", label="refresh time (ms)",
), ),
Configuration(id="loop", type=ConfigDataTypes.BOOL, default="1", label="loop"),
Configuration( Configuration(
_id="loop", _type=ConfigDataTypes.BOOL, default="1", label="loop" id="autostart",
), type=ConfigDataTypes.STRING,
Configuration(
_id="autostart",
_type=ConfigDataTypes.STRING,
label="auto-start seconds (0.0 for runtime)", label="auto-start seconds (0.0 for runtime)",
), ),
Configuration( Configuration(
_id="map", id="map",
_type=ConfigDataTypes.STRING, type=ConfigDataTypes.STRING,
label="node mapping (optional, e.g. 0:1,1:2,2:3)", label="node mapping (optional, e.g. 0:1,1:2,2:3)",
), ),
Configuration( Configuration(
_id="script_start", id="script_start",
_type=ConfigDataTypes.STRING, type=ConfigDataTypes.STRING,
label="script file to run upon start", label="script file to run upon start",
), ),
Configuration( Configuration(
_id="script_pause", id="script_pause",
_type=ConfigDataTypes.STRING, type=ConfigDataTypes.STRING,
label="script file to run upon pause", label="script file to run upon pause",
), ),
Configuration( Configuration(
_id="script_stop", id="script_stop",
_type=ConfigDataTypes.STRING, type=ConfigDataTypes.STRING,
label="script file to run upon stop", label="script file to run upon stop",
), ),
] ]

View file

@ -1,6 +1,7 @@
""" """
Example custom emane model. Example custom emane model.
""" """
from pathlib import Path
from typing import Dict, List, Optional, Set from typing import Dict, List, Optional, Set
from core.config import Configuration from core.config import Configuration
@ -39,17 +40,34 @@ class ExampleModel(emanemodel.EmaneModel):
name: str = "emane_example" name: str = "emane_example"
mac_library: str = "rfpipemaclayer" mac_library: str = "rfpipemaclayer"
mac_xml: str = "/usr/share/emane/manifest/rfpipemaclayer.xml" mac_xml: str = "rfpipemaclayer.xml"
mac_defaults: Dict[str, str] = { mac_defaults: Dict[str, str] = {
"pcrcurveuri": "/usr/share/emane/xml/models/mac/rfpipe/rfpipepcr.xml" "pcrcurveuri": "/usr/share/emane/xml/models/mac/rfpipe/rfpipepcr.xml"
} }
mac_config: List[Configuration] = emanemanifest.parse(mac_xml, mac_defaults) mac_config: List[Configuration] = []
phy_library: Optional[str] = None phy_library: Optional[str] = None
phy_xml: str = "/usr/share/emane/manifest/emanephy.xml" phy_xml: str = "emanephy.xml"
phy_defaults: Dict[str, str] = { phy_defaults: Dict[str, str] = {
"subid": "1", "subid": "1",
"propagationmodel": "2ray", "propagationmodel": "2ray",
"noisemode": "none", "noisemode": "none",
} }
phy_config: List[Configuration] = emanemanifest.parse(phy_xml, phy_defaults) phy_config: List[Configuration] = []
config_ignore: Set[str] = set() config_ignore: Set[str] = set()
@classmethod
def load(cls, emane_prefix: Path) -> None:
"""
Called after being loaded within the EmaneManager. Provides configured
emane_prefix for parsing xml files.
:param emane_prefix: configured emane prefix path
:return: nothing
"""
manifest_path = "share/emane/manifest"
# load mac configuration
mac_xml_path = emane_prefix / manifest_path / cls.mac_xml
cls.mac_config = emanemanifest.parse(mac_xml_path, cls.mac_defaults)
# load phy configuration
phy_xml_path = emane_prefix / manifest_path / cls.phy_xml
cls.phy_config = emanemanifest.parse(phy_xml_path, cls.phy_defaults)

View file

@ -17,8 +17,8 @@ class TestConfigurableOptions(ConfigurableOptions):
name1 = "value1" name1 = "value1"
name2 = "value2" name2 = "value2"
options = [ options = [
Configuration(_id=name1, _type=ConfigDataTypes.STRING, label=name1), Configuration(id=name1, type=ConfigDataTypes.STRING, label=name1),
Configuration(_id=name2, _type=ConfigDataTypes.STRING, label=name2), Configuration(id=name2, type=ConfigDataTypes.STRING, label=name2),
] ]

View file

@ -27,11 +27,11 @@ class MyService(ConfigService):
shutdown = [f"pkill {files[0]}"] shutdown = [f"pkill {files[0]}"]
validation_mode = ConfigServiceMode.BLOCKING validation_mode = ConfigServiceMode.BLOCKING
default_configs = [ default_configs = [
Configuration(_id="value1", _type=ConfigDataTypes.STRING, label="Text"), Configuration(id="value1", type=ConfigDataTypes.STRING, label="Text"),
Configuration(_id="value2", _type=ConfigDataTypes.BOOL, label="Boolean"), Configuration(id="value2", type=ConfigDataTypes.BOOL, label="Boolean"),
Configuration( Configuration(
_id="value3", id="value3",
_type=ConfigDataTypes.STRING, type=ConfigDataTypes.STRING,
label="Multiple Choice", label="Multiple Choice",
options=["value1", "value2", "value3"], options=["value1", "value2", "value3"],
), ),

View file

@ -120,7 +120,8 @@ Here is an example model with documentation describing functionality:
""" """
Example custom emane model. Example custom emane model.
""" """
from typing import Dict, List, Optional, Set from pathlib import Path
from typing import Dict, Optional, Set, List
from core.config import Configuration from core.config import Configuration
from core.emane import emanemanifest, emanemodel from core.emane import emanemanifest, emanemodel
@ -162,14 +163,31 @@ class ExampleModel(emanemodel.EmaneModel):
mac_defaults: Dict[str, str] = { mac_defaults: Dict[str, str] = {
"pcrcurveuri": "/usr/share/emane/xml/models/mac/rfpipe/rfpipepcr.xml" "pcrcurveuri": "/usr/share/emane/xml/models/mac/rfpipe/rfpipepcr.xml"
} }
mac_config: List[Configuration] = emanemanifest.parse(mac_xml, mac_defaults) mac_config: List[Configuration] = []
phy_library: Optional[str] = None phy_library: Optional[str] = None
phy_xml: str = "/usr/share/emane/manifest/emanephy.xml" phy_xml: str = "/usr/share/emane/manifest/emanephy.xml"
phy_defaults: Dict[str, str] = { phy_defaults: Dict[str, str] = {
"subid": "1", "propagationmodel": "2ray", "noisemode": "none" "subid": "1", "propagationmodel": "2ray", "noisemode": "none"
} }
phy_config: List[Configuration] = emanemanifest.parse(phy_xml, phy_defaults) phy_config: List[Configuration] = []
config_ignore: Set[str] = set() config_ignore: Set[str] = set()
@classmethod
def load(cls, emane_prefix: Path) -> None:
"""
Called after being loaded within the EmaneManager. Provides configured
emane_prefix for parsing xml files.
:param emane_prefix: configured emane prefix path
:return: nothing
"""
manifest_path = "share/emane/manifest"
# load mac configuration
mac_xml_path = emane_prefix / manifest_path / cls.mac_xml
cls.mac_config = emanemanifest.parse(mac_xml_path, cls.mac_defaults)
# load phy configuration
phy_xml_path = emane_prefix / manifest_path / cls.phy_xml
cls.phy_config = emanemanifest.parse(phy_xml_path, cls.phy_defaults)
``` ```
## Single PC with EMANE ## Single PC with EMANE