daemon: added link option to configure buffer, added support in pygui to allow configuring buffer

This commit is contained in:
Blake Harnden 2020-11-30 16:49:44 -08:00
parent d95c2ec05f
commit a35e91aeba
8 changed files with 68 additions and 5 deletions

View file

@ -128,6 +128,7 @@ def add_link_data(
options.mer = options_proto.mer options.mer = options_proto.mer
options.burst = options_proto.burst options.burst = options_proto.burst
options.mburst = options_proto.mburst options.mburst = options_proto.mburst
options.buffer = options_proto.buffer
options.unidirectional = options_proto.unidirectional options.unidirectional = options_proto.unidirectional
options.key = options_proto.key options.key = options_proto.key
return iface1_data, iface2_data, options, link_type return iface1_data, iface2_data, options, link_type
@ -329,6 +330,7 @@ def convert_link_options(options_data: LinkOptions) -> core_pb2.LinkOptions:
burst=options_data.burst, burst=options_data.burst,
delay=options_data.delay, delay=options_data.delay,
dup=options_data.dup, dup=options_data.dup,
buffer=options_data.buffer,
unidirectional=options_data.unidirectional, unidirectional=options_data.unidirectional,
) )

View file

@ -972,6 +972,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
mburst=options_proto.mburst, mburst=options_proto.mburst,
unidirectional=options_proto.unidirectional, unidirectional=options_proto.unidirectional,
key=options_proto.key, key=options_proto.key,
buffer=options_proto.buffer,
) )
session.update_link(node1_id, node2_id, iface1_id, iface2_id, options) session.update_link(node1_id, node2_id, iface1_id, iface2_id, options)
iface1 = InterfaceData(id=iface1_id) iface1 = InterfaceData(id=iface1_id)

View file

@ -457,6 +457,7 @@ class LinkOptions:
delay: int = 0 delay: int = 0
dup: int = 0 dup: int = 0
unidirectional: bool = False unidirectional: bool = False
buffer: int = 0
@classmethod @classmethod
def from_proto(cls, proto: core_pb2.LinkOptions) -> "LinkOptions": def from_proto(cls, proto: core_pb2.LinkOptions) -> "LinkOptions":
@ -471,6 +472,7 @@ class LinkOptions:
delay=proto.delay, delay=proto.delay,
dup=proto.dup, dup=proto.dup,
unidirectional=proto.unidirectional, unidirectional=proto.unidirectional,
buffer=proto.buffer,
) )
def to_proto(self) -> core_pb2.LinkOptions: def to_proto(self) -> core_pb2.LinkOptions:
@ -485,6 +487,7 @@ class LinkOptions:
delay=self.delay, delay=self.delay,
dup=self.dup, dup=self.dup,
unidirectional=self.unidirectional, unidirectional=self.unidirectional,
buffer=self.buffer,
) )

View file

@ -172,6 +172,7 @@ class LinkOptions:
mburst: int = None mburst: int = None
unidirectional: int = None unidirectional: int = None
key: int = None key: int = None
buffer: int = None
@dataclass @dataclass

View file

@ -49,12 +49,14 @@ class LinkConfigurationDialog(Dialog):
self.jitter: tk.StringVar = tk.StringVar() self.jitter: tk.StringVar = tk.StringVar()
self.loss: tk.StringVar = tk.StringVar() self.loss: tk.StringVar = tk.StringVar()
self.duplicate: tk.StringVar = tk.StringVar() self.duplicate: tk.StringVar = tk.StringVar()
self.buffer: tk.StringVar = tk.StringVar()
self.down_bandwidth: tk.StringVar = tk.StringVar() self.down_bandwidth: tk.StringVar = tk.StringVar()
self.down_delay: tk.StringVar = tk.StringVar() self.down_delay: tk.StringVar = tk.StringVar()
self.down_jitter: tk.StringVar = tk.StringVar() self.down_jitter: tk.StringVar = tk.StringVar()
self.down_loss: tk.StringVar = tk.StringVar() self.down_loss: tk.StringVar = tk.StringVar()
self.down_duplicate: tk.StringVar = tk.StringVar() self.down_duplicate: tk.StringVar = tk.StringVar()
self.down_buffer: tk.StringVar = tk.StringVar()
self.color: tk.StringVar = tk.StringVar(value=self.edge.color) self.color: tk.StringVar = tk.StringVar(value=self.edge.color)
self.color_button: Optional[tk.Button] = None self.color_button: Optional[tk.Button] = None
@ -187,6 +189,19 @@ class LinkConfigurationDialog(Dialog):
entry.grid(row=row, column=2, sticky=tk.EW, pady=PADY) entry.grid(row=row, column=2, sticky=tk.EW, pady=PADY)
row = row + 1 row = row + 1
label = ttk.Label(frame, text="Buffer (Packets)")
label.grid(row=row, column=0, sticky=tk.EW)
entry = validation.PositiveIntEntry(
frame, empty_enabled=False, textvariable=self.buffer
)
entry.grid(row=row, column=1, sticky=tk.EW, pady=PADY)
if not self.is_symmetric:
entry = validation.PositiveIntEntry(
frame, empty_enabled=False, textvariable=self.down_buffer
)
entry.grid(row=row, column=2, sticky=tk.EW, pady=PADY)
row = row + 1
label = ttk.Label(frame, text="Color") label = ttk.Label(frame, text="Color")
label.grid(row=row, column=0, sticky=tk.EW) label.grid(row=row, column=0, sticky=tk.EW)
self.color_button = tk.Button( self.color_button = tk.Button(
@ -224,9 +239,15 @@ class LinkConfigurationDialog(Dialog):
jitter = get_int(self.jitter) jitter = get_int(self.jitter)
delay = get_int(self.delay) delay = get_int(self.delay)
duplicate = get_int(self.duplicate) duplicate = get_int(self.duplicate)
buffer = get_int(self.buffer)
loss = get_float(self.loss) loss = get_float(self.loss)
options = LinkOptions( options = LinkOptions(
bandwidth=bandwidth, jitter=jitter, delay=delay, dup=duplicate, loss=loss bandwidth=bandwidth,
jitter=jitter,
delay=delay,
dup=duplicate,
loss=loss,
buffer=buffer,
) )
link.options = options link.options = options
iface1_id = link.iface1.id if link.iface1 else None iface1_id = link.iface1.id if link.iface1 else None
@ -243,6 +264,7 @@ class LinkConfigurationDialog(Dialog):
down_jitter = get_int(self.down_jitter) down_jitter = get_int(self.down_jitter)
down_delay = get_int(self.down_delay) down_delay = get_int(self.down_delay)
down_duplicate = get_int(self.down_duplicate) down_duplicate = get_int(self.down_duplicate)
down_buffer = get_int(self.down_buffer)
down_loss = get_float(self.down_loss) down_loss = get_float(self.down_loss)
options = LinkOptions( options = LinkOptions(
bandwidth=down_bandwidth, bandwidth=down_bandwidth,
@ -250,6 +272,7 @@ class LinkConfigurationDialog(Dialog):
delay=down_delay, delay=down_delay,
dup=down_duplicate, dup=down_duplicate,
loss=down_loss, loss=down_loss,
buffer=down_buffer,
unidirectional=True, unidirectional=True,
) )
self.edge.asymmetric_link = Link( self.edge.asymmetric_link = Link(
@ -304,6 +327,7 @@ class LinkConfigurationDialog(Dialog):
self.duplicate.set(str(link.options.dup)) self.duplicate.set(str(link.options.dup))
self.loss.set(str(link.options.loss)) self.loss.set(str(link.options.loss))
self.delay.set(str(link.options.delay)) self.delay.set(str(link.options.delay))
self.buffer.set(str(link.options.buffer))
if not self.is_symmetric: if not self.is_symmetric:
asym_link = self.edge.asymmetric_link asym_link = self.edge.asymmetric_link
self.down_bandwidth.set(str(asym_link.options.bandwidth)) self.down_bandwidth.set(str(asym_link.options.bandwidth))
@ -311,3 +335,4 @@ class LinkConfigurationDialog(Dialog):
self.down_duplicate.set(str(asym_link.options.dup)) self.down_duplicate.set(str(asym_link.options.dup))
self.down_loss.set(str(asym_link.options.loss)) self.down_loss.set(str(asym_link.options.loss))
self.down_delay.set(str(asym_link.options.delay)) self.down_delay.set(str(asym_link.options.delay))
self.down_buffer.set(str(asym_link.options.buffer))

View file

@ -456,6 +456,7 @@ class CoreNetwork(CoreNetworkBase):
iface.setparam("loss", options.loss), iface.setparam("loss", options.loss),
iface.setparam("duplicate", options.dup), iface.setparam("duplicate", options.dup),
iface.setparam("jitter", options.jitter), iface.setparam("jitter", options.jitter),
iface.setparam("buffer", options.buffer),
] ]
) )
if not changed: if not changed:
@ -470,6 +471,7 @@ class CoreNetwork(CoreNetworkBase):
options.loss is None or options.loss <= 0, options.loss is None or options.loss <= 0,
options.dup is None or options.dup <= 0, options.dup is None or options.dup <= 0,
options.bandwidth is None or options.bandwidth <= 0, options.bandwidth is None or options.bandwidth <= 0,
options.buffer is None or options.buffer <= 0,
] ]
): ):
if not iface.getparam("has_netem"): if not iface.getparam("has_netem"):
@ -483,7 +485,9 @@ class CoreNetwork(CoreNetworkBase):
if options.bandwidth is not None: if options.bandwidth is not None:
limit = 1000 limit = 1000
bw = options.bandwidth / 1000 bw = options.bandwidth / 1000
if options.delay and options.bandwidth: if options.buffer is not None and options.buffer > 0:
limit = options.buffer
elif options.delay and options.bandwidth:
delay = options.delay / 1000 delay = options.delay / 1000
limit = max(2, math.ceil((2 * bw * delay) / (8 * iface.mtu))) limit = max(2, math.ceil((2 * bw * delay) / (8 * iface.mtu)))
netem += f" rate {bw}kbit" netem += f" rate {bw}kbit"

View file

@ -777,6 +777,7 @@ message LinkOptions {
int64 delay = 8; int64 delay = 8;
int32 dup = 9; int32 dup = 9;
bool unidirectional = 10; bool unidirectional = 10;
int32 buffer = 11;
} }
message Interface { message Interface {

View file

@ -83,6 +83,7 @@ class TestLinks:
loss = 25 loss = 25
dup = 25 dup = 25
jitter = 10 jitter = 10
buffer = 100
node1 = session.add_node(CoreNode) node1 = session.add_node(CoreNode)
node2 = session.add_node(SwitchNode) node2 = session.add_node(SwitchNode)
iface1_data = ip_prefixes.create_iface(node1) iface1_data = ip_prefixes.create_iface(node1)
@ -93,10 +94,16 @@ class TestLinks:
assert iface1.getparam("loss") != loss assert iface1.getparam("loss") != loss
assert iface1.getparam("duplicate") != dup assert iface1.getparam("duplicate") != dup
assert iface1.getparam("jitter") != jitter assert iface1.getparam("jitter") != jitter
assert iface1.getparam("buffer") != buffer
# when # when
options = LinkOptions( options = LinkOptions(
delay=delay, bandwidth=bandwidth, loss=loss, dup=dup, jitter=jitter delay=delay,
bandwidth=bandwidth,
loss=loss,
dup=dup,
jitter=jitter,
buffer=buffer,
) )
session.update_link( session.update_link(
node1.id, node2.id, iface1_id=iface1_data.id, options=options node1.id, node2.id, iface1_id=iface1_data.id, options=options
@ -108,6 +115,7 @@ class TestLinks:
assert iface1.getparam("loss") == loss assert iface1.getparam("loss") == loss
assert iface1.getparam("duplicate") == dup assert iface1.getparam("duplicate") == dup
assert iface1.getparam("jitter") == jitter assert iface1.getparam("jitter") == jitter
assert iface1.getparam("buffer") == buffer
def test_update_net_to_node(self, session: Session, ip_prefixes: IpPrefixes): def test_update_net_to_node(self, session: Session, ip_prefixes: IpPrefixes):
# given # given
@ -116,6 +124,7 @@ class TestLinks:
loss = 25 loss = 25
dup = 25 dup = 25
jitter = 10 jitter = 10
buffer = 100
node1 = session.add_node(SwitchNode) node1 = session.add_node(SwitchNode)
node2 = session.add_node(CoreNode) node2 = session.add_node(CoreNode)
iface2_data = ip_prefixes.create_iface(node2) iface2_data = ip_prefixes.create_iface(node2)
@ -126,10 +135,16 @@ class TestLinks:
assert iface2.getparam("loss") != loss assert iface2.getparam("loss") != loss
assert iface2.getparam("duplicate") != dup assert iface2.getparam("duplicate") != dup
assert iface2.getparam("jitter") != jitter assert iface2.getparam("jitter") != jitter
assert iface2.getparam("buffer") != buffer
# when # when
options = LinkOptions( options = LinkOptions(
delay=delay, bandwidth=bandwidth, loss=loss, dup=dup, jitter=jitter delay=delay,
bandwidth=bandwidth,
loss=loss,
dup=dup,
jitter=jitter,
buffer=buffer,
) )
session.update_link( session.update_link(
node1.id, node2.id, iface2_id=iface2_data.id, options=options node1.id, node2.id, iface2_id=iface2_data.id, options=options
@ -141,6 +156,7 @@ class TestLinks:
assert iface2.getparam("loss") == loss assert iface2.getparam("loss") == loss
assert iface2.getparam("duplicate") == dup assert iface2.getparam("duplicate") == dup
assert iface2.getparam("jitter") == jitter assert iface2.getparam("jitter") == jitter
assert iface2.getparam("buffer") == buffer
def test_update_ptp(self, session: Session, ip_prefixes: IpPrefixes): def test_update_ptp(self, session: Session, ip_prefixes: IpPrefixes):
# given # given
@ -149,6 +165,7 @@ class TestLinks:
loss = 25 loss = 25
dup = 25 dup = 25
jitter = 10 jitter = 10
buffer = 100
node1 = session.add_node(CoreNode) node1 = session.add_node(CoreNode)
node2 = session.add_node(CoreNode) node2 = session.add_node(CoreNode)
iface1_data = ip_prefixes.create_iface(node1) iface1_data = ip_prefixes.create_iface(node1)
@ -161,15 +178,22 @@ class TestLinks:
assert iface1.getparam("loss") != loss assert iface1.getparam("loss") != loss
assert iface1.getparam("duplicate") != dup assert iface1.getparam("duplicate") != dup
assert iface1.getparam("jitter") != jitter assert iface1.getparam("jitter") != jitter
assert iface1.getparam("buffer") != buffer
assert iface2.getparam("delay") != delay assert iface2.getparam("delay") != delay
assert iface2.getparam("bw") != bandwidth assert iface2.getparam("bw") != bandwidth
assert iface2.getparam("loss") != loss assert iface2.getparam("loss") != loss
assert iface2.getparam("duplicate") != dup assert iface2.getparam("duplicate") != dup
assert iface2.getparam("jitter") != jitter assert iface2.getparam("jitter") != jitter
assert iface2.getparam("buffer") != buffer
# when # when
options = LinkOptions( options = LinkOptions(
delay=delay, bandwidth=bandwidth, loss=loss, dup=dup, jitter=jitter delay=delay,
bandwidth=bandwidth,
loss=loss,
dup=dup,
jitter=jitter,
buffer=buffer,
) )
session.update_link(node1.id, node2.id, iface1_data.id, iface2_data.id, options) session.update_link(node1.id, node2.id, iface1_data.id, iface2_data.id, options)
@ -179,11 +203,13 @@ class TestLinks:
assert iface1.getparam("loss") == loss assert iface1.getparam("loss") == loss
assert iface1.getparam("duplicate") == dup assert iface1.getparam("duplicate") == dup
assert iface1.getparam("jitter") == jitter assert iface1.getparam("jitter") == jitter
assert iface1.getparam("buffer") == buffer
assert iface2.getparam("delay") == delay assert iface2.getparam("delay") == delay
assert iface2.getparam("bw") == bandwidth assert iface2.getparam("bw") == bandwidth
assert iface2.getparam("loss") == loss assert iface2.getparam("loss") == loss
assert iface2.getparam("duplicate") == dup assert iface2.getparam("duplicate") == dup
assert iface2.getparam("jitter") == jitter assert iface2.getparam("jitter") == jitter
assert iface2.getparam("buffer") == buffer
def test_delete_ptp(self, session: Session, ip_prefixes: IpPrefixes): def test_delete_ptp(self, session: Session, ip_prefixes: IpPrefixes):
# given # given