Merge pull request #502 from coreemu/cleanup/pygui-session-wrapper
Cleanup/pygui session wrapper
This commit is contained in:
commit
787f02f024
28 changed files with 687 additions and 683 deletions
|
|
@ -8,13 +8,22 @@ from grpc import ServicerContext
|
||||||
|
|
||||||
from core import utils
|
from core import utils
|
||||||
from core.api.grpc import common_pb2, core_pb2
|
from core.api.grpc import common_pb2, core_pb2
|
||||||
from core.api.grpc.services_pb2 import NodeServiceData, ServiceConfig
|
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.services_pb2 import (
|
||||||
|
NodeServiceConfig,
|
||||||
|
NodeServiceData,
|
||||||
|
ServiceConfig,
|
||||||
|
ServiceDefaults,
|
||||||
|
)
|
||||||
from core.config import ConfigurableOptions
|
from core.config import ConfigurableOptions
|
||||||
from core.emane.nodes import EmaneNet
|
from core.emane.nodes import EmaneNet
|
||||||
from core.emulator.data import InterfaceData, LinkData, LinkOptions, NodeOptions
|
from core.emulator.data import InterfaceData, LinkData, LinkOptions, NodeOptions
|
||||||
from core.emulator.enumerations import LinkTypes, NodeTypes
|
from core.emulator.enumerations import LinkTypes, NodeTypes
|
||||||
from core.emulator.session import Session
|
from core.emulator.session import Session
|
||||||
from core.nodes.base import CoreNode, NodeBase
|
from core.location.mobility import BasicRangeModel, Ns2ScriptedMobility
|
||||||
|
from core.nodes.base import CoreNode, CoreNodeBase, NodeBase
|
||||||
from core.nodes.interface import CoreInterface
|
from core.nodes.interface import CoreInterface
|
||||||
from core.services.coreservices import CoreService
|
from core.services.coreservices import CoreService
|
||||||
|
|
||||||
|
|
@ -536,3 +545,119 @@ def get_nem_id(
|
||||||
message = f"{node.name} interface {iface_id} nem id does not exist"
|
message = f"{node.name} interface {iface_id} nem id does not exist"
|
||||||
context.abort(grpc.StatusCode.INVALID_ARGUMENT, message)
|
context.abort(grpc.StatusCode.INVALID_ARGUMENT, message)
|
||||||
return nem_id
|
return nem_id
|
||||||
|
|
||||||
|
|
||||||
|
def get_emane_model_configs(session: Session) -> List[GetEmaneModelConfig]:
|
||||||
|
configs = []
|
||||||
|
for _id in session.emane.node_configurations:
|
||||||
|
if _id == -1:
|
||||||
|
continue
|
||||||
|
model_configs = session.emane.node_configurations[_id]
|
||||||
|
for model_name in model_configs:
|
||||||
|
model = session.emane.models[model_name]
|
||||||
|
current_config = session.emane.get_model_config(_id, model_name)
|
||||||
|
config = get_config_options(current_config, model)
|
||||||
|
node_id, iface_id = parse_emane_model_id(_id)
|
||||||
|
model_config = GetEmaneModelConfig(
|
||||||
|
node_id=node_id, 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
|
||||||
|
return configs
|
||||||
|
|
||||||
|
|
||||||
|
def get_hooks(session: Session) -> List[core_pb2.Hook]:
|
||||||
|
hooks = []
|
||||||
|
for state in session.hooks:
|
||||||
|
state_hooks = session.hooks[state]
|
||||||
|
for file_name, file_data in state_hooks:
|
||||||
|
hook = core_pb2.Hook(state=state.value, file=file_name, data=file_data)
|
||||||
|
hooks.append(hook)
|
||||||
|
return hooks
|
||||||
|
|
||||||
|
|
||||||
|
def get_emane_models(session: Session) -> List[str]:
|
||||||
|
emane_models = []
|
||||||
|
for model in session.emane.models.keys():
|
||||||
|
if len(model.split("_")) != 2:
|
||||||
|
continue
|
||||||
|
emane_models.append(model)
|
||||||
|
return emane_models
|
||||||
|
|
||||||
|
|
||||||
|
def get_default_services(session: Session) -> List[ServiceDefaults]:
|
||||||
|
default_services = []
|
||||||
|
for name, services in session.services.default_services.items():
|
||||||
|
default_service = ServiceDefaults(node_type=name, services=services)
|
||||||
|
default_services.append(default_service)
|
||||||
|
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_emane_config(session: Session) -> Dict[str, common_pb2.ConfigOption]:
|
||||||
|
current_config = session.emane.get_configs()
|
||||||
|
return get_config_options(current_config, session.emane.emane_config)
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ from core.api.grpc import (
|
||||||
core_pb2_grpc,
|
core_pb2_grpc,
|
||||||
grpcutils,
|
grpcutils,
|
||||||
)
|
)
|
||||||
from core.api.grpc.common_pb2 import MappedConfig
|
|
||||||
from core.api.grpc.configservices_pb2 import (
|
from core.api.grpc.configservices_pb2 import (
|
||||||
ConfigService,
|
ConfigService,
|
||||||
GetConfigServiceDefaultsRequest,
|
GetConfigServiceDefaultsRequest,
|
||||||
|
|
@ -89,7 +88,6 @@ from core.api.grpc.services_pb2 import (
|
||||||
ServiceAction,
|
ServiceAction,
|
||||||
ServiceActionRequest,
|
ServiceActionRequest,
|
||||||
ServiceActionResponse,
|
ServiceActionResponse,
|
||||||
ServiceDefaults,
|
|
||||||
SetNodeServiceFileRequest,
|
SetNodeServiceFileRequest,
|
||||||
SetNodeServiceFileResponse,
|
SetNodeServiceFileResponse,
|
||||||
SetNodeServiceRequest,
|
SetNodeServiceRequest,
|
||||||
|
|
@ -118,7 +116,7 @@ from core.emulator.enumerations import (
|
||||||
from core.emulator.session import NT, Session
|
from core.emulator.session import NT, Session
|
||||||
from core.errors import CoreCommandError, CoreError
|
from core.errors import CoreCommandError, CoreError
|
||||||
from core.location.mobility import BasicRangeModel, Ns2ScriptedMobility
|
from core.location.mobility import BasicRangeModel, Ns2ScriptedMobility
|
||||||
from core.nodes.base import CoreNode, CoreNodeBase, NodeBase
|
from core.nodes.base import CoreNode, NodeBase
|
||||||
from core.nodes.network import PtpNet, WlanNode
|
from core.nodes.network import PtpNet, WlanNode
|
||||||
from core.services.coreservices import ServiceManager
|
from core.services.coreservices import ServiceManager
|
||||||
|
|
||||||
|
|
@ -558,7 +556,6 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
||||||
"""
|
"""
|
||||||
logging.debug("get session: %s", request)
|
logging.debug("get session: %s", request)
|
||||||
session = self.get_session(request.session_id, context)
|
session = self.get_session(request.session_id, context)
|
||||||
|
|
||||||
links = []
|
links = []
|
||||||
nodes = []
|
nodes = []
|
||||||
for _id in session.nodes:
|
for _id in session.nodes:
|
||||||
|
|
@ -568,9 +565,38 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
||||||
nodes.append(node_proto)
|
nodes.append(node_proto)
|
||||||
node_links = get_links(node)
|
node_links = get_links(node)
|
||||||
links.extend(node_links)
|
links.extend(node_links)
|
||||||
|
default_services = grpcutils.get_default_services(session)
|
||||||
|
x, y, z = session.location.refxyz
|
||||||
|
lat, lon, alt = session.location.refgeo
|
||||||
|
location = core_pb2.SessionLocation(
|
||||||
|
x=x, y=y, z=z, lat=lat, lon=lon, alt=alt, scale=session.location.refscale
|
||||||
|
)
|
||||||
|
hooks = grpcutils.get_hooks(session)
|
||||||
|
emane_models = grpcutils.get_emane_models(session)
|
||||||
|
emane_config = grpcutils.get_emane_config(session)
|
||||||
|
emane_model_configs = grpcutils.get_emane_model_configs(session)
|
||||||
|
wlan_configs = grpcutils.get_wlan_configs(session)
|
||||||
|
mobility_configs = grpcutils.get_mobility_configs(session)
|
||||||
|
service_configs = grpcutils.get_node_service_configs(session)
|
||||||
|
config_service_configs = grpcutils.get_node_config_service_configs(session)
|
||||||
session_proto = core_pb2.Session(
|
session_proto = core_pb2.Session(
|
||||||
state=session.state.value, nodes=nodes, links=links, dir=session.session_dir
|
id=session.id,
|
||||||
|
state=session.state.value,
|
||||||
|
nodes=nodes,
|
||||||
|
links=links,
|
||||||
|
dir=session.session_dir,
|
||||||
|
user=session.user,
|
||||||
|
default_services=default_services,
|
||||||
|
location=location,
|
||||||
|
hooks=hooks,
|
||||||
|
emane_models=emane_models,
|
||||||
|
emane_config=emane_config,
|
||||||
|
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,
|
||||||
)
|
)
|
||||||
return core_pb2.GetSessionResponse(session=session_proto)
|
return core_pb2.GetSessionResponse(session=session_proto)
|
||||||
|
|
||||||
|
|
@ -1012,12 +1038,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
||||||
"""
|
"""
|
||||||
logging.debug("get hooks: %s", request)
|
logging.debug("get hooks: %s", request)
|
||||||
session = self.get_session(request.session_id, context)
|
session = self.get_session(request.session_id, context)
|
||||||
hooks = []
|
hooks = grpcutils.get_hooks(session)
|
||||||
for state in session.hooks:
|
|
||||||
state_hooks = session.hooks[state]
|
|
||||||
for file_name, file_data in state_hooks:
|
|
||||||
hook = core_pb2.Hook(state=state.value, file=file_name, data=file_data)
|
|
||||||
hooks.append(hook)
|
|
||||||
return core_pb2.GetHooksResponse(hooks=hooks)
|
return core_pb2.GetHooksResponse(hooks=hooks)
|
||||||
|
|
||||||
def AddHook(
|
def AddHook(
|
||||||
|
|
@ -1050,19 +1071,8 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
||||||
"""
|
"""
|
||||||
logging.debug("get mobility configs: %s", request)
|
logging.debug("get mobility configs: %s", request)
|
||||||
session = self.get_session(request.session_id, context)
|
session = self.get_session(request.session_id, context)
|
||||||
response = GetMobilityConfigsResponse()
|
configs = grpcutils.get_mobility_configs(session)
|
||||||
for node_id in session.mobility.node_configurations:
|
return GetMobilityConfigsResponse(configs=configs)
|
||||||
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)
|
|
||||||
response.configs[node_id].CopyFrom(mapped_config)
|
|
||||||
return response
|
|
||||||
|
|
||||||
def GetMobilityConfig(
|
def GetMobilityConfig(
|
||||||
self, request: GetMobilityConfigRequest, context: ServicerContext
|
self, request: GetMobilityConfigRequest, context: ServicerContext
|
||||||
|
|
@ -1157,12 +1167,8 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
||||||
"""
|
"""
|
||||||
logging.debug("get service defaults: %s", request)
|
logging.debug("get service defaults: %s", request)
|
||||||
session = self.get_session(request.session_id, context)
|
session = self.get_session(request.session_id, context)
|
||||||
all_service_defaults = []
|
defaults = grpcutils.get_default_services(session)
|
||||||
for node_type in session.services.default_services:
|
return GetServiceDefaultsResponse(defaults=defaults)
|
||||||
services = session.services.default_services[node_type]
|
|
||||||
service_defaults = ServiceDefaults(node_type=node_type, services=services)
|
|
||||||
all_service_defaults.append(service_defaults)
|
|
||||||
return GetServiceDefaultsResponse(defaults=all_service_defaults)
|
|
||||||
|
|
||||||
def SetServiceDefaults(
|
def SetServiceDefaults(
|
||||||
self, request: SetServiceDefaultsRequest, context: ServicerContext
|
self, request: SetServiceDefaultsRequest, context: ServicerContext
|
||||||
|
|
@ -1196,18 +1202,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
||||||
"""
|
"""
|
||||||
logging.debug("get node service configs: %s", request)
|
logging.debug("get node service configs: %s", request)
|
||||||
session = self.get_session(request.session_id, context)
|
session = self.get_session(request.session_id, context)
|
||||||
configs = []
|
configs = grpcutils.get_node_service_configs(session)
|
||||||
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 = grpcutils.get_service_configuration(service)
|
|
||||||
config = GetNodeServiceConfigsResponse.ServiceConfig(
|
|
||||||
node_id=node_id,
|
|
||||||
service=name,
|
|
||||||
data=service_proto,
|
|
||||||
files=service.config_data,
|
|
||||||
)
|
|
||||||
configs.append(config)
|
|
||||||
return GetNodeServiceConfigsResponse(configs=configs)
|
return GetNodeServiceConfigsResponse(configs=configs)
|
||||||
|
|
||||||
def GetNodeService(
|
def GetNodeService(
|
||||||
|
|
@ -1337,19 +1332,8 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
||||||
"""
|
"""
|
||||||
logging.debug("get wlan configs: %s", request)
|
logging.debug("get wlan configs: %s", request)
|
||||||
session = self.get_session(request.session_id, context)
|
session = self.get_session(request.session_id, context)
|
||||||
response = GetWlanConfigsResponse()
|
configs = grpcutils.get_wlan_configs(session)
|
||||||
for node_id in session.mobility.node_configurations:
|
return GetWlanConfigsResponse(configs=configs)
|
||||||
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)
|
|
||||||
response.configs[node_id].CopyFrom(mapped_config)
|
|
||||||
return response
|
|
||||||
|
|
||||||
def GetWlanConfig(
|
def GetWlanConfig(
|
||||||
self, request: GetWlanConfigRequest, context: ServicerContext
|
self, request: GetWlanConfigRequest, context: ServicerContext
|
||||||
|
|
@ -1401,8 +1385,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
||||||
"""
|
"""
|
||||||
logging.debug("get emane config: %s", request)
|
logging.debug("get emane config: %s", request)
|
||||||
session = self.get_session(request.session_id, context)
|
session = self.get_session(request.session_id, context)
|
||||||
current_config = session.emane.get_configs()
|
config = grpcutils.get_emane_config(session)
|
||||||
config = get_config_options(current_config, session.emane.emane_config)
|
|
||||||
return GetEmaneConfigResponse(config=config)
|
return GetEmaneConfigResponse(config=config)
|
||||||
|
|
||||||
def SetEmaneConfig(
|
def SetEmaneConfig(
|
||||||
|
|
@ -1433,11 +1416,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
||||||
"""
|
"""
|
||||||
logging.debug("get emane models: %s", request)
|
logging.debug("get emane models: %s", request)
|
||||||
session = self.get_session(request.session_id, context)
|
session = self.get_session(request.session_id, context)
|
||||||
models = []
|
models = grpcutils.get_emane_models(session)
|
||||||
for model in session.emane.models.keys():
|
|
||||||
if len(model.split("_")) != 2:
|
|
||||||
continue
|
|
||||||
models.append(model)
|
|
||||||
return GetEmaneModelsResponse(models=models)
|
return GetEmaneModelsResponse(models=models)
|
||||||
|
|
||||||
def GetEmaneModelConfig(
|
def GetEmaneModelConfig(
|
||||||
|
|
@ -1491,22 +1470,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
||||||
"""
|
"""
|
||||||
logging.debug("get emane model configs: %s", request)
|
logging.debug("get emane model configs: %s", request)
|
||||||
session = self.get_session(request.session_id, context)
|
session = self.get_session(request.session_id, context)
|
||||||
|
configs = grpcutils.get_emane_model_configs(session)
|
||||||
configs = []
|
|
||||||
for _id in session.emane.node_configurations:
|
|
||||||
if _id == -1:
|
|
||||||
continue
|
|
||||||
|
|
||||||
model_configs = session.emane.node_configurations[_id]
|
|
||||||
for model_name in model_configs:
|
|
||||||
model = session.emane.models[model_name]
|
|
||||||
current_config = session.emane.get_model_config(_id, model_name)
|
|
||||||
config = get_config_options(current_config, model)
|
|
||||||
node_id, iface_id = grpcutils.parse_emane_model_id(_id)
|
|
||||||
model_config = GetEmaneModelConfigsResponse.ModelConfig(
|
|
||||||
node_id=node_id, model=model_name, iface_id=iface_id, config=config
|
|
||||||
)
|
|
||||||
configs.append(model_config)
|
|
||||||
return GetEmaneModelConfigsResponse(configs=configs)
|
return GetEmaneModelConfigsResponse(configs=configs)
|
||||||
|
|
||||||
def SaveXml(
|
def SaveXml(
|
||||||
|
|
@ -1713,21 +1677,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
||||||
:return: get node config service configs response
|
:return: get node config service configs response
|
||||||
"""
|
"""
|
||||||
session = self.get_session(request.session_id, context)
|
session = self.get_session(request.session_id, context)
|
||||||
configs = []
|
configs = grpcutils.get_node_config_service_configs(session)
|
||||||
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 = configservices_pb2.ConfigServiceConfig(
|
|
||||||
node_id=node.id,
|
|
||||||
name=name,
|
|
||||||
templates=service.custom_templates,
|
|
||||||
config=service.custom_config,
|
|
||||||
)
|
|
||||||
configs.append(config_proto)
|
|
||||||
return GetNodeConfigServiceConfigsResponse(configs=configs)
|
return GetNodeConfigServiceConfigsResponse(configs=configs)
|
||||||
|
|
||||||
def GetNodeConfigServices(
|
def GetNodeConfigServices(
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@ class Session:
|
||||||
# states and hooks handlers
|
# states and hooks handlers
|
||||||
self.state: EventTypes = EventTypes.DEFINITION_STATE
|
self.state: EventTypes = EventTypes.DEFINITION_STATE
|
||||||
self.state_time: float = time.monotonic()
|
self.state_time: float = time.monotonic()
|
||||||
self.hooks: Dict[EventTypes, Tuple[str, str]] = {}
|
self.hooks: Dict[EventTypes, List[Tuple[str, str]]] = {}
|
||||||
self.state_hooks: Dict[EventTypes, List[Callable[[EventTypes], None]]] = {}
|
self.state_hooks: Dict[EventTypes, List[Callable[[EventTypes], None]]] = {}
|
||||||
self.add_state_hook(
|
self.add_state_hook(
|
||||||
state=EventTypes.RUNTIME_STATE, hook=self.runtime_state_hook
|
state=EventTypes.RUNTIME_STATE, hook=self.runtime_state_hook
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,6 @@ from core.gui.wrappers import (
|
||||||
ConfigOption,
|
ConfigOption,
|
||||||
ConfigService,
|
ConfigService,
|
||||||
ExceptionEvent,
|
ExceptionEvent,
|
||||||
Hook,
|
|
||||||
Interface,
|
Interface,
|
||||||
Link,
|
Link,
|
||||||
LinkEvent,
|
LinkEvent,
|
||||||
|
|
@ -61,6 +60,10 @@ GUI_SOURCE = "gui"
|
||||||
CPU_USAGE_DELAY = 3
|
CPU_USAGE_DELAY = 3
|
||||||
|
|
||||||
|
|
||||||
|
def to_dict(config: Dict[str, ConfigOption]) -> Dict[str, str]:
|
||||||
|
return {x: y.value for x, y in config.items()}
|
||||||
|
|
||||||
|
|
||||||
class CoreClient:
|
class CoreClient:
|
||||||
def __init__(self, app: "Application", proxy: bool) -> None:
|
def __init__(self, app: "Application", proxy: bool) -> None:
|
||||||
"""
|
"""
|
||||||
|
|
@ -69,14 +72,13 @@ class CoreClient:
|
||||||
self.app: "Application" = app
|
self.app: "Application" = app
|
||||||
self.master: tk.Tk = app.master
|
self.master: tk.Tk = app.master
|
||||||
self._client: client.CoreGrpcClient = client.CoreGrpcClient(proxy=proxy)
|
self._client: client.CoreGrpcClient = client.CoreGrpcClient(proxy=proxy)
|
||||||
self.session_id: Optional[int] = None
|
self.session: Optional[Session] = None
|
||||||
|
self.user = getpass.getuser()
|
||||||
|
|
||||||
|
# global service settings
|
||||||
self.services: Dict[str, Set[str]] = {}
|
self.services: Dict[str, Set[str]] = {}
|
||||||
self.config_services_groups: Dict[str, Set[str]] = {}
|
self.config_services_groups: Dict[str, Set[str]] = {}
|
||||||
self.config_services: Dict[str, ConfigService] = {}
|
self.config_services: Dict[str, ConfigService] = {}
|
||||||
self.default_services: Dict[NodeType, Set[str]] = {}
|
|
||||||
self.emane_models: List[str] = []
|
|
||||||
self.observer: Optional[str] = None
|
|
||||||
self.user = getpass.getuser()
|
|
||||||
|
|
||||||
# loaded configuration data
|
# loaded configuration data
|
||||||
self.servers: Dict[str, CoreServer] = {}
|
self.servers: Dict[str, CoreServer] = {}
|
||||||
|
|
@ -87,15 +89,12 @@ class CoreClient:
|
||||||
# helpers
|
# helpers
|
||||||
self.iface_to_edge: Dict[Tuple[int, ...], Tuple[int, ...]] = {}
|
self.iface_to_edge: Dict[Tuple[int, ...], Tuple[int, ...]] = {}
|
||||||
self.ifaces_manager: InterfaceManager = InterfaceManager(self.app)
|
self.ifaces_manager: InterfaceManager = InterfaceManager(self.app)
|
||||||
|
self.observer: Optional[str] = None
|
||||||
|
|
||||||
# session data
|
# session data
|
||||||
self.state: Optional[SessionState] = None
|
|
||||||
self.canvas_nodes: Dict[int, CanvasNode] = {}
|
|
||||||
self.location: Optional[SessionLocation] = None
|
|
||||||
self.links: Dict[Tuple[int, int], CanvasEdge] = {}
|
|
||||||
self.hooks: Dict[str, Hook] = {}
|
|
||||||
self.emane_config: Dict[str, ConfigOption] = {}
|
|
||||||
self.mobility_players: Dict[int, MobilityPlayer] = {}
|
self.mobility_players: Dict[int, MobilityPlayer] = {}
|
||||||
|
self.canvas_nodes: Dict[int, CanvasNode] = {}
|
||||||
|
self.links: Dict[Tuple[int, int], CanvasEdge] = {}
|
||||||
self.handling_throughputs: Optional[grpc.Future] = None
|
self.handling_throughputs: Optional[grpc.Future] = None
|
||||||
self.handling_cpu_usage: Optional[grpc.Future] = None
|
self.handling_cpu_usage: Optional[grpc.Future] = None
|
||||||
self.handling_events: Optional[grpc.Future] = None
|
self.handling_events: Optional[grpc.Future] = None
|
||||||
|
|
@ -104,21 +103,27 @@ class CoreClient:
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def client(self) -> client.CoreGrpcClient:
|
def client(self) -> client.CoreGrpcClient:
|
||||||
if self.session_id:
|
if self.session:
|
||||||
response = self._client.check_session(self.session_id)
|
response = self._client.check_session(self.session.id)
|
||||||
if not response.result:
|
if not response.result:
|
||||||
throughputs_enabled = self.handling_throughputs is not None
|
throughputs_enabled = self.handling_throughputs is not None
|
||||||
self.cancel_throughputs()
|
self.cancel_throughputs()
|
||||||
self.cancel_events()
|
self.cancel_events()
|
||||||
self._client.create_session(self.session_id)
|
self._client.create_session(self.session.id)
|
||||||
self.handling_events = self._client.events(
|
self.handling_events = self._client.events(
|
||||||
self.session_id, self.handle_events
|
self.session.id, self.handle_events
|
||||||
)
|
)
|
||||||
if throughputs_enabled:
|
if throughputs_enabled:
|
||||||
self.enable_throughputs()
|
self.enable_throughputs()
|
||||||
self.setup_cpu_usage()
|
self.setup_cpu_usage()
|
||||||
return self._client
|
return self._client
|
||||||
|
|
||||||
|
def set_canvas_node(self, node: Node, canvas_node: CanvasNode) -> None:
|
||||||
|
self.canvas_nodes[node.id] = canvas_node
|
||||||
|
|
||||||
|
def get_canvas_node(self, node_id: int) -> CanvasNode:
|
||||||
|
return self.canvas_nodes[node_id]
|
||||||
|
|
||||||
def reset(self) -> None:
|
def reset(self) -> None:
|
||||||
# helpers
|
# helpers
|
||||||
self.ifaces_manager.reset()
|
self.ifaces_manager.reset()
|
||||||
|
|
@ -126,8 +131,6 @@ class CoreClient:
|
||||||
# session data
|
# session data
|
||||||
self.canvas_nodes.clear()
|
self.canvas_nodes.clear()
|
||||||
self.links.clear()
|
self.links.clear()
|
||||||
self.hooks.clear()
|
|
||||||
self.emane_config = None
|
|
||||||
self.close_mobility_players()
|
self.close_mobility_players()
|
||||||
self.mobility_players.clear()
|
self.mobility_players.clear()
|
||||||
# clear streams
|
# clear streams
|
||||||
|
|
@ -145,12 +148,10 @@ class CoreClient:
|
||||||
# read distributed servers
|
# read distributed servers
|
||||||
for server in self.app.guiconfig.servers:
|
for server in self.app.guiconfig.servers:
|
||||||
self.servers[server.name] = server
|
self.servers[server.name] = server
|
||||||
|
|
||||||
# read custom nodes
|
# read custom nodes
|
||||||
for custom_node in self.app.guiconfig.nodes:
|
for custom_node in self.app.guiconfig.nodes:
|
||||||
node_draw = NodeDraw.from_custom(custom_node)
|
node_draw = NodeDraw.from_custom(custom_node)
|
||||||
self.custom_nodes[custom_node.name] = node_draw
|
self.custom_nodes[custom_node.name] = node_draw
|
||||||
|
|
||||||
# read observers
|
# read observers
|
||||||
for observer in self.app.guiconfig.observers:
|
for observer in self.app.guiconfig.observers:
|
||||||
self.custom_observers[observer.name] = observer
|
self.custom_observers[observer.name] = observer
|
||||||
|
|
@ -158,11 +159,11 @@ class CoreClient:
|
||||||
def handle_events(self, event: core_pb2.Event) -> None:
|
def handle_events(self, event: core_pb2.Event) -> None:
|
||||||
if event.source == GUI_SOURCE:
|
if event.source == GUI_SOURCE:
|
||||||
return
|
return
|
||||||
if event.session_id != self.session_id:
|
if event.session_id != self.session.id:
|
||||||
logging.warning(
|
logging.warning(
|
||||||
"ignoring event session(%s) current(%s)",
|
"ignoring event session(%s) current(%s)",
|
||||||
event.session_id,
|
event.session_id,
|
||||||
self.session_id,
|
self.session.id,
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
@ -173,7 +174,7 @@ class CoreClient:
|
||||||
logging.info("session event: %s", event)
|
logging.info("session event: %s", event)
|
||||||
session_event = event.session_event
|
session_event = event.session_event
|
||||||
if session_event.event <= SessionState.SHUTDOWN.value:
|
if session_event.event <= SessionState.SHUTDOWN.value:
|
||||||
self.state = SessionState(session_event.event)
|
self.session.state = SessionState(session_event.event)
|
||||||
elif session_event.event in {7, 8, 9}:
|
elif session_event.event in {7, 8, 9}:
|
||||||
node_id = session_event.node_id
|
node_id = session_event.node_id
|
||||||
dialog = self.mobility_players.get(node_id)
|
dialog = self.mobility_players.get(node_id)
|
||||||
|
|
@ -236,24 +237,27 @@ class CoreClient:
|
||||||
|
|
||||||
def handle_node_event(self, event: NodeEvent) -> None:
|
def handle_node_event(self, event: NodeEvent) -> None:
|
||||||
logging.debug("node event: %s", event)
|
logging.debug("node event: %s", event)
|
||||||
|
node = event.node
|
||||||
if event.message_type == MessageType.NONE:
|
if event.message_type == MessageType.NONE:
|
||||||
canvas_node = self.canvas_nodes[event.node.id]
|
canvas_node = self.canvas_nodes[node.id]
|
||||||
x = event.node.position.x
|
x = node.position.x
|
||||||
y = event.node.position.y
|
y = node.position.y
|
||||||
canvas_node.move(x, y)
|
canvas_node.move(x, y)
|
||||||
elif event.message_type == MessageType.DELETE:
|
elif event.message_type == MessageType.DELETE:
|
||||||
canvas_node = self.canvas_nodes[event.node.id]
|
canvas_node = self.canvas_nodes[node.id]
|
||||||
self.app.canvas.clear_selection()
|
self.app.canvas.clear_selection()
|
||||||
self.app.canvas.select_object(canvas_node.id)
|
self.app.canvas.select_object(canvas_node.id)
|
||||||
self.app.canvas.delete_selected_objects()
|
self.app.canvas.delete_selected_objects()
|
||||||
elif event.message_type == MessageType.ADD:
|
elif event.message_type == MessageType.ADD:
|
||||||
self.app.canvas.add_core_node(event.node)
|
if node.id in self.session.nodes:
|
||||||
|
logging.error("core node already exists: %s", node)
|
||||||
|
self.app.canvas.add_core_node(node)
|
||||||
else:
|
else:
|
||||||
logging.warning("unknown node event: %s", event)
|
logging.warning("unknown node event: %s", event)
|
||||||
|
|
||||||
def enable_throughputs(self) -> None:
|
def enable_throughputs(self) -> None:
|
||||||
self.handling_throughputs = self.client.throughputs(
|
self.handling_throughputs = self.client.throughputs(
|
||||||
self.session_id, self.handle_throughputs
|
self.session.id, self.handle_throughputs
|
||||||
)
|
)
|
||||||
|
|
||||||
def cancel_throughputs(self) -> None:
|
def cancel_throughputs(self) -> None:
|
||||||
|
|
@ -283,11 +287,11 @@ class CoreClient:
|
||||||
|
|
||||||
def handle_throughputs(self, event: core_pb2.ThroughputsEvent) -> None:
|
def handle_throughputs(self, event: core_pb2.ThroughputsEvent) -> None:
|
||||||
event = ThroughputsEvent.from_proto(event)
|
event = ThroughputsEvent.from_proto(event)
|
||||||
if event.session_id != self.session_id:
|
if event.session_id != self.session.id:
|
||||||
logging.warning(
|
logging.warning(
|
||||||
"ignoring throughput event session(%s) current(%s)",
|
"ignoring throughput event session(%s) current(%s)",
|
||||||
event.session_id,
|
event.session_id,
|
||||||
self.session_id,
|
self.session.id,
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
logging.debug("handling throughputs event: %s", event)
|
logging.debug("handling throughputs event: %s", event)
|
||||||
|
|
@ -300,126 +304,33 @@ class CoreClient:
|
||||||
logging.info("exception event: %s", event)
|
logging.info("exception event: %s", event)
|
||||||
self.app.statusbar.add_alert(event)
|
self.app.statusbar.add_alert(event)
|
||||||
|
|
||||||
def join_session(self, session_id: int, query_location: bool = True) -> None:
|
def join_session(self, session_id: int) -> None:
|
||||||
logging.info("join session(%s)", session_id)
|
logging.info("joining session(%s)", session_id)
|
||||||
# update session and title
|
|
||||||
self.session_id = session_id
|
|
||||||
self.master.title(f"CORE Session({self.session_id})")
|
|
||||||
|
|
||||||
# clear session data
|
|
||||||
self.reset()
|
self.reset()
|
||||||
|
|
||||||
# get session data
|
|
||||||
try:
|
try:
|
||||||
response = self.client.get_session(self.session_id)
|
response = self.client.get_session(session_id)
|
||||||
session = Session.from_proto(response.session)
|
self.session = Session.from_proto(response.session)
|
||||||
self.state = session.state
|
self.client.set_session_user(self.session.id, self.user)
|
||||||
|
self.master.title(f"CORE Session({self.session.id})")
|
||||||
self.handling_events = self.client.events(
|
self.handling_events = self.client.events(
|
||||||
self.session_id, self.handle_events
|
self.session.id, self.handle_events
|
||||||
)
|
)
|
||||||
|
self.ifaces_manager.joined(self.session.links)
|
||||||
# set session user
|
self.app.canvas.reset_and_redraw(self.session)
|
||||||
self.client.set_session_user(self.session_id, self.user)
|
self.parse_metadata()
|
||||||
|
self.app.canvas.organize()
|
||||||
# get session service defaults
|
if self.is_runtime():
|
||||||
response = self.client.get_service_defaults(self.session_id)
|
self.show_mobility_players()
|
||||||
self.default_services = {
|
self.app.after(0, self.app.joined_session_update)
|
||||||
x.node_type: set(x.services) for x in response.defaults
|
|
||||||
}
|
|
||||||
|
|
||||||
# get location
|
|
||||||
if query_location:
|
|
||||||
response = self.client.get_session_location(self.session_id)
|
|
||||||
self.location = SessionLocation.from_proto(response.location)
|
|
||||||
|
|
||||||
# get emane models
|
|
||||||
response = self.client.get_emane_models(self.session_id)
|
|
||||||
self.emane_models = response.models
|
|
||||||
|
|
||||||
# get hooks
|
|
||||||
response = self.client.get_hooks(self.session_id)
|
|
||||||
for hook_proto in response.hooks:
|
|
||||||
hook = Hook.from_proto(hook_proto)
|
|
||||||
self.hooks[hook.file] = hook
|
|
||||||
|
|
||||||
# get emane config
|
|
||||||
response = self.client.get_emane_config(self.session_id)
|
|
||||||
self.emane_config = ConfigOption.from_dict(response.config)
|
|
||||||
|
|
||||||
# update interface manager
|
|
||||||
self.ifaces_manager.joined(session.links)
|
|
||||||
|
|
||||||
# draw session
|
|
||||||
self.app.canvas.reset_and_redraw(session)
|
|
||||||
|
|
||||||
# get mobility configs
|
|
||||||
response = self.client.get_mobility_configs(self.session_id)
|
|
||||||
for node_id in response.configs:
|
|
||||||
config = response.configs[node_id].config
|
|
||||||
canvas_node = self.canvas_nodes[node_id]
|
|
||||||
canvas_node.mobility_config = ConfigOption.from_dict(config)
|
|
||||||
|
|
||||||
# get emane model config
|
|
||||||
response = self.client.get_emane_model_configs(self.session_id)
|
|
||||||
for config in response.configs:
|
|
||||||
iface_id = None
|
|
||||||
if config.iface_id != -1:
|
|
||||||
iface_id = config.iface_id
|
|
||||||
canvas_node = self.canvas_nodes[config.node_id]
|
|
||||||
canvas_node.emane_model_configs[
|
|
||||||
(config.model, iface_id)
|
|
||||||
] = ConfigOption.from_dict(config.config)
|
|
||||||
|
|
||||||
# get wlan configurations
|
|
||||||
response = self.client.get_wlan_configs(self.session_id)
|
|
||||||
for _id in response.configs:
|
|
||||||
mapped_config = response.configs[_id]
|
|
||||||
canvas_node = self.canvas_nodes[_id]
|
|
||||||
canvas_node.wlan_config = ConfigOption.from_dict(mapped_config.config)
|
|
||||||
|
|
||||||
# get service configurations
|
|
||||||
response = self.client.get_node_service_configs(self.session_id)
|
|
||||||
for config in response.configs:
|
|
||||||
canvas_node = self.canvas_nodes[config.node_id]
|
|
||||||
canvas_node.service_configs[config.service] = config.data
|
|
||||||
logging.debug("service file configs: %s", config.files)
|
|
||||||
for file_name in config.files:
|
|
||||||
data = config.files[file_name]
|
|
||||||
files = canvas_node.service_file_configs.setdefault(
|
|
||||||
config.service, {}
|
|
||||||
)
|
|
||||||
files[file_name] = data
|
|
||||||
|
|
||||||
# get config service configurations
|
|
||||||
response = self.client.get_node_config_service_configs(self.session_id)
|
|
||||||
for config in response.configs:
|
|
||||||
canvas_node = self.canvas_nodes[config.node_id]
|
|
||||||
service_config = canvas_node.config_service_configs.setdefault(
|
|
||||||
config.name, {}
|
|
||||||
)
|
|
||||||
if config.templates:
|
|
||||||
service_config["templates"] = config.templates
|
|
||||||
if config.config:
|
|
||||||
service_config["config"] = config.config
|
|
||||||
|
|
||||||
# get metadata
|
|
||||||
response = self.client.get_session_metadata(self.session_id)
|
|
||||||
self.parse_metadata(response.config)
|
|
||||||
except grpc.RpcError as e:
|
except grpc.RpcError as e:
|
||||||
self.app.show_grpc_exception("Join Session Error", e)
|
self.app.show_grpc_exception("Join Session Error", e)
|
||||||
|
|
||||||
# organize canvas
|
|
||||||
self.app.canvas.organize()
|
|
||||||
if self.is_runtime():
|
|
||||||
self.show_mobility_players()
|
|
||||||
# update ui to represent current state
|
|
||||||
self.app.after(0, self.app.joined_session_update)
|
|
||||||
|
|
||||||
def is_runtime(self) -> bool:
|
def is_runtime(self) -> bool:
|
||||||
return self.state == SessionState.RUNTIME
|
return self.session and self.session.state == SessionState.RUNTIME
|
||||||
|
|
||||||
def parse_metadata(self, config: Dict[str, str]) -> None:
|
def parse_metadata(self) -> None:
|
||||||
# canvas setting
|
# canvas setting
|
||||||
|
config = self.session.metadata
|
||||||
canvas_config = config.get("canvas")
|
canvas_config = config.get("canvas")
|
||||||
logging.debug("canvas metadata: %s", canvas_config)
|
logging.debug("canvas metadata: %s", canvas_config)
|
||||||
if canvas_config:
|
if canvas_config:
|
||||||
|
|
@ -447,7 +358,7 @@ class CoreClient:
|
||||||
if shapes_config:
|
if shapes_config:
|
||||||
shapes_config = json.loads(shapes_config)
|
shapes_config = json.loads(shapes_config)
|
||||||
for shape_config in shapes_config:
|
for shape_config in shapes_config:
|
||||||
logging.info("loading shape: %s", shape_config)
|
logging.debug("loading shape: %s", shape_config)
|
||||||
shape_type = shape_config["type"]
|
shape_type = shape_config["type"]
|
||||||
try:
|
try:
|
||||||
shape_type = ShapeType(shape_type)
|
shape_type = ShapeType(shape_type)
|
||||||
|
|
@ -478,8 +389,9 @@ class CoreClient:
|
||||||
try:
|
try:
|
||||||
response = self.client.create_session()
|
response = self.client.create_session()
|
||||||
logging.info("created session: %s", response)
|
logging.info("created session: %s", response)
|
||||||
|
self.join_session(response.session_id)
|
||||||
location_config = self.app.guiconfig.location
|
location_config = self.app.guiconfig.location
|
||||||
self.location = SessionLocation(
|
self.session.location = SessionLocation(
|
||||||
x=location_config.x,
|
x=location_config.x,
|
||||||
y=location_config.y,
|
y=location_config.y,
|
||||||
z=location_config.z,
|
z=location_config.z,
|
||||||
|
|
@ -488,13 +400,14 @@ class CoreClient:
|
||||||
alt=location_config.alt,
|
alt=location_config.alt,
|
||||||
scale=location_config.scale,
|
scale=location_config.scale,
|
||||||
)
|
)
|
||||||
self.join_session(response.session_id, query_location=False)
|
|
||||||
except grpc.RpcError as e:
|
except grpc.RpcError as e:
|
||||||
self.app.show_grpc_exception("New Session Error", e)
|
self.app.show_grpc_exception("New Session Error", e)
|
||||||
|
|
||||||
def delete_session(self, session_id: int = None) -> None:
|
def delete_session(self, session_id: int = None) -> None:
|
||||||
|
if session_id is None and not self.session:
|
||||||
|
return
|
||||||
if session_id is None:
|
if session_id is None:
|
||||||
session_id = self.session_id
|
session_id = self.session.id
|
||||||
try:
|
try:
|
||||||
response = self.client.delete_session(session_id)
|
response = self.client.delete_session(session_id)
|
||||||
logging.info("deleted session(%s), Result: %s", session_id, response)
|
logging.info("deleted session(%s), Result: %s", session_id, response)
|
||||||
|
|
@ -507,13 +420,11 @@ class CoreClient:
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
self.client.connect()
|
self.client.connect()
|
||||||
|
# get all available services
|
||||||
# get service information
|
|
||||||
response = self.client.get_services()
|
response = self.client.get_services()
|
||||||
for service in response.services:
|
for service in response.services:
|
||||||
group_services = self.services.setdefault(service.group, set())
|
group_services = self.services.setdefault(service.group, set())
|
||||||
group_services.add(service.name)
|
group_services.add(service.name)
|
||||||
|
|
||||||
# get config service informations
|
# get config service informations
|
||||||
response = self.client.get_config_services()
|
response = self.client.get_config_services()
|
||||||
for service in response.services:
|
for service in response.services:
|
||||||
|
|
@ -522,7 +433,6 @@ class CoreClient:
|
||||||
service.group, set()
|
service.group, set()
|
||||||
)
|
)
|
||||||
group_services.add(service.name)
|
group_services.add(service.name)
|
||||||
|
|
||||||
# join provided session, create new session, or show dialog to select an
|
# join provided session, create new session, or show dialog to select an
|
||||||
# existing session
|
# existing session
|
||||||
response = self.client.get_sessions()
|
response = self.client.get_sessions()
|
||||||
|
|
@ -553,19 +463,20 @@ class CoreClient:
|
||||||
try:
|
try:
|
||||||
position = core_node.position.to_proto()
|
position = core_node.position.to_proto()
|
||||||
self.client.edit_node(
|
self.client.edit_node(
|
||||||
self.session_id, core_node.id, position, source=GUI_SOURCE
|
self.session.id, core_node.id, position, source=GUI_SOURCE
|
||||||
)
|
)
|
||||||
except grpc.RpcError as e:
|
except grpc.RpcError as e:
|
||||||
self.app.show_grpc_exception("Edit Node Error", e)
|
self.app.show_grpc_exception("Edit Node Error", e)
|
||||||
|
|
||||||
def send_servers(self) -> None:
|
def send_servers(self) -> None:
|
||||||
for server in self.servers.values():
|
for server in self.servers.values():
|
||||||
self.client.add_session_server(self.session_id, server.name, server.address)
|
self.client.add_session_server(self.session.id, server.name, server.address)
|
||||||
|
|
||||||
def start_session(self) -> Tuple[bool, List[str]]:
|
def start_session(self) -> Tuple[bool, List[str]]:
|
||||||
self.ifaces_manager.reset_mac()
|
self.ifaces_manager.reset_mac()
|
||||||
nodes = [x.core_node.to_proto() for x in self.canvas_nodes.values()]
|
nodes = [x.to_proto() for x in self.session.nodes.values()]
|
||||||
links = []
|
links = []
|
||||||
|
asymmetric_links = []
|
||||||
for edge in self.links.values():
|
for edge in self.links.values():
|
||||||
link = edge.link
|
link = edge.link
|
||||||
if link.iface1 and not link.iface1.mac:
|
if link.iface1 and not link.iface1.mac:
|
||||||
|
|
@ -573,29 +484,25 @@ class CoreClient:
|
||||||
if link.iface2 and not link.iface2.mac:
|
if link.iface2 and not link.iface2.mac:
|
||||||
link.iface2.mac = self.ifaces_manager.next_mac()
|
link.iface2.mac = self.ifaces_manager.next_mac()
|
||||||
links.append(link.to_proto())
|
links.append(link.to_proto())
|
||||||
|
if edge.asymmetric_link:
|
||||||
|
asymmetric_links.append(edge.asymmetric_link.to_proto())
|
||||||
wlan_configs = self.get_wlan_configs_proto()
|
wlan_configs = self.get_wlan_configs_proto()
|
||||||
mobility_configs = self.get_mobility_configs_proto()
|
mobility_configs = self.get_mobility_configs_proto()
|
||||||
emane_model_configs = self.get_emane_model_configs_proto()
|
emane_model_configs = self.get_emane_model_configs_proto()
|
||||||
hooks = [x.to_proto() for x in self.hooks.values()]
|
hooks = [x.to_proto() for x in self.session.hooks.values()]
|
||||||
service_configs = self.get_service_configs_proto()
|
service_configs = self.get_service_configs_proto()
|
||||||
file_configs = self.get_service_file_configs_proto()
|
file_configs = self.get_service_file_configs_proto()
|
||||||
asymmetric_links = [
|
|
||||||
x.asymmetric_link for x in self.links.values() if x.asymmetric_link
|
|
||||||
]
|
|
||||||
config_service_configs = self.get_config_service_configs_proto()
|
config_service_configs = self.get_config_service_configs_proto()
|
||||||
if self.emane_config:
|
emane_config = to_dict(self.session.emane_config)
|
||||||
emane_config = {x: self.emane_config[x].value for x in self.emane_config}
|
|
||||||
else:
|
|
||||||
emane_config = None
|
|
||||||
result = False
|
result = False
|
||||||
exceptions = []
|
exceptions = []
|
||||||
try:
|
try:
|
||||||
self.send_servers()
|
self.send_servers()
|
||||||
response = self.client.start_session(
|
response = self.client.start_session(
|
||||||
self.session_id,
|
self.session.id,
|
||||||
nodes,
|
nodes,
|
||||||
links,
|
links,
|
||||||
self.location.to_proto(),
|
self.session.location.to_proto(),
|
||||||
hooks,
|
hooks,
|
||||||
emane_config,
|
emane_config,
|
||||||
emane_model_configs,
|
emane_model_configs,
|
||||||
|
|
@ -607,7 +514,7 @@ class CoreClient:
|
||||||
config_service_configs,
|
config_service_configs,
|
||||||
)
|
)
|
||||||
logging.info(
|
logging.info(
|
||||||
"start session(%s), result: %s", self.session_id, response.result
|
"start session(%s), result: %s", self.session.id, response.result
|
||||||
)
|
)
|
||||||
if response.result:
|
if response.result:
|
||||||
self.set_metadata()
|
self.set_metadata()
|
||||||
|
|
@ -619,7 +526,7 @@ class CoreClient:
|
||||||
|
|
||||||
def stop_session(self, session_id: int = None) -> bool:
|
def stop_session(self, session_id: int = None) -> bool:
|
||||||
if not session_id:
|
if not session_id:
|
||||||
session_id = self.session_id
|
session_id = self.session.id
|
||||||
result = False
|
result = False
|
||||||
try:
|
try:
|
||||||
response = self.client.stop_session(session_id)
|
response = self.client.stop_session(session_id)
|
||||||
|
|
@ -630,15 +537,12 @@ class CoreClient:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def show_mobility_players(self) -> None:
|
def show_mobility_players(self) -> None:
|
||||||
for canvas_node in self.canvas_nodes.values():
|
for node in self.session.nodes.values():
|
||||||
if canvas_node.core_node.type != NodeType.WIRELESS_LAN:
|
if node.type != NodeType.WIRELESS_LAN:
|
||||||
continue
|
continue
|
||||||
if canvas_node.mobility_config:
|
if node.mobility_config:
|
||||||
mobility_player = MobilityPlayer(
|
mobility_player = MobilityPlayer(self.app, node)
|
||||||
self.app, canvas_node, canvas_node.mobility_config
|
self.mobility_players[node.id] = mobility_player
|
||||||
)
|
|
||||||
node_id = canvas_node.core_node.id
|
|
||||||
self.mobility_players[node_id] = mobility_player
|
|
||||||
mobility_player.show()
|
mobility_player.show()
|
||||||
|
|
||||||
def set_metadata(self) -> None:
|
def set_metadata(self) -> None:
|
||||||
|
|
@ -662,8 +566,8 @@ class CoreClient:
|
||||||
shapes = json.dumps(shapes)
|
shapes = json.dumps(shapes)
|
||||||
|
|
||||||
metadata = {"canvas": canvas_config, "shapes": shapes}
|
metadata = {"canvas": canvas_config, "shapes": shapes}
|
||||||
response = self.client.set_session_metadata(self.session_id, metadata)
|
response = self.client.set_session_metadata(self.session.id, metadata)
|
||||||
logging.info("set session metadata %s, result: %s", metadata, response)
|
logging.debug("set session metadata %s, result: %s", metadata, response)
|
||||||
|
|
||||||
def launch_terminal(self, node_id: int) -> None:
|
def launch_terminal(self, node_id: int) -> None:
|
||||||
try:
|
try:
|
||||||
|
|
@ -675,7 +579,7 @@ class CoreClient:
|
||||||
parent=self.app,
|
parent=self.app,
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
response = self.client.get_node_terminal(self.session_id, node_id)
|
response = self.client.get_node_terminal(self.session.id, node_id)
|
||||||
cmd = f"{terminal} {response.terminal} &"
|
cmd = f"{terminal} {response.terminal} &"
|
||||||
logging.info("launching terminal %s", cmd)
|
logging.info("launching terminal %s", cmd)
|
||||||
os.system(cmd)
|
os.system(cmd)
|
||||||
|
|
@ -687,10 +591,10 @@ class CoreClient:
|
||||||
Save core session as to an xml file
|
Save core session as to an xml file
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
if self.state != SessionState.RUNTIME:
|
if not self.is_runtime():
|
||||||
logging.debug("Send session data to the daemon")
|
logging.debug("Send session data to the daemon")
|
||||||
self.send_data()
|
self.send_data()
|
||||||
response = self.client.save_xml(self.session_id, file_path)
|
response = self.client.save_xml(self.session.id, file_path)
|
||||||
logging.info("saved xml file %s, result: %s", file_path, response)
|
logging.info("saved xml file %s, result: %s", file_path, response)
|
||||||
except grpc.RpcError as e:
|
except grpc.RpcError as e:
|
||||||
self.app.show_grpc_exception("Save XML Error", e)
|
self.app.show_grpc_exception("Save XML Error", e)
|
||||||
|
|
@ -707,7 +611,7 @@ class CoreClient:
|
||||||
self.app.show_grpc_exception("Open XML Error", e)
|
self.app.show_grpc_exception("Open XML Error", e)
|
||||||
|
|
||||||
def get_node_service(self, node_id: int, service_name: str) -> NodeServiceData:
|
def get_node_service(self, node_id: int, service_name: str) -> NodeServiceData:
|
||||||
response = self.client.get_node_service(self.session_id, node_id, service_name)
|
response = self.client.get_node_service(self.session.id, node_id, service_name)
|
||||||
logging.debug(
|
logging.debug(
|
||||||
"get node(%s) %s service, response: %s", node_id, service_name, response
|
"get node(%s) %s service, response: %s", node_id, service_name, response
|
||||||
)
|
)
|
||||||
|
|
@ -724,7 +628,7 @@ class CoreClient:
|
||||||
shutdowns: List[str],
|
shutdowns: List[str],
|
||||||
) -> NodeServiceData:
|
) -> NodeServiceData:
|
||||||
response = self.client.set_node_service(
|
response = self.client.set_node_service(
|
||||||
self.session_id,
|
self.session.id,
|
||||||
node_id,
|
node_id,
|
||||||
service_name,
|
service_name,
|
||||||
directories=dirs,
|
directories=dirs,
|
||||||
|
|
@ -744,14 +648,14 @@ class CoreClient:
|
||||||
shutdowns,
|
shutdowns,
|
||||||
response,
|
response,
|
||||||
)
|
)
|
||||||
response = self.client.get_node_service(self.session_id, node_id, service_name)
|
response = self.client.get_node_service(self.session.id, node_id, service_name)
|
||||||
return NodeServiceData.from_proto(response.service)
|
return NodeServiceData.from_proto(response.service)
|
||||||
|
|
||||||
def get_node_service_file(
|
def get_node_service_file(
|
||||||
self, node_id: int, service_name: str, file_name: str
|
self, node_id: int, service_name: str, file_name: str
|
||||||
) -> str:
|
) -> str:
|
||||||
response = self.client.get_node_service_file(
|
response = self.client.get_node_service_file(
|
||||||
self.session_id, node_id, service_name, file_name
|
self.session.id, node_id, service_name, file_name
|
||||||
)
|
)
|
||||||
logging.debug(
|
logging.debug(
|
||||||
"get service file for node(%s), service: %s, file: %s, result: %s",
|
"get service file for node(%s), service: %s, file: %s, result: %s",
|
||||||
|
|
@ -766,7 +670,7 @@ class CoreClient:
|
||||||
self, node_id: int, service_name: str, file_name: str, data: str
|
self, node_id: int, service_name: str, file_name: str, data: str
|
||||||
) -> None:
|
) -> None:
|
||||||
response = self.client.set_node_service_file(
|
response = self.client.set_node_service_file(
|
||||||
self.session_id, node_id, service_name, file_name, data
|
self.session.id, node_id, service_name, file_name, data
|
||||||
)
|
)
|
||||||
logging.info(
|
logging.info(
|
||||||
"set node(%s) service file, service: %s, file: %s, data: %s, result: %s",
|
"set node(%s) service file, service: %s, file: %s, data: %s, result: %s",
|
||||||
|
|
@ -781,22 +685,36 @@ class CoreClient:
|
||||||
"""
|
"""
|
||||||
create nodes and links that have not been created yet
|
create nodes and links that have not been created yet
|
||||||
"""
|
"""
|
||||||
node_protos = [x.core_node.to_proto() for x in self.canvas_nodes.values()]
|
self.client.set_session_state(self.session.id, SessionState.DEFINITION.value)
|
||||||
link_protos = [x.link.to_proto() for x in self.links.values()]
|
for node in self.session.nodes.values():
|
||||||
self.client.set_session_state(self.session_id, SessionState.DEFINITION.value)
|
response = self.client.add_node(self.session.id, node.to_proto())
|
||||||
for node_proto in node_protos:
|
logging.debug("created node: %s", response)
|
||||||
response = self.client.add_node(self.session_id, node_proto)
|
asymmetric_links = []
|
||||||
logging.debug("create node: %s", response)
|
for edge in self.links.values():
|
||||||
for link_proto in link_protos:
|
link = edge.link
|
||||||
response = self.client.add_link(
|
response = self.client.add_link(
|
||||||
self.session_id,
|
self.session.id,
|
||||||
link_proto.node1_id,
|
link.node1_id,
|
||||||
link_proto.node2_id,
|
link.node2_id,
|
||||||
link_proto.iface1,
|
link.iface1,
|
||||||
link_proto.iface2,
|
link.iface2,
|
||||||
link_proto.options,
|
link.options,
|
||||||
|
source=GUI_SOURCE,
|
||||||
)
|
)
|
||||||
logging.debug("create link: %s", response)
|
logging.debug("created link: %s", response)
|
||||||
|
if edge.asymmetric_link:
|
||||||
|
asymmetric_links.append(edge.asymmetric_link)
|
||||||
|
for link in asymmetric_links:
|
||||||
|
response = self.client.add_link(
|
||||||
|
self.session.id,
|
||||||
|
link.node1_id,
|
||||||
|
link.node2_id,
|
||||||
|
link.iface1,
|
||||||
|
link.iface2,
|
||||||
|
link.options,
|
||||||
|
source=GUI_SOURCE,
|
||||||
|
)
|
||||||
|
logging.debug("created asymmetric link: %s", response)
|
||||||
|
|
||||||
def send_data(self) -> None:
|
def send_data(self) -> None:
|
||||||
"""
|
"""
|
||||||
|
|
@ -806,15 +724,15 @@ class CoreClient:
|
||||||
self.create_nodes_and_links()
|
self.create_nodes_and_links()
|
||||||
for config_proto in self.get_wlan_configs_proto():
|
for config_proto in self.get_wlan_configs_proto():
|
||||||
self.client.set_wlan_config(
|
self.client.set_wlan_config(
|
||||||
self.session_id, config_proto.node_id, config_proto.config
|
self.session.id, config_proto.node_id, config_proto.config
|
||||||
)
|
)
|
||||||
for config_proto in self.get_mobility_configs_proto():
|
for config_proto in self.get_mobility_configs_proto():
|
||||||
self.client.set_mobility_config(
|
self.client.set_mobility_config(
|
||||||
self.session_id, config_proto.node_id, config_proto.config
|
self.session.id, config_proto.node_id, config_proto.config
|
||||||
)
|
)
|
||||||
for config_proto in self.get_service_configs_proto():
|
for config_proto in self.get_service_configs_proto():
|
||||||
self.client.set_node_service(
|
self.client.set_node_service(
|
||||||
self.session_id,
|
self.session.id,
|
||||||
config_proto.node_id,
|
config_proto.node_id,
|
||||||
config_proto.service,
|
config_proto.service,
|
||||||
startup=config_proto.startup,
|
startup=config_proto.startup,
|
||||||
|
|
@ -823,38 +741,37 @@ class CoreClient:
|
||||||
)
|
)
|
||||||
for config_proto in self.get_service_file_configs_proto():
|
for config_proto in self.get_service_file_configs_proto():
|
||||||
self.client.set_node_service_file(
|
self.client.set_node_service_file(
|
||||||
self.session_id,
|
self.session.id,
|
||||||
config_proto.node_id,
|
config_proto.node_id,
|
||||||
config_proto.service,
|
config_proto.service,
|
||||||
config_proto.file,
|
config_proto.file,
|
||||||
config_proto.data,
|
config_proto.data,
|
||||||
)
|
)
|
||||||
for hook in self.hooks.values():
|
for hook in self.session.hooks.values():
|
||||||
self.client.add_hook(
|
self.client.add_hook(
|
||||||
self.session_id, hook.state.value, hook.file, hook.data
|
self.session.id, hook.state.value, hook.file, hook.data
|
||||||
)
|
)
|
||||||
for config_proto in self.get_emane_model_configs_proto():
|
for config_proto in self.get_emane_model_configs_proto():
|
||||||
self.client.set_emane_model_config(
|
self.client.set_emane_model_config(
|
||||||
self.session_id,
|
self.session.id,
|
||||||
config_proto.node_id,
|
config_proto.node_id,
|
||||||
config_proto.model,
|
config_proto.model,
|
||||||
config_proto.config,
|
config_proto.config,
|
||||||
config_proto.iface_id,
|
config_proto.iface_id,
|
||||||
)
|
)
|
||||||
if self.emane_config:
|
config = to_dict(self.session.emane_config)
|
||||||
config = {x: self.emane_config[x].value for x in self.emane_config}
|
self.client.set_emane_config(self.session.id, config)
|
||||||
self.client.set_emane_config(self.session_id, config)
|
location = self.session.location
|
||||||
if self.location:
|
self.client.set_session_location(
|
||||||
self.client.set_session_location(
|
self.session.id,
|
||||||
self.session_id,
|
location.x,
|
||||||
self.location.x,
|
location.y,
|
||||||
self.location.y,
|
location.z,
|
||||||
self.location.z,
|
location.lat,
|
||||||
self.location.lat,
|
location.lon,
|
||||||
self.location.lon,
|
location.alt,
|
||||||
self.location.alt,
|
location.scale,
|
||||||
self.location.scale,
|
)
|
||||||
)
|
|
||||||
self.set_metadata()
|
self.set_metadata()
|
||||||
|
|
||||||
def close(self) -> None:
|
def close(self) -> None:
|
||||||
|
|
@ -870,7 +787,7 @@ class CoreClient:
|
||||||
"""
|
"""
|
||||||
i = 1
|
i = 1
|
||||||
while True:
|
while True:
|
||||||
if i not in self.canvas_nodes:
|
if i not in self.session.nodes:
|
||||||
break
|
break
|
||||||
i += 1
|
i += 1
|
||||||
return i
|
return i
|
||||||
|
|
@ -888,16 +805,16 @@ class CoreClient:
|
||||||
image = "ubuntu:latest"
|
image = "ubuntu:latest"
|
||||||
emane = None
|
emane = None
|
||||||
if node_type == NodeType.EMANE:
|
if node_type == NodeType.EMANE:
|
||||||
if not self.emane_models:
|
if not self.session.emane_models:
|
||||||
dialog = EmaneInstallDialog(self.app)
|
dialog = EmaneInstallDialog(self.app)
|
||||||
dialog.show()
|
dialog.show()
|
||||||
return
|
return
|
||||||
emane = self.emane_models[0]
|
emane = self.session.emane_models[0]
|
||||||
name = f"EMANE{node_id}"
|
name = f"emane{node_id}"
|
||||||
elif node_type == NodeType.WIRELESS_LAN:
|
elif node_type == NodeType.WIRELESS_LAN:
|
||||||
name = f"WLAN{node_id}"
|
name = f"wlan{node_id}"
|
||||||
elif node_type in [NodeType.RJ45, NodeType.TUNNEL]:
|
elif node_type in [NodeType.RJ45, NodeType.TUNNEL]:
|
||||||
name = "UNASSIGNED"
|
name = "unassigned"
|
||||||
else:
|
else:
|
||||||
name = f"n{node_id}"
|
name = f"n{node_id}"
|
||||||
node = Node(
|
node = Node(
|
||||||
|
|
@ -914,28 +831,30 @@ class CoreClient:
|
||||||
node.services[:] = services
|
node.services[:] = services
|
||||||
# assign default services to CORE node
|
# assign default services to CORE node
|
||||||
else:
|
else:
|
||||||
services = self.default_services.get(model)
|
services = self.session.default_services.get(model)
|
||||||
if services:
|
if services:
|
||||||
node.services[:] = services
|
node.services = services.copy()
|
||||||
logging.info(
|
logging.info(
|
||||||
"add node(%s) to session(%s), coordinates(%s, %s)",
|
"add node(%s) to session(%s), coordinates(%s, %s)",
|
||||||
node.name,
|
node.name,
|
||||||
self.session_id,
|
self.session.id,
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
)
|
)
|
||||||
|
self.session.nodes[node.id] = node
|
||||||
return node
|
return node
|
||||||
|
|
||||||
def deleted_graph_nodes(self, canvas_nodes: List[CanvasNode]) -> None:
|
def deleted_canvas_nodes(self, canvas_nodes: List[CanvasNode]) -> None:
|
||||||
"""
|
"""
|
||||||
remove the nodes selected by the user and anything related to that node
|
remove the nodes selected by the user and anything related to that node
|
||||||
such as link, configurations, interfaces
|
such as link, configurations, interfaces
|
||||||
"""
|
"""
|
||||||
for canvas_node in canvas_nodes:
|
for canvas_node in canvas_nodes:
|
||||||
node_id = canvas_node.core_node.id
|
node = canvas_node.core_node
|
||||||
del self.canvas_nodes[node_id]
|
del self.canvas_nodes[node.id]
|
||||||
|
del self.session.nodes[node.id]
|
||||||
|
|
||||||
def deleted_graph_edges(self, edges: Iterable[CanvasEdge]) -> None:
|
def deleted_canvas_edges(self, edges: Iterable[CanvasEdge]) -> None:
|
||||||
links = []
|
links = []
|
||||||
for edge in edges:
|
for edge in edges:
|
||||||
del self.links[edge.token]
|
del self.links[edge.token]
|
||||||
|
|
@ -969,20 +888,19 @@ class CoreClient:
|
||||||
"""
|
"""
|
||||||
src_node = canvas_src_node.core_node
|
src_node = canvas_src_node.core_node
|
||||||
dst_node = canvas_dst_node.core_node
|
dst_node = canvas_dst_node.core_node
|
||||||
|
|
||||||
# determine subnet
|
|
||||||
self.ifaces_manager.determine_subnets(canvas_src_node, canvas_dst_node)
|
self.ifaces_manager.determine_subnets(canvas_src_node, canvas_dst_node)
|
||||||
|
|
||||||
src_iface = None
|
src_iface = None
|
||||||
if NodeUtils.is_container_node(src_node.type):
|
if NodeUtils.is_container_node(src_node.type):
|
||||||
src_iface = self.create_iface(canvas_src_node)
|
src_iface = self.create_iface(canvas_src_node)
|
||||||
self.iface_to_edge[(src_node.id, src_iface.id)] = edge.token
|
self.iface_to_edge[(src_node.id, src_iface.id)] = edge.token
|
||||||
|
edge.src_iface = src_iface
|
||||||
|
canvas_src_node.ifaces[src_iface.id] = src_iface
|
||||||
dst_iface = None
|
dst_iface = None
|
||||||
if NodeUtils.is_container_node(dst_node.type):
|
if NodeUtils.is_container_node(dst_node.type):
|
||||||
dst_iface = self.create_iface(canvas_dst_node)
|
dst_iface = self.create_iface(canvas_dst_node)
|
||||||
self.iface_to_edge[(dst_node.id, dst_iface.id)] = edge.token
|
self.iface_to_edge[(dst_node.id, dst_iface.id)] = edge.token
|
||||||
|
edge.dst_iface = dst_iface
|
||||||
|
canvas_dst_node.ifaces[dst_iface.id] = dst_iface
|
||||||
link = Link(
|
link = Link(
|
||||||
type=LinkType.WIRED,
|
type=LinkType.WIRED,
|
||||||
node1_id=src_node.id,
|
node1_id=src_node.id,
|
||||||
|
|
@ -990,75 +908,62 @@ class CoreClient:
|
||||||
iface1=src_iface,
|
iface1=src_iface,
|
||||||
iface2=dst_iface,
|
iface2=dst_iface,
|
||||||
)
|
)
|
||||||
# assign after creating link proto, since interfaces are copied
|
|
||||||
if src_iface:
|
|
||||||
iface1 = link.iface1
|
|
||||||
edge.src_iface = iface1
|
|
||||||
canvas_src_node.ifaces[iface1.id] = iface1
|
|
||||||
if dst_iface:
|
|
||||||
iface2 = link.iface2
|
|
||||||
edge.dst_iface = iface2
|
|
||||||
canvas_dst_node.ifaces[iface2.id] = iface2
|
|
||||||
edge.set_link(link)
|
edge.set_link(link)
|
||||||
self.links[edge.token] = edge
|
self.links[edge.token] = edge
|
||||||
logging.info("Add link between %s and %s", src_node.name, dst_node.name)
|
logging.info("added link between %s and %s", src_node.name, dst_node.name)
|
||||||
|
|
||||||
def get_wlan_configs_proto(self) -> List[wlan_pb2.WlanConfig]:
|
def get_wlan_configs_proto(self) -> List[wlan_pb2.WlanConfig]:
|
||||||
configs = []
|
configs = []
|
||||||
for canvas_node in self.canvas_nodes.values():
|
for node in self.session.nodes.values():
|
||||||
if canvas_node.core_node.type != NodeType.WIRELESS_LAN:
|
if node.type != NodeType.WIRELESS_LAN:
|
||||||
continue
|
continue
|
||||||
if not canvas_node.wlan_config:
|
if not node.wlan_config:
|
||||||
continue
|
continue
|
||||||
config = ConfigOption.to_dict(canvas_node.wlan_config)
|
config = ConfigOption.to_dict(node.wlan_config)
|
||||||
node_id = canvas_node.core_node.id
|
wlan_config = wlan_pb2.WlanConfig(node_id=node.id, config=config)
|
||||||
wlan_config = wlan_pb2.WlanConfig(node_id=node_id, config=config)
|
|
||||||
configs.append(wlan_config)
|
configs.append(wlan_config)
|
||||||
return configs
|
return configs
|
||||||
|
|
||||||
def get_mobility_configs_proto(self) -> List[mobility_pb2.MobilityConfig]:
|
def get_mobility_configs_proto(self) -> List[mobility_pb2.MobilityConfig]:
|
||||||
configs = []
|
configs = []
|
||||||
for canvas_node in self.canvas_nodes.values():
|
for node in self.session.nodes.values():
|
||||||
if canvas_node.core_node.type != NodeType.WIRELESS_LAN:
|
if node.type != NodeType.WIRELESS_LAN:
|
||||||
continue
|
continue
|
||||||
if not canvas_node.mobility_config:
|
if not node.mobility_config:
|
||||||
continue
|
continue
|
||||||
config = ConfigOption.to_dict(canvas_node.mobility_config)
|
config = ConfigOption.to_dict(node.mobility_config)
|
||||||
node_id = canvas_node.core_node.id
|
|
||||||
mobility_config = mobility_pb2.MobilityConfig(
|
mobility_config = mobility_pb2.MobilityConfig(
|
||||||
node_id=node_id, config=config
|
node_id=node.id, config=config
|
||||||
)
|
)
|
||||||
configs.append(mobility_config)
|
configs.append(mobility_config)
|
||||||
return configs
|
return configs
|
||||||
|
|
||||||
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 canvas_node in self.canvas_nodes.values():
|
for node in self.session.nodes.values():
|
||||||
if canvas_node.core_node.type != NodeType.EMANE:
|
if node.type != NodeType.EMANE:
|
||||||
continue
|
continue
|
||||||
node_id = canvas_node.core_node.id
|
for key, config in node.emane_model_configs.items():
|
||||||
for key, config in canvas_node.emane_model_configs.items():
|
|
||||||
model, iface_id = key
|
model, iface_id = key
|
||||||
config = ConfigOption.to_dict(config)
|
config = ConfigOption.to_dict(config)
|
||||||
if iface_id is None:
|
if iface_id is None:
|
||||||
iface_id = -1
|
iface_id = -1
|
||||||
config_proto = emane_pb2.EmaneModelConfig(
|
config_proto = emane_pb2.EmaneModelConfig(
|
||||||
node_id=node_id, iface_id=iface_id, model=model, config=config
|
node_id=node.id, iface_id=iface_id, model=model, config=config
|
||||||
)
|
)
|
||||||
configs.append(config_proto)
|
configs.append(config_proto)
|
||||||
return configs
|
return configs
|
||||||
|
|
||||||
def get_service_configs_proto(self) -> List[services_pb2.ServiceConfig]:
|
def get_service_configs_proto(self) -> List[services_pb2.ServiceConfig]:
|
||||||
configs = []
|
configs = []
|
||||||
for canvas_node in self.canvas_nodes.values():
|
for node in self.session.nodes.values():
|
||||||
if not NodeUtils.is_container_node(canvas_node.core_node.type):
|
if not NodeUtils.is_container_node(node.type):
|
||||||
continue
|
continue
|
||||||
if not canvas_node.service_configs:
|
if not node.service_configs:
|
||||||
continue
|
continue
|
||||||
node_id = canvas_node.core_node.id
|
for name, config in node.service_configs.items():
|
||||||
for name, config in canvas_node.service_configs.items():
|
|
||||||
config_proto = services_pb2.ServiceConfig(
|
config_proto = services_pb2.ServiceConfig(
|
||||||
node_id=node_id,
|
node_id=node.id,
|
||||||
service=name,
|
service=name,
|
||||||
directories=config.dirs,
|
directories=config.dirs,
|
||||||
files=config.configs,
|
files=config.configs,
|
||||||
|
|
@ -1071,16 +976,15 @@ class CoreClient:
|
||||||
|
|
||||||
def get_service_file_configs_proto(self) -> List[services_pb2.ServiceFileConfig]:
|
def get_service_file_configs_proto(self) -> List[services_pb2.ServiceFileConfig]:
|
||||||
configs = []
|
configs = []
|
||||||
for canvas_node in self.canvas_nodes.values():
|
for node in self.session.nodes.values():
|
||||||
if not NodeUtils.is_container_node(canvas_node.core_node.type):
|
if not NodeUtils.is_container_node(node.type):
|
||||||
continue
|
continue
|
||||||
if not canvas_node.service_file_configs:
|
if not node.service_file_configs:
|
||||||
continue
|
continue
|
||||||
node_id = canvas_node.core_node.id
|
for service, file_configs in node.service_file_configs.items():
|
||||||
for service, file_configs in canvas_node.service_file_configs.items():
|
|
||||||
for file, data in file_configs.items():
|
for file, data in file_configs.items():
|
||||||
config_proto = services_pb2.ServiceFileConfig(
|
config_proto = services_pb2.ServiceFileConfig(
|
||||||
node_id=node_id, service=service, file=file, data=data
|
node_id=node.id, service=service, file=file, data=data
|
||||||
)
|
)
|
||||||
configs.append(config_proto)
|
configs.append(config_proto)
|
||||||
return configs
|
return configs
|
||||||
|
|
@ -1089,29 +993,27 @@ class CoreClient:
|
||||||
self
|
self
|
||||||
) -> List[configservices_pb2.ConfigServiceConfig]:
|
) -> List[configservices_pb2.ConfigServiceConfig]:
|
||||||
config_service_protos = []
|
config_service_protos = []
|
||||||
for canvas_node in self.canvas_nodes.values():
|
for node in self.session.nodes.values():
|
||||||
if not NodeUtils.is_container_node(canvas_node.core_node.type):
|
if not NodeUtils.is_container_node(node.type):
|
||||||
continue
|
continue
|
||||||
if not canvas_node.config_service_configs:
|
if not node.config_service_configs:
|
||||||
continue
|
continue
|
||||||
node_id = canvas_node.core_node.id
|
for name, service_config in node.config_service_configs.items():
|
||||||
for name, service_config in canvas_node.config_service_configs.items():
|
|
||||||
config = service_config.get("config", {})
|
|
||||||
config_proto = configservices_pb2.ConfigServiceConfig(
|
config_proto = configservices_pb2.ConfigServiceConfig(
|
||||||
node_id=node_id,
|
node_id=node.id,
|
||||||
name=name,
|
name=name,
|
||||||
templates=service_config["templates"],
|
templates=service_config.templates,
|
||||||
config=config,
|
config=service_config.config,
|
||||||
)
|
)
|
||||||
config_service_protos.append(config_proto)
|
config_service_protos.append(config_proto)
|
||||||
return config_service_protos
|
return config_service_protos
|
||||||
|
|
||||||
def run(self, node_id: int) -> str:
|
def run(self, node_id: int) -> str:
|
||||||
logging.info("running node(%s) cmd: %s", node_id, self.observer)
|
logging.info("running node(%s) cmd: %s", node_id, self.observer)
|
||||||
return self.client.node_command(self.session_id, node_id, self.observer).output
|
return self.client.node_command(self.session.id, node_id, self.observer).output
|
||||||
|
|
||||||
def get_wlan_config(self, node_id: int) -> Dict[str, ConfigOption]:
|
def get_wlan_config(self, node_id: int) -> Dict[str, ConfigOption]:
|
||||||
response = self.client.get_wlan_config(self.session_id, node_id)
|
response = self.client.get_wlan_config(self.session.id, node_id)
|
||||||
config = response.config
|
config = response.config
|
||||||
logging.debug(
|
logging.debug(
|
||||||
"get wlan configuration from node %s, result configuration: %s",
|
"get wlan configuration from node %s, result configuration: %s",
|
||||||
|
|
@ -1121,7 +1023,7 @@ class CoreClient:
|
||||||
return ConfigOption.from_dict(config)
|
return ConfigOption.from_dict(config)
|
||||||
|
|
||||||
def get_mobility_config(self, node_id: int) -> Dict[str, ConfigOption]:
|
def get_mobility_config(self, node_id: int) -> Dict[str, ConfigOption]:
|
||||||
response = self.client.get_mobility_config(self.session_id, node_id)
|
response = self.client.get_mobility_config(self.session.id, node_id)
|
||||||
config = response.config
|
config = response.config
|
||||||
logging.debug(
|
logging.debug(
|
||||||
"get mobility config from node %s, result configuration: %s",
|
"get mobility config from node %s, result configuration: %s",
|
||||||
|
|
@ -1136,7 +1038,7 @@ class CoreClient:
|
||||||
if iface_id is None:
|
if iface_id is None:
|
||||||
iface_id = -1
|
iface_id = -1
|
||||||
response = self.client.get_emane_model_config(
|
response = self.client.get_emane_model_config(
|
||||||
self.session_id, node_id, model, iface_id
|
self.session.id, node_id, model, iface_id
|
||||||
)
|
)
|
||||||
config = response.config
|
config = response.config
|
||||||
logging.debug(
|
logging.debug(
|
||||||
|
|
@ -1154,3 +1056,18 @@ class CoreClient:
|
||||||
logging.info("execute python script %s", response)
|
logging.info("execute python script %s", response)
|
||||||
if response.session_id != -1:
|
if response.session_id != -1:
|
||||||
self.join_session(response.session_id)
|
self.join_session(response.session_id)
|
||||||
|
|
||||||
|
def edit_link(self, link: Link) -> None:
|
||||||
|
iface1_id = link.iface1.id if link.iface1 else None
|
||||||
|
iface2_id = link.iface2.id if link.iface2 else None
|
||||||
|
response = self.client.edit_link(
|
||||||
|
self.session.id,
|
||||||
|
link.node1_id,
|
||||||
|
link.node2_id,
|
||||||
|
link.options.to_proto(),
|
||||||
|
iface1_id,
|
||||||
|
iface2_id,
|
||||||
|
source=GUI_SOURCE,
|
||||||
|
)
|
||||||
|
if not response.result:
|
||||||
|
logging.error("error editing link: %s", link)
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ class SizeAndScaleDialog(Dialog):
|
||||||
width, height = self.canvas.current_dimensions
|
width, height = self.canvas.current_dimensions
|
||||||
self.pixel_width: tk.IntVar = tk.IntVar(value=width)
|
self.pixel_width: tk.IntVar = tk.IntVar(value=width)
|
||||||
self.pixel_height: tk.IntVar = tk.IntVar(value=height)
|
self.pixel_height: tk.IntVar = tk.IntVar(value=height)
|
||||||
location = self.app.core.location
|
location = self.app.core.session.location
|
||||||
self.x: tk.DoubleVar = tk.DoubleVar(value=location.x)
|
self.x: tk.DoubleVar = tk.DoubleVar(value=location.x)
|
||||||
self.y: tk.DoubleVar = tk.DoubleVar(value=location.y)
|
self.y: tk.DoubleVar = tk.DoubleVar(value=location.y)
|
||||||
self.lat: tk.DoubleVar = tk.DoubleVar(value=location.lat)
|
self.lat: tk.DoubleVar = tk.DoubleVar(value=location.lat)
|
||||||
|
|
@ -192,7 +192,7 @@ class SizeAndScaleDialog(Dialog):
|
||||||
self.canvas.redraw_canvas((width, height))
|
self.canvas.redraw_canvas((width, height))
|
||||||
if self.canvas.wallpaper:
|
if self.canvas.wallpaper:
|
||||||
self.canvas.redraw_wallpaper()
|
self.canvas.redraw_wallpaper()
|
||||||
location = self.app.core.location
|
location = self.app.core.session.location
|
||||||
location.x = self.x.get()
|
location.x = self.x.get()
|
||||||
location.y = self.y.get()
|
location.y = self.y.get()
|
||||||
location.lat = self.lat.get()
|
location.lat = self.lat.get()
|
||||||
|
|
|
||||||
|
|
@ -11,28 +11,26 @@ import grpc
|
||||||
from core.gui.dialogs.dialog import Dialog
|
from core.gui.dialogs.dialog import Dialog
|
||||||
from core.gui.themes import FRAME_PAD, PADX, PADY
|
from core.gui.themes import FRAME_PAD, PADX, PADY
|
||||||
from core.gui.widgets import CodeText, ConfigFrame, ListboxScroll
|
from core.gui.widgets import CodeText, ConfigFrame, ListboxScroll
|
||||||
from core.gui.wrappers import ConfigOption, ServiceValidationMode
|
from core.gui.wrappers import (
|
||||||
|
ConfigOption,
|
||||||
|
ConfigServiceData,
|
||||||
|
Node,
|
||||||
|
ServiceValidationMode,
|
||||||
|
)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from core.gui.app import Application
|
from core.gui.app import Application
|
||||||
from core.gui.graph.node import CanvasNode
|
|
||||||
from core.gui.coreclient import CoreClient
|
from core.gui.coreclient import CoreClient
|
||||||
|
|
||||||
|
|
||||||
class ConfigServiceConfigDialog(Dialog):
|
class ConfigServiceConfigDialog(Dialog):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self, master: tk.BaseWidget, app: "Application", service_name: str, node: Node
|
||||||
master: tk.BaseWidget,
|
|
||||||
app: "Application",
|
|
||||||
service_name: str,
|
|
||||||
canvas_node: "CanvasNode",
|
|
||||||
node_id: int,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
title = f"{service_name} Config Service"
|
title = f"{service_name} Config Service"
|
||||||
super().__init__(app, title, master=master)
|
super().__init__(app, title, master=master)
|
||||||
self.core: "CoreClient" = app.core
|
self.core: "CoreClient" = app.core
|
||||||
self.canvas_node: "CanvasNode" = canvas_node
|
self.node: Node = node
|
||||||
self.node_id: int = node_id
|
|
||||||
self.service_name: str = service_name
|
self.service_name: str = service_name
|
||||||
self.radiovar: tk.IntVar = tk.IntVar()
|
self.radiovar: tk.IntVar = tk.IntVar()
|
||||||
self.radiovar.set(2)
|
self.radiovar.set(2)
|
||||||
|
|
@ -50,7 +48,7 @@ class ConfigServiceConfigDialog(Dialog):
|
||||||
self.validation_time: Optional[int] = None
|
self.validation_time: Optional[int] = None
|
||||||
self.validation_period: tk.StringVar = tk.StringVar()
|
self.validation_period: tk.StringVar = tk.StringVar()
|
||||||
self.modes: List[str] = []
|
self.modes: List[str] = []
|
||||||
self.mode_configs: Dict[str, str] = {}
|
self.mode_configs: Dict[str, Dict[str, str]] = {}
|
||||||
|
|
||||||
self.notebook: Optional[ttk.Notebook] = None
|
self.notebook: Optional[ttk.Notebook] = None
|
||||||
self.templates_combobox: Optional[ttk.Combobox] = None
|
self.templates_combobox: Optional[ttk.Combobox] = None
|
||||||
|
|
@ -91,25 +89,18 @@ class ConfigServiceConfigDialog(Dialog):
|
||||||
response = self.core.client.get_config_service_defaults(self.service_name)
|
response = self.core.client.get_config_service_defaults(self.service_name)
|
||||||
self.original_service_files = response.templates
|
self.original_service_files = response.templates
|
||||||
self.temp_service_files = dict(self.original_service_files)
|
self.temp_service_files = dict(self.original_service_files)
|
||||||
|
|
||||||
self.modes = sorted(x.name for x in response.modes)
|
self.modes = sorted(x.name for x in response.modes)
|
||||||
self.mode_configs = {x.name: x.config for x in response.modes}
|
self.mode_configs = {x.name: x.config for x in response.modes}
|
||||||
|
|
||||||
service_config = self.canvas_node.config_service_configs.get(
|
|
||||||
self.service_name, {}
|
|
||||||
)
|
|
||||||
self.config = ConfigOption.from_dict(response.config)
|
self.config = ConfigOption.from_dict(response.config)
|
||||||
self.default_config = {x.name: x.value for x in self.config.values()}
|
self.default_config = {x.name: x.value for x in self.config.values()}
|
||||||
custom_config = service_config.get("config")
|
service_config = self.node.config_service_configs.get(self.service_name)
|
||||||
if custom_config:
|
if service_config:
|
||||||
for key, value in custom_config.items():
|
for key, value in service_config.config.items():
|
||||||
self.config[key].value = value
|
self.config[key].value = value
|
||||||
logging.info("default config: %s", self.default_config)
|
logging.info("default config: %s", self.default_config)
|
||||||
|
for file, data in service_config.templates.items():
|
||||||
custom_templates = service_config.get("templates", {})
|
self.modified_files.add(file)
|
||||||
for file, data in custom_templates.items():
|
self.temp_service_files[file] = data
|
||||||
self.modified_files.add(file)
|
|
||||||
self.temp_service_files[file] = data
|
|
||||||
except grpc.RpcError as e:
|
except grpc.RpcError as e:
|
||||||
self.app.show_grpc_exception("Get Config Service Error", e)
|
self.app.show_grpc_exception("Get Config Service Error", e)
|
||||||
self.has_error = True
|
self.has_error = True
|
||||||
|
|
@ -313,20 +304,18 @@ class ConfigServiceConfigDialog(Dialog):
|
||||||
def click_apply(self) -> None:
|
def click_apply(self) -> None:
|
||||||
current_listbox = self.master.current.listbox
|
current_listbox = self.master.current.listbox
|
||||||
if not self.is_custom():
|
if not self.is_custom():
|
||||||
self.canvas_node.config_service_configs.pop(self.service_name, None)
|
self.node.config_service_configs.pop(self.service_name, None)
|
||||||
current_listbox.itemconfig(current_listbox.curselection()[0], bg="")
|
current_listbox.itemconfig(current_listbox.curselection()[0], bg="")
|
||||||
self.destroy()
|
self.destroy()
|
||||||
return
|
return
|
||||||
|
service_config = self.node.config_service_configs.get(self.service_name)
|
||||||
service_config = self.canvas_node.config_service_configs.setdefault(
|
if not service_config:
|
||||||
self.service_name, {}
|
service_config = ConfigServiceData()
|
||||||
)
|
|
||||||
if self.config_frame:
|
if self.config_frame:
|
||||||
self.config_frame.parse_config()
|
self.config_frame.parse_config()
|
||||||
service_config["config"] = {x.name: x.value for x in self.config.values()}
|
service_config.config = {x.name: x.value for x in self.config.values()}
|
||||||
templates_config = service_config.setdefault("templates", {})
|
|
||||||
for file in self.modified_files:
|
for file in self.modified_files:
|
||||||
templates_config[file] = self.temp_service_files[file]
|
service_config.templates[file] = self.temp_service_files[file]
|
||||||
all_current = current_listbox.get(0, tk.END)
|
all_current = current_listbox.get(0, tk.END)
|
||||||
current_listbox.itemconfig(all_current.index(self.service_name), bg="green")
|
current_listbox.itemconfig(all_current.index(self.service_name), bg="green")
|
||||||
self.destroy()
|
self.destroy()
|
||||||
|
|
@ -360,9 +349,9 @@ class ConfigServiceConfigDialog(Dialog):
|
||||||
return has_custom_templates or has_custom_config
|
return has_custom_templates or has_custom_config
|
||||||
|
|
||||||
def click_defaults(self) -> None:
|
def click_defaults(self) -> None:
|
||||||
self.canvas_node.config_service_configs.pop(self.service_name, None)
|
self.node.config_service_configs.pop(self.service_name, None)
|
||||||
logging.info(
|
logging.info(
|
||||||
"cleared config service config: %s", self.canvas_node.config_service_configs
|
"cleared config service config: %s", self.node.config_service_configs
|
||||||
)
|
)
|
||||||
self.temp_service_files = dict(self.original_service_files)
|
self.temp_service_files = dict(self.original_service_files)
|
||||||
filename = self.templates_combobox.get()
|
filename = self.templates_combobox.get()
|
||||||
|
|
|
||||||
|
|
@ -43,16 +43,15 @@ class CopyServiceConfigDialog(Dialog):
|
||||||
listbox_scroll = ListboxScroll(self.top)
|
listbox_scroll = ListboxScroll(self.top)
|
||||||
listbox_scroll.grid(sticky="nsew", pady=PADY)
|
listbox_scroll.grid(sticky="nsew", pady=PADY)
|
||||||
self.listbox = listbox_scroll.listbox
|
self.listbox = listbox_scroll.listbox
|
||||||
for canvas_node in self.app.canvas.nodes.values():
|
for node in self.app.core.session.nodes.values():
|
||||||
file_configs = canvas_node.service_file_configs.get(self.service)
|
file_configs = node.service_file_configs.get(self.service)
|
||||||
if not file_configs:
|
if not file_configs:
|
||||||
continue
|
continue
|
||||||
data = file_configs.get(self.file_name)
|
data = file_configs.get(self.file_name)
|
||||||
if not data:
|
if not data:
|
||||||
continue
|
continue
|
||||||
name = canvas_node.core_node.name
|
self.nodes[node.name] = node.id
|
||||||
self.nodes[name] = canvas_node.id
|
self.listbox.insert(tk.END, node.name)
|
||||||
self.listbox.insert(tk.END, name)
|
|
||||||
|
|
||||||
frame = ttk.Frame(self.top)
|
frame = ttk.Frame(self.top)
|
||||||
frame.grid(sticky="ew")
|
frame.grid(sticky="ew")
|
||||||
|
|
@ -70,9 +69,9 @@ class CopyServiceConfigDialog(Dialog):
|
||||||
if not selection:
|
if not selection:
|
||||||
return
|
return
|
||||||
name = self.listbox.get(selection)
|
name = self.listbox.get(selection)
|
||||||
canvas_node_id = self.nodes[name]
|
node_id = self.nodes[name]
|
||||||
canvas_node = self.app.canvas.nodes[canvas_node_id]
|
node = self.app.core.session.nodes[node_id]
|
||||||
data = canvas_node.service_file_configs[self.service][self.file_name]
|
data = node.service_file_configs[self.service][self.file_name]
|
||||||
self.dialog.temp_service_files[self.file_name] = data
|
self.dialog.temp_service_files[self.file_name] = data
|
||||||
self.dialog.modified_files.add(self.file_name)
|
self.dialog.modified_files.add(self.file_name)
|
||||||
self.dialog.service_file_data.text.delete(1.0, tk.END)
|
self.dialog.service_file_data.text.delete(1.0, tk.END)
|
||||||
|
|
@ -84,9 +83,9 @@ class CopyServiceConfigDialog(Dialog):
|
||||||
if not selection:
|
if not selection:
|
||||||
return
|
return
|
||||||
name = self.listbox.get(selection)
|
name = self.listbox.get(selection)
|
||||||
canvas_node_id = self.nodes[name]
|
node_id = self.nodes[name]
|
||||||
canvas_node = self.app.canvas.nodes[canvas_node_id]
|
node = self.app.core.session.nodes[node_id]
|
||||||
data = canvas_node.service_file_configs[self.service][self.file_name]
|
data = node.service_file_configs[self.service][self.file_name]
|
||||||
dialog = ViewConfigDialog(
|
dialog = ViewConfigDialog(
|
||||||
self.app, self, name, self.service, self.file_name, data
|
self.app, self, name, self.service, self.file_name, data
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ from core.gui.wrappers import ConfigOption, Node
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from core.gui.app import Application
|
from core.gui.app import Application
|
||||||
from core.gui.graph.node import CanvasNode
|
|
||||||
|
|
||||||
|
|
||||||
class GlobalEmaneDialog(Dialog):
|
class GlobalEmaneDialog(Dialog):
|
||||||
|
|
@ -29,8 +28,9 @@ class GlobalEmaneDialog(Dialog):
|
||||||
def draw(self) -> None:
|
def draw(self) -> None:
|
||||||
self.top.columnconfigure(0, weight=1)
|
self.top.columnconfigure(0, weight=1)
|
||||||
self.top.rowconfigure(0, weight=1)
|
self.top.rowconfigure(0, weight=1)
|
||||||
|
session = self.app.core.session
|
||||||
self.config_frame = ConfigFrame(
|
self.config_frame = ConfigFrame(
|
||||||
self.top, self.app, self.app.core.emane_config, self.enabled
|
self.top, self.app, session.emane_config, self.enabled
|
||||||
)
|
)
|
||||||
self.config_frame.draw_config()
|
self.config_frame.draw_config()
|
||||||
self.config_frame.grid(sticky="nsew", pady=PADY)
|
self.config_frame.grid(sticky="nsew", pady=PADY)
|
||||||
|
|
@ -58,24 +58,19 @@ class EmaneModelDialog(Dialog):
|
||||||
self,
|
self,
|
||||||
master: tk.BaseWidget,
|
master: tk.BaseWidget,
|
||||||
app: "Application",
|
app: "Application",
|
||||||
canvas_node: "CanvasNode",
|
node: Node,
|
||||||
model: str,
|
model: str,
|
||||||
iface_id: int = None,
|
iface_id: int = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
super().__init__(
|
super().__init__(app, f"{node.name} {model} Configuration", master=master)
|
||||||
app, f"{canvas_node.core_node.name} {model} Configuration", master=master
|
self.node: Node = node
|
||||||
)
|
|
||||||
self.canvas_node: "CanvasNode" = canvas_node
|
|
||||||
self.node: Node = canvas_node.core_node
|
|
||||||
self.model: str = f"emane_{model}"
|
self.model: str = f"emane_{model}"
|
||||||
self.iface_id: int = iface_id
|
self.iface_id: int = iface_id
|
||||||
self.config_frame: Optional[ConfigFrame] = None
|
self.config_frame: Optional[ConfigFrame] = None
|
||||||
self.enabled: bool = not self.app.core.is_runtime()
|
self.enabled: bool = not self.app.core.is_runtime()
|
||||||
self.has_error: bool = False
|
self.has_error: bool = False
|
||||||
try:
|
try:
|
||||||
config = self.canvas_node.emane_model_configs.get(
|
config = self.node.emane_model_configs.get((self.model, self.iface_id))
|
||||||
(self.model, self.iface_id)
|
|
||||||
)
|
|
||||||
if not config:
|
if not config:
|
||||||
config = self.app.core.get_emane_model_config(
|
config = self.app.core.get_emane_model_config(
|
||||||
self.node.id, self.model, self.iface_id
|
self.node.id, self.model, self.iface_id
|
||||||
|
|
@ -110,19 +105,18 @@ class EmaneModelDialog(Dialog):
|
||||||
def click_apply(self) -> None:
|
def click_apply(self) -> None:
|
||||||
self.config_frame.parse_config()
|
self.config_frame.parse_config()
|
||||||
key = (self.model, self.iface_id)
|
key = (self.model, self.iface_id)
|
||||||
self.canvas_node.emane_model_configs[key] = self.config
|
self.node.emane_model_configs[key] = self.config
|
||||||
self.destroy()
|
self.destroy()
|
||||||
|
|
||||||
|
|
||||||
class EmaneConfigDialog(Dialog):
|
class EmaneConfigDialog(Dialog):
|
||||||
def __init__(self, app: "Application", canvas_node: "CanvasNode") -> None:
|
def __init__(self, app: "Application", node: Node) -> None:
|
||||||
super().__init__(app, f"{canvas_node.core_node.name} EMANE Configuration")
|
super().__init__(app, f"{node.name} EMANE Configuration")
|
||||||
self.canvas_node: "CanvasNode" = canvas_node
|
self.node: Node = node
|
||||||
self.node: Node = canvas_node.core_node
|
|
||||||
self.radiovar: tk.IntVar = tk.IntVar()
|
self.radiovar: tk.IntVar = tk.IntVar()
|
||||||
self.radiovar.set(1)
|
self.radiovar.set(1)
|
||||||
self.emane_models: List[str] = [
|
self.emane_models: List[str] = [
|
||||||
x.split("_")[1] for x in self.app.core.emane_models
|
x.split("_")[1] for x in self.app.core.session.emane_models
|
||||||
]
|
]
|
||||||
model = self.node.emane.split("_")[1]
|
model = self.node.emane.split("_")[1]
|
||||||
self.emane_model: tk.StringVar = tk.StringVar(value=model)
|
self.emane_model: tk.StringVar = tk.StringVar(value=model)
|
||||||
|
|
@ -231,7 +225,7 @@ class EmaneConfigDialog(Dialog):
|
||||||
draw emane model configuration
|
draw emane model configuration
|
||||||
"""
|
"""
|
||||||
model_name = self.emane_model.get()
|
model_name = self.emane_model.get()
|
||||||
dialog = EmaneModelDialog(self, self.app, self.canvas_node, model_name)
|
dialog = EmaneModelDialog(self, self.app, self.node, model_name)
|
||||||
if not dialog.has_error:
|
if not dialog.has_error:
|
||||||
dialog.show()
|
dialog.show()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -87,22 +87,19 @@ class FindDialog(Dialog):
|
||||||
"""
|
"""
|
||||||
node_name = self.find_text.get().strip()
|
node_name = self.find_text.get().strip()
|
||||||
self.clear_treeview_items()
|
self.clear_treeview_items()
|
||||||
for node_id, node in sorted(
|
for node in self.app.core.session.nodes.values():
|
||||||
self.app.core.canvas_nodes.items(), key=lambda x: x[0]
|
name = node.name
|
||||||
):
|
|
||||||
name = node.core_node.name
|
|
||||||
if not node_name or node_name == name:
|
if not node_name or node_name == name:
|
||||||
pos_x = round(node.core_node.position.x, 1)
|
pos_x = round(node.position.x, 1)
|
||||||
pos_y = round(node.core_node.position.y, 1)
|
pos_y = round(node.position.y, 1)
|
||||||
# TODO: I am not sure what to insert for Detail column
|
# TODO: I am not sure what to insert for Detail column
|
||||||
# leaving it blank for now
|
# leaving it blank for now
|
||||||
self.tree.insert(
|
self.tree.insert(
|
||||||
"",
|
"",
|
||||||
tk.END,
|
tk.END,
|
||||||
text=str(node_id),
|
text=str(node.id),
|
||||||
values=(node_id, name, f"<{pos_x}, {pos_y}>", ""),
|
values=(node.id, name, f"<{pos_x}, {pos_y}>", ""),
|
||||||
)
|
)
|
||||||
|
|
||||||
results = self.tree.get_children("")
|
results = self.tree.get_children("")
|
||||||
if results:
|
if results:
|
||||||
self.tree.selection_set(results[0])
|
self.tree.selection_set(results[0])
|
||||||
|
|
@ -121,7 +118,7 @@ class FindDialog(Dialog):
|
||||||
if item:
|
if item:
|
||||||
self.app.canvas.delete("find")
|
self.app.canvas.delete("find")
|
||||||
node_id = int(self.tree.item(item, "text"))
|
node_id = int(self.tree.item(item, "text"))
|
||||||
canvas_node = self.app.core.canvas_nodes[node_id]
|
canvas_node = self.app.core.get_canvas_node(node_id)
|
||||||
|
|
||||||
x0, y0, x1, y1 = self.app.canvas.bbox(canvas_node.id)
|
x0, y0, x1, y1 = self.app.canvas.bbox(canvas_node.id)
|
||||||
dist = 5 * self.app.guiconfig.scale
|
dist = 5 * self.app.guiconfig.scale
|
||||||
|
|
|
||||||
|
|
@ -113,8 +113,9 @@ class HooksDialog(Dialog):
|
||||||
listbox_scroll.grid(sticky="nsew", pady=PADY)
|
listbox_scroll.grid(sticky="nsew", pady=PADY)
|
||||||
self.listbox = listbox_scroll.listbox
|
self.listbox = listbox_scroll.listbox
|
||||||
self.listbox.bind("<<ListboxSelect>>", self.select)
|
self.listbox.bind("<<ListboxSelect>>", self.select)
|
||||||
for hook_file in self.app.core.hooks:
|
session = self.app.core.session
|
||||||
self.listbox.insert(tk.END, hook_file)
|
for file in session.hooks:
|
||||||
|
self.listbox.insert(tk.END, file)
|
||||||
|
|
||||||
frame = ttk.Frame(self.top)
|
frame = ttk.Frame(self.top)
|
||||||
frame.grid(sticky="ew")
|
frame.grid(sticky="ew")
|
||||||
|
|
@ -138,20 +139,22 @@ class HooksDialog(Dialog):
|
||||||
dialog.show()
|
dialog.show()
|
||||||
hook = dialog.hook
|
hook = dialog.hook
|
||||||
if hook:
|
if hook:
|
||||||
self.app.core.hooks[hook.file] = hook
|
self.app.core.session.hooks[hook.file] = hook
|
||||||
self.listbox.insert(tk.END, hook.file)
|
self.listbox.insert(tk.END, hook.file)
|
||||||
|
|
||||||
def click_edit(self) -> None:
|
def click_edit(self) -> None:
|
||||||
hook = self.app.core.hooks.pop(self.selected)
|
session = self.app.core.session
|
||||||
|
hook = session.hooks.pop(self.selected)
|
||||||
dialog = HookDialog(self, self.app)
|
dialog = HookDialog(self, self.app)
|
||||||
dialog.set(hook)
|
dialog.set(hook)
|
||||||
dialog.show()
|
dialog.show()
|
||||||
self.app.core.hooks[hook.file] = hook
|
session.hooks[hook.file] = hook
|
||||||
self.listbox.delete(self.selected_index)
|
self.listbox.delete(self.selected_index)
|
||||||
self.listbox.insert(self.selected_index, hook.file)
|
self.listbox.insert(self.selected_index, hook.file)
|
||||||
|
|
||||||
def click_delete(self) -> None:
|
def click_delete(self) -> None:
|
||||||
del self.app.core.hooks[self.selected]
|
session = self.app.core.session
|
||||||
|
del session.hooks[self.selected]
|
||||||
self.listbox.delete(tk.ANCHOR)
|
self.listbox.delete(tk.ANCHOR)
|
||||||
self.edit_button.config(state=tk.DISABLED)
|
self.edit_button.config(state=tk.DISABLED)
|
||||||
self.delete_button.config(state=tk.DISABLED)
|
self.delete_button.config(state=tk.DISABLED)
|
||||||
|
|
|
||||||
|
|
@ -228,21 +228,15 @@ class LinkConfigurationDialog(Dialog):
|
||||||
bandwidth=bandwidth, jitter=jitter, delay=delay, dup=duplicate, loss=loss
|
bandwidth=bandwidth, jitter=jitter, delay=delay, dup=duplicate, loss=loss
|
||||||
)
|
)
|
||||||
link.options = options
|
link.options = options
|
||||||
|
iface1_id = link.iface1.id if link.iface1 else None
|
||||||
iface1_id = None
|
iface2_id = link.iface2.id if link.iface2 else None
|
||||||
if link.iface1:
|
|
||||||
iface1_id = link.iface1.id
|
|
||||||
iface2_id = None
|
|
||||||
if link.iface2:
|
|
||||||
iface2_id = link.iface2.id
|
|
||||||
|
|
||||||
if not self.is_symmetric:
|
if not self.is_symmetric:
|
||||||
link.options.unidirectional = True
|
link.options.unidirectional = True
|
||||||
asym_iface1 = None
|
asym_iface1 = None
|
||||||
if iface1_id:
|
if iface1_id is not None:
|
||||||
asym_iface1 = Interface(id=iface1_id)
|
asym_iface1 = Interface(id=iface1_id)
|
||||||
asym_iface2 = None
|
asym_iface2 = None
|
||||||
if iface2_id:
|
if iface2_id is not None:
|
||||||
asym_iface2 = Interface(id=iface2_id)
|
asym_iface2 = Interface(id=iface2_id)
|
||||||
down_bandwidth = get_int(self.down_bandwidth)
|
down_bandwidth = get_int(self.down_bandwidth)
|
||||||
down_jitter = get_int(self.down_jitter)
|
down_jitter = get_int(self.down_jitter)
|
||||||
|
|
@ -260,8 +254,8 @@ class LinkConfigurationDialog(Dialog):
|
||||||
self.edge.asymmetric_link = Link(
|
self.edge.asymmetric_link = Link(
|
||||||
node1_id=link.node2_id,
|
node1_id=link.node2_id,
|
||||||
node2_id=link.node1_id,
|
node2_id=link.node1_id,
|
||||||
iface1=asym_iface1,
|
iface1=asym_iface2,
|
||||||
iface2=asym_iface2,
|
iface2=asym_iface1,
|
||||||
options=options,
|
options=options,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
|
@ -269,24 +263,9 @@ class LinkConfigurationDialog(Dialog):
|
||||||
self.edge.asymmetric_link = None
|
self.edge.asymmetric_link = None
|
||||||
|
|
||||||
if self.app.core.is_runtime() and link.options:
|
if self.app.core.is_runtime() and link.options:
|
||||||
session_id = self.app.core.session_id
|
self.app.core.edit_link(link)
|
||||||
self.app.core.client.edit_link(
|
|
||||||
session_id,
|
|
||||||
link.node1_id,
|
|
||||||
link.node2_id,
|
|
||||||
link.options,
|
|
||||||
iface1_id,
|
|
||||||
iface2_id,
|
|
||||||
)
|
|
||||||
if self.edge.asymmetric_link:
|
if self.edge.asymmetric_link:
|
||||||
self.app.core.client.edit_link(
|
self.app.core.edit_link(self.edge.asymmetric_link)
|
||||||
session_id,
|
|
||||||
link.node2_id,
|
|
||||||
link.node1_id,
|
|
||||||
self.edge.asymmetric_link.options,
|
|
||||||
iface1_id,
|
|
||||||
iface2_id,
|
|
||||||
)
|
|
||||||
|
|
||||||
# update edge label
|
# update edge label
|
||||||
self.edge.draw_link_options()
|
self.edge.draw_link_options()
|
||||||
|
|
|
||||||
|
|
@ -13,18 +13,16 @@ from core.gui.wrappers import ConfigOption, Node
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from core.gui.app import Application
|
from core.gui.app import Application
|
||||||
from core.gui.graph.node import CanvasNode
|
|
||||||
|
|
||||||
|
|
||||||
class MobilityConfigDialog(Dialog):
|
class MobilityConfigDialog(Dialog):
|
||||||
def __init__(self, app: "Application", canvas_node: "CanvasNode") -> None:
|
def __init__(self, app: "Application", node: Node) -> None:
|
||||||
super().__init__(app, f"{canvas_node.core_node.name} Mobility Configuration")
|
super().__init__(app, f"{node.name} Mobility Configuration")
|
||||||
self.canvas_node: "CanvasNode" = canvas_node
|
self.node: Node = node
|
||||||
self.node: Node = canvas_node.core_node
|
|
||||||
self.config_frame: Optional[ConfigFrame] = None
|
self.config_frame: Optional[ConfigFrame] = None
|
||||||
self.has_error: bool = False
|
self.has_error: bool = False
|
||||||
try:
|
try:
|
||||||
config = self.canvas_node.mobility_config
|
config = self.node.mobility_config
|
||||||
if not config:
|
if not config:
|
||||||
config = self.app.core.get_mobility_config(self.node.id)
|
config = self.app.core.get_mobility_config(self.node.id)
|
||||||
self.config: Dict[str, ConfigOption] = config
|
self.config: Dict[str, ConfigOption] = config
|
||||||
|
|
@ -56,5 +54,5 @@ class MobilityConfigDialog(Dialog):
|
||||||
|
|
||||||
def click_apply(self) -> None:
|
def click_apply(self) -> None:
|
||||||
self.config_frame.parse_config()
|
self.config_frame.parse_config()
|
||||||
self.canvas_node.mobility_config = self.config
|
self.node.mobility_config = self.config
|
||||||
self.destroy()
|
self.destroy()
|
||||||
|
|
|
||||||
|
|
@ -1,38 +1,31 @@
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from tkinter import ttk
|
from tkinter import ttk
|
||||||
from typing import TYPE_CHECKING, Dict, Optional
|
from typing import TYPE_CHECKING, Optional
|
||||||
|
|
||||||
import grpc
|
import grpc
|
||||||
|
|
||||||
from core.gui.dialogs.dialog import Dialog
|
from core.gui.dialogs.dialog import Dialog
|
||||||
from core.gui.images import ImageEnum
|
from core.gui.images import ImageEnum
|
||||||
from core.gui.themes import PADX, PADY
|
from core.gui.themes import PADX, PADY
|
||||||
from core.gui.wrappers import ConfigOption, MobilityAction, Node
|
from core.gui.wrappers import MobilityAction, Node
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from core.gui.app import Application
|
from core.gui.app import Application
|
||||||
from core.gui.graph.node import CanvasNode
|
|
||||||
|
|
||||||
ICON_SIZE: int = 16
|
ICON_SIZE: int = 16
|
||||||
|
|
||||||
|
|
||||||
class MobilityPlayer:
|
class MobilityPlayer:
|
||||||
def __init__(
|
def __init__(self, app: "Application", node: Node) -> None:
|
||||||
self,
|
|
||||||
app: "Application",
|
|
||||||
canvas_node: "CanvasNode",
|
|
||||||
config: Dict[str, ConfigOption],
|
|
||||||
) -> None:
|
|
||||||
self.app: "Application" = app
|
self.app: "Application" = app
|
||||||
self.canvas_node: "CanvasNode" = canvas_node
|
self.node: Node = node
|
||||||
self.config: Dict[str, ConfigOption] = config
|
|
||||||
self.dialog: Optional[MobilityPlayerDialog] = None
|
self.dialog: Optional[MobilityPlayerDialog] = None
|
||||||
self.state: Optional[MobilityAction] = None
|
self.state: Optional[MobilityAction] = None
|
||||||
|
|
||||||
def show(self) -> None:
|
def show(self) -> None:
|
||||||
if self.dialog:
|
if self.dialog:
|
||||||
self.dialog.destroy()
|
self.dialog.destroy()
|
||||||
self.dialog = MobilityPlayerDialog(self.app, self.canvas_node, self.config)
|
self.dialog = MobilityPlayerDialog(self.app, self.node)
|
||||||
self.dialog.protocol("WM_DELETE_WINDOW", self.close)
|
self.dialog.protocol("WM_DELETE_WINDOW", self.close)
|
||||||
if self.state == MobilityAction.START:
|
if self.state == MobilityAction.START:
|
||||||
self.set_play()
|
self.set_play()
|
||||||
|
|
@ -64,20 +57,11 @@ class MobilityPlayer:
|
||||||
|
|
||||||
|
|
||||||
class MobilityPlayerDialog(Dialog):
|
class MobilityPlayerDialog(Dialog):
|
||||||
def __init__(
|
def __init__(self, app: "Application", node: Node) -> None:
|
||||||
self,
|
super().__init__(app, f"{node.name} Mobility Player", modal=False)
|
||||||
app: "Application",
|
|
||||||
canvas_node: "CanvasNode",
|
|
||||||
config: Dict[str, ConfigOption],
|
|
||||||
) -> None:
|
|
||||||
super().__init__(
|
|
||||||
app, f"{canvas_node.core_node.name} Mobility Player", modal=False
|
|
||||||
)
|
|
||||||
self.resizable(False, False)
|
self.resizable(False, False)
|
||||||
self.geometry("")
|
self.geometry("")
|
||||||
self.canvas_node: "CanvasNode" = canvas_node
|
self.node: Node = node
|
||||||
self.node: Node = canvas_node.core_node
|
|
||||||
self.config: Dict[str, ConfigOption] = config
|
|
||||||
self.play_button: Optional[ttk.Button] = None
|
self.play_button: Optional[ttk.Button] = None
|
||||||
self.pause_button: Optional[ttk.Button] = None
|
self.pause_button: Optional[ttk.Button] = None
|
||||||
self.stop_button: Optional[ttk.Button] = None
|
self.stop_button: Optional[ttk.Button] = None
|
||||||
|
|
@ -85,9 +69,10 @@ class MobilityPlayerDialog(Dialog):
|
||||||
self.draw()
|
self.draw()
|
||||||
|
|
||||||
def draw(self) -> None:
|
def draw(self) -> None:
|
||||||
|
config = self.node.mobility_config
|
||||||
self.top.columnconfigure(0, weight=1)
|
self.top.columnconfigure(0, weight=1)
|
||||||
|
|
||||||
file_name = self.config["file"].value
|
file_name = config["file"].value
|
||||||
label = ttk.Label(self.top, text=file_name)
|
label = ttk.Label(self.top, text=file_name)
|
||||||
label.grid(sticky="ew", pady=PADY)
|
label.grid(sticky="ew", pady=PADY)
|
||||||
|
|
||||||
|
|
@ -114,13 +99,13 @@ class MobilityPlayerDialog(Dialog):
|
||||||
self.stop_button.image = image
|
self.stop_button.image = image
|
||||||
self.stop_button.grid(row=0, column=2, sticky="ew", padx=PADX)
|
self.stop_button.grid(row=0, column=2, sticky="ew", padx=PADX)
|
||||||
|
|
||||||
loop = tk.IntVar(value=int(self.config["loop"].value == "1"))
|
loop = tk.IntVar(value=int(config["loop"].value == "1"))
|
||||||
checkbutton = ttk.Checkbutton(
|
checkbutton = ttk.Checkbutton(
|
||||||
frame, text="Loop?", variable=loop, state=tk.DISABLED
|
frame, text="Loop?", variable=loop, state=tk.DISABLED
|
||||||
)
|
)
|
||||||
checkbutton.grid(row=0, column=3, padx=PADX)
|
checkbutton.grid(row=0, column=3, padx=PADX)
|
||||||
|
|
||||||
rate = self.config["refresh_ms"].value
|
rate = config["refresh_ms"].value
|
||||||
label = ttk.Label(frame, text=f"rate {rate} ms")
|
label = ttk.Label(frame, text=f"rate {rate} ms")
|
||||||
label.grid(row=0, column=4)
|
label.grid(row=0, column=4)
|
||||||
|
|
||||||
|
|
@ -146,7 +131,7 @@ class MobilityPlayerDialog(Dialog):
|
||||||
|
|
||||||
def click_play(self) -> None:
|
def click_play(self) -> None:
|
||||||
self.set_play()
|
self.set_play()
|
||||||
session_id = self.app.core.session_id
|
session_id = self.app.core.session.id
|
||||||
try:
|
try:
|
||||||
self.app.core.client.mobility_action(
|
self.app.core.client.mobility_action(
|
||||||
session_id, self.node.id, MobilityAction.START.value
|
session_id, self.node.id, MobilityAction.START.value
|
||||||
|
|
@ -156,7 +141,7 @@ class MobilityPlayerDialog(Dialog):
|
||||||
|
|
||||||
def click_pause(self) -> None:
|
def click_pause(self) -> None:
|
||||||
self.set_pause()
|
self.set_pause()
|
||||||
session_id = self.app.core.session_id
|
session_id = self.app.core.session.id
|
||||||
try:
|
try:
|
||||||
self.app.core.client.mobility_action(
|
self.app.core.client.mobility_action(
|
||||||
session_id, self.node.id, MobilityAction.PAUSE.value
|
session_id, self.node.id, MobilityAction.PAUSE.value
|
||||||
|
|
@ -166,7 +151,7 @@ class MobilityPlayerDialog(Dialog):
|
||||||
|
|
||||||
def click_stop(self) -> None:
|
def click_stop(self) -> None:
|
||||||
self.set_stop()
|
self.set_stop()
|
||||||
session_id = self.app.core.session_id
|
session_id = self.app.core.session.id
|
||||||
try:
|
try:
|
||||||
self.app.core.client.mobility_action(
|
self.app.core.client.mobility_action(
|
||||||
session_id, self.node.id, MobilityAction.STOP.value
|
session_id, self.node.id, MobilityAction.STOP.value
|
||||||
|
|
|
||||||
|
|
@ -10,25 +10,24 @@ from core.gui.dialogs.configserviceconfig import ConfigServiceConfigDialog
|
||||||
from core.gui.dialogs.dialog import Dialog
|
from core.gui.dialogs.dialog import Dialog
|
||||||
from core.gui.themes import FRAME_PAD, PADX, PADY
|
from core.gui.themes import FRAME_PAD, PADX, PADY
|
||||||
from core.gui.widgets import CheckboxList, ListboxScroll
|
from core.gui.widgets import CheckboxList, ListboxScroll
|
||||||
|
from core.gui.wrappers import Node
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from core.gui.app import Application
|
from core.gui.app import Application
|
||||||
from core.gui.graph.node import CanvasNode
|
|
||||||
|
|
||||||
|
|
||||||
class NodeConfigServiceDialog(Dialog):
|
class NodeConfigServiceDialog(Dialog):
|
||||||
def __init__(
|
def __init__(
|
||||||
self, app: "Application", canvas_node: "CanvasNode", services: Set[str] = None
|
self, app: "Application", node: Node, services: Set[str] = None
|
||||||
) -> None:
|
) -> None:
|
||||||
title = f"{canvas_node.core_node.name} Config Services"
|
title = f"{node.name} Config Services"
|
||||||
super().__init__(app, title)
|
super().__init__(app, title)
|
||||||
self.canvas_node: "CanvasNode" = canvas_node
|
self.node: Node = node
|
||||||
self.node_id: int = canvas_node.core_node.id
|
|
||||||
self.groups: Optional[ListboxScroll] = None
|
self.groups: Optional[ListboxScroll] = None
|
||||||
self.services: Optional[CheckboxList] = None
|
self.services: Optional[CheckboxList] = None
|
||||||
self.current: Optional[ListboxScroll] = None
|
self.current: Optional[ListboxScroll] = None
|
||||||
if services is None:
|
if services is None:
|
||||||
services = set(canvas_node.core_node.config_services)
|
services = set(node.config_services)
|
||||||
self.current_services: Set[str] = services
|
self.current_services: Set[str] = services
|
||||||
self.draw()
|
self.draw()
|
||||||
|
|
||||||
|
|
@ -102,7 +101,7 @@ class NodeConfigServiceDialog(Dialog):
|
||||||
elif not var.get() and name in self.current_services:
|
elif not var.get() and name in self.current_services:
|
||||||
self.current_services.remove(name)
|
self.current_services.remove(name)
|
||||||
self.draw_current_services()
|
self.draw_current_services()
|
||||||
self.canvas_node.core_node.config_services[:] = self.current_services
|
self.node.config_services[:] = self.current_services
|
||||||
|
|
||||||
def click_configure(self) -> None:
|
def click_configure(self) -> None:
|
||||||
current_selection = self.current.listbox.curselection()
|
current_selection = self.current.listbox.curselection()
|
||||||
|
|
@ -111,8 +110,7 @@ class NodeConfigServiceDialog(Dialog):
|
||||||
self,
|
self,
|
||||||
self.app,
|
self.app,
|
||||||
self.current.listbox.get(current_selection[0]),
|
self.current.listbox.get(current_selection[0]),
|
||||||
self.canvas_node,
|
self.node,
|
||||||
self.node_id,
|
|
||||||
)
|
)
|
||||||
if not dialog.has_error:
|
if not dialog.has_error:
|
||||||
dialog.show()
|
dialog.show()
|
||||||
|
|
@ -132,10 +130,8 @@ class NodeConfigServiceDialog(Dialog):
|
||||||
self.current.listbox.itemconfig(tk.END, bg="green")
|
self.current.listbox.itemconfig(tk.END, bg="green")
|
||||||
|
|
||||||
def click_save(self) -> None:
|
def click_save(self) -> None:
|
||||||
self.canvas_node.core_node.config_services[:] = self.current_services
|
self.node.config_services[:] = self.current_services
|
||||||
logging.info(
|
logging.info("saved node config services: %s", self.node.config_services)
|
||||||
"saved node config services: %s", self.canvas_node.core_node.config_services
|
|
||||||
)
|
|
||||||
self.destroy()
|
self.destroy()
|
||||||
|
|
||||||
def click_cancel(self) -> None:
|
def click_cancel(self) -> None:
|
||||||
|
|
@ -154,4 +150,4 @@ class NodeConfigServiceDialog(Dialog):
|
||||||
return
|
return
|
||||||
|
|
||||||
def is_custom_service(self, service: str) -> bool:
|
def is_custom_service(self, service: str) -> bool:
|
||||||
return service in self.canvas_node.config_service_configs
|
return service in self.node.config_service_configs
|
||||||
|
|
|
||||||
|
|
@ -9,22 +9,21 @@ from core.gui.dialogs.dialog import Dialog
|
||||||
from core.gui.dialogs.serviceconfig import ServiceConfigDialog
|
from core.gui.dialogs.serviceconfig import ServiceConfigDialog
|
||||||
from core.gui.themes import FRAME_PAD, PADX, PADY
|
from core.gui.themes import FRAME_PAD, PADX, PADY
|
||||||
from core.gui.widgets import CheckboxList, ListboxScroll
|
from core.gui.widgets import CheckboxList, ListboxScroll
|
||||||
|
from core.gui.wrappers import Node
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from core.gui.app import Application
|
from core.gui.app import Application
|
||||||
from core.gui.graph.node import CanvasNode
|
|
||||||
|
|
||||||
|
|
||||||
class NodeServiceDialog(Dialog):
|
class NodeServiceDialog(Dialog):
|
||||||
def __init__(self, app: "Application", canvas_node: "CanvasNode") -> None:
|
def __init__(self, app: "Application", node: Node) -> None:
|
||||||
title = f"{canvas_node.core_node.name} Services"
|
title = f"{node.name} Services"
|
||||||
super().__init__(app, title)
|
super().__init__(app, title)
|
||||||
self.canvas_node: "CanvasNode" = canvas_node
|
self.node: Node = node
|
||||||
self.node_id: int = canvas_node.core_node.id
|
|
||||||
self.groups: Optional[ListboxScroll] = None
|
self.groups: Optional[ListboxScroll] = None
|
||||||
self.services: Optional[CheckboxList] = None
|
self.services: Optional[CheckboxList] = None
|
||||||
self.current: Optional[ListboxScroll] = None
|
self.current: Optional[ListboxScroll] = None
|
||||||
services = set(canvas_node.core_node.services)
|
services = set(node.services)
|
||||||
self.current_services: Set[str] = services
|
self.current_services: Set[str] = services
|
||||||
self.draw()
|
self.draw()
|
||||||
|
|
||||||
|
|
@ -104,7 +103,7 @@ class NodeServiceDialog(Dialog):
|
||||||
self.current.listbox.insert(tk.END, name)
|
self.current.listbox.insert(tk.END, name)
|
||||||
if self.is_custom_service(name):
|
if self.is_custom_service(name):
|
||||||
self.current.listbox.itemconfig(tk.END, bg="green")
|
self.current.listbox.itemconfig(tk.END, bg="green")
|
||||||
self.canvas_node.core_node.services[:] = self.current_services
|
self.node.services = self.current_services.copy()
|
||||||
|
|
||||||
def click_configure(self) -> None:
|
def click_configure(self) -> None:
|
||||||
current_selection = self.current.listbox.curselection()
|
current_selection = self.current.listbox.curselection()
|
||||||
|
|
@ -113,8 +112,7 @@ class NodeServiceDialog(Dialog):
|
||||||
self,
|
self,
|
||||||
self.app,
|
self.app,
|
||||||
self.current.listbox.get(current_selection[0]),
|
self.current.listbox.get(current_selection[0]),
|
||||||
self.canvas_node,
|
self.node,
|
||||||
self.node_id,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# if error occurred when creating ServiceConfigDialog, don't show the dialog
|
# if error occurred when creating ServiceConfigDialog, don't show the dialog
|
||||||
|
|
@ -128,8 +126,7 @@ class NodeServiceDialog(Dialog):
|
||||||
)
|
)
|
||||||
|
|
||||||
def click_save(self) -> None:
|
def click_save(self) -> None:
|
||||||
core_node = self.canvas_node.core_node
|
self.node.services[:] = self.current_services
|
||||||
core_node.services[:] = self.current_services
|
|
||||||
self.destroy()
|
self.destroy()
|
||||||
|
|
||||||
def click_remove(self) -> None:
|
def click_remove(self) -> None:
|
||||||
|
|
@ -144,6 +141,6 @@ class NodeServiceDialog(Dialog):
|
||||||
return
|
return
|
||||||
|
|
||||||
def is_custom_service(self, service: str) -> bool:
|
def is_custom_service(self, service: str) -> bool:
|
||||||
has_service_config = service in self.canvas_node.service_configs
|
has_service_config = service in self.node.service_configs
|
||||||
has_file_config = service in self.canvas_node.service_file_configs
|
has_file_config = service in self.node.service_file_configs
|
||||||
return has_service_config or has_file_config
|
return has_service_config or has_file_config
|
||||||
|
|
|
||||||
|
|
@ -25,9 +25,9 @@ class RunToolDialog(Dialog):
|
||||||
"""
|
"""
|
||||||
store all CORE nodes (nodes that execute commands) from all existing nodes
|
store all CORE nodes (nodes that execute commands) from all existing nodes
|
||||||
"""
|
"""
|
||||||
for nid, node in self.app.core.canvas_nodes.items():
|
for node in self.app.core.session.nodes.values():
|
||||||
if NodeUtils.is_container_node(node.core_node.type):
|
if NodeUtils.is_container_node(node.type):
|
||||||
self.executable_nodes[node.core_node.name] = nid
|
self.executable_nodes[node.name] = node.id
|
||||||
|
|
||||||
def draw(self) -> None:
|
def draw(self) -> None:
|
||||||
self.top.rowconfigure(0, weight=1)
|
self.top.rowconfigure(0, weight=1)
|
||||||
|
|
@ -107,7 +107,7 @@ class RunToolDialog(Dialog):
|
||||||
node_name = self.node_list.listbox.get(selection)
|
node_name = self.node_list.listbox.get(selection)
|
||||||
node_id = self.executable_nodes[node_name]
|
node_id = self.executable_nodes[node_name]
|
||||||
response = self.app.core.client.node_command(
|
response = self.app.core.client.node_command(
|
||||||
self.app.core.session_id, node_id, command
|
self.app.core.session.id, node_id, command
|
||||||
)
|
)
|
||||||
self.result.text.insert(
|
self.result.text.insert(
|
||||||
tk.END, f"> {node_name} > {command}:\n{response.output}\n"
|
tk.END, f"> {node_name} > {command}:\n{response.output}\n"
|
||||||
|
|
|
||||||
|
|
@ -12,11 +12,10 @@ from core.gui.dialogs.dialog import Dialog
|
||||||
from core.gui.images import ImageEnum, Images
|
from core.gui.images import ImageEnum, Images
|
||||||
from core.gui.themes import FRAME_PAD, PADX, PADY
|
from core.gui.themes import FRAME_PAD, PADX, PADY
|
||||||
from core.gui.widgets import CodeText, ListboxScroll
|
from core.gui.widgets import CodeText, ListboxScroll
|
||||||
from core.gui.wrappers import NodeServiceData, ServiceValidationMode
|
from core.gui.wrappers import Node, NodeServiceData, ServiceValidationMode
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from core.gui.app import Application
|
from core.gui.app import Application
|
||||||
from core.gui.graph.node import CanvasNode
|
|
||||||
from core.gui.coreclient import CoreClient
|
from core.gui.coreclient import CoreClient
|
||||||
|
|
||||||
ICON_SIZE: int = 16
|
ICON_SIZE: int = 16
|
||||||
|
|
@ -24,18 +23,12 @@ ICON_SIZE: int = 16
|
||||||
|
|
||||||
class ServiceConfigDialog(Dialog):
|
class ServiceConfigDialog(Dialog):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self, master: tk.BaseWidget, app: "Application", service_name: str, node: Node
|
||||||
master: tk.BaseWidget,
|
|
||||||
app: "Application",
|
|
||||||
service_name: str,
|
|
||||||
canvas_node: "CanvasNode",
|
|
||||||
node_id: int,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
title = f"{service_name} Service"
|
title = f"{service_name} Service"
|
||||||
super().__init__(app, title, master=master)
|
super().__init__(app, title, master=master)
|
||||||
self.core: "CoreClient" = app.core
|
self.core: "CoreClient" = app.core
|
||||||
self.canvas_node: "CanvasNode" = canvas_node
|
self.node: Node = node
|
||||||
self.node_id: int = node_id
|
|
||||||
self.service_name: str = service_name
|
self.service_name: str = service_name
|
||||||
self.radiovar: tk.IntVar = tk.IntVar(value=2)
|
self.radiovar: tk.IntVar = tk.IntVar(value=2)
|
||||||
self.metadata: str = ""
|
self.metadata: str = ""
|
||||||
|
|
@ -84,15 +77,13 @@ class ServiceConfigDialog(Dialog):
|
||||||
try:
|
try:
|
||||||
self.app.core.create_nodes_and_links()
|
self.app.core.create_nodes_and_links()
|
||||||
default_config = self.app.core.get_node_service(
|
default_config = self.app.core.get_node_service(
|
||||||
self.node_id, self.service_name
|
self.node.id, self.service_name
|
||||||
)
|
)
|
||||||
self.default_startup = default_config.startup[:]
|
self.default_startup = default_config.startup[:]
|
||||||
self.default_validate = default_config.validate[:]
|
self.default_validate = default_config.validate[:]
|
||||||
self.default_shutdown = default_config.shutdown[:]
|
self.default_shutdown = default_config.shutdown[:]
|
||||||
self.default_directories = default_config.dirs[:]
|
self.default_directories = default_config.dirs[:]
|
||||||
custom_service_config = self.canvas_node.service_configs.get(
|
custom_service_config = self.node.service_configs.get(self.service_name)
|
||||||
self.service_name
|
|
||||||
)
|
|
||||||
self.default_config = default_config
|
self.default_config = default_config
|
||||||
service_config = (
|
service_config = (
|
||||||
custom_service_config if custom_service_config else default_config
|
custom_service_config if custom_service_config else default_config
|
||||||
|
|
@ -109,15 +100,13 @@ class ServiceConfigDialog(Dialog):
|
||||||
self.temp_directories = service_config.dirs[:]
|
self.temp_directories = service_config.dirs[:]
|
||||||
self.original_service_files = {
|
self.original_service_files = {
|
||||||
x: self.app.core.get_node_service_file(
|
x: self.app.core.get_node_service_file(
|
||||||
self.node_id, self.service_name, x
|
self.node.id, self.service_name, x
|
||||||
)
|
)
|
||||||
for x in default_config.configs
|
for x in default_config.configs
|
||||||
}
|
}
|
||||||
self.temp_service_files = dict(self.original_service_files)
|
self.temp_service_files = dict(self.original_service_files)
|
||||||
|
|
||||||
file_configs = self.canvas_node.service_file_configs.get(
|
file_configs = self.node.service_file_configs.get(self.service_name, {})
|
||||||
self.service_name, {}
|
|
||||||
)
|
|
||||||
for file, data in file_configs.items():
|
for file, data in file_configs.items():
|
||||||
self.temp_service_files[file] = data
|
self.temp_service_files[file] = data
|
||||||
except grpc.RpcError as e:
|
except grpc.RpcError as e:
|
||||||
|
|
@ -453,7 +442,7 @@ class ServiceConfigDialog(Dialog):
|
||||||
and not self.has_new_files()
|
and not self.has_new_files()
|
||||||
and not self.is_custom_directory()
|
and not self.is_custom_directory()
|
||||||
):
|
):
|
||||||
self.canvas_node.service_configs.pop(self.service_name, None)
|
self.node.service_configs.pop(self.service_name, None)
|
||||||
self.current_service_color("")
|
self.current_service_color("")
|
||||||
self.destroy()
|
self.destroy()
|
||||||
return
|
return
|
||||||
|
|
@ -466,7 +455,7 @@ class ServiceConfigDialog(Dialog):
|
||||||
):
|
):
|
||||||
startup, validate, shutdown = self.get_commands()
|
startup, validate, shutdown = self.get_commands()
|
||||||
config = self.core.set_node_service(
|
config = self.core.set_node_service(
|
||||||
self.node_id,
|
self.node.id,
|
||||||
self.service_name,
|
self.service_name,
|
||||||
dirs=self.temp_directories,
|
dirs=self.temp_directories,
|
||||||
files=list(self.filename_combobox["values"]),
|
files=list(self.filename_combobox["values"]),
|
||||||
|
|
@ -474,15 +463,15 @@ class ServiceConfigDialog(Dialog):
|
||||||
validations=validate,
|
validations=validate,
|
||||||
shutdowns=shutdown,
|
shutdowns=shutdown,
|
||||||
)
|
)
|
||||||
self.canvas_node.service_configs[self.service_name] = config
|
self.node.service_configs[self.service_name] = config
|
||||||
for file in self.modified_files:
|
for file in self.modified_files:
|
||||||
file_configs = self.canvas_node.service_file_configs.setdefault(
|
file_configs = self.node.service_file_configs.setdefault(
|
||||||
self.service_name, {}
|
self.service_name, {}
|
||||||
)
|
)
|
||||||
file_configs[file] = self.temp_service_files[file]
|
file_configs[file] = self.temp_service_files[file]
|
||||||
# TODO: check if this is really needed
|
# TODO: check if this is really needed
|
||||||
self.app.core.set_node_service_file(
|
self.app.core.set_node_service_file(
|
||||||
self.node_id, self.service_name, file, self.temp_service_files[file]
|
self.node.id, self.service_name, file, self.temp_service_files[file]
|
||||||
)
|
)
|
||||||
self.current_service_color("green")
|
self.current_service_color("green")
|
||||||
except grpc.RpcError as e:
|
except grpc.RpcError as e:
|
||||||
|
|
@ -526,8 +515,8 @@ class ServiceConfigDialog(Dialog):
|
||||||
clears out any custom configuration permanently
|
clears out any custom configuration permanently
|
||||||
"""
|
"""
|
||||||
# clear coreclient data
|
# clear coreclient data
|
||||||
self.canvas_node.service_configs.pop(self.service_name, None)
|
self.node.service_configs.pop(self.service_name, None)
|
||||||
file_configs = self.canvas_node.service_file_configs.pop(self.service_name, {})
|
file_configs = self.node.service_file_configs.pop(self.service_name, {})
|
||||||
file_configs.pop(self.service_name, None)
|
file_configs.pop(self.service_name, None)
|
||||||
self.temp_service_files = dict(self.original_service_files)
|
self.temp_service_files = dict(self.original_service_files)
|
||||||
self.modified_files.clear()
|
self.modified_files.clear()
|
||||||
|
|
@ -564,9 +553,8 @@ class ServiceConfigDialog(Dialog):
|
||||||
|
|
||||||
def click_copy(self) -> None:
|
def click_copy(self) -> None:
|
||||||
file_name = self.filename_combobox.get()
|
file_name = self.filename_combobox.get()
|
||||||
name = self.canvas_node.core_node.name
|
|
||||||
dialog = CopyServiceConfigDialog(
|
dialog = CopyServiceConfigDialog(
|
||||||
self.app, self, name, self.service_name, file_name
|
self.app, self, self.node.name, self.service_name, file_name
|
||||||
)
|
)
|
||||||
dialog.show()
|
dialog.show()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ class SessionOptionsDialog(Dialog):
|
||||||
|
|
||||||
def get_config(self) -> Dict[str, ConfigOption]:
|
def get_config(self) -> Dict[str, ConfigOption]:
|
||||||
try:
|
try:
|
||||||
session_id = self.app.core.session_id
|
session_id = self.app.core.session.id
|
||||||
response = self.app.core.client.get_session_options(session_id)
|
response = self.app.core.client.get_session_options(session_id)
|
||||||
return ConfigOption.from_dict(response.config)
|
return ConfigOption.from_dict(response.config)
|
||||||
except grpc.RpcError as e:
|
except grpc.RpcError as e:
|
||||||
|
|
@ -54,7 +54,7 @@ class SessionOptionsDialog(Dialog):
|
||||||
def save(self) -> None:
|
def save(self) -> None:
|
||||||
config = self.config_frame.parse_config()
|
config = self.config_frame.parse_config()
|
||||||
try:
|
try:
|
||||||
session_id = self.app.core.session_id
|
session_id = self.app.core.session.id
|
||||||
response = self.app.core.client.set_session_options(session_id, config)
|
response = self.app.core.client.set_session_options(session_id, config)
|
||||||
logging.info("saved session config: %s", response)
|
logging.info("saved session config: %s", response)
|
||||||
except grpc.RpcError as e:
|
except grpc.RpcError as e:
|
||||||
|
|
|
||||||
|
|
@ -201,7 +201,7 @@ class SessionsDialog(Dialog):
|
||||||
logging.debug("delete session: %s", self.selected_session)
|
logging.debug("delete session: %s", self.selected_session)
|
||||||
self.tree.delete(self.selected_id)
|
self.tree.delete(self.selected_id)
|
||||||
self.app.core.delete_session(self.selected_session)
|
self.app.core.delete_session(self.selected_session)
|
||||||
if self.selected_session == self.app.core.session_id:
|
if self.selected_session == self.app.core.session.id:
|
||||||
self.click_new()
|
self.click_new()
|
||||||
self.destroy()
|
self.destroy()
|
||||||
self.click_select()
|
self.click_select()
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ class WlanConfigDialog(Dialog):
|
||||||
self.ranges: Dict[int, int] = {}
|
self.ranges: Dict[int, int] = {}
|
||||||
self.positive_int: int = self.app.master.register(self.validate_and_update)
|
self.positive_int: int = self.app.master.register(self.validate_and_update)
|
||||||
try:
|
try:
|
||||||
config = self.canvas_node.wlan_config
|
config = self.node.wlan_config
|
||||||
if not config:
|
if not config:
|
||||||
config = self.app.core.get_wlan_config(self.node.id)
|
config = self.app.core.get_wlan_config(self.node.id)
|
||||||
self.config: Dict[str, ConfigOption] = config
|
self.config: Dict[str, ConfigOption] = config
|
||||||
|
|
@ -83,9 +83,9 @@ class WlanConfigDialog(Dialog):
|
||||||
retrieve user's wlan configuration and store the new configuration values
|
retrieve user's wlan configuration and store the new configuration values
|
||||||
"""
|
"""
|
||||||
config = self.config_frame.parse_config()
|
config = self.config_frame.parse_config()
|
||||||
self.canvas_node.wlan_config = self.config
|
self.node.wlan_config = self.config
|
||||||
if self.app.core.is_runtime():
|
if self.app.core.is_runtime():
|
||||||
session_id = self.app.core.session_id
|
session_id = self.app.core.session.id
|
||||||
self.app.core.client.set_wlan_config(session_id, self.node.id, config)
|
self.app.core.client.set_wlan_config(session_id, self.node.id, config)
|
||||||
self.remove_ranges()
|
self.remove_ranges()
|
||||||
self.destroy()
|
self.destroy()
|
||||||
|
|
|
||||||
|
|
@ -34,10 +34,8 @@ class EdgeInfoFrame(InfoFrameBase):
|
||||||
self.columnconfigure(0, weight=1)
|
self.columnconfigure(0, weight=1)
|
||||||
link = self.edge.link
|
link = self.edge.link
|
||||||
options = link.options
|
options = link.options
|
||||||
src_canvas_node = self.app.core.canvas_nodes[link.node1_id]
|
src_node = self.app.core.session.nodes[link.node1_id]
|
||||||
src_node = src_canvas_node.core_node
|
dst_node = self.app.core.session.nodes[link.node2_id]
|
||||||
dst_canvas_node = self.app.core.canvas_nodes[link.node2_id]
|
|
||||||
dst_node = dst_canvas_node.core_node
|
|
||||||
|
|
||||||
frame = DetailsFrame(self)
|
frame = DetailsFrame(self)
|
||||||
frame.grid(sticky="ew")
|
frame.grid(sticky="ew")
|
||||||
|
|
@ -81,9 +79,9 @@ class WirelessEdgeInfoFrame(InfoFrameBase):
|
||||||
|
|
||||||
def draw(self) -> None:
|
def draw(self) -> None:
|
||||||
link = self.edge.link
|
link = self.edge.link
|
||||||
src_canvas_node = self.app.core.canvas_nodes[link.node1_id]
|
src_canvas_node = self.app.canvas.nodes[self.edge.src]
|
||||||
src_node = src_canvas_node.core_node
|
src_node = src_canvas_node.core_node
|
||||||
dst_canvas_node = self.app.core.canvas_nodes[link.node2_id]
|
dst_canvas_node = self.app.canvas.nodes[self.edge.dst]
|
||||||
dst_node = dst_canvas_node.core_node
|
dst_node = dst_canvas_node.core_node
|
||||||
|
|
||||||
# find interface for each node connected to network
|
# find interface for each node connected to network
|
||||||
|
|
|
||||||
|
|
@ -262,7 +262,7 @@ class CanvasGraph(tk.Canvas):
|
||||||
edge = self.edges.get(token)
|
edge = self.edges.get(token)
|
||||||
if not edge:
|
if not edge:
|
||||||
return
|
return
|
||||||
edge.link.options.CopyFrom(link.options)
|
edge.link.options = deepcopy(link.options)
|
||||||
|
|
||||||
def add_wireless_edge(self, src: CanvasNode, dst: CanvasNode, link: Link) -> None:
|
def add_wireless_edge(self, src: CanvasNode, dst: CanvasNode, link: Link) -> None:
|
||||||
network_id = link.network_id if link.network_id else None
|
network_id = link.network_id if link.network_id else None
|
||||||
|
|
@ -311,10 +311,7 @@ class CanvasGraph(tk.Canvas):
|
||||||
edge.middle_label_text(link.label)
|
edge.middle_label_text(link.label)
|
||||||
|
|
||||||
def add_core_node(self, core_node: Node) -> None:
|
def add_core_node(self, core_node: Node) -> None:
|
||||||
if core_node.id in self.core.canvas_nodes:
|
logging.debug("adding node: %s", core_node)
|
||||||
logging.error("core node already exists: %s", core_node)
|
|
||||||
return
|
|
||||||
logging.debug("adding node %s", core_node)
|
|
||||||
# if the gui can't find node's image, default to the "edit-node" image
|
# if the gui can't find node's image, default to the "edit-node" image
|
||||||
image = NodeUtils.node_image(core_node, self.app.guiconfig, self.app.app_scale)
|
image = NodeUtils.node_image(core_node, self.app.guiconfig, self.app.app_scale)
|
||||||
if not image:
|
if not image:
|
||||||
|
|
@ -323,25 +320,24 @@ class CanvasGraph(tk.Canvas):
|
||||||
y = core_node.position.y
|
y = core_node.position.y
|
||||||
node = CanvasNode(self.app, x, y, core_node, image)
|
node = CanvasNode(self.app, x, y, core_node, image)
|
||||||
self.nodes[node.id] = node
|
self.nodes[node.id] = node
|
||||||
self.core.canvas_nodes[core_node.id] = node
|
self.core.set_canvas_node(core_node, node)
|
||||||
|
|
||||||
def draw_session(self, session: Session) -> None:
|
def draw_session(self, session: Session) -> None:
|
||||||
"""
|
"""
|
||||||
Draw existing session.
|
Draw existing session.
|
||||||
"""
|
"""
|
||||||
# draw existing nodes
|
# draw existing nodes
|
||||||
for core_node in session.nodes:
|
for core_node in session.nodes.values():
|
||||||
logging.debug("drawing node: %s", core_node)
|
logging.debug("drawing node: %s", core_node)
|
||||||
# peer to peer node is not drawn on the GUI
|
# peer to peer node is not drawn on the GUI
|
||||||
if NodeUtils.is_ignore_node(core_node.type):
|
if NodeUtils.is_ignore_node(core_node.type):
|
||||||
continue
|
continue
|
||||||
self.add_core_node(core_node)
|
self.add_core_node(core_node)
|
||||||
|
# draw existing links
|
||||||
# draw existing links
|
|
||||||
for link in session.links:
|
for link in session.links:
|
||||||
logging.debug("drawing link: %s", link)
|
logging.debug("drawing link: %s", link)
|
||||||
canvas_node1 = self.core.canvas_nodes[link.node1_id]
|
canvas_node1 = self.core.get_canvas_node(link.node1_id)
|
||||||
canvas_node2 = self.core.canvas_nodes[link.node2_id]
|
canvas_node2 = self.core.get_canvas_node(link.node2_id)
|
||||||
if link.type == LinkType.WIRELESS:
|
if link.type == LinkType.WIRELESS:
|
||||||
self.add_wireless_edge(canvas_node1, canvas_node2, link)
|
self.add_wireless_edge(canvas_node1, canvas_node2, link)
|
||||||
else:
|
else:
|
||||||
|
|
@ -544,8 +540,8 @@ class CanvasGraph(tk.Canvas):
|
||||||
shape.delete()
|
shape.delete()
|
||||||
|
|
||||||
self.selection.clear()
|
self.selection.clear()
|
||||||
self.core.deleted_graph_nodes(nodes)
|
self.core.deleted_canvas_nodes(nodes)
|
||||||
self.core.deleted_graph_edges(edges)
|
self.core.deleted_canvas_edges(edges)
|
||||||
|
|
||||||
def delete_edge(self, edge: CanvasEdge) -> None:
|
def delete_edge(self, edge: CanvasEdge) -> None:
|
||||||
edge.delete()
|
edge.delete()
|
||||||
|
|
@ -564,7 +560,7 @@ class CanvasGraph(tk.Canvas):
|
||||||
dst_wireless = NodeUtils.is_wireless_node(dst_node.core_node.type)
|
dst_wireless = NodeUtils.is_wireless_node(dst_node.core_node.type)
|
||||||
if dst_wireless:
|
if dst_wireless:
|
||||||
src_node.delete_antenna()
|
src_node.delete_antenna()
|
||||||
self.core.deleted_graph_edges([edge])
|
self.core.deleted_canvas_edges([edge])
|
||||||
|
|
||||||
def zoom(self, event: tk.Event, factor: float = None) -> None:
|
def zoom(self, event: tk.Event, factor: float = None) -> None:
|
||||||
if not factor:
|
if not factor:
|
||||||
|
|
@ -750,8 +746,8 @@ class CanvasGraph(tk.Canvas):
|
||||||
image_file = self.node_draw.image_file
|
image_file = self.node_draw.image_file
|
||||||
self.node_draw.image = self.app.get_custom_icon(image_file, ICON_SIZE)
|
self.node_draw.image = self.app.get_custom_icon(image_file, ICON_SIZE)
|
||||||
node = CanvasNode(self.app, x, y, core_node, self.node_draw.image)
|
node = CanvasNode(self.app, x, y, core_node, self.node_draw.image)
|
||||||
self.core.canvas_nodes[core_node.id] = node
|
|
||||||
self.nodes[node.id] = node
|
self.nodes[node.id] = node
|
||||||
|
self.core.set_canvas_node(core_node, node)
|
||||||
|
|
||||||
def width_and_height(self) -> Tuple[int, int]:
|
def width_and_height(self) -> Tuple[int, int]:
|
||||||
"""
|
"""
|
||||||
|
|
@ -928,7 +924,8 @@ class CanvasGraph(tk.Canvas):
|
||||||
# maps original node canvas id to copy node canvas id
|
# maps original node canvas id to copy node canvas id
|
||||||
copy_map = {}
|
copy_map = {}
|
||||||
# the edges that will be copy over
|
# the edges that will be copy over
|
||||||
to_copy_edges = []
|
to_copy_edges = set()
|
||||||
|
to_copy_ids = {x.id for x in self.to_copy}
|
||||||
for canvas_node in self.to_copy:
|
for canvas_node in self.to_copy:
|
||||||
core_node = canvas_node.core_node
|
core_node = canvas_node.core_node
|
||||||
actual_x = core_node.position.x + 50
|
actual_x = core_node.position.x + 50
|
||||||
|
|
@ -940,30 +937,57 @@ class CanvasGraph(tk.Canvas):
|
||||||
if not copy:
|
if not copy:
|
||||||
continue
|
continue
|
||||||
node = CanvasNode(self.app, scaled_x, scaled_y, copy, canvas_node.image)
|
node = CanvasNode(self.app, scaled_x, scaled_y, copy, canvas_node.image)
|
||||||
|
|
||||||
# copy configurations and services
|
# copy configurations and services
|
||||||
node.core_node.services[:] = canvas_node.core_node.services
|
node.core_node.services = core_node.services.copy()
|
||||||
node.core_node.config_services[:] = canvas_node.core_node.config_services
|
node.core_node.config_services = core_node.config_services.copy()
|
||||||
node.emane_model_configs = deepcopy(canvas_node.emane_model_configs)
|
node.core_node.emane_model_configs = deepcopy(core_node.emane_model_configs)
|
||||||
node.wlan_config = deepcopy(canvas_node.wlan_config)
|
node.core_node.wlan_config = deepcopy(core_node.wlan_config)
|
||||||
node.mobility_config = deepcopy(canvas_node.mobility_config)
|
node.core_node.mobility_config = deepcopy(core_node.mobility_config)
|
||||||
node.service_configs = deepcopy(canvas_node.service_configs)
|
node.core_node.service_configs = deepcopy(core_node.service_configs)
|
||||||
node.service_file_configs = deepcopy(canvas_node.service_file_configs)
|
node.core_node.service_file_configs = deepcopy(
|
||||||
node.config_service_configs = deepcopy(canvas_node.config_service_configs)
|
core_node.service_file_configs
|
||||||
|
)
|
||||||
|
node.core_node.config_service_configs = deepcopy(
|
||||||
|
core_node.config_service_configs
|
||||||
|
)
|
||||||
|
|
||||||
copy_map[canvas_node.id] = node.id
|
copy_map[canvas_node.id] = node.id
|
||||||
self.core.canvas_nodes[copy.id] = node
|
|
||||||
self.nodes[node.id] = node
|
self.nodes[node.id] = node
|
||||||
|
self.core.set_canvas_node(copy, node)
|
||||||
for edge in canvas_node.edges:
|
for edge in canvas_node.edges:
|
||||||
if edge.src not in self.to_copy or edge.dst not in self.to_copy:
|
if edge.src not in to_copy_ids or edge.dst not in to_copy_ids:
|
||||||
if canvas_node.id == edge.src:
|
if canvas_node.id == edge.src:
|
||||||
dst_node = self.nodes[edge.dst]
|
dst_node = self.nodes[edge.dst]
|
||||||
self.create_edge(node, dst_node)
|
self.create_edge(node, dst_node)
|
||||||
|
token = create_edge_token(node.id, dst_node.id)
|
||||||
elif canvas_node.id == edge.dst:
|
elif canvas_node.id == edge.dst:
|
||||||
src_node = self.nodes[edge.src]
|
src_node = self.nodes[edge.src]
|
||||||
self.create_edge(src_node, node)
|
self.create_edge(src_node, node)
|
||||||
|
token = create_edge_token(src_node.id, node.id)
|
||||||
|
copy_edge = self.edges[token]
|
||||||
|
copy_link = copy_edge.link
|
||||||
|
iface1_id = copy_link.iface1.id if copy_link.iface1 else None
|
||||||
|
iface2_id = copy_link.iface2.id if copy_link.iface2 else None
|
||||||
|
options = edge.link.options
|
||||||
|
if options:
|
||||||
|
copy_edge.link.options = deepcopy(options)
|
||||||
|
if options and options.unidirectional:
|
||||||
|
asym_iface1 = None
|
||||||
|
if iface1_id is not None:
|
||||||
|
asym_iface1 = Interface(id=iface1_id)
|
||||||
|
asym_iface2 = None
|
||||||
|
if iface2_id is not None:
|
||||||
|
asym_iface2 = Interface(id=iface2_id)
|
||||||
|
copy_edge.asymmetric_link = Link(
|
||||||
|
node1_id=copy_link.node2_id,
|
||||||
|
node2_id=copy_link.node1_id,
|
||||||
|
iface1=asym_iface2,
|
||||||
|
iface2=asym_iface1,
|
||||||
|
options=deepcopy(edge.asymmetric_link.options),
|
||||||
|
)
|
||||||
|
copy_edge.redraw()
|
||||||
else:
|
else:
|
||||||
to_copy_edges.append(edge)
|
to_copy_edges.add(edge)
|
||||||
|
|
||||||
# copy link and link config
|
# copy link and link config
|
||||||
for edge in to_copy_edges:
|
for edge in to_copy_edges:
|
||||||
|
|
@ -975,30 +999,26 @@ class CanvasGraph(tk.Canvas):
|
||||||
token = create_edge_token(src_node_copy.id, dst_node_copy.id)
|
token = create_edge_token(src_node_copy.id, dst_node_copy.id)
|
||||||
copy_edge = self.edges[token]
|
copy_edge = self.edges[token]
|
||||||
copy_link = copy_edge.link
|
copy_link = copy_edge.link
|
||||||
|
iface1_id = copy_link.iface1.id if copy_link.iface1 else None
|
||||||
|
iface2_id = copy_link.iface2.id if copy_link.iface2 else None
|
||||||
options = edge.link.options
|
options = edge.link.options
|
||||||
copy_link.options = deepcopy(options)
|
if options:
|
||||||
iface1_id = None
|
copy_link.options = deepcopy(options)
|
||||||
if copy_link.iface1:
|
if options and options.unidirectional:
|
||||||
iface1_id = copy_link.iface1.id
|
|
||||||
iface2_id = None
|
|
||||||
if copy_link.iface2:
|
|
||||||
iface2_id = copy_link.iface2.id
|
|
||||||
if not options.unidirectional:
|
|
||||||
copy_edge.asymmetric_link = None
|
|
||||||
else:
|
|
||||||
asym_iface1 = None
|
asym_iface1 = None
|
||||||
if iface1_id:
|
if iface1_id is not None:
|
||||||
asym_iface1 = Interface(id=iface1_id)
|
asym_iface1 = Interface(id=iface1_id)
|
||||||
asym_iface2 = None
|
asym_iface2 = None
|
||||||
if iface2_id:
|
if iface2_id is not None:
|
||||||
asym_iface2 = Interface(id=iface2_id)
|
asym_iface2 = Interface(id=iface2_id)
|
||||||
copy_edge.asymmetric_link = Link(
|
copy_edge.asymmetric_link = Link(
|
||||||
node1_id=copy_link.node2_id,
|
node1_id=copy_link.node2_id,
|
||||||
node2_id=copy_link.node1_id,
|
node2_id=copy_link.node1_id,
|
||||||
iface1=asym_iface1,
|
iface1=asym_iface2,
|
||||||
iface2=asym_iface2,
|
iface2=asym_iface1,
|
||||||
options=edge.asymmetric_link.options,
|
options=deepcopy(edge.asymmetric_link.options),
|
||||||
)
|
)
|
||||||
|
copy_edge.redraw()
|
||||||
self.itemconfig(
|
self.itemconfig(
|
||||||
copy_edge.id,
|
copy_edge.id,
|
||||||
width=self.itemcget(edge.id, "width"),
|
width=self.itemcget(edge.id, "width"),
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import functools
|
import functools
|
||||||
import logging
|
import logging
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Set, Tuple
|
from typing import TYPE_CHECKING, Dict, List, Set
|
||||||
|
|
||||||
import grpc
|
import grpc
|
||||||
from PIL.ImageTk import PhotoImage
|
from PIL.ImageTk import PhotoImage
|
||||||
|
|
@ -19,7 +19,7 @@ from core.gui.graph.edges import CanvasEdge, CanvasWirelessEdge
|
||||||
from core.gui.graph.tooltip import CanvasTooltip
|
from core.gui.graph.tooltip import CanvasTooltip
|
||||||
from core.gui.images import ImageEnum
|
from core.gui.images import ImageEnum
|
||||||
from core.gui.nodeutils import ANTENNA_SIZE, NodeUtils
|
from core.gui.nodeutils import ANTENNA_SIZE, NodeUtils
|
||||||
from core.gui.wrappers import ConfigOption, Interface, Node, NodeServiceData, NodeType
|
from core.gui.wrappers import Interface, Node, NodeType
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from core.gui.app import Application
|
from core.gui.app import Application
|
||||||
|
|
@ -56,15 +56,6 @@ class CanvasNode:
|
||||||
self.wireless_edges: Set[CanvasWirelessEdge] = set()
|
self.wireless_edges: Set[CanvasWirelessEdge] = set()
|
||||||
self.antennas: List[int] = []
|
self.antennas: List[int] = []
|
||||||
self.antenna_images: Dict[int, PhotoImage] = {}
|
self.antenna_images: Dict[int, PhotoImage] = {}
|
||||||
# possible configurations
|
|
||||||
self.emane_model_configs: Dict[
|
|
||||||
Tuple[str, Optional[int]], Dict[str, ConfigOption]
|
|
||||||
] = {}
|
|
||||||
self.wlan_config: Dict[str, ConfigOption] = {}
|
|
||||||
self.mobility_config: Dict[str, ConfigOption] = {}
|
|
||||||
self.service_configs: Dict[str, NodeServiceData] = {}
|
|
||||||
self.service_file_configs: Dict[str, Dict[str, str]] = {}
|
|
||||||
self.config_service_configs: Dict[str, Any] = {}
|
|
||||||
self.setup_bindings()
|
self.setup_bindings()
|
||||||
self.context: tk.Menu = tk.Menu(self.canvas)
|
self.context: tk.Menu = tk.Menu(self.canvas)
|
||||||
themes.style_menu(self.context)
|
themes.style_menu(self.context)
|
||||||
|
|
@ -299,7 +290,7 @@ class CanvasNode:
|
||||||
dialog.show()
|
dialog.show()
|
||||||
|
|
||||||
def show_mobility_config(self) -> None:
|
def show_mobility_config(self) -> None:
|
||||||
dialog = MobilityConfigDialog(self.app, self)
|
dialog = MobilityConfigDialog(self.app, self.core_node)
|
||||||
if not dialog.has_error:
|
if not dialog.has_error:
|
||||||
dialog.show()
|
dialog.show()
|
||||||
|
|
||||||
|
|
@ -308,15 +299,15 @@ class CanvasNode:
|
||||||
mobility_player.show()
|
mobility_player.show()
|
||||||
|
|
||||||
def show_emane_config(self) -> None:
|
def show_emane_config(self) -> None:
|
||||||
dialog = EmaneConfigDialog(self.app, self)
|
dialog = EmaneConfigDialog(self.app, self.core_node)
|
||||||
dialog.show()
|
dialog.show()
|
||||||
|
|
||||||
def show_services(self) -> None:
|
def show_services(self) -> None:
|
||||||
dialog = NodeServiceDialog(self.app, self)
|
dialog = NodeServiceDialog(self.app, self.core_node)
|
||||||
dialog.show()
|
dialog.show()
|
||||||
|
|
||||||
def show_config_services(self) -> None:
|
def show_config_services(self) -> None:
|
||||||
dialog = NodeConfigServiceDialog(self.app, self)
|
dialog = NodeConfigServiceDialog(self.app, self.core_node)
|
||||||
dialog.show()
|
dialog.show()
|
||||||
|
|
||||||
def has_emane_link(self, iface_id: int) -> Node:
|
def has_emane_link(self, iface_id: int) -> Node:
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,6 @@ class ProgressTask:
|
||||||
thread.start()
|
thread.start()
|
||||||
|
|
||||||
def run(self) -> None:
|
def run(self) -> None:
|
||||||
logging.info("running task")
|
|
||||||
try:
|
try:
|
||||||
values = self.task(*self.args)
|
values = self.task(*self.args)
|
||||||
if values is None:
|
if values is None:
|
||||||
|
|
@ -41,7 +40,6 @@ class ProgressTask:
|
||||||
elif values and not isinstance(values, tuple):
|
elif values and not isinstance(values, tuple):
|
||||||
values = (values,)
|
values = (values,)
|
||||||
if self.callback:
|
if self.callback:
|
||||||
logging.info("calling callback")
|
|
||||||
self.app.after(0, self.callback, *values)
|
self.app.after(0, self.callback, *values)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception("progress task exception")
|
logging.exception("progress task exception")
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Dict, List
|
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, services_pb2
|
||||||
|
|
||||||
|
|
@ -119,6 +119,12 @@ class ConfigService:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ConfigServiceData:
|
||||||
|
templates: Dict[str, str] = field(default_factory=dict)
|
||||||
|
config: Dict[str, str] = field(default_factory=dict)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class NodeServiceData:
|
class NodeServiceData:
|
||||||
executables: List[str]
|
executables: List[str]
|
||||||
|
|
@ -498,8 +504,8 @@ class Node:
|
||||||
type: NodeType
|
type: NodeType
|
||||||
model: str = None
|
model: str = None
|
||||||
position: Position = None
|
position: Position = None
|
||||||
services: List[str] = field(default_factory=list)
|
services: Set[str] = field(default_factory=set)
|
||||||
config_services: List[str] = field(default_factory=list)
|
config_services: Set[str] = field(default_factory=set)
|
||||||
emane: str = None
|
emane: str = None
|
||||||
icon: str = None
|
icon: str = None
|
||||||
image: str = None
|
image: str = None
|
||||||
|
|
@ -508,6 +514,22 @@ class Node:
|
||||||
dir: str = None
|
dir: str = None
|
||||||
channel: str = None
|
channel: str = None
|
||||||
|
|
||||||
|
# configurations
|
||||||
|
emane_model_configs: Dict[
|
||||||
|
Tuple[str, Optional[int]], Dict[str, ConfigOption]
|
||||||
|
] = field(default_factory=dict, repr=False)
|
||||||
|
wlan_config: Dict[str, ConfigOption] = field(default_factory=dict, repr=False)
|
||||||
|
mobility_config: Dict[str, ConfigOption] = field(default_factory=dict, repr=False)
|
||||||
|
service_configs: Dict[str, NodeServiceData] = field(
|
||||||
|
default_factory=dict, repr=False
|
||||||
|
)
|
||||||
|
service_file_configs: Dict[str, Dict[str, str]] = field(
|
||||||
|
default_factory=dict, repr=False
|
||||||
|
)
|
||||||
|
config_service_configs: Dict[str, ConfigServiceData] = field(
|
||||||
|
default_factory=dict, repr=False
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_proto(cls, proto: core_pb2.Node) -> "Node":
|
def from_proto(cls, proto: core_pb2.Node) -> "Node":
|
||||||
return Node(
|
return Node(
|
||||||
|
|
@ -516,8 +538,8 @@ class Node:
|
||||||
type=NodeType(proto.type),
|
type=NodeType(proto.type),
|
||||||
model=proto.model,
|
model=proto.model,
|
||||||
position=Position.from_proto(proto.position),
|
position=Position.from_proto(proto.position),
|
||||||
services=list(proto.services),
|
services=set(proto.services),
|
||||||
config_services=list(proto.config_services),
|
config_services=set(proto.config_services),
|
||||||
emane=proto.emane,
|
emane=proto.emane,
|
||||||
icon=proto.icon,
|
icon=proto.icon,
|
||||||
image=proto.image,
|
image=proto.image,
|
||||||
|
|
@ -549,20 +571,64 @@ class Node:
|
||||||
class Session:
|
class Session:
|
||||||
id: int
|
id: int
|
||||||
state: SessionState
|
state: SessionState
|
||||||
nodes: List[Node]
|
nodes: Dict[int, Node]
|
||||||
links: List[Link]
|
links: List[Link]
|
||||||
dir: str
|
dir: str
|
||||||
|
user: str
|
||||||
|
default_services: Dict[str, Set[str]]
|
||||||
|
location: SessionLocation
|
||||||
|
hooks: Dict[str, Hook]
|
||||||
|
emane_models: List[str]
|
||||||
|
emane_config: Dict[str, ConfigOption]
|
||||||
|
metadata: Dict[str, str]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_proto(cls, proto: core_pb2.Session) -> "Session":
|
def from_proto(cls, proto: core_pb2.Session) -> "Session":
|
||||||
nodes = [Node.from_proto(x) for x in proto.nodes]
|
nodes: Dict[int, Node] = {x.id: Node.from_proto(x) for x in proto.nodes}
|
||||||
links = [Link.from_proto(x) for x in proto.links]
|
links = [Link.from_proto(x) for x in proto.links]
|
||||||
|
default_services = {
|
||||||
|
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)
|
||||||
return Session(
|
return Session(
|
||||||
id=proto.id,
|
id=proto.id,
|
||||||
state=SessionState(proto.state),
|
state=SessionState(proto.state),
|
||||||
nodes=nodes,
|
nodes=nodes,
|
||||||
links=links,
|
links=links,
|
||||||
dir=proto.dir,
|
dir=proto.dir,
|
||||||
|
user=proto.user,
|
||||||
|
default_services=default_services,
|
||||||
|
location=SessionLocation.from_proto(proto.location),
|
||||||
|
hooks=hooks,
|
||||||
|
emane_models=list(proto.emane_models),
|
||||||
|
emane_config=ConfigOption.from_dict(proto.emane_config),
|
||||||
|
metadata=dict(proto.metadata),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -714,6 +714,18 @@ message Session {
|
||||||
repeated Node nodes = 3;
|
repeated Node nodes = 3;
|
||||||
repeated Link links = 4;
|
repeated Link links = 4;
|
||||||
string dir = 5;
|
string dir = 5;
|
||||||
|
string user = 6;
|
||||||
|
repeated services.ServiceDefaults default_services = 7;
|
||||||
|
SessionLocation location = 8;
|
||||||
|
repeated Hook hooks = 9;
|
||||||
|
repeated string emane_models = 10;
|
||||||
|
map<string, common.ConfigOption> emane_config = 11;
|
||||||
|
repeated emane.GetEmaneModelConfig emane_model_configs = 12;
|
||||||
|
map<int32, common.MappedConfig> wlan_configs = 13;
|
||||||
|
repeated services.NodeServiceConfig service_configs = 14;
|
||||||
|
repeated configservices.ConfigServiceConfig config_service_configs = 15;
|
||||||
|
map<int32, common.MappedConfig> mobility_configs = 16;
|
||||||
|
map<string, string> metadata = 17;
|
||||||
}
|
}
|
||||||
|
|
||||||
message SessionSummary {
|
message SessionSummary {
|
||||||
|
|
|
||||||
|
|
@ -53,14 +53,15 @@ message GetEmaneModelConfigsRequest {
|
||||||
int32 session_id = 1;
|
int32 session_id = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message GetEmaneModelConfig {
|
||||||
|
int32 node_id = 1;
|
||||||
|
string model = 2;
|
||||||
|
int32 iface_id = 3;
|
||||||
|
map<string, common.ConfigOption> config = 4;
|
||||||
|
}
|
||||||
|
|
||||||
message GetEmaneModelConfigsResponse {
|
message GetEmaneModelConfigsResponse {
|
||||||
message ModelConfig {
|
repeated GetEmaneModelConfig configs = 1;
|
||||||
int32 node_id = 1;
|
|
||||||
string model = 2;
|
|
||||||
int32 iface_id = 3;
|
|
||||||
map<string, common.ConfigOption> config = 4;
|
|
||||||
}
|
|
||||||
repeated ModelConfig configs = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message GetEmaneEventChannelRequest {
|
message GetEmaneEventChannelRequest {
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,13 @@ message NodeServiceData {
|
||||||
string meta = 10;
|
string meta = 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message NodeServiceConfig {
|
||||||
|
int32 node_id = 1;
|
||||||
|
string service = 2;
|
||||||
|
NodeServiceData data = 3;
|
||||||
|
map<string, string> files = 4;
|
||||||
|
}
|
||||||
|
|
||||||
message GetServicesRequest {
|
message GetServicesRequest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -89,13 +96,7 @@ message GetNodeServiceConfigsRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
message GetNodeServiceConfigsResponse {
|
message GetNodeServiceConfigsResponse {
|
||||||
message ServiceConfig {
|
repeated NodeServiceConfig configs = 1;
|
||||||
int32 node_id = 1;
|
|
||||||
string service = 2;
|
|
||||||
NodeServiceData data = 3;
|
|
||||||
map<string, string> files = 4;
|
|
||||||
}
|
|
||||||
repeated ServiceConfig configs = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message GetNodeServiceRequest {
|
message GetNodeServiceRequest {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue