added files to config services, added default logic for creating files from templates, added new method to provide extra data to templates, updated gui to display templates raw
This commit is contained in:
parent
9447ddb94f
commit
7b5df11dc7
11 changed files with 120 additions and 52 deletions
|
@ -15,6 +15,8 @@ from core.api.grpc import core_pb2, core_pb2_grpc
|
|||
from core.api.grpc.configservices_pb2 import (
|
||||
GetConfigServicesRequest,
|
||||
GetConfigServicesResponse,
|
||||
GetConfigServiceTemplatesRequest,
|
||||
GetConfigServiceTemplatesResponse,
|
||||
GetNodeConfigServiceRequest,
|
||||
GetNodeConfigServiceResponse,
|
||||
GetNodeConfigServicesRequest,
|
||||
|
@ -1092,6 +1094,12 @@ class CoreGrpcClient:
|
|||
request = GetConfigServicesRequest()
|
||||
return self.stub.GetConfigServices(request)
|
||||
|
||||
def get_config_service_templates(
|
||||
self, name: str
|
||||
) -> GetConfigServiceTemplatesResponse:
|
||||
request = GetConfigServiceTemplatesRequest(name=name)
|
||||
return self.stub.GetConfigServiceTemplates(request)
|
||||
|
||||
def get_node_config_service(
|
||||
self, session_id: int, node_id: int, name: str
|
||||
) -> GetNodeConfigServiceResponse:
|
||||
|
|
|
@ -5,6 +5,7 @@ import re
|
|||
import tempfile
|
||||
import time
|
||||
from concurrent import futures
|
||||
from typing import Type
|
||||
|
||||
import grpc
|
||||
from grpc import ServicerContext
|
||||
|
@ -14,6 +15,8 @@ from core.api.grpc.configservices_pb2 import (
|
|||
ConfigService,
|
||||
GetConfigServicesRequest,
|
||||
GetConfigServicesResponse,
|
||||
GetConfigServiceTemplatesRequest,
|
||||
GetConfigServiceTemplatesResponse,
|
||||
GetNodeConfigServiceRequest,
|
||||
GetNodeConfigServiceResponse,
|
||||
GetNodeConfigServicesRequest,
|
||||
|
@ -112,9 +115,13 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
except CoreError:
|
||||
context.abort(grpc.StatusCode.NOT_FOUND, f"node {node_id} not found")
|
||||
|
||||
def validate_service(self, name: str, context: ServicerContext) -> None:
|
||||
if name not in self.coreemu.service_manager.services:
|
||||
def validate_service(
|
||||
self, name: str, context: ServicerContext
|
||||
) -> Type[ConfigService]:
|
||||
service = self.coreemu.service_manager.services.get(name)
|
||||
if not service:
|
||||
context.abort(grpc.StatusCode.NOT_FOUND, f"unknown service {name}")
|
||||
return service
|
||||
|
||||
def StartSession(
|
||||
self, request: core_pb2.StartSessionRequest, context: ServicerContext
|
||||
|
@ -1456,6 +1463,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
executables=service.executables,
|
||||
dependencies=service.dependencies,
|
||||
directories=service.directories,
|
||||
files=service.files,
|
||||
startup=service.startup,
|
||||
validate=service.validate,
|
||||
shutdown=service.shutdown,
|
||||
|
@ -1481,6 +1489,14 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
config = {x.id: x.default for x in service.default_configs}
|
||||
return GetNodeConfigServiceResponse(config=config)
|
||||
|
||||
def GetConfigServiceTemplates(
|
||||
self, request: GetConfigServiceTemplatesRequest, context: ServicerContext
|
||||
) -> GetConfigServiceTemplatesResponse:
|
||||
service_class = self.validate_service(request.name, context)
|
||||
service = service_class(None)
|
||||
templates = service.get_templates()
|
||||
return GetConfigServiceTemplatesResponse(templates=templates)
|
||||
|
||||
def GetNodeConfigServices(
|
||||
self, request: GetNodeConfigServicesRequest, context: ServicerContext
|
||||
) -> GetNodeConfigServicesResponse:
|
||||
|
|
|
@ -55,6 +55,11 @@ class ConfigService(abc.ABC):
|
|||
def directories(self) -> List[str]:
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
@abc.abstractmethod
|
||||
def files(self) -> List[str]:
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
@abc.abstractmethod
|
||||
def default_configs(self) -> List[Configuration]:
|
||||
|
@ -120,8 +125,34 @@ class ConfigService(abc.ABC):
|
|||
f"failure to create service directory: {directory}"
|
||||
)
|
||||
|
||||
def data(self) -> Dict[str, Any]:
|
||||
return {}
|
||||
|
||||
def get_text(self, name: str) -> str:
|
||||
raise CoreError(
|
||||
f"node({self.node.name} service({self.name}) unknown template({name})"
|
||||
)
|
||||
|
||||
def get_templates(self) -> Dict[str, str]:
|
||||
templates = {}
|
||||
for name in self.files:
|
||||
if self.templates.has_template(name):
|
||||
template = self.templates.get_template(name).source
|
||||
else:
|
||||
template = self.get_text(name)
|
||||
template = inspect.cleandoc(template)
|
||||
templates[name] = template
|
||||
return templates
|
||||
|
||||
def create_files(self) -> None:
|
||||
raise NotImplementedError
|
||||
data = self.data()
|
||||
for name in self.files:
|
||||
if self.templates.has_template(name):
|
||||
self.render_template(name, data)
|
||||
else:
|
||||
text = self.get_text(name)
|
||||
text = inspect.cleandoc(text)
|
||||
self.render_text(name, text, data)
|
||||
|
||||
def run_startup(self) -> None:
|
||||
for cmd in self.startup:
|
||||
|
@ -176,7 +207,6 @@ class ConfigService(abc.ABC):
|
|||
|
||||
def render_text(self, name: str, text: str, data: Dict[str, Any] = None) -> None:
|
||||
try:
|
||||
text = inspect.cleandoc(text)
|
||||
template = Template(text)
|
||||
self._render(name, template, data)
|
||||
except Exception:
|
||||
|
|
|
@ -7,6 +7,7 @@ class VpnClient(ConfigService):
|
|||
name = "VPNClient"
|
||||
group = GROUP_NAME
|
||||
directories = []
|
||||
files = ["vpnclient.sh"]
|
||||
executables = ["openvpn", "ip", "killall"]
|
||||
dependencies = []
|
||||
startup = ["sh vpnclient.sh"]
|
||||
|
@ -14,6 +15,3 @@ class VpnClient(ConfigService):
|
|||
shutdown = ["killall openvpn"]
|
||||
validation_mode = ConfigServiceMode.BLOCKING
|
||||
default_configs = []
|
||||
|
||||
def create_files(self):
|
||||
self.render_template("vpnclient.sh")
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
from typing import Any, Dict
|
||||
|
||||
import netaddr
|
||||
|
||||
from core import utils
|
||||
|
@ -12,6 +14,7 @@ class DefaultRoute(ConfigService):
|
|||
name = "DefaultRoute"
|
||||
group = GROUP_NAME
|
||||
directories = []
|
||||
files = ["defaultroute.sh"]
|
||||
executables = ["ip"]
|
||||
dependencies = []
|
||||
startup = ["sh defaultroute.sh"]
|
||||
|
@ -24,7 +27,7 @@ class DefaultRoute(ConfigService):
|
|||
Configuration(_id="value3", _type=ConfigDataTypes.STRING, label="Value 3"),
|
||||
]
|
||||
|
||||
def create_files(self):
|
||||
def data(self) -> Dict[str, Any]:
|
||||
addresses = []
|
||||
for netif in self.node.netifs():
|
||||
if getattr(netif, "control", False):
|
||||
|
@ -33,14 +36,14 @@ class DefaultRoute(ConfigService):
|
|||
net = netaddr.IPNetwork(addr)
|
||||
if net[1] != net[-2]:
|
||||
addresses.append(net[1])
|
||||
data = dict(addresses=addresses)
|
||||
self.render_template("defaultroute.sh", data)
|
||||
return dict(addresses=addresses)
|
||||
|
||||
|
||||
class IpForwardService(ConfigService):
|
||||
name = "IPForward"
|
||||
group = GROUP_NAME
|
||||
directories = []
|
||||
files = ["ipforward.sh"]
|
||||
executables = ["sysctl"]
|
||||
dependencies = []
|
||||
startup = ["sh ipforward.sh"]
|
||||
|
@ -49,10 +52,9 @@ class IpForwardService(ConfigService):
|
|||
validation_mode = ConfigServiceMode.BLOCKING
|
||||
default_configs = []
|
||||
|
||||
def create_files(self) -> None:
|
||||
def data(self) -> Dict[str, Any]:
|
||||
devnames = []
|
||||
for ifc in self.node.netifs():
|
||||
devname = utils.sysctl_devname(ifc.name)
|
||||
devnames.append(devname)
|
||||
data = dict(devnames=devnames)
|
||||
self.render_template("ipforward.sh", data)
|
||||
return dict(devnames=devnames)
|
||||
|
|
|
@ -5,6 +5,7 @@ class SimpleService(ConfigService):
|
|||
name = "Simple"
|
||||
group = "SimpleGroup"
|
||||
directories = []
|
||||
files = ["test1.sh"]
|
||||
executables = []
|
||||
dependencies = []
|
||||
startup = []
|
||||
|
@ -13,10 +14,10 @@ class SimpleService(ConfigService):
|
|||
validation_mode = ConfigServiceMode.BLOCKING
|
||||
default_configs = []
|
||||
|
||||
def create_files(self):
|
||||
text = """
|
||||
# sample script
|
||||
# node id(${node.id}) name(${node.name})
|
||||
echo hello
|
||||
"""
|
||||
self.render_text("test1.sh", text)
|
||||
def get_text(self, name: str) -> str:
|
||||
if name == "test1.sh":
|
||||
return """
|
||||
# sample script
|
||||
# node id(${node.id}) name(${node.name})
|
||||
echo hello
|
||||
"""
|
||||
|
|
|
@ -63,6 +63,7 @@ class CoreClient:
|
|||
self.app = app
|
||||
self.master = app.master
|
||||
self.services = {}
|
||||
self.config_services_groups = {}
|
||||
self.config_services = {}
|
||||
self.default_services = {}
|
||||
self.emane_models = []
|
||||
|
@ -417,7 +418,10 @@ class CoreClient:
|
|||
# get config service informations
|
||||
response = self.client.get_config_services()
|
||||
for service in response.services:
|
||||
group_services = self.config_services.setdefault(service.group, set())
|
||||
self.config_services[service.name] = service
|
||||
group_services = self.config_services_groups.setdefault(
|
||||
service.group, set()
|
||||
)
|
||||
group_services.add(service.name)
|
||||
|
||||
# if there are no sessions, create a new session, else join a session
|
||||
|
|
|
@ -46,7 +46,7 @@ class ConfigServiceConfigDialog(Dialog):
|
|||
self.default_shutdown = []
|
||||
self.validation_mode = None
|
||||
self.validation_time = None
|
||||
self.validation_period = None
|
||||
self.validation_period = tk.StringVar()
|
||||
self.documentnew_img = Images.get(ImageEnum.DOCUMENTNEW, 16)
|
||||
self.editdelete_img = Images.get(ImageEnum.EDITDELETE, 16)
|
||||
|
||||
|
@ -68,35 +68,33 @@ class ConfigServiceConfigDialog(Dialog):
|
|||
def load(self):
|
||||
try:
|
||||
self.app.core.create_nodes_and_links()
|
||||
default_config = self.app.core.get_node_service(
|
||||
self.node_id, self.service_name
|
||||
)
|
||||
self.default_startup = default_config.startup[:]
|
||||
self.default_validate = default_config.validate[:]
|
||||
self.default_shutdown = default_config.shutdown[:]
|
||||
custom_configs = self.service_configs
|
||||
if (
|
||||
self.node_id in custom_configs
|
||||
and self.service_name in custom_configs[self.node_id]
|
||||
):
|
||||
service_config = custom_configs[self.node_id][self.service_name]
|
||||
else:
|
||||
service_config = default_config
|
||||
# default_config = self.app.core.get_node_service(
|
||||
# self.node_id, self.service_name
|
||||
# )
|
||||
# self.default_startup = default_config.startup[:]
|
||||
# self.default_validate = default_config.validate[:]
|
||||
# self.default_shutdown = default_config.shutdown[:]
|
||||
# custom_configs = self.service_configs
|
||||
# if (
|
||||
# self.node_id in custom_configs
|
||||
# and self.service_name in custom_configs[self.node_id]
|
||||
# ):
|
||||
# service_config = custom_configs[self.node_id][self.service_name]
|
||||
# else:
|
||||
# service_config = default_config
|
||||
|
||||
service_config = self.core.config_services[self.service_name]
|
||||
self.dependencies = service_config.dependencies[:]
|
||||
self.executables = service_config.executables[:]
|
||||
self.filenames = service_config.configs[:]
|
||||
self.filenames = service_config.files[:]
|
||||
self.startup_commands = service_config.startup[:]
|
||||
self.validation_commands = service_config.validate[:]
|
||||
self.shutdown_commands = service_config.shutdown[:]
|
||||
self.validation_mode = service_config.validation_mode
|
||||
self.validation_time = service_config.validation_timer
|
||||
self.original_service_files = {
|
||||
x: self.app.core.get_node_service_file(
|
||||
self.node_id, self.service_name, x
|
||||
)
|
||||
for x in self.filenames
|
||||
}
|
||||
self.validation_period.set(service_config.validation_period)
|
||||
response = self.core.client.get_config_service_templates(self.service_name)
|
||||
self.original_service_files = response.templates
|
||||
self.temp_service_files = dict(self.original_service_files)
|
||||
file_configs = self.file_configs
|
||||
if (
|
||||
|
@ -303,7 +301,7 @@ class ConfigServiceConfigDialog(Dialog):
|
|||
label = ttk.Label(frame, text="Validation Period")
|
||||
label.grid(row=2, column=0, sticky="w", padx=PADX)
|
||||
self.validation_period_entry = ttk.Entry(
|
||||
frame, state=tk.DISABLED, textvariable=tk.StringVar()
|
||||
frame, state=tk.DISABLED, textvariable=self.validation_period
|
||||
)
|
||||
self.validation_period_entry.grid(row=2, column=1, sticky="ew", pady=PADY)
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ class NodeConfigServiceDialog(Dialog):
|
|||
label_frame.columnconfigure(0, weight=1)
|
||||
self.groups = ListboxScroll(label_frame)
|
||||
self.groups.grid(sticky="nsew")
|
||||
for group in sorted(self.app.core.config_services):
|
||||
for group in sorted(self.app.core.config_services_groups):
|
||||
self.groups.listbox.insert(tk.END, group)
|
||||
self.groups.listbox.bind("<<ListboxSelect>>", self.handle_group_change)
|
||||
self.groups.listbox.selection_set(0)
|
||||
|
@ -98,7 +98,7 @@ class NodeConfigServiceDialog(Dialog):
|
|||
index = selection[0]
|
||||
group = self.groups.listbox.get(index)
|
||||
self.services.clear()
|
||||
for name in sorted(self.app.core.config_services[group]):
|
||||
for name in sorted(self.app.core.config_services_groups[group]):
|
||||
checked = name in self.current_services
|
||||
self.services.add(name, checked)
|
||||
|
||||
|
|
|
@ -16,12 +16,13 @@ message ConfigService {
|
|||
repeated string executables = 3;
|
||||
repeated string dependencies = 4;
|
||||
repeated string directories = 5;
|
||||
repeated string startup = 6;
|
||||
repeated string validate = 7;
|
||||
repeated string shutdown = 8;
|
||||
ConfigServiceValidationMode.Enum validation_mode = 9;
|
||||
int32 validation_timer = 10;
|
||||
float validation_period = 11;
|
||||
repeated string files = 6;
|
||||
repeated string startup = 7;
|
||||
repeated string validate = 8;
|
||||
repeated string shutdown = 9;
|
||||
ConfigServiceValidationMode.Enum validation_mode = 10;
|
||||
int32 validation_timer = 11;
|
||||
float validation_period = 12;
|
||||
}
|
||||
|
||||
message GetConfigServicesRequest {
|
||||
|
@ -32,6 +33,14 @@ message GetConfigServicesResponse {
|
|||
repeated ConfigService services = 1;
|
||||
}
|
||||
|
||||
message GetConfigServiceTemplatesRequest {
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
message GetConfigServiceTemplatesResponse {
|
||||
map<string, string> templates = 1;
|
||||
}
|
||||
|
||||
message GetNodeConfigServiceRequest {
|
||||
int32 session_id = 1;
|
||||
int32 node_id = 2;
|
||||
|
|
|
@ -107,6 +107,8 @@ service CoreApi {
|
|||
// config services
|
||||
rpc GetConfigServices (configservices.GetConfigServicesRequest) returns (configservices.GetConfigServicesResponse) {
|
||||
}
|
||||
rpc GetConfigServiceTemplates (configservices.GetConfigServiceTemplatesRequest) returns (configservices.GetConfigServiceTemplatesResponse) {
|
||||
}
|
||||
rpc GetNodeConfigService (configservices.GetNodeConfigServiceRequest) returns (configservices.GetNodeConfigServiceResponse) {
|
||||
}
|
||||
rpc GetNodeConfigServices (configservices.GetNodeConfigServicesRequest) returns (configservices.GetNodeConfigServicesResponse) {
|
||||
|
|
Loading…
Reference in a new issue