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)
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)
current_config = session.emane.get_model_config(_id, request.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
) -> 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 context: context object

View file

@ -3,7 +3,13 @@ from enum import Enum
from pathlib import Path
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):
@ -87,6 +93,13 @@ class MessageType(Enum):
TTY = 64
class ServiceAction(Enum):
START = 0
STOP = 1
RESTART = 2
VALIDATE = 3
@dataclass
class ConfigService:
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
class ConfigServiceData:
templates: 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
class NodeServiceData:
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
class BridgeThroughput:
node_id: int
@ -471,6 +561,30 @@ class Hook:
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
class Position:
x: float
@ -660,3 +774,18 @@ class NodeEvent:
message_type=MessageType(proto.message_type),
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(
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.

View file

@ -145,14 +145,17 @@ class EmaneManager(ModelManager):
key += iface.node_id
# try retrieve interface specific configuration, avoid getting defaults
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:
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:
# with EMANE 0.9.2+, we need an extra NEM XML from
# model.buildnemxmlfiles(), so defaults are returned here
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
def config_reset(self, node_id: int = None) -> None:

View file

@ -545,7 +545,12 @@ class Session:
# ensure default emane configuration
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:
self.emane.add_node(node)
# 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]:
configs = []
for node in self.session.nodes.values():
if node.type != NodeType.EMANE:
continue
for key, config in node.emane_model_configs.items():
model, iface_id = key
config = ConfigOption.to_dict(config)

View file

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

View file

@ -91,10 +91,14 @@ def create_emane_config(session: "Session") -> etree.Element:
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:
emane_element = etree.Element("emane_configuration")
add_attribute(emane_element, "node", node_id)
add_attribute(emane_element, "iface", iface_id)
add_attribute(emane_element, "model", model.name)
mac_element = etree.SubElement(emane_element, "mac")
@ -378,13 +382,19 @@ class CoreXmlWriter:
all_configs = self.session.emane.get_all_configs(node_id)
if not all_configs:
continue
iface_id = None
if node_id >= 1000:
iface_id = node_id % 1000
node_id = node_id // 1000
for model_name in all_configs:
config = all_configs[model_name]
logging.debug(
"writing emane config node(%s) model(%s)", node_id, 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)
if emane_configurations.getchildren():
self.scenario.append(emane_configurations)
@ -588,9 +598,9 @@ class CoreXmlReader:
self.read_mobility_configs()
self.read_emane_global_config()
self.read_nodes()
self.read_links()
self.read_emane_configs()
self.read_configservice_configs()
self.read_links()
def read_default_services(self) -> None:
default_services = self.scenario.find("default_services")
@ -748,6 +758,7 @@ class CoreXmlReader:
for emane_configuration in emane_configurations.iterchildren():
node_id = get_int(emane_configuration, "node")
iface_id = get_int(emane_configuration, "iface")
model_name = emane_configuration.get("model")
configs = {}
@ -755,12 +766,13 @@ class CoreXmlReader:
node = self.session.nodes.get(node_id)
if not node:
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)
if not model:
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
mac_configuration = emane_configuration.find("mac")
@ -784,7 +796,10 @@ class CoreXmlReader:
logging.info(
"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:
mobility_configurations = self.scenario.find("mobility_configurations")
@ -869,6 +884,9 @@ class CoreXmlReader:
icon = network_element.get("icon")
server = network_element.get("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")
if position_element is not None: