updates to config services dialogs in coretk, initial working state for running config services from the coretk gui

This commit is contained in:
Blake Harnden 2020-01-21 10:35:46 -08:00
parent 83e7853821
commit da107cc1d9
9 changed files with 111 additions and 209 deletions

View file

@ -11,7 +11,7 @@ import grpc
import netaddr import netaddr
from core import utils from core import utils
from core.api.grpc import core_pb2, core_pb2_grpc from core.api.grpc import configservices_pb2, core_pb2, core_pb2_grpc
from core.api.grpc.configservices_pb2 import ( from core.api.grpc.configservices_pb2 import (
GetConfigServiceDefaultsRequest, GetConfigServiceDefaultsRequest,
GetConfigServiceDefaultsResponse, GetConfigServiceDefaultsResponse,
@ -175,6 +175,7 @@ class CoreGrpcClient:
service_configs: List[core_pb2.ServiceConfig] = None, service_configs: List[core_pb2.ServiceConfig] = None,
service_file_configs: List[core_pb2.ServiceFileConfig] = None, service_file_configs: List[core_pb2.ServiceFileConfig] = None,
asymmetric_links: List[core_pb2.Link] = None, asymmetric_links: List[core_pb2.Link] = None,
config_service_configs: List[configservices_pb2.ConfigServiceConfig] = None,
) -> core_pb2.StartSessionResponse: ) -> core_pb2.StartSessionResponse:
""" """
Start a session. Start a session.
@ -191,6 +192,7 @@ class CoreGrpcClient:
:param service_configs: node service configurations :param service_configs: node service configurations
:param service_file_configs: node service file configurations :param service_file_configs: node service file configurations
:param asymmetric_links: asymmetric links to edit :param asymmetric_links: asymmetric links to edit
:param config_service_configs: config service configurations
:return: start session response :return: start session response
""" """
request = core_pb2.StartSessionRequest( request = core_pb2.StartSessionRequest(
@ -206,6 +208,7 @@ class CoreGrpcClient:
service_configs=service_configs, service_configs=service_configs,
service_file_configs=service_file_configs, service_file_configs=service_file_configs,
asymmetric_links=asymmetric_links, asymmetric_links=asymmetric_links,
config_service_configs=config_service_configs,
) )
return self.stub.StartSession(request) return self.stub.StartSession(request)

View file

@ -33,6 +33,7 @@ def add_node_data(node_proto: core_pb2.Node) -> Tuple[NodeTypes, int, NodeOption
options.opaque = node_proto.opaque options.opaque = node_proto.opaque
options.image = node_proto.image options.image = node_proto.image
options.services = node_proto.services options.services = node_proto.services
options.config_services = node_proto.config_services
if node_proto.emane: if node_proto.emane:
options.emane = node_proto.emane options.emane = node_proto.emane
if node_proto.server: if node_proto.server:

View file

@ -179,6 +179,14 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
for config in request.service_configs: for config in request.service_configs:
grpcutils.service_configuration(session, config) grpcutils.service_configuration(session, config)
# config service configs
for config in request.config_service_configs:
node = self.get_node(session, config.node_id, context)
service = node.config_services[request.name]
service.set_config(config.config)
for name, template in config.templates.values():
service.custom_template(name, template)
# service file configs # service file configs
for config in request.service_file_configs: for config in request.service_file_configs:
session.services.set_service_file( session.services.set_service_file(

View file

@ -4,8 +4,8 @@ from core.configservice.base import ConfigService, ConfigServiceMode
class SimpleService(ConfigService): class SimpleService(ConfigService):
name = "Simple" name = "Simple"
group = "SimpleGroup" group = "SimpleGroup"
directories = [] directories = ["/etc/quagga", "/usr/local/lib"]
files = ["test1.sh"] files = ["test1.sh", "test2.sh"]
executables = [] executables = []
dependencies = [] dependencies = []
startup = [] startup = []
@ -17,7 +17,13 @@ class SimpleService(ConfigService):
def get_text(self, name: str) -> str: def get_text(self, name: str) -> str:
if name == "test1.sh": if name == "test1.sh":
return """ return """
# sample script # sample script 1
# node id(${node.id}) name(${node.name}) # node id(${node.id}) name(${node.name})
echo hello echo hello
""" """
elif name == "test2.sh":
return """
# sample script 2
# node id(${node.id}) name(${node.name})
echo hello2
"""

View file

@ -9,7 +9,6 @@ from typing import TYPE_CHECKING, Any, List
import grpc import grpc
from core.api.grpc import core_pb2 from core.api.grpc import core_pb2
from core.gui.dialogs.copyserviceconfig import CopyServiceConfigDialog
from core.gui.dialogs.dialog import Dialog from core.gui.dialogs.dialog import Dialog
from core.gui.errors import show_grpc_error from core.gui.errors import show_grpc_error
from core.gui.images import ImageEnum, Images from core.gui.images import ImageEnum, Images
@ -35,7 +34,8 @@ class ConfigServiceConfigDialog(Dialog):
self.radiovar = tk.IntVar() self.radiovar = tk.IntVar()
self.radiovar.set(2) self.radiovar.set(2)
self.filenames = [] self.directories = []
self.templates = []
self.dependencies = [] self.dependencies = []
self.executables = [] self.executables = []
self.startup_commands = [] self.startup_commands = []
@ -51,29 +51,31 @@ class ConfigServiceConfigDialog(Dialog):
self.editdelete_img = Images.get(ImageEnum.EDITDELETE, 16) self.editdelete_img = Images.get(ImageEnum.EDITDELETE, 16)
self.notebook = None self.notebook = None
self.filename_combobox = None self.templates_combobox = None
self.startup_commands_listbox = None self.startup_commands_listbox = None
self.shutdown_commands_listbox = None self.shutdown_commands_listbox = None
self.validate_commands_listbox = None self.validate_commands_listbox = None
self.validation_time_entry = None self.validation_time_entry = None
self.validation_mode_entry = None self.validation_mode_entry = None
self.service_file_data = None self.template_text = None
self.validation_period_entry = None self.validation_period_entry = None
self.original_service_files = {} self.original_service_files = {}
self.temp_service_files = {} self.temp_service_files = {}
self.modified_files = set() self.modified_files = set()
self.config_frame = None self.config_frame = None
self.default_config = None
self.config = None self.config = None
self.load() self.load()
self.draw() self.draw()
def load(self): def load(self):
try: try:
self.app.core.create_nodes_and_links() self.core.create_nodes_and_links()
service = self.core.config_services[self.service_name] service = self.core.config_services[self.service_name]
self.dependencies = service.dependencies[:] self.dependencies = service.dependencies[:]
self.executables = service.executables[:] self.executables = service.executables[:]
self.filenames = service.files[:] self.directories = service.directories[:]
self.templates = service.files[:]
self.startup_commands = service.startup[:] self.startup_commands = service.startup[:]
self.validation_commands = service.validate[:] self.validation_commands = service.validate[:]
self.shutdown_commands = service.shutdown[:] self.shutdown_commands = service.shutdown[:]
@ -84,10 +86,17 @@ 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.config = response.config
node_configs = self.service_configs.get(self.node_id, {}) node_configs = self.service_configs.get(self.node_id, {})
service_config = node_configs.get(self.service_name, {}) service_config = node_configs.get(self.service_name, {})
self.default_config = response.config
custom_config = service_config.get("config")
if custom_config:
self.config = custom_config
else:
self.config = dict(self.default_config)
custom_templates = service_config.get("templates", {}) custom_templates = service_config.get("templates", {})
for file, data in custom_templates.items(): for file, data in custom_templates.items():
self.temp_service_files[file] = data self.temp_service_files[file] = data
@ -96,98 +105,61 @@ class ConfigServiceConfigDialog(Dialog):
def draw(self): def draw(self):
self.top.columnconfigure(0, weight=1) self.top.columnconfigure(0, weight=1)
self.top.rowconfigure(1, weight=1) self.top.rowconfigure(0, weight=1)
# draw notebook # draw notebook
self.notebook = ttk.Notebook(self.top) self.notebook = ttk.Notebook(self.top)
self.notebook.grid(sticky="nsew", pady=PADY) self.notebook.grid(sticky="nsew", pady=PADY)
self.draw_tab_files() self.draw_tab_files()
if self.config:
self.draw_tab_config() self.draw_tab_config()
self.draw_tab_directories()
self.draw_tab_startstop() self.draw_tab_startstop()
self.draw_tab_validation() self.draw_tab_validation()
self.draw_buttons() self.draw_buttons()
def draw_tab_files(self): def draw_tab_files(self):
tab = ttk.Frame(self.notebook, padding=FRAME_PAD) tab = ttk.Frame(self.notebook, padding=FRAME_PAD)
tab.grid(sticky="nsew") tab.grid(sticky="nsew")
tab.columnconfigure(0, weight=1) tab.columnconfigure(0, weight=1)
self.notebook.add(tab, text="Files") self.notebook.add(tab, text="Directories/Files")
label = ttk.Label( label = ttk.Label(
tab, text="Config files and scripts that are generated for this service." tab, text="Directories and templates that will be used for this service."
) )
label.grid() label.grid(pady=PADY)
frame = ttk.Frame(tab) frame = ttk.Frame(tab)
frame.grid(sticky="ew", pady=PADY) frame.grid(sticky="ew", pady=PADY)
frame.columnconfigure(1, weight=1) frame.columnconfigure(1, weight=1)
label = ttk.Label(frame, text="File Name") label = ttk.Label(frame, text="Directories")
label.grid(row=0, column=0, padx=PADX, sticky="w") label.grid(row=0, column=0, sticky="w", padx=PADX)
self.filename_combobox = ttk.Combobox( directories_combobox = ttk.Combobox(
frame, values=self.filenames, state="readonly" frame, values=self.directories, state="readonly"
) )
self.filename_combobox.bind( directories_combobox.grid(row=0, column=1, sticky="ew", pady=PADY)
"<<ComboboxSelected>>", self.display_service_file_data if self.directories:
) directories_combobox.current(0)
self.filename_combobox.grid(row=0, column=1, sticky="ew", padx=PADX)
button = ttk.Button(frame, image=self.documentnew_img, state="disabled")
button.bind("<Button-1>", self.add_filename)
button.grid(row=0, column=2, padx=PADX)
button = ttk.Button(frame, image=self.editdelete_img, state="disabled")
button.bind("<Button-1>", self.delete_filename)
button.grid(row=0, column=3)
frame = ttk.Frame(tab) label = ttk.Label(frame, text="Templates")
frame.grid(sticky="ew", pady=PADY) label.grid(row=1, column=0, sticky="w", padx=PADX)
frame.columnconfigure(1, weight=1) self.templates_combobox = ttk.Combobox(
button = ttk.Radiobutton( frame, values=self.templates, state="readonly"
frame,
variable=self.radiovar,
text="Copy Source File",
value=1,
state=tk.DISABLED,
) )
button.grid(row=0, column=0, sticky="w", padx=PADX) self.templates_combobox.bind(
entry = ttk.Entry(frame, state=tk.DISABLED) "<<ComboboxSelected>>", self.handle_template_changed
entry.grid(row=0, column=1, sticky="ew", padx=PADX) )
image = Images.get(ImageEnum.FILEOPEN, 16) self.templates_combobox.grid(row=1, column=1, sticky="ew", pady=PADY)
button = ttk.Button(frame, image=image)
button.image = image
button.grid(row=0, column=2)
frame = ttk.Frame(tab) self.template_text = CodeText(tab)
frame.grid(sticky="ew", pady=PADY) self.template_text.grid(sticky="nsew")
frame.columnconfigure(0, weight=1) tab.rowconfigure(self.template_text.grid_info()["row"], weight=1)
button = ttk.Radiobutton( if self.templates:
frame, self.templates_combobox.current(0)
variable=self.radiovar, self.template_text.text.delete(1.0, "end")
text="Use text below for file contents", self.template_text.text.insert(
value=2, "end", self.temp_service_files[self.templates[0]]
)
button.grid(row=0, column=0, sticky="ew")
image = Images.get(ImageEnum.FILEOPEN, 16)
button = ttk.Button(frame, image=image)
button.image = image
button.grid(row=0, column=1)
image = Images.get(ImageEnum.DOCUMENTSAVE, 16)
button = ttk.Button(frame, image=image)
button.image = image
button.grid(row=0, column=2)
self.service_file_data = CodeText(tab)
self.service_file_data.grid(sticky="nsew")
tab.rowconfigure(self.service_file_data.grid_info()["row"], weight=1)
if len(self.filenames) > 0:
self.filename_combobox.current(0)
self.service_file_data.text.delete(1.0, "end")
self.service_file_data.text.insert(
"end", self.temp_service_files[self.filenames[0]]
)
self.service_file_data.text.bind(
"<FocusOut>", self.update_temp_service_file_data
) )
self.template_text.text.bind("<FocusOut>", self.update_template_file_data)
def draw_tab_config(self): def draw_tab_config(self):
tab = ttk.Frame(self.notebook, padding=FRAME_PAD) tab = ttk.Frame(self.notebook, padding=FRAME_PAD)
@ -199,18 +171,6 @@ class ConfigServiceConfigDialog(Dialog):
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)
def draw_tab_directories(self):
tab = ttk.Frame(self.notebook, padding=FRAME_PAD)
tab.grid(sticky="nsew")
tab.columnconfigure(0, weight=1)
self.notebook.add(tab, text="Directories")
label = ttk.Label(
tab,
text="Directories required by this service that are unique for each node.",
)
label.grid()
def draw_tab_startstop(self): def draw_tab_startstop(self):
tab = ttk.Frame(self.notebook, padding=FRAME_PAD) tab = ttk.Frame(self.notebook, padding=FRAME_PAD)
tab.grid(sticky="nsew") tab.grid(sticky="nsew")
@ -238,26 +198,13 @@ class ConfigServiceConfigDialog(Dialog):
) )
commands = self.validation_commands commands = self.validation_commands
label_frame.columnconfigure(0, weight=1) label_frame.columnconfigure(0, weight=1)
label_frame.rowconfigure(1, weight=1) label_frame.rowconfigure(0, weight=1)
label_frame.grid(row=i, column=0, sticky="nsew", pady=PADY) label_frame.grid(row=i, column=0, sticky="nsew", pady=PADY)
frame = ttk.Frame(label_frame)
frame.grid(row=0, column=0, sticky="nsew", pady=PADY)
frame.columnconfigure(0, weight=1)
entry = ttk.Entry(frame, textvariable=tk.StringVar())
entry.grid(row=0, column=0, stick="ew", padx=PADX)
button = ttk.Button(frame, image=self.documentnew_img)
button.bind("<Button-1>", self.add_command)
button.grid(row=0, column=1, sticky="ew", padx=PADX)
button = ttk.Button(frame, image=self.editdelete_img)
button.grid(row=0, column=2, sticky="ew")
button.bind("<Button-1>", self.delete_command)
listbox_scroll = ListboxScroll(label_frame) listbox_scroll = ListboxScroll(label_frame)
listbox_scroll.listbox.bind("<<ListboxSelect>>", self.update_entry)
for command in commands: for command in commands:
listbox_scroll.listbox.insert("end", command) listbox_scroll.listbox.insert("end", command)
listbox_scroll.listbox.config(height=4) listbox_scroll.listbox.config(height=4)
listbox_scroll.grid(row=1, column=0, sticky="nsew") listbox_scroll.grid(sticky="nsew")
if i == 0: if i == 0:
self.startup_commands_listbox = listbox_scroll.listbox self.startup_commands_listbox = listbox_scroll.listbox
elif i == 1: elif i == 1:
@ -267,7 +214,7 @@ class ConfigServiceConfigDialog(Dialog):
def draw_tab_validation(self): def draw_tab_validation(self):
tab = ttk.Frame(self.notebook, padding=FRAME_PAD) tab = ttk.Frame(self.notebook, padding=FRAME_PAD)
tab.grid(sticky="nsew") tab.grid(sticky="ew")
tab.columnconfigure(0, weight=1) tab.columnconfigure(0, weight=1)
self.notebook.add(tab, text="Validation", sticky="nsew") self.notebook.add(tab, text="Validation", sticky="nsew")
@ -338,57 +285,6 @@ class ConfigServiceConfigDialog(Dialog):
button = ttk.Button(frame, text="Cancel", command=self.destroy) button = ttk.Button(frame, text="Cancel", command=self.destroy)
button.grid(row=0, column=3, sticky="ew") button.grid(row=0, column=3, sticky="ew")
def add_filename(self, event: tk.Event):
# not worry about it for now
return
frame_contains_button = event.widget.master
combobox = frame_contains_button.grid_slaves(row=0, column=1)[0]
filename = combobox.get()
if filename not in combobox["values"]:
combobox["values"] += (filename,)
def delete_filename(self, event: tk.Event):
# not worry about it for now
return
frame_comntains_button = event.widget.master
combobox = frame_comntains_button.grid_slaves(row=0, column=1)[0]
filename = combobox.get()
if filename in combobox["values"]:
combobox["values"] = tuple([x for x in combobox["values"] if x != filename])
combobox.set("")
def add_command(self, event: tk.Event):
frame_contains_button = event.widget.master
listbox = frame_contains_button.master.grid_slaves(row=1, column=0)[0].listbox
command_to_add = frame_contains_button.grid_slaves(row=0, column=0)[0].get()
if command_to_add == "":
return
for cmd in listbox.get(0, tk.END):
if cmd == command_to_add:
return
listbox.insert(tk.END, command_to_add)
def update_entry(self, event: tk.Event):
listbox = event.widget
current_selection = listbox.curselection()
if len(current_selection) > 0:
cmd = listbox.get(current_selection[0])
entry = listbox.master.master.grid_slaves(row=0, column=0)[0].grid_slaves(
row=0, column=0
)[0]
entry.delete(0, "end")
entry.insert(0, cmd)
def delete_command(self, event: tk.Event):
button = event.widget
frame_contains_button = button.master
listbox = frame_contains_button.master.grid_slaves(row=1, column=0)[0].listbox
current_selection = listbox.curselection()
if len(current_selection) > 0:
listbox.delete(current_selection[0])
entry = frame_contains_button.grid_slaves(row=0, column=0)[0]
entry.delete(0, tk.END)
def click_apply(self): def click_apply(self):
current_listbox = self.master.current.listbox 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_service_config() and not self.is_custom_service_file():
@ -399,53 +295,32 @@ class ConfigServiceConfigDialog(Dialog):
return return
try: try:
if self.is_custom_service_config(): node_config = self.service_configs.setdefault(self.node_id, {})
startup_commands = self.startup_commands_listbox.get(0, "end") service_config = node_config.setdefault(self.service_name, {})
shutdown_commands = self.shutdown_commands_listbox.get(0, "end") self.config_frame.parse_config()
validate_commands = self.validate_commands_listbox.get(0, "end") service_config["config"] = self.config
config = self.core.set_node_service( templates_config = service_config.setdefault("templates", {})
self.node_id,
self.service_name,
startup_commands,
validate_commands,
shutdown_commands,
)
if self.node_id not in self.service_configs:
self.service_configs[self.node_id] = {}
self.service_configs[self.node_id][self.service_name] = config
for file in self.modified_files: for file in self.modified_files:
if self.node_id not in self.file_configs: templates_config[file] = self.temp_service_files[file]
self.file_configs[self.node_id] = {}
if self.service_name not in self.file_configs[self.node_id]:
self.file_configs[self.node_id][self.service_name] = {}
self.file_configs[self.node_id][self.service_name][
file
] = self.temp_service_files[file]
self.app.core.set_node_service_file(
self.node_id, self.service_name, 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")
except grpc.RpcError as e: except grpc.RpcError as e:
show_grpc_error(e) show_grpc_error(e)
self.destroy() self.destroy()
def display_service_file_data(self, event: tk.Event): def handle_template_changed(self, event: tk.Event):
combobox = event.widget template = self.templates_combobox.get()
filename = combobox.get() self.template_text.text.delete(1.0, "end")
self.service_file_data.text.delete(1.0, "end") self.template_text.text.insert("end", self.temp_service_files[template])
self.service_file_data.text.insert("end", self.temp_service_files[filename])
def update_temp_service_file_data(self, event: tk.Event): def update_template_file_data(self, event: tk.Event):
scrolledtext = event.widget scrolledtext = event.widget
filename = self.filename_combobox.get() template = self.templates_combobox.get()
self.temp_service_files[filename] = scrolledtext.get(1.0, "end") self.temp_service_files[template] = scrolledtext.get(1.0, "end")
if self.temp_service_files[filename] != self.original_service_files[filename]: if self.temp_service_files[template] != self.original_service_files[template]:
self.modified_files.add(filename) self.modified_files.add(template)
else: else:
self.modified_files.discard(filename) self.modified_files.discard(template)
def is_custom_service_config(self): def is_custom_service_config(self):
startup_commands = self.startup_commands_listbox.get(0, "end") startup_commands = self.startup_commands_listbox.get(0, "end")
@ -462,13 +337,13 @@ class ConfigServiceConfigDialog(Dialog):
def click_defaults(self): def click_defaults(self):
if self.node_id in self.service_configs: if self.node_id in self.service_configs:
self.service_configs[self.node_id].pop(self.service_name, None) node_config = self.service_configs.get(self.node_id, {})
if self.node_id in self.file_configs: node_config.pop(self.service_name, None)
self.file_configs[self.node_id].pop(self.service_name, None)
self.temp_service_files = dict(self.original_service_files) self.temp_service_files = dict(self.original_service_files)
filename = self.filename_combobox.get() filename = self.templates_combobox.get()
self.service_file_data.text.delete(1.0, "end") self.template_text.text.delete(1.0, "end")
self.service_file_data.text.insert("end", self.temp_service_files[filename]) self.template_text.text.insert("end", self.temp_service_files[filename])
self.config_frame.set_values(self.default_config)
self.startup_commands_listbox.delete(0, tk.END) self.startup_commands_listbox.delete(0, tk.END)
self.validate_commands_listbox.delete(0, tk.END) self.validate_commands_listbox.delete(0, tk.END)
self.shutdown_commands_listbox.delete(0, tk.END) self.shutdown_commands_listbox.delete(0, tk.END)
@ -480,8 +355,7 @@ class ConfigServiceConfigDialog(Dialog):
self.shutdown_commands_listbox.insert(tk.END, cmd) self.shutdown_commands_listbox.insert(tk.END, cmd)
def click_copy(self): def click_copy(self):
dialog = CopyServiceConfigDialog(self, self.app, self.node_id) pass
dialog.show()
def append_commands( def append_commands(
self, commands: List[str], listbox: tk.Listbox, to_add: List[str] self, commands: List[str], listbox: tk.Listbox, to_add: List[str]

View file

@ -1,6 +1,7 @@
""" """
core node services core node services
""" """
import logging
import tkinter as tk import tkinter as tk
from tkinter import messagebox, ttk from tkinter import messagebox, ttk
from typing import TYPE_CHECKING, Any, Set from typing import TYPE_CHECKING, Any, Set
@ -130,14 +131,10 @@ class NodeConfigServiceDialog(Dialog):
) )
def click_save(self): def click_save(self):
if (
self.current_services
!= self.app.core.default_services[self.canvas_node.core_node.model]
):
self.canvas_node.core_node.config_services[:] = self.current_services self.canvas_node.core_node.config_services[:] = self.current_services
else: logging.info(
if len(self.canvas_node.core_node.config_services) > 0: "saved node config services: %s", self.canvas_node.core_node.config_services
self.canvas_node.core_node.config_services[:] = [] )
self.destroy() self.destroy()
def click_cancel(self): def click_cancel(self):

View file

@ -184,6 +184,11 @@ class ConfigFrame(ttk.Notebook):
return {x: self.config[x].value for x in self.config} return {x: self.config[x].value for x in self.config}
def set_values(self, config: Dict[str, common_pb2.ConfigOption]) -> None:
for name, option in config.items():
value = self.values[name]
value.set(option.value)
class ListboxScroll(ttk.Frame): class ListboxScroll(ttk.Frame):
def __init__(self, master: tk.Widget = None, **kw): def __init__(self, master: tk.Widget = None, **kw):

View file

@ -4,6 +4,13 @@ package configservices;
import "core/api/grpc/common.proto"; import "core/api/grpc/common.proto";
message ConfigServiceConfig {
int32 node_id = 1;
string name = 2;
map<string, string> templates = 3;
map<string, string> config = 4;
}
message ConfigServiceValidationMode { message ConfigServiceValidationMode {
enum Enum { enum Enum {
BLOCKING = 0; BLOCKING = 0;

View file

@ -166,6 +166,7 @@ message StartSessionRequest {
repeated ServiceConfig service_configs = 10; repeated ServiceConfig service_configs = 10;
repeated ServiceFileConfig service_file_configs = 11; repeated ServiceFileConfig service_file_configs = 11;
repeated Link asymmetric_links = 12; repeated Link asymmetric_links = 12;
repeated configservices.ConfigServiceConfig config_service_configs = 13;
} }
message StartSessionResponse { message StartSessionResponse {