From a35e91aeba64affbb7346c2a9187c0e428c58dfe Mon Sep 17 00:00:00 2001 From: Blake Harnden <32446120+bharnden@users.noreply.github.com> Date: Mon, 30 Nov 2020 16:49:44 -0800 Subject: [PATCH] daemon: added link option to configure buffer, added support in pygui to allow configuring buffer --- daemon/core/api/grpc/grpcutils.py | 2 ++ daemon/core/api/grpc/server.py | 1 + daemon/core/api/grpc/wrappers.py | 3 +++ daemon/core/emulator/data.py | 1 + daemon/core/gui/dialogs/linkconfig.py | 27 +++++++++++++++++++++- daemon/core/nodes/network.py | 6 ++++- daemon/proto/core/api/grpc/core.proto | 1 + daemon/tests/test_links.py | 32 ++++++++++++++++++++++++--- 8 files changed, 68 insertions(+), 5 deletions(-) diff --git a/daemon/core/api/grpc/grpcutils.py b/daemon/core/api/grpc/grpcutils.py index 7f25f1c1..145f7029 100644 --- a/daemon/core/api/grpc/grpcutils.py +++ b/daemon/core/api/grpc/grpcutils.py @@ -128,6 +128,7 @@ def add_link_data( options.mer = options_proto.mer options.burst = options_proto.burst options.mburst = options_proto.mburst + options.buffer = options_proto.buffer options.unidirectional = options_proto.unidirectional options.key = options_proto.key 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, delay=options_data.delay, dup=options_data.dup, + buffer=options_data.buffer, unidirectional=options_data.unidirectional, ) diff --git a/daemon/core/api/grpc/server.py b/daemon/core/api/grpc/server.py index 62212485..3159776a 100644 --- a/daemon/core/api/grpc/server.py +++ b/daemon/core/api/grpc/server.py @@ -972,6 +972,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): mburst=options_proto.mburst, unidirectional=options_proto.unidirectional, key=options_proto.key, + buffer=options_proto.buffer, ) session.update_link(node1_id, node2_id, iface1_id, iface2_id, options) iface1 = InterfaceData(id=iface1_id) diff --git a/daemon/core/api/grpc/wrappers.py b/daemon/core/api/grpc/wrappers.py index 2146198a..8cc55446 100644 --- a/daemon/core/api/grpc/wrappers.py +++ b/daemon/core/api/grpc/wrappers.py @@ -457,6 +457,7 @@ class LinkOptions: delay: int = 0 dup: int = 0 unidirectional: bool = False + buffer: int = 0 @classmethod def from_proto(cls, proto: core_pb2.LinkOptions) -> "LinkOptions": @@ -471,6 +472,7 @@ class LinkOptions: delay=proto.delay, dup=proto.dup, unidirectional=proto.unidirectional, + buffer=proto.buffer, ) def to_proto(self) -> core_pb2.LinkOptions: @@ -485,6 +487,7 @@ class LinkOptions: delay=self.delay, dup=self.dup, unidirectional=self.unidirectional, + buffer=self.buffer, ) diff --git a/daemon/core/emulator/data.py b/daemon/core/emulator/data.py index 15d922a9..68a92eea 100644 --- a/daemon/core/emulator/data.py +++ b/daemon/core/emulator/data.py @@ -172,6 +172,7 @@ class LinkOptions: mburst: int = None unidirectional: int = None key: int = None + buffer: int = None @dataclass diff --git a/daemon/core/gui/dialogs/linkconfig.py b/daemon/core/gui/dialogs/linkconfig.py index 9879890d..6cb22862 100644 --- a/daemon/core/gui/dialogs/linkconfig.py +++ b/daemon/core/gui/dialogs/linkconfig.py @@ -49,12 +49,14 @@ class LinkConfigurationDialog(Dialog): self.jitter: tk.StringVar = tk.StringVar() self.loss: 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_delay: tk.StringVar = tk.StringVar() self.down_jitter: tk.StringVar = tk.StringVar() self.down_loss: 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_button: Optional[tk.Button] = None @@ -187,6 +189,19 @@ class LinkConfigurationDialog(Dialog): entry.grid(row=row, column=2, sticky=tk.EW, pady=PADY) 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.grid(row=row, column=0, sticky=tk.EW) self.color_button = tk.Button( @@ -224,9 +239,15 @@ class LinkConfigurationDialog(Dialog): jitter = get_int(self.jitter) delay = get_int(self.delay) duplicate = get_int(self.duplicate) + buffer = get_int(self.buffer) loss = get_float(self.loss) 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 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_delay = get_int(self.down_delay) down_duplicate = get_int(self.down_duplicate) + down_buffer = get_int(self.down_buffer) down_loss = get_float(self.down_loss) options = LinkOptions( bandwidth=down_bandwidth, @@ -250,6 +272,7 @@ class LinkConfigurationDialog(Dialog): delay=down_delay, dup=down_duplicate, loss=down_loss, + buffer=down_buffer, unidirectional=True, ) self.edge.asymmetric_link = Link( @@ -304,6 +327,7 @@ class LinkConfigurationDialog(Dialog): self.duplicate.set(str(link.options.dup)) self.loss.set(str(link.options.loss)) self.delay.set(str(link.options.delay)) + self.buffer.set(str(link.options.buffer)) if not self.is_symmetric: asym_link = self.edge.asymmetric_link 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_loss.set(str(asym_link.options.loss)) self.down_delay.set(str(asym_link.options.delay)) + self.down_buffer.set(str(asym_link.options.buffer)) diff --git a/daemon/core/nodes/network.py b/daemon/core/nodes/network.py index a5510084..aef4b04b 100644 --- a/daemon/core/nodes/network.py +++ b/daemon/core/nodes/network.py @@ -456,6 +456,7 @@ class CoreNetwork(CoreNetworkBase): iface.setparam("loss", options.loss), iface.setparam("duplicate", options.dup), iface.setparam("jitter", options.jitter), + iface.setparam("buffer", options.buffer), ] ) if not changed: @@ -470,6 +471,7 @@ class CoreNetwork(CoreNetworkBase): options.loss is None or options.loss <= 0, options.dup is None or options.dup <= 0, options.bandwidth is None or options.bandwidth <= 0, + options.buffer is None or options.buffer <= 0, ] ): if not iface.getparam("has_netem"): @@ -483,7 +485,9 @@ class CoreNetwork(CoreNetworkBase): if options.bandwidth is not None: limit = 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 limit = max(2, math.ceil((2 * bw * delay) / (8 * iface.mtu))) netem += f" rate {bw}kbit" diff --git a/daemon/proto/core/api/grpc/core.proto b/daemon/proto/core/api/grpc/core.proto index 4727bbef..d168afe0 100644 --- a/daemon/proto/core/api/grpc/core.proto +++ b/daemon/proto/core/api/grpc/core.proto @@ -777,6 +777,7 @@ message LinkOptions { int64 delay = 8; int32 dup = 9; bool unidirectional = 10; + int32 buffer = 11; } message Interface { diff --git a/daemon/tests/test_links.py b/daemon/tests/test_links.py index 535ad837..94c8c699 100644 --- a/daemon/tests/test_links.py +++ b/daemon/tests/test_links.py @@ -83,6 +83,7 @@ class TestLinks: loss = 25 dup = 25 jitter = 10 + buffer = 100 node1 = session.add_node(CoreNode) node2 = session.add_node(SwitchNode) iface1_data = ip_prefixes.create_iface(node1) @@ -93,10 +94,16 @@ class TestLinks: assert iface1.getparam("loss") != loss assert iface1.getparam("duplicate") != dup assert iface1.getparam("jitter") != jitter + assert iface1.getparam("buffer") != buffer # when 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_id=iface1_data.id, options=options @@ -108,6 +115,7 @@ class TestLinks: assert iface1.getparam("loss") == loss assert iface1.getparam("duplicate") == dup assert iface1.getparam("jitter") == jitter + assert iface1.getparam("buffer") == buffer def test_update_net_to_node(self, session: Session, ip_prefixes: IpPrefixes): # given @@ -116,6 +124,7 @@ class TestLinks: loss = 25 dup = 25 jitter = 10 + buffer = 100 node1 = session.add_node(SwitchNode) node2 = session.add_node(CoreNode) iface2_data = ip_prefixes.create_iface(node2) @@ -126,10 +135,16 @@ class TestLinks: assert iface2.getparam("loss") != loss assert iface2.getparam("duplicate") != dup assert iface2.getparam("jitter") != jitter + assert iface2.getparam("buffer") != buffer # when 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, iface2_id=iface2_data.id, options=options @@ -141,6 +156,7 @@ class TestLinks: assert iface2.getparam("loss") == loss assert iface2.getparam("duplicate") == dup assert iface2.getparam("jitter") == jitter + assert iface2.getparam("buffer") == buffer def test_update_ptp(self, session: Session, ip_prefixes: IpPrefixes): # given @@ -149,6 +165,7 @@ class TestLinks: loss = 25 dup = 25 jitter = 10 + buffer = 100 node1 = session.add_node(CoreNode) node2 = session.add_node(CoreNode) iface1_data = ip_prefixes.create_iface(node1) @@ -161,15 +178,22 @@ class TestLinks: assert iface1.getparam("loss") != loss assert iface1.getparam("duplicate") != dup assert iface1.getparam("jitter") != jitter + assert iface1.getparam("buffer") != buffer assert iface2.getparam("delay") != delay assert iface2.getparam("bw") != bandwidth assert iface2.getparam("loss") != loss assert iface2.getparam("duplicate") != dup assert iface2.getparam("jitter") != jitter + assert iface2.getparam("buffer") != buffer # when 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) @@ -179,11 +203,13 @@ class TestLinks: assert iface1.getparam("loss") == loss assert iface1.getparam("duplicate") == dup assert iface1.getparam("jitter") == jitter + assert iface1.getparam("buffer") == buffer assert iface2.getparam("delay") == delay assert iface2.getparam("bw") == bandwidth assert iface2.getparam("loss") == loss assert iface2.getparam("duplicate") == dup assert iface2.getparam("jitter") == jitter + assert iface2.getparam("buffer") == buffer def test_delete_ptp(self, session: Session, ip_prefixes: IpPrefixes): # given