Merge pull request #584 from coreemu/enhancement/grpc-node-configs

Enhancement/grpc node configs
This commit is contained in:
bharnden 2021-07-14 13:35:26 -07:00 committed by GitHub
commit eb1a9e2fe4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 279 additions and 291 deletions

View file

@ -11,16 +11,7 @@ from typing import Any, Callable, Dict, Generator, Iterable, List, Optional, Tup
import grpc
from core.api.grpc import (
configservices_pb2,
core_pb2,
core_pb2_grpc,
emane_pb2,
mobility_pb2,
services_pb2,
wlan_pb2,
wrappers,
)
from core.api.grpc import core_pb2, core_pb2_grpc, emane_pb2, wrappers
from core.api.grpc.configservices_pb2 import (
GetConfigServiceDefaultsRequest,
GetNodeConfigServiceRequest,
@ -233,95 +224,17 @@ class CoreGrpcClient:
self.proxy: bool = proxy
def start_session(
self,
session: wrappers.Session,
asymmetric_links: List[wrappers.Link] = None,
definition: bool = False,
self, session: wrappers.Session, definition: bool = False
) -> Tuple[bool, List[str]]:
"""
Start a session.
:param session: session to start
:param asymmetric_links: link configuration for asymmetric links
:param definition: True to only define session data, False to start session
:return: tuple of result and exception strings
"""
nodes = [x.to_proto() for x in session.nodes.values()]
links = [x.to_proto() for x in session.links]
if asymmetric_links:
asymmetric_links = [x.to_proto() for x in asymmetric_links]
hooks = [x.to_proto() for x in session.hooks.values()]
emane_model_configs = []
mobility_configs = []
wlan_configs = []
service_configs = []
service_file_configs = []
config_service_configs = []
for node in session.nodes.values():
for key, config in node.emane_model_configs.items():
model, iface_id = key
config = wrappers.ConfigOption.to_dict(config)
if iface_id is None:
iface_id = -1
emane_model_config = emane_pb2.EmaneModelConfig(
node_id=node.id, iface_id=iface_id, model=model, config=config
)
emane_model_configs.append(emane_model_config)
if node.wlan_config:
config = wrappers.ConfigOption.to_dict(node.wlan_config)
wlan_config = wlan_pb2.WlanConfig(node_id=node.id, config=config)
wlan_configs.append(wlan_config)
if node.mobility_config:
config = wrappers.ConfigOption.to_dict(node.mobility_config)
mobility_config = mobility_pb2.MobilityConfig(
node_id=node.id, config=config
)
mobility_configs.append(mobility_config)
for name, config in node.service_configs.items():
service_config = services_pb2.ServiceConfig(
node_id=node.id,
service=name,
directories=config.dirs,
files=config.configs,
startup=config.startup,
validate=config.validate,
shutdown=config.shutdown,
)
service_configs.append(service_config)
for service, file_configs in node.service_file_configs.items():
for file, data in file_configs.items():
service_file_config = services_pb2.ServiceFileConfig(
node_id=node.id, service=service, file=file, data=data
)
service_file_configs.append(service_file_config)
for name, service_config in node.config_service_configs.items():
config_service_config = configservices_pb2.ConfigServiceConfig(
node_id=node.id,
name=name,
templates=service_config.templates,
config=service_config.config,
)
config_service_configs.append(config_service_config)
options = {k: v.value for k, v in session.options.items()}
servers = [x.to_proto() for x in session.servers]
request = core_pb2.StartSessionRequest(
session_id=session.id,
nodes=nodes,
links=links,
location=session.location.to_proto(),
hooks=hooks,
emane_model_configs=emane_model_configs,
wlan_configs=wlan_configs,
mobility_configs=mobility_configs,
service_configs=service_configs,
service_file_configs=service_file_configs,
asymmetric_links=asymmetric_links,
config_service_configs=config_service_configs,
options=options,
user=session.user,
definition=definition,
metadata=session.metadata,
servers=servers,
session=session.to_proto(), definition=definition
)
response = self.stub.StartSession(request)
return response.result, list(response.exceptions)

View file

@ -8,9 +8,8 @@ from grpc import ServicerContext
from core import utils
from core.api.grpc import common_pb2, core_pb2, wrappers
from core.api.grpc.common_pb2 import MappedConfig
from core.api.grpc.configservices_pb2 import ConfigServiceConfig
from core.api.grpc.emane_pb2 import GetEmaneModelConfig
from core.api.grpc.emane_pb2 import NodeEmaneConfig
from core.api.grpc.services_pb2 import (
NodeServiceConfig,
NodeServiceData,
@ -252,12 +251,15 @@ def get_config_options(
return results
def get_node_proto(session: Session, node: NodeBase) -> core_pb2.Node:
def get_node_proto(
session: Session, node: NodeBase, emane_configs: List[NodeEmaneConfig]
) -> core_pb2.Node:
"""
Convert CORE node to protobuf representation.
:param session: session containing node
:param node: node to convert
:param emane_configs: emane configs related to node
:return: node proto
"""
node_type = session.get_node_type(node.__class__)
@ -283,6 +285,42 @@ def get_node_proto(session: Session, node: NodeBase) -> core_pb2.Node:
image = None
if isinstance(node, (DockerNode, LxcNode)):
image = node.image
# check for wlan config
wlan_config = session.mobility.get_configs(
node.id, config_type=BasicRangeModel.name
)
if wlan_config:
wlan_config = get_config_options(wlan_config, BasicRangeModel)
# check for mobility config
mobility_config = session.mobility.get_configs(
node.id, config_type=Ns2ScriptedMobility.name
)
if mobility_config:
mobility_config = get_config_options(mobility_config, Ns2ScriptedMobility)
# check for service configs
custom_services = session.services.custom_services.get(node.id)
service_configs = {}
if custom_services:
for service in custom_services.values():
service_proto = get_service_configuration(service)
service_configs[service.name] = NodeServiceConfig(
node_id=node.id,
service=service.name,
data=service_proto,
files=service.config_data,
)
# check for config service configs
config_service_configs = {}
if isinstance(node, CoreNode):
for service in node.config_services.values():
if not service.custom_templates and not service.custom_config:
continue
config_service_configs[service.name] = ConfigServiceConfig(
node_id=node.id,
name=service.name,
templates=service.custom_templates,
config=service.custom_config,
)
return core_pb2.Node(
id=node.id,
name=node.name,
@ -298,6 +336,11 @@ def get_node_proto(session: Session, node: NodeBase) -> core_pb2.Node:
dir=node_dir,
channel=channel,
canvas=node.canvas,
wlan_config=wlan_config,
mobility_config=mobility_config,
service_configs=service_configs,
config_service_configs=config_service_configs,
emane_configs=emane_configs,
)
@ -530,8 +573,8 @@ def get_nem_id(
return nem_id
def get_emane_model_configs(session: Session) -> List[GetEmaneModelConfig]:
configs = []
def get_emane_model_configs_dict(session: Session) -> Dict[int, List[NodeEmaneConfig]]:
configs = {}
for _id, model_configs in session.emane.node_configs.items():
for model_name in model_configs:
model_class = session.emane.get_model(model_name)
@ -539,42 +582,11 @@ def get_emane_model_configs(session: Session) -> List[GetEmaneModelConfig]:
config = get_config_options(current_config, model_class)
node_id, iface_id = utils.parse_iface_config_id(_id)
iface_id = iface_id if iface_id is not None else -1
model_config = GetEmaneModelConfig(
node_id=node_id, model=model_name, iface_id=iface_id, config=config
node_config = NodeEmaneConfig(
model=model_name, iface_id=iface_id, config=config
)
configs.append(model_config)
return configs
def get_wlan_configs(session: Session) -> Dict[int, MappedConfig]:
configs = {}
for node_id in session.mobility.node_configurations:
model_config = session.mobility.node_configurations[node_id]
if node_id == -1:
continue
for model_name in model_config:
if model_name != BasicRangeModel.name:
continue
current_config = session.mobility.get_model_config(node_id, model_name)
config = get_config_options(current_config, BasicRangeModel)
mapped_config = MappedConfig(config=config)
configs[node_id] = mapped_config
return configs
def get_mobility_configs(session: Session) -> Dict[int, MappedConfig]:
configs = {}
for node_id in session.mobility.node_configurations:
model_config = session.mobility.node_configurations[node_id]
if node_id == -1:
continue
for model_name in model_config:
if model_name != Ns2ScriptedMobility.name:
continue
current_config = session.mobility.get_model_config(node_id, model_name)
config = get_config_options(current_config, Ns2ScriptedMobility)
mapped_config = MappedConfig(config=config)
configs[node_id] = mapped_config
node_configs = configs.setdefault(node_id, [])
node_configs.append(node_config)
return configs
@ -596,40 +608,6 @@ def get_default_services(session: Session) -> List[ServiceDefaults]:
return default_services
def get_node_service_configs(session: Session) -> List[NodeServiceConfig]:
configs = []
for node_id, service_configs in session.services.custom_services.items():
for name in service_configs:
service = session.services.get_service(node_id, name)
service_proto = get_service_configuration(service)
config = NodeServiceConfig(
node_id=node_id,
service=name,
data=service_proto,
files=service.config_data,
)
configs.append(config)
return configs
def get_node_config_service_configs(session: Session) -> List[ConfigServiceConfig]:
configs = []
for node in session.nodes.values():
if not isinstance(node, CoreNodeBase):
continue
for name, service in node.config_services.items():
if not service.custom_templates and not service.custom_config:
continue
config_proto = ConfigServiceConfig(
node_id=node.id,
name=name,
templates=service.custom_templates,
config=service.custom_config,
)
configs.append(config_proto)
return configs
def get_mobility_node(
session: Session, node_id: int, context: ServicerContext
) -> Union[WlanNode, EmaneNet]:
@ -645,10 +623,12 @@ def get_mobility_node(
def convert_session(session: Session) -> wrappers.Session:
links = []
nodes = []
emane_configs = get_emane_model_configs_dict(session)
for _id in session.nodes:
node = session.nodes[_id]
if not isinstance(node, (PtpNet, CtrlNet)):
node_proto = get_node_proto(session, node)
node_emane_configs = emane_configs.get(node.id, [])
node_proto = get_node_proto(session, node, node_emane_configs)
nodes.append(node_proto)
node_links = get_links(node)
links.extend(node_links)
@ -659,11 +639,6 @@ def convert_session(session: Session) -> wrappers.Session:
x=x, y=y, z=z, lat=lat, lon=lon, alt=alt, scale=session.location.refscale
)
hooks = get_hooks(session)
emane_model_configs = get_emane_model_configs(session)
wlan_configs = get_wlan_configs(session)
mobility_configs = get_mobility_configs(session)
service_configs = get_node_service_configs(session)
config_service_configs = get_node_config_service_configs(session)
session_file = str(session.file_path) if session.file_path else None
options = get_config_options(session.options.get_configs(), session.options)
servers = [
@ -680,13 +655,51 @@ def convert_session(session: Session) -> wrappers.Session:
default_services=default_services,
location=location,
hooks=hooks,
emane_model_configs=emane_model_configs,
wlan_configs=wlan_configs,
service_configs=service_configs,
config_service_configs=config_service_configs,
mobility_configs=mobility_configs,
metadata=session.metadata,
file=session_file,
options=options,
servers=servers,
)
def configure_node(
session: Session, node: core_pb2.Node, core_node: NodeBase, context: ServicerContext
) -> None:
for emane_config in node.emane_configs:
_id = utils.iface_config_id(node.id, emane_config.iface_id)
config = {k: v.value for k, v in emane_config.config.items()}
session.emane.set_config(_id, emane_config.model, config)
if node.wlan_config:
config = {k: v.value for k, v in node.wlan_config.items()}
session.mobility.set_model_config(node.id, BasicRangeModel.name, config)
if node.mobility_config:
config = {k: v.value for k, v in node.mobility_config.items()}
session.mobility.set_model_config(node.id, Ns2ScriptedMobility.name, config)
for service_name, service_config in node.service_configs.items():
data = service_config.data
config = ServiceConfig(
node_id=node.id,
service=service_name,
startup=data.startup,
validate=data.validate,
shutdown=data.shutdown,
files=data.configs,
directories=data.dirs,
)
service_configuration(session, config)
for file_name, file_data in service_config.files.items():
session.services.set_service_file(
node.id, service_name, file_name, file_data
)
if node.config_service_configs:
if not isinstance(core_node, CoreNode):
context.abort(
grpc.StatusCode.INVALID_ARGUMENT,
"invalid node type with config service configs",
)
for service_name, service_config in node.config_service_configs.items():
service = core_node.config_services[service_name]
if service_config.config:
service.set_config(service_config.config)
for name, template in service_config.templates.items():
service.set_template(name, template)

View file

@ -224,7 +224,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
:return: start session response
"""
logger.debug("start session: %s", request)
session = self.get_session(request.session_id, context)
session = self.get_session(request.session.id, context)
# clear previous state and setup for creation
session.clear()
@ -234,77 +234,51 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
state = EventTypes.CONFIGURATION_STATE
session.directory.mkdir(exist_ok=True)
session.set_state(state)
session.user = request.user
session.user = request.session.user
# session options
session.options.config_reset()
for key, value in request.options.items():
session.options.set_config(key, value)
session.metadata = dict(request.metadata)
for option in request.session.options.values():
session.options.set_config(option.name, option.value)
session.metadata = dict(request.session.metadata)
# add servers
for server in request.servers:
for server in request.session.servers:
session.distributed.add_server(server.name, server.host)
# location
if request.HasField("location"):
grpcutils.session_location(session, request.location)
if request.session.HasField("location"):
grpcutils.session_location(session, request.session.location)
# add all hooks
for hook in request.hooks:
for hook in request.session.hooks:
state = EventTypes(hook.state)
session.add_hook(state, hook.file, hook.data)
# create nodes
_, exceptions = grpcutils.create_nodes(session, request.nodes)
_, exceptions = grpcutils.create_nodes(session, request.session.nodes)
if exceptions:
exceptions = [str(x) for x in exceptions]
return core_pb2.StartSessionResponse(result=False, exceptions=exceptions)
# emane configs
for config in request.emane_model_configs:
_id = utils.iface_config_id(config.node_id, config.iface_id)
session.emane.set_config(_id, config.model, config.config)
# wlan configs
for config in request.wlan_configs:
session.mobility.set_model_config(
config.node_id, BasicRangeModel.name, config.config
)
# mobility configs
for config in request.mobility_configs:
session.mobility.set_model_config(
config.node_id, Ns2ScriptedMobility.name, config.config
)
# service configs
for config in request.service_configs:
grpcutils.service_configuration(session, config)
# service file configs
for config in request.service_file_configs:
session.services.set_service_file(
config.node_id, config.service, config.file, config.data
)
# config service configs
for config in request.config_service_configs:
node = self.get_node(session, config.node_id, context, CoreNode)
service = node.config_services[config.name]
if config.config:
service.set_config(config.config)
for name, template in config.templates.items():
service.set_template(name, template)
# check for configurations
for node in request.session.nodes:
core_node = self.get_node(session, node.id, context, NodeBase)
grpcutils.configure_node(session, node, core_node, context)
# create links
_, exceptions = grpcutils.create_links(session, request.links)
links = []
asym_links = []
for link in request.session.links:
if link.options.unidirectional:
asym_links.append(link)
else:
links.append(link)
_, exceptions = grpcutils.create_links(session, links)
if exceptions:
exceptions = [str(x) for x in exceptions]
return core_pb2.StartSessionResponse(result=False, exceptions=exceptions)
# asymmetric links
_, exceptions = grpcutils.edit_links(session, request.asymmetric_links)
_, exceptions = grpcutils.edit_links(session, asym_links)
if exceptions:
exceptions = [str(x) for x in exceptions]
return core_pb2.StartSessionResponse(result=False, exceptions=exceptions)
@ -541,6 +515,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
_type, _id, options = grpcutils.add_node_data(request.node)
_class = session.get_node_class(_type)
node = session.add_node(_class, _id, options)
grpcutils.configure_node(session, request.node, node, context)
source = request.source if request.source else None
session.broadcast_node(node, MessageFlags.ADD, source)
return core_pb2.AddNodeResponse(node_id=node.id)
@ -563,7 +538,9 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
iface = node.ifaces[iface_id]
iface_proto = grpcutils.iface_to_proto(request.node_id, iface)
ifaces.append(iface_proto)
node_proto = grpcutils.get_node_proto(session, node)
emane_configs = grpcutils.get_emane_model_configs_dict(session)
node_emane_configs = emane_configs.get(node.id, [])
node_proto = grpcutils.get_node_proto(session, node, node_emane_configs)
links = get_links(node)
return core_pb2.GetNodeResponse(node=node_proto, ifaces=ifaces, links=links)

View file

@ -239,12 +239,26 @@ class NodeServiceData:
configs=proto.configs,
startup=proto.startup,
validate=proto.validate,
validation_mode=proto.validation_mode,
validation_mode=ServiceValidationMode(proto.validation_mode),
validation_timer=proto.validation_timer,
shutdown=proto.shutdown,
meta=proto.meta,
)
def to_proto(self) -> services_pb2.NodeServiceData:
return services_pb2.NodeServiceData(
executables=self.executables,
dependencies=self.dependencies,
dirs=self.dirs,
configs=self.configs,
startup=self.startup,
validate=self.validate,
validation_mode=self.validation_mode.value,
validation_timer=self.validation_timer,
shutdown=self.shutdown,
meta=self.meta,
)
@dataclass
class NodeServiceConfig:
@ -430,15 +444,27 @@ class ConfigOption:
@classmethod
def from_proto(cls, proto: common_pb2.ConfigOption) -> "ConfigOption":
config_type = ConfigOptionType(proto.type) if proto.type is not None else None
return ConfigOption(
label=proto.label,
name=proto.name,
value=proto.value,
type=ConfigOptionType(proto.type),
type=config_type,
group=proto.group,
select=proto.select,
)
def to_proto(self) -> common_pb2.ConfigOption:
config_type = self.type.value if self.type is not None else None
return common_pb2.ConfigOption(
label=self.label,
name=self.name,
value=self.value,
type=config_type,
select=self.select,
group=self.group,
)
@dataclass
class Interface:
@ -714,6 +740,23 @@ class Node:
@classmethod
def from_proto(cls, proto: core_pb2.Node) -> "Node":
service_configs = {}
service_file_configs = {}
for service, node_config in proto.service_configs.items():
service_configs[service] = NodeServiceData.from_proto(node_config.data)
service_file_configs[service] = dict(node_config.files)
emane_configs = {}
for emane_config in proto.emane_configs:
iface_id = None if emane_config.iface_id == -1 else emane_config.iface_id
model = emane_config.model
key = (model, iface_id)
emane_configs[key] = ConfigOption.from_dict(emane_config.config)
config_service_configs = {}
for service, service_config in proto.config_service_configs.items():
config_service_configs[service] = ConfigServiceData(
templates=dict(service_config.templates),
config=dict(service_config.config),
)
return Node(
id=proto.id,
name=proto.name,
@ -730,9 +773,43 @@ class Node:
dir=proto.dir,
channel=proto.channel,
canvas=proto.canvas,
wlan_config=ConfigOption.from_dict(proto.wlan_config),
mobility_config=ConfigOption.from_dict(proto.mobility_config),
service_configs=service_configs,
service_file_configs=service_file_configs,
config_service_configs=config_service_configs,
emane_model_configs=emane_configs,
)
def to_proto(self) -> core_pb2.Node:
emane_configs = []
for key, config in self.emane_model_configs.items():
model, iface_id = key
if iface_id is None:
iface_id = -1
config = {k: v.to_proto() for k, v in config.items()}
emane_config = emane_pb2.NodeEmaneConfig(
iface_id=iface_id, model=model, config=config
)
emane_configs.append(emane_config)
service_configs = {}
for service, service_data in self.service_configs.items():
service_configs[service] = services_pb2.NodeServiceConfig(
service=service, data=service_data.to_proto()
)
for service, file_configs in self.service_file_configs.items():
service_config = service_configs.get(service)
if service_config:
service_config.files.update(file_configs)
else:
service_configs[service] = services_pb2.NodeServiceConfig(
service=service, files=file_configs
)
config_service_configs = {}
for service, service_config in self.config_service_configs.items():
config_service_configs[service] = configservices_pb2.ConfigServiceConfig(
templates=service_config.templates, config=service_config.config
)
return core_pb2.Node(
id=self.id,
name=self.name,
@ -748,6 +825,11 @@ class Node:
dir=self.dir,
channel=self.channel,
canvas=self.canvas,
wlan_config={k: v.to_proto() for k, v in self.wlan_config.items()},
mobility_config={k: v.to_proto() for k, v in self.mobility_config.items()},
service_configs=service_configs,
config_service_configs=config_service_configs,
emane_configs=emane_configs,
)
def set_wlan(self, config: Dict[str, str]) -> None:
@ -796,32 +878,6 @@ class Session:
x.node_type: set(x.services) for x in proto.default_services
}
hooks = {x.file: Hook.from_proto(x) for x in proto.hooks}
# update nodes with their current configurations
for model in proto.emane_model_configs:
iface_id = None
if model.iface_id != -1:
iface_id = model.iface_id
node = nodes[model.node_id]
key = (model.model, iface_id)
node.emane_model_configs[key] = ConfigOption.from_dict(model.config)
for node_id, mapped_config in proto.wlan_configs.items():
node = nodes[node_id]
node.wlan_config = ConfigOption.from_dict(mapped_config.config)
for config in proto.service_configs:
service = config.service
node = nodes[config.node_id]
node.service_configs[service] = NodeServiceData.from_proto(config.data)
for file, data in config.files.items():
files = node.service_file_configs.setdefault(service, {})
files[file] = data
for config in proto.config_service_configs:
node = nodes[config.node_id]
node.config_service_configs[config.name] = ConfigServiceData(
templates=dict(config.templates), config=dict(config.config)
)
for node_id, mapped_config in proto.mobility_configs.items():
node = nodes[node_id]
node.mobility_config = ConfigOption.from_dict(mapped_config.config)
file_path = Path(proto.file) if proto.file else None
options = ConfigOption.from_dict(proto.options)
servers = [Server.from_proto(x) for x in proto.servers]
@ -841,6 +897,35 @@ class Session:
servers=servers,
)
def to_proto(self) -> core_pb2.Session:
nodes = [x.to_proto() for x in self.nodes.values()]
links = [x.to_proto() for x in self.links]
hooks = [x.to_proto() for x in self.hooks.values()]
options = {k: v.to_proto() for k, v in self.options.items()}
servers = [x.to_proto() for x in self.servers]
default_services = []
for node_type, services in self.default_services.items():
default_service = services_pb2.ServiceDefaults(
node_type=node_type, services=services
)
default_services.append(default_service)
file = str(self.file) if self.file else None
return core_pb2.Session(
id=self.id,
state=self.state.value,
nodes=nodes,
links=links,
dir=self.dir,
user=self.user,
default_services=default_services,
location=self.location.to_proto(),
hooks=hooks,
metadata=self.metadata,
file=file,
options=options,
servers=servers,
)
def add_node(
self,
_id: int,

View file

@ -439,7 +439,7 @@ class CoreClient:
def get_links(self, definition: bool = False) -> Tuple[List[Link], List[Link]]:
if not definition:
self.ifaces_manager.set_macs([x.link for x in self.links.values()])
links, asym_links = [], []
links = []
for edge in self.links.values():
link = edge.link
if not definition:
@ -449,12 +449,11 @@ class CoreClient:
link.iface2.mac = self.ifaces_manager.next_mac()
links.append(link)
if edge.asymmetric_link:
asym_links.append(edge.asymmetric_link)
return links, asym_links
links.append(edge.asymmetric_link)
return links
def start_session(self, definition: bool = False) -> Tuple[bool, List[str]]:
links, asym_links = self.get_links(definition)
self.session.links = links
self.session.links = self.get_links(definition)
self.session.metadata = self.get_metadata()
self.session.servers.clear()
for server in self.servers.values():
@ -462,9 +461,7 @@ class CoreClient:
result = False
exceptions = []
try:
result, exceptions = self.client.start_session(
self.session, asym_links, definition
)
result, exceptions = self.client.start_session(self.session, definition)
logger.info(
"start session(%s) definition(%s), result: %s",
self.session.id,

View file

@ -310,9 +310,9 @@ class ConfigServiceConfigDialog(Dialog):
current_listbox.itemconfig(current_listbox.curselection()[0], bg="")
self.destroy()
return
service_config = self.node.config_service_configs.get(self.service_name)
if not service_config:
service_config = ConfigServiceData()
service_config = self.node.config_service_configs.setdefault(
self.service_name, ConfigServiceData()
)
if self.config_frame:
self.config_frame.parse_config()
service_config.config = {x.name: x.value for x in self.config.values()}

View file

@ -31,6 +31,7 @@ class NodeConfigServiceDialog(Dialog):
if services is None:
services = set(node.config_services)
self.current_services: Set[str] = services
self.protocol("WM_DELETE_WINDOW", self.click_cancel)
self.draw()
def draw(self) -> None:
@ -102,6 +103,7 @@ class NodeConfigServiceDialog(Dialog):
self.current_services.add(name)
elif not var.get() and name in self.current_services:
self.current_services.remove(name)
self.node.config_service_configs.pop(name, None)
self.draw_current_services()
self.node.config_services = self.current_services.copy()
@ -146,6 +148,7 @@ class NodeConfigServiceDialog(Dialog):
service = self.current.listbox.get(cur[0])
self.current.listbox.delete(cur[0])
self.current_services.remove(service)
self.node.config_service_configs.pop(service, None)
for checkbutton in self.services.frame.winfo_children():
if checkbutton["text"] == service:
checkbutton.invoke()

View file

@ -25,6 +25,7 @@ class NodeServiceDialog(Dialog):
self.current: Optional[ListboxScroll] = None
services = set(node.services)
self.current_services: Set[str] = services
self.protocol("WM_DELETE_WINDOW", self.click_cancel)
self.draw()
def draw(self) -> None:
@ -77,7 +78,7 @@ class NodeServiceDialog(Dialog):
button.grid(row=0, column=1, sticky=tk.EW, padx=PADX)
button = ttk.Button(frame, text="Remove", command=self.click_remove)
button.grid(row=0, column=2, sticky=tk.EW, padx=PADX)
button = ttk.Button(frame, text="Cancel", command=self.destroy)
button = ttk.Button(frame, text="Cancel", command=self.click_cancel)
button.grid(row=0, column=3, sticky=tk.EW)
# trigger group change
@ -98,6 +99,8 @@ class NodeServiceDialog(Dialog):
self.current_services.add(name)
elif not var.get() and name in self.current_services:
self.current_services.remove(name)
self.node.service_configs.pop(name, None)
self.node.service_file_configs.pop(name, None)
self.current.listbox.delete(0, tk.END)
for name in sorted(self.current_services):
self.current.listbox.insert(tk.END, name)
@ -125,6 +128,9 @@ class NodeServiceDialog(Dialog):
"Service Configuration", "Select a service to configure", parent=self
)
def click_cancel(self) -> None:
self.destroy()
def click_save(self) -> None:
self.node.services = self.current_services.copy()
self.destroy()
@ -135,6 +141,8 @@ class NodeServiceDialog(Dialog):
service = self.current.listbox.get(cur[0])
self.current.listbox.delete(cur[0])
self.current_services.remove(service)
self.node.service_configs.pop(service, None)
self.node.service_file_configs.pop(service, None)
for checkbutton in self.services.frame.winfo_children():
if checkbutton["text"] == service:
checkbutton.invoke()

View file

@ -655,6 +655,7 @@ class CoreNode(CoreNodeBase):
:param dir_path: path to create
:return: nothing
"""
logger.info("creating private directory: %s", dir_path)
if not str(dir_path).startswith("/"):
raise CoreError(f"private directory path not fully qualified: {dir_path}")
host_path = self.host_path(dir_path, is_dir=True)

View file

@ -135,23 +135,8 @@ message GetConfigResponse {
message StartSessionRequest {
int32 session_id = 1;
repeated Node nodes = 2;
repeated Link links = 3;
repeated Hook hooks = 4;
SessionLocation location = 5;
repeated wlan.WlanConfig wlan_configs = 6;
repeated emane.EmaneModelConfig emane_model_configs = 7;
repeated mobility.MobilityConfig mobility_configs = 8;
repeated services.ServiceConfig service_configs = 9;
repeated services.ServiceFileConfig service_file_configs = 10;
repeated Link asymmetric_links = 11;
repeated configservices.ConfigServiceConfig config_service_configs = 12;
map<string, string> options = 13;
string user = 14;
bool definition = 15;
map<string, string> metadata = 16;
repeated Server servers = 17;
Session session = 1;
bool definition = 2;
}
message StartSessionResponse {
@ -577,15 +562,10 @@ message Session {
repeated services.ServiceDefaults default_services = 7;
SessionLocation location = 8;
repeated Hook hooks = 9;
repeated emane.GetEmaneModelConfig emane_model_configs = 10;
map<int32, common.MappedConfig> wlan_configs = 11;
repeated services.NodeServiceConfig service_configs = 12;
repeated configservices.ConfigServiceConfig config_service_configs = 13;
map<int32, common.MappedConfig> mobility_configs = 14;
map<string, string> metadata = 15;
string file = 16;
map<string, common.ConfigOption> options = 17;
repeated Server servers = 18;
map<string, string> metadata = 10;
string file = 11;
map<string, common.ConfigOption> options = 12;
repeated Server servers = 13;
}
message SessionSummary {
@ -612,6 +592,11 @@ message Node {
string dir = 13;
string channel = 14;
int32 canvas = 15;
map<string, common.ConfigOption> wlan_config = 16;
map<string, common.ConfigOption> mobility_config = 17;
map<string, services.NodeServiceConfig> service_configs = 18;
map<string, configservices.ConfigServiceConfig> config_service_configs= 19;
repeated emane.NodeEmaneConfig emane_configs = 20;
}
message Link {

View file

@ -31,6 +31,12 @@ message GetEmaneModelConfig {
map<string, common.ConfigOption> config = 4;
}
message NodeEmaneConfig {
int32 iface_id = 1;
string model = 2;
map<string, common.ConfigOption> config = 3;
}
message GetEmaneEventChannelRequest {
int32 session_id = 1;
int32 nem_id = 2;