Merge pull request #584 from coreemu/enhancement/grpc-node-configs
Enhancement/grpc node configs
This commit is contained in:
commit
eb1a9e2fe4
11 changed files with 279 additions and 291 deletions
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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()}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue