initial code for a wrapped grpc client, fix for pygui node emane config, fix for xml reading emane configs specific to nodes/interfaces, fix for adding emane nodes and setting the emane model properly

This commit is contained in:
Blake Harnden 2020-09-01 16:19:01 -07:00
parent b0bac1d319
commit 570ad9522c
9 changed files with 1583 additions and 19 deletions

File diff suppressed because it is too large Load diff

View file

@ -1438,7 +1438,9 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
""" """
logging.debug("get emane model config: %s", request) logging.debug("get emane model config: %s", request)
session = self.get_session(request.session_id, context) session = self.get_session(request.session_id, context)
model = session.emane.models[request.model] model = session.emane.models.get(request.model)
if not model:
raise CoreError(f"invalid emane model: {request.model}")
_id = get_emane_model_id(request.node_id, request.iface_id) _id = get_emane_model_id(request.node_id, request.iface_id)
current_config = session.emane.get_model_config(_id, request.model) current_config = session.emane.get_model_config(_id, request.model)
config = get_config_options(current_config, model) config = get_config_options(current_config, model)
@ -1483,7 +1485,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
self, request: core_pb2.SaveXmlRequest, context: ServicerContext self, request: core_pb2.SaveXmlRequest, context: ServicerContext
) -> core_pb2.SaveXmlResponse: ) -> core_pb2.SaveXmlResponse:
""" """
Export the session nto the EmulationScript XML format Export the session into the EmulationScript XML format
:param request: save xml request :param request: save xml request
:param context: context object :param context: context object

View file

@ -3,7 +3,13 @@ from enum import Enum
from pathlib import Path from pathlib import Path
from typing import Dict, List, Optional, Set, Tuple from typing import Dict, List, Optional, Set, Tuple
from core.api.grpc import common_pb2, configservices_pb2, core_pb2, services_pb2 from core.api.grpc import (
common_pb2,
configservices_pb2,
core_pb2,
emane_pb2,
services_pb2,
)
class ConfigServiceValidationMode(Enum): class ConfigServiceValidationMode(Enum):
@ -87,6 +93,13 @@ class MessageType(Enum):
TTY = 64 TTY = 64
class ServiceAction(Enum):
START = 0
STOP = 1
RESTART = 2
VALIDATE = 3
@dataclass @dataclass
class ConfigService: class ConfigService:
group: str group: str
@ -120,12 +133,67 @@ class ConfigService:
) )
@dataclass
class ConfigServiceConfig:
node_id: int
name: str
templates: Dict[str, str]
config: Dict[str, str]
@classmethod
def from_proto(
cls, proto: configservices_pb2.ConfigServiceConfig
) -> "ConfigServiceConfig":
return ConfigServiceConfig(
node_id=proto.node_id,
name=proto.name,
templates=dict(proto.templates),
config=dict(proto.config),
)
@dataclass @dataclass
class ConfigServiceData: class ConfigServiceData:
templates: Dict[str, str] = field(default_factory=dict) templates: Dict[str, str] = field(default_factory=dict)
config: Dict[str, str] = field(default_factory=dict) config: Dict[str, str] = field(default_factory=dict)
@dataclass
class ConfigServiceDefaults:
templates: Dict[str, str]
config: Dict[str, "ConfigOption"]
modes: List[str]
@classmethod
def from_proto(
cls, proto: configservices_pb2.GetConfigServicesResponse
) -> "ConfigServiceDefaults":
config = ConfigOption.from_dict(proto.config)
return ConfigServiceDefaults(
templates=dict(proto.templates), config=config, modes=list(proto.modes)
)
@dataclass
class Service:
group: str
name: str
@classmethod
def from_proto(cls, proto: services_pb2.Service) -> "Service":
return Service(group=proto.group, name=proto.name)
@dataclass
class ServiceDefault:
node_type: str
services: List[str]
@classmethod
def from_proto(cls, proto: services_pb2.ServiceDefaults) -> "ServiceDefault":
return ServiceDefault(node_type=proto.node_type, services=list(proto.services))
@dataclass @dataclass
class NodeServiceData: class NodeServiceData:
executables: List[str] executables: List[str]
@ -155,6 +223,28 @@ class NodeServiceData:
) )
@dataclass
class ServiceConfig:
node_id: int
service: str
files: List[str] = None
directories: List[str] = None
startup: List[str] = None
validate: List[str] = None
shutdown: List[str] = None
def to_proto(self) -> services_pb2.ServiceConfig:
return services_pb2.ServiceConfig(
node_id=self.node_id,
service=self.service,
files=self.files,
directories=self.directories,
startup=self.startup,
validate=self.validate,
shutdown=self.shutdown,
)
@dataclass @dataclass
class BridgeThroughput: class BridgeThroughput:
node_id: int node_id: int
@ -471,6 +561,30 @@ class Hook:
return core_pb2.Hook(state=self.state.value, file=self.file, data=self.data) return core_pb2.Hook(state=self.state.value, file=self.file, data=self.data)
@dataclass
class EmaneModelConfig:
node_id: int
model: str
iface_id: int = -1
config: Dict[str, ConfigOption] = None
@classmethod
def from_proto(cls, proto: emane_pb2.GetEmaneModelConfig) -> "EmaneModelConfig":
iface_id = proto.iface_id if proto.iface_id != -1 else None
config = ConfigOption.from_dict(proto.config)
return EmaneModelConfig(
node_id=proto.node_id, iface_id=iface_id, model=proto.model, config=config
)
def to_proto(self) -> emane_pb2.EmaneModelConfig:
return emane_pb2.EmaneModelConfig(
node_id=self.node_id,
model=self.model,
iface_id=self.iface_id,
config=self.config,
)
@dataclass @dataclass
class Position: class Position:
x: float x: float
@ -660,3 +774,18 @@ class NodeEvent:
message_type=MessageType(proto.message_type), message_type=MessageType(proto.message_type),
node=Node.from_proto(proto.node), node=Node.from_proto(proto.node),
) )
@dataclass
class EmaneEventChannel:
group: str
port: int
device: str
@classmethod
def from_proto(
cls, proto: emane_pb2.GetEmaneEventChannelResponse
) -> "EmaneEventChannel":
return EmaneEventChannel(
group=proto.group, port=proto.port, device=proto.device
)

View file

@ -212,7 +212,7 @@ class ConfigurableManager:
def get_configs( def get_configs(
self, node_id: int = _default_node, config_type: str = _default_type self, node_id: int = _default_node, config_type: str = _default_type
) -> Dict[str, str]: ) -> Optional[Dict[str, str]]:
""" """
Retrieve configurations for a node and configuration type. Retrieve configurations for a node and configuration type.

View file

@ -145,14 +145,17 @@ class EmaneManager(ModelManager):
key += iface.node_id key += iface.node_id
# try retrieve interface specific configuration, avoid getting defaults # try retrieve interface specific configuration, avoid getting defaults
config = self.get_configs(node_id=key, config_type=model_name) config = self.get_configs(node_id=key, config_type=model_name)
# otherwise retrieve the interfaces node configuration, avoid using defaults # attempt to retrieve node specific conifg, when iface config is not present
if not config: if not config:
config = self.get_configs(node_id=iface.node.id, config_type=model_name) config = self.get_configs(node_id=iface.node.id, config_type=model_name)
# get non interface config, when none found # attempt to get emane net specific config, when node config is not present
if not config: if not config:
# with EMANE 0.9.2+, we need an extra NEM XML from # with EMANE 0.9.2+, we need an extra NEM XML from
# model.buildnemxmlfiles(), so defaults are returned here # model.buildnemxmlfiles(), so defaults are returned here
config = self.get_configs(node_id=emane_net.id, config_type=model_name) config = self.get_configs(node_id=emane_net.id, config_type=model_name)
# return default config values, when a config is not present
if not config:
config = emane_net.model.default_values()
return config return config
def config_reset(self, node_id: int = None) -> None: def config_reset(self, node_id: int = None) -> None:

View file

@ -545,7 +545,12 @@ class Session:
# ensure default emane configuration # ensure default emane configuration
if isinstance(node, EmaneNet) and options.emane: if isinstance(node, EmaneNet) and options.emane:
self.emane.set_model_config(_id, options.emane) model = self.emane.models.get(options.emane)
if not model:
raise CoreError(
f"node({node.name}) emane model({options.emane}) does not exist"
)
node.setmodel(model, {})
if self.state == EventTypes.RUNTIME_STATE: if self.state == EventTypes.RUNTIME_STATE:
self.emane.add_node(node) self.emane.add_node(node)
# set default wlan config if needed # set default wlan config if needed

View file

@ -937,8 +937,6 @@ class CoreClient:
def get_emane_model_configs_proto(self) -> List[emane_pb2.EmaneModelConfig]: def get_emane_model_configs_proto(self) -> List[emane_pb2.EmaneModelConfig]:
configs = [] configs = []
for node in self.session.nodes.values(): for node in self.session.nodes.values():
if node.type != NodeType.EMANE:
continue
for key, config in node.emane_model_configs.items(): for key, config in node.emane_model_configs.items():
model, iface_id = key model, iface_id = key
config = ConfigOption.to_dict(config) config = ConfigOption.to_dict(config)

View file

@ -282,9 +282,7 @@ class NodeConfigDialog(Dialog):
button.grid(row=0, column=1, sticky=tk.EW) button.grid(row=0, column=1, sticky=tk.EW)
def click_emane_config(self, emane_model: str, iface_id: int) -> None: def click_emane_config(self, emane_model: str, iface_id: int) -> None:
dialog = EmaneModelDialog( dialog = EmaneModelDialog(self, self.app, self.node, emane_model, iface_id)
self, self.app, self.canvas_node, emane_model, iface_id
)
dialog.show() dialog.show()
def click_icon(self) -> None: def click_icon(self) -> None:

View file

@ -91,10 +91,14 @@ def create_emane_config(session: "Session") -> etree.Element:
def create_emane_model_config( def create_emane_model_config(
node_id: int, model: "EmaneModelType", config: Dict[str, str] node_id: int,
model: "EmaneModelType",
config: Dict[str, str],
iface_id: Optional[int],
) -> etree.Element: ) -> etree.Element:
emane_element = etree.Element("emane_configuration") emane_element = etree.Element("emane_configuration")
add_attribute(emane_element, "node", node_id) add_attribute(emane_element, "node", node_id)
add_attribute(emane_element, "iface", iface_id)
add_attribute(emane_element, "model", model.name) add_attribute(emane_element, "model", model.name)
mac_element = etree.SubElement(emane_element, "mac") mac_element = etree.SubElement(emane_element, "mac")
@ -378,13 +382,19 @@ class CoreXmlWriter:
all_configs = self.session.emane.get_all_configs(node_id) all_configs = self.session.emane.get_all_configs(node_id)
if not all_configs: if not all_configs:
continue continue
iface_id = None
if node_id >= 1000:
iface_id = node_id % 1000
node_id = node_id // 1000
for model_name in all_configs: for model_name in all_configs:
config = all_configs[model_name] config = all_configs[model_name]
logging.debug( logging.debug(
"writing emane config node(%s) model(%s)", node_id, model_name "writing emane config node(%s) model(%s)", node_id, model_name
) )
model = self.session.emane.models[model_name] model = self.session.emane.models[model_name]
emane_configuration = create_emane_model_config(node_id, model, config) emane_configuration = create_emane_model_config(
node_id, model, config, iface_id
)
emane_configurations.append(emane_configuration) emane_configurations.append(emane_configuration)
if emane_configurations.getchildren(): if emane_configurations.getchildren():
self.scenario.append(emane_configurations) self.scenario.append(emane_configurations)
@ -588,9 +598,9 @@ class CoreXmlReader:
self.read_mobility_configs() self.read_mobility_configs()
self.read_emane_global_config() self.read_emane_global_config()
self.read_nodes() self.read_nodes()
self.read_links()
self.read_emane_configs() self.read_emane_configs()
self.read_configservice_configs() self.read_configservice_configs()
self.read_links()
def read_default_services(self) -> None: def read_default_services(self) -> None:
default_services = self.scenario.find("default_services") default_services = self.scenario.find("default_services")
@ -748,6 +758,7 @@ class CoreXmlReader:
for emane_configuration in emane_configurations.iterchildren(): for emane_configuration in emane_configurations.iterchildren():
node_id = get_int(emane_configuration, "node") node_id = get_int(emane_configuration, "node")
iface_id = get_int(emane_configuration, "iface")
model_name = emane_configuration.get("model") model_name = emane_configuration.get("model")
configs = {} configs = {}
@ -755,12 +766,13 @@ class CoreXmlReader:
node = self.session.nodes.get(node_id) node = self.session.nodes.get(node_id)
if not node: if not node:
raise CoreXmlError(f"node for emane config doesn't exist: {node_id}") raise CoreXmlError(f"node for emane config doesn't exist: {node_id}")
if not isinstance(node, EmaneNet):
raise CoreXmlError(f"invalid node for emane config: {node.name}")
model = self.session.emane.models.get(model_name) model = self.session.emane.models.get(model_name)
if not model: if not model:
raise CoreXmlError(f"invalid emane model: {model_name}") raise CoreXmlError(f"invalid emane model: {model_name}")
node.setmodel(model, {}) if iface_id is not None and iface_id not in node.ifaces:
raise CoreXmlError(
f"invalid interface id({iface_id}) for node({node.name})"
)
# read and set emane model configuration # read and set emane model configuration
mac_configuration = emane_configuration.find("mac") mac_configuration = emane_configuration.find("mac")
@ -784,7 +796,10 @@ class CoreXmlReader:
logging.info( logging.info(
"reading emane configuration node(%s) model(%s)", node_id, model_name "reading emane configuration node(%s) model(%s)", node_id, model_name
) )
self.session.emane.set_model_config(node_id, model_name, configs) key = node_id
if iface_id is not None:
key = node_id * 1000 + iface_id
self.session.emane.set_model_config(key, model_name, configs)
def read_mobility_configs(self) -> None: def read_mobility_configs(self) -> None:
mobility_configurations = self.scenario.find("mobility_configurations") mobility_configurations = self.scenario.find("mobility_configurations")
@ -869,6 +884,9 @@ class CoreXmlReader:
icon = network_element.get("icon") icon = network_element.get("icon")
server = network_element.get("server") server = network_element.get("server")
options = NodeOptions(name=name, icon=icon, server=server) options = NodeOptions(name=name, icon=icon, server=server)
if node_type == NodeTypes.EMANE:
model = network_element.get("model")
options.emane = model
position_element = network_element.find("position") position_element = network_element.find("position")
if position_element is not None: if position_element is not None: