updates to support saving config services to xml, loading config services from xml, retrieving config services from coretk when joining a session
This commit is contained in:
parent
0ea2f73a80
commit
d7d0a55fd2
9 changed files with 162 additions and 43 deletions
|
@ -17,6 +17,8 @@ from core.api.grpc.configservices_pb2 import (
|
|||
GetConfigServiceDefaultsResponse,
|
||||
GetConfigServicesRequest,
|
||||
GetConfigServicesResponse,
|
||||
GetNodeConfigServiceConfigsRequest,
|
||||
GetNodeConfigServiceConfigsResponse,
|
||||
GetNodeConfigServiceRequest,
|
||||
GetNodeConfigServiceResponse,
|
||||
GetNodeConfigServicesRequest,
|
||||
|
@ -1103,6 +1105,12 @@ class CoreGrpcClient:
|
|||
request = GetConfigServiceDefaultsRequest(name=name)
|
||||
return self.stub.GetConfigServiceDefaults(request)
|
||||
|
||||
def get_node_config_service_configs(
|
||||
self, session_id: int
|
||||
) -> GetNodeConfigServiceConfigsResponse:
|
||||
request = GetNodeConfigServiceConfigsRequest(session_id=session_id)
|
||||
return self.stub.GetNodeConfigServiceConfigs(request)
|
||||
|
||||
def get_node_config_service(
|
||||
self, session_id: int, node_id: int, name: str
|
||||
) -> GetNodeConfigServiceResponse:
|
||||
|
|
|
@ -23,6 +23,8 @@ from core.api.grpc.configservices_pb2 import (
|
|||
GetConfigServiceDefaultsResponse,
|
||||
GetConfigServicesRequest,
|
||||
GetConfigServicesResponse,
|
||||
GetNodeConfigServiceConfigsRequest,
|
||||
GetNodeConfigServiceConfigsResponse,
|
||||
GetNodeConfigServiceRequest,
|
||||
GetNodeConfigServiceResponse,
|
||||
GetNodeConfigServicesRequest,
|
||||
|
@ -45,7 +47,7 @@ from core.emulator.enumerations import EventTypes, LinkTypes, MessageFlags
|
|||
from core.emulator.session import Session
|
||||
from core.errors import CoreCommandError, CoreError
|
||||
from core.location.mobility import BasicRangeModel, Ns2ScriptedMobility
|
||||
from core.nodes.base import NodeBase
|
||||
from core.nodes.base import CoreNodeBase, NodeBase
|
||||
from core.nodes.docker import DockerNode
|
||||
from core.nodes.lxd import LxcNode
|
||||
from core.services.coreservices import ServiceManager
|
||||
|
@ -192,7 +194,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
if config.config:
|
||||
service.set_config(config.config)
|
||||
for name, template in config.templates.items():
|
||||
service.custom_template(name, template)
|
||||
service.set_template(name, template)
|
||||
|
||||
# service file configs
|
||||
for config in request.service_file_configs:
|
||||
|
@ -463,6 +465,8 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
if services is None:
|
||||
services = []
|
||||
services = [x.name for x in services]
|
||||
config_services = getattr(node, "config_services", {})
|
||||
config_services = [x for x in config_services]
|
||||
emane_model = None
|
||||
if isinstance(node, EmaneNet):
|
||||
emane_model = node.model.name
|
||||
|
@ -478,6 +482,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
services=services,
|
||||
icon=node.icon,
|
||||
image=image,
|
||||
config_services=config_services,
|
||||
)
|
||||
if isinstance(node, (DockerNode, LxcNode)):
|
||||
node_proto.image = node.image
|
||||
|
@ -1528,6 +1533,27 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
templates=templates, config=config, modes=modes
|
||||
)
|
||||
|
||||
def GetNodeConfigServiceConfigs(
|
||||
self, request: GetNodeConfigServiceConfigsRequest, context: ServicerContext
|
||||
) -> GetNodeConfigServiceConfigsResponse:
|
||||
session = self.get_session(request.session_id, context)
|
||||
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 = 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)
|
||||
|
||||
def GetNodeConfigServices(
|
||||
self, request: GetNodeConfigServicesRequest, context: ServicerContext
|
||||
) -> GetNodeConfigServicesResponse:
|
||||
|
|
|
@ -38,6 +38,7 @@ class ConfigService(abc.ABC):
|
|||
self.templates = TemplateLookup(directories=templates_path)
|
||||
self.config = {}
|
||||
self.custom_templates = {}
|
||||
self.custom_config = {}
|
||||
configs = self.default_configs[:]
|
||||
self._define_config(configs)
|
||||
|
||||
|
@ -134,7 +135,7 @@ class ConfigService(abc.ABC):
|
|||
def data(self) -> Dict[str, Any]:
|
||||
return {}
|
||||
|
||||
def custom_template(self, name: str, template: str) -> None:
|
||||
def set_template(self, name: str, template: str) -> None:
|
||||
self.custom_templates[name] = template
|
||||
|
||||
def get_text(self, name: str) -> str:
|
||||
|
@ -248,11 +249,13 @@ class ConfigService(abc.ABC):
|
|||
self.config[config.id] = config
|
||||
|
||||
def render_config(self) -> Dict[str, str]:
|
||||
if self.custom_config:
|
||||
return self.custom_config
|
||||
else:
|
||||
return {k: v.default for k, v in self.config.items()}
|
||||
|
||||
def set_config(self, data: Dict[str, str]) -> None:
|
||||
for key, value in data.items():
|
||||
config = self.config.get(key)
|
||||
if config is None:
|
||||
if key not in self.config:
|
||||
raise CoreError(f"unknown config: {key}")
|
||||
config.default = value
|
||||
self.custom_config[key] = value
|
||||
|
|
|
@ -297,6 +297,18 @@ class CoreClient:
|
|||
data = config.files[file_name]
|
||||
files[file_name] = data
|
||||
|
||||
# get config service configurations
|
||||
response = self.client.get_node_config_service_configs(self.session_id)
|
||||
for config in response.configs:
|
||||
node_configs = self.config_service_configs.setdefault(
|
||||
config.node_id, {}
|
||||
)
|
||||
service_config = node_configs.setdefault(config.name, {})
|
||||
if config.templates:
|
||||
service_config["templates"] = config.templates
|
||||
if config.config:
|
||||
service_config["config"] = config.config
|
||||
|
||||
# draw session
|
||||
self.app.canvas.reset_and_redraw(session)
|
||||
|
||||
|
@ -880,14 +892,11 @@ class CoreClient:
|
|||
for node_id, node_config in self.config_service_configs.items():
|
||||
for name, service_config in node_config.items():
|
||||
config = service_config.get("config", {})
|
||||
config_values = {}
|
||||
for key, option in config.items():
|
||||
config_values[key] = option.value
|
||||
config_proto = configservices_pb2.ConfigServiceConfig(
|
||||
node_id=node_id,
|
||||
name=name,
|
||||
templates=service_config["templates"],
|
||||
config=config_values,
|
||||
config=config,
|
||||
)
|
||||
config_service_protos.append(config_proto)
|
||||
return config_service_protos
|
||||
|
|
|
@ -93,15 +93,17 @@ class ConfigServiceConfigDialog(Dialog):
|
|||
node_configs = self.service_configs.get(self.node_id, {})
|
||||
service_config = node_configs.get(self.service_name, {})
|
||||
|
||||
self.default_config = response.config
|
||||
self.config = response.config
|
||||
self.default_config = {x.name: x.value for x in self.config.values()}
|
||||
custom_config = service_config.get("config")
|
||||
if custom_config:
|
||||
self.config = custom_config
|
||||
else:
|
||||
self.config = dict(self.default_config)
|
||||
for key, value in custom_config.items():
|
||||
self.config[key].value = value
|
||||
logging.info("default config: %s", self.default_config)
|
||||
|
||||
custom_templates = service_config.get("templates", {})
|
||||
for file, data in custom_templates.items():
|
||||
self.modified_files.add(file)
|
||||
self.temp_service_files[file] = data
|
||||
except grpc.RpcError as e:
|
||||
show_grpc_error(e)
|
||||
|
@ -304,7 +306,7 @@ class ConfigServiceConfigDialog(Dialog):
|
|||
|
||||
def click_apply(self):
|
||||
current_listbox = self.master.current.listbox
|
||||
if not self.is_custom_service_config() and not self.is_custom_service_file():
|
||||
if not self.is_custom():
|
||||
if self.node_id in self.service_configs:
|
||||
self.service_configs[self.node_id].pop(self.service_name, None)
|
||||
current_listbox.itemconfig(current_listbox.curselection()[0], bg="")
|
||||
|
@ -316,7 +318,9 @@ class ConfigServiceConfigDialog(Dialog):
|
|||
service_config = node_config.setdefault(self.service_name, {})
|
||||
if self.config_frame:
|
||||
self.config_frame.parse_config()
|
||||
service_config["config"] = self.config
|
||||
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:
|
||||
templates_config[file] = self.temp_service_files[file]
|
||||
|
@ -346,18 +350,13 @@ class ConfigServiceConfigDialog(Dialog):
|
|||
else:
|
||||
self.modified_files.discard(template)
|
||||
|
||||
def is_custom_service_config(self):
|
||||
startup_commands = self.startup_commands_listbox.get(0, "end")
|
||||
shutdown_commands = self.shutdown_commands_listbox.get(0, "end")
|
||||
validate_commands = self.validate_commands_listbox.get(0, "end")
|
||||
return (
|
||||
set(self.default_startup) != set(startup_commands)
|
||||
or set(self.default_validate) != set(validate_commands)
|
||||
or set(self.default_shutdown) != set(shutdown_commands)
|
||||
)
|
||||
|
||||
def is_custom_service_file(self):
|
||||
return len(self.modified_files) > 0
|
||||
def is_custom(self):
|
||||
has_custom_templates = len(self.modified_files) > 0
|
||||
has_custom_config = False
|
||||
if self.config_frame:
|
||||
current = self.config_frame.parse_config()
|
||||
has_custom_config = self.default_config != current
|
||||
return has_custom_templates or has_custom_config
|
||||
|
||||
def click_defaults(self):
|
||||
if self.node_id in self.service_configs:
|
||||
|
@ -368,8 +367,8 @@ class ConfigServiceConfigDialog(Dialog):
|
|||
self.template_text.text.delete(1.0, "end")
|
||||
self.template_text.text.insert("end", self.temp_service_files[filename])
|
||||
if self.config_frame:
|
||||
defaults = {x.id: x.value for x in self.default_config.values()}
|
||||
self.config_frame.set_values(defaults)
|
||||
logging.info("resetting defaults: %s", self.default_config)
|
||||
self.config_frame.set_values(self.default_config)
|
||||
|
||||
def click_copy(self):
|
||||
pass
|
||||
|
|
|
@ -153,14 +153,9 @@ class NodeConfigServiceDialog(Dialog):
|
|||
return
|
||||
|
||||
def is_custom_service(self, service: str) -> bool:
|
||||
service_configs = self.app.core.service_configs
|
||||
file_configs = self.app.core.file_configs
|
||||
if self.node_id in service_configs and service in service_configs[self.node_id]:
|
||||
return True
|
||||
if (
|
||||
self.node_id in file_configs
|
||||
and service in file_configs[self.node_id]
|
||||
and file_configs[self.node_id][service]
|
||||
):
|
||||
node_configs = self.app.core.config_service_configs.get(self.node_id, {})
|
||||
service_config = node_configs.get(service)
|
||||
if node_configs and service_config:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
|
|
@ -9,7 +9,7 @@ from core.emane.nodes import EmaneNet
|
|||
from core.emulator.data import LinkData
|
||||
from core.emulator.emudata import InterfaceData, LinkOptions, NodeOptions
|
||||
from core.emulator.enumerations import NodeTypes
|
||||
from core.nodes.base import CoreNetworkBase, NodeBase
|
||||
from core.nodes.base import CoreNetworkBase, CoreNodeBase, NodeBase
|
||||
from core.nodes.network import CtrlNet
|
||||
from core.services.coreservices import CoreService
|
||||
|
||||
|
@ -219,10 +219,15 @@ class DeviceElement(NodeElement):
|
|||
service_elements = etree.Element("services")
|
||||
for service in self.node.services:
|
||||
etree.SubElement(service_elements, "service", name=service.name)
|
||||
|
||||
if service_elements.getchildren():
|
||||
self.element.append(service_elements)
|
||||
|
||||
config_service_elements = etree.Element("configservices")
|
||||
for name, service in self.node.config_services.items():
|
||||
etree.SubElement(config_service_elements, "service", name=name)
|
||||
if config_service_elements.getchildren():
|
||||
self.element.append(config_service_elements)
|
||||
|
||||
|
||||
class NetworkElement(NodeElement):
|
||||
def __init__(self, session: "Session", node: NodeBase) -> None:
|
||||
|
@ -261,6 +266,7 @@ class CoreXmlWriter:
|
|||
self.write_mobility_configs()
|
||||
self.write_emane_configs()
|
||||
self.write_service_configs()
|
||||
self.write_configservice_configs()
|
||||
self.write_session_origin()
|
||||
self.write_session_hooks()
|
||||
self.write_session_options()
|
||||
|
@ -399,6 +405,32 @@ class CoreXmlWriter:
|
|||
if service_configurations.getchildren():
|
||||
self.scenario.append(service_configurations)
|
||||
|
||||
def write_configservice_configs(self) -> None:
|
||||
service_configurations = etree.Element("configservice_configurations")
|
||||
for node in self.session.nodes.values():
|
||||
if not isinstance(node, CoreNodeBase):
|
||||
continue
|
||||
for name, service in node.config_services.items():
|
||||
service_element = etree.SubElement(
|
||||
service_configurations, "service", name=name
|
||||
)
|
||||
add_attribute(service_element, "node", node.id)
|
||||
if service.custom_config:
|
||||
configs_element = etree.SubElement(service_element, "configs")
|
||||
for key, value in service.custom_config.items():
|
||||
etree.SubElement(
|
||||
configs_element, "config", key=key, value=value
|
||||
)
|
||||
if service.custom_templates:
|
||||
templates_element = etree.SubElement(service_element, "templates")
|
||||
for template_name, template in service.custom_templates.items():
|
||||
template_element = etree.SubElement(
|
||||
templates_element, "template", name=template_name
|
||||
)
|
||||
template_element.text = etree.CDATA(template)
|
||||
if service_configurations.getchildren():
|
||||
self.scenario.append(service_configurations)
|
||||
|
||||
def write_default_services(self) -> None:
|
||||
node_types = etree.Element("default_services")
|
||||
for node_type in self.session.services.default_services:
|
||||
|
@ -568,6 +600,7 @@ class CoreXmlReader:
|
|||
self.read_mobility_configs()
|
||||
self.read_emane_configs()
|
||||
self.read_nodes()
|
||||
self.read_configservice_configs()
|
||||
self.read_links()
|
||||
|
||||
def read_default_services(self) -> None:
|
||||
|
@ -768,6 +801,12 @@ class CoreXmlReader:
|
|||
if service_elements is not None:
|
||||
options.services = [x.get("name") for x in service_elements.iterchildren()]
|
||||
|
||||
config_service_elements = device_element.find("configservices")
|
||||
if config_service_elements is not None:
|
||||
options.config_services = [
|
||||
x.get("name") for x in config_service_elements.iterchildren()
|
||||
]
|
||||
|
||||
position_element = device_element.find("position")
|
||||
if position_element is not None:
|
||||
x = get_float(position_element, "x")
|
||||
|
@ -808,6 +847,36 @@ class CoreXmlReader:
|
|||
)
|
||||
self.session.add_node(_type=node_type, _id=node_id, options=options)
|
||||
|
||||
def read_configservice_configs(self) -> None:
|
||||
configservice_configs = self.scenario.find("configservice_configurations")
|
||||
if configservice_configs is None:
|
||||
return
|
||||
|
||||
for configservice_element in configservice_configs.iterchildren():
|
||||
name = configservice_element.get("name")
|
||||
node_id = get_int(configservice_element, "node")
|
||||
node = self.session.get_node(node_id)
|
||||
service = node.config_services[name]
|
||||
|
||||
configs_element = configservice_element.find("configs")
|
||||
if configs_element is not None:
|
||||
config = {}
|
||||
for config_element in configs_element.iterchildren():
|
||||
key = config_element.get("key")
|
||||
value = config_element.get("value")
|
||||
config[key] = value
|
||||
service.set_config(config)
|
||||
|
||||
templates_element = configservice_element.find("templates")
|
||||
if templates_element is not None:
|
||||
for template_element in templates_element.iterchildren():
|
||||
name = template_element.get("name")
|
||||
template = template_element.text
|
||||
logging.info(
|
||||
"loading xml template(%s): %s", type(template), template
|
||||
)
|
||||
service.set_template(name, template)
|
||||
|
||||
def read_links(self) -> None:
|
||||
link_elements = self.scenario.find("links")
|
||||
if link_elements is None:
|
||||
|
|
|
@ -57,6 +57,14 @@ message GetConfigServiceDefaultsResponse {
|
|||
repeated ConfigMode modes = 3;
|
||||
}
|
||||
|
||||
message GetNodeConfigServiceConfigsRequest {
|
||||
int32 session_id = 1;
|
||||
}
|
||||
|
||||
message GetNodeConfigServiceConfigsResponse {
|
||||
repeated ConfigServiceConfig configs = 1;
|
||||
}
|
||||
|
||||
message GetNodeConfigServiceRequest {
|
||||
int32 session_id = 1;
|
||||
int32 node_id = 2;
|
||||
|
|
|
@ -110,6 +110,8 @@ service CoreApi {
|
|||
}
|
||||
rpc GetConfigServiceDefaults (configservices.GetConfigServiceDefaultsRequest) returns (configservices.GetConfigServiceDefaultsResponse) {
|
||||
}
|
||||
rpc GetNodeConfigServiceConfigs (configservices.GetNodeConfigServiceConfigsRequest) returns (configservices.GetNodeConfigServiceConfigsResponse) {
|
||||
}
|
||||
rpc GetNodeConfigService (configservices.GetNodeConfigServiceRequest) returns (configservices.GetNodeConfigServiceResponse) {
|
||||
}
|
||||
rpc GetNodeConfigServices (configservices.GetNodeConfigServicesRequest) returns (configservices.GetNodeConfigServicesResponse) {
|
||||
|
|
Loading…
Add table
Reference in a new issue