updated linkconfig to support asymmetric links, updated grpc start session to provide asymmetric links, since they currently depend on being processed as a link edit
This commit is contained in:
parent
1884cda271
commit
8eb4df7b1d
10 changed files with 162 additions and 55 deletions
|
@ -452,6 +452,9 @@ class CoreClient:
|
||||||
hooks = list(self.hooks.values())
|
hooks = list(self.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
|
||||||
|
]
|
||||||
if self.emane_config:
|
if self.emane_config:
|
||||||
emane_config = {x: self.emane_config[x].value for x in self.emane_config}
|
emane_config = {x: self.emane_config[x].value for x in self.emane_config}
|
||||||
else:
|
else:
|
||||||
|
@ -471,6 +474,7 @@ class CoreClient:
|
||||||
mobility_configs,
|
mobility_configs,
|
||||||
service_configs,
|
service_configs,
|
||||||
file_configs,
|
file_configs,
|
||||||
|
asymmetric_links,
|
||||||
)
|
)
|
||||||
self.set_metadata()
|
self.set_metadata()
|
||||||
process_time = time.perf_counter() - start
|
process_time = time.perf_counter() - start
|
||||||
|
|
|
@ -5,17 +5,34 @@ import logging
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from tkinter import ttk
|
from tkinter import ttk
|
||||||
|
|
||||||
|
from core.api.grpc import core_pb2
|
||||||
from coretk.dialogs.colorpicker import ColorPicker
|
from coretk.dialogs.colorpicker import ColorPicker
|
||||||
from coretk.dialogs.dialog import Dialog
|
from coretk.dialogs.dialog import Dialog
|
||||||
from coretk.themes import PADX, PADY
|
from coretk.themes import PADX, PADY
|
||||||
|
|
||||||
|
|
||||||
|
def get_int(var):
|
||||||
|
value = var.get()
|
||||||
|
if value != "":
|
||||||
|
return int(value)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def get_float(var):
|
||||||
|
value = var.get()
|
||||||
|
if value != "":
|
||||||
|
return float(value)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
class LinkConfiguration(Dialog):
|
class LinkConfiguration(Dialog):
|
||||||
def __init__(self, master, app, edge):
|
def __init__(self, master, app, edge):
|
||||||
super().__init__(master, app, "Link Configuration", modal=True)
|
super().__init__(master, app, "Link Configuration", modal=True)
|
||||||
self.app = app
|
self.app = app
|
||||||
self.edge = edge
|
self.edge = edge
|
||||||
self.is_symmetric = True
|
self.is_symmetric = edge.link.options.unidirectional is False
|
||||||
if self.is_symmetric:
|
if self.is_symmetric:
|
||||||
self.symmetry_var = tk.StringVar(value=">>")
|
self.symmetry_var = tk.StringVar(value=">>")
|
||||||
else:
|
else:
|
||||||
|
@ -26,16 +43,17 @@ class LinkConfiguration(Dialog):
|
||||||
self.jitter = tk.StringVar()
|
self.jitter = tk.StringVar()
|
||||||
self.loss = tk.StringVar()
|
self.loss = tk.StringVar()
|
||||||
self.duplicate = tk.StringVar()
|
self.duplicate = tk.StringVar()
|
||||||
|
|
||||||
|
self.down_bandwidth = tk.StringVar()
|
||||||
|
self.down_delay = tk.StringVar()
|
||||||
|
self.down_jitter = tk.StringVar()
|
||||||
|
self.down_loss = tk.StringVar()
|
||||||
|
self.down_duplicate = tk.StringVar()
|
||||||
|
|
||||||
self.color = tk.StringVar(value="#000000")
|
self.color = tk.StringVar(value="#000000")
|
||||||
self.color_button = None
|
self.color_button = None
|
||||||
self.width = tk.DoubleVar()
|
self.width = tk.DoubleVar()
|
||||||
|
|
||||||
self.down_bandwidth = tk.IntVar(value="")
|
|
||||||
self.down_delay = tk.IntVar(value="")
|
|
||||||
self.down_jitter = tk.IntVar(value="")
|
|
||||||
self.down_loss = tk.DoubleVar(value="")
|
|
||||||
self.down_duplicate = tk.IntVar(value="")
|
|
||||||
|
|
||||||
self.load_link_config()
|
self.load_link_config()
|
||||||
self.symmetric_frame = None
|
self.symmetric_frame = None
|
||||||
self.asymmetric_frame = None
|
self.asymmetric_frame = None
|
||||||
|
@ -113,7 +131,7 @@ class LinkConfiguration(Dialog):
|
||||||
validate="key",
|
validate="key",
|
||||||
validatecommand=(self.app.validation.positive_int, "%P"),
|
validatecommand=(self.app.validation.positive_int, "%P"),
|
||||||
)
|
)
|
||||||
entry.grid(row=row, column=2, sticky="nsew")
|
entry.grid(row=row, column=2, sticky="ew", pady=PADY)
|
||||||
row = row + 1
|
row = row + 1
|
||||||
|
|
||||||
label = ttk.Label(frame, text="Delay (us)")
|
label = ttk.Label(frame, text="Delay (us)")
|
||||||
|
@ -132,7 +150,7 @@ class LinkConfiguration(Dialog):
|
||||||
validate="key",
|
validate="key",
|
||||||
validatecommand=(self.app.validation.positive_int, "%P"),
|
validatecommand=(self.app.validation.positive_int, "%P"),
|
||||||
)
|
)
|
||||||
entry.grid(row=row, column=2, sticky="ew")
|
entry.grid(row=row, column=2, sticky="ew", pady=PADY)
|
||||||
row = row + 1
|
row = row + 1
|
||||||
|
|
||||||
label = ttk.Label(frame, text="Jitter (us)")
|
label = ttk.Label(frame, text="Jitter (us)")
|
||||||
|
@ -151,7 +169,7 @@ class LinkConfiguration(Dialog):
|
||||||
validate="key",
|
validate="key",
|
||||||
validatecommand=(self.app.validation.positive_int, "%P"),
|
validatecommand=(self.app.validation.positive_int, "%P"),
|
||||||
)
|
)
|
||||||
entry.grid(row=row, column=2, sticky="ew")
|
entry.grid(row=row, column=2, sticky="ew", pady=PADY)
|
||||||
row = row + 1
|
row = row + 1
|
||||||
|
|
||||||
label = ttk.Label(frame, text="Loss (%)")
|
label = ttk.Label(frame, text="Loss (%)")
|
||||||
|
@ -170,7 +188,7 @@ class LinkConfiguration(Dialog):
|
||||||
validate="key",
|
validate="key",
|
||||||
validatecommand=(self.app.validation.positive_float, "%P"),
|
validatecommand=(self.app.validation.positive_float, "%P"),
|
||||||
)
|
)
|
||||||
entry.grid(row=row, column=2, sticky="ew")
|
entry.grid(row=row, column=2, sticky="ew", pady=PADY)
|
||||||
row = row + 1
|
row = row + 1
|
||||||
|
|
||||||
label = ttk.Label(frame, text="Duplicate (%)")
|
label = ttk.Label(frame, text="Duplicate (%)")
|
||||||
|
@ -189,7 +207,7 @@ class LinkConfiguration(Dialog):
|
||||||
validate="key",
|
validate="key",
|
||||||
validatecommand=(self.app.validation.positive_int, "%P"),
|
validatecommand=(self.app.validation.positive_int, "%P"),
|
||||||
)
|
)
|
||||||
entry.grid(row=row, column=2, sticky="ew")
|
entry.grid(row=row, column=2, sticky="ew", pady=PADY)
|
||||||
row = row + 1
|
row = row + 1
|
||||||
|
|
||||||
label = ttk.Label(frame, text="Color")
|
label = ttk.Label(frame, text="Color")
|
||||||
|
@ -229,29 +247,56 @@ class LinkConfiguration(Dialog):
|
||||||
self.app.canvas.itemconfigure(self.edge.id, width=self.width.get())
|
self.app.canvas.itemconfigure(self.edge.id, width=self.width.get())
|
||||||
self.app.canvas.itemconfigure(self.edge.id, fill=self.color.get())
|
self.app.canvas.itemconfigure(self.edge.id, fill=self.color.get())
|
||||||
link = self.edge.link
|
link = self.edge.link
|
||||||
bandwidth = self.bandwidth.get()
|
bandwidth = get_int(self.bandwidth)
|
||||||
if bandwidth != "":
|
jitter = get_int(self.jitter)
|
||||||
link.options.bandwidth = int(bandwidth)
|
delay = get_int(self.delay)
|
||||||
jitter = self.jitter.get()
|
duplicate = get_int(self.duplicate)
|
||||||
if jitter != "":
|
loss = get_float(self.loss)
|
||||||
link.options.jitter = int(jitter)
|
options = core_pb2.LinkOptions(
|
||||||
delay = self.delay.get()
|
bandwidth=bandwidth, jitter=jitter, delay=delay, dup=duplicate, per=loss
|
||||||
if delay != "":
|
)
|
||||||
link.options.delay = int(delay)
|
link.options.CopyFrom(options)
|
||||||
duplicate = self.duplicate.get()
|
|
||||||
if duplicate != "":
|
interface_one = None
|
||||||
link.options.dup = int(duplicate)
|
if link.HasField("interface_one"):
|
||||||
loss = self.loss.get()
|
interface_one = link.interface_one.id
|
||||||
if loss != "":
|
interface_two = None
|
||||||
link.options.per = float(loss)
|
if link.HasField("interface_two"):
|
||||||
|
interface_two = link.interface_two.id
|
||||||
|
|
||||||
|
if not self.is_symmetric:
|
||||||
|
link.options.unidirectional = True
|
||||||
|
asym_interface_one = None
|
||||||
|
if interface_one:
|
||||||
|
asym_interface_one = core_pb2.Interface(id=interface_one)
|
||||||
|
asym_interface_two = None
|
||||||
|
if interface_two:
|
||||||
|
asym_interface_two = core_pb2.Interface(id=interface_two)
|
||||||
|
down_bandwidth = get_int(self.down_bandwidth)
|
||||||
|
down_jitter = get_int(self.down_jitter)
|
||||||
|
down_delay = get_int(self.down_delay)
|
||||||
|
down_duplicate = get_int(self.down_duplicate)
|
||||||
|
down_loss = get_float(self.down_loss)
|
||||||
|
options = core_pb2.LinkOptions(
|
||||||
|
bandwidth=down_bandwidth,
|
||||||
|
jitter=down_jitter,
|
||||||
|
delay=down_delay,
|
||||||
|
dup=down_duplicate,
|
||||||
|
per=down_loss,
|
||||||
|
unidirectional=True,
|
||||||
|
)
|
||||||
|
self.edge.asymmetric_link = core_pb2.Link(
|
||||||
|
node_one_id=link.node_two_id,
|
||||||
|
node_two_id=link.node_one_id,
|
||||||
|
interface_one=asym_interface_one,
|
||||||
|
interface_two=asym_interface_two,
|
||||||
|
options=options,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
link.options.unidirectional = False
|
||||||
|
self.edge.asymmetric_link = None
|
||||||
|
|
||||||
if self.app.core.is_runtime() and link.HasField("options"):
|
if self.app.core.is_runtime() and link.HasField("options"):
|
||||||
interface_one = None
|
|
||||||
if link.HasField("interface_one"):
|
|
||||||
interface_one = link.interface_one.id
|
|
||||||
interface_two = None
|
|
||||||
if link.HasField("interface_two"):
|
|
||||||
interface_two = link.interface_two.id
|
|
||||||
session_id = self.app.core.session_id
|
session_id = self.app.core.session_id
|
||||||
self.app.core.client.edit_link(
|
self.app.core.client.edit_link(
|
||||||
session_id,
|
session_id,
|
||||||
|
@ -261,6 +306,15 @@ class LinkConfiguration(Dialog):
|
||||||
interface_one,
|
interface_one,
|
||||||
interface_two,
|
interface_two,
|
||||||
)
|
)
|
||||||
|
if self.edge.asymmetric_link:
|
||||||
|
self.app.core.client.edit_link(
|
||||||
|
session_id,
|
||||||
|
link.node_two_id,
|
||||||
|
link.node_one_id,
|
||||||
|
self.edge.asymmetric_link.options,
|
||||||
|
interface_one,
|
||||||
|
interface_two,
|
||||||
|
)
|
||||||
|
|
||||||
self.destroy()
|
self.destroy()
|
||||||
|
|
||||||
|
@ -299,3 +353,10 @@ class LinkConfiguration(Dialog):
|
||||||
self.duplicate.set(str(link.options.dup))
|
self.duplicate.set(str(link.options.dup))
|
||||||
self.loss.set(str(link.options.per))
|
self.loss.set(str(link.options.per))
|
||||||
self.delay.set(str(link.options.delay))
|
self.delay.set(str(link.options.delay))
|
||||||
|
if not self.is_symmetric:
|
||||||
|
asym_link = self.edge.asymmetric_link
|
||||||
|
self.down_bandwidth.set(str(asym_link.options.bandwidth))
|
||||||
|
self.down_jitter.set(str(asym_link.options.jitter))
|
||||||
|
self.down_duplicate.set(str(asym_link.options.dup))
|
||||||
|
self.down_loss.set(str(asym_link.options.per))
|
||||||
|
self.down_delay.set(str(asym_link.options.delay))
|
||||||
|
|
|
@ -54,6 +54,7 @@ class CanvasEdge:
|
||||||
self.token = None
|
self.token = None
|
||||||
self.font = Font(size=8)
|
self.font = Font(size=8)
|
||||||
self.link = None
|
self.link = None
|
||||||
|
self.asymmetric_link = None
|
||||||
self.throughput = None
|
self.throughput = None
|
||||||
self.set_binding()
|
self.set_binding()
|
||||||
|
|
||||||
|
|
|
@ -201,12 +201,12 @@ class CanvasGraph(tk.Canvas):
|
||||||
"""
|
"""
|
||||||
# draw existing nodes
|
# draw existing nodes
|
||||||
for core_node in session.nodes:
|
for core_node in session.nodes:
|
||||||
logging.info("drawing core 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
|
||||||
|
|
||||||
# draw nodes on the canvas
|
# draw nodes on the canvas
|
||||||
|
logging.info("drawing core node: %s", core_node)
|
||||||
image = NodeUtils.node_icon(core_node.type, core_node.model)
|
image = NodeUtils.node_icon(core_node.type, core_node.model)
|
||||||
if core_node.icon:
|
if core_node.icon:
|
||||||
try:
|
try:
|
||||||
|
@ -222,33 +222,42 @@ class CanvasGraph(tk.Canvas):
|
||||||
|
|
||||||
# draw existing links
|
# draw existing links
|
||||||
for link in session.links:
|
for link in session.links:
|
||||||
|
logging.info("drawing link: %s", link)
|
||||||
canvas_node_one = self.core.canvas_nodes[link.node_one_id]
|
canvas_node_one = self.core.canvas_nodes[link.node_one_id]
|
||||||
node_one = canvas_node_one.core_node
|
node_one = canvas_node_one.core_node
|
||||||
canvas_node_two = self.core.canvas_nodes[link.node_two_id]
|
canvas_node_two = self.core.canvas_nodes[link.node_two_id]
|
||||||
node_two = canvas_node_two.core_node
|
node_two = canvas_node_two.core_node
|
||||||
|
token = tuple(sorted((canvas_node_one.id, canvas_node_two.id)))
|
||||||
|
|
||||||
if link.type == core_pb2.LinkType.WIRELESS:
|
if link.type == core_pb2.LinkType.WIRELESS:
|
||||||
self.add_wireless_edge(canvas_node_one, canvas_node_two)
|
self.add_wireless_edge(canvas_node_one, canvas_node_two)
|
||||||
else:
|
else:
|
||||||
edge = CanvasEdge(
|
if token not in self.edges:
|
||||||
node_one.position.x,
|
edge = CanvasEdge(
|
||||||
node_one.position.y,
|
node_one.position.x,
|
||||||
node_two.position.x,
|
node_one.position.y,
|
||||||
node_two.position.y,
|
node_two.position.x,
|
||||||
canvas_node_one.id,
|
node_two.position.y,
|
||||||
self,
|
canvas_node_one.id,
|
||||||
)
|
self,
|
||||||
edge.set_link(link)
|
)
|
||||||
edge.token = tuple(sorted((canvas_node_one.id, canvas_node_two.id)))
|
edge.token = token
|
||||||
edge.dst = canvas_node_two.id
|
edge.dst = canvas_node_two.id
|
||||||
edge.check_wireless()
|
edge.set_link(link)
|
||||||
canvas_node_one.edges.add(edge)
|
edge.check_wireless()
|
||||||
canvas_node_two.edges.add(edge)
|
canvas_node_one.edges.add(edge)
|
||||||
self.edges[edge.token] = edge
|
canvas_node_two.edges.add(edge)
|
||||||
self.core.links[edge.token] = edge
|
self.edges[edge.token] = edge
|
||||||
if link.HasField("interface_one"):
|
self.core.links[edge.token] = edge
|
||||||
canvas_node_one.interfaces.append(link.interface_one)
|
if link.HasField("interface_one"):
|
||||||
if link.HasField("interface_two"):
|
canvas_node_one.interfaces.append(link.interface_one)
|
||||||
canvas_node_two.interfaces.append(link.interface_two)
|
if link.HasField("interface_two"):
|
||||||
|
canvas_node_two.interfaces.append(link.interface_two)
|
||||||
|
elif link.options.unidirectional:
|
||||||
|
edge = self.edges[token]
|
||||||
|
edge.asymmetric_link = link
|
||||||
|
else:
|
||||||
|
logging.error("duplicate link received: %s", link)
|
||||||
|
|
||||||
# raise the nodes so they on top of the links
|
# raise the nodes so they on top of the links
|
||||||
self.tag_raise(tags.NODE)
|
self.tag_raise(tags.NODE)
|
||||||
|
|
|
@ -161,6 +161,7 @@ class CoreGrpcClient:
|
||||||
mobility_configs=None,
|
mobility_configs=None,
|
||||||
service_configs=None,
|
service_configs=None,
|
||||||
service_file_configs=None,
|
service_file_configs=None,
|
||||||
|
asymmetric_links=None,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Start a session.
|
Start a session.
|
||||||
|
@ -176,6 +177,7 @@ class CoreGrpcClient:
|
||||||
:param list mobility_configs: node mobility configurations
|
:param list mobility_configs: node mobility configurations
|
||||||
:param list service_configs: node service configurations
|
:param list service_configs: node service configurations
|
||||||
:param list service_file_configs: node service file configurations
|
:param list service_file_configs: node service file configurations
|
||||||
|
:param list asymmetric_links: asymmetric links to edit
|
||||||
:return: start session response
|
:return: start session response
|
||||||
:rtype: core_pb2.StartSessionResponse
|
:rtype: core_pb2.StartSessionResponse
|
||||||
"""
|
"""
|
||||||
|
@ -191,6 +193,7 @@ class CoreGrpcClient:
|
||||||
mobility_configs=mobility_configs,
|
mobility_configs=mobility_configs,
|
||||||
service_configs=service_configs,
|
service_configs=service_configs,
|
||||||
service_file_configs=service_file_configs,
|
service_file_configs=service_file_configs,
|
||||||
|
asymmetric_links=asymmetric_links,
|
||||||
)
|
)
|
||||||
return self.stub.StartSession(request)
|
return self.stub.StartSession(request)
|
||||||
|
|
||||||
|
|
|
@ -128,7 +128,7 @@ def create_nodes(session, node_protos):
|
||||||
|
|
||||||
def create_links(session, link_protos):
|
def create_links(session, link_protos):
|
||||||
"""
|
"""
|
||||||
Create nodes using a thread pool and wait for completion.
|
Create links using a thread pool and wait for completion.
|
||||||
|
|
||||||
:param core.emulator.session.Session session: session to create nodes in
|
:param core.emulator.session.Session session: session to create nodes in
|
||||||
:param list[core_pb2.Link] link_protos: link proto messages
|
:param list[core_pb2.Link] link_protos: link proto messages
|
||||||
|
@ -149,6 +149,29 @@ def create_links(session, link_protos):
|
||||||
return results, exceptions
|
return results, exceptions
|
||||||
|
|
||||||
|
|
||||||
|
def edit_links(session, link_protos):
|
||||||
|
"""
|
||||||
|
Edit links using a thread pool and wait for completion.
|
||||||
|
|
||||||
|
:param core.emulator.session.Session session: session to create nodes in
|
||||||
|
:param list[core_pb2.Link] link_protos: link proto messages
|
||||||
|
:return: results and exceptions for created links
|
||||||
|
:rtype: tuple
|
||||||
|
"""
|
||||||
|
funcs = []
|
||||||
|
for link_proto in link_protos:
|
||||||
|
node_one_id = link_proto.node_one_id
|
||||||
|
node_two_id = link_proto.node_two_id
|
||||||
|
interface_one, interface_two, options = add_link_data(link_proto)
|
||||||
|
args = (node_one_id, node_two_id, interface_one.id, interface_two.id, options)
|
||||||
|
funcs.append((session.update_link, args, {}))
|
||||||
|
start = time.monotonic()
|
||||||
|
results, exceptions = utils.threadpool(funcs)
|
||||||
|
total = time.monotonic() - start
|
||||||
|
logging.debug("grpc edit links time: %s", total)
|
||||||
|
return results, exceptions
|
||||||
|
|
||||||
|
|
||||||
def convert_value(value):
|
def convert_value(value):
|
||||||
"""
|
"""
|
||||||
Convert value into string.
|
Convert value into string.
|
||||||
|
|
|
@ -158,6 +158,9 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
||||||
# create links
|
# create links
|
||||||
grpcutils.create_links(session, request.links)
|
grpcutils.create_links(session, request.links)
|
||||||
|
|
||||||
|
# asymmetric links
|
||||||
|
grpcutils.edit_links(session, request.asymmetric_links)
|
||||||
|
|
||||||
# set to instantiation and start
|
# set to instantiation and start
|
||||||
session.set_state(EventTypes.INSTANTIATION_STATE)
|
session.set_state(EventTypes.INSTANTIATION_STATE)
|
||||||
session.instantiate()
|
session.instantiate()
|
||||||
|
|
|
@ -1053,6 +1053,7 @@ class CoreNetworkBase(NodeBase):
|
||||||
message_type=0,
|
message_type=0,
|
||||||
node1_id=linked_node.id,
|
node1_id=linked_node.id,
|
||||||
node2_id=self.id,
|
node2_id=self.id,
|
||||||
|
link_type=self.linktype,
|
||||||
unidirectional=1,
|
unidirectional=1,
|
||||||
delay=netif.getparam("delay"),
|
delay=netif.getparam("delay"),
|
||||||
bandwidth=netif.getparam("bw"),
|
bandwidth=netif.getparam("bw"),
|
||||||
|
|
|
@ -965,6 +965,7 @@ class PtpNet(CoreNetwork):
|
||||||
if unidirectional:
|
if unidirectional:
|
||||||
link_data = LinkData(
|
link_data = LinkData(
|
||||||
message_type=0,
|
message_type=0,
|
||||||
|
link_type=self.linktype,
|
||||||
node1_id=if2.node.id,
|
node1_id=if2.node.id,
|
||||||
node2_id=if1.node.id,
|
node2_id=if1.node.id,
|
||||||
delay=if2.getparam("delay"),
|
delay=if2.getparam("delay"),
|
||||||
|
|
|
@ -150,6 +150,7 @@ message StartSessionRequest {
|
||||||
repeated MobilityConfig mobility_configs = 9;
|
repeated MobilityConfig mobility_configs = 9;
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
message StartSessionResponse {
|
message StartSessionResponse {
|
||||||
|
|
Loading…
Reference in a new issue