From 0a445f2706f9c048c199df9ffd78ce30979260e0 Mon Sep 17 00:00:00 2001 From: bharnden Date: Sun, 17 Feb 2019 23:41:30 -0800 Subject: [PATCH 01/72] initial files to support grpc server/client --- .gitignore | 4 ++++ daemon/core/grpc/__init__.py | 0 daemon/core/grpc/client.py | 38 ++++++++++++++++++++++++++++++++++++ daemon/core/grpc/server.py | 37 +++++++++++++++++++++++++++++++++++ daemon/proto/core.proto | 18 +++++++++++++++++ 5 files changed, 97 insertions(+) create mode 100644 daemon/core/grpc/__init__.py create mode 100644 daemon/core/grpc/client.py create mode 100644 daemon/core/grpc/server.py create mode 100644 daemon/proto/core.proto diff --git a/.gitignore b/.gitignore index 7415be63..200bc9b9 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,10 @@ configure debian stamp-h1 +# generated protobuf files +daemon/core/grpc/core_pb2.py +daemon/core/grpc/core_pb2_grpc.py + # python build directory dist diff --git a/daemon/core/grpc/__init__.py b/daemon/core/grpc/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/daemon/core/grpc/client.py b/daemon/core/grpc/client.py new file mode 100644 index 00000000..40c7842b --- /dev/null +++ b/daemon/core/grpc/client.py @@ -0,0 +1,38 @@ +from __future__ import print_function +import logging +from contextlib import contextmanager + +import grpc + +import core_pb2 +import core_pb2_grpc + + +class CoreApiClient(object): + def __init__(self, address="localhost:50051"): + self.address = address + self.stub = None + + def get_sessions(self): + return self.stub.GetSessions(core_pb2.SessionsRequest()) + + @contextmanager + def connect(self): + channel = grpc.insecure_channel(self.address) + try: + self.stub = core_pb2_grpc.CoreApiStub(channel) + yield channel + finally: + channel.close() + + +def main(): + client = CoreApiClient() + with client.connect(): + response = client.get_sessions() + print("core client received: %s" % response) + + +if __name__ == "__main__": + logging.basicConfig() + main() diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py new file mode 100644 index 00000000..45472915 --- /dev/null +++ b/daemon/core/grpc/server.py @@ -0,0 +1,37 @@ +from concurrent import futures +import time +import logging + +import grpc + +import core_pb2 +import core_pb2_grpc + +_ONE_DAY_IN_SECONDS = 60 * 60 * 24 + + +class CoreApiServer(core_pb2_grpc.CoreApiServicer): + + def GetSessions(self, request, context): + response = core_pb2.SessionsResponse() + session = response.sessions.add() + session.id = 1 + return response + + +def main(): + server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) + core_pb2_grpc.add_CoreApiServicer_to_server(CoreApiServer(), server) + server.add_insecure_port("[::]:50051") + server.start() + + try: + while True: + time.sleep(_ONE_DAY_IN_SECONDS) + except KeyboardInterrupt: + server.stop(0) + + +if __name__ == "__main__": + logging.basicConfig() + main() diff --git a/daemon/proto/core.proto b/daemon/proto/core.proto new file mode 100644 index 00000000..51d11cdc --- /dev/null +++ b/daemon/proto/core.proto @@ -0,0 +1,18 @@ +syntax = "proto3"; + +package core; + +service CoreApi { + rpc GetSessions (SessionsRequest) returns (SessionsResponse) {} +} + +message SessionsRequest { +} + +message SessionsResponse { + repeated Session sessions = 1; +} + +message Session { + int32 id = 1; +} From e72f13348865af80113f0b3e7c733074232bb225 Mon Sep 17 00:00:00 2001 From: bharnden Date: Sun, 17 Feb 2019 23:42:57 -0800 Subject: [PATCH 02/72] added proto makefile to repo --- .gitignore | 1 + daemon/proto/Makefile | 5 +++++ 2 files changed, 6 insertions(+) create mode 100644 daemon/proto/Makefile diff --git a/.gitignore b/.gitignore index 200bc9b9..83bf7e29 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ .version.date Makefile !kernel/**/Makefile +!daemon/proto/Makefile Makefile.in aclocal.m4 autom4te.cache diff --git a/daemon/proto/Makefile b/daemon/proto/Makefile new file mode 100644 index 00000000..e4edb47a --- /dev/null +++ b/daemon/proto/Makefile @@ -0,0 +1,5 @@ +all: + python -m grpc_tools.protoc -I . --python_out=../core/grpc --grpc_python_out=../core/grpc core.proto + +clean: + rm core_pb2* From 7aed803aaedbe1496791c65c3ee0b1dafce93508 Mon Sep 17 00:00:00 2001 From: bharnden Date: Mon, 18 Feb 2019 22:54:14 -0800 Subject: [PATCH 03/72] added grpc api to core-daemon, added get sessions and get session rpc calls --- daemon/core/grpc/client.py | 10 +++++ daemon/core/grpc/server.py | 75 ++++++++++++++++++++++++++++++----- daemon/core/misc/nodeutils.py | 13 ++++++ daemon/proto/core.proto | 38 +++++++++++++++++- daemon/scripts/core-daemon | 7 ++++ 5 files changed, 132 insertions(+), 11 deletions(-) diff --git a/daemon/core/grpc/client.py b/daemon/core/grpc/client.py index 40c7842b..d371a8d6 100644 --- a/daemon/core/grpc/client.py +++ b/daemon/core/grpc/client.py @@ -16,6 +16,11 @@ class CoreApiClient(object): def get_sessions(self): return self.stub.GetSessions(core_pb2.SessionsRequest()) + def get_session(self, _id): + request = core_pb2.SessionRequest() + request.id = _id + return self.stub.GetSession(request) + @contextmanager def connect(self): channel = grpc.insecure_channel(self.address) @@ -32,6 +37,11 @@ def main(): response = client.get_sessions() print("core client received: %s" % response) + if len(response.sessions) > 0: + session_data = response.sessions[0] + session = client.get_session(session_data.id) + print(session) + if __name__ == "__main__": logging.basicConfig() diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index 45472915..9f4e4dc6 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -1,3 +1,5 @@ +from core.enumerations import NodeTypes + from concurrent import futures import time import logging @@ -6,23 +8,81 @@ import grpc import core_pb2 import core_pb2_grpc +from core.misc import nodeutils _ONE_DAY_IN_SECONDS = 60 * 60 * 24 class CoreApiServer(core_pb2_grpc.CoreApiServicer): + def __init__(self, coreemu): + super(CoreApiServer, self).__init__() + self.coreemu = coreemu def GetSessions(self, request, context): response = core_pb2.SessionsResponse() - session = response.sessions.add() - session.id = 1 + for session_id in self.coreemu.sessions: + session = self.coreemu.sessions[session_id] + session_data = response.sessions.add() + session_data.id = session_id + session_data.state = session.state + session_data.nodes = session.get_node_count() + return response + + def GetSession(self, request, context): + session = self.coreemu.sessions.get(request.id) + if not request: + pass + + response = core_pb2.SessionResponse() + for node_id in session.objects: + node = session.objects[node_id] + + if not isinstance(node.objid, int): + continue + + node_data = response.nodes.add() + node_data.id = node.objid + node_data.name = node.name + node_data.type = nodeutils.get_node_type(node.__class__).value + model = getattr(node, "type", None) + if model: + node_data.model = model + + x = node.position.x + if x is not None: + node_data.position.x = x + y = node.position.y + if y is not None: + node_data.position.y = y + z = node.position.z + if z is not None: + node_data.position.z = z + + services = getattr(node, "services", []) + if services is None: + services = [] + services = [x.name for x in services] + node_data.services.extend(services) + + emane_model = None + if nodeutils.is_node(node, NodeTypes.EMANE): + emane_model = node.model.name + if emane_model: + node_data.emane = emane_model + + links_data = node.all_link_data(0) + for link_data in links_data: + pass + # link = core_utils.convert_link(session, link_data) + # links.append(link) return response -def main(): +def listen(coreemu, address="[::]:50051"): + logging.info("starting grpc api: %s", address) server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) - core_pb2_grpc.add_CoreApiServicer_to_server(CoreApiServer(), server) - server.add_insecure_port("[::]:50051") + core_pb2_grpc.add_CoreApiServicer_to_server(CoreApiServer(coreemu), server) + server.add_insecure_port(address) server.start() try: @@ -30,8 +90,3 @@ def main(): time.sleep(_ONE_DAY_IN_SECONDS) except KeyboardInterrupt: server.stop(0) - - -if __name__ == "__main__": - logging.basicConfig() - main() diff --git a/daemon/core/misc/nodeutils.py b/daemon/core/misc/nodeutils.py index 645c0543..ef4dacb6 100644 --- a/daemon/core/misc/nodeutils.py +++ b/daemon/core/misc/nodeutils.py @@ -63,6 +63,19 @@ def get_node_class(node_type): return _NODE_MAP[node_type] +def get_node_type(node_class): + """ + Retrieve the node type given a node class. + + :param class node_class: node class to get type for + :return: node type + :rtype: core.enumerations.NodeTypes + """ + global _NODE_MAP + node_type_map = {v: k for k, v in _NODE_MAP.iteritems()} + return node_type_map.get(node_class) + + def is_node(obj, node_types): """ Validates if an object is one of the provided node types. diff --git a/daemon/proto/core.proto b/daemon/proto/core.proto index 51d11cdc..a68b38f6 100644 --- a/daemon/proto/core.proto +++ b/daemon/proto/core.proto @@ -4,6 +4,7 @@ package core; service CoreApi { rpc GetSessions (SessionsRequest) returns (SessionsResponse) {} + rpc GetSession (SessionRequest) returns (SessionResponse) {} } message SessionsRequest { @@ -13,6 +14,41 @@ message SessionsResponse { repeated Session sessions = 1; } -message Session { +message SessionRequest { int32 id = 1; } + +message SessionResponse { + int32 state = 1; + repeated Node nodes = 2; + repeated Link links = 3; +} + +message Session { + int32 id = 1; + int32 state = 2; + int32 nodes = 3; +} + +message Node { + int32 id = 1; + string name = 2; + int32 type = 3; + string model = 4; + Position position = 5; + repeated string services = 6; + string emane = 7; +} + +message Link { + +} + +message Position { + int32 x = 1; + int32 y = 2; + int32 z = 3; + float lat = 4; + float lon = 5; + float alt = 6; +} diff --git a/daemon/scripts/core-daemon b/daemon/scripts/core-daemon index 34c516d3..29410884 100644 --- a/daemon/scripts/core-daemon +++ b/daemon/scripts/core-daemon @@ -9,6 +9,7 @@ import ConfigParser import logging import optparse import sys +import threading import time from core import load_logging_config @@ -16,6 +17,7 @@ from core import constants from core import enumerations from core.corehandlers import CoreHandler from core.coreserver import CoreServer +from core.grpc.server import listen from core.misc.utils import close_onexec load_logging_config() @@ -52,6 +54,11 @@ def cored(cfg, use_ovs): logging.exception("error starting main server on: %s:%s", host, port) sys.exit(1) + # initialize grpc api + grpc_thread = threading.Thread(target=listen, args=(server.coreemu,)) + grpc_thread.daemon = True + grpc_thread.start() + close_onexec(server.fileno()) logging.info("server started, listening on: %s:%s", host, port) server.serve_forever() From b573eb641eeaeb217337c9590eee12007d813c53 Mon Sep 17 00:00:00 2001 From: bharnden Date: Tue, 19 Feb 2019 23:32:04 -0800 Subject: [PATCH 04/72] grpc updates to provide link data within a session query --- daemon/core/grpc/server.py | 69 ++++++++++++++++++++++++++++++++++++-- daemon/proto/core.proto | 29 ++++++++++++++++ 2 files changed, 95 insertions(+), 3 deletions(-) diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index 9f4e4dc6..a045aba0 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -13,6 +13,69 @@ from core.misc import nodeutils _ONE_DAY_IN_SECONDS = 60 * 60 * 24 +def convert_value(value): + if value is None: + return value + else: + return str(value) + + +def update_proto(obj, **kwargs): + for key in kwargs: + value = kwargs[key] + if value is not None: + logging.info("setting proto key(%s) value(%s)", key, value) + setattr(obj, key, value) + + +def convert_link(session, link_data, link): + if link_data.interface1_id is not None: + node = session.get_object(link_data.node1_id) + interface = node.netif(link_data.interface1_id) + link.interface_one.id = link_data.interface1_id + link.interface_one.name = interface.name + update_proto( + link.interface_one, + mac=convert_value(link_data.interface1_mac), + ip4=convert_value(link_data.interface1_ip4), + ip4mask=link_data.interface1_ip4_mask, + ip6=convert_value(link_data.interface1_ip6), + ip6mask=link_data.interface1_ip6_mask + ) + + if link_data.interface2_id is not None: + node = session.get_object(link_data.node2_id) + interface = node.netif(link_data.interface2_id) + link.interface_two.id = link_data.interface2_id + link.interface_two.name = interface.name + update_proto( + link.interface_two, + mac=convert_value(link_data.interface2_mac), + ip4=convert_value(link_data.interface2_ip4), + ip4mask=link_data.interface2_ip4_mask, + ip6=convert_value(link_data.interface2_ip6), + ip6mask=link_data.interface2_ip6_mask + ) + + link.node_one = link_data.node1_id + link.node_two = link_data.node2_id + link.type = link_data.link_type + update_proto( + link.options, + opaque=link_data.opaque, + jitter=link_data.jitter, + key=link_data.key, + mburst=link_data.mburst, + mer=link_data.mer, + per=link_data.per, + bandwidth=link_data.bandwidth, + burst=link_data.burst, + delay=link_data.delay, + dup=link_data.dup, + unidirectional=link_data.unidirectional + ) + + class CoreApiServer(core_pb2_grpc.CoreApiServicer): def __init__(self, coreemu): super(CoreApiServer, self).__init__() @@ -72,9 +135,9 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): links_data = node.all_link_data(0) for link_data in links_data: - pass - # link = core_utils.convert_link(session, link_data) - # links.append(link) + link = response.links.add() + convert_link(session, link_data, link) + return response diff --git a/daemon/proto/core.proto b/daemon/proto/core.proto index a68b38f6..2bafbb76 100644 --- a/daemon/proto/core.proto +++ b/daemon/proto/core.proto @@ -41,7 +41,36 @@ message Node { } message Link { + int32 node_one = 1; + int32 node_two = 2; + int32 type = 3; + Interface interface_one = 4; + Interface interface_two = 5; + LinkOptions options = 6; +} +message LinkOptions { + string opaque = 1; + float jitter = 2; + string key = 3; + float mburst = 4; + float mer = 5; + float per = 6; + float bandwidth = 7; + float burst = 8; + float delay = 9; + float dup = 10; + bool unidirectional = 11; +} + +message Interface { + int32 id = 1; + string name = 2; + string mac = 3; + string ip4 = 4; + int32 ip4mask = 5; + string ip6 = 6; + int32 ip6mask = 7; } message Position { From e819b706bcb7c03deb73aca61ac64d98ea42481c Mon Sep 17 00:00:00 2001 From: bharnden Date: Wed, 20 Feb 2019 21:19:35 -0800 Subject: [PATCH 05/72] grpc support for get session options and location --- daemon/core/grpc/client.py | 13 ++++ daemon/core/grpc/server.py | 75 ++++++++++++++++----- daemon/proto/core.proto | 130 +++++++++++++++++++++++-------------- daemon/scripts/core-daemon | 0 4 files changed, 153 insertions(+), 65 deletions(-) mode change 100644 => 100755 daemon/scripts/core-daemon diff --git a/daemon/core/grpc/client.py b/daemon/core/grpc/client.py index d371a8d6..6d74e9fe 100644 --- a/daemon/core/grpc/client.py +++ b/daemon/core/grpc/client.py @@ -21,6 +21,16 @@ class CoreApiClient(object): request.id = _id return self.stub.GetSession(request) + def get_session_options(self, _id): + request = core_pb2.SessionOptionsRequest() + request.id = _id + return self.stub.GetSessionOptions(request) + + def get_session_location(self, _id): + request = core_pb2.SessionLocationRequest() + request.id = _id + return self.stub.GetSessionLocation(request) + @contextmanager def connect(self): channel = grpc.insecure_channel(self.address) @@ -42,6 +52,9 @@ def main(): session = client.get_session(session_data.id) print(session) + print(client.get_session_options(session_data.id)) + print(client.get_session_location(session_data.id)) + if __name__ == "__main__": logging.basicConfig() diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index a045aba0..7e5cc78c 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -91,6 +91,48 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): session_data.nodes = session.get_node_count() return response + def GetSessionLocation(self, request, context): + session = self.coreemu.sessions.get(request.id) + x, y, z = session.location.refxyz + lat, lon, alt = session.location.refgeo + response = core_pb2.SessionLocationResponse() + update_proto( + response.position, + x=x, + y=y, + z=z, + lat=lat, + lon=lon, + alt=alt + ) + update_proto(response, scale=session.location.refscale) + return response + + def GetSessionOptions(self, request, context): + session = self.coreemu.sessions.get(request.id) + config = session.options.get_configs() + + response = core_pb2.SessionOptionsResponse() + config_options = [] + for configuration in session.options.configurations(): + value = config[configuration.id] + config_option = core_pb2.ConfigOption() + config_option.label = configuration.label + config_option.name = configuration.id + config_option.value = value + config_option.type = configuration.type.value + config_option.select.extend(configuration.options) + config_options.append(config_option) + + for config_group in session.options.config_groups(): + start = config_group.start - 1 + stop = config_group.stop + config_group_proto = response.groups.add() + config_group_proto.name = config_group.name + config_group_proto.options.extend(config_options[start: stop]) + + return response + def GetSession(self, request, context): session = self.coreemu.sessions.get(request.id) if not request: @@ -103,35 +145,32 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): if not isinstance(node.objid, int): continue - node_data = response.nodes.add() - node_data.id = node.objid - node_data.name = node.name - node_data.type = nodeutils.get_node_type(node.__class__).value + node_proto = response.nodes.add() + node_proto.id = node.objid + node_proto.name = node.name + node_proto.type = nodeutils.get_node_type(node.__class__).value model = getattr(node, "type", None) - if model: - node_data.model = model + if model is not None: + node_proto.model = model - x = node.position.x - if x is not None: - node_data.position.x = x - y = node.position.y - if y is not None: - node_data.position.y = y - z = node.position.z - if z is not None: - node_data.position.z = z + update_proto( + node_proto.position, + x=node.position.x, + y=node.position.y, + z=node.position.z + ) services = getattr(node, "services", []) if services is None: services = [] services = [x.name for x in services] - node_data.services.extend(services) + node_proto.services.extend(services) emane_model = None if nodeutils.is_node(node, NodeTypes.EMANE): emane_model = node.model.name - if emane_model: - node_data.emane = emane_model + if emane_model is not None: + node_proto.emane = emane_model links_data = node.all_link_data(0) for link_data in links_data: diff --git a/daemon/proto/core.proto b/daemon/proto/core.proto index 2bafbb76..7e295b52 100644 --- a/daemon/proto/core.proto +++ b/daemon/proto/core.proto @@ -3,81 +3,117 @@ syntax = "proto3"; package core; service CoreApi { - rpc GetSessions (SessionsRequest) returns (SessionsResponse) {} - rpc GetSession (SessionRequest) returns (SessionResponse) {} + rpc GetSessions (SessionsRequest) returns (SessionsResponse) { + } + rpc GetSession (SessionRequest) returns (SessionResponse) { + } + rpc GetSessionOptions (SessionOptionsRequest) returns (SessionOptionsResponse) { + } + rpc GetSessionLocation (SessionLocationRequest) returns (SessionLocationResponse) { + } } message SessionsRequest { } message SessionsResponse { - repeated Session sessions = 1; + repeated Session sessions = 1; } message SessionRequest { - int32 id = 1; + int32 id = 1; } message SessionResponse { - int32 state = 1; - repeated Node nodes = 2; - repeated Link links = 3; + int32 state = 1; + repeated Node nodes = 2; + repeated Link links = 3; +} + +message SessionOptionsRequest { + int32 id = 1; +} + +message SessionOptionsResponse { + repeated ConfigGroup groups = 1; +} + +message SessionLocationRequest { + int32 id = 1; +} + +message SessionLocationResponse { + Position position = 1; + float scale = 2; +} + +message ConfigGroup { + string name = 1; + repeated ConfigOption options = 2; +} + +message ConfigOption { + string label = 1; + string name = 2; + string value = 3; + int32 type = 4; + repeated string select = 5; } message Session { - int32 id = 1; - int32 state = 2; - int32 nodes = 3; + int32 id = 1; + int32 state = 2; + int32 nodes = 3; } message Node { - int32 id = 1; - string name = 2; - int32 type = 3; - string model = 4; - Position position = 5; - repeated string services = 6; - string emane = 7; + int32 id = 1; + string name = 2; + int32 type = 3; + string model = 4; + Position position = 5; + repeated string services = 6; + string emane = 7; } message Link { - int32 node_one = 1; - int32 node_two = 2; - int32 type = 3; - Interface interface_one = 4; - Interface interface_two = 5; - LinkOptions options = 6; + int32 node_one = 1; + int32 node_two = 2; + int32 type = 3; + Interface interface_one = 4; + Interface interface_two = 5; + LinkOptions options = 6; } message LinkOptions { - string opaque = 1; - float jitter = 2; - string key = 3; - float mburst = 4; - float mer = 5; - float per = 6; - float bandwidth = 7; - float burst = 8; - float delay = 9; - float dup = 10; - bool unidirectional = 11; + string opaque = 1; + float jitter = 2; + string key = 3; + float mburst = 4; + float mer = 5; + float per = 6; + float bandwidth = 7; + float burst = 8; + float delay = 9; + float dup = 10; + bool unidirectional = 11; } message Interface { - int32 id = 1; - string name = 2; - string mac = 3; - string ip4 = 4; - int32 ip4mask = 5; - string ip6 = 6; - int32 ip6mask = 7; + int32 id = 1; + string name = 2; + string mac = 3; + string ip4 = 4; + int32 ip4mask = 5; + string ip6 = 6; + int32 ip6mask = 7; } message Position { - int32 x = 1; - int32 y = 2; - int32 z = 3; - float lat = 4; - float lon = 5; - float alt = 6; + float x = 1; + float y = 2; + float z = 3; + float lat = 4; + float lon = 5; + float alt = 6; } diff --git a/daemon/scripts/core-daemon b/daemon/scripts/core-daemon old mode 100644 new mode 100755 From fcff9e455116b1b8f961aa6f5d0fb69dcc3343e9 Mon Sep 17 00:00:00 2001 From: bharnden Date: Sun, 24 Feb 2019 07:44:41 -0800 Subject: [PATCH 06/72] grpc added create session and set session location --- daemon/core/grpc/client.py | 38 +++++++++++++++++++++++++++++++++++++- daemon/core/grpc/server.py | 34 ++++++++++++++++++++++++++++++++-- daemon/proto/core.proto | 32 +++++++++++++++++++++++++++++--- 3 files changed, 98 insertions(+), 6 deletions(-) diff --git a/daemon/core/grpc/client.py b/daemon/core/grpc/client.py index 6d74e9fe..7d945d33 100644 --- a/daemon/core/grpc/client.py +++ b/daemon/core/grpc/client.py @@ -8,11 +8,22 @@ import core_pb2 import core_pb2_grpc +def update_proto(obj, **kwargs): + for key in kwargs: + value = kwargs[key] + if value is not None: + logging.info("setting proto key(%s) value(%s)", key, value) + setattr(obj, key, value) + + class CoreApiClient(object): def __init__(self, address="localhost:50051"): self.address = address self.stub = None + def create_session(self): + return self.stub.CreateSession(core_pb2.CreateSessionRequest()) + def get_sessions(self): return self.stub.GetSessions(core_pb2.SessionsRequest()) @@ -27,10 +38,17 @@ class CoreApiClient(object): return self.stub.GetSessionOptions(request) def get_session_location(self, _id): - request = core_pb2.SessionLocationRequest() + request = core_pb2.GetSessionLocationRequest() request.id = _id return self.stub.GetSessionLocation(request) + def set_session_location(self, _id, x=None, y=None , z=None, lat=None, lon=None, alt=None, scale=None): + request = core_pb2.SetSessionLocationRequest() + request.id = _id + update_proto(request.position, x=x, y=y, z=z, lat=lat, lon=lon, alt=alt) + update_proto(request, scale=scale) + return self.stub.SetSessionLocation(request) + @contextmanager def connect(self): channel = grpc.insecure_channel(self.address) @@ -44,15 +62,33 @@ class CoreApiClient(object): def main(): client = CoreApiClient() with client.connect(): + # create session + response = client.create_session() + print("created session: %s" % response) + response = client.get_sessions() print("core client received: %s" % response) if len(response.sessions) > 0: session_data = response.sessions[0] + + # set session location + response = client.set_session_location( + session_data.id, + x=0, y=0, z=None, + lat=47.57917, lon=-122.13232, alt=2.0, + scale=150000.0 + ) + print("set location response: %s" % response) + + # get session session = client.get_session(session_data.id) print(session) + # get options print(client.get_session_options(session_data.id)) + + # get location print(client.get_session_location(session_data.id)) diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index 7e5cc78c..4093ac00 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -1,4 +1,4 @@ -from core.enumerations import NodeTypes +from core.enumerations import NodeTypes, EventTypes from concurrent import futures import time @@ -81,6 +81,27 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): super(CoreApiServer, self).__init__() self.coreemu = coreemu + def CreateSession(self, request, context): + session = self.coreemu.create_session() + session.set_state(EventTypes.DEFINITION_STATE) + + # default set session location + session.location.setrefgeo(47.57917, -122.13232, 2.0) + session.location.refscale = 150000.0 + + # grpc stream handlers + # session.event_handlers.append(websocket_routes.broadcast_event) + # session.node_handlers.append(websocket_routes.broadcast_node) + # session.config_handlers.append(websocket_routes.broadcast_config) + # session.link_handlers.append(websocket_routes.broadcast_link) + # session.exception_handlers.append(websocket_routes.broadcast_exception) + # session.file_handlers.append(websocket_routes.broadcast_file) + + response = core_pb2.CreateSessionResponse() + response.id = session.session_id + response.state = session.state + return response + def GetSessions(self, request, context): response = core_pb2.SessionsResponse() for session_id in self.coreemu.sessions: @@ -95,7 +116,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): session = self.coreemu.sessions.get(request.id) x, y, z = session.location.refxyz lat, lon, alt = session.location.refgeo - response = core_pb2.SessionLocationResponse() + response = core_pb2.GetSessionLocationResponse() update_proto( response.position, x=x, @@ -108,6 +129,15 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): update_proto(response, scale=session.location.refscale) return response + def SetSessionLocation(self, request, context): + session = self.coreemu.sessions.get(request.id) + + session.location.refxyz = (request.position.x, request.position.y, request.position.z) + session.location.setrefgeo(request.position.lat, request.position.lon, request.position.alt) + session.location.refscale = request.scale + + return core_pb2.SetSessionLocationResponse() + def GetSessionOptions(self, request, context): session = self.coreemu.sessions.get(request.id) config = session.options.get_configs() diff --git a/daemon/proto/core.proto b/daemon/proto/core.proto index 7e295b52..c33eebdd 100644 --- a/daemon/proto/core.proto +++ b/daemon/proto/core.proto @@ -3,14 +3,29 @@ syntax = "proto3"; package core; service CoreApi { + rpc CreateSession (CreateSessionRequest) returns (CreateSessionResponse) { + } rpc GetSessions (SessionsRequest) returns (SessionsResponse) { } rpc GetSession (SessionRequest) returns (SessionResponse) { } rpc GetSessionOptions (SessionOptionsRequest) returns (SessionOptionsResponse) { } - rpc GetSessionLocation (SessionLocationRequest) returns (SessionLocationResponse) { + rpc GetSessionLocation (GetSessionLocationRequest) returns (GetSessionLocationResponse) { } + rpc SetSessionLocation (SetSessionLocationRequest) returns (SetSessionLocationResponse) { + + } +} + +// rpc request/response messages +message CreateSessionRequest { + +} + +message CreateSessionResponse { + int32 id = 1; + int32 state = 2; } message SessionsRequest { @@ -38,15 +53,26 @@ message SessionOptionsResponse { repeated ConfigGroup groups = 1; } -message SessionLocationRequest { +message GetSessionLocationRequest { int32 id = 1; } -message SessionLocationResponse { +message GetSessionLocationResponse { Position position = 1; float scale = 2; } +message SetSessionLocationRequest { + int32 id = 1; + Position position = 2; + float scale = 3; +} + +message SetSessionLocationResponse { + +} + +// data structures for messages below message ConfigGroup { string name = 1; repeated ConfigOption options = 2; From ad7522b5bc08c5c8878ef33dd3a2c28419330499 Mon Sep 17 00:00:00 2001 From: bharnden Date: Sun, 24 Feb 2019 08:15:33 -0800 Subject: [PATCH 07/72] grpc added delete session and set session state apis --- daemon/core/grpc/client.py | 30 +++++++++++++++++++++++------- daemon/core/grpc/server.py | 35 +++++++++++++++++++++++++++++++++++ daemon/proto/core.proto | 31 ++++++++++++++++++++++++++++++- 3 files changed, 88 insertions(+), 8 deletions(-) diff --git a/daemon/core/grpc/client.py b/daemon/core/grpc/client.py index 7d945d33..d33ad7d6 100644 --- a/daemon/core/grpc/client.py +++ b/daemon/core/grpc/client.py @@ -24,6 +24,11 @@ class CoreApiClient(object): def create_session(self): return self.stub.CreateSession(core_pb2.CreateSessionRequest()) + def delete_session(self, _id): + request = core_pb2.DeleteSessionRequest() + request.id = _id + return self.stub.DeleteSession(request) + def get_sessions(self): return self.stub.GetSessions(core_pb2.SessionsRequest()) @@ -49,6 +54,12 @@ class CoreApiClient(object): update_proto(request, scale=scale) return self.stub.SetSessionLocation(request) + def set_session_state(self, _id, state): + request = core_pb2.SetSessionStateRequest() + request.id = _id + request.state = state + return self.stub.SetSessionState(request) + @contextmanager def connect(self): channel = grpc.insecure_channel(self.address) @@ -76,20 +87,25 @@ def main(): response = client.set_session_location( session_data.id, x=0, y=0, z=None, - lat=47.57917, lon=-122.13232, alt=2.0, + lat=47.57917, lon=-122.13232, alt=3.0, scale=150000.0 ) print("set location response: %s" % response) - # get session - session = client.get_session(session_data.id) - print(session) - # get options - print(client.get_session_options(session_data.id)) + print("get options: %s" % client.get_session_options(session_data.id)) # get location - print(client.get_session_location(session_data.id)) + print("get location: %s" % client.get_session_location(session_data.id)) + + # change session state + print("set session state: %s" % client.set_session_state(session_data.id, core_pb2.INSTANTIATION)) + + # get session + print("get session: %s" % client.get_session(session_data.id)) + + # delete session + print("delete session: %s" % client.delete_session(session_data.id)) if __name__ == "__main__": diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index 4093ac00..23f14c9d 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -1,3 +1,5 @@ +import os + from core.enumerations import NodeTypes, EventTypes from concurrent import futures @@ -102,6 +104,11 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): response.state = session.state return response + def DeleteSession(self, request, context): + response = core_pb2.DeleteSessionResponse() + response.result = self.coreemu.delete_session(request.id) + return response + def GetSessions(self, request, context): response = core_pb2.SessionsResponse() for session_id in self.coreemu.sessions: @@ -138,6 +145,32 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return core_pb2.SetSessionLocationResponse() + def SetSessionState(self, request, context): + response = core_pb2.SetSessionStateResponse() + session = self.coreemu.sessions.get(request.id) + + try: + state = EventTypes(request.state) + session.set_state(state) + + if state == EventTypes.INSTANTIATION_STATE: + # create session directory if it does not exist + if not os.path.exists(session.session_dir): + os.mkdir(session.session_dir) + session.instantiate() + elif state == EventTypes.SHUTDOWN_STATE: + session.shutdown() + elif state == EventTypes.DATACOLLECT_STATE: + session.data_collect() + elif state == EventTypes.DEFINITION_STATE: + session.clear() + + response.result = True + except KeyError: + response.result = False + + return response + def GetSessionOptions(self, request, context): session = self.coreemu.sessions.get(request.id) config = session.options.get_configs() @@ -169,6 +202,8 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): pass response = core_pb2.SessionResponse() + response.state = session.state + for node_id in session.objects: node = session.objects[node_id] diff --git a/daemon/proto/core.proto b/daemon/proto/core.proto index c33eebdd..f22d72e1 100644 --- a/daemon/proto/core.proto +++ b/daemon/proto/core.proto @@ -5,6 +5,8 @@ package core; service CoreApi { rpc CreateSession (CreateSessionRequest) returns (CreateSessionResponse) { } + rpc DeleteSession (DeleteSessionRequest) returns (DeleteSessionResponse) { + } rpc GetSessions (SessionsRequest) returns (SessionsResponse) { } rpc GetSession (SessionRequest) returns (SessionResponse) { @@ -14,7 +16,8 @@ service CoreApi { rpc GetSessionLocation (GetSessionLocationRequest) returns (GetSessionLocationResponse) { } rpc SetSessionLocation (SetSessionLocationRequest) returns (SetSessionLocationResponse) { - + } + rpc SetSessionState (SetSessionStateRequest) returns (SetSessionStateResponse) { } } @@ -28,6 +31,14 @@ message CreateSessionResponse { int32 state = 2; } +message DeleteSessionRequest { + int32 id = 1; +} + +message DeleteSessionResponse { + bool result = 1; +} + message SessionsRequest { } @@ -69,10 +80,28 @@ message SetSessionLocationRequest { } message SetSessionLocationResponse { +} +message SetSessionStateRequest { + int32 id = 1; + SessionState state = 2; +} + +message SetSessionStateResponse { + bool result = 1; } // data structures for messages below +enum SessionState { + NONE = 0; + DEFINITION = 1; + CONFIGURATION = 2; + INSTANTIATION = 3; + RUNTIME = 4; + DATACOLLECT = 5; + SHUTDOWN = 6; +} + message ConfigGroup { string name = 1; repeated ConfigOption options = 2; From 601615249003bafa228452f68b3dbf843dd20067 Mon Sep 17 00:00:00 2001 From: bharnden Date: Mon, 25 Feb 2019 22:45:57 -0800 Subject: [PATCH 08/72] added grpc create/edit node calls --- daemon/core/grpc/client.py | 51 ++++++++++++++++++++++++++++++++++ daemon/core/grpc/server.py | 57 ++++++++++++++++++++++++++++++++++++-- daemon/proto/core.proto | 48 ++++++++++++++++++++++++++++++++ 3 files changed, 154 insertions(+), 2 deletions(-) diff --git a/daemon/core/grpc/client.py b/daemon/core/grpc/client.py index d33ad7d6..48d1261f 100644 --- a/daemon/core/grpc/client.py +++ b/daemon/core/grpc/client.py @@ -6,6 +6,8 @@ import grpc import core_pb2 import core_pb2_grpc +from core.emulator.emudata import NodeOptions +from core.enumerations import NodeTypes def update_proto(obj, **kwargs): @@ -60,6 +62,47 @@ class CoreApiClient(object): request.state = state return self.stub.SetSessionState(request) + def create_node(self, session, _type=NodeTypes.DEFAULT, _id=None, node_options=None, emane=None): + if not node_options: + node_options = NodeOptions() + + request = core_pb2.CreateNodeRequest() + request.session = session + request.type = _type.value + update_proto( + request, + id=_id, + name=node_options.name, + model=node_options.model, + icon=node_options.icon, + opaque=node_options.opaque, + emane=emane + ) + update_proto( + request.position, + x=node_options.x, + y=node_options.y, + lat=node_options.lat, + lon=node_options.lon, + alt=node_options.alt + ) + request.services.extend(node_options.services) + return self.stub.CreateNode(request) + + def edit_node(self, session, _id, node_options): + request = core_pb2.EditNodeRequest() + request.session = session + request.id = _id + update_proto( + request.position, + x=node_options.x, + y=node_options.y, + lat=node_options.lat, + lon=node_options.lon, + alt=node_options.alt + ) + return self.stub.EditNode(request) + @contextmanager def connect(self): channel = grpc.insecure_channel(self.address) @@ -101,6 +144,14 @@ def main(): # change session state print("set session state: %s" % client.set_session_state(session_data.id, core_pb2.INSTANTIATION)) + for i in xrange(2): + response = client.create_node(session_data.id) + print("created node: %s" % response) + node_options = NodeOptions() + node_options.x = 5 + node_options.y = 5 + print("edit node: %s" % client.edit_node(session_data.id, response.id, node_options)) + # get session print("get session: %s" % client.get_session(session_data.id)) diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index 23f14c9d..b3037019 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -1,5 +1,6 @@ import os +from core.emulator.emudata import NodeOptions from core.enumerations import NodeTypes, EventTypes from concurrent import futures @@ -198,8 +199,8 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): def GetSession(self, request, context): session = self.coreemu.sessions.get(request.id) - if not request: - pass + if not session: + raise Exception("no session found") response = core_pb2.SessionResponse() response.state = session.state @@ -244,6 +245,58 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response + def CreateNode(self, request, context): + session = self.coreemu.sessions.get(request.session) + if not session: + raise Exception("no session found") + + node_id = request.id + node_type = request.type + if node_type is None: + node_type = NodeTypes.DEFAULT.value + node_type = NodeTypes(node_type) + logging.info("creating node: %s - %s", node_type.name, request) + + node_options = NodeOptions(name=request.name, model=request.model) + node_options.icon = request.icon + node_options.opaque = request.opaque + node_options.services = request.services + + position = request.position + node_options.set_position(position.x, position.y) + node_options.set_location(position.lat, position.lon, position.alt) + node = session.add_node(_type=node_type, _id=node_id, node_options=node_options) + + # configure emane if provided + emane_model = request.emane + if emane_model: + session.emane.set_model_config(node_id, emane_model) + + response = core_pb2.CreateNodeResponse() + response.id = node.objid + return response + + def EditNode(self, request, context): + session = self.coreemu.sessions.get(request.session) + if not session: + raise Exception("no session found") + + node_id = request.id + node_options = NodeOptions() + x = request.position.x + y = request.position.y + node_options.set_position(x, y) + lat = request.position.lat + lon = request.position.lon + alt = request.position.alt + node_options.set_location(lat, lon, alt) + logging.debug("updating node(%s) - pos(%s, %s) geo(%s, %s, %s)", node_id, x, y, lat, lon, alt) + + result = session.update_node(node_id, node_options) + response = core_pb2.EditNodeResponse() + response.result = result + return response + def listen(coreemu, address="[::]:50051"): logging.info("starting grpc api: %s", address) diff --git a/daemon/proto/core.proto b/daemon/proto/core.proto index f22d72e1..1705af6e 100644 --- a/daemon/proto/core.proto +++ b/daemon/proto/core.proto @@ -19,6 +19,10 @@ service CoreApi { } rpc SetSessionState (SetSessionStateRequest) returns (SetSessionStateResponse) { } + rpc CreateNode (CreateNodeRequest) returns (CreateNodeResponse) { + } + rpc EditNode (EditNodeRequest) returns (EditNodeResponse) { + } } // rpc request/response messages @@ -91,6 +95,33 @@ message SetSessionStateResponse { bool result = 1; } +message CreateNodeRequest { + int32 session = 1; + int32 id = 2; + NodeType type = 3; + string name = 4; + string model = 5; + string icon = 6; + repeated string services = 7; + Position position = 8; + string emane = 9; + string opaque = 10; +} + +message CreateNodeResponse { + int32 id = 1; +} + +message EditNodeRequest { + int32 session = 1; + int32 id = 2; + Position position = 3; +} + +message EditNodeResponse { + bool result = 1; +} + // data structures for messages below enum SessionState { NONE = 0; @@ -102,6 +133,23 @@ enum SessionState { SHUTDOWN = 6; } +enum NodeType { + DEFAULT = 0; + PHYSICAL = 1; + TBD = 3; + SWITCH = 4; + HUB = 5; + WIRELESS_LAN = 6; + RJ45 = 7; + TUNNEL = 8; + KTUNNEL = 9; + EMANE = 10; + TAP_BRIDGE = 11; + PEER_TO_PEER = 12; + CONTROL_NET = 13; + EMANE_NET = 14; +} + message ConfigGroup { string name = 1; repeated ConfigOption options = 2; From 460e5c04d085ee6e47bb16b42f6306a0da2a9179 Mon Sep 17 00:00:00 2001 From: bharnden Date: Tue, 26 Feb 2019 14:34:52 -0800 Subject: [PATCH 09/72] grpc added get/delete node, create/edit/delete link apis --- daemon/core/grpc/client.py | 139 ++++++++++++++++++++++++++-- daemon/core/grpc/server.py | 182 ++++++++++++++++++++++++++++++++++++- daemon/proto/core.proto | 66 ++++++++++++++ 3 files changed, 378 insertions(+), 9 deletions(-) diff --git a/daemon/core/grpc/client.py b/daemon/core/grpc/client.py index 48d1261f..34e96744 100644 --- a/daemon/core/grpc/client.py +++ b/daemon/core/grpc/client.py @@ -6,8 +6,8 @@ import grpc import core_pb2 import core_pb2_grpc -from core.emulator.emudata import NodeOptions -from core.enumerations import NodeTypes +from core.emulator.emudata import NodeOptions, IpPrefixes, InterfaceData, LinkOptions +from core.enumerations import NodeTypes, LinkTypes, EventTypes def update_proto(obj, **kwargs): @@ -49,7 +49,7 @@ class CoreApiClient(object): request.id = _id return self.stub.GetSessionLocation(request) - def set_session_location(self, _id, x=None, y=None , z=None, lat=None, lon=None, alt=None, scale=None): + def set_session_location(self, _id, x=None, y=None, z=None, lat=None, lon=None, alt=None, scale=None): request = core_pb2.SetSessionLocationRequest() request.id = _id update_proto(request.position, x=x, y=y, z=z, lat=lat, lon=lon, alt=alt) @@ -59,7 +59,7 @@ class CoreApiClient(object): def set_session_state(self, _id, state): request = core_pb2.SetSessionStateRequest() request.id = _id - request.state = state + request.state = state.value return self.stub.SetSessionState(request) def create_node(self, session, _type=NodeTypes.DEFAULT, _id=None, node_options=None, emane=None): @@ -89,6 +89,12 @@ class CoreApiClient(object): request.services.extend(node_options.services) return self.stub.CreateNode(request) + def get_node(self, session, _id): + request = core_pb2.GetNodeRequest() + request.session = session + request.id = _id + return self.stub.GetNode(request) + def edit_node(self, session, _id, node_options): request = core_pb2.EditNodeRequest() request.session = session @@ -103,6 +109,102 @@ class CoreApiClient(object): ) return self.stub.EditNode(request) + def delete_node(self, session, _id): + request = core_pb2.DeleteNodeRequest() + request.session = session + request.id = _id + return self.stub.DeleteNode(request) + + def create_link(self, session, node_one, node_two, interface_one=None, interface_two=None, link_options=None): + request = core_pb2.CreateLinkRequest() + request.session = session + update_proto( + request.link, + node_one=node_one, + node_two=node_two, + type=LinkTypes.WIRED.value + ) + + if interface_one is not None: + update_proto( + request.link.interface_one, + id=interface_one.id, + name=interface_one.name, + mac=interface_one.mac, + ip4=interface_one.ip4, + ip4mask=interface_one.ip4_mask, + ip6=interface_one.ip6, + ip6mask=interface_one.ip6_mask + ) + + if interface_two is not None: + update_proto( + request.link.interface_two, + id=interface_two.id, + name=interface_two.name, + mac=interface_two.mac, + ip4=interface_two.ip4, + ip4mask=interface_two.ip4_mask, + ip6=interface_two.ip6, + ip6mask=interface_two.ip6_mask + ) + + if link_options is not None: + update_proto( + request.link.options, + delay=link_options.delay, + bandwidth=link_options.bandwidth, + per=link_options.per, + dup=link_options.dup, + jitter=link_options.jitter, + mer=link_options.mer, + burst=link_options.burst, + mburst=link_options.mburst, + unidirectional=link_options.unidirectional, + key=link_options.key, + opaque=link_options.opaque + ) + + return self.stub.CreateLink(request) + + def edit_link(self, session, node_one, node_two, link_options, interface_one=None, interface_two=None): + request = core_pb2.EditLinkRequest() + request.session = session + request.node_one = node_one + request.node_two = node_two + update_proto( + request, + interface_one=interface_one, + interface_two=interface_two + ) + update_proto( + request.options, + delay=link_options.delay, + bandwidth=link_options.bandwidth, + per=link_options.per, + dup=link_options.dup, + jitter=link_options.jitter, + mer=link_options.mer, + burst=link_options.burst, + mburst=link_options.mburst, + unidirectional=link_options.unidirectional, + key=link_options.key, + opaque=link_options.opaque + ) + return self.stub.EditLink(request) + + def delete_link(self, session, node_one, node_two, interface_one=None, interface_two=None): + request = core_pb2.DeleteLinkRequest() + request.session = session + request.node_one = node_one + request.node_two = node_two + update_proto( + request, + interface_one=interface_one, + interface_two=interface_two + ) + return self.stub.DeleteLink(request) + @contextmanager def connect(self): channel = grpc.insecure_channel(self.address) @@ -142,15 +244,40 @@ def main(): print("get location: %s" % client.get_session_location(session_data.id)) # change session state - print("set session state: %s" % client.set_session_state(session_data.id, core_pb2.INSTANTIATION)) + print("set session state: %s" % client.set_session_state(session_data.id, EventTypes.CONFIGURATION_STATE)) + + # create switch node + response = client.create_node(session_data.id, _type=NodeTypes.SWITCH) + print("created switch: %s" % response) + switch_id = response.id + + # ip generator for example + prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16") for i in xrange(2): response = client.create_node(session_data.id) print("created node: %s" % response) + node_id = response.id node_options = NodeOptions() node_options.x = 5 node_options.y = 5 - print("edit node: %s" % client.edit_node(session_data.id, response.id, node_options)) + print("edit node: %s" % client.edit_node(session_data.id, node_id, node_options)) + print("get node: %s" % client.get_node(session_data.id, node_id)) + + # create link + interface_one = InterfaceData( + _id=None, name=None, mac=None, + ip4=str(prefixes.ip4.addr(node_id)), ip4_mask=prefixes.ip4.prefixlen, + ip6=None, ip6_mask=None + ) + print("created link: %s" % client.create_link(session_data.id, node_id, switch_id, interface_one)) + link_options = LinkOptions() + link_options.per = 50 + print("edit link: %s" % client.edit_link( + session_data.id, node_id, switch_id, link_options, interface_one=0)) + + # change session state + print("set session state: %s" % client.set_session_state(session_data.id, EventTypes.INSTANTIATION_STATE)) # get session print("get session: %s" % client.get_session(session_data.id)) diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index b3037019..91665979 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -1,7 +1,7 @@ import os -from core.emulator.emudata import NodeOptions -from core.enumerations import NodeTypes, EventTypes +from core.emulator.emudata import NodeOptions, InterfaceData, LinkOptions +from core.enumerations import NodeTypes, EventTypes, LinkTypes from concurrent import futures import time @@ -27,7 +27,6 @@ def update_proto(obj, **kwargs): for key in kwargs: value = kwargs[key] if value is not None: - logging.info("setting proto key(%s) value(%s)", key, value) setattr(obj, key, value) @@ -276,6 +275,52 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): response.id = node.objid return response + def GetNode(self, request, context): + session = self.coreemu.sessions.get(request.session) + if not session: + raise Exception("no session found") + node = session.get_object(request.id) + if not node: + raise Exception("no node found") + + response = core_pb2.GetNodeResponse() + + for interface_id, interface in node._netif.iteritems(): + net_id = None + if interface.net: + net_id = interface.net.objid + + interface_proto = response.interfaces.add() + interface_proto.id = interface_id + interface_proto.netid = net_id + interface_proto.name = interface.name + interface_proto.mac = str(interface.hwaddr) + interface_proto.mtu = interface.mtu + interface_proto.flowid = interface.flow_id + + emane_model = None + if nodeutils.is_node(node, NodeTypes.EMANE): + emane_model = node.model.name + + update_proto( + response.node, + name=node.name, + type=nodeutils.get_node_type(node.__class__).value, + emane=emane_model, + model=node.type + ) + + update_proto( + response.node.position, + x=node.position.x, + y=node.position.y, + z=node.position.z, + ) + + services = [x.name for x in getattr(node, "services", [])] + response.node.services.extend(services) + return response + def EditNode(self, request, context): session = self.coreemu.sessions.get(request.session) if not session: @@ -297,6 +342,137 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): response.result = result return response + def DeleteNode(self, request, context): + logging.info("delete node: %s", request) + session = self.coreemu.sessions.get(request.session) + if not session: + raise Exception("no session found") + + response = core_pb2.DeleteNodeResponse() + response.result = session.delete_node(request.id) + return response + + def CreateLink(self, request, context): + session = self.coreemu.sessions.get(request.session) + if not session: + raise Exception("no session found") + + logging.info("adding link: %s", request) + + node_one = request.link.node_one + node_two = request.link.node_two + + interface_one = None + interface_one_data = request.link.interface_one + if interface_one_data: + name = interface_one_data.name + if name == "": + name = None + mac = interface_one_data.mac + if mac == "": + mac = None + interface_one = InterfaceData( + _id=interface_one_data.id, + name=name, + mac=mac, + ip4=interface_one_data.ip4, + ip4_mask=interface_one_data.ip4mask, + ip6=interface_one_data.ip6, + ip6_mask=interface_one_data.ip6mask, + ) + + interface_two = None + interface_two_data = request.link.interface_two + if interface_two_data: + name = interface_two_data.name + if name == "": + name = None + mac = interface_two_data.mac + if mac == "": + mac = None + interface_two = InterfaceData( + _id=interface_two_data.id, + name=name, + mac=mac, + ip4=interface_two_data.ip4, + ip4_mask=interface_two_data.ip4mask, + ip6=interface_two_data.ip6, + ip6_mask=interface_two_data.ip6mask, + ) + + link_type = None + link_type_value = request.link.type + if link_type_value is not None: + link_type = LinkTypes(link_type_value) + + options_data = request.link.options + link_options = LinkOptions(_type=link_type) + if options_data: + link_options.delay = options_data.delay + link_options.bandwidth = options_data.bandwidth + link_options.per = options_data.per + link_options.dup = options_data.dup + link_options.jitter = options_data.jitter + link_options.mer = options_data.mer + link_options.burst = options_data.burst + link_options.mburst = options_data.mburst + link_options.unidirectional = options_data.unidirectional + link_options.key = options_data.key + link_options.opaque = options_data.opaque + + session.add_link(node_one, node_two, interface_one, interface_two, link_options=link_options) + + response = core_pb2.CreateLinkResponse() + response.result = True + return response + + def EditLink(self, request, context): + logging.info("edit link: %s", request) + session = self.coreemu.sessions.get(request.session) + if not session: + raise Exception("no session found") + + node_one = request.node_one + node_two = request.node_two + interface_one_id = request.interface_one + interface_two_id = request.interface_two + + options_data = request.options + link_options = LinkOptions() + link_options.delay = options_data.delay + link_options.bandwidth = options_data.bandwidth + link_options.per = options_data.per + link_options.dup = options_data.dup + link_options.jitter = options_data.jitter + link_options.mer = options_data.mer + link_options.burst = options_data.burst + link_options.mburst = options_data.mburst + link_options.unidirectional = options_data.unidirectional + link_options.key = options_data.key + link_options.opaque = options_data.opaque + + session.update_link(node_one, node_two, interface_one_id, interface_two_id, link_options) + + response = core_pb2.EditLinkResponse() + response.result = True + return response + + def DeleteLink(self, request, context): + logging.info("delete link: %s", request) + session = self.coreemu.sessions.get(request.session) + if not session: + raise Exception("no session found") + + node_one = request.node_one + node_two = request.node_two + interface_one = request.interface_one + interface_two = request.interface_two + session.delete_link(node_one, node_two, interface_one, interface_two) + + response = core_pb2.DeleteLinkResponse() + response.result = True + return response + def listen(coreemu, address="[::]:50051"): logging.info("starting grpc api: %s", address) diff --git a/daemon/proto/core.proto b/daemon/proto/core.proto index 1705af6e..e5c145de 100644 --- a/daemon/proto/core.proto +++ b/daemon/proto/core.proto @@ -21,8 +21,18 @@ service CoreApi { } rpc CreateNode (CreateNodeRequest) returns (CreateNodeResponse) { } + rpc GetNode (GetNodeRequest) returns (GetNodeResponse) { + } rpc EditNode (EditNodeRequest) returns (EditNodeResponse) { } + rpc DeleteNode (DeleteNodeRequest) returns (DeleteNodeResponse) { + } + rpc CreateLink (CreateLinkRequest) returns (CreateLinkResponse) { + } + rpc EditLink (EditLinkRequest) returns (EditLinkResponse) { + } + rpc DeleteLink (DeleteLinkRequest) returns (DeleteLinkResponse) { + } } // rpc request/response messages @@ -112,6 +122,16 @@ message CreateNodeResponse { int32 id = 1; } +message GetNodeRequest { + int32 session = 1; + int32 id = 2; +} + +message GetNodeResponse { + Node node = 1; + repeated Interface interfaces = 2; +} + message EditNodeRequest { int32 session = 1; int32 id = 2; @@ -122,6 +142,49 @@ message EditNodeResponse { bool result = 1; } +message DeleteNodeRequest { + int32 session = 1; + int32 id = 2; +} + +message DeleteNodeResponse { + bool result = 1; +} + +message CreateLinkRequest { + int32 session = 1; + Link link = 2; +} + +message CreateLinkResponse { + bool result = 1; +} + +message EditLinkRequest { + int32 session = 1; + int32 node_one = 2; + int32 node_two = 3; + int32 interface_one = 4; + int32 interface_two = 5; + LinkOptions options = 6; +} + +message EditLinkResponse { + bool result = 1; +} + +message DeleteLinkRequest { + int32 session = 1; + int32 node_one = 2; + int32 node_two = 3; + int32 interface_one = 4; + int32 interface_two = 5; +} + +message DeleteLinkResponse { + bool result = 1; +} + // data structures for messages below enum SessionState { NONE = 0; @@ -210,6 +273,9 @@ message Interface { int32 ip4mask = 5; string ip6 = 6; int32 ip6mask = 7; + int32 netid = 8; + int32 flowid = 9; + int32 mtu = 10; } message Position { From 44f70d0c2e746af4be739a19b343eb374a5bec3a Mon Sep 17 00:00:00 2001 From: bharnden Date: Fri, 1 Mar 2019 22:14:16 -0800 Subject: [PATCH 10/72] grpc added get node links, get services, get emane config --- daemon/core/grpc/client.py | 22 ++++++++++ daemon/core/grpc/server.py | 82 +++++++++++++++++++++++++++++--------- daemon/proto/core.proto | 36 +++++++++++++++++ 3 files changed, 122 insertions(+), 18 deletions(-) diff --git a/daemon/core/grpc/client.py b/daemon/core/grpc/client.py index 34e96744..624aacc5 100644 --- a/daemon/core/grpc/client.py +++ b/daemon/core/grpc/client.py @@ -115,6 +115,12 @@ class CoreApiClient(object): request.id = _id return self.stub.DeleteNode(request) + def get_node_links(self, session, _id): + request = core_pb2.GetNodeLinksRequest() + request.session = session + request.id = _id + return self.stub.GetNodeLinks(request) + def create_link(self, session, node_one, node_two, interface_one=None, interface_two=None, link_options=None): request = core_pb2.CreateLinkRequest() request.session = session @@ -205,6 +211,15 @@ class CoreApiClient(object): ) return self.stub.DeleteLink(request) + def get_services(self): + request = core_pb2.GetServicesRequest() + return self.stub.GetServices(request) + + def get_emane_config(self, session): + request = core_pb2.GetEmaneConfigRequest() + request.session = session + return self.stub.GetEmaneConfig(request) + @contextmanager def connect(self): channel = grpc.insecure_channel(self.address) @@ -218,6 +233,8 @@ class CoreApiClient(object): def main(): client = CoreApiClient() with client.connect(): + print("services: %s" % client.get_services()) + # create session response = client.create_session() print("created session: %s" % response) @@ -228,6 +245,8 @@ def main(): if len(response.sessions) > 0: session_data = response.sessions[0] + print("emane config: %s" % client.get_emane_config(session_data.id)) + # set session location response = client.set_session_location( session_data.id, @@ -276,8 +295,11 @@ def main(): print("edit link: %s" % client.edit_link( session_data.id, node_id, switch_id, link_options, interface_one=0)) + print("get node links: %s" % client.get_node_links(session_data.id, node_id)) + # change session state print("set session state: %s" % client.set_session_state(session_data.id, EventTypes.INSTANTIATION_STATE)) + # import pdb; pdb.set_trace() # get session print("get session: %s" % client.get_session(session_data.id)) diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index 91665979..5132b52c 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -12,6 +12,7 @@ import grpc import core_pb2 import core_pb2_grpc from core.misc import nodeutils +from core.service import ServiceManager _ONE_DAY_IN_SECONDS = 60 * 60 * 24 @@ -30,6 +31,31 @@ def update_proto(obj, **kwargs): setattr(obj, key, value) +def get_config_groups(config, configurable_options): + groups = [] + config_options = [] + + for configuration in configurable_options.configurations(): + value = config[configuration.id] + config_option = core_pb2.ConfigOption() + config_option.label = configuration.label + config_option.name = configuration.id + config_option.value = value + config_option.type = configuration.type.value + config_option.select.extend(configuration.options) + config_options.append(config_option) + + for config_group in configurable_options.config_groups(): + start = config_group.start - 1 + stop = config_group.stop + config_group_proto = core_pb2.ConfigGroup() + config_group_proto.name = config_group.name + config_group_proto.options.extend(config_options[start: stop]) + groups.append(config_group_proto) + + return groups + + def convert_link(session, link_data, link): if link_data.interface1_id is not None: node = session.get_object(link_data.node1_id) @@ -173,27 +199,12 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): def GetSessionOptions(self, request, context): session = self.coreemu.sessions.get(request.id) + config = session.options.get_configs() + groups = get_config_groups(config, session.options) response = core_pb2.SessionOptionsResponse() - config_options = [] - for configuration in session.options.configurations(): - value = config[configuration.id] - config_option = core_pb2.ConfigOption() - config_option.label = configuration.label - config_option.name = configuration.id - config_option.value = value - config_option.type = configuration.type.value - config_option.select.extend(configuration.options) - config_options.append(config_option) - - for config_group in session.options.config_groups(): - start = config_group.start - 1 - stop = config_group.stop - config_group_proto = response.groups.add() - config_group_proto.name = config_group.name - config_group_proto.options.extend(config_options[start: stop]) - + response.groups.extend(groups) return response def GetSession(self, request, context): @@ -352,6 +363,23 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): response.result = session.delete_node(request.id) return response + def GetNodeLinks(self, request, context): + logging.info("get node links: %s", request) + session = self.coreemu.sessions.get(request.session) + if not session: + raise Exception("no session found") + node = session.get_object(request.id) + if not node: + raise Exception("no node found") + + response = core_pb2.GetNodeLinksResponse() + links_data = node.all_link_data(0) + for link_data in links_data: + link = response.links.add() + convert_link(session, link_data, link) + + return response + def CreateLink(self, request, context): session = self.coreemu.sessions.get(request.session) if not session: @@ -473,6 +501,24 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): response.result = True return response + def GetServices(self, request, context): + response = core_pb2.GetServicesResponse() + for service in ServiceManager.services.itervalues(): + service_proto = response.services.add() + service_proto.group = service.group + service_proto.name = service.name + return response + + def GetEmaneConfig(self, request, context): + session = self.coreemu.sessions.get(request.session) + if not session: + raise Exception("no session found") + config = session.emane.get_configs() + groups = get_config_groups(config, session.emane.emane_config) + response = core_pb2.GetEmaneConfigResponse() + response.groups.extend(groups) + return response + def listen(coreemu, address="[::]:50051"): logging.info("starting grpc api: %s", address) diff --git a/daemon/proto/core.proto b/daemon/proto/core.proto index e5c145de..f390408e 100644 --- a/daemon/proto/core.proto +++ b/daemon/proto/core.proto @@ -27,12 +27,18 @@ service CoreApi { } rpc DeleteNode (DeleteNodeRequest) returns (DeleteNodeResponse) { } + rpc GetNodeLinks (GetNodeLinksRequest) returns (GetNodeLinksResponse) { + } rpc CreateLink (CreateLinkRequest) returns (CreateLinkResponse) { } rpc EditLink (EditLinkRequest) returns (EditLinkResponse) { } rpc DeleteLink (DeleteLinkRequest) returns (DeleteLinkResponse) { } + rpc GetServices (GetServicesRequest) returns (GetServicesResponse) { + } + rpc GetEmaneConfig (GetEmaneConfigRequest) returns (GetEmaneConfigResponse) { + } } // rpc request/response messages @@ -151,6 +157,15 @@ message DeleteNodeResponse { bool result = 1; } +message GetNodeLinksRequest { + int32 session = 1; + int32 id = 2; +} + +message GetNodeLinksResponse { + repeated Link links = 1; +} + message CreateLinkRequest { int32 session = 1; Link link = 2; @@ -185,6 +200,22 @@ message DeleteLinkResponse { bool result = 1; } +message GetServicesRequest { + +} + +message GetServicesResponse { + repeated Service services = 1; +} + +message GetEmaneConfigRequest { + int32 session = 1; +} + +message GetEmaneConfigResponse { + repeated ConfigGroup groups = 1; +} + // data structures for messages below enum SessionState { NONE = 0; @@ -213,6 +244,11 @@ enum NodeType { EMANE_NET = 14; } +message Service { + string group = 1; + string name = 2; +} + message ConfigGroup { string name = 1; repeated ConfigOption options = 2; From 0ccf5a7456d13d13c963f204e84b7a4f52a4d377 Mon Sep 17 00:00:00 2001 From: bharnden Date: Sat, 2 Mar 2019 13:04:55 -0800 Subject: [PATCH 11/72] updated core-daemon to use argparse and formally added ovs and grpc flags --- daemon/scripts/core-daemon | 61 +++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 34 deletions(-) diff --git a/daemon/scripts/core-daemon b/daemon/scripts/core-daemon index 29410884..37608897 100755 --- a/daemon/scripts/core-daemon +++ b/daemon/scripts/core-daemon @@ -5,9 +5,9 @@ messages and instantiates emulated nodes and networks within the kernel. Various message handlers are defined and some support for sending messages. """ +import argparse import ConfigParser import logging -import optparse import sys import threading import time @@ -32,7 +32,7 @@ def banner(): logging.info("CORE daemon v.%s started %s", constants.COREDPY_VERSION, time.ctime()) -def cored(cfg, use_ovs): +def cored(cfg): """ Start the CoreServer object and enter the server loop. @@ -47,7 +47,7 @@ def cored(cfg, use_ovs): try: server = CoreServer((host, port), CoreHandler, cfg) - if use_ovs: + if cfg["ovs"] == "True": from core.netns.openvswitch import OVS_NODES server.coreemu.update_nodes(OVS_NODES) except: @@ -55,9 +55,10 @@ def cored(cfg, use_ovs): sys.exit(1) # initialize grpc api - grpc_thread = threading.Thread(target=listen, args=(server.coreemu,)) - grpc_thread.daemon = True - grpc_thread.start() + if cfg["grpc"] == "True": + grpc_thread = threading.Thread(target=listen, args=(server.coreemu,)) + grpc_thread.daemon = True + grpc_thread.start() close_onexec(server.fileno()) logging.info("server started, listening on: %s:%s", host, port) @@ -80,24 +81,24 @@ def get_merged_config(filename): "numthreads": "1", } - usagestr = "usage: %prog [-h] [options] [args]\n\n" + \ - "CORE daemon v.%s instantiates Linux network namespace " \ - "nodes." % constants.COREDPY_VERSION - parser = optparse.OptionParser(usage=usagestr) - parser.add_option("-f", "--configfile", dest="configfile", type="string", - help="read config from specified file; default = %s" % filename) - parser.add_option("-p", "--port", dest="port", type=int, - help="port number to listen on; default = %s" % defaults["port"]) - parser.add_option("-t", "--numthreads", dest="numthreads", type=int, - help="number of server threads; default = %s" % defaults["numthreads"]) + parser = argparse.ArgumentParser( + description="CORE daemon v.%s instantiates Linux network namespace nodes." % constants.COREDPY_VERSION) + parser.add_argument("-f", "--configfile", dest="configfile", + help="read config from specified file; default = %s" % filename) + parser.add_argument("-p", "--port", dest="port", type=int, + help="port number to listen on; default = %s" % defaults["port"]) + parser.add_argument("-n", "--numthreads", dest="numthreads", type=int, + help="number of server threads; default = %s" % defaults["numthreads"]) + parser.add_argument("--ovs", action="store_true", help="enable experimental ovs mode, default is false") + parser.add_argument("--grpc", action="store_true", help="enable grpc api, default is false") # parse command line options - options, args = parser.parse_args() + args = parser.parse_args() # read the config file - if options.configfile is not None: - filename = options.configfile - del options.configfile + if args.configfile is not None: + filename = args.configfile + del args.configfile cfg = ConfigParser.SafeConfigParser(defaults) cfg.read(filename) @@ -106,12 +107,12 @@ def get_merged_config(filename): cfg.add_section(section) # merge command line with config file - for opt in options.__dict__: - val = options.__dict__[opt] + for opt in args.__dict__: + val = args.__dict__[opt] if val is not None: - cfg.set(section, opt, val.__str__()) + cfg.set(section, opt, str(val)) - return dict(cfg.items(section)), args + return dict(cfg.items(section)) def main(): @@ -121,22 +122,14 @@ def main(): :return: nothing """ # get a configuration merged from config file and command-line arguments - cfg, args = get_merged_config("%s/core.conf" % constants.CORE_CONF_DIR) - for a in args: - logging.error("ignoring command line argument: %s", a) - + cfg = get_merged_config("%s/core.conf" % constants.CORE_CONF_DIR) banner() - # check if ovs flag was provided - use_ovs = len(sys.argv) == 2 and sys.argv[1] == "ovs" - try: - cored(cfg, use_ovs) + cored(cfg) except KeyboardInterrupt: logging.info("keyboard interrupt, stopping core daemon") - sys.exit(0) - if __name__ == "__main__": main() From 0c20e7663092722ad364b2246a569932da3721bd Mon Sep 17 00:00:00 2001 From: bharnden Date: Sat, 2 Mar 2019 13:44:27 -0800 Subject: [PATCH 12/72] grpc added save/open xml, fixed session.get_node_count(), fixed issue with grpc get session options --- daemon/core/grpc/client.py | 137 +++++++++++++++++++++---------------- daemon/core/grpc/server.py | 41 ++++++++++- daemon/core/session.py | 8 ++- daemon/proto/core.proto | 21 ++++++ 4 files changed, 145 insertions(+), 62 deletions(-) diff --git a/daemon/core/grpc/client.py b/daemon/core/grpc/client.py index 624aacc5..2f5340b3 100644 --- a/daemon/core/grpc/client.py +++ b/daemon/core/grpc/client.py @@ -1,5 +1,6 @@ from __future__ import print_function import logging +import os from contextlib import contextmanager import grpc @@ -220,6 +221,21 @@ class CoreApiClient(object): request.session = session return self.stub.GetEmaneConfig(request) + def save_xml(self, session, file_path): + request = core_pb2.SaveXmlRequest() + request.session = session + response = self.stub.SaveXml(request) + with open(file_path, "wb") as xml_file: + xml_file.write(response.data) + + def open_xml(self, file_path): + with open(file_path, "rb") as xml_file: + data = xml_file.read() + + request = core_pb2.OpenXmlRequest() + request.data = data + return self.stub.OpenXml(request) + @contextmanager def connect(self): channel = grpc.insecure_channel(self.address) @@ -231,81 +247,86 @@ class CoreApiClient(object): def main(): + xml_file_name = "/tmp/core.xml" + client = CoreApiClient() with client.connect(): + if os.path.exists(xml_file_name): + print("open xml: %s" % client.open_xml(xml_file_name)) + print("services: %s" % client.get_services()) # create session - response = client.create_session() - print("created session: %s" % response) + session_data = client.create_session() + print("created session: %s" % session_data) response = client.get_sessions() print("core client received: %s" % response) - if len(response.sessions) > 0: - session_data = response.sessions[0] + print("emane config: %s" % client.get_emane_config(session_data.id)) - print("emane config: %s" % client.get_emane_config(session_data.id)) + # set session location + response = client.set_session_location( + session_data.id, + x=0, y=0, z=None, + lat=47.57917, lon=-122.13232, alt=3.0, + scale=150000.0 + ) + print("set location response: %s" % response) - # set session location - response = client.set_session_location( - session_data.id, - x=0, y=0, z=None, - lat=47.57917, lon=-122.13232, alt=3.0, - scale=150000.0 + # get options + print("get options: %s" % client.get_session_options(session_data.id)) + + # get location + print("get location: %s" % client.get_session_location(session_data.id)) + + # change session state + print("set session state: %s" % client.set_session_state(session_data.id, EventTypes.CONFIGURATION_STATE)) + + # create switch node + response = client.create_node(session_data.id, _type=NodeTypes.SWITCH) + print("created switch: %s" % response) + switch_id = response.id + + # ip generator for example + prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16") + + for i in xrange(2): + response = client.create_node(session_data.id) + print("created node: %s" % response) + node_id = response.id + node_options = NodeOptions() + node_options.x = 5 + node_options.y = 5 + print("edit node: %s" % client.edit_node(session_data.id, node_id, node_options)) + print("get node: %s" % client.get_node(session_data.id, node_id)) + + # create link + interface_one = InterfaceData( + _id=None, name=None, mac=None, + ip4=str(prefixes.ip4.addr(node_id)), ip4_mask=prefixes.ip4.prefixlen, + ip6=None, ip6_mask=None ) - print("set location response: %s" % response) + print("created link: %s" % client.create_link(session_data.id, node_id, switch_id, interface_one)) + link_options = LinkOptions() + link_options.per = 50 + print("edit link: %s" % client.edit_link( + session_data.id, node_id, switch_id, link_options, interface_one=0)) - # get options - print("get options: %s" % client.get_session_options(session_data.id)) + print("get node links: %s" % client.get_node_links(session_data.id, node_id)) - # get location - print("get location: %s" % client.get_session_location(session_data.id)) + # change session state + print("set session state: %s" % client.set_session_state(session_data.id, EventTypes.INSTANTIATION_STATE)) + # import pdb; pdb.set_trace() - # change session state - print("set session state: %s" % client.set_session_state(session_data.id, EventTypes.CONFIGURATION_STATE)) + # get session + print("get session: %s" % client.get_session(session_data.id)) - # create switch node - response = client.create_node(session_data.id, _type=NodeTypes.SWITCH) - print("created switch: %s" % response) - switch_id = response.id + # save xml + client.save_xml(session_data.id, xml_file_name) - # ip generator for example - prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16") - - for i in xrange(2): - response = client.create_node(session_data.id) - print("created node: %s" % response) - node_id = response.id - node_options = NodeOptions() - node_options.x = 5 - node_options.y = 5 - print("edit node: %s" % client.edit_node(session_data.id, node_id, node_options)) - print("get node: %s" % client.get_node(session_data.id, node_id)) - - # create link - interface_one = InterfaceData( - _id=None, name=None, mac=None, - ip4=str(prefixes.ip4.addr(node_id)), ip4_mask=prefixes.ip4.prefixlen, - ip6=None, ip6_mask=None - ) - print("created link: %s" % client.create_link(session_data.id, node_id, switch_id, interface_one)) - link_options = LinkOptions() - link_options.per = 50 - print("edit link: %s" % client.edit_link( - session_data.id, node_id, switch_id, link_options, interface_one=0)) - - print("get node links: %s" % client.get_node_links(session_data.id, node_id)) - - # change session state - print("set session state: %s" % client.set_session_state(session_data.id, EventTypes.INSTANTIATION_STATE)) - # import pdb; pdb.set_trace() - - # get session - print("get session: %s" % client.get_session(session_data.id)) - - # delete session - print("delete session: %s" % client.delete_session(session_data.id)) + # delete session + print("delete session: %s" % client.delete_session(session_data.id)) if __name__ == "__main__": diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index 5132b52c..2eac714b 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -1,4 +1,5 @@ import os +import tempfile from core.emulator.emudata import NodeOptions, InterfaceData, LinkOptions from core.enumerations import NodeTypes, EventTypes, LinkTypes @@ -201,7 +202,10 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): session = self.coreemu.sessions.get(request.id) config = session.options.get_configs() - groups = get_config_groups(config, session.options) + defaults = session.options.default_values() + defaults.update(config) + + groups = get_config_groups(defaults, session.options) response = core_pb2.SessionOptionsResponse() response.groups.extend(groups) @@ -519,6 +523,41 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): response.groups.extend(groups) return response + def SaveXml(self, request, context): + session = self.coreemu.sessions.get(request.session) + if not session: + raise Exception("no session found") + + _, temp_path = tempfile.mkstemp() + session.save_xml(temp_path) + + with open(temp_path, "rb") as xml_file: + data = xml_file.read() + + response = core_pb2.SaveXmlResponse() + response.data = data + return response + + def OpenXml(self, request, context): + session = self.coreemu.create_session() + session.set_state(EventTypes.CONFIGURATION_STATE) + + _, temp_path = tempfile.mkstemp() + with open(temp_path, "wb") as xml_file: + xml_file.write(request.data) + + response = core_pb2.OpenXmlResponse() + try: + session.open_xml(temp_path, start=True) + response.session = session.session_id + response.result = True + except: + response.result = False + logging.exception("error opening session file") + self.coreemu.delete_session(session.session_id) + + return response + def listen(coreemu, address="[::]:50051"): logging.info("starting grpc api: %s", address) diff --git a/daemon/core/session.py b/daemon/core/session.py index 5d955c2a..d9c02a5f 100644 --- a/daemon/core/session.py +++ b/daemon/core/session.py @@ -564,7 +564,7 @@ class Session(object): """ logging.info("session id=%s name=%s state=%s", self.session_id, self.name, self.state) logging.info("file=%s thumbnail=%s node_count=%s/%s", - self.file_name, self.thumbnail, self.get_node_count(), len(self.objects)) + self.file_name, self.thumbnail, self.get_node_count(), len(self.objects)) def exception(self, level, source, object_id, text): """ @@ -631,10 +631,12 @@ class Session(object): """ with self._objects_lock: - count = len([x for x in self.objects if not nodeutils.is_node(x, (NodeTypes.PEER_TO_PEER, NodeTypes.CONTROL_NET))]) + count = len([x for x in self.objects.itervalues() + if not nodeutils.is_node(x, (NodeTypes.PEER_TO_PEER, NodeTypes.CONTROL_NET))]) # on Linux, GreTapBridges are auto-created, not part of GUI's node count - count -= len([x for x in self.objects if nodeutils.is_node(x, NodeTypes.TAP_BRIDGE) and not nodeutils.is_node(x, NodeTypes.TUNNEL)]) + count -= len([x for x in self.objects.itervalues() + if nodeutils.is_node(x, NodeTypes.TAP_BRIDGE) and not nodeutils.is_node(x, NodeTypes.TUNNEL)]) return count diff --git a/daemon/proto/core.proto b/daemon/proto/core.proto index f390408e..e62a6590 100644 --- a/daemon/proto/core.proto +++ b/daemon/proto/core.proto @@ -39,6 +39,10 @@ service CoreApi { } rpc GetEmaneConfig (GetEmaneConfigRequest) returns (GetEmaneConfigResponse) { } + rpc SaveXml (SaveXmlRequest) returns (SaveXmlResponse) { + } + rpc OpenXml (OpenXmlRequest) returns (OpenXmlResponse) { + } } // rpc request/response messages @@ -216,6 +220,23 @@ message GetEmaneConfigResponse { repeated ConfigGroup groups = 1; } +message SaveXmlRequest { + int32 session = 1; +} + +message SaveXmlResponse { + bytes data = 1; +} + +message OpenXmlRequest { + bytes data = 1; +} + +message OpenXmlResponse { + bool result = 1; + int32 session = 2; +} + // data structures for messages below enum SessionState { NONE = 0; From 5753c919060d2b1aefe6f879cf6568a7d2b16f21 Mon Sep 17 00:00:00 2001 From: bharnden Date: Thu, 14 Mar 2019 22:33:10 -0700 Subject: [PATCH 13/72] grpc added get/set service defaults and get node service data --- daemon/core/grpc/client.py | 27 ++++++++++++++++++ daemon/core/grpc/server.py | 49 ++++++++++++++++++++++++++++++++ daemon/proto/core.proto | 57 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 133 insertions(+) diff --git a/daemon/core/grpc/client.py b/daemon/core/grpc/client.py index 2f5340b3..81e644d0 100644 --- a/daemon/core/grpc/client.py +++ b/daemon/core/grpc/client.py @@ -216,6 +216,29 @@ class CoreApiClient(object): request = core_pb2.GetServicesRequest() return self.stub.GetServices(request) + def get_service_defaults(self, session): + request = core_pb2.GetServiceDefaultsRequest() + request.session = session + return self.stub.GetServiceDefaults(request) + + def set_service_defaults(self, session, service_defaults): + request = core_pb2.SetServiceDefaultsRequest() + request.session = session + for node_type in service_defaults: + services = service_defaults[node_type] + service_defaults_proto = request.defaults.add() + service_defaults_proto.node_type = node_type + service_defaults_proto.services.extend(services) + + return self.stub.SetServiceDefaults(request) + + def get_node_service(self, session, _id, service): + request = core_pb2.GetNodeServiceRequest() + request.session = session + request.id = _id + request.service = service + return self.stub.GetNodeService(request) + def get_emane_config(self, session): request = core_pb2.GetEmaneConfigRequest() request.session = session @@ -260,6 +283,8 @@ def main(): session_data = client.create_session() print("created session: %s" % session_data) + print("default services: %s" % client.get_service_defaults(session_data.id)) + response = client.get_sessions() print("core client received: %s" % response) @@ -301,6 +326,8 @@ def main(): print("edit node: %s" % client.edit_node(session_data.id, node_id, node_options)) print("get node: %s" % client.get_node(session_data.id, node_id)) + print("node service: %s" % client.get_node_service(session_data.id, node_id, "zebra")) + # create link interface_one = InterfaceData( _id=None, name=None, mac=None, diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index 2eac714b..42bd9599 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -513,6 +513,55 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): service_proto.name = service.name return response + def GetServiceDefaults(self, request, context): + session = self.coreemu.sessions.get(request.session) + if not session: + raise Exception("no session found") + + response = core_pb2.GetServiceDefaultsResponse() + for node_type in session.services.default_services: + services = session.services.default_services[node_type] + service_defaults = response.defaults.add() + service_defaults.node_type = node_type + service_defaults.services.extend(services) + return response + + def SetServiceDefaults(self, request, context): + session = self.coreemu.sessions.get(request.session) + if not session: + raise Exception("no session found") + + session.services.default_services.clear() + for service_defaults in request.defaults: + session.services.default_services[service_defaults.node_type] = service_defaults.services + + response = core_pb2.SetServiceDefaultsResponse() + response.result = True + return response + + def GetNodeService(self, request, context): + session = self.coreemu.sessions.get(request.session) + if not session: + raise Exception("no session found") + node = session.get_object(request.id) + if not node: + raise Exception("no node found") + + service = session.services.get_service(node.objid, request.service, default_service=True) + response = core_pb2.GetNodeServiceResponse() + response.service.executables.extend(service.executables) + response.service.dependencies.extend(service.dependencies) + response.service.dirs.extend(service.dirs) + response.service.configs.extend(service.configs) + response.service.startup.extend(service.startup) + response.service.validate.extend(service.validate) + response.service.validation_mode = service.validation_mode.value + response.service.validation_timer = service.validation_timer + response.service.shutdown.extend(service.shutdown) + if service.meta: + response.service.meta = service.meta + return response + def GetEmaneConfig(self, request, context): session = self.coreemu.sessions.get(request.session) if not session: diff --git a/daemon/proto/core.proto b/daemon/proto/core.proto index e62a6590..bdf6e60f 100644 --- a/daemon/proto/core.proto +++ b/daemon/proto/core.proto @@ -37,6 +37,12 @@ service CoreApi { } rpc GetServices (GetServicesRequest) returns (GetServicesResponse) { } + rpc GetServiceDefaults (GetServiceDefaultsRequest) returns (GetServiceDefaultsResponse) { + } + rpc SetServiceDefaults (SetServiceDefaultsRequest) returns (SetServiceDefaultsResponse) { + } + rpc GetNodeService (GetNodeServiceRequest) returns (GetNodeServiceResponse) { + } rpc GetEmaneConfig (GetEmaneConfigRequest) returns (GetEmaneConfigResponse) { } rpc SaveXml (SaveXmlRequest) returns (SaveXmlResponse) { @@ -212,6 +218,33 @@ message GetServicesResponse { repeated Service services = 1; } +message GetServiceDefaultsRequest { + int32 session = 1; +} + +message GetServiceDefaultsResponse { + repeated ServiceDefaults defaults = 1; +} + +message SetServiceDefaultsRequest { + int32 session = 1; + repeated ServiceDefaults defaults = 2; +} + +message SetServiceDefaultsResponse { + bool result = 1; +} + +message GetNodeServiceRequest { + int32 session = 1; + int32 id = 2; + string service = 3; +} + +message GetNodeServiceResponse { + NodeServiceData service = 1; +} + message GetEmaneConfigRequest { int32 session = 1; } @@ -265,11 +298,35 @@ enum NodeType { EMANE_NET = 14; } +enum ServiceValidationMode { + BLOCKING = 0; + NON_BLOCKING = 1; + TIMER = 2; +} + +message ServiceDefaults { + string node_type = 1; + repeated string services = 2; +} + message Service { string group = 1; string name = 2; } +message NodeServiceData { + repeated string executables = 1; + repeated string dependencies = 2; + repeated string dirs = 3; + repeated string configs = 4; + repeated string startup = 5; + repeated string validate = 6; + ServiceValidationMode validation_mode = 7; + int32 validation_timer = 8; + repeated string shutdown = 9; + string meta = 10; +} + message ConfigGroup { string name = 1; repeated ConfigOption options = 2; From 641427671c08e2a26a78840e39daa2fe6b6c6cc3 Mon Sep 17 00:00:00 2001 From: bharnden Date: Fri, 15 Mar 2019 21:38:29 -0700 Subject: [PATCH 14/72] grpc added get emane models, model config, and model configs --- daemon/core/grpc/client.py | 25 ++++++++++++++++- daemon/core/grpc/server.py | 55 +++++++++++++++++++++++++++++++++++++- daemon/proto/core.proto | 37 +++++++++++++++++++++++++ 3 files changed, 115 insertions(+), 2 deletions(-) diff --git a/daemon/core/grpc/client.py b/daemon/core/grpc/client.py index 81e644d0..2bd3141b 100644 --- a/daemon/core/grpc/client.py +++ b/daemon/core/grpc/client.py @@ -244,6 +244,25 @@ class CoreApiClient(object): request.session = session return self.stub.GetEmaneConfig(request) + def get_emane_models(self, session): + request = core_pb2.GetEmaneModelsRequest() + request.session = session + return self.stub.GetEmaneModels(request) + + def get_emane_model_config(self, session, _id, model, interface_id=None): + request = core_pb2.GetEmaneModelConfigRequest() + request.session = session + if interface_id is not None: + _id = _id * 1000 + interface_id + request.id = _id + request.model = model + return self.stub.GetEmaneModelConfig(request) + + def get_emane_model_configs(self, session): + request = core_pb2.GetEmaneModelConfigsRequest() + request.session = session + return self.stub.GetEmaneModelConfigs(request) + def save_xml(self, session, file_path): request = core_pb2.SaveXmlRequest() request.session = session @@ -285,10 +304,12 @@ def main(): print("default services: %s" % client.get_service_defaults(session_data.id)) + print("emane models: {}".format(client.get_emane_models(session_data.id))) + response = client.get_sessions() print("core client received: %s" % response) - print("emane config: %s" % client.get_emane_config(session_data.id)) + print("emane config: {}".format(client.get_emane_config(session_data.id))) # set session location response = client.set_session_location( @@ -325,6 +346,8 @@ def main(): node_options.y = 5 print("edit node: %s" % client.edit_node(session_data.id, node_id, node_options)) print("get node: %s" % client.get_node(session_data.id, node_id)) + print("emane model config: {}".format( + client.get_emane_model_config(session_data.id, node_id, "emane_tdma"))) print("node service: %s" % client.get_node_service(session_data.id, node_id, "zebra")) diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index 42bd9599..83a00701 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -170,7 +170,9 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): session.location.setrefgeo(request.position.lat, request.position.lon, request.position.alt) session.location.refscale = request.scale - return core_pb2.SetSessionLocationResponse() + response = core_pb2.SetSessionLocationResponse() + response.result = True + return response def SetSessionState(self, request, context): response = core_pb2.SetSessionStateResponse() @@ -572,6 +574,57 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): response.groups.extend(groups) return response + def GetEmaneModels(self, request, context): + session = self.coreemu.sessions.get(request.session) + if not session: + raise Exception("no session found") + + models = [] + for model in session.emane.models.keys(): + if len(model.split("_")) != 2: + continue + models.append(model) + + response = core_pb2.GetEmaneModelsResponse() + response.models.extend(models) + return response + + def GetEmaneModelConfig(self, request, context): + session = self.coreemu.sessions.get(request.session) + if not session: + raise Exception("no session found") + node = session.get_object(request.id) + if not node: + raise Exception("no node found") + + model = session.emane.models[request.model] + config = session.emane.get_model_config(node.objid, request.model) + groups = get_config_groups(config, model) + response = core_pb2.GetEmaneModelConfigResponse() + response.groups.extend(groups) + return response + + def GetEmaneModelConfigs(self, request, context): + session = self.coreemu.sessions.get(request.session) + if not session: + raise Exception("no session found") + + response = core_pb2.GetEmaneModelConfigsResponse() + for node_id, model_config in session.emane.node_configurations.iteritems(): + if node_id == -1: + continue + + for model_name in model_config.iterkeys(): + model = session.emane.models[model_name] + config = session.emane.get_model_config(node_id, model_name) + config_groups = get_config_groups(config, model) + # node_configurations = response.setdefault(node_id, {}) + node_configurations = response.configs[node_id] + node_configurations.model = model_name + node_configurations.groups.extend(config_groups) + # node_configurations[model_name] = config_groups + return response + def SaveXml(self, request, context): session = self.coreemu.sessions.get(request.session) if not session: diff --git a/daemon/proto/core.proto b/daemon/proto/core.proto index bdf6e60f..07931da0 100644 --- a/daemon/proto/core.proto +++ b/daemon/proto/core.proto @@ -45,6 +45,12 @@ service CoreApi { } rpc GetEmaneConfig (GetEmaneConfigRequest) returns (GetEmaneConfigResponse) { } + rpc GetEmaneModels (GetEmaneModelsRequest) returns (GetEmaneModelsResponse) { + } + rpc GetEmaneModelConfig (GetEmaneModelConfigRequest) returns (GetEmaneModelConfigResponse) { + } + rpc GetEmaneModelConfigs (GetEmaneModelConfigsRequest) returns (GetEmaneModelConfigsResponse) { + } rpc SaveXml (SaveXmlRequest) returns (SaveXmlResponse) { } rpc OpenXml (OpenXmlRequest) returns (OpenXmlResponse) { @@ -110,6 +116,7 @@ message SetSessionLocationRequest { } message SetSessionLocationResponse { + bool result = 1; } message SetSessionStateRequest { @@ -253,6 +260,36 @@ message GetEmaneConfigResponse { repeated ConfigGroup groups = 1; } +message GetEmaneModelsRequest { + int32 session = 1; +} + +message GetEmaneModelsResponse { + repeated string models = 1; +} + +message GetEmaneModelConfigRequest { + int32 session = 1; + int32 id = 2; + string model = 3; +} + +message GetEmaneModelConfigResponse { + repeated ConfigGroup groups = 1; +} + +message GetEmaneModelConfigsRequest { + int32 session = 1; +} + +message GetEmaneModelConfigsResponse { + message ModelConfig { + string model = 1; + repeated ConfigGroup groups = 2; + } + map configs = 1; +} + message SaveXmlRequest { int32 session = 1; } From 8559b425eea3e0cd655ea4bf529f3302aaf87519 Mon Sep 17 00:00:00 2001 From: bharnden Date: Fri, 15 Mar 2019 22:03:37 -0700 Subject: [PATCH 15/72] grpc added set emane and emane model configs --- daemon/core/grpc/client.py | 59 ++++++++++++++++++++++++-------------- daemon/core/grpc/server.py | 20 +++++++++++++ daemon/proto/core.proto | 24 ++++++++++++++++ 3 files changed, 82 insertions(+), 21 deletions(-) diff --git a/daemon/core/grpc/client.py b/daemon/core/grpc/client.py index 2bd3141b..33a4134f 100644 --- a/daemon/core/grpc/client.py +++ b/daemon/core/grpc/client.py @@ -244,6 +244,12 @@ class CoreApiClient(object): request.session = session return self.stub.GetEmaneConfig(request) + def set_emane_config(self, session, config): + request = core_pb2.SetEmaneConfigRequest() + request.session = session + request.config.update(config) + return self.stub.SetEmaneConfig(request) + def get_emane_models(self, session): request = core_pb2.GetEmaneModelsRequest() request.session = session @@ -258,6 +264,16 @@ class CoreApiClient(object): request.model = model return self.stub.GetEmaneModelConfig(request) + def set_emane_model_config(self, session, _id, model, config, interface_id=None): + request = core_pb2.SetEmaneModelConfigRequest() + request.session = session + if interface_id is not None: + _id = _id * 1000 + interface_id + request.id = _id + request.model = model + request.config.update(config) + return self.stub.SetEmaneModelConfig(request) + def get_emane_model_configs(self, session): request = core_pb2.GetEmaneModelConfigsRequest() request.session = session @@ -294,21 +310,22 @@ def main(): client = CoreApiClient() with client.connect(): if os.path.exists(xml_file_name): - print("open xml: %s" % client.open_xml(xml_file_name)) + print("open xml: {}".format(client.open_xml(xml_file_name))) - print("services: %s" % client.get_services()) + print("services: {}".format(client.get_services())) # create session session_data = client.create_session() - print("created session: %s" % session_data) + print("created session: {}".format(session_data)) - print("default services: %s" % client.get_service_defaults(session_data.id)) + print("default services: {}".format(client.get_service_defaults(session_data.id))) print("emane models: {}".format(client.get_emane_models(session_data.id))) response = client.get_sessions() - print("core client received: %s" % response) + print("core client received: {}".format(response)) + print("set emane config: {}".format(client.set_emane_config(session_data.id, {"otamanagerttl": "2"}))) print("emane config: {}".format(client.get_emane_config(session_data.id))) # set session location @@ -318,20 +335,20 @@ def main(): lat=47.57917, lon=-122.13232, alt=3.0, scale=150000.0 ) - print("set location response: %s" % response) + print("set location response: {}".format(response)) # get options - print("get options: %s" % client.get_session_options(session_data.id)) + print("get options: {}".format(client.get_session_options(session_data.id))) # get location - print("get location: %s" % client.get_session_location(session_data.id)) + print("get location: {}".format(client.get_session_location(session_data.id))) # change session state - print("set session state: %s" % client.set_session_state(session_data.id, EventTypes.CONFIGURATION_STATE)) + print("set session state: {}".format(client.set_session_state(session_data.id, EventTypes.CONFIGURATION_STATE))) # create switch node response = client.create_node(session_data.id, _type=NodeTypes.SWITCH) - print("created switch: %s" % response) + print("created switch: {}".format(response)) switch_id = response.id # ip generator for example @@ -339,17 +356,17 @@ def main(): for i in xrange(2): response = client.create_node(session_data.id) - print("created node: %s" % response) + print("created node: {}".format(response)) node_id = response.id node_options = NodeOptions() node_options.x = 5 node_options.y = 5 - print("edit node: %s" % client.edit_node(session_data.id, node_id, node_options)) - print("get node: %s" % client.get_node(session_data.id, node_id)) + print("edit node: {}".format(client.edit_node(session_data.id, node_id, node_options))) + print("get node: {}".format(client.get_node(session_data.id, node_id))) print("emane model config: {}".format( client.get_emane_model_config(session_data.id, node_id, "emane_tdma"))) - print("node service: %s" % client.get_node_service(session_data.id, node_id, "zebra")) + print("node service: {}".format(client.get_node_service(session_data.id, node_id, "zebra"))) # create link interface_one = InterfaceData( @@ -357,26 +374,26 @@ def main(): ip4=str(prefixes.ip4.addr(node_id)), ip4_mask=prefixes.ip4.prefixlen, ip6=None, ip6_mask=None ) - print("created link: %s" % client.create_link(session_data.id, node_id, switch_id, interface_one)) + print("created link: {}".format(client.create_link(session_data.id, node_id, switch_id, interface_one))) link_options = LinkOptions() link_options.per = 50 - print("edit link: %s" % client.edit_link( - session_data.id, node_id, switch_id, link_options, interface_one=0)) + print("edit link: {}".format(client.edit_link( + session_data.id, node_id, switch_id, link_options, interface_one=0))) - print("get node links: %s" % client.get_node_links(session_data.id, node_id)) + print("get node links: {}".format(client.get_node_links(session_data.id, node_id))) # change session state - print("set session state: %s" % client.set_session_state(session_data.id, EventTypes.INSTANTIATION_STATE)) + print("set session state: {}".format(client.set_session_state(session_data.id, EventTypes.INSTANTIATION_STATE))) # import pdb; pdb.set_trace() # get session - print("get session: %s" % client.get_session(session_data.id)) + print("get session: {}".format(client.get_session(session_data.id))) # save xml client.save_xml(session_data.id, xml_file_name) # delete session - print("delete session: %s" % client.delete_session(session_data.id)) + print("delete session: {}".format(client.delete_session(session_data.id))) if __name__ == "__main__": diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index 83a00701..817b627c 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -574,6 +574,16 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): response.groups.extend(groups) return response + def SetEmaneConfig(self, request, context): + session = self.coreemu.sessions.get(request.session) + if not session: + raise Exception("no session found") + + session.emane.set_configs(request.config) + response = core_pb2.SetEmaneConfigResponse() + response.result = True + return response + def GetEmaneModels(self, request, context): session = self.coreemu.sessions.get(request.session) if not session: @@ -604,6 +614,16 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): response.groups.extend(groups) return response + def SetEmaneModelConfig(self, request, context): + session = self.coreemu.sessions.get(request.session) + if not session: + raise Exception("no session found") + + session.emane.set_model_config(request.id, request.model, request.config) + response = core_pb2.SetEmaneModelConfigResponse() + response.result = True + return response + def GetEmaneModelConfigs(self, request, context): session = self.coreemu.sessions.get(request.session) if not session: diff --git a/daemon/proto/core.proto b/daemon/proto/core.proto index 07931da0..506e0b40 100644 --- a/daemon/proto/core.proto +++ b/daemon/proto/core.proto @@ -45,10 +45,14 @@ service CoreApi { } rpc GetEmaneConfig (GetEmaneConfigRequest) returns (GetEmaneConfigResponse) { } + rpc SetEmaneConfig (SetEmaneConfigRequest) returns (SetEmaneConfigResponse) { + } rpc GetEmaneModels (GetEmaneModelsRequest) returns (GetEmaneModelsResponse) { } rpc GetEmaneModelConfig (GetEmaneModelConfigRequest) returns (GetEmaneModelConfigResponse) { } + rpc SetEmaneModelConfig (SetEmaneModelConfigRequest) returns (SetEmaneModelConfigResponse) { + } rpc GetEmaneModelConfigs (GetEmaneModelConfigsRequest) returns (GetEmaneModelConfigsResponse) { } rpc SaveXml (SaveXmlRequest) returns (SaveXmlResponse) { @@ -260,6 +264,15 @@ message GetEmaneConfigResponse { repeated ConfigGroup groups = 1; } +message SetEmaneConfigRequest { + int32 session = 1; + map config = 2; +} + +message SetEmaneConfigResponse { + bool result = 1; +} + message GetEmaneModelsRequest { int32 session = 1; } @@ -278,6 +291,17 @@ message GetEmaneModelConfigResponse { repeated ConfigGroup groups = 1; } +message SetEmaneModelConfigRequest { + int32 session = 1; + int32 id = 2; + string model = 3; + map config = 4; +} + +message SetEmaneModelConfigResponse { + bool result = 1; +} + message GetEmaneModelConfigsRequest { int32 session = 1; } From 5d72b6af264ca993a7015567285fbc05f075d95a Mon Sep 17 00:00:00 2001 From: bharnden Date: Sat, 16 Mar 2019 12:33:24 -0700 Subject: [PATCH 16/72] grpc added get hooks and add hook --- daemon/core/grpc/client.py | 17 ++++++++++++++-- daemon/core/grpc/server.py | 26 +++++++++++++++++++++++++ daemon/proto/core.proto | 40 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 2 deletions(-) diff --git a/daemon/core/grpc/client.py b/daemon/core/grpc/client.py index 33a4134f..2a856261 100644 --- a/daemon/core/grpc/client.py +++ b/daemon/core/grpc/client.py @@ -212,6 +212,19 @@ class CoreApiClient(object): ) return self.stub.DeleteLink(request) + def get_hooks(self, session): + request = core_pb2.GetHooksRequest() + request.session = session + return self.stub.GetHooks(request) + + def add_hook(self, session, state, file_name, file_data): + request = core_pb2.AddHookRequest() + request.session = session + request.hook.state = state.value + request.hook.file = file_name + request.hook.data = file_data + return self.stub.AddHook(request) + def get_services(self): request = core_pb2.GetServicesRequest() return self.stub.GetServices(request) @@ -317,10 +330,10 @@ def main(): # create session session_data = client.create_session() print("created session: {}".format(session_data)) - print("default services: {}".format(client.get_service_defaults(session_data.id))) - print("emane models: {}".format(client.get_emane_models(session_data.id))) + print("add hook: {}".format(client.add_hook(session_data.id, EventTypes.RUNTIME_STATE, "test", "echo hello"))) + print("hooks: {}".format(client.get_hooks(session_data.id))) response = client.get_sessions() print("core client received: {}".format(response)) diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index 817b627c..33db3a7d 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -507,6 +507,32 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): response.result = True return response + def GetHooks(self, request, context): + session = self.coreemu.sessions.get(request.session) + if not session: + raise Exception("no session found") + + response = core_pb2.GetHooksResponse() + for state, state_hooks in session._hooks.iteritems(): + for file_name, file_data in state_hooks: + hook = response.hooks.add() + hook.state = state + hook.file = file_name + hook.data = file_data + + return response + + def AddHook(self, request, context): + session = self.coreemu.sessions.get(request.session) + if not session: + raise Exception("no session found") + + hook = request.hook + session.add_hook(hook.state, hook.file, None, hook.data) + response = core_pb2.AddHookResponse() + response.result = True + return response + def GetServices(self, request, context): response = core_pb2.GetServicesResponse() for service in ServiceManager.services.itervalues(): diff --git a/daemon/proto/core.proto b/daemon/proto/core.proto index 506e0b40..3bcce8c7 100644 --- a/daemon/proto/core.proto +++ b/daemon/proto/core.proto @@ -3,6 +3,7 @@ syntax = "proto3"; package core; service CoreApi { + // session rpc rpc CreateSession (CreateSessionRequest) returns (CreateSessionResponse) { } rpc DeleteSession (DeleteSessionRequest) returns (DeleteSessionResponse) { @@ -19,6 +20,8 @@ service CoreApi { } rpc SetSessionState (SetSessionStateRequest) returns (SetSessionStateResponse) { } + + // node rpc rpc CreateNode (CreateNodeRequest) returns (CreateNodeResponse) { } rpc GetNode (GetNodeRequest) returns (GetNodeResponse) { @@ -27,6 +30,8 @@ service CoreApi { } rpc DeleteNode (DeleteNodeRequest) returns (DeleteNodeResponse) { } + + // link rpc rpc GetNodeLinks (GetNodeLinksRequest) returns (GetNodeLinksResponse) { } rpc CreateLink (CreateLinkRequest) returns (CreateLinkResponse) { @@ -35,6 +40,14 @@ service CoreApi { } rpc DeleteLink (DeleteLinkRequest) returns (DeleteLinkResponse) { } + + // hook rpc + rpc GetHooks (GetHooksRequest) returns (GetHooksResponse) { + } + rpc AddHook (AddHookRequest) returns (AddHookResponse) { + } + + // service rpc rpc GetServices (GetServicesRequest) returns (GetServicesResponse) { } rpc GetServiceDefaults (GetServiceDefaultsRequest) returns (GetServiceDefaultsResponse) { @@ -43,6 +56,8 @@ service CoreApi { } rpc GetNodeService (GetNodeServiceRequest) returns (GetNodeServiceResponse) { } + + // emane rpc rpc GetEmaneConfig (GetEmaneConfigRequest) returns (GetEmaneConfigResponse) { } rpc SetEmaneConfig (SetEmaneConfigRequest) returns (SetEmaneConfigResponse) { @@ -55,6 +70,8 @@ service CoreApi { } rpc GetEmaneModelConfigs (GetEmaneModelConfigsRequest) returns (GetEmaneModelConfigsResponse) { } + + // xml rpc rpc SaveXml (SaveXmlRequest) returns (SaveXmlResponse) { } rpc OpenXml (OpenXmlRequest) returns (OpenXmlResponse) { @@ -221,6 +238,23 @@ message DeleteLinkResponse { bool result = 1; } +message GetHooksRequest { + int32 session = 1; +} + +message GetHooksResponse { + repeated Hook hooks = 1; +} + +message AddHookRequest { + int32 session = 1; + Hook hook = 2; +} + +message AddHookResponse { + bool result = 1; +} + message GetServicesRequest { } @@ -365,6 +399,12 @@ enum ServiceValidationMode { TIMER = 2; } +message Hook { + SessionState state = 1; + string file = 2; + bytes data = 3; +} + message ServiceDefaults { string node_type = 1; repeated string services = 2; From 9c973249e8eaace616fdcb8d33036a51d914a2a7 Mon Sep 17 00:00:00 2001 From: bharnden Date: Sat, 16 Mar 2019 12:48:37 -0700 Subject: [PATCH 17/72] grpc added get wlan and set wlan config --- daemon/core/grpc/client.py | 13 +++++++++++++ daemon/core/grpc/server.py | 28 ++++++++++++++++++++++++++++ daemon/proto/core.proto | 25 +++++++++++++++++++++++++ 3 files changed, 66 insertions(+) diff --git a/daemon/core/grpc/client.py b/daemon/core/grpc/client.py index 2a856261..2e4bfda9 100644 --- a/daemon/core/grpc/client.py +++ b/daemon/core/grpc/client.py @@ -252,6 +252,19 @@ class CoreApiClient(object): request.service = service return self.stub.GetNodeService(request) + def get_wlan_config(self, session, _id): + request = core_pb2.GetWlanConfigRequest() + request.session = session + request.id = _id + return self.stub.GetWlanConfig(request) + + def set_wlan_config(self, session, _id, config): + request = core_pb2.SetWlanConfigRequest() + request.session = session + request.id = _id + request.config.update(config) + return self.stub.SetWlanConfig(request) + def get_emane_config(self, session): request = core_pb2.GetEmaneConfigRequest() request.session = session diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index 33db3a7d..65cd0c6a 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -13,6 +13,7 @@ import grpc import core_pb2 import core_pb2_grpc from core.misc import nodeutils +from core.mobility import BasicRangeModel from core.service import ServiceManager _ONE_DAY_IN_SECONDS = 60 * 60 * 24 @@ -590,6 +591,33 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): response.service.meta = service.meta return response + def GetWlanConfig(self, request, context): + session = self.coreemu.sessions.get(request.session) + if not session: + raise Exception("no session found") + node = session.get_object(request.id) + if not node: + raise Exception("no node found") + + config = session.mobility.get_model_config(node.objid, BasicRangeModel.name) + groups = get_config_groups(config, BasicRangeModel) + response = core_pb2.GetWlanConfigResponse() + response.groups.extend(groups) + return response + + def SetWlanConfig(self, request, context): + session = self.coreemu.sessions.get(request.session) + if not session: + raise Exception("no session found") + node = session.get_object(request.id) + if not node: + raise Exception("no node found") + + session.mobility.set_model_config(node.objid, BasicRangeModel.name, request.config) + response = core_pb2.SetWlanConfigResponse() + response.result = True + return response + def GetEmaneConfig(self, request, context): session = self.coreemu.sessions.get(request.session) if not session: diff --git a/daemon/proto/core.proto b/daemon/proto/core.proto index 3bcce8c7..9fccd63a 100644 --- a/daemon/proto/core.proto +++ b/daemon/proto/core.proto @@ -57,6 +57,12 @@ service CoreApi { rpc GetNodeService (GetNodeServiceRequest) returns (GetNodeServiceResponse) { } + // wlan rpc + rpc GetWlanConfig (GetWlanConfigRequest) returns (GetWlanConfigResponse) { + } + rpc SetWlanConfig (SetWlanConfigRequest) returns (SetWlanConfigResponse) { + } + // emane rpc rpc GetEmaneConfig (GetEmaneConfigRequest) returns (GetEmaneConfigResponse) { } @@ -290,6 +296,25 @@ message GetNodeServiceResponse { NodeServiceData service = 1; } +message GetWlanConfigRequest { + int32 session = 1; + int32 id = 2; +} + +message GetWlanConfigResponse { + repeated ConfigGroup groups = 1; +} + +message SetWlanConfigRequest { + int32 session = 1; + int32 id = 2; + map config = 3; +} + +message SetWlanConfigResponse { + bool result = 1; +} + message GetEmaneConfigRequest { int32 session = 1; } From d9ae7d5c34f5b894f5f54894e95d0886cfad4651 Mon Sep 17 00:00:00 2001 From: bharnden Date: Sat, 16 Mar 2019 12:57:59 -0700 Subject: [PATCH 18/72] grpc added standard connect/close along with context connect --- daemon/core/grpc/client.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/daemon/core/grpc/client.py b/daemon/core/grpc/client.py index 2e4bfda9..e9047809 100644 --- a/daemon/core/grpc/client.py +++ b/daemon/core/grpc/client.py @@ -23,6 +23,7 @@ class CoreApiClient(object): def __init__(self, address="localhost:50051"): self.address = address self.stub = None + self.channel = None def create_session(self): return self.stub.CreateSession(core_pb2.CreateSessionRequest()) @@ -320,21 +321,29 @@ class CoreApiClient(object): request.data = data return self.stub.OpenXml(request) - @contextmanager def connect(self): - channel = grpc.insecure_channel(self.address) + self.channel = grpc.insecure_channel(self.address) + self.stub = core_pb2_grpc.CoreApiStub(self.channel) + + def close(self): + if self.channel: + self.channel.close() + self.channel = None + + @contextmanager + def context_connect(self): try: - self.stub = core_pb2_grpc.CoreApiStub(channel) - yield channel + self.connect() + yield finally: - channel.close() + self.close() def main(): xml_file_name = "/tmp/core.xml" client = CoreApiClient() - with client.connect(): + with client.context_connect(): if os.path.exists(xml_file_name): print("open xml: {}".format(client.open_xml(xml_file_name))) From f24376d66c7b5534f2a5f779331413999bc7840e Mon Sep 17 00:00:00 2001 From: bharnden Date: Sat, 16 Mar 2019 13:48:25 -0700 Subject: [PATCH 19/72] grpc added node/session event streaming requests --- daemon/core/grpc/client.py | 25 ++++++++++++++ daemon/core/grpc/server.py | 70 ++++++++++++++++++++++++++++++-------- daemon/proto/core.proto | 27 +++++++++++++++ 3 files changed, 108 insertions(+), 14 deletions(-) diff --git a/daemon/core/grpc/client.py b/daemon/core/grpc/client.py index e9047809..a33347c1 100644 --- a/daemon/core/grpc/client.py +++ b/daemon/core/grpc/client.py @@ -1,6 +1,7 @@ from __future__ import print_function import logging import os +import threading from contextlib import contextmanager import grpc @@ -64,6 +65,30 @@ class CoreApiClient(object): request.state = state.value return self.stub.SetSessionState(request) + def node_events(self, _id, handler): + request = core_pb2.NodeEventsRequest() + request.id = _id + + def listen(): + for event in self.stub.NodeEvents(request): + handler(event) + + thread = threading.Thread(target=listen) + thread.daemon = True + thread.start() + + def session_events(self, _id, handler): + request = core_pb2.SessionEventsRequest() + request.id = _id + + def listen(): + for event in self.stub.SessionEvents(request): + handler(event) + + thread = threading.Thread(target=listen) + thread.daemon = True + thread.start() + def create_node(self, session, _type=NodeTypes.DEFAULT, _id=None, node_options=None, emane=None): if not node_options: node_options = NodeOptions() diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index 65cd0c6a..79f00e00 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -1,17 +1,16 @@ +import logging import os import tempfile - -from core.emulator.emudata import NodeOptions, InterfaceData, LinkOptions -from core.enumerations import NodeTypes, EventTypes, LinkTypes - -from concurrent import futures import time -import logging import grpc +from concurrent import futures +from Queue import Queue import core_pb2 import core_pb2_grpc +from core.emulator.emudata import NodeOptions, InterfaceData, LinkOptions +from core.enumerations import NodeTypes, EventTypes, LinkTypes from core.misc import nodeutils from core.mobility import BasicRangeModel from core.service import ServiceManager @@ -119,14 +118,6 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): session.location.setrefgeo(47.57917, -122.13232, 2.0) session.location.refscale = 150000.0 - # grpc stream handlers - # session.event_handlers.append(websocket_routes.broadcast_event) - # session.node_handlers.append(websocket_routes.broadcast_node) - # session.config_handlers.append(websocket_routes.broadcast_config) - # session.link_handlers.append(websocket_routes.broadcast_link) - # session.exception_handlers.append(websocket_routes.broadcast_exception) - # session.file_handlers.append(websocket_routes.broadcast_file) - response = core_pb2.CreateSessionResponse() response.id = session.session_id response.state = session.state @@ -262,6 +253,57 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response + def NodeEvents(self, request, context): + session = self.coreemu.sessions.get(request.id) + if not session: + raise Exception("no session found") + queue = Queue() + + session.node_handlers.append(lambda x: queue.put(x)) + + while True: + node = queue.get() + node_event = core_pb2.NodeEvent() + update_proto( + node_event.node, + id=node.id, + name=node.name, + model=node.model + ) + update_proto( + node_event.node.position, + x=node.x_position, + y=node.y_position + ) + services = node.services or "" + node_event.node.services.extend(services.split("|")) + yield node_event + + def SessionEvents(self, request, context): + session = self.coreemu.sessions.get(request.id) + if not session: + raise Exception("no session found") + queue = Queue() + + session.event_handlers.append(lambda x: queue.put(x)) + + while True: + event = queue.get() + session_event = core_pb2.SessionEvent() + event_time = event.time + if event_time is not None: + event_time = float(event_time) + update_proto( + session_event, + node=event.node, + event=event.event_type, + name=event.name, + data=event.data, + time=event_time, + session=session.session_id + ) + yield session_event + def CreateNode(self, request, context): session = self.coreemu.sessions.get(request.session) if not session: diff --git a/daemon/proto/core.proto b/daemon/proto/core.proto index 9fccd63a..384e18ec 100644 --- a/daemon/proto/core.proto +++ b/daemon/proto/core.proto @@ -21,6 +21,12 @@ service CoreApi { rpc SetSessionState (SetSessionStateRequest) returns (SetSessionStateResponse) { } + // event streams + rpc NodeEvents (NodeEventsRequest) returns (stream NodeEvent) { + } + rpc SessionEvents (SessionEventsRequest) returns (stream SessionEvent) { + } + // node rpc rpc CreateNode (CreateNodeRequest) returns (CreateNodeResponse) { } @@ -155,6 +161,27 @@ message SetSessionStateResponse { bool result = 1; } +message NodeEventsRequest { + int32 id = 1; +} + +message NodeEvent { + Node node = 1; +} + +message SessionEventsRequest { + int32 id = 1; +} + +message SessionEvent { + int32 node = 1; + int32 event = 2; + string name = 3; + bytes data = 4; + float time = 5; + int32 session = 6; +} + message CreateNodeRequest { int32 session = 1; int32 id = 2; From a62a03e6b9a04629b94c4ff2e35ea4391300ab5e Mon Sep 17 00:00:00 2001 From: bharnden Date: Sat, 16 Mar 2019 20:53:26 -0700 Subject: [PATCH 20/72] grpc added node service file and service action --- daemon/core/grpc/client.py | 16 +++++++++++ daemon/core/grpc/server.py | 58 ++++++++++++++++++++++++++++++++++++++ daemon/proto/core.proto | 33 ++++++++++++++++++++++ 3 files changed, 107 insertions(+) diff --git a/daemon/core/grpc/client.py b/daemon/core/grpc/client.py index a33347c1..975699d5 100644 --- a/daemon/core/grpc/client.py +++ b/daemon/core/grpc/client.py @@ -278,6 +278,22 @@ class CoreApiClient(object): request.service = service return self.stub.GetNodeService(request) + def get_node_service_file(self, session, _id, service, file_name): + request = core_pb2.GetNodeServiceFileRequest() + request.session = session + request.id = _id + request.service = service + request.file = file_name + return self.stub.GetNodeServiceFile(request) + + def service_action(self, session, _id, service, action): + request = core_pb2.ServiceActionRequest() + request.session = session + request.id = _id + request.service = service + request.action = action + return self.stub.ServiceAction(request) + def get_wlan_config(self, session, _id): request = core_pb2.GetWlanConfigRequest() request.session = session diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index 79f00e00..ab44a446 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -633,6 +633,64 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): response.service.meta = service.meta return response + def GetNodeServiceFile(self, request, context): + session = self.coreemu.sessions.get(request.session) + if not session: + raise Exception("no session found") + node = session.get_object(request.id) + if not node: + raise Exception("no node found") + + service = None + for current_service in node.services: + if current_service.name == request.service: + service = current_service + break + + response = core_pb2.GetNodeServiceFileResponse() + if not service: + return response + file_data = session.services.get_service_file(node, request.service, request.file) + response.data = file_data.data + return response + + def ServiceAction(self, request, context): + session = self.coreemu.sessions.get(request.session) + if not session: + raise Exception("no session found") + node = session.get_object(request.id) + if not node: + raise Exception("no node found") + + service = None + for current_service in node.services: + if current_service.name == request.service: + service = current_service + break + + response = core_pb2.ServiceActionResponse() + response.result = False + + if not service: + return response + + status = -1 + if request.action == core_pb2.START: + status = session.services.startup_service(node, service, wait=True) + elif request.action == core_pb2.STOP: + status = session.services.stop_service(node, service) + elif request.action == core_pb2.RESTART: + status = session.services.stop_service(node, service) + if not status: + status = session.services.startup_service(node, service, wait=True) + elif request.action == core_pb2.VALIDATE: + status = session.services.validate_service(node, service) + + if not status: + response.result = True + + return response + def GetWlanConfig(self, request, context): session = self.coreemu.sessions.get(request.session) if not session: diff --git a/daemon/proto/core.proto b/daemon/proto/core.proto index 384e18ec..5b6be70a 100644 --- a/daemon/proto/core.proto +++ b/daemon/proto/core.proto @@ -62,6 +62,10 @@ service CoreApi { } rpc GetNodeService (GetNodeServiceRequest) returns (GetNodeServiceResponse) { } + rpc GetNodeServiceFile (GetNodeServiceFileRequest) returns (GetNodeServiceFileResponse) { + } + rpc ServiceAction (ServiceActionRequest) returns (ServiceActionResponse) { + } // wlan rpc rpc GetWlanConfig (GetWlanConfigRequest) returns (GetWlanConfigResponse) { @@ -323,6 +327,28 @@ message GetNodeServiceResponse { NodeServiceData service = 1; } +message GetNodeServiceFileRequest { + int32 session = 1; + int32 id = 2; + string service = 3; + string file = 4; +} + +message GetNodeServiceFileResponse { + bytes data = 1; +} + +message ServiceActionRequest { + int32 session = 1; + int32 id = 2; + string service = 3; + ServiceAction action = 4; +} + +message ServiceActionResponse { + bool result = 1; +} + message GetWlanConfigRequest { int32 session = 1; int32 id = 2; @@ -451,6 +477,13 @@ enum ServiceValidationMode { TIMER = 2; } +enum ServiceAction { + START = 0; + STOP = 1; + RESTART = 2; + VALIDATE = 3; +} + message Hook { SessionState state = 1; string file = 2; From 9185c6b8eb0a5e8a1f470ae897d5af34ee53d351 Mon Sep 17 00:00:00 2001 From: bharnden Date: Sat, 16 Mar 2019 21:40:15 -0700 Subject: [PATCH 21/72] grpc added set node service, set node service file, set session options --- daemon/core/grpc/client.py | 31 ++++++++++++++++-- daemon/core/grpc/server.py | 66 +++++++++++++++++++++++++++----------- daemon/proto/core.proto | 58 +++++++++++++++++++++++++++------ 3 files changed, 124 insertions(+), 31 deletions(-) diff --git a/daemon/core/grpc/client.py b/daemon/core/grpc/client.py index 975699d5..95ddd41e 100644 --- a/daemon/core/grpc/client.py +++ b/daemon/core/grpc/client.py @@ -35,18 +35,24 @@ class CoreApiClient(object): return self.stub.DeleteSession(request) def get_sessions(self): - return self.stub.GetSessions(core_pb2.SessionsRequest()) + return self.stub.GetSessions(core_pb2.GetSessionsRequest()) def get_session(self, _id): - request = core_pb2.SessionRequest() + request = core_pb2.GetSessionRequest() request.id = _id return self.stub.GetSession(request) def get_session_options(self, _id): - request = core_pb2.SessionOptionsRequest() + request = core_pb2.GetSessionOptionsRequest() request.id = _id return self.stub.GetSessionOptions(request) + def set_session_options(self, _id, config): + request = core_pb2.SetSessionOptionsRequest() + request.id = _id + request.config.update(config) + return self.stub.SetSessionOptions(request) + def get_session_location(self, _id): request = core_pb2.GetSessionLocationRequest() request.id = _id @@ -286,6 +292,25 @@ class CoreApiClient(object): request.file = file_name return self.stub.GetNodeServiceFile(request) + def set_node_service(self, session, _id, service, startup, validate, shutdown): + request = core_pb2.SetNodeServiceRequest() + request.session = session + request.id = _id + request.service = service + request.startup.extend(startup) + request.validate.extend(validate) + request.shutdown.extend(shutdown) + return self.stub.SetNodeService(request) + + def set_node_service_file(self, session, _id, service, file_name, data): + request = core_pb2.SetNodeServiceFileRequest() + request.session = session + request.id = _id + request.service = service + request.file = file_name + request.data = data + return self.stub.SetNodeServiceFile(request) + def service_action(self, session, _id, service, action): request = core_pb2.ServiceActionRequest() request.session = session diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index ab44a446..ac93963a 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -129,7 +129,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def GetSessions(self, request, context): - response = core_pb2.SessionsResponse() + response = core_pb2.GetSessionsResponse() for session_id in self.coreemu.sessions: session = self.coreemu.sessions[session_id] session_data = response.sessions.add() @@ -201,16 +201,26 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): groups = get_config_groups(defaults, session.options) - response = core_pb2.SessionOptionsResponse() + response = core_pb2.GetSessionOptionsResponse() response.groups.extend(groups) return response + def SetSessionOptions(self, request, context): + session = self.coreemu.sessions.get(request.id) + if not session: + raise Exception("no session found") + + session.options.set_configs(request.config) + response = core_pb2.SetSessionOptionsResponse() + response.result = True + return response + def GetSession(self, request, context): session = self.coreemu.sessions.get(request.id) if not session: raise Exception("no session found") - response = core_pb2.SessionResponse() + response = core_pb2.GetSessionResponse() response.state = session.state for node_id in session.objects: @@ -614,11 +624,8 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): session = self.coreemu.sessions.get(request.session) if not session: raise Exception("no session found") - node = session.get_object(request.id) - if not node: - raise Exception("no node found") - service = session.services.get_service(node.objid, request.service, default_service=True) + service = session.services.get_service(request.id, request.service, default_service=True) response = core_pb2.GetNodeServiceResponse() response.service.executables.extend(service.executables) response.service.dependencies.extend(service.dependencies) @@ -654,6 +661,36 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): response.data = file_data.data return response + def SetNodeService(self, request, context): + session = self.coreemu.sessions.get(request.session) + if not session: + raise Exception("no session found") + + # guarantee custom service exists + session.services.set_service(request.id, request.service) + service = session.services.get_service(request.id, request.service) + service.startup = tuple(request.startup) + logging.info("custom startup: %s", service.startup) + service.validate = tuple(request.validate) + logging.info("custom validate: %s", service.validate) + service.shutdown = tuple(request.shutdown) + logging.info("custom shutdown: %s", service.shutdown) + + response = core_pb2.SetNodeServiceResponse() + response.result = True + return response + + def SetNodeServiceFile(self, request, context): + session = self.coreemu.sessions.get(request.session) + if not session: + raise Exception("no session found") + + session.services.set_service_file(request.id, request.service, request.file, request.data) + + response = core_pb2.SetNodeServiceFileResponse() + response.result = True + return response + def ServiceAction(self, request, context): session = self.coreemu.sessions.get(request.session) if not session: @@ -695,11 +732,8 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): session = self.coreemu.sessions.get(request.session) if not session: raise Exception("no session found") - node = session.get_object(request.id) - if not node: - raise Exception("no node found") - config = session.mobility.get_model_config(node.objid, BasicRangeModel.name) + config = session.mobility.get_model_config(request.id, BasicRangeModel.name) groups = get_config_groups(config, BasicRangeModel) response = core_pb2.GetWlanConfigResponse() response.groups.extend(groups) @@ -709,11 +743,8 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): session = self.coreemu.sessions.get(request.session) if not session: raise Exception("no session found") - node = session.get_object(request.id) - if not node: - raise Exception("no node found") - session.mobility.set_model_config(node.objid, BasicRangeModel.name, request.config) + session.mobility.set_model_config(request.id, BasicRangeModel.name, request.config) response = core_pb2.SetWlanConfigResponse() response.result = True return response @@ -757,12 +788,9 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): session = self.coreemu.sessions.get(request.session) if not session: raise Exception("no session found") - node = session.get_object(request.id) - if not node: - raise Exception("no node found") model = session.emane.models[request.model] - config = session.emane.get_model_config(node.objid, request.model) + config = session.emane.get_model_config(request.id, request.model) groups = get_config_groups(config, model) response = core_pb2.GetEmaneModelConfigResponse() response.groups.extend(groups) diff --git a/daemon/proto/core.proto b/daemon/proto/core.proto index 5b6be70a..258f5d38 100644 --- a/daemon/proto/core.proto +++ b/daemon/proto/core.proto @@ -8,11 +8,13 @@ service CoreApi { } rpc DeleteSession (DeleteSessionRequest) returns (DeleteSessionResponse) { } - rpc GetSessions (SessionsRequest) returns (SessionsResponse) { + rpc GetSessions (GetSessionsRequest) returns (GetSessionsResponse) { } - rpc GetSession (SessionRequest) returns (SessionResponse) { + rpc GetSession (GetSessionRequest) returns (GetSessionResponse) { } - rpc GetSessionOptions (SessionOptionsRequest) returns (SessionOptionsResponse) { + rpc GetSessionOptions (GetSessionOptionsRequest) returns (GetSessionOptionsResponse) { + } + rpc SetSessionOptions (SetSessionOptionsRequest) returns (SetSessionOptionsResponse) { } rpc GetSessionLocation (GetSessionLocationRequest) returns (GetSessionLocationResponse) { } @@ -64,6 +66,10 @@ service CoreApi { } rpc GetNodeServiceFile (GetNodeServiceFileRequest) returns (GetNodeServiceFileResponse) { } + rpc SetNodeService (SetNodeServiceRequest) returns (SetNodeServiceResponse) { + } + rpc SetNodeServiceFile (SetNodeServiceFileRequest) returns (SetNodeServiceFileResponse) { + } rpc ServiceAction (ServiceActionRequest) returns (ServiceActionResponse) { } @@ -112,31 +118,40 @@ message DeleteSessionResponse { bool result = 1; } -message SessionsRequest { +message GetSessionsRequest { } -message SessionsResponse { +message GetSessionsResponse { repeated Session sessions = 1; } -message SessionRequest { +message GetSessionRequest { int32 id = 1; } -message SessionResponse { +message GetSessionResponse { int32 state = 1; repeated Node nodes = 2; repeated Link links = 3; } -message SessionOptionsRequest { +message GetSessionOptionsRequest { int32 id = 1; } -message SessionOptionsResponse { +message GetSessionOptionsResponse { repeated ConfigGroup groups = 1; } +message SetSessionOptionsRequest { + int32 id = 1; + map config = 2; +} + +message SetSessionOptionsResponse { + bool result = 1; +} + message GetSessionLocationRequest { int32 id = 1; } @@ -338,6 +353,31 @@ message GetNodeServiceFileResponse { bytes data = 1; } +message SetNodeServiceRequest { + int32 session = 1; + int32 id = 2; + string service = 3; + repeated string startup = 4; + repeated string validate = 5; + repeated string shutdown = 6; +} + +message SetNodeServiceResponse { + bool result = 1; +} + +message SetNodeServiceFileRequest { + int32 session = 1; + int32 id = 2; + string service = 3; + string file = 4; + bytes data = 5; +} + +message SetNodeServiceFileResponse { + bool result = 1; +} + message ServiceActionRequest { int32 session = 1; int32 id = 2; From a07b0c891949827a5be43f2531a424216efd80ba Mon Sep 17 00:00:00 2001 From: bharnden Date: Sun, 17 Mar 2019 15:28:51 -0700 Subject: [PATCH 22/72] grpc added mobility get configs, get config, set config, and action --- daemon/core/grpc/client.py | 25 +++++ daemon/core/grpc/server.py | 225 +++++++++++++++++-------------------- daemon/proto/core.proto | 57 ++++++++++ 3 files changed, 183 insertions(+), 124 deletions(-) diff --git a/daemon/core/grpc/client.py b/daemon/core/grpc/client.py index 95ddd41e..43aeee3b 100644 --- a/daemon/core/grpc/client.py +++ b/daemon/core/grpc/client.py @@ -257,6 +257,31 @@ class CoreApiClient(object): request.hook.data = file_data return self.stub.AddHook(request) + def get_mobility_configs(self, session): + request = core_pb2.GetMobilityConfigsRequest() + request.session = session + return self.stub.GetMobilityConfigs(request) + + def get_mobility_config(self, session, _id): + request = core_pb2.GetMobilityConfigRequest() + request.session = session + request.id = _id + return self.stub.GetMobilityConfig(request) + + def set_mobility_config(self, session, _id, config): + request = core_pb2.SetMobilityConfigRequest() + request.session = session + request.id = _id + request.config.update(config) + return self.stub.SetMobilityConfig(request) + + def mobility_action(self, session, _id, action): + request = core_pb2.MobilityActionRequest() + request.session = session + request.id = _id + request.action = action + return self.stub.MobilityAction(request) + def get_services(self): request = core_pb2.GetServicesRequest() return self.stub.GetServices(request) diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index ac93963a..237dabcc 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -12,7 +12,7 @@ import core_pb2_grpc from core.emulator.emudata import NodeOptions, InterfaceData, LinkOptions from core.enumerations import NodeTypes, EventTypes, LinkTypes from core.misc import nodeutils -from core.mobility import BasicRangeModel +from core.mobility import BasicRangeModel, Ns2ScriptedMobility from core.service import ServiceManager _ONE_DAY_IN_SECONDS = 60 * 60 * 24 @@ -110,6 +110,18 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): super(CoreApiServer, self).__init__() self.coreemu = coreemu + def get_session(self, _id): + session = self.coreemu.sessions.get(_id) + if not session: + raise Exception("no session found") + return session + + def get_node(self, session, _id): + node = session.get_object(_id) + if not node: + raise Exception("no node found") + return node + def CreateSession(self, request, context): session = self.coreemu.create_session() session.set_state(EventTypes.DEFINITION_STATE) @@ -139,7 +151,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def GetSessionLocation(self, request, context): - session = self.coreemu.sessions.get(request.id) + session = self.get_session(request.id) x, y, z = session.location.refxyz lat, lon, alt = session.location.refgeo response = core_pb2.GetSessionLocationResponse() @@ -156,7 +168,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def SetSessionLocation(self, request, context): - session = self.coreemu.sessions.get(request.id) + session = self.get_session(request.id) session.location.refxyz = (request.position.x, request.position.y, request.position.z) session.location.setrefgeo(request.position.lat, request.position.lon, request.position.alt) @@ -168,7 +180,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): def SetSessionState(self, request, context): response = core_pb2.SetSessionStateResponse() - session = self.coreemu.sessions.get(request.id) + session = self.get_session(request.id) try: state = EventTypes(request.state) @@ -193,7 +205,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def GetSessionOptions(self, request, context): - session = self.coreemu.sessions.get(request.id) + session = self.get_session(request.id) config = session.options.get_configs() defaults = session.options.default_values() @@ -206,20 +218,14 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def SetSessionOptions(self, request, context): - session = self.coreemu.sessions.get(request.id) - if not session: - raise Exception("no session found") - + session = self.get_session(request.id) session.options.set_configs(request.config) response = core_pb2.SetSessionOptionsResponse() response.result = True return response def GetSession(self, request, context): - session = self.coreemu.sessions.get(request.id) - if not session: - raise Exception("no session found") - + session = self.get_session(request.id) response = core_pb2.GetSessionResponse() response.state = session.state @@ -264,11 +270,8 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def NodeEvents(self, request, context): - session = self.coreemu.sessions.get(request.id) - if not session: - raise Exception("no session found") + session = self.get_session(request.id) queue = Queue() - session.node_handlers.append(lambda x: queue.put(x)) while True: @@ -290,11 +293,8 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): yield node_event def SessionEvents(self, request, context): - session = self.coreemu.sessions.get(request.id) - if not session: - raise Exception("no session found") + session = self.get_session(request.id) queue = Queue() - session.event_handlers.append(lambda x: queue.put(x)) while True: @@ -315,9 +315,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): yield session_event def CreateNode(self, request, context): - session = self.coreemu.sessions.get(request.session) - if not session: - raise Exception("no session found") + session = self.get_session(request.session) node_id = request.id node_type = request.type @@ -346,13 +344,8 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def GetNode(self, request, context): - session = self.coreemu.sessions.get(request.session) - if not session: - raise Exception("no session found") - node = session.get_object(request.id) - if not node: - raise Exception("no node found") - + session = self.get_session(request.session) + node = self.get_node(session, request.id) response = core_pb2.GetNodeResponse() for interface_id, interface in node._netif.iteritems(): @@ -392,9 +385,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def EditNode(self, request, context): - session = self.coreemu.sessions.get(request.session) - if not session: - raise Exception("no session found") + session = self.get_session(request.session) node_id = request.id node_options = NodeOptions() @@ -414,23 +405,15 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): def DeleteNode(self, request, context): logging.info("delete node: %s", request) - session = self.coreemu.sessions.get(request.session) - if not session: - raise Exception("no session found") - + session = self.get_session(request.session) response = core_pb2.DeleteNodeResponse() response.result = session.delete_node(request.id) return response def GetNodeLinks(self, request, context): logging.info("get node links: %s", request) - session = self.coreemu.sessions.get(request.session) - if not session: - raise Exception("no session found") - node = session.get_object(request.id) - if not node: - raise Exception("no node found") - + session = self.get_session(request.session) + node = self.get_node(session, request.id) response = core_pb2.GetNodeLinksResponse() links_data = node.all_link_data(0) for link_data in links_data: @@ -440,12 +423,8 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def CreateLink(self, request, context): - session = self.coreemu.sessions.get(request.session) - if not session: - raise Exception("no session found") - + session = self.get_session(request.session) logging.info("adding link: %s", request) - node_one = request.link.node_one node_two = request.link.node_two @@ -515,9 +494,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): def EditLink(self, request, context): logging.info("edit link: %s", request) - session = self.coreemu.sessions.get(request.session) - if not session: - raise Exception("no session found") + session = self.get_session(request.session) node_one = request.node_one node_two = request.node_two @@ -546,9 +523,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): def DeleteLink(self, request, context): logging.info("delete link: %s", request) - session = self.coreemu.sessions.get(request.session) - if not session: - raise Exception("no session found") + session = self.get_session(request.session) node_one = request.node_one node_two = request.node_two @@ -561,9 +536,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def GetHooks(self, request, context): - session = self.coreemu.sessions.get(request.session) - if not session: - raise Exception("no session found") + session = self.get_session(request.session) response = core_pb2.GetHooksResponse() for state, state_hooks in session._hooks.iteritems(): @@ -576,9 +549,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def AddHook(self, request, context): - session = self.coreemu.sessions.get(request.session) - if not session: - raise Exception("no session found") + session = self.get_session(request.session) hook = request.hook session.add_hook(hook.state, hook.file, None, hook.data) @@ -586,6 +557,56 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): response.result = True return response + def GetMobilityConfigs(self, request, context): + session = self.get_session(request.session) + + response = core_pb2.GetMobilityConfigsResponse() + for node_id, model_config in session.mobility.node_configurations.iteritems(): + if node_id == -1: + continue + + for model_name in model_config.iterkeys(): + if model_name != Ns2ScriptedMobility.name: + continue + + config = session.mobility.get_model_config(node_id, model_name) + groups = get_config_groups(config, Ns2ScriptedMobility) + mobility_config = response.configs[node_id] + mobility_config.groups.extend(groups) + return response + + def GetMobilityConfig(self, request, context): + session = self.get_session(request.session) + config = session.mobility.get_model_config(request.id, Ns2ScriptedMobility.name) + groups = get_config_groups(config, Ns2ScriptedMobility) + response = core_pb2.GetMobilityConfigResponse() + response.groups.extend(groups) + return response + + def SetMobilityConfig(self, request, context): + session = self.get_session(request.session) + session.mobility.set_model_config(request.id, Ns2ScriptedMobility.name, request.config) + response = core_pb2.SetMobilityConfigResponse() + response.result = True + return response + + def MobilityAction(self, request, context): + session = self.get_session(request.session) + node = self.get_node(session, request.id) + + response = core_pb2.MobilityActionResponse() + response.result = True + if request.action == core_pb2.MOBILITY_START: + node.mobility.start() + elif request.action == core_pb2.MOBILITY_PAUSE: + node.mobility.pause() + elif request.action == core_pb2.MOBILITY_STOP: + node.mobility.stop(move_initial=True) + else: + response.result = False + + return response + def GetServices(self, request, context): response = core_pb2.GetServicesResponse() for service in ServiceManager.services.itervalues(): @@ -595,9 +616,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def GetServiceDefaults(self, request, context): - session = self.coreemu.sessions.get(request.session) - if not session: - raise Exception("no session found") + session = self.get_session(request.session) response = core_pb2.GetServiceDefaultsResponse() for node_type in session.services.default_services: @@ -608,10 +627,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def SetServiceDefaults(self, request, context): - session = self.coreemu.sessions.get(request.session) - if not session: - raise Exception("no session found") - + session = self.get_session(request.session) session.services.default_services.clear() for service_defaults in request.defaults: session.services.default_services[service_defaults.node_type] = service_defaults.services @@ -621,9 +637,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def GetNodeService(self, request, context): - session = self.coreemu.sessions.get(request.session) - if not session: - raise Exception("no session found") + session = self.get_session(request.session) service = session.services.get_service(request.id, request.service, default_service=True) response = core_pb2.GetNodeServiceResponse() @@ -641,12 +655,8 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def GetNodeServiceFile(self, request, context): - session = self.coreemu.sessions.get(request.session) - if not session: - raise Exception("no session found") - node = session.get_object(request.id) - if not node: - raise Exception("no node found") + session = self.get_session(request.session) + node = self.get_node(session, request.id) service = None for current_service in node.services: @@ -662,9 +672,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def SetNodeService(self, request, context): - session = self.coreemu.sessions.get(request.session) - if not session: - raise Exception("no session found") + session = self.get_session(request.session) # guarantee custom service exists session.services.set_service(request.id, request.service) @@ -681,23 +689,15 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def SetNodeServiceFile(self, request, context): - session = self.coreemu.sessions.get(request.session) - if not session: - raise Exception("no session found") - + session = self.get_session(request.session) session.services.set_service_file(request.id, request.service, request.file, request.data) - response = core_pb2.SetNodeServiceFileResponse() response.result = True return response def ServiceAction(self, request, context): - session = self.coreemu.sessions.get(request.session) - if not session: - raise Exception("no session found") - node = session.get_object(request.id) - if not node: - raise Exception("no node found") + session = self.get_session(request.session) + node = self.get_node(session, request.id) service = None for current_service in node.services: @@ -729,10 +729,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def GetWlanConfig(self, request, context): - session = self.coreemu.sessions.get(request.session) - if not session: - raise Exception("no session found") - + session = self.get_session(request.session) config = session.mobility.get_model_config(request.id, BasicRangeModel.name) groups = get_config_groups(config, BasicRangeModel) response = core_pb2.GetWlanConfigResponse() @@ -740,19 +737,14 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def SetWlanConfig(self, request, context): - session = self.coreemu.sessions.get(request.session) - if not session: - raise Exception("no session found") - + session = self.get_session(request.session) session.mobility.set_model_config(request.id, BasicRangeModel.name, request.config) response = core_pb2.SetWlanConfigResponse() response.result = True return response def GetEmaneConfig(self, request, context): - session = self.coreemu.sessions.get(request.session) - if not session: - raise Exception("no session found") + session = self.get_session(request.session) config = session.emane.get_configs() groups = get_config_groups(config, session.emane.emane_config) response = core_pb2.GetEmaneConfigResponse() @@ -760,19 +752,14 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def SetEmaneConfig(self, request, context): - session = self.coreemu.sessions.get(request.session) - if not session: - raise Exception("no session found") - + session = self.get_session(request.session) session.emane.set_configs(request.config) response = core_pb2.SetEmaneConfigResponse() response.result = True return response def GetEmaneModels(self, request, context): - session = self.coreemu.sessions.get(request.session) - if not session: - raise Exception("no session found") + session = self.get_session(request.session) models = [] for model in session.emane.models.keys(): @@ -785,10 +772,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def GetEmaneModelConfig(self, request, context): - session = self.coreemu.sessions.get(request.session) - if not session: - raise Exception("no session found") - + session = self.get_session(request.session) model = session.emane.models[request.model] config = session.emane.get_model_config(request.id, request.model) groups = get_config_groups(config, model) @@ -797,19 +781,14 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def SetEmaneModelConfig(self, request, context): - session = self.coreemu.sessions.get(request.session) - if not session: - raise Exception("no session found") - + session = self.get_session(request.session) session.emane.set_model_config(request.id, request.model, request.config) response = core_pb2.SetEmaneModelConfigResponse() response.result = True return response def GetEmaneModelConfigs(self, request, context): - session = self.coreemu.sessions.get(request.session) - if not session: - raise Exception("no session found") + session = self.get_session(request.session) response = core_pb2.GetEmaneModelConfigsResponse() for node_id, model_config in session.emane.node_configurations.iteritems(): @@ -828,9 +807,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def SaveXml(self, request, context): - session = self.coreemu.sessions.get(request.session) - if not session: - raise Exception("no session found") + session = self.get_session(request.session) _, temp_path = tempfile.mkstemp() session.save_xml(temp_path) diff --git a/daemon/proto/core.proto b/daemon/proto/core.proto index 258f5d38..b7faa76e 100644 --- a/daemon/proto/core.proto +++ b/daemon/proto/core.proto @@ -55,6 +55,17 @@ service CoreApi { rpc AddHook (AddHookRequest) returns (AddHookResponse) { } + // mobility rpc + rpc GetMobilityConfigs (GetMobilityConfigsRequest) returns (GetMobilityConfigsResponse) { + } + rpc GetMobilityConfig (GetMobilityConfigRequest) returns (GetMobilityConfigResponse) { + } + rpc SetMobilityConfig (SetMobilityConfigRequest) returns (SetMobilityConfigResponse) { + } + rpc MobilityAction (MobilityActionRequest) returns (MobilityActionResponse) { + } + + // service rpc rpc GetServices (GetServicesRequest) returns (GetServicesResponse) { } @@ -307,6 +318,46 @@ message AddHookResponse { bool result = 1; } +message GetMobilityConfigsRequest { + int32 session = 1; +} + +message GetMobilityConfigsResponse { + message MobilityConfig { + repeated ConfigGroup groups = 1; + } + map configs = 1; +} + +message GetMobilityConfigRequest { + int32 session = 1; + int32 id = 2; +} + +message GetMobilityConfigResponse { + repeated ConfigGroup groups = 1; +} + +message SetMobilityConfigRequest { + int32 session = 1; + int32 id = 2; + map config = 3; +} + +message SetMobilityConfigResponse { + bool result = 1; +} + +message MobilityActionRequest { + int32 session = 1; + int32 id = 2; + MobilityAction action = 3; +} + +message MobilityActionResponse { + bool result = 1; +} + message GetServicesRequest { } @@ -524,6 +575,12 @@ enum ServiceAction { VALIDATE = 3; } +enum MobilityAction { + MOBILITY_START = 0; + MOBILITY_PAUSE = 1; + MOBILITY_STOP = 2; +} + message Hook { SessionState state = 1; string file = 2; From f60a6720f009af249875dc7e75b1365e93036988 Mon Sep 17 00:00:00 2001 From: bharnden Date: Sun, 17 Mar 2019 15:53:29 -0700 Subject: [PATCH 23/72] grpc added specific grpc aborts for node/session not being found --- daemon/core/grpc/server.py | 94 +++++++++++++++++++------------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index 237dabcc..76c55cef 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -110,16 +110,16 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): super(CoreApiServer, self).__init__() self.coreemu = coreemu - def get_session(self, _id): + def get_session(self, _id, context): session = self.coreemu.sessions.get(_id) if not session: - raise Exception("no session found") + context.abort(grpc.StatusCode.NOT_FOUND, "session not found") return session - def get_node(self, session, _id): + def get_node(self, session, _id, context): node = session.get_object(_id) if not node: - raise Exception("no node found") + context.abort(grpc.StatusCode.NOT_FOUND, "node not found") return node def CreateSession(self, request, context): @@ -151,7 +151,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def GetSessionLocation(self, request, context): - session = self.get_session(request.id) + session = self.get_session(request.id, context) x, y, z = session.location.refxyz lat, lon, alt = session.location.refgeo response = core_pb2.GetSessionLocationResponse() @@ -168,7 +168,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def SetSessionLocation(self, request, context): - session = self.get_session(request.id) + session = self.get_session(request.id, context) session.location.refxyz = (request.position.x, request.position.y, request.position.z) session.location.setrefgeo(request.position.lat, request.position.lon, request.position.alt) @@ -180,7 +180,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): def SetSessionState(self, request, context): response = core_pb2.SetSessionStateResponse() - session = self.get_session(request.id) + session = self.get_session(request.id, context) try: state = EventTypes(request.state) @@ -205,7 +205,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def GetSessionOptions(self, request, context): - session = self.get_session(request.id) + session = self.get_session(request.id, context) config = session.options.get_configs() defaults = session.options.default_values() @@ -218,14 +218,14 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def SetSessionOptions(self, request, context): - session = self.get_session(request.id) + session = self.get_session(request.id, context) session.options.set_configs(request.config) response = core_pb2.SetSessionOptionsResponse() response.result = True return response def GetSession(self, request, context): - session = self.get_session(request.id) + session = self.get_session(request.id, context) response = core_pb2.GetSessionResponse() response.state = session.state @@ -270,7 +270,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def NodeEvents(self, request, context): - session = self.get_session(request.id) + session = self.get_session(request.id, context) queue = Queue() session.node_handlers.append(lambda x: queue.put(x)) @@ -293,7 +293,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): yield node_event def SessionEvents(self, request, context): - session = self.get_session(request.id) + session = self.get_session(request.id, context) queue = Queue() session.event_handlers.append(lambda x: queue.put(x)) @@ -315,7 +315,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): yield session_event def CreateNode(self, request, context): - session = self.get_session(request.session) + session = self.get_session(request.session, context) node_id = request.id node_type = request.type @@ -344,8 +344,8 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def GetNode(self, request, context): - session = self.get_session(request.session) - node = self.get_node(session, request.id) + session = self.get_session(request.session, context) + node = self.get_node(session, request.id, context) response = core_pb2.GetNodeResponse() for interface_id, interface in node._netif.iteritems(): @@ -385,7 +385,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def EditNode(self, request, context): - session = self.get_session(request.session) + session = self.get_session(request.session, context) node_id = request.id node_options = NodeOptions() @@ -405,15 +405,15 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): def DeleteNode(self, request, context): logging.info("delete node: %s", request) - session = self.get_session(request.session) + session = self.get_session(request.session, context) response = core_pb2.DeleteNodeResponse() response.result = session.delete_node(request.id) return response def GetNodeLinks(self, request, context): logging.info("get node links: %s", request) - session = self.get_session(request.session) - node = self.get_node(session, request.id) + session = self.get_session(request.session, context) + node = self.get_node(session, request.id, context) response = core_pb2.GetNodeLinksResponse() links_data = node.all_link_data(0) for link_data in links_data: @@ -423,7 +423,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def CreateLink(self, request, context): - session = self.get_session(request.session) + session = self.get_session(request.session, context) logging.info("adding link: %s", request) node_one = request.link.node_one node_two = request.link.node_two @@ -494,7 +494,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): def EditLink(self, request, context): logging.info("edit link: %s", request) - session = self.get_session(request.session) + session = self.get_session(request.session, context) node_one = request.node_one node_two = request.node_two @@ -523,7 +523,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): def DeleteLink(self, request, context): logging.info("delete link: %s", request) - session = self.get_session(request.session) + session = self.get_session(request.session, context) node_one = request.node_one node_two = request.node_two @@ -536,7 +536,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def GetHooks(self, request, context): - session = self.get_session(request.session) + session = self.get_session(request.session, context) response = core_pb2.GetHooksResponse() for state, state_hooks in session._hooks.iteritems(): @@ -549,7 +549,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def AddHook(self, request, context): - session = self.get_session(request.session) + session = self.get_session(request.session, context) hook = request.hook session.add_hook(hook.state, hook.file, None, hook.data) @@ -558,7 +558,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def GetMobilityConfigs(self, request, context): - session = self.get_session(request.session) + session = self.get_session(request.session, context) response = core_pb2.GetMobilityConfigsResponse() for node_id, model_config in session.mobility.node_configurations.iteritems(): @@ -576,7 +576,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def GetMobilityConfig(self, request, context): - session = self.get_session(request.session) + session = self.get_session(request.session, context) config = session.mobility.get_model_config(request.id, Ns2ScriptedMobility.name) groups = get_config_groups(config, Ns2ScriptedMobility) response = core_pb2.GetMobilityConfigResponse() @@ -584,15 +584,15 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def SetMobilityConfig(self, request, context): - session = self.get_session(request.session) + session = self.get_session(request.session, context) session.mobility.set_model_config(request.id, Ns2ScriptedMobility.name, request.config) response = core_pb2.SetMobilityConfigResponse() response.result = True return response def MobilityAction(self, request, context): - session = self.get_session(request.session) - node = self.get_node(session, request.id) + session = self.get_session(request.session, context) + node = self.get_node(session, request.id, context) response = core_pb2.MobilityActionResponse() response.result = True @@ -616,7 +616,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def GetServiceDefaults(self, request, context): - session = self.get_session(request.session) + session = self.get_session(request.session, context) response = core_pb2.GetServiceDefaultsResponse() for node_type in session.services.default_services: @@ -627,7 +627,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def SetServiceDefaults(self, request, context): - session = self.get_session(request.session) + session = self.get_session(request.session, context) session.services.default_services.clear() for service_defaults in request.defaults: session.services.default_services[service_defaults.node_type] = service_defaults.services @@ -637,7 +637,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def GetNodeService(self, request, context): - session = self.get_session(request.session) + session = self.get_session(request.session, context) service = session.services.get_service(request.id, request.service, default_service=True) response = core_pb2.GetNodeServiceResponse() @@ -655,8 +655,8 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def GetNodeServiceFile(self, request, context): - session = self.get_session(request.session) - node = self.get_node(session, request.id) + session = self.get_session(request.session, context) + node = self.get_node(session, request.id, context) service = None for current_service in node.services: @@ -672,7 +672,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def SetNodeService(self, request, context): - session = self.get_session(request.session) + session = self.get_session(request.session, context) # guarantee custom service exists session.services.set_service(request.id, request.service) @@ -689,15 +689,15 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def SetNodeServiceFile(self, request, context): - session = self.get_session(request.session) + session = self.get_session(request.session, context) session.services.set_service_file(request.id, request.service, request.file, request.data) response = core_pb2.SetNodeServiceFileResponse() response.result = True return response def ServiceAction(self, request, context): - session = self.get_session(request.session) - node = self.get_node(session, request.id) + session = self.get_session(request.session, context) + node = self.get_node(session, request.id, context) service = None for current_service in node.services: @@ -729,7 +729,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def GetWlanConfig(self, request, context): - session = self.get_session(request.session) + session = self.get_session(request.session, context) config = session.mobility.get_model_config(request.id, BasicRangeModel.name) groups = get_config_groups(config, BasicRangeModel) response = core_pb2.GetWlanConfigResponse() @@ -737,14 +737,14 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def SetWlanConfig(self, request, context): - session = self.get_session(request.session) + session = self.get_session(request.session, context) session.mobility.set_model_config(request.id, BasicRangeModel.name, request.config) response = core_pb2.SetWlanConfigResponse() response.result = True return response def GetEmaneConfig(self, request, context): - session = self.get_session(request.session) + session = self.get_session(request.session, context) config = session.emane.get_configs() groups = get_config_groups(config, session.emane.emane_config) response = core_pb2.GetEmaneConfigResponse() @@ -752,14 +752,14 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def SetEmaneConfig(self, request, context): - session = self.get_session(request.session) + session = self.get_session(request.session, context) session.emane.set_configs(request.config) response = core_pb2.SetEmaneConfigResponse() response.result = True return response def GetEmaneModels(self, request, context): - session = self.get_session(request.session) + session = self.get_session(request.session, context) models = [] for model in session.emane.models.keys(): @@ -772,7 +772,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def GetEmaneModelConfig(self, request, context): - session = self.get_session(request.session) + session = self.get_session(request.session, context) model = session.emane.models[request.model] config = session.emane.get_model_config(request.id, request.model) groups = get_config_groups(config, model) @@ -781,14 +781,14 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def SetEmaneModelConfig(self, request, context): - session = self.get_session(request.session) + session = self.get_session(request.session, context) session.emane.set_model_config(request.id, request.model, request.config) response = core_pb2.SetEmaneModelConfigResponse() response.result = True return response def GetEmaneModelConfigs(self, request, context): - session = self.get_session(request.session) + session = self.get_session(request.session, context) response = core_pb2.GetEmaneModelConfigsResponse() for node_id, model_config in session.emane.node_configurations.iteritems(): @@ -807,7 +807,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def SaveXml(self, request, context): - session = self.get_session(request.session) + session = self.get_session(request.session, context) _, temp_path = tempfile.mkstemp() session.save_xml(temp_path) From 8ee1db5dc8b90d324ea6e6ee6ea4fae14d2fd139 Mon Sep 17 00:00:00 2001 From: bharnden Date: Sun, 17 Mar 2019 23:29:38 -0700 Subject: [PATCH 24/72] grpc added config, exception, and file event streams --- daemon/core/grpc/client.py | 39 ++++++++++++++++++- daemon/core/grpc/server.py | 79 ++++++++++++++++++++++++++++++++++++-- daemon/proto/core.proto | 69 +++++++++++++++++++++++++++++++++ 3 files changed, 182 insertions(+), 5 deletions(-) diff --git a/daemon/core/grpc/client.py b/daemon/core/grpc/client.py index 43aeee3b..19acf39a 100644 --- a/daemon/core/grpc/client.py +++ b/daemon/core/grpc/client.py @@ -95,6 +95,42 @@ class CoreApiClient(object): thread.daemon = True thread.start() + def config_events(self, _id, handler): + request = core_pb2.ConfigEventsRequest() + request.id = _id + + def listen(): + for event in self.stub.ConfigEvents(request): + handler(event) + + thread = threading.Thread(target=listen) + thread.daemon = True + thread.start() + + def exception_events(self, _id, handler): + request = core_pb2.ExceptionEventsRequest() + request.id = _id + + def listen(): + for event in self.stub.ExceptionEvents(request): + handler(event) + + thread = threading.Thread(target=listen) + thread.daemon = True + thread.start() + + def file_events(self, _id, handler): + request = core_pb2.FileEventsRequest() + request.id = _id + + def listen(): + for event in self.stub.FileEvents(request): + handler(event) + + thread = threading.Thread(target=listen) + thread.daemon = True + thread.start() + def create_node(self, session, _type=NodeTypes.DEFAULT, _id=None, node_options=None, emane=None): if not node_options: node_options = NodeOptions() @@ -436,7 +472,8 @@ def main(): client = CoreApiClient() with client.context_connect(): if os.path.exists(xml_file_name): - print("open xml: {}".format(client.open_xml(xml_file_name))) + response = client.open_xml(xml_file_name) + print("open xml: {}".format(response)) print("services: {}".format(client.get_services())) diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index 76c55cef..5a0fb984 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -2,10 +2,10 @@ import logging import os import tempfile import time +from Queue import Queue import grpc from concurrent import futures -from Queue import Queue import core_pb2 import core_pb2_grpc @@ -311,9 +311,82 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): data=event.data, time=event_time, session=session.session_id - ) + ) yield session_event + def ConfigEvents(self, request, context): + session = self.get_session(request.id, context) + queue = Queue() + session.config_handlers.append(lambda x: queue.put(x)) + + while True: + event = queue.get() + config_event = core_pb2.ConfigEvent() + update_proto( + config_event, + message_type=event.message_type, + node=event.node, + object=event.object, + type=event.type, + captions=event.captions, + bitmap=event.bitmap, + data_values=event.data_values, + possible_values=event.possible_values, + groups=event.groups, + session=event.session, + interface=event.interface_number, + network_id=event.network_id, + opaque=event.opaque + ) + config_event.data_types.extend(event.data_types) + yield config_event + + def ExceptionEvents(self, request, context): + session = self.get_session(request.id, context) + queue = Queue() + session.exception_handlers.append(lambda x: queue.put(x)) + + while True: + event = queue.get() + exception_event = core_pb2.ExceptionEvent() + event_time = event.date + if event_time is not None: + event_time = float(event_time) + update_proto( + exception_event, + node=event.node, + session=event.session, + level=event.level, + source=event.source, + date=event_time, + text=event.text, + opaque=event.opaque + ) + yield exception_event + + def FileEvents(self, request, context): + session = self.get_session(request.id, context) + queue = Queue() + session.file_handlers.append(lambda x: queue.put(x)) + + while True: + event = queue.get() + file_event = core_pb2.FileEvent() + update_proto( + file_event, + message_type=event.message_type, + node=event.node, + name=event.name, + mode=event.mode, + number=event.number, + type=event.type, + source=event.source, + session=event.session, + data=event.data, + compressed_data=event.compressed_data + ) + yield file_event + def CreateNode(self, request, context): session = self.get_session(request.session, context) @@ -799,11 +872,9 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): model = session.emane.models[model_name] config = session.emane.get_model_config(node_id, model_name) config_groups = get_config_groups(config, model) - # node_configurations = response.setdefault(node_id, {}) node_configurations = response.configs[node_id] node_configurations.model = model_name node_configurations.groups.extend(config_groups) - # node_configurations[model_name] = config_groups return response def SaveXml(self, request, context): diff --git a/daemon/proto/core.proto b/daemon/proto/core.proto index b7faa76e..d6984a4e 100644 --- a/daemon/proto/core.proto +++ b/daemon/proto/core.proto @@ -28,6 +28,12 @@ service CoreApi { } rpc SessionEvents (SessionEventsRequest) returns (stream SessionEvent) { } + rpc ConfigEvents (ConfigEventsRequest) returns (stream ConfigEvent) { + } + rpc ExceptionEvents (ExceptionEventsRequest) returns (stream ExceptionEvent) { + } + rpc FileEvents (FileEventsRequest) returns (stream FileEvent) { + } // node rpc rpc CreateNode (CreateNodeRequest) returns (CreateNodeResponse) { @@ -212,6 +218,58 @@ message SessionEvent { int32 session = 6; } +message ConfigEventsRequest { + int32 id = 1; +} + +message ConfigEvent { + MessageType message_type = 1; + int32 node = 2; + string object = 3; + int32 type = 4; + repeated int32 data_types = 5; + string data_values = 6; + string captions = 7; + string bitmap = 8; + string possible_values = 9; + string groups = 10; + string session = 11; + int32 interface = 12; + int32 network_id = 13; + string opaque = 14; +} + +message ExceptionEventsRequest { + int32 id = 1; +} + +message ExceptionEvent { + int32 node = 1; + int32 session = 2; + string level = 3; + string source = 4; + float date = 5; + string text = 6; + string opaque = 7; +} + +message FileEventsRequest { + int32 id = 1; +} + +message FileEvent { + MessageType message_type = 1; + int32 node = 2; + string name = 3; + string mode = 4; + int32 number = 5; + string type = 6; + string source = 7; + int32 session = 8; + bytes data = 9; + bytes compressed_data = 10; +} + message CreateNodeRequest { int32 session = 1; int32 id = 2; @@ -535,6 +593,17 @@ message OpenXmlResponse { } // data structures for messages below +enum MessageType { + NOTHING = 0; + ADD = 1; + DELETE = 2; + CRI = 4; + LOCAL = 8; + STRING = 16; + TEXT = 32; + TTY = 64; +} + enum SessionState { NONE = 0; DEFINITION = 1; From e282b3b8f8258589e000618bcde22e10e3a13b99 Mon Sep 17 00:00:00 2001 From: bharnden Date: Mon, 18 Mar 2019 21:46:27 -0700 Subject: [PATCH 25/72] grpc added link events --- daemon/core/grpc/client.py | 13 +++ daemon/core/grpc/server.py | 164 ++++++++++++++++++++++++++++++++++++- daemon/proto/core.proto | 11 +++ 3 files changed, 186 insertions(+), 2 deletions(-) diff --git a/daemon/core/grpc/client.py b/daemon/core/grpc/client.py index 19acf39a..6d1720e3 100644 --- a/daemon/core/grpc/client.py +++ b/daemon/core/grpc/client.py @@ -1,4 +1,5 @@ from __future__ import print_function + import logging import os import threading @@ -83,6 +84,18 @@ class CoreApiClient(object): thread.daemon = True thread.start() + def link_events(self, _id, handler): + request = core_pb2.LinkEventsRequest() + request.id = _id + + def listen(): + for event in self.stub.LinkEvents(request): + handler(event) + + thread = threading.Thread(target=listen) + thread.daemon = True + thread.start() + def session_events(self, _id, handler): request = core_pb2.SessionEventsRequest() request.id = _id diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index 5a0fb984..bb3f8d27 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -3,17 +3,20 @@ import os import tempfile import time from Queue import Queue +from itertools import repeat import grpc from concurrent import futures import core_pb2 import core_pb2_grpc +from core.conf import ConfigShim +from core.data import ConfigData, FileData from core.emulator.emudata import NodeOptions, InterfaceData, LinkOptions -from core.enumerations import NodeTypes, EventTypes, LinkTypes +from core.enumerations import NodeTypes, EventTypes, LinkTypes, MessageFlags, ConfigFlags, ConfigDataTypes from core.misc import nodeutils from core.mobility import BasicRangeModel, Ns2ScriptedMobility -from core.service import ServiceManager +from core.service import ServiceManager, ServiceShim _ONE_DAY_IN_SECONDS = 60 * 60 * 24 @@ -105,6 +108,106 @@ def convert_link(session, link_data, link): ) +def send_objects(session): + time.sleep(1) + # find all nodes and links + nodes_data = [] + links_data = [] + with session._objects_lock: + for obj in session.objects.itervalues(): + node_data = obj.data(message_type=MessageFlags.ADD.value) + if node_data: + nodes_data.append(node_data) + + node_links = obj.all_link_data(flags=MessageFlags.ADD.value) + for link_data in node_links: + links_data.append(link_data) + + # send all nodes first, so that they will exist for any links + for node_data in nodes_data: + session.broadcast_node(node_data) + + for link_data in links_data: + session.broadcast_link(link_data) + + # send mobility model info + for node_id in session.mobility.nodes(): + for model_name, config in session.mobility.get_all_configs(node_id).iteritems(): + model_class = session.mobility.models[model_name] + logging.debug("mobility config: node(%s) class(%s) values(%s)", node_id, model_class, config) + config_data = ConfigShim.config_data(0, node_id, ConfigFlags.UPDATE.value, model_class, config) + session.broadcast_config(config_data) + + # send emane model info + for node_id in session.emane.nodes(): + for model_name, config in session.emane.get_all_configs(node_id).iteritems(): + model_class = session.emane.models[model_name] + logging.debug("emane config: node(%s) class(%s) values(%s)", node_id, model_class, config) + config_data = ConfigShim.config_data(0, node_id, ConfigFlags.UPDATE.value, model_class, config) + session.broadcast_config(config_data) + + # service customizations + service_configs = session.services.all_configs() + for node_id, service in service_configs: + opaque = "service:%s" % service.name + data_types = tuple(repeat(ConfigDataTypes.STRING.value, len(ServiceShim.keys))) + node = session.get_object(node_id) + values = ServiceShim.tovaluelist(node, service) + config_data = ConfigData( + message_type=0, + node=node_id, + object=session.services.name, + type=ConfigFlags.UPDATE.value, + data_types=data_types, + data_values=values, + session=str(session.session_id), + opaque=opaque + ) + session.broadcast_config(config_data) + + for file_name, config_data in session.services.all_files(service): + file_data = FileData( + message_type=MessageFlags.ADD.value, + node=node_id, + name=str(file_name), + type=opaque, + data=str(config_data) + ) + session.broadcast_file(file_data) + + # TODO: send location info + + # send hook scripts + for state in sorted(session._hooks.keys()): + for file_name, config_data in session._hooks[state]: + file_data = FileData( + message_type=MessageFlags.ADD.value, + name=str(file_name), + type="hook:%s" % state, + data=str(config_data) + ) + session.broadcast_file(file_data) + + # send session configuration + session_config = session.options.get_configs() + config_data = ConfigShim.config_data(0, None, ConfigFlags.UPDATE.value, session.options, session_config) + session.broadcast_config(config_data) + + # send session metadata + data_values = "|".join(["%s=%s" % item for item in session.metadata.get_configs().iteritems()]) + data_types = tuple(ConfigDataTypes.STRING.value for _ in session.metadata.get_configs()) + config_data = ConfigData( + message_type=0, + object=session.metadata.name, + type=ConfigFlags.NONE.value, + data_types=data_types, + data_values=data_values + ) + session.broadcast_config(config_data) + + logging.info("informed GUI about %d nodes and %d links", len(nodes_data), len(links_data)) + + class CoreApiServer(core_pb2_grpc.CoreApiServicer): def __init__(self, coreemu): super(CoreApiServer, self).__init__() @@ -292,6 +395,63 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): node_event.node.services.extend(services.split("|")) yield node_event + def LinkEvents(self, request, context): + session = self.get_session(request.id, context) + queue = Queue() + session.link_handlers.append(lambda x: queue.put(x)) + + while True: + event = queue.get() + link_event = core_pb2.LinkEvent() + if event.interface1_id is not None: + interface_one = link_event.link.interface_one + update_proto( + interface_one, + id=event.interface1_id, + name=event.interface1_name, + mac=convert_value(event.interface1_mac), + ip4=convert_value(event.interface1_ip4), + ip4mask=event.interface1_ip4_mask, + ip6=convert_value(event.interface1_ip6), + ip6mask=event.interface1_ip6_mask, + ) + + if event.interface2_id is not None: + interface_two = link_event.link.interface_two + update_proto( + interface_two, + id=event.interface2_id, + name=event.interface2_name, + mac=convert_value(event.interface2_mac), + ip4=convert_value(event.interface2_ip4), + ip4mask=event.interface2_ip4_mask, + ip6=convert_value(event.interface2_ip6), + ip6mask=event.interface2_ip6_mask, + ) + + link_event.message_type = event.message_type + update_proto( + link_event.link, + type=event.link_type, + node_one=event.node1_id, + node_two=event.node2_id + ) + update_proto( + link_event.link.options, + opaque=event.opaque, + jitter=event.jitter, + key=event.key, + mburst=event.mburst, + mer=event.mer, + per=event.per, + bandwidth=event.bandwidth, + burst=event.burst, + delay=event.delay, + dup=event.dup, + unidirectional=event.unidirectional + ) + yield link_event + def SessionEvents(self, request, context): session = self.get_session(request.id, context) queue = Queue() diff --git a/daemon/proto/core.proto b/daemon/proto/core.proto index d6984a4e..de4d18be 100644 --- a/daemon/proto/core.proto +++ b/daemon/proto/core.proto @@ -26,6 +26,8 @@ service CoreApi { // event streams rpc NodeEvents (NodeEventsRequest) returns (stream NodeEvent) { } + rpc LinkEvents (LinkEventsRequest) returns (stream LinkEvent) { + } rpc SessionEvents (SessionEventsRequest) returns (stream SessionEvent) { } rpc ConfigEvents (ConfigEventsRequest) returns (stream ConfigEvent) { @@ -205,6 +207,15 @@ message NodeEvent { Node node = 1; } +message LinkEventsRequest { + int32 id = 1; +} + +message LinkEvent { + MessageType message_type = 1; + Link link = 2; +} + message SessionEventsRequest { int32 id = 1; } From 84ff1f4275db16cd9a26cd3dc91d2991f356a7b7 Mon Sep 17 00:00:00 2001 From: bharnden Date: Mon, 18 Mar 2019 22:32:01 -0700 Subject: [PATCH 26/72] grpc added client disconnect detection for streams --- daemon/core/grpc/server.py | 294 ++++++++++++++++++++----------------- 1 file changed, 156 insertions(+), 138 deletions(-) diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index bb3f8d27..0bb51704 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -2,7 +2,7 @@ import logging import os import tempfile import time -from Queue import Queue +from Queue import Queue, Empty from itertools import repeat import grpc @@ -377,175 +377,193 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): queue = Queue() session.node_handlers.append(lambda x: queue.put(x)) - while True: - node = queue.get() - node_event = core_pb2.NodeEvent() - update_proto( - node_event.node, - id=node.id, - name=node.name, - model=node.model - ) - update_proto( - node_event.node.position, - x=node.x_position, - y=node.y_position - ) - services = node.services or "" - node_event.node.services.extend(services.split("|")) - yield node_event + while context.is_active(): + try: + node = queue.get(timeout=1) + node_event = core_pb2.NodeEvent() + update_proto( + node_event.node, + id=node.id, + name=node.name, + model=node.model + ) + update_proto( + node_event.node.position, + x=node.x_position, + y=node.y_position + ) + services = node.services or "" + node_event.node.services.extend(services.split("|")) + yield node_event + except Empty: + continue def LinkEvents(self, request, context): session = self.get_session(request.id, context) queue = Queue() session.link_handlers.append(lambda x: queue.put(x)) - while True: - event = queue.get() - link_event = core_pb2.LinkEvent() - if event.interface1_id is not None: - interface_one = link_event.link.interface_one - update_proto( - interface_one, - id=event.interface1_id, - name=event.interface1_name, - mac=convert_value(event.interface1_mac), - ip4=convert_value(event.interface1_ip4), - ip4mask=event.interface1_ip4_mask, - ip6=convert_value(event.interface1_ip6), - ip6mask=event.interface1_ip6_mask, - ) + while context.is_active(): + try: + event = queue.get(timeout=1) + link_event = core_pb2.LinkEvent() + if event.interface1_id is not None: + interface_one = link_event.link.interface_one + update_proto( + interface_one, + id=event.interface1_id, + name=event.interface1_name, + mac=convert_value(event.interface1_mac), + ip4=convert_value(event.interface1_ip4), + ip4mask=event.interface1_ip4_mask, + ip6=convert_value(event.interface1_ip6), + ip6mask=event.interface1_ip6_mask, + ) - if event.interface2_id is not None: - interface_two = link_event.link.interface_two - update_proto( - interface_two, - id=event.interface2_id, - name=event.interface2_name, - mac=convert_value(event.interface2_mac), - ip4=convert_value(event.interface2_ip4), - ip4mask=event.interface2_ip4_mask, - ip6=convert_value(event.interface2_ip6), - ip6mask=event.interface2_ip6_mask, - ) + if event.interface2_id is not None: + interface_two = link_event.link.interface_two + update_proto( + interface_two, + id=event.interface2_id, + name=event.interface2_name, + mac=convert_value(event.interface2_mac), + ip4=convert_value(event.interface2_ip4), + ip4mask=event.interface2_ip4_mask, + ip6=convert_value(event.interface2_ip6), + ip6mask=event.interface2_ip6_mask, + ) - link_event.message_type = event.message_type - update_proto( - link_event.link, - type=event.link_type, - node_one=event.node1_id, - node_two=event.node2_id - ) - update_proto( - link_event.link.options, - opaque=event.opaque, - jitter=event.jitter, - key=event.key, - mburst=event.mburst, - mer=event.mer, - per=event.per, - bandwidth=event.bandwidth, - burst=event.burst, - delay=event.delay, - dup=event.dup, - unidirectional=event.unidirectional - ) - yield link_event + link_event.message_type = event.message_type + update_proto( + link_event.link, + type=event.link_type, + node_one=event.node1_id, + node_two=event.node2_id + ) + update_proto( + link_event.link.options, + opaque=event.opaque, + jitter=event.jitter, + key=event.key, + mburst=event.mburst, + mer=event.mer, + per=event.per, + bandwidth=event.bandwidth, + burst=event.burst, + delay=event.delay, + dup=event.dup, + unidirectional=event.unidirectional + ) + yield link_event + except Empty: + continue def SessionEvents(self, request, context): session = self.get_session(request.id, context) queue = Queue() session.event_handlers.append(lambda x: queue.put(x)) - while True: - event = queue.get() - session_event = core_pb2.SessionEvent() - event_time = event.time - if event_time is not None: - event_time = float(event_time) - update_proto( - session_event, - node=event.node, - event=event.event_type, - name=event.name, - data=event.data, - time=event_time, - session=session.session_id - ) - yield session_event + while context.is_active(): + try: + event = queue.get(timeout=1) + session_event = core_pb2.SessionEvent() + event_time = event.time + if event_time is not None: + event_time = float(event_time) + update_proto( + session_event, + node=event.node, + event=event.event_type, + name=event.name, + data=event.data, + time=event_time, + session=session.session_id + ) + yield session_event + except Empty: + continue def ConfigEvents(self, request, context): session = self.get_session(request.id, context) queue = Queue() session.config_handlers.append(lambda x: queue.put(x)) - while True: - event = queue.get() - config_event = core_pb2.ConfigEvent() - update_proto( - config_event, - message_type=event.message_type, - node=event.node, - object=event.object, - type=event.type, - captions=event.captions, - bitmap=event.bitmap, - data_values=event.data_values, - possible_values=event.possible_values, - groups=event.groups, - session=event.session, - interface=event.interface_number, - network_id=event.network_id, - opaque=event.opaque - ) - config_event.data_types.extend(event.data_types) - yield config_event + while context.is_active(): + try: + event = queue.get(timeout=1) + config_event = core_pb2.ConfigEvent() + update_proto( + config_event, + message_type=event.message_type, + node=event.node, + object=event.object, + type=event.type, + captions=event.captions, + bitmap=event.bitmap, + data_values=event.data_values, + possible_values=event.possible_values, + groups=event.groups, + session=event.session, + interface=event.interface_number, + network_id=event.network_id, + opaque=event.opaque + ) + config_event.data_types.extend(event.data_types) + yield config_event + except Empty: + continue def ExceptionEvents(self, request, context): session = self.get_session(request.id, context) queue = Queue() session.exception_handlers.append(lambda x: queue.put(x)) - while True: - event = queue.get() - exception_event = core_pb2.ExceptionEvent() - event_time = event.date - if event_time is not None: - event_time = float(event_time) - update_proto( - exception_event, - node=event.node, - session=event.session, - level=event.level, - source=event.source, - date=event_time, - text=event.text, - opaque=event.opaque - ) - yield exception_event + while context.is_active(): + try: + event = queue.get(timeout=1) + exception_event = core_pb2.ExceptionEvent() + event_time = event.date + if event_time is not None: + event_time = float(event_time) + update_proto( + exception_event, + node=event.node, + session=event.session, + level=event.level, + source=event.source, + date=event_time, + text=event.text, + opaque=event.opaque + ) + yield exception_event + except Empty: + continue def FileEvents(self, request, context): session = self.get_session(request.id, context) queue = Queue() session.file_handlers.append(lambda x: queue.put(x)) - while True: - event = queue.get() - file_event = core_pb2.FileEvent() - update_proto( - file_event, - message_type=event.message_type, - node=event.node, - name=event.name, - mode=event.mode, - number=event.number, - type=event.type, - source=event.source, - session=event.session, - data=event.data, - compressed_data=event.compressed_data - ) - yield file_event + while context.is_active(): + try: + event = queue.get(timeout=1) + file_event = core_pb2.FileEvent() + update_proto( + file_event, + message_type=event.message_type, + node=event.node, + name=event.name, + mode=event.mode, + number=event.number, + type=event.type, + source=event.source, + session=event.session, + data=event.data, + compressed_data=event.compressed_data + ) + yield file_event + except Empty: + continue def CreateNode(self, request, context): session = self.get_session(request.session, context) From 6b8857328e3d3611e251faacb6e857b9b2e5458d Mon Sep 17 00:00:00 2001 From: bharnden Date: Tue, 19 Mar 2019 21:26:06 -0700 Subject: [PATCH 27/72] grpc updated and dixed exception events to working order --- daemon/core/grpc/client.py | 1 + daemon/core/grpc/server.py | 9 +++------ daemon/proto/core.proto | 12 ++++++++++-- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/daemon/core/grpc/client.py b/daemon/core/grpc/client.py index 6d1720e3..e935ba44 100644 --- a/daemon/core/grpc/client.py +++ b/daemon/core/grpc/client.py @@ -492,6 +492,7 @@ def main(): # create session session_data = client.create_session() + client.exception_events(session_data.id, lambda x: print(x)) print("created session: {}".format(session_data)) print("default services: {}".format(client.get_service_defaults(session_data.id))) print("emane models: {}".format(client.get_emane_models(session_data.id))) diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index 0bb51704..2d910cfa 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -522,16 +522,13 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): try: event = queue.get(timeout=1) exception_event = core_pb2.ExceptionEvent() - event_time = event.date - if event_time is not None: - event_time = float(event_time) update_proto( exception_event, node=event.node, - session=event.session, - level=event.level, + session=int(event.session), + level=event.level.value, source=event.source, - date=event_time, + date=event.date, text=event.text, opaque=event.opaque ) diff --git a/daemon/proto/core.proto b/daemon/proto/core.proto index de4d18be..7b06ce84 100644 --- a/daemon/proto/core.proto +++ b/daemon/proto/core.proto @@ -257,9 +257,9 @@ message ExceptionEventsRequest { message ExceptionEvent { int32 node = 1; int32 session = 2; - string level = 3; + ExceptionLevel level = 3; string source = 4; - float date = 5; + string date = 5; string text = 6; string opaque = 7; } @@ -661,6 +661,14 @@ enum MobilityAction { MOBILITY_STOP = 2; } +enum ExceptionLevel { + EXCEPTION_DEFAULT = 0; + FATAL = 1; + ERROR = 2; + WARNING = 3; + NOTICE = 4; +} + message Hook { SessionState state = 1; string file = 2; From 8009a18a1cae3630149e8a661ee2a4bb3a7c662a Mon Sep 17 00:00:00 2001 From: bharnden Date: Tue, 19 Mar 2019 21:43:11 -0700 Subject: [PATCH 28/72] grpc added debug logs for all requests --- daemon/core/grpc/server.py | 53 ++++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 11 deletions(-) diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index 2d910cfa..613b4e77 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -205,7 +205,7 @@ def send_objects(session): ) session.broadcast_config(config_data) - logging.info("informed GUI about %d nodes and %d links", len(nodes_data), len(links_data)) + logging.debug("informed GUI about %d nodes and %d links", len(nodes_data), len(links_data)) class CoreApiServer(core_pb2_grpc.CoreApiServicer): @@ -226,6 +226,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return node def CreateSession(self, request, context): + logging.debug("create session: %s", request) session = self.coreemu.create_session() session.set_state(EventTypes.DEFINITION_STATE) @@ -239,11 +240,13 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def DeleteSession(self, request, context): + logging.debug("delete session: %s", request) response = core_pb2.DeleteSessionResponse() response.result = self.coreemu.delete_session(request.id) return response def GetSessions(self, request, context): + logging.debug("get sessions: %s", request) response = core_pb2.GetSessionsResponse() for session_id in self.coreemu.sessions: session = self.coreemu.sessions[session_id] @@ -254,6 +257,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def GetSessionLocation(self, request, context): + logging.debug("get session location: %s", request) session = self.get_session(request.id, context) x, y, z = session.location.refxyz lat, lon, alt = session.location.refgeo @@ -271,6 +275,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def SetSessionLocation(self, request, context): + logging.debug("set session location: %s", request) session = self.get_session(request.id, context) session.location.refxyz = (request.position.x, request.position.y, request.position.z) @@ -282,6 +287,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def SetSessionState(self, request, context): + logging.debug("set session state: %s", request) response = core_pb2.SetSessionStateResponse() session = self.get_session(request.id, context) @@ -308,6 +314,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def GetSessionOptions(self, request, context): + logging.debug("get session options: %s", request) session = self.get_session(request.id, context) config = session.options.get_configs() @@ -321,6 +328,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def SetSessionOptions(self, request, context): + logging.debug("set session options: %s", request) session = self.get_session(request.id, context) session.options.set_configs(request.config) response = core_pb2.SetSessionOptionsResponse() @@ -328,6 +336,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def GetSession(self, request, context): + logging.debug("get session: %s", request) session = self.get_session(request.id, context) response = core_pb2.GetSessionResponse() response.state = session.state @@ -563,6 +572,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): continue def CreateNode(self, request, context): + logging.debug("create node: %s", request) session = self.get_session(request.session, context) node_id = request.id @@ -570,7 +580,6 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): if node_type is None: node_type = NodeTypes.DEFAULT.value node_type = NodeTypes(node_type) - logging.info("creating node: %s - %s", node_type.name, request) node_options = NodeOptions(name=request.name, model=request.model) node_options.icon = request.icon @@ -592,6 +601,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def GetNode(self, request, context): + logging.debug("get node: %s", request) session = self.get_session(request.session, context) node = self.get_node(session, request.id, context) response = core_pb2.GetNodeResponse() @@ -633,6 +643,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def EditNode(self, request, context): + logging.debug("edit node: %s", request) session = self.get_session(request.session, context) node_id = request.id @@ -644,7 +655,6 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): lon = request.position.lon alt = request.position.alt node_options.set_location(lat, lon, alt) - logging.debug("updating node(%s) - pos(%s, %s) geo(%s, %s, %s)", node_id, x, y, lat, lon, alt) result = session.update_node(node_id, node_options) response = core_pb2.EditNodeResponse() @@ -652,14 +662,14 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def DeleteNode(self, request, context): - logging.info("delete node: %s", request) + logging.debug("delete node: %s", request) session = self.get_session(request.session, context) response = core_pb2.DeleteNodeResponse() response.result = session.delete_node(request.id) return response def GetNodeLinks(self, request, context): - logging.info("get node links: %s", request) + logging.debug("get node links: %s", request) session = self.get_session(request.session, context) node = self.get_node(session, request.id, context) response = core_pb2.GetNodeLinksResponse() @@ -671,8 +681,8 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def CreateLink(self, request, context): + logging.debug("create link: %s", request) session = self.get_session(request.session, context) - logging.info("adding link: %s", request) node_one = request.link.node_one node_two = request.link.node_two @@ -741,7 +751,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def EditLink(self, request, context): - logging.info("edit link: %s", request) + logging.debug("edit link: %s", request) session = self.get_session(request.session, context) node_one = request.node_one @@ -770,7 +780,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def DeleteLink(self, request, context): - logging.info("delete link: %s", request) + logging.debug("delete link: %s", request) session = self.get_session(request.session, context) node_one = request.node_one @@ -784,6 +794,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def GetHooks(self, request, context): + logging.debug("get hooks: %s", request) session = self.get_session(request.session, context) response = core_pb2.GetHooksResponse() @@ -797,6 +808,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def AddHook(self, request, context): + logging.debug("add hook: %s", request) session = self.get_session(request.session, context) hook = request.hook @@ -806,6 +818,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def GetMobilityConfigs(self, request, context): + logging.debug("get mobility configs: %s", request) session = self.get_session(request.session, context) response = core_pb2.GetMobilityConfigsResponse() @@ -824,6 +837,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def GetMobilityConfig(self, request, context): + logging.debug("get mobility config: %s", request) session = self.get_session(request.session, context) config = session.mobility.get_model_config(request.id, Ns2ScriptedMobility.name) groups = get_config_groups(config, Ns2ScriptedMobility) @@ -832,6 +846,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def SetMobilityConfig(self, request, context): + logging.debug("set mobility config: %s", request) session = self.get_session(request.session, context) session.mobility.set_model_config(request.id, Ns2ScriptedMobility.name, request.config) response = core_pb2.SetMobilityConfigResponse() @@ -839,6 +854,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def MobilityAction(self, request, context): + logging.debug("mobility action: %s", request) session = self.get_session(request.session, context) node = self.get_node(session, request.id, context) @@ -856,6 +872,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def GetServices(self, request, context): + logging.debug("get services: %s", request) response = core_pb2.GetServicesResponse() for service in ServiceManager.services.itervalues(): service_proto = response.services.add() @@ -864,6 +881,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def GetServiceDefaults(self, request, context): + logging.debug("get service defaults: %s", request) session = self.get_session(request.session, context) response = core_pb2.GetServiceDefaultsResponse() @@ -875,6 +893,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def SetServiceDefaults(self, request, context): + logging.debug("set service defaults: %s", request) session = self.get_session(request.session, context) session.services.default_services.clear() for service_defaults in request.defaults: @@ -885,6 +904,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def GetNodeService(self, request, context): + logging.debug("get node service: %s", request) session = self.get_session(request.session, context) service = session.services.get_service(request.id, request.service, default_service=True) @@ -903,6 +923,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def GetNodeServiceFile(self, request, context): + logging.debug("get node service file: %s", request) session = self.get_session(request.session, context) node = self.get_node(session, request.id, context) @@ -920,23 +941,22 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def SetNodeService(self, request, context): + logging.debug("set node service: %s", request) session = self.get_session(request.session, context) # guarantee custom service exists session.services.set_service(request.id, request.service) service = session.services.get_service(request.id, request.service) service.startup = tuple(request.startup) - logging.info("custom startup: %s", service.startup) service.validate = tuple(request.validate) - logging.info("custom validate: %s", service.validate) service.shutdown = tuple(request.shutdown) - logging.info("custom shutdown: %s", service.shutdown) response = core_pb2.SetNodeServiceResponse() response.result = True return response def SetNodeServiceFile(self, request, context): + logging.debug("set node service file: %s", request) session = self.get_session(request.session, context) session.services.set_service_file(request.id, request.service, request.file, request.data) response = core_pb2.SetNodeServiceFileResponse() @@ -944,6 +964,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def ServiceAction(self, request, context): + logging.debug("service action: %s", request) session = self.get_session(request.session, context) node = self.get_node(session, request.id, context) @@ -977,6 +998,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def GetWlanConfig(self, request, context): + logging.debug("get wlan config: %s", request) session = self.get_session(request.session, context) config = session.mobility.get_model_config(request.id, BasicRangeModel.name) groups = get_config_groups(config, BasicRangeModel) @@ -985,6 +1007,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def SetWlanConfig(self, request, context): + logging.debug("set wlan config: %s", request) session = self.get_session(request.session, context) session.mobility.set_model_config(request.id, BasicRangeModel.name, request.config) response = core_pb2.SetWlanConfigResponse() @@ -992,6 +1015,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def GetEmaneConfig(self, request, context): + logging.debug("get emane config: %s", request) session = self.get_session(request.session, context) config = session.emane.get_configs() groups = get_config_groups(config, session.emane.emane_config) @@ -1000,6 +1024,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def SetEmaneConfig(self, request, context): + logging.debug("set emane config: %s", request) session = self.get_session(request.session, context) session.emane.set_configs(request.config) response = core_pb2.SetEmaneConfigResponse() @@ -1007,6 +1032,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def GetEmaneModels(self, request, context): + logging.debug("get emane models: %s", request) session = self.get_session(request.session, context) models = [] @@ -1020,6 +1046,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def GetEmaneModelConfig(self, request, context): + logging.debug("get emane model config: %s", request) session = self.get_session(request.session, context) model = session.emane.models[request.model] config = session.emane.get_model_config(request.id, request.model) @@ -1029,6 +1056,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def SetEmaneModelConfig(self, request, context): + logging.debug("set emane model config: %s", request) session = self.get_session(request.session, context) session.emane.set_model_config(request.id, request.model, request.config) response = core_pb2.SetEmaneModelConfigResponse() @@ -1036,6 +1064,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def GetEmaneModelConfigs(self, request, context): + logging.debug("get emane model configs: %s", request) session = self.get_session(request.session, context) response = core_pb2.GetEmaneModelConfigsResponse() @@ -1053,6 +1082,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def SaveXml(self, request, context): + logging.debug("save xml: %s", request) session = self.get_session(request.session, context) _, temp_path = tempfile.mkstemp() @@ -1066,6 +1096,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): return response def OpenXml(self, request, context): + logging.debug("open xml: %s", request) session = self.coreemu.create_session() session.set_state(EventTypes.CONFIGURATION_STATE) From 16d9009c3f486550a4f52b685330d3f9980cdf9b Mon Sep 17 00:00:00 2001 From: bharnden Date: Wed, 20 Mar 2019 22:11:09 -0700 Subject: [PATCH 29/72] grpc added convenience methods for starting streams, updated logic for bailing out on streams and allow handling them better --- daemon/core/grpc/client.py | 86 ++++++++++++++++---------------------- daemon/core/grpc/server.py | 59 +++++++++++++++++++------- 2 files changed, 79 insertions(+), 66 deletions(-) diff --git a/daemon/core/grpc/client.py b/daemon/core/grpc/client.py index e935ba44..0685499c 100644 --- a/daemon/core/grpc/client.py +++ b/daemon/core/grpc/client.py @@ -21,6 +21,23 @@ def update_proto(obj, **kwargs): setattr(obj, key, value) +def stream_listener(stream, handler): + try: + for event in stream: + handler(event) + except grpc.RpcError as e: + if e.code() == grpc.StatusCode.CANCELLED: + logging.debug("stream closed") + else: + logging.exception("stream error") + + +def start_streamer(stream, handler): + thread = threading.Thread(target=stream_listener, args=(stream, handler)) + thread.daemon = True + thread.start() + + class CoreApiClient(object): def __init__(self, address="localhost:50051"): self.address = address @@ -75,74 +92,38 @@ class CoreApiClient(object): def node_events(self, _id, handler): request = core_pb2.NodeEventsRequest() request.id = _id - - def listen(): - for event in self.stub.NodeEvents(request): - handler(event) - - thread = threading.Thread(target=listen) - thread.daemon = True - thread.start() + stream = self.stub.NodeEvents(request) + start_streamer(stream, handler) def link_events(self, _id, handler): request = core_pb2.LinkEventsRequest() request.id = _id - - def listen(): - for event in self.stub.LinkEvents(request): - handler(event) - - thread = threading.Thread(target=listen) - thread.daemon = True - thread.start() + stream = self.stub.LinkEvents(request) + start_streamer(stream, handler) def session_events(self, _id, handler): request = core_pb2.SessionEventsRequest() request.id = _id - - def listen(): - for event in self.stub.SessionEvents(request): - handler(event) - - thread = threading.Thread(target=listen) - thread.daemon = True - thread.start() + stream = self.stub.SessionEvents(request) + start_streamer(stream, handler) def config_events(self, _id, handler): request = core_pb2.ConfigEventsRequest() request.id = _id - - def listen(): - for event in self.stub.ConfigEvents(request): - handler(event) - - thread = threading.Thread(target=listen) - thread.daemon = True - thread.start() + stream = self.stub.ConfigEvents(request) + start_streamer(stream, handler) def exception_events(self, _id, handler): request = core_pb2.ExceptionEventsRequest() request.id = _id - - def listen(): - for event in self.stub.ExceptionEvents(request): - handler(event) - - thread = threading.Thread(target=listen) - thread.daemon = True - thread.start() + stream = self.stub.ExceptionEvents(request) + start_streamer(stream, handler) def file_events(self, _id, handler): request = core_pb2.FileEventsRequest() request.id = _id - - def listen(): - for event in self.stub.FileEvents(request): - handler(event) - - thread = threading.Thread(target=listen) - thread.daemon = True - thread.start() + stream = self.stub.FileEvents(request) + start_streamer(stream, handler) def create_node(self, session, _type=NodeTypes.DEFAULT, _id=None, node_options=None, emane=None): if not node_options: @@ -492,7 +473,12 @@ def main(): # create session session_data = client.create_session() - client.exception_events(session_data.id, lambda x: print(x)) + client.exception_events(session_data.id, lambda x: print(type(x))) + client.node_events(session_data.id, lambda x: print(type(x))) + client.session_events(session_data.id, lambda x: print(type(x))) + client.link_events(session_data.id, lambda x: print(type(x))) + client.file_events(session_data.id, lambda x: print(type(x))) + client.config_events(session_data.id, lambda x: print(type(x))) print("created session: {}".format(session_data)) print("default services: {}".format(client.get_service_defaults(session_data.id))) print("emane models: {}".format(client.get_emane_models(session_data.id))) @@ -574,5 +560,5 @@ def main(): if __name__ == "__main__": - logging.basicConfig() + logging.basicConfig(level=logging.DEBUG) main() diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index 613b4e77..ec583743 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -1,3 +1,4 @@ +import atexit import logging import os import tempfile @@ -194,16 +195,18 @@ def send_objects(session): session.broadcast_config(config_data) # send session metadata - data_values = "|".join(["%s=%s" % item for item in session.metadata.get_configs().iteritems()]) - data_types = tuple(ConfigDataTypes.STRING.value for _ in session.metadata.get_configs()) - config_data = ConfigData( - message_type=0, - object=session.metadata.name, - type=ConfigFlags.NONE.value, - data_types=data_types, - data_values=data_values - ) - session.broadcast_config(config_data) + configs = session.metadata.get_configs() + if configs: + data_values = "|".join(["%s=%s" % item for item in configs.iteritems()]) + data_types = tuple(ConfigDataTypes.STRING.value for _ in session.metadata.get_configs()) + config_data = ConfigData( + message_type=0, + object=session.metadata.name, + type=ConfigFlags.NONE.value, + data_types=data_types, + data_values=data_values + ) + session.broadcast_config(config_data) logging.debug("informed GUI about %d nodes and %d links", len(nodes_data), len(links_data)) @@ -212,6 +215,18 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): def __init__(self, coreemu): super(CoreApiServer, self).__init__() self.coreemu = coreemu + self.running = True + atexit.register(self._exit_handler) + + def _exit_handler(self): + logging.debug("catching exit, stop running") + self.running = False + + def _is_running(self, context): + return self.running and context.is_active() + + def _cancel_stream(self, context): + context.abort(grpc.StatusCode.CANCELLED, "server stopping") def get_session(self, _id, context): session = self.coreemu.sessions.get(_id) @@ -386,7 +401,7 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): queue = Queue() session.node_handlers.append(lambda x: queue.put(x)) - while context.is_active(): + while self._is_running(context): try: node = queue.get(timeout=1) node_event = core_pb2.NodeEvent() @@ -407,12 +422,14 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): except Empty: continue + self._cancel_stream(context) + def LinkEvents(self, request, context): session = self.get_session(request.id, context) queue = Queue() session.link_handlers.append(lambda x: queue.put(x)) - while context.is_active(): + while self._is_running(context): try: event = queue.get(timeout=1) link_event = core_pb2.LinkEvent() @@ -467,12 +484,14 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): except Empty: continue + self._cancel_stream(context) + def SessionEvents(self, request, context): session = self.get_session(request.id, context) queue = Queue() session.event_handlers.append(lambda x: queue.put(x)) - while context.is_active(): + while self._is_running(context): try: event = queue.get(timeout=1) session_event = core_pb2.SessionEvent() @@ -492,12 +511,14 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): except Empty: continue + self._cancel_stream(context) + def ConfigEvents(self, request, context): session = self.get_session(request.id, context) queue = Queue() session.config_handlers.append(lambda x: queue.put(x)) - while context.is_active(): + while self._is_running(context): try: event = queue.get(timeout=1) config_event = core_pb2.ConfigEvent() @@ -522,12 +543,14 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): except Empty: continue + self._cancel_stream(context) + def ExceptionEvents(self, request, context): session = self.get_session(request.id, context) queue = Queue() session.exception_handlers.append(lambda x: queue.put(x)) - while context.is_active(): + while self._is_running(context): try: event = queue.get(timeout=1) exception_event = core_pb2.ExceptionEvent() @@ -545,12 +568,14 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): except Empty: continue + self._cancel_stream(context) + def FileEvents(self, request, context): session = self.get_session(request.id, context) queue = Queue() session.file_handlers.append(lambda x: queue.put(x)) - while context.is_active(): + while self._is_running(context): try: event = queue.get(timeout=1) file_event = core_pb2.FileEvent() @@ -571,6 +596,8 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): except Empty: continue + self._cancel_stream(context) + def CreateNode(self, request, context): logging.debug("create node: %s", request) session = self.get_session(request.session, context) From 0c0ff95fb6b617b52b441d2937af96718b0602cd Mon Sep 17 00:00:00 2001 From: bharnden Date: Thu, 21 Mar 2019 22:56:33 -0700 Subject: [PATCH 30/72] grpc added listen to server class and updated server/client to specify grpc in their names --- daemon/core/grpc/client.py | 16 ++++++++-------- daemon/core/grpc/server.py | 31 +++++++++++++++---------------- daemon/scripts/core-daemon | 5 +++-- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/daemon/core/grpc/client.py b/daemon/core/grpc/client.py index 0685499c..d208bb25 100644 --- a/daemon/core/grpc/client.py +++ b/daemon/core/grpc/client.py @@ -38,7 +38,7 @@ def start_streamer(stream, handler): thread.start() -class CoreApiClient(object): +class CoreGrpcClient(object): def __init__(self, address="localhost:50051"): self.address = address self.stub = None @@ -463,7 +463,7 @@ class CoreApiClient(object): def main(): xml_file_name = "/tmp/core.xml" - client = CoreApiClient() + client = CoreGrpcClient() with client.context_connect(): if os.path.exists(xml_file_name): response = client.open_xml(xml_file_name) @@ -473,12 +473,12 @@ def main(): # create session session_data = client.create_session() - client.exception_events(session_data.id, lambda x: print(type(x))) - client.node_events(session_data.id, lambda x: print(type(x))) - client.session_events(session_data.id, lambda x: print(type(x))) - client.link_events(session_data.id, lambda x: print(type(x))) - client.file_events(session_data.id, lambda x: print(type(x))) - client.config_events(session_data.id, lambda x: print(type(x))) + client.exception_events(session_data.id, lambda x: print(x)) + client.node_events(session_data.id, lambda x: print(x)) + client.session_events(session_data.id, lambda x: print(x)) + client.link_events(session_data.id, lambda x: print(x)) + client.file_events(session_data.id, lambda x: print(x)) + client.config_events(session_data.id, lambda x: print(x)) print("created session: {}".format(session_data)) print("default services: {}".format(client.get_service_defaults(session_data.id))) print("emane models: {}".format(client.get_emane_models(session_data.id))) diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index ec583743..6a3c35d3 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -211,9 +211,9 @@ def send_objects(session): logging.debug("informed GUI about %d nodes and %d links", len(nodes_data), len(links_data)) -class CoreApiServer(core_pb2_grpc.CoreApiServicer): +class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): def __init__(self, coreemu): - super(CoreApiServer, self).__init__() + super(CoreGrpcServer, self).__init__() self.coreemu = coreemu self.running = True atexit.register(self._exit_handler) @@ -228,6 +228,19 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): def _cancel_stream(self, context): context.abort(grpc.StatusCode.CANCELLED, "server stopping") + def listen(self, address="[::]:50051"): + logging.info("starting grpc api: %s", address) + server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) + core_pb2_grpc.add_CoreApiServicer_to_server(self, server) + server.add_insecure_port(address) + server.start() + + try: + while True: + time.sleep(_ONE_DAY_IN_SECONDS) + except KeyboardInterrupt: + server.stop(0) + def get_session(self, _id, context): session = self.coreemu.sessions.get(_id) if not session: @@ -1142,17 +1155,3 @@ class CoreApiServer(core_pb2_grpc.CoreApiServicer): self.coreemu.delete_session(session.session_id) return response - - -def listen(coreemu, address="[::]:50051"): - logging.info("starting grpc api: %s", address) - server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) - core_pb2_grpc.add_CoreApiServicer_to_server(CoreApiServer(coreemu), server) - server.add_insecure_port(address) - server.start() - - try: - while True: - time.sleep(_ONE_DAY_IN_SECONDS) - except KeyboardInterrupt: - server.stop(0) diff --git a/daemon/scripts/core-daemon b/daemon/scripts/core-daemon index 37608897..f7c56718 100755 --- a/daemon/scripts/core-daemon +++ b/daemon/scripts/core-daemon @@ -17,7 +17,7 @@ from core import constants from core import enumerations from core.corehandlers import CoreHandler from core.coreserver import CoreServer -from core.grpc.server import listen +from core.grpc.server import CoreGrpcServer from core.misc.utils import close_onexec load_logging_config() @@ -56,7 +56,8 @@ def cored(cfg): # initialize grpc api if cfg["grpc"] == "True": - grpc_thread = threading.Thread(target=listen, args=(server.coreemu,)) + api_server = CoreGrpcServer(server.coreemu) + grpc_thread = threading.Thread(target=api_server.listen) grpc_thread.daemon = True grpc_thread.start() From 3498a59ed58d7754022156a34e4026ec451e3baf Mon Sep 17 00:00:00 2001 From: bharnden Date: Thu, 21 Mar 2019 23:29:26 -0700 Subject: [PATCH 31/72] grpc added initial tests and fixture to support them --- daemon/core/grpc/server.py | 11 ++++---- daemon/scripts/core-daemon | 4 +-- daemon/tests/conftest.py | 17 +++++++++++-- daemon/tests/test_grpc.py | 51 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 74 insertions(+), 9 deletions(-) create mode 100644 daemon/tests/test_grpc.py diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index 6a3c35d3..66517bad 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -216,6 +216,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): super(CoreGrpcServer, self).__init__() self.coreemu = coreemu self.running = True + self.server = None atexit.register(self._exit_handler) def _exit_handler(self): @@ -230,16 +231,16 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): def listen(self, address="[::]:50051"): logging.info("starting grpc api: %s", address) - server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) - core_pb2_grpc.add_CoreApiServicer_to_server(self, server) - server.add_insecure_port(address) - server.start() + self.server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) + core_pb2_grpc.add_CoreApiServicer_to_server(self, self.server) + self.server.add_insecure_port(address) + self.server.start() try: while True: time.sleep(_ONE_DAY_IN_SECONDS) except KeyboardInterrupt: - server.stop(0) + self.server.stop(None) def get_session(self, _id, context): session = self.coreemu.sessions.get(_id) diff --git a/daemon/scripts/core-daemon b/daemon/scripts/core-daemon index f7c56718..a1c14cec 100755 --- a/daemon/scripts/core-daemon +++ b/daemon/scripts/core-daemon @@ -56,8 +56,8 @@ def cored(cfg): # initialize grpc api if cfg["grpc"] == "True": - api_server = CoreGrpcServer(server.coreemu) - grpc_thread = threading.Thread(target=api_server.listen) + grpc_server = CoreGrpcServer(server.coreemu) + grpc_thread = threading.Thread(target=grpc_server.listen) grpc_thread.daemon = True grpc_thread.start() diff --git a/daemon/tests/conftest.py b/daemon/tests/conftest.py index d087e2e6..b03dbd1f 100644 --- a/daemon/tests/conftest.py +++ b/daemon/tests/conftest.py @@ -3,6 +3,7 @@ Unit test fixture module. """ import os +import threading import pytest from mock.mock import MagicMock @@ -26,6 +27,8 @@ from core.enumerations import LinkTypes from core.enumerations import MessageFlags from core.enumerations import NodeTlvs from core.enumerations import NodeTypes +from core.grpc.client import CoreGrpcClient +from core.grpc.server import CoreGrpcServer from core.misc import ipaddress from core.misc.ipaddress import MacAddress from core.service import ServiceManager @@ -204,6 +207,18 @@ class CoreServerTest(object): self.server.server_close() +@pytest.fixture +def grpc_server(): + coremu = CoreEmu() + grpc_server = CoreGrpcServer(coremu) + thread = threading.Thread(target=grpc_server.listen) + thread.daemon = True + thread.start() + yield grpc_server + coremu.shutdown() + grpc_server.server.stop(None) + + @pytest.fixture def session(): # use coreemu and create a session @@ -242,8 +257,6 @@ def cored(): # cleanup server.shutdown() - # - # cleanup services ServiceManager.services.clear() diff --git a/daemon/tests/test_grpc.py b/daemon/tests/test_grpc.py new file mode 100644 index 00000000..fd729fda --- /dev/null +++ b/daemon/tests/test_grpc.py @@ -0,0 +1,51 @@ +import os +import time + +import pytest + +from core.grpc import core_pb2 +from core.enumerations import NodeTypes +from core.grpc.client import CoreGrpcClient + +MODELS = [ + "router", + "host", + "PC", + "mdr", +] + +NET_TYPES = [ + NodeTypes.SWITCH, + NodeTypes.HUB, + NodeTypes.WIRELESS_LAN +] + + +class TestGrpc: + def test_create_session(self, grpc_server): + # given + client = CoreGrpcClient() + + # when + with client.context_connect(): + response = client.create_session() + + # then + assert isinstance(response.id, int) + assert isinstance(response.state, int) + session = grpc_server.coreemu.sessions.get(response.id) + assert session is not None + assert session.state == response.state + + def test_delete_session(self, grpc_server): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + + # then + with client.context_connect(): + response = client.delete_session(session.session_id) + + # then + assert response.result is True + assert grpc_server.coreemu.sessions.get(session.session_id) is None From 1f3e72e014e305e3387816810df088c484975d50 Mon Sep 17 00:00:00 2001 From: bharnden Date: Fri, 22 Mar 2019 13:45:53 -0700 Subject: [PATCH 32/72] grpc create session can now specify id, updated all session proto to use SessionState for state, added suite for session testing for grpc --- daemon/core/grpc/client.py | 5 +- daemon/core/grpc/server.py | 2 +- daemon/proto/core.proto | 8 +-- daemon/tests/conftest.py | 3 +- daemon/tests/test_grpc.py | 123 ++++++++++++++++++++++++++++++++++++- 5 files changed, 130 insertions(+), 11 deletions(-) diff --git a/daemon/core/grpc/client.py b/daemon/core/grpc/client.py index d208bb25..f591f297 100644 --- a/daemon/core/grpc/client.py +++ b/daemon/core/grpc/client.py @@ -44,8 +44,9 @@ class CoreGrpcClient(object): self.stub = None self.channel = None - def create_session(self): - return self.stub.CreateSession(core_pb2.CreateSessionRequest()) + def create_session(self, _id=None): + request = core_pb2.CreateSessionRequest(id=_id) + return self.stub.CreateSession(request) def delete_session(self, _id): request = core_pb2.DeleteSessionRequest() diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index 66517bad..1d2ac141 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -256,7 +256,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): def CreateSession(self, request, context): logging.debug("create session: %s", request) - session = self.coreemu.create_session() + session = self.coreemu.create_session(request.id) session.set_state(EventTypes.DEFINITION_STATE) # default set session location diff --git a/daemon/proto/core.proto b/daemon/proto/core.proto index 7b06ce84..bc1b21ad 100644 --- a/daemon/proto/core.proto +++ b/daemon/proto/core.proto @@ -121,12 +121,12 @@ service CoreApi { // rpc request/response messages message CreateSessionRequest { - + int32 id = 1; } message CreateSessionResponse { int32 id = 1; - int32 state = 2; + SessionState state = 2; } message DeleteSessionRequest { @@ -149,7 +149,7 @@ message GetSessionRequest { } message GetSessionResponse { - int32 state = 1; + SessionState state = 1; repeated Node nodes = 2; repeated Link links = 3; } @@ -713,7 +713,7 @@ message ConfigOption { message Session { int32 id = 1; - int32 state = 2; + SessionState state = 2; int32 nodes = 3; } diff --git a/daemon/tests/conftest.py b/daemon/tests/conftest.py index b03dbd1f..5b98fe7c 100644 --- a/daemon/tests/conftest.py +++ b/daemon/tests/conftest.py @@ -4,6 +4,7 @@ Unit test fixture module. import os import threading +import time import pytest from mock.mock import MagicMock @@ -27,7 +28,6 @@ from core.enumerations import LinkTypes from core.enumerations import MessageFlags from core.enumerations import NodeTlvs from core.enumerations import NodeTypes -from core.grpc.client import CoreGrpcClient from core.grpc.server import CoreGrpcServer from core.misc import ipaddress from core.misc.ipaddress import MacAddress @@ -214,6 +214,7 @@ def grpc_server(): thread = threading.Thread(target=grpc_server.listen) thread.daemon = True thread.start() + time.sleep(0.1) yield grpc_server coremu.shutdown() grpc_server.server.stop(None) diff --git a/daemon/tests/test_grpc.py b/daemon/tests/test_grpc.py index fd729fda..9454dc7f 100644 --- a/daemon/tests/test_grpc.py +++ b/daemon/tests/test_grpc.py @@ -4,7 +4,7 @@ import time import pytest from core.grpc import core_pb2 -from core.enumerations import NodeTypes +from core.enumerations import NodeTypes, EventTypes from core.grpc.client import CoreGrpcClient MODELS = [ @@ -22,13 +22,14 @@ NET_TYPES = [ class TestGrpc: - def test_create_session(self, grpc_server): + @pytest.mark.parametrize("session_id", [None, 6013]) + def test_create_session(self, grpc_server, session_id): # given client = CoreGrpcClient() # when with client.context_connect(): - response = client.create_session() + response = client.create_session(session_id) # then assert isinstance(response.id, int) @@ -36,6 +37,9 @@ class TestGrpc: session = grpc_server.coreemu.sessions.get(response.id) assert session is not None assert session.state == response.state + if session_id is not None: + assert response.id == session_id + assert session.session_id == session_id def test_delete_session(self, grpc_server): # given @@ -49,3 +53,116 @@ class TestGrpc: # then assert response.result is True assert grpc_server.coreemu.sessions.get(session.session_id) is None + + def test_get_session(self, grpc_server): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + session.add_node() + session.set_state(EventTypes.DEFINITION_STATE) + + # then + with client.context_connect(): + response = client.get_session(session.session_id) + + # then + assert response.state == core_pb2.DEFINITION + assert len(response.nodes) == 1 + assert len(response.links) == 0 + + def test_get_sessions(self, grpc_server): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + + # then + with client.context_connect(): + response = client.get_sessions() + + # then + found_session = None + for current_session in response.sessions: + if current_session.id == session.session_id: + found_session = current_session + break + assert len(response.sessions) == 1 + assert found_session is not None + + def test_get_session_options(self, grpc_server): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + + # then + with client.context_connect(): + response = client.get_session_options(session.session_id) + + # then + assert len(response.groups) > 0 + + def test_get_session_location(self, grpc_server): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + + # then + with client.context_connect(): + response = client.get_session_location(session.session_id) + + # then + assert response.scale == 1.0 + assert response.position.x == 0 + assert response.position.y == 0 + assert response.position.z == 0 + assert response.position.lat == 0 + assert response.position.lon == 0 + assert response.position.alt == 0 + + def test_set_session_location(self, grpc_server): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + + # then + scale = 2 + xyz = (1, 1, 1) + lat_lon_alt = (1, 1, 1) + with client.context_connect(): + response = client.set_session_location( + session.session_id, + x=xyz[0], y=xyz[1], z=xyz[2], + lat=lat_lon_alt[0], lon=lat_lon_alt[1], alt=lat_lon_alt[2], + scale=scale + ) + + # then + assert response.result is True + assert session.location.refxyz == xyz + assert session.location.refscale == scale + assert session.location.refgeo == lat_lon_alt + + def test_set_session_options(self, grpc_server): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + + # then + with client.context_connect(): + response = client.set_session_options(session.session_id, {"preservedir": "1"}) + + # then + assert response.result is True + assert session.options.get_config("preservedir") == "1" + + def test_set_session_state(self, grpc_server): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + + # then + with client.context_connect(): + response = client.set_session_state(session.session_id, EventTypes.DEFINITION_STATE) + + # then + assert response.result is True + assert session.state == EventTypes.DEFINITION_STATE.value From c6cfe1a8f4aeaced70196f61b61ef715539b3bf5 Mon Sep 17 00:00:00 2001 From: bharnden Date: Fri, 22 Mar 2019 14:02:18 -0700 Subject: [PATCH 33/72] grpc update to get session and get sessions --- daemon/core/grpc/server.py | 14 +++++++------- daemon/proto/core.proto | 13 +++++++++---- daemon/tests/test_grpc.py | 12 +++++++----- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index 1d2ac141..959a2509 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -279,10 +279,10 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): response = core_pb2.GetSessionsResponse() for session_id in self.coreemu.sessions: session = self.coreemu.sessions[session_id] - session_data = response.sessions.add() - session_data.id = session_id - session_data.state = session.state - session_data.nodes = session.get_node_count() + session_summary = response.sessions.add() + session_summary.id = session_id + session_summary.state = session.state + session_summary.nodes = session.get_node_count() return response def GetSessionLocation(self, request, context): @@ -368,7 +368,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): logging.debug("get session: %s", request) session = self.get_session(request.id, context) response = core_pb2.GetSessionResponse() - response.state = session.state + response.session.state = session.state for node_id in session.objects: node = session.objects[node_id] @@ -376,7 +376,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): if not isinstance(node.objid, int): continue - node_proto = response.nodes.add() + node_proto = response.session.nodes.add() node_proto.id = node.objid node_proto.name = node.name node_proto.type = nodeutils.get_node_type(node.__class__).value @@ -405,7 +405,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): links_data = node.all_link_data(0) for link_data in links_data: - link = response.links.add() + link = response.session.links.add() convert_link(session, link_data, link) return response diff --git a/daemon/proto/core.proto b/daemon/proto/core.proto index bc1b21ad..67cfcbce 100644 --- a/daemon/proto/core.proto +++ b/daemon/proto/core.proto @@ -141,7 +141,7 @@ message GetSessionsRequest { } message GetSessionsResponse { - repeated Session sessions = 1; + repeated SessionSummary sessions = 1; } message GetSessionRequest { @@ -149,9 +149,7 @@ message GetSessionRequest { } message GetSessionResponse { - SessionState state = 1; - repeated Node nodes = 2; - repeated Link links = 3; + Session session = 1; } message GetSessionOptionsRequest { @@ -712,6 +710,13 @@ message ConfigOption { } message Session { + int32 id = 1; + SessionState state = 2; + repeated Node nodes = 3; + repeated Link links = 4; +} + +message SessionSummary { int32 id = 1; SessionState state = 2; int32 nodes = 3; diff --git a/daemon/tests/test_grpc.py b/daemon/tests/test_grpc.py index 9454dc7f..973b4204 100644 --- a/daemon/tests/test_grpc.py +++ b/daemon/tests/test_grpc.py @@ -66,9 +66,9 @@ class TestGrpc: response = client.get_session(session.session_id) # then - assert response.state == core_pb2.DEFINITION - assert len(response.nodes) == 1 - assert len(response.links) == 0 + assert response.session.state == core_pb2.DEFINITION + assert len(response.session.nodes) == 1 + assert len(response.session.links) == 0 def test_get_sessions(self, grpc_server): # given @@ -147,12 +147,14 @@ class TestGrpc: session = grpc_server.coreemu.create_session() # then + option = "enablerj45" + value = "1" with client.context_connect(): - response = client.set_session_options(session.session_id, {"preservedir": "1"}) + response = client.set_session_options(session.session_id, {option: value}) # then assert response.result is True - assert session.options.get_config("preservedir") == "1" + assert session.options.get_config(option) == value def test_set_session_state(self, grpc_server): # given From 5c2521bc126f13d1ff12cb9dd49fe60cc69fcc2c Mon Sep 17 00:00:00 2001 From: bharnden Date: Fri, 22 Mar 2019 14:32:39 -0700 Subject: [PATCH 34/72] grpc cleaned up session rpc calls to leverage proto constructors --- daemon/core/grpc/client.py | 26 +++----- daemon/core/grpc/server.py | 129 +++++++++++-------------------------- 2 files changed, 45 insertions(+), 110 deletions(-) diff --git a/daemon/core/grpc/client.py b/daemon/core/grpc/client.py index f591f297..8005d30f 100644 --- a/daemon/core/grpc/client.py +++ b/daemon/core/grpc/client.py @@ -49,45 +49,35 @@ class CoreGrpcClient(object): return self.stub.CreateSession(request) def delete_session(self, _id): - request = core_pb2.DeleteSessionRequest() - request.id = _id + request = core_pb2.DeleteSessionRequest(id=_id) return self.stub.DeleteSession(request) def get_sessions(self): return self.stub.GetSessions(core_pb2.GetSessionsRequest()) def get_session(self, _id): - request = core_pb2.GetSessionRequest() - request.id = _id + request = core_pb2.GetSessionRequest(id=_id) return self.stub.GetSession(request) def get_session_options(self, _id): - request = core_pb2.GetSessionOptionsRequest() - request.id = _id + request = core_pb2.GetSessionOptionsRequest(id=_id) return self.stub.GetSessionOptions(request) def set_session_options(self, _id, config): - request = core_pb2.SetSessionOptionsRequest() - request.id = _id - request.config.update(config) + request = core_pb2.SetSessionOptionsRequest(id=_id, config=config) return self.stub.SetSessionOptions(request) def get_session_location(self, _id): - request = core_pb2.GetSessionLocationRequest() - request.id = _id + request = core_pb2.GetSessionLocationRequest(id=_id) return self.stub.GetSessionLocation(request) def set_session_location(self, _id, x=None, y=None, z=None, lat=None, lon=None, alt=None, scale=None): - request = core_pb2.SetSessionLocationRequest() - request.id = _id - update_proto(request.position, x=x, y=y, z=z, lat=lat, lon=lon, alt=alt) - update_proto(request, scale=scale) + position = core_pb2.Position(x=x, y=y, z=z, lat=lat, lon=lon, alt=alt) + request = core_pb2.SetSessionLocationRequest(id=_id, position=position, scale=scale) return self.stub.SetSessionLocation(request) def set_session_state(self, _id, state): - request = core_pb2.SetSessionStateRequest() - request.id = _id - request.state = state.value + request = core_pb2.SetSessionStateRequest(id=_id, state=state.value) return self.stub.SetSessionState(request) def node_events(self, _id, handler): diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index 959a2509..56ca86b4 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -61,40 +61,27 @@ def get_config_groups(config, configurable_options): return groups -def convert_link(session, link_data, link): +def convert_link(session, link_data, links): + interface_one = None if link_data.interface1_id is not None: node = session.get_object(link_data.node1_id) interface = node.netif(link_data.interface1_id) - link.interface_one.id = link_data.interface1_id - link.interface_one.name = interface.name - update_proto( - link.interface_one, - mac=convert_value(link_data.interface1_mac), - ip4=convert_value(link_data.interface1_ip4), - ip4mask=link_data.interface1_ip4_mask, - ip6=convert_value(link_data.interface1_ip6), - ip6mask=link_data.interface1_ip6_mask - ) + interface_one = core_pb2.Interface( + id=link_data.interface1_id, name=interface.name, mac=convert_value(link_data.interface1_mac), + ip4=convert_value(link_data.interface1_ip4), ip4mask=link_data.interface1_ip4_mask, + ip6=convert_value(link_data.interface1_ip6), ip6mask=link_data.interface1_ip6_mask) + interface_two = None if link_data.interface2_id is not None: node = session.get_object(link_data.node2_id) interface = node.netif(link_data.interface2_id) - link.interface_two.id = link_data.interface2_id - link.interface_two.name = interface.name - update_proto( - link.interface_two, - mac=convert_value(link_data.interface2_mac), - ip4=convert_value(link_data.interface2_ip4), - ip4mask=link_data.interface2_ip4_mask, - ip6=convert_value(link_data.interface2_ip6), - ip6mask=link_data.interface2_ip6_mask + interface_two = core_pb2.Interface( + id=link_data.interface2_id, name=interface.name, mac=convert_value(link_data.interface2_mac), + ip4=convert_value(link_data.interface2_ip4), ip4mask=link_data.interface2_ip4_mask, + ip6=convert_value(link_data.interface2_ip6), ip6mask=link_data.interface2_ip6_mask ) - link.node_one = link_data.node1_id - link.node_two = link_data.node2_id - link.type = link_data.link_type - update_proto( - link.options, + options = core_pb2.LinkOptions( opaque=link_data.opaque, jitter=link_data.jitter, key=link_data.key, @@ -108,6 +95,11 @@ def convert_link(session, link_data, link): unidirectional=link_data.unidirectional ) + links.add( + type=link_data.link_type, node_one=link_data.node1_id, node_two=link_data.node2_id, + interface_one=interface_one, interface_two=interface_two, options=options + ) + def send_objects(session): time.sleep(1) @@ -258,31 +250,21 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): logging.debug("create session: %s", request) session = self.coreemu.create_session(request.id) session.set_state(EventTypes.DEFINITION_STATE) - - # default set session location session.location.setrefgeo(47.57917, -122.13232, 2.0) session.location.refscale = 150000.0 - - response = core_pb2.CreateSessionResponse() - response.id = session.session_id - response.state = session.state - return response + return core_pb2.CreateSessionResponse(id=session.session_id, state=session.state) def DeleteSession(self, request, context): logging.debug("delete session: %s", request) - response = core_pb2.DeleteSessionResponse() - response.result = self.coreemu.delete_session(request.id) - return response + result = self.coreemu.delete_session(request.id) + return core_pb2.DeleteSessionResponse(result=result) def GetSessions(self, request, context): logging.debug("get sessions: %s", request) response = core_pb2.GetSessionsResponse() for session_id in self.coreemu.sessions: session = self.coreemu.sessions[session_id] - session_summary = response.sessions.add() - session_summary.id = session_id - session_summary.state = session.state - session_summary.nodes = session.get_node_count() + response.sessions.add(id=session_id, state=session.state, nodes=session.get_node_count()) return response def GetSessionLocation(self, request, context): @@ -290,34 +272,19 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): session = self.get_session(request.id, context) x, y, z = session.location.refxyz lat, lon, alt = session.location.refgeo - response = core_pb2.GetSessionLocationResponse() - update_proto( - response.position, - x=x, - y=y, - z=z, - lat=lat, - lon=lon, - alt=alt - ) - update_proto(response, scale=session.location.refscale) - return response + position = core_pb2.Position(x=x, y=y, z=z, lat=lat, lon=lon, alt=alt) + return core_pb2.GetSessionLocationResponse(position=position, scale=session.location.refscale) def SetSessionLocation(self, request, context): logging.debug("set session location: %s", request) session = self.get_session(request.id, context) - session.location.refxyz = (request.position.x, request.position.y, request.position.z) session.location.setrefgeo(request.position.lat, request.position.lon, request.position.alt) session.location.refscale = request.scale - - response = core_pb2.SetSessionLocationResponse() - response.result = True - return response + return core_pb2.SetSessionLocationResponse(result=True) def SetSessionState(self, request, context): logging.debug("set session state: %s", request) - response = core_pb2.SetSessionStateResponse() session = self.get_session(request.id, context) try: @@ -325,7 +292,6 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): session.set_state(state) if state == EventTypes.INSTANTIATION_STATE: - # create session directory if it does not exist if not os.path.exists(session.session_dir): os.mkdir(session.session_dir) session.instantiate() @@ -336,79 +302,58 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): elif state == EventTypes.DEFINITION_STATE: session.clear() - response.result = True + result = True except KeyError: - response.result = False + result = False - return response + return core_pb2.SetSessionStateResponse(result=result) def GetSessionOptions(self, request, context): logging.debug("get session options: %s", request) session = self.get_session(request.id, context) - config = session.options.get_configs() defaults = session.options.default_values() defaults.update(config) - groups = get_config_groups(defaults, session.options) - - response = core_pb2.GetSessionOptionsResponse() - response.groups.extend(groups) - return response + return core_pb2.GetSessionOptionsResponse(groups=groups) def SetSessionOptions(self, request, context): logging.debug("set session options: %s", request) session = self.get_session(request.id, context) session.options.set_configs(request.config) - response = core_pb2.SetSessionOptionsResponse() - response.result = True - return response + return core_pb2.SetSessionOptionsResponse(result=True) def GetSession(self, request, context): logging.debug("get session: %s", request) session = self.get_session(request.id, context) - response = core_pb2.GetSessionResponse() - response.session.state = session.state + session_proto = core_pb2.Session(state=session.state) for node_id in session.objects: node = session.objects[node_id] - if not isinstance(node.objid, int): continue - node_proto = response.session.nodes.add() - node_proto.id = node.objid - node_proto.name = node.name - node_proto.type = nodeutils.get_node_type(node.__class__).value + node_type = nodeutils.get_node_type(node.__class__).value model = getattr(node, "type", None) - if model is not None: - node_proto.model = model - - update_proto( - node_proto.position, - x=node.position.x, - y=node.position.y, - z=node.position.z - ) + position = core_pb2.Position(x=node.position.x, y=node.position.y, z=node.position.z) services = getattr(node, "services", []) if services is None: services = [] services = [x.name for x in services] - node_proto.services.extend(services) emane_model = None if nodeutils.is_node(node, NodeTypes.EMANE): emane_model = node.model.name - if emane_model is not None: - node_proto.emane = emane_model + + session_proto.nodes.add(id=node.objid, name=node.name, emane=emane_model, model=model, + type=node_type, position=position, services=services) links_data = node.all_link_data(0) for link_data in links_data: - link = response.session.links.add() - convert_link(session, link_data, link) + convert_link(session, link_data, session_proto.links) - return response + return core_pb2.GetSessionResponse(session=session_proto) def NodeEvents(self, request, context): session = self.get_session(request.id, context) From 4a6a462d48eb2042f3163347e36c49c81e2599dc Mon Sep 17 00:00:00 2001 From: bharnden Date: Fri, 22 Mar 2019 15:11:40 -0700 Subject: [PATCH 35/72] grpc updated client to leverage constructors throughout and remove update_proto --- daemon/core/grpc/client.py | 268 ++++++++++--------------------------- daemon/core/grpc/server.py | 8 +- 2 files changed, 74 insertions(+), 202 deletions(-) diff --git a/daemon/core/grpc/client.py b/daemon/core/grpc/client.py index 8005d30f..d2a7f222 100644 --- a/daemon/core/grpc/client.py +++ b/daemon/core/grpc/client.py @@ -13,14 +13,6 @@ from core.emulator.emudata import NodeOptions, IpPrefixes, InterfaceData, LinkOp from core.enumerations import NodeTypes, LinkTypes, EventTypes -def update_proto(obj, **kwargs): - for key in kwargs: - value = kwargs[key] - if value is not None: - logging.info("setting proto key(%s) value(%s)", key, value) - setattr(obj, key, value) - - def stream_listener(stream, handler): try: for event in stream: @@ -81,137 +73,84 @@ class CoreGrpcClient(object): return self.stub.SetSessionState(request) def node_events(self, _id, handler): - request = core_pb2.NodeEventsRequest() - request.id = _id + request = core_pb2.NodeEventsRequest(id=_id) stream = self.stub.NodeEvents(request) start_streamer(stream, handler) def link_events(self, _id, handler): - request = core_pb2.LinkEventsRequest() - request.id = _id + request = core_pb2.LinkEventsRequest(id=_id) stream = self.stub.LinkEvents(request) start_streamer(stream, handler) def session_events(self, _id, handler): - request = core_pb2.SessionEventsRequest() - request.id = _id + request = core_pb2.SessionEventsRequest(id=_id) stream = self.stub.SessionEvents(request) start_streamer(stream, handler) def config_events(self, _id, handler): - request = core_pb2.ConfigEventsRequest() - request.id = _id + request = core_pb2.ConfigEventsRequest(id=_id) stream = self.stub.ConfigEvents(request) start_streamer(stream, handler) def exception_events(self, _id, handler): - request = core_pb2.ExceptionEventsRequest() - request.id = _id + request = core_pb2.ExceptionEventsRequest(id=_id) stream = self.stub.ExceptionEvents(request) start_streamer(stream, handler) def file_events(self, _id, handler): - request = core_pb2.FileEventsRequest() - request.id = _id + request = core_pb2.FileEventsRequest(id=_id) stream = self.stub.FileEvents(request) start_streamer(stream, handler) def create_node(self, session, _type=NodeTypes.DEFAULT, _id=None, node_options=None, emane=None): if not node_options: node_options = NodeOptions() - - request = core_pb2.CreateNodeRequest() - request.session = session - request.type = _type.value - update_proto( - request, - id=_id, - name=node_options.name, - model=node_options.model, - icon=node_options.icon, - opaque=node_options.opaque, - emane=emane - ) - update_proto( - request.position, - x=node_options.x, - y=node_options.y, - lat=node_options.lat, - lon=node_options.lon, - alt=node_options.alt - ) - request.services.extend(node_options.services) + position = core_pb2.Position( + x=node_options.x, y=node_options.y, + lat=node_options.lat, lon=node_options.lon, alt=node_options.alt) + request = core_pb2.CreateNodeRequest( + session=session, type=_type.value, name=node_options.name, + model=node_options.model, icon=node_options.icon, services=node_options.services, + opaque=node_options.opaque, emane=emane, position=position) return self.stub.CreateNode(request) def get_node(self, session, _id): - request = core_pb2.GetNodeRequest() - request.session = session - request.id = _id + request = core_pb2.GetNodeRequest(session=session, id=_id) return self.stub.GetNode(request) def edit_node(self, session, _id, node_options): - request = core_pb2.EditNodeRequest() - request.session = session - request.id = _id - update_proto( - request.position, - x=node_options.x, - y=node_options.y, - lat=node_options.lat, - lon=node_options.lon, - alt=node_options.alt - ) + position = core_pb2.Position( + x=node_options.x, y=node_options.y, + lat=node_options.lat, lon=node_options.lon, alt=node_options.alt) + request = core_pb2.EditNodeRequest(session=session, id=_id, position=position) return self.stub.EditNode(request) def delete_node(self, session, _id): - request = core_pb2.DeleteNodeRequest() - request.session = session - request.id = _id + request = core_pb2.DeleteNodeRequest(session=session, id=_id) return self.stub.DeleteNode(request) def get_node_links(self, session, _id): - request = core_pb2.GetNodeLinksRequest() - request.session = session - request.id = _id + request = core_pb2.GetNodeLinksRequest(session=session, id=_id) return self.stub.GetNodeLinks(request) def create_link(self, session, node_one, node_two, interface_one=None, interface_two=None, link_options=None): - request = core_pb2.CreateLinkRequest() - request.session = session - update_proto( - request.link, - node_one=node_one, - node_two=node_two, - type=LinkTypes.WIRED.value - ) - + interface_one_proto = None if interface_one is not None: - update_proto( - request.link.interface_one, - id=interface_one.id, - name=interface_one.name, - mac=interface_one.mac, - ip4=interface_one.ip4, - ip4mask=interface_one.ip4_mask, - ip6=interface_one.ip6, - ip6mask=interface_one.ip6_mask - ) + interface_one_proto = core_pb2.Interface( + id=interface_one.id, name=interface_one.name, mac=interface_one.mac, + ip4=interface_one.ip4, ip4mask=interface_one.ip4_mask, + ip6=interface_one.ip6, ip6mask=interface_one.ip6_mask) + interface_two_proto = None if interface_two is not None: - update_proto( - request.link.interface_two, - id=interface_two.id, - name=interface_two.name, - mac=interface_two.mac, - ip4=interface_two.ip4, - ip4mask=interface_two.ip4_mask, - ip6=interface_two.ip6, - ip6mask=interface_two.ip6_mask - ) + interface_two_proto = core_pb2.Interface( + id=interface_two.id, name=interface_two.name, mac=interface_two.mac, + ip4=interface_two.ip4, ip4mask=interface_two.ip4_mask, + ip6=interface_two.ip6, ip6mask=interface_two.ip6_mask) + options = None if link_options is not None: - update_proto( - request.link.options, + options = core_pb2.LinkOptions( delay=link_options.delay, bandwidth=link_options.bandwidth, per=link_options.per, @@ -225,20 +164,14 @@ class CoreGrpcClient(object): opaque=link_options.opaque ) + link = core_pb2.Link( + node_one=node_one, node_two=node_two, type=LinkTypes.WIRED.value, + interface_one=interface_one_proto, interface_two=interface_two_proto, options=options) + request = core_pb2.CreateLinkRequest(session=session, link=link) return self.stub.CreateLink(request) def edit_link(self, session, node_one, node_two, link_options, interface_one=None, interface_two=None): - request = core_pb2.EditLinkRequest() - request.session = session - request.node_one = node_one - request.node_two = node_two - update_proto( - request, - interface_one=interface_one, - interface_two=interface_two - ) - update_proto( - request.options, + options = core_pb2.LinkOptions( delay=link_options.delay, bandwidth=link_options.bandwidth, per=link_options.per, @@ -251,56 +184,40 @@ class CoreGrpcClient(object): key=link_options.key, opaque=link_options.opaque ) + request = core_pb2.EditLinkRequest( + session=session, node_one=node_one, node_two=node_two, options=options, + interface_one=interface_one, interface_two=interface_two) return self.stub.EditLink(request) def delete_link(self, session, node_one, node_two, interface_one=None, interface_two=None): - request = core_pb2.DeleteLinkRequest() - request.session = session - request.node_one = node_one - request.node_two = node_two - update_proto( - request, - interface_one=interface_one, - interface_two=interface_two - ) + request = core_pb2.DeleteLinkRequest( + session=session, node_one=node_one, node_two=node_two, + interface_one=interface_one, interface_two=interface_two) return self.stub.DeleteLink(request) def get_hooks(self, session): - request = core_pb2.GetHooksRequest() - request.session = session + request = core_pb2.GetHooksRequest(session=session) return self.stub.GetHooks(request) def add_hook(self, session, state, file_name, file_data): - request = core_pb2.AddHookRequest() - request.session = session - request.hook.state = state.value - request.hook.file = file_name - request.hook.data = file_data + hook = core_pb2.Hook(state=state.value, file=file_name, data=file_data) + request = core_pb2.AddHookRequest(session=session, hook=hook) return self.stub.AddHook(request) def get_mobility_configs(self, session): - request = core_pb2.GetMobilityConfigsRequest() - request.session = session + request = core_pb2.GetMobilityConfigsRequest(session=session) return self.stub.GetMobilityConfigs(request) def get_mobility_config(self, session, _id): - request = core_pb2.GetMobilityConfigRequest() - request.session = session - request.id = _id + request = core_pb2.GetMobilityConfigRequest(session=session, id=_id) return self.stub.GetMobilityConfig(request) def set_mobility_config(self, session, _id, config): - request = core_pb2.SetMobilityConfigRequest() - request.session = session - request.id = _id - request.config.update(config) + request = core_pb2.SetMobilityConfigRequest(session=session, id=_id, config=config) return self.stub.SetMobilityConfig(request) def mobility_action(self, session, _id, action): - request = core_pb2.MobilityActionRequest() - request.session = session - request.id = _id - request.action = action + request = core_pb2.MobilityActionRequest(session=session, id=_id, action=action) return self.stub.MobilityAction(request) def get_services(self): @@ -308,119 +225,76 @@ class CoreGrpcClient(object): return self.stub.GetServices(request) def get_service_defaults(self, session): - request = core_pb2.GetServiceDefaultsRequest() - request.session = session + request = core_pb2.GetServiceDefaultsRequest(session=session) return self.stub.GetServiceDefaults(request) def set_service_defaults(self, session, service_defaults): - request = core_pb2.SetServiceDefaultsRequest() - request.session = session + request = core_pb2.SetServiceDefaultsRequest(session=session) for node_type in service_defaults: services = service_defaults[node_type] - service_defaults_proto = request.defaults.add() - service_defaults_proto.node_type = node_type - service_defaults_proto.services.extend(services) - + request.defaults.add(node_type=node_type, services=services) return self.stub.SetServiceDefaults(request) def get_node_service(self, session, _id, service): - request = core_pb2.GetNodeServiceRequest() - request.session = session - request.id = _id - request.service = service + request = core_pb2.GetNodeServiceRequest(session=session, id=_id, service=service) return self.stub.GetNodeService(request) def get_node_service_file(self, session, _id, service, file_name): - request = core_pb2.GetNodeServiceFileRequest() - request.session = session - request.id = _id - request.service = service - request.file = file_name + request = core_pb2.GetNodeServiceFileRequest(session=session, id=_id, service=service, file=file_name) return self.stub.GetNodeServiceFile(request) def set_node_service(self, session, _id, service, startup, validate, shutdown): - request = core_pb2.SetNodeServiceRequest() - request.session = session - request.id = _id - request.service = service - request.startup.extend(startup) - request.validate.extend(validate) - request.shutdown.extend(shutdown) + request = core_pb2.SetNodeServiceRequest( + session=session, id=_id, service=service, startup=startup, validate=validate, shutdown=shutdown) return self.stub.SetNodeService(request) def set_node_service_file(self, session, _id, service, file_name, data): - request = core_pb2.SetNodeServiceFileRequest() - request.session = session - request.id = _id - request.service = service - request.file = file_name - request.data = data + request = core_pb2.SetNodeServiceFileRequest( + session=session, id=_id, service=service, file=file_name, data=data) return self.stub.SetNodeServiceFile(request) def service_action(self, session, _id, service, action): - request = core_pb2.ServiceActionRequest() - request.session = session - request.id = _id - request.service = service - request.action = action + request = core_pb2.ServiceActionRequest(session=session, id=_id, service=service, action=action) return self.stub.ServiceAction(request) def get_wlan_config(self, session, _id): - request = core_pb2.GetWlanConfigRequest() - request.session = session - request.id = _id + request = core_pb2.GetWlanConfigRequest(session=session, id=_id) return self.stub.GetWlanConfig(request) def set_wlan_config(self, session, _id, config): - request = core_pb2.SetWlanConfigRequest() - request.session = session - request.id = _id - request.config.update(config) + request = core_pb2.SetWlanConfigRequest(session=session, id=_id, config=config) return self.stub.SetWlanConfig(request) def get_emane_config(self, session): - request = core_pb2.GetEmaneConfigRequest() - request.session = session + request = core_pb2.GetEmaneConfigRequest(session=session) return self.stub.GetEmaneConfig(request) def set_emane_config(self, session, config): - request = core_pb2.SetEmaneConfigRequest() - request.session = session - request.config.update(config) + request = core_pb2.SetEmaneConfigRequest(session=session, config=config) return self.stub.SetEmaneConfig(request) def get_emane_models(self, session): - request = core_pb2.GetEmaneModelsRequest() - request.session = session + request = core_pb2.GetEmaneModelsRequest(session=session) return self.stub.GetEmaneModels(request) def get_emane_model_config(self, session, _id, model, interface_id=None): - request = core_pb2.GetEmaneModelConfigRequest() - request.session = session if interface_id is not None: _id = _id * 1000 + interface_id - request.id = _id - request.model = model + request = core_pb2.GetEmaneModelConfigRequest(session=session, id=_id, model=model) return self.stub.GetEmaneModelConfig(request) def set_emane_model_config(self, session, _id, model, config, interface_id=None): - request = core_pb2.SetEmaneModelConfigRequest() - request.session = session if interface_id is not None: _id = _id * 1000 + interface_id - request.id = _id - request.model = model - request.config.update(config) + request = core_pb2.SetEmaneModelConfigRequest(session=session, id=_id, model=model, config=config) return self.stub.SetEmaneModelConfig(request) def get_emane_model_configs(self, session): - request = core_pb2.GetEmaneModelConfigsRequest() - request.session = session + request = core_pb2.GetEmaneModelConfigsRequest(session=session) return self.stub.GetEmaneModelConfigs(request) def save_xml(self, session, file_path): - request = core_pb2.SaveXmlRequest() - request.session = session + request = core_pb2.SaveXmlRequest(session=session) response = self.stub.SaveXml(request) with open(file_path, "wb") as xml_file: xml_file.write(response.data) @@ -428,9 +302,7 @@ class CoreGrpcClient(object): def open_xml(self, file_path): with open(file_path, "rb") as xml_file: data = xml_file.read() - - request = core_pb2.OpenXmlRequest() - request.data = data + request = core_pb2.OpenXmlRequest(data=data) return self.stub.OpenXml(request) def connect(self): diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index 56ca86b4..96f742f7 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -78,8 +78,7 @@ def convert_link(session, link_data, links): interface_two = core_pb2.Interface( id=link_data.interface2_id, name=interface.name, mac=convert_value(link_data.interface2_mac), ip4=convert_value(link_data.interface2_ip4), ip4mask=link_data.interface2_ip4_mask, - ip6=convert_value(link_data.interface2_ip6), ip6mask=link_data.interface2_ip6_mask - ) + ip6=convert_value(link_data.interface2_ip6), ip6mask=link_data.interface2_ip6_mask) options = core_pb2.LinkOptions( opaque=link_data.opaque, @@ -346,8 +345,9 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): if nodeutils.is_node(node, NodeTypes.EMANE): emane_model = node.model.name - session_proto.nodes.add(id=node.objid, name=node.name, emane=emane_model, model=model, - type=node_type, position=position, services=services) + session_proto.nodes.add( + id=node.objid, name=node.name, emane=emane_model, model=model, + type=node_type, position=position, services=services) links_data = node.all_link_data(0) for link_data in links_data: From 1c00834f2368d3b6ab5b77a9ac5384232b3dcad0 Mon Sep 17 00:00:00 2001 From: bharnden Date: Fri, 22 Mar 2019 16:16:30 -0700 Subject: [PATCH 36/72] grpc cleared out usage of update_proto in server code --- daemon/core/grpc/server.py | 129 +++++++++++-------------------------- 1 file changed, 37 insertions(+), 92 deletions(-) diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index 96f742f7..9c23a14e 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -29,13 +29,6 @@ def convert_value(value): return str(value) -def update_proto(obj, **kwargs): - for key in kwargs: - value = kwargs[key] - if value is not None: - setattr(obj, key, value) - - def get_config_groups(config, configurable_options): groups = [] config_options = [] @@ -363,20 +356,12 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): while self._is_running(context): try: node = queue.get(timeout=1) - node_event = core_pb2.NodeEvent() - update_proto( - node_event.node, - id=node.id, - name=node.name, - model=node.model - ) - update_proto( - node_event.node.position, - x=node.x_position, - y=node.y_position - ) + position = core_pb2.Position(x=node.x_position, y=node.y_position) services = node.services or "" - node_event.node.services.extend(services.split("|")) + services = services.split("|") + node_proto = core_pb2.Node( + id=node.id, name=node.name, model=node.model, position=position, services=services) + node_event = core_pb2.NodeEvent(node=node_proto) yield node_event except Empty: continue @@ -391,42 +376,21 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): while self._is_running(context): try: event = queue.get(timeout=1) - link_event = core_pb2.LinkEvent() + interface_one = None if event.interface1_id is not None: - interface_one = link_event.link.interface_one - update_proto( - interface_one, - id=event.interface1_id, - name=event.interface1_name, - mac=convert_value(event.interface1_mac), - ip4=convert_value(event.interface1_ip4), - ip4mask=event.interface1_ip4_mask, - ip6=convert_value(event.interface1_ip6), - ip6mask=event.interface1_ip6_mask, - ) + interface_one = core_pb2.Interface( + id=event.interface1_id, name=event.interface1_name, mac=convert_value(event.interface1_mac), + ip4=convert_value(event.interface1_ip4), ip4mask=event.interface1_ip4_mask, + ip6=convert_value(event.interface1_ip6), ip6mask=event.interface1_ip6_mask) + interface_two = None if event.interface2_id is not None: - interface_two = link_event.link.interface_two - update_proto( - interface_two, - id=event.interface2_id, - name=event.interface2_name, - mac=convert_value(event.interface2_mac), - ip4=convert_value(event.interface2_ip4), - ip4mask=event.interface2_ip4_mask, - ip6=convert_value(event.interface2_ip6), - ip6mask=event.interface2_ip6_mask, - ) + interface_two = core_pb2.Interface( + id=event.interface2_id, name=event.interface2_name, mac=convert_value(event.interface2_mac), + ip4=convert_value(event.interface2_ip4), ip4mask=event.interface2_ip4_mask, + ip6=convert_value(event.interface2_ip6), ip6mask=event.interface2_ip6_mask) - link_event.message_type = event.message_type - update_proto( - link_event.link, - type=event.link_type, - node_one=event.node1_id, - node_two=event.node2_id - ) - update_proto( - link_event.link.options, + options = core_pb2.LinkOptions( opaque=event.opaque, jitter=event.jitter, key=event.key, @@ -439,6 +403,10 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): dup=event.dup, unidirectional=event.unidirectional ) + link = core_pb2.Link( + type=event.link_type, node_one=event.node1_id, node_two=event.node2_id, + interface_one=interface_one, interface_two=interface_two, options=options) + link_event = core_pb2.LinkEvent(message_type=event.message_type, link=link) yield link_event except Empty: continue @@ -453,12 +421,10 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): while self._is_running(context): try: event = queue.get(timeout=1) - session_event = core_pb2.SessionEvent() event_time = event.time if event_time is not None: event_time = float(event_time) - update_proto( - session_event, + session_event = core_pb2.SessionEvent( node=event.node, event=event.event_type, name=event.name, @@ -480,9 +446,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): while self._is_running(context): try: event = queue.get(timeout=1) - config_event = core_pb2.ConfigEvent() - update_proto( - config_event, + config_event = core_pb2.ConfigEvent( message_type=event.message_type, node=event.node, object=event.object, @@ -495,9 +459,9 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): session=event.session, interface=event.interface_number, network_id=event.network_id, - opaque=event.opaque + opaque=event.opaque, + data_types=event.data_types ) - config_event.data_types.extend(event.data_types) yield config_event except Empty: continue @@ -512,9 +476,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): while self._is_running(context): try: event = queue.get(timeout=1) - exception_event = core_pb2.ExceptionEvent() - update_proto( - exception_event, + exception_event = core_pb2.ExceptionEvent( node=event.node, session=int(event.session), level=event.level.value, @@ -537,9 +499,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): while self._is_running(context): try: event = queue.get(timeout=1) - file_event = core_pb2.FileEvent() - update_proto( - file_event, + file_event = core_pb2.FileEvent( message_type=event.message_type, node=event.node, name=event.name, @@ -590,43 +550,28 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): logging.debug("get node: %s", request) session = self.get_session(request.session, context) node = self.get_node(session, request.id, context) - response = core_pb2.GetNodeResponse() + interfaces = [] for interface_id, interface in node._netif.iteritems(): net_id = None if interface.net: net_id = interface.net.objid - - interface_proto = response.interfaces.add() - interface_proto.id = interface_id - interface_proto.netid = net_id - interface_proto.name = interface.name - interface_proto.mac = str(interface.hwaddr) - interface_proto.mtu = interface.mtu - interface_proto.flowid = interface.flow_id + interface_proto = core_pb2.Interface( + id=interface_id, netid=net_id, name=interface.name, mac=str(interface.hwaddr), + mtu=interface.mtu, flowid=interface.flow_id) + interfaces.append(interface_proto) emane_model = None if nodeutils.is_node(node, NodeTypes.EMANE): emane_model = node.model.name - update_proto( - response.node, - name=node.name, - type=nodeutils.get_node_type(node.__class__).value, - emane=emane_model, - model=node.type - ) - - update_proto( - response.node.position, - x=node.position.x, - y=node.position.y, - z=node.position.z, - ) - services = [x.name for x in getattr(node, "services", [])] - response.node.services.extend(services) - return response + position = core_pb2.Position(x=node.position.x, y=node.position.y, z=node.position.z) + node_type = nodeutils.get_node_type(node.__class__).value + node = core_pb2.Node( + name=node.name, type=node_type, emane=emane_model, model=node.type, position=position, services=services) + + return core_pb2.GetNodeResponse(node=node, interfaces=interfaces) def EditNode(self, request, context): logging.debug("edit node: %s", request) From cb62750b60b712a95cfb618f96ea9958d9cc7aa1 Mon Sep 17 00:00:00 2001 From: bharnden Date: Fri, 22 Mar 2019 17:07:54 -0700 Subject: [PATCH 37/72] grpc further cleanup of server code --- daemon/core/grpc/client.py | 12 +- daemon/core/grpc/server.py | 224 ++++++++++++------------------------- 2 files changed, 78 insertions(+), 158 deletions(-) diff --git a/daemon/core/grpc/client.py b/daemon/core/grpc/client.py index d2a7f222..97aeb6e2 100644 --- a/daemon/core/grpc/client.py +++ b/daemon/core/grpc/client.py @@ -7,10 +7,10 @@ from contextlib import contextmanager import grpc -import core_pb2 -import core_pb2_grpc from core.emulator.emudata import NodeOptions, IpPrefixes, InterfaceData, LinkOptions from core.enumerations import NodeTypes, LinkTypes, EventTypes +from core.grpc import core_pb2 +from core.grpc import core_pb2_grpc def stream_listener(stream, handler): @@ -229,10 +229,12 @@ class CoreGrpcClient(object): return self.stub.GetServiceDefaults(request) def set_service_defaults(self, session, service_defaults): - request = core_pb2.SetServiceDefaultsRequest(session=session) + defaults = [] for node_type in service_defaults: services = service_defaults[node_type] - request.defaults.add(node_type=node_type, services=services) + default = core_pb2.ServiceDefaults(node_type=node_type, services=services) + defaults.append(default) + request = core_pb2.SetServiceDefaultsRequest(session=session, defaults=defaults) return self.stub.SetServiceDefaults(request) def get_node_service(self, session, _id, service): @@ -380,7 +382,7 @@ def main(): # ip generator for example prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16") - for i in xrange(2): + for _ in xrange(2): response = client.create_node(session_data.id) print("created node: {}".format(response)) node_id = response.id diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index 9c23a14e..c9dc9355 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -9,12 +9,12 @@ from itertools import repeat import grpc from concurrent import futures -import core_pb2 -import core_pb2_grpc from core.conf import ConfigShim from core.data import ConfigData, FileData from core.emulator.emudata import NodeOptions, InterfaceData, LinkOptions from core.enumerations import NodeTypes, EventTypes, LinkTypes, MessageFlags, ConfigFlags, ConfigDataTypes +from core.grpc import core_pb2 +from core.grpc import core_pb2_grpc from core.misc import nodeutils from core.mobility import BasicRangeModel, Ns2ScriptedMobility from core.service import ServiceManager, ServiceShim @@ -23,10 +23,9 @@ _ONE_DAY_IN_SECONDS = 60 * 60 * 24 def convert_value(value): - if value is None: - return value - else: - return str(value) + if value is not None: + value = str(value) + return value def get_config_groups(config, configurable_options): @@ -253,11 +252,13 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): def GetSessions(self, request, context): logging.debug("get sessions: %s", request) - response = core_pb2.GetSessionsResponse() + sessions = [] for session_id in self.coreemu.sessions: session = self.coreemu.sessions[session_id] - response.sessions.add(id=session_id, state=session.state, nodes=session.get_node_count()) - return response + session_summary = core_pb2.SessionSummary( + id=session_id, state=session.state, nodes=session.get_node_count()) + sessions.append(session_summary) + return core_pb2.GetSessionsResponse(sessions=sessions) def GetSessionLocation(self, request, context): logging.debug("get session location: %s", request) @@ -542,9 +543,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): if emane_model: session.emane.set_model_config(node_id, emane_model) - response = core_pb2.CreateNodeResponse() - response.id = node.objid - return response + return core_pb2.CreateNodeResponse(id=node.objid) def GetNode(self, request, context): logging.debug("get node: %s", request) @@ -576,7 +575,6 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): def EditNode(self, request, context): logging.debug("edit node: %s", request) session = self.get_session(request.session, context) - node_id = request.id node_options = NodeOptions() x = request.position.x @@ -586,18 +584,14 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): lon = request.position.lon alt = request.position.alt node_options.set_location(lat, lon, alt) - result = session.update_node(node_id, node_options) - response = core_pb2.EditNodeResponse() - response.result = result - return response + return core_pb2.EditNodeResponse(result=result) def DeleteNode(self, request, context): logging.debug("delete node: %s", request) session = self.get_session(request.session, context) - response = core_pb2.DeleteNodeResponse() - response.result = session.delete_node(request.id) - return response + result = session.delete_node(request.id) + return core_pb2.DeleteNodeResponse(result=result) def GetNodeLinks(self, request, context): logging.debug("get node links: %s", request) @@ -608,7 +602,6 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): for link_data in links_data: link = response.links.add() convert_link(session, link_data, link) - return response def CreateLink(self, request, context): @@ -676,20 +669,15 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): link_options.opaque = options_data.opaque session.add_link(node_one, node_two, interface_one, interface_two, link_options=link_options) - - response = core_pb2.CreateLinkResponse() - response.result = True - return response + return core_pb2.CreateLinkResponse(result=True) def EditLink(self, request, context): logging.debug("edit link: %s", request) session = self.get_session(request.session, context) - node_one = request.node_one node_two = request.node_two interface_one_id = request.interface_one interface_two_id = request.interface_two - options_data = request.options link_options = LinkOptions() link_options.delay = options_data.delay @@ -703,94 +691,69 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): link_options.unidirectional = options_data.unidirectional link_options.key = options_data.key link_options.opaque = options_data.opaque - session.update_link(node_one, node_two, interface_one_id, interface_two_id, link_options) - - response = core_pb2.EditLinkResponse() - response.result = True - return response + return core_pb2.EditLinkResponse(result=True) def DeleteLink(self, request, context): logging.debug("delete link: %s", request) session = self.get_session(request.session, context) - node_one = request.node_one node_two = request.node_two interface_one = request.interface_one interface_two = request.interface_two session.delete_link(node_one, node_two, interface_one, interface_two) - - response = core_pb2.DeleteLinkResponse() - response.result = True - return response + return core_pb2.DeleteLinkResponse(result=True) def GetHooks(self, request, context): logging.debug("get hooks: %s", request) session = self.get_session(request.session, context) - - response = core_pb2.GetHooksResponse() + hooks = [] for state, state_hooks in session._hooks.iteritems(): for file_name, file_data in state_hooks: - hook = response.hooks.add() - hook.state = state - hook.file = file_name - hook.data = file_data - - return response + hook = core_pb2.Hook(state=state, file=file_name, data=file_data) + hooks.append(hook) + return core_pb2.GetHooksResponse(hooks=hooks) def AddHook(self, request, context): logging.debug("add hook: %s", request) session = self.get_session(request.session, context) - hook = request.hook session.add_hook(hook.state, hook.file, None, hook.data) - response = core_pb2.AddHookResponse() - response.result = True - return response + return core_pb2.AddHookResponse(result=True) def GetMobilityConfigs(self, request, context): logging.debug("get mobility configs: %s", request) session = self.get_session(request.session, context) - - response = core_pb2.GetMobilityConfigsResponse() + mobility_configs = {} for node_id, model_config in session.mobility.node_configurations.iteritems(): if node_id == -1: continue - for model_name in model_config.iterkeys(): if model_name != Ns2ScriptedMobility.name: continue - config = session.mobility.get_model_config(node_id, model_name) groups = get_config_groups(config, Ns2ScriptedMobility) - mobility_config = response.configs[node_id] - mobility_config.groups.extend(groups) - return response + mobility_configs[node_id] = groups + return core_pb2.GetMobilityConfigsResponse(configs=mobility_configs) def GetMobilityConfig(self, request, context): logging.debug("get mobility config: %s", request) session = self.get_session(request.session, context) config = session.mobility.get_model_config(request.id, Ns2ScriptedMobility.name) groups = get_config_groups(config, Ns2ScriptedMobility) - response = core_pb2.GetMobilityConfigResponse() - response.groups.extend(groups) - return response + return core_pb2.GetMobilityConfigResponse(groups=groups) def SetMobilityConfig(self, request, context): logging.debug("set mobility config: %s", request) session = self.get_session(request.session, context) session.mobility.set_model_config(request.id, Ns2ScriptedMobility.name, request.config) - response = core_pb2.SetMobilityConfigResponse() - response.result = True - return response + return core_pb2.SetMobilityConfigResponse(result=True) def MobilityAction(self, request, context): logging.debug("mobility action: %s", request) session = self.get_session(request.session, context) node = self.get_node(session, request.id, context) - - response = core_pb2.MobilityActionResponse() - response.result = True + result = True if request.action == core_pb2.MOBILITY_START: node.mobility.start() elif request.action == core_pb2.MOBILITY_PAUSE: @@ -798,30 +761,26 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): elif request.action == core_pb2.MOBILITY_STOP: node.mobility.stop(move_initial=True) else: - response.result = False - - return response + result = False + return core_pb2.MobilityActionResponse(result=result) def GetServices(self, request, context): logging.debug("get services: %s", request) - response = core_pb2.GetServicesResponse() + services = [] for service in ServiceManager.services.itervalues(): - service_proto = response.services.add() - service_proto.group = service.group - service_proto.name = service.name - return response + service_proto = core_pb2.Service(group=service.group, name=service.name) + services.append(service_proto) + return core_pb2.GetServicesResponse(services=services) def GetServiceDefaults(self, request, context): logging.debug("get service defaults: %s", request) session = self.get_session(request.session, context) - - response = core_pb2.GetServiceDefaultsResponse() + all_service_defaults = [] for node_type in session.services.default_services: services = session.services.default_services[node_type] - service_defaults = response.defaults.add() - service_defaults.node_type = node_type - service_defaults.services.extend(services) - return response + service_defaults = core_pb2.ServiceDefaults(node_type=node_type, services=services) + all_service_defaults.append(service_defaults) + return core_pb2.GetServiceDefaultsResponse(defaults=all_service_defaults) def SetServiceDefaults(self, request, context): logging.debug("set service defaults: %s", request) @@ -829,87 +788,68 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): session.services.default_services.clear() for service_defaults in request.defaults: session.services.default_services[service_defaults.node_type] = service_defaults.services - - response = core_pb2.SetServiceDefaultsResponse() - response.result = True - return response + return core_pb2.SetServiceDefaultsResponse(result=True) def GetNodeService(self, request, context): logging.debug("get node service: %s", request) session = self.get_session(request.session, context) - service = session.services.get_service(request.id, request.service, default_service=True) - response = core_pb2.GetNodeServiceResponse() - response.service.executables.extend(service.executables) - response.service.dependencies.extend(service.dependencies) - response.service.dirs.extend(service.dirs) - response.service.configs.extend(service.configs) - response.service.startup.extend(service.startup) - response.service.validate.extend(service.validate) - response.service.validation_mode = service.validation_mode.value - response.service.validation_timer = service.validation_timer - response.service.shutdown.extend(service.shutdown) - if service.meta: - response.service.meta = service.meta - return response + service_proto = core_pb2.NodeServiceData( + executables=service.executables, + dependencies=service.dependencies, + dirs=service.dirs, + configs=service.configs, + startup=service.startup, + validate=service.validate, + validation_mode=service.validation_mode.value, + validation_timer=service.validation_timer, + shutdown=service.shutdown, + meta=service.meta + ) + return core_pb2.GetNodeServiceResponse(service=service_proto) def GetNodeServiceFile(self, request, context): logging.debug("get node service file: %s", request) session = self.get_session(request.session, context) node = self.get_node(session, request.id, context) - service = None for current_service in node.services: if current_service.name == request.service: service = current_service break - - response = core_pb2.GetNodeServiceFileResponse() if not service: - return response + context.abort(grpc.StatusCode.NOT_FOUND, "service not found") file_data = session.services.get_service_file(node, request.service, request.file) - response.data = file_data.data - return response + return core_pb2.GetNodeServiceFileResponse(data=file_data.data) def SetNodeService(self, request, context): logging.debug("set node service: %s", request) session = self.get_session(request.session, context) - - # guarantee custom service exists session.services.set_service(request.id, request.service) service = session.services.get_service(request.id, request.service) service.startup = tuple(request.startup) service.validate = tuple(request.validate) service.shutdown = tuple(request.shutdown) - - response = core_pb2.SetNodeServiceResponse() - response.result = True - return response + return core_pb2.SetNodeServiceResponse(result=True) def SetNodeServiceFile(self, request, context): logging.debug("set node service file: %s", request) session = self.get_session(request.session, context) session.services.set_service_file(request.id, request.service, request.file, request.data) - response = core_pb2.SetNodeServiceFileResponse() - response.result = True - return response + return core_pb2.SetNodeServiceFileResponse(result=True) def ServiceAction(self, request, context): logging.debug("service action: %s", request) session = self.get_session(request.session, context) node = self.get_node(session, request.id, context) - service = None for current_service in node.services: if current_service.name == request.service: service = current_service break - response = core_pb2.ServiceActionResponse() - response.result = False - if not service: - return response + context.abort(grpc.StatusCode.NOT_FOUND, "service not found") status = -1 if request.action == core_pb2.START: @@ -923,58 +863,47 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): elif request.action == core_pb2.VALIDATE: status = session.services.validate_service(node, service) + result = False if not status: - response.result = True + result = True - return response + return core_pb2.ServiceActionResponse(result=result) def GetWlanConfig(self, request, context): logging.debug("get wlan config: %s", request) session = self.get_session(request.session, context) config = session.mobility.get_model_config(request.id, BasicRangeModel.name) groups = get_config_groups(config, BasicRangeModel) - response = core_pb2.GetWlanConfigResponse() - response.groups.extend(groups) - return response + return core_pb2.GetWlanConfigResponse(groups=groups) def SetWlanConfig(self, request, context): logging.debug("set wlan config: %s", request) session = self.get_session(request.session, context) session.mobility.set_model_config(request.id, BasicRangeModel.name, request.config) - response = core_pb2.SetWlanConfigResponse() - response.result = True - return response + return core_pb2.SetWlanConfigResponse(result=True) def GetEmaneConfig(self, request, context): logging.debug("get emane config: %s", request) session = self.get_session(request.session, context) config = session.emane.get_configs() groups = get_config_groups(config, session.emane.emane_config) - response = core_pb2.GetEmaneConfigResponse() - response.groups.extend(groups) - return response + return core_pb2.GetEmaneConfigResponse(groups=groups) def SetEmaneConfig(self, request, context): logging.debug("set emane config: %s", request) session = self.get_session(request.session, context) session.emane.set_configs(request.config) - response = core_pb2.SetEmaneConfigResponse() - response.result = True - return response + return core_pb2.SetEmaneConfigResponse(result=True) def GetEmaneModels(self, request, context): logging.debug("get emane models: %s", request) session = self.get_session(request.session, context) - models = [] for model in session.emane.models.keys(): if len(model.split("_")) != 2: continue models.append(model) - - response = core_pb2.GetEmaneModelsResponse() - response.models.extend(models) - return response + return core_pb2.GetEmaneModelsResponse(models=models) def GetEmaneModelConfig(self, request, context): logging.debug("get emane model config: %s", request) @@ -982,22 +911,17 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): model = session.emane.models[request.model] config = session.emane.get_model_config(request.id, request.model) groups = get_config_groups(config, model) - response = core_pb2.GetEmaneModelConfigResponse() - response.groups.extend(groups) - return response + return core_pb2.GetEmaneModelConfigResponse(groups=groups) def SetEmaneModelConfig(self, request, context): logging.debug("set emane model config: %s", request) session = self.get_session(request.session, context) session.emane.set_model_config(request.id, request.model, request.config) - response = core_pb2.SetEmaneModelConfigResponse() - response.result = True - return response + return core_pb2.SetEmaneModelConfigResponse(result=True) def GetEmaneModelConfigs(self, request, context): logging.debug("get emane model configs: %s", request) session = self.get_session(request.session, context) - response = core_pb2.GetEmaneModelConfigsResponse() for node_id, model_config in session.emane.node_configurations.iteritems(): if node_id == -1: @@ -1022,9 +946,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): with open(temp_path, "rb") as xml_file: data = xml_file.read() - response = core_pb2.SaveXmlResponse() - response.data = data - return response + return core_pb2.SaveXmlResponse(data=data) def OpenXml(self, request, context): logging.debug("open xml: %s", request) @@ -1035,14 +957,10 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): with open(temp_path, "wb") as xml_file: xml_file.write(request.data) - response = core_pb2.OpenXmlResponse() try: session.open_xml(temp_path, start=True) - response.session = session.session_id - response.result = True - except: - response.result = False + return core_pb2.OpenXmlResponse(session=session.session_id, result=True) + except IOError: logging.exception("error opening session file") self.coreemu.delete_session(session.session_id) - - return response + context.abort(grpc.StatusCode.INVALID_ARGUMENT, "invalid xml file") From 9b237a24139d727b301486387456445751b71322 Mon Sep 17 00:00:00 2001 From: bharnden Date: Fri, 22 Mar 2019 21:06:10 -0700 Subject: [PATCH 38/72] grpc made common method for node links --- daemon/core/grpc/server.py | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index c9dc9355..03c2cf06 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -45,15 +45,22 @@ def get_config_groups(config, configurable_options): for config_group in configurable_options.config_groups(): start = config_group.start - 1 stop = config_group.stop - config_group_proto = core_pb2.ConfigGroup() - config_group_proto.name = config_group.name - config_group_proto.options.extend(config_options[start: stop]) + options = config_options[start: stop] + config_group_proto = core_pb2.ConfigGroup(name=config_group.name, options=options) groups.append(config_group_proto) return groups -def convert_link(session, link_data, links): +def get_links(session, node): + links = [] + for link_data in node.all_link_data(0): + link = convert_link(session, link_data) + links.append(link) + return links + + +def convert_link(session, link_data): interface_one = None if link_data.interface1_id is not None: node = session.get_object(link_data.node1_id) @@ -86,7 +93,7 @@ def convert_link(session, link_data, links): unidirectional=link_data.unidirectional ) - links.add( + return core_pb2.Link( type=link_data.link_type, node_one=link_data.node1_id, node_two=link_data.node2_id, interface_one=interface_one, interface_two=interface_two, options=options ) @@ -319,8 +326,9 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): def GetSession(self, request, context): logging.debug("get session: %s", request) session = self.get_session(request.id, context) - session_proto = core_pb2.Session(state=session.state) + links = [] + nodes = [] for node_id in session.objects: node = session.objects[node_id] if not isinstance(node.objid, int): @@ -339,14 +347,15 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): if nodeutils.is_node(node, NodeTypes.EMANE): emane_model = node.model.name - session_proto.nodes.add( + node_proto = core_pb2.Node( id=node.objid, name=node.name, emane=emane_model, model=model, type=node_type, position=position, services=services) + nodes.append(node_proto) - links_data = node.all_link_data(0) - for link_data in links_data: - convert_link(session, link_data, session_proto.links) + node_links = get_links(session, node) + links.extend(node_links) + session_proto = core_pb2.Session(state=session.state, nodes=nodes, links=links) return core_pb2.GetSessionResponse(session=session_proto) def NodeEvents(self, request, context): @@ -597,12 +606,8 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): logging.debug("get node links: %s", request) session = self.get_session(request.session, context) node = self.get_node(session, request.id, context) - response = core_pb2.GetNodeLinksResponse() - links_data = node.all_link_data(0) - for link_data in links_data: - link = response.links.add() - convert_link(session, link_data, link) - return response + links = get_links(session, node) + return core_pb2.GetNodeLinksResponse(links=links) def CreateLink(self, request, context): logging.debug("create link: %s", request) From 01979d53ecbf05fc0f5f36cc8d0c61f4ae83f18c Mon Sep 17 00:00:00 2001 From: bharnden Date: Fri, 22 Mar 2019 21:20:37 -0700 Subject: [PATCH 39/72] grpc added node rpc tests --- daemon/core/grpc/server.py | 3 +- daemon/tests/test_grpc.py | 60 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index 03c2cf06..88514f80 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -577,7 +577,8 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): position = core_pb2.Position(x=node.position.x, y=node.position.y, z=node.position.z) node_type = nodeutils.get_node_type(node.__class__).value node = core_pb2.Node( - name=node.name, type=node_type, emane=emane_model, model=node.type, position=position, services=services) + id=node.objid, name=node.name, type=node_type, emane=emane_model, model=node.type, position=position, + services=services) return core_pb2.GetNodeResponse(node=node, interfaces=interfaces) diff --git a/daemon/tests/test_grpc.py b/daemon/tests/test_grpc.py index 973b4204..0accf2f0 100644 --- a/daemon/tests/test_grpc.py +++ b/daemon/tests/test_grpc.py @@ -3,6 +3,7 @@ import time import pytest +from core.emulator.emudata import NodeOptions from core.grpc import core_pb2 from core.enumerations import NodeTypes, EventTypes from core.grpc.client import CoreGrpcClient @@ -168,3 +169,62 @@ class TestGrpc: # then assert response.result is True assert session.state == EventTypes.DEFINITION_STATE.value + + def test_create_node(self, grpc_server): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + + # then + with client.context_connect(): + response = client.create_node(session.session_id) + + # then + assert response.id is not None + assert session.get_object(response.id) is not None + + def test_get_node(self, grpc_server): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + node = session.add_node() + + # then + with client.context_connect(): + response = client.get_node(session.session_id, node.objid) + + # then + assert response.node.id == node.objid + + def test_edit_node(self, grpc_server): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + node = session.add_node() + + # then + x, y = 10, 10 + with client.context_connect(): + node_options = NodeOptions() + node_options.set_position(x, y) + response = client.edit_node(session.session_id, node.objid, node_options) + + # then + assert response.result is True + assert node.position.x == x + assert node.position.y == y + + def test_delete_node(self, grpc_server): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + node = session.add_node() + + # then + with client.context_connect(): + response = client.delete_node(session.session_id, node.objid) + + # then + assert response.result is True + with pytest.raises(KeyError): + assert session.get_object(node.objid) From 782b9d5ce6a0db45d54dbe675e5b8da39b468374 Mon Sep 17 00:00:00 2001 From: bharnden Date: Fri, 22 Mar 2019 21:44:16 -0700 Subject: [PATCH 40/72] grpc added hook and xml rpc tests --- daemon/tests/test_grpc.py | 61 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/daemon/tests/test_grpc.py b/daemon/tests/test_grpc.py index 0accf2f0..f6e18f0b 100644 --- a/daemon/tests/test_grpc.py +++ b/daemon/tests/test_grpc.py @@ -228,3 +228,64 @@ class TestGrpc: assert response.result is True with pytest.raises(KeyError): assert session.get_object(node.objid) + + def test_get_hooks(self, grpc_server): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + file_name = "test" + file_data = "echo hello" + session.add_hook(EventTypes.RUNTIME_STATE.value, file_name, None, file_data) + + # then + with client.context_connect(): + response = client.get_hooks(session.session_id) + + # then + assert len(response.hooks) == 1 + hook = response.hooks[0] + assert hook.state == EventTypes.RUNTIME_STATE.value + assert hook.file == file_name + assert hook.data == file_data + + def test_add_hook(self, grpc_server): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + + # then + file_name = "test" + file_data = "echo hello" + with client.context_connect(): + response = client.add_hook(session.session_id, EventTypes.RUNTIME_STATE, file_name, file_data) + + # then + assert response.result is True + + def test_save_xml(self, grpc_server, tmpdir): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + tmp = tmpdir.join("text.xml") + + # then + with client.context_connect(): + response = client.save_xml(session.session_id, str(tmp)) + + # then + assert tmp.exists() + + def test_open_xml_hook(self, grpc_server, tmpdir): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + tmp = tmpdir.join("text.xml") + session.save_xml(str(tmp)) + + # then + with client.context_connect(): + response = client.open_xml(str(tmp)) + + # then + assert response.result is True + assert response.session is not None From 6f428bd68a83e47faeeea0f913cfa0b1c9058f98 Mon Sep 17 00:00:00 2001 From: bharnden Date: Sun, 24 Mar 2019 13:04:18 -0700 Subject: [PATCH 41/72] grpc added link tests, fixed client mac conversion --- daemon/core/grpc/client.py | 4 +- daemon/tests/test_grpc.py | 83 +++++++++++++++++++++++++++++++++++++- 2 files changed, 84 insertions(+), 3 deletions(-) diff --git a/daemon/core/grpc/client.py b/daemon/core/grpc/client.py index 97aeb6e2..a8ad288b 100644 --- a/daemon/core/grpc/client.py +++ b/daemon/core/grpc/client.py @@ -137,14 +137,14 @@ class CoreGrpcClient(object): interface_one_proto = None if interface_one is not None: interface_one_proto = core_pb2.Interface( - id=interface_one.id, name=interface_one.name, mac=interface_one.mac, + id=interface_one.id, name=interface_one.name, mac=str(interface_one.mac), ip4=interface_one.ip4, ip4mask=interface_one.ip4_mask, ip6=interface_one.ip6, ip6mask=interface_one.ip6_mask) interface_two_proto = None if interface_two is not None: interface_two_proto = core_pb2.Interface( - id=interface_two.id, name=interface_two.name, mac=interface_two.mac, + id=interface_two.id, name=interface_two.name, mac=str(interface_two.mac), ip4=interface_two.ip4, ip4mask=interface_two.ip4_mask, ip6=interface_two.ip6, ip6mask=interface_two.ip6_mask) diff --git a/daemon/tests/test_grpc.py b/daemon/tests/test_grpc.py index f6e18f0b..5deb1a28 100644 --- a/daemon/tests/test_grpc.py +++ b/daemon/tests/test_grpc.py @@ -3,7 +3,7 @@ import time import pytest -from core.emulator.emudata import NodeOptions +from core.emulator.emudata import NodeOptions, LinkOptions from core.grpc import core_pb2 from core.enumerations import NodeTypes, EventTypes from core.grpc.client import CoreGrpcClient @@ -289,3 +289,84 @@ class TestGrpc: # then assert response.result is True assert response.session is not None + + def test_get_node_links(self, grpc_server, ip_prefixes): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + switch = session.add_node(_type=NodeTypes.SWITCH) + node = session.add_node() + interface = ip_prefixes.create_interface(node) + session.add_link(node.objid, switch.objid, interface) + + # then + with client.context_connect(): + response = client.get_node_links(session.session_id, switch.objid) + + # then + assert len(response.links) == 1 + + def test_create_link(self, grpc_server, ip_prefixes): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + switch = session.add_node(_type=NodeTypes.SWITCH) + node = session.add_node() + assert len(switch.all_link_data(0)) == 0 + + # then + interface = ip_prefixes.create_interface(node) + with client.context_connect(): + response = client.create_link(session.session_id, node.objid, switch.objid, interface) + + # then + assert response.result is True + assert len(switch.all_link_data(0)) == 1 + + def test_edit_link(self, grpc_server, ip_prefixes): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + switch = session.add_node(_type=NodeTypes.SWITCH) + node = session.add_node() + interface = ip_prefixes.create_interface(node) + session.add_link(node.objid, switch.objid, interface) + options = LinkOptions() + options.bandwidth = 30000 + link = switch.all_link_data(0)[0] + assert options.bandwidth != link.bandwidth + + # then + with client.context_connect(): + response = client.edit_link(session.session_id, node.objid, switch.objid, options) + + # then + assert response.result is True + link = switch.all_link_data(0)[0] + assert options.bandwidth == link.bandwidth + + def test_delete_link(self, grpc_server, ip_prefixes): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + node_one = session.add_node() + interface_one = ip_prefixes.create_interface(node_one) + node_two = session.add_node() + interface_two = ip_prefixes.create_interface(node_two) + session.add_link(node_one.objid, node_two.objid, interface_one, interface_two) + link_node = None + for node_id in session.objects: + node = session.objects[node_id] + if node.objid not in {node_one.objid, node_two.objid}: + link_node = node + break + assert len(link_node.all_link_data(0)) == 1 + + # then + with client.context_connect(): + response = client.delete_link( + session.session_id, node_one.objid, node_two.objid, interface_one.id, interface_two.id) + + # then + assert response.result is True + assert len(link_node.all_link_data(0)) == 0 From 96d38e0b4021a6b18ade24f9fe26334ee43d1d20 Mon Sep 17 00:00:00 2001 From: bharnden Date: Sun, 24 Mar 2019 13:14:39 -0700 Subject: [PATCH 42/72] grpc updated create link/node to use the verbage add, to align with the actual python EmuSession class --- daemon/core/grpc/client.py | 18 +++++++++--------- daemon/core/grpc/server.py | 12 ++++++------ daemon/proto/core.proto | 12 ++++++------ daemon/tests/test_grpc.py | 8 ++++---- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/daemon/core/grpc/client.py b/daemon/core/grpc/client.py index a8ad288b..42e73e3b 100644 --- a/daemon/core/grpc/client.py +++ b/daemon/core/grpc/client.py @@ -102,17 +102,17 @@ class CoreGrpcClient(object): stream = self.stub.FileEvents(request) start_streamer(stream, handler) - def create_node(self, session, _type=NodeTypes.DEFAULT, _id=None, node_options=None, emane=None): + def add_node(self, session, _type=NodeTypes.DEFAULT, _id=None, node_options=None, emane=None): if not node_options: node_options = NodeOptions() position = core_pb2.Position( x=node_options.x, y=node_options.y, lat=node_options.lat, lon=node_options.lon, alt=node_options.alt) - request = core_pb2.CreateNodeRequest( + request = core_pb2.AddNodeRequest( session=session, type=_type.value, name=node_options.name, model=node_options.model, icon=node_options.icon, services=node_options.services, opaque=node_options.opaque, emane=emane, position=position) - return self.stub.CreateNode(request) + return self.stub.AddNode(request) def get_node(self, session, _id): request = core_pb2.GetNodeRequest(session=session, id=_id) @@ -133,7 +133,7 @@ class CoreGrpcClient(object): request = core_pb2.GetNodeLinksRequest(session=session, id=_id) return self.stub.GetNodeLinks(request) - def create_link(self, session, node_one, node_two, interface_one=None, interface_two=None, link_options=None): + def add_link(self, session, node_one, node_two, interface_one=None, interface_two=None, link_options=None): interface_one_proto = None if interface_one is not None: interface_one_proto = core_pb2.Interface( @@ -167,8 +167,8 @@ class CoreGrpcClient(object): link = core_pb2.Link( node_one=node_one, node_two=node_two, type=LinkTypes.WIRED.value, interface_one=interface_one_proto, interface_two=interface_two_proto, options=options) - request = core_pb2.CreateLinkRequest(session=session, link=link) - return self.stub.CreateLink(request) + request = core_pb2.AddLinkRequest(session=session, link=link) + return self.stub.AddLink(request) def edit_link(self, session, node_one, node_two, link_options, interface_one=None, interface_two=None): options = core_pb2.LinkOptions( @@ -375,7 +375,7 @@ def main(): print("set session state: {}".format(client.set_session_state(session_data.id, EventTypes.CONFIGURATION_STATE))) # create switch node - response = client.create_node(session_data.id, _type=NodeTypes.SWITCH) + response = client.add_node(session_data.id, _type=NodeTypes.SWITCH) print("created switch: {}".format(response)) switch_id = response.id @@ -383,7 +383,7 @@ def main(): prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16") for _ in xrange(2): - response = client.create_node(session_data.id) + response = client.add_node(session_data.id) print("created node: {}".format(response)) node_id = response.id node_options = NodeOptions() @@ -402,7 +402,7 @@ def main(): ip4=str(prefixes.ip4.addr(node_id)), ip4_mask=prefixes.ip4.prefixlen, ip6=None, ip6_mask=None ) - print("created link: {}".format(client.create_link(session_data.id, node_id, switch_id, interface_one))) + print("created link: {}".format(client.add_link(session_data.id, node_id, switch_id, interface_one))) link_options = LinkOptions() link_options.per = 50 print("edit link: {}".format(client.edit_link( diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index 88514f80..dd75bccd 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -527,8 +527,8 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): self._cancel_stream(context) - def CreateNode(self, request, context): - logging.debug("create node: %s", request) + def AddNode(self, request, context): + logging.debug("add node: %s", request) session = self.get_session(request.session, context) node_id = request.id @@ -552,7 +552,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): if emane_model: session.emane.set_model_config(node_id, emane_model) - return core_pb2.CreateNodeResponse(id=node.objid) + return core_pb2.AddNodeResponse(id=node.objid) def GetNode(self, request, context): logging.debug("get node: %s", request) @@ -610,8 +610,8 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): links = get_links(session, node) return core_pb2.GetNodeLinksResponse(links=links) - def CreateLink(self, request, context): - logging.debug("create link: %s", request) + def AddLink(self, request, context): + logging.debug("add link: %s", request) session = self.get_session(request.session, context) node_one = request.link.node_one node_two = request.link.node_two @@ -675,7 +675,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): link_options.opaque = options_data.opaque session.add_link(node_one, node_two, interface_one, interface_two, link_options=link_options) - return core_pb2.CreateLinkResponse(result=True) + return core_pb2.AddLinkResponse(result=True) def EditLink(self, request, context): logging.debug("edit link: %s", request) diff --git a/daemon/proto/core.proto b/daemon/proto/core.proto index 67cfcbce..6f744726 100644 --- a/daemon/proto/core.proto +++ b/daemon/proto/core.proto @@ -38,7 +38,7 @@ service CoreApi { } // node rpc - rpc CreateNode (CreateNodeRequest) returns (CreateNodeResponse) { + rpc AddNode (AddNodeRequest) returns (AddNodeResponse) { } rpc GetNode (GetNodeRequest) returns (GetNodeResponse) { } @@ -50,7 +50,7 @@ service CoreApi { // link rpc rpc GetNodeLinks (GetNodeLinksRequest) returns (GetNodeLinksResponse) { } - rpc CreateLink (CreateLinkRequest) returns (CreateLinkResponse) { + rpc AddLink (AddLinkRequest) returns (AddLinkResponse) { } rpc EditLink (EditLinkRequest) returns (EditLinkResponse) { } @@ -279,7 +279,7 @@ message FileEvent { bytes compressed_data = 10; } -message CreateNodeRequest { +message AddNodeRequest { int32 session = 1; int32 id = 2; NodeType type = 3; @@ -292,7 +292,7 @@ message CreateNodeRequest { string opaque = 10; } -message CreateNodeResponse { +message AddNodeResponse { int32 id = 1; } @@ -334,12 +334,12 @@ message GetNodeLinksResponse { repeated Link links = 1; } -message CreateLinkRequest { +message AddLinkRequest { int32 session = 1; Link link = 2; } -message CreateLinkResponse { +message AddLinkResponse { bool result = 1; } diff --git a/daemon/tests/test_grpc.py b/daemon/tests/test_grpc.py index 5deb1a28..6348dae7 100644 --- a/daemon/tests/test_grpc.py +++ b/daemon/tests/test_grpc.py @@ -170,14 +170,14 @@ class TestGrpc: assert response.result is True assert session.state == EventTypes.DEFINITION_STATE.value - def test_create_node(self, grpc_server): + def test_add_node(self, grpc_server): # given client = CoreGrpcClient() session = grpc_server.coreemu.create_session() # then with client.context_connect(): - response = client.create_node(session.session_id) + response = client.add_node(session.session_id) # then assert response.id is not None @@ -306,7 +306,7 @@ class TestGrpc: # then assert len(response.links) == 1 - def test_create_link(self, grpc_server, ip_prefixes): + def test_add_link(self, grpc_server, ip_prefixes): # given client = CoreGrpcClient() session = grpc_server.coreemu.create_session() @@ -317,7 +317,7 @@ class TestGrpc: # then interface = ip_prefixes.create_interface(node) with client.context_connect(): - response = client.create_link(session.session_id, node.objid, switch.objid, interface) + response = client.add_link(session.session_id, node.objid, switch.objid, interface) # then assert response.result is True From 6d79014aadb928389f165c59ddd52b00e0e8c92c Mon Sep 17 00:00:00 2001 From: bharnden Date: Sun, 24 Mar 2019 13:48:27 -0700 Subject: [PATCH 43/72] grpc added wlan rpc tests --- daemon/tests/test_grpc.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/daemon/tests/test_grpc.py b/daemon/tests/test_grpc.py index 6348dae7..f6de42b2 100644 --- a/daemon/tests/test_grpc.py +++ b/daemon/tests/test_grpc.py @@ -7,6 +7,7 @@ from core.emulator.emudata import NodeOptions, LinkOptions from core.grpc import core_pb2 from core.enumerations import NodeTypes, EventTypes from core.grpc.client import CoreGrpcClient +from core.mobility import BasicRangeModel MODELS = [ "router", @@ -370,3 +371,33 @@ class TestGrpc: # then assert response.result is True assert len(link_node.all_link_data(0)) == 0 + + def test_get_wlan_config(self, grpc_server): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + wlan = session.add_node(_type=NodeTypes.WIRELESS_LAN) + + # then + with client.context_connect(): + response = client.get_wlan_config(session.session_id, wlan.objid) + + # then + assert len(response.groups) > 0 + + def test_set_wlan_config(self, grpc_server): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + wlan = session.add_node(_type=NodeTypes.WIRELESS_LAN) + range_key = "range" + range_value = "300" + + # then + with client.context_connect(): + response = client.set_wlan_config(session.session_id, wlan.objid, {range_key: range_value}) + + # then + assert response.result is True + config = session.mobility.get_model_config(wlan.objid, BasicRangeModel.name) + assert config[range_key] == range_value From d880960cf7be60f1dc78482b4401dcf7bdc7a697 Mon Sep 17 00:00:00 2001 From: bharnden Date: Sun, 24 Mar 2019 14:13:04 -0700 Subject: [PATCH 44/72] grpc added get/set emane config test, fixed logic for setting emane/session configs in grpc server --- daemon/core/grpc/server.py | 6 ++++-- daemon/tests/test_grpc.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index dd75bccd..21f00696 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -320,7 +320,8 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): def SetSessionOptions(self, request, context): logging.debug("set session options: %s", request) session = self.get_session(request.id, context) - session.options.set_configs(request.config) + config = session.options.get_configs() + config.update(request.config) return core_pb2.SetSessionOptionsResponse(result=True) def GetSession(self, request, context): @@ -898,7 +899,8 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): def SetEmaneConfig(self, request, context): logging.debug("set emane config: %s", request) session = self.get_session(request.session, context) - session.emane.set_configs(request.config) + config = session.emane.get_configs() + config.update(request.config) return core_pb2.SetEmaneConfigResponse(result=True) def GetEmaneModels(self, request, context): diff --git a/daemon/tests/test_grpc.py b/daemon/tests/test_grpc.py index f6de42b2..9bb15184 100644 --- a/daemon/tests/test_grpc.py +++ b/daemon/tests/test_grpc.py @@ -3,6 +3,7 @@ import time import pytest +from core.emane.ieee80211abg import EmaneIeee80211abgModel from core.emulator.emudata import NodeOptions, LinkOptions from core.grpc import core_pb2 from core.enumerations import NodeTypes, EventTypes @@ -157,6 +158,8 @@ class TestGrpc: # then assert response.result is True assert session.options.get_config(option) == value + config = session.options.get_configs() + assert len(config) > 0 def test_set_session_state(self, grpc_server): # given @@ -401,3 +404,32 @@ class TestGrpc: assert response.result is True config = session.mobility.get_model_config(wlan.objid, BasicRangeModel.name) assert config[range_key] == range_value + + def test_get_emane_config(self, grpc_server): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + + # then + with client.context_connect(): + response = client.get_emane_config(session.session_id) + + # then + assert len(response.groups) > 0 + + def test_set_emane_config(self, grpc_server): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + config_key = "platform_id_start" + config_value = "2" + + # then + with client.context_connect(): + response = client.set_emane_config(session.session_id, {config_key: config_value}) + + # then + assert response.result is True + config = session.emane.get_configs() + assert len(config) > 1 + assert config[config_key] == config_value From 03c221efa95b9a60a024ebce8942cc187c36a2cc Mon Sep 17 00:00:00 2001 From: bharnden Date: Mon, 25 Mar 2019 11:52:16 -0700 Subject: [PATCH 45/72] grpc added all emane rpc tests --- daemon/tests/test_grpc.py | 70 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/daemon/tests/test_grpc.py b/daemon/tests/test_grpc.py index 9bb15184..954ba174 100644 --- a/daemon/tests/test_grpc.py +++ b/daemon/tests/test_grpc.py @@ -433,3 +433,73 @@ class TestGrpc: config = session.emane.get_configs() assert len(config) > 1 assert config[config_key] == config_value + + def test_get_emane_model_configs(self, grpc_server): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + emane_network = session.create_emane_network( + model=EmaneIeee80211abgModel, + geo_reference=(47.57917, -122.13232, 2.00000) + ) + config_key = "platform_id_start" + config_value = "2" + session.emane.set_model_config(emane_network.objid, EmaneIeee80211abgModel.name, {config_key: config_value}) + + # then + with client.context_connect(): + response = client.get_emane_model_configs(session.session_id) + + # then + assert len(response.configs) == 1 + assert emane_network.objid in response.configs + + def test_set_emane_model_config(self, grpc_server): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + emane_network = session.create_emane_network( + model=EmaneIeee80211abgModel, + geo_reference=(47.57917, -122.13232, 2.00000) + ) + config_key = "bandwidth" + config_value = "900000" + + # then + with client.context_connect(): + response = client.set_emane_model_config( + session.session_id, emane_network.objid, EmaneIeee80211abgModel.name, {config_key: config_value}) + + # then + assert response.result is True + config = session.emane.get_model_config(emane_network.objid, EmaneIeee80211abgModel.name) + assert config[config_key] == config_value + + def test_get_emane_model_config(self, grpc_server): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + emane_network = session.create_emane_network( + model=EmaneIeee80211abgModel, + geo_reference=(47.57917, -122.13232, 2.00000) + ) + + # then + with client.context_connect(): + response = client.get_emane_model_config( + session.session_id, emane_network.objid, EmaneIeee80211abgModel.name) + + # then + assert len(response.groups) > 0 + + def test_get_emane_models(self, grpc_server): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + + # then + with client.context_connect(): + response = client.get_emane_models(session.session_id) + + # then + assert len(response.models) > 0 From b15e525cc1f128553dfa7fe1b188582076b86b83 Mon Sep 17 00:00:00 2001 From: bharnden Date: Mon, 25 Mar 2019 12:59:07 -0700 Subject: [PATCH 46/72] grpc added mobility tests --- daemon/core/grpc/server.py | 6 ++-- daemon/proto/core.proto | 1 - daemon/tests/test_grpc.py | 63 +++++++++++++++++++++++++++++++++++++- 3 files changed, 65 insertions(+), 5 deletions(-) diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index 21f00696..80a24f57 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -731,7 +731,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): def GetMobilityConfigs(self, request, context): logging.debug("get mobility configs: %s", request) session = self.get_session(request.session, context) - mobility_configs = {} + response = core_pb2.GetMobilityConfigsResponse() for node_id, model_config in session.mobility.node_configurations.iteritems(): if node_id == -1: continue @@ -740,8 +740,8 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): continue config = session.mobility.get_model_config(node_id, model_name) groups = get_config_groups(config, Ns2ScriptedMobility) - mobility_configs[node_id] = groups - return core_pb2.GetMobilityConfigsResponse(configs=mobility_configs) + response.configs[node_id].groups.extend(groups) + return response def GetMobilityConfig(self, request, context): logging.debug("get mobility config: %s", request) diff --git a/daemon/proto/core.proto b/daemon/proto/core.proto index 6f744726..482b9cc2 100644 --- a/daemon/proto/core.proto +++ b/daemon/proto/core.proto @@ -73,7 +73,6 @@ service CoreApi { rpc MobilityAction (MobilityActionRequest) returns (MobilityActionResponse) { } - // service rpc rpc GetServices (GetServicesRequest) returns (GetServicesResponse) { } diff --git a/daemon/tests/test_grpc.py b/daemon/tests/test_grpc.py index 954ba174..d38b4000 100644 --- a/daemon/tests/test_grpc.py +++ b/daemon/tests/test_grpc.py @@ -8,7 +8,7 @@ from core.emulator.emudata import NodeOptions, LinkOptions from core.grpc import core_pb2 from core.enumerations import NodeTypes, EventTypes from core.grpc.client import CoreGrpcClient -from core.mobility import BasicRangeModel +from core.mobility import BasicRangeModel, Ns2ScriptedMobility MODELS = [ "router", @@ -503,3 +503,64 @@ class TestGrpc: # then assert len(response.models) > 0 + + def test_get_mobility_configs(self, grpc_server): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + wlan = session.add_node(_type=NodeTypes.WIRELESS_LAN) + session.mobility.set_model_config(wlan.objid, Ns2ScriptedMobility.name, {}) + + # then + with client.context_connect(): + response = client.get_mobility_configs(session.session_id) + + # then + assert len(response.configs) > 0 + assert wlan.objid in response.configs + + def test_get_mobility_config(self, grpc_server): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + wlan = session.add_node(_type=NodeTypes.WIRELESS_LAN) + session.mobility.set_model_config(wlan.objid, Ns2ScriptedMobility.name, {}) + + # then + with client.context_connect(): + response = client.get_mobility_config(session.session_id, wlan.objid) + + # then + assert len(response.groups) > 0 + + def test_set_mobility_config(self, grpc_server): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + wlan = session.add_node(_type=NodeTypes.WIRELESS_LAN) + config_key = "refresh_ms" + config_value = "60" + + # then + with client.context_connect(): + response = client.set_mobility_config(session.session_id, wlan.objid, {config_key: config_value}) + + # then + assert response.result is True + config = session.mobility.get_model_config(wlan.objid, Ns2ScriptedMobility.name) + assert config[config_key] == config_value + + def test_mobility_action(self, grpc_server): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + wlan = session.add_node(_type=NodeTypes.WIRELESS_LAN) + session.mobility.set_model_config(wlan.objid, Ns2ScriptedMobility.name, {}) + session.instantiate() + + # then + with client.context_connect(): + response = client.mobility_action(session.session_id, wlan.objid, core_pb2.MOBILITY_STOP) + + # then + assert response.result is True From 7299abd64db807c476b55ae5d89e35ea595d2be8 Mon Sep 17 00:00:00 2001 From: bharnden Date: Mon, 25 Mar 2019 14:03:04 -0700 Subject: [PATCH 47/72] grpc added service/events rpc tests --- daemon/core/grpc/server.py | 8 +- daemon/proto/core.proto | 8 +- daemon/tests/test_grpc.py | 240 ++++++++++++++++++++++++++++++++++++- 3 files changed, 246 insertions(+), 10 deletions(-) diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index 80a24f57..7c8cabbe 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -859,15 +859,15 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): context.abort(grpc.StatusCode.NOT_FOUND, "service not found") status = -1 - if request.action == core_pb2.START: + if request.action == core_pb2.SERVICE_START: status = session.services.startup_service(node, service, wait=True) - elif request.action == core_pb2.STOP: + elif request.action == core_pb2.SERVICE_STOP: status = session.services.stop_service(node, service) - elif request.action == core_pb2.RESTART: + elif request.action == core_pb2.SERVICE_RESTART: status = session.services.stop_service(node, service) if not status: status = session.services.startup_service(node, service, wait=True) - elif request.action == core_pb2.VALIDATE: + elif request.action == core_pb2.SERVICE_VALIDATE: status = session.services.validate_service(node, service) result = False diff --git a/daemon/proto/core.proto b/daemon/proto/core.proto index 482b9cc2..9e85ba88 100644 --- a/daemon/proto/core.proto +++ b/daemon/proto/core.proto @@ -646,10 +646,10 @@ enum ServiceValidationMode { } enum ServiceAction { - START = 0; - STOP = 1; - RESTART = 2; - VALIDATE = 3; + SERVICE_START = 0; + SERVICE_STOP = 1; + SERVICE_RESTART = 2; + SERVICE_VALIDATE = 3; } enum MobilityAction { diff --git a/daemon/tests/test_grpc.py b/daemon/tests/test_grpc.py index d38b4000..956117ad 100644 --- a/daemon/tests/test_grpc.py +++ b/daemon/tests/test_grpc.py @@ -1,12 +1,15 @@ -import os import time +from Queue import Queue + import pytest +from core.conf import ConfigShim +from core.data import EventData from core.emane.ieee80211abg import EmaneIeee80211abgModel from core.emulator.emudata import NodeOptions, LinkOptions +from core.enumerations import NodeTypes, EventTypes, ConfigFlags, ExceptionLevels from core.grpc import core_pb2 -from core.enumerations import NodeTypes, EventTypes from core.grpc.client import CoreGrpcClient from core.mobility import BasicRangeModel, Ns2ScriptedMobility @@ -564,3 +567,236 @@ class TestGrpc: # then assert response.result is True + + def test_get_services(self, grpc_server): + # given + client = CoreGrpcClient() + + # then + with client.context_connect(): + response = client.get_services() + + # then + assert len(response.services) > 0 + + def test_get_service_defaults(self, grpc_server): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + + # then + with client.context_connect(): + response = client.get_service_defaults(session.session_id) + + # then + assert len(response.defaults) > 0 + + def test_set_service_defaults(self, grpc_server): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + node_type = "test" + services = ["SSH"] + + # then + with client.context_connect(): + response = client.set_service_defaults(session.session_id, {node_type: services}) + + # then + assert response.result is True + assert session.services.default_services[node_type] == services + + def test_get_node_service(self, grpc_server): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + node = session.add_node() + + # then + with client.context_connect(): + response = client.get_node_service(session.session_id, node.objid, "IPForward") + + # then + assert len(response.service.configs) > 0 + + def test_get_node_service_file(self, grpc_server): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + node = session.add_node() + + # then + with client.context_connect(): + response = client.get_node_service_file(session.session_id, node.objid, "IPForward", "ipforward.sh") + + # then + assert response.data is not None + + def test_set_node_service(self, grpc_server): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + node = session.add_node() + service_name = "IPForward" + validate = ("echo hello",) + + # then + with client.context_connect(): + response = client.set_node_service(session.session_id, node.objid, service_name, (), validate, ()) + + # then + assert response.result is True + service = session.services.get_service(node.objid, service_name, default_service=True) + assert service.validate == validate + + def test_set_node_service_file(self, grpc_server): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + node = session.add_node() + service_name = "IPForward" + file_name = "ipforward.sh" + file_data = "echo hello" + + # then + with client.context_connect(): + response = client.set_node_service_file(session.session_id, node.objid, service_name, file_name, file_data) + + # then + assert response.result is True + service_file = session.services.get_service_file(node, service_name, file_name) + assert service_file.data == file_data + + def test_service_action(self, grpc_server): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + node = session.add_node() + service_name = "IPForward" + + # then + with client.context_connect(): + response = client.service_action(session.session_id, node.objid, service_name, core_pb2.SERVICE_STOP) + + # then + assert response.result is True + + def test_node_events(self, grpc_server): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + node = session.add_node() + node_data = node.data(message_type=0) + queue = Queue() + + def handle_event(event_data): + queue.put(event_data) + + # then + with client.context_connect(): + client.node_events(session.session_id, handle_event) + time.sleep(0.1) + session.broadcast_node(node_data) + + # then + queue.get(timeout=5) + + def test_link_events(self, grpc_server, ip_prefixes): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + wlan = session.add_node(_type=NodeTypes.WIRELESS_LAN) + node = session.add_node() + interface = ip_prefixes.create_interface(node) + session.add_link(node.objid, wlan.objid, interface) + link_data = wlan.all_link_data(0)[0] + queue = Queue() + + def handle_event(event_data): + queue.put(event_data) + + # then + with client.context_connect(): + client.link_events(session.session_id, handle_event) + time.sleep(0.1) + session.broadcast_link(link_data) + + # then + queue.get(timeout=5) + + def test_session_events(self, grpc_server): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + queue = Queue() + + def handle_event(event_data): + queue.put(event_data) + + # then + with client.context_connect(): + client.session_events(session.session_id, handle_event) + time.sleep(0.1) + event = EventData(event_type=EventTypes.RUNTIME_STATE.value, time="%s" % time.time()) + session.broadcast_event(event) + + # then + queue.get(timeout=5) + + def test_config_events(self, grpc_server): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + queue = Queue() + + def handle_event(event_data): + queue.put(event_data) + + # then + with client.context_connect(): + client.config_events(session.session_id, handle_event) + time.sleep(0.1) + session_config = session.options.get_configs() + config_data = ConfigShim.config_data(0, None, ConfigFlags.UPDATE.value, session.options, session_config) + session.broadcast_config(config_data) + + # then + queue.get(timeout=5) + + def test_exception_events(self, grpc_server): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + queue = Queue() + + def handle_event(event_data): + queue.put(event_data) + + # then + with client.context_connect(): + client.exception_events(session.session_id, handle_event) + time.sleep(0.1) + session.exception(ExceptionLevels.FATAL, "test", None, "exception message") + + # then + queue.get(timeout=5) + + def test_file_events(self, grpc_server): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + node = session.add_node() + queue = Queue() + + def handle_event(event_data): + queue.put(event_data) + + # then + with client.context_connect(): + client.file_events(session.session_id, handle_event) + time.sleep(0.1) + file_data = session.services.get_service_file(node, "IPForward", "ipforward.sh") + session.broadcast_file(file_data) + + # then + queue.get(timeout=5) From fdc0362ba5e4fc631cd3e1f0cc28bf38149e46f3 Mon Sep 17 00:00:00 2001 From: bharnden Date: Mon, 25 Mar 2019 17:16:54 -0700 Subject: [PATCH 48/72] updated proto enums to be prefixed with a contextual name, since they all derive from the module directly --- daemon/core/grpc/client.py | 10 +++- daemon/core/grpc/server.py | 109 +------------------------------------ daemon/proto/core.proto | 72 ++++++++++++------------ daemon/tests/test_grpc.py | 4 +- 4 files changed, 48 insertions(+), 147 deletions(-) diff --git a/daemon/core/grpc/client.py b/daemon/core/grpc/client.py index 42e73e3b..026b1f22 100644 --- a/daemon/core/grpc/client.py +++ b/daemon/core/grpc/client.py @@ -136,15 +136,21 @@ class CoreGrpcClient(object): def add_link(self, session, node_one, node_two, interface_one=None, interface_two=None, link_options=None): interface_one_proto = None if interface_one is not None: + mac = interface_one.mac + if mac is not None: + mac = str(mac) interface_one_proto = core_pb2.Interface( - id=interface_one.id, name=interface_one.name, mac=str(interface_one.mac), + id=interface_one.id, name=interface_one.name, mac=mac, ip4=interface_one.ip4, ip4mask=interface_one.ip4_mask, ip6=interface_one.ip6, ip6mask=interface_one.ip6_mask) interface_two_proto = None if interface_two is not None: + mac = interface_two.mac + if mac is not None: + mac = str(mac) interface_two_proto = core_pb2.Interface( - id=interface_two.id, name=interface_two.name, mac=str(interface_two.mac), + id=interface_two.id, name=interface_two.name, mac=mac, ip4=interface_two.ip4, ip4mask=interface_two.ip4_mask, ip6=interface_two.ip6, ip6mask=interface_two.ip6_mask) diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index 7c8cabbe..2511d9f8 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -4,20 +4,17 @@ import os import tempfile import time from Queue import Queue, Empty -from itertools import repeat import grpc from concurrent import futures -from core.conf import ConfigShim -from core.data import ConfigData, FileData from core.emulator.emudata import NodeOptions, InterfaceData, LinkOptions -from core.enumerations import NodeTypes, EventTypes, LinkTypes, MessageFlags, ConfigFlags, ConfigDataTypes +from core.enumerations import NodeTypes, EventTypes, LinkTypes from core.grpc import core_pb2 from core.grpc import core_pb2_grpc from core.misc import nodeutils from core.mobility import BasicRangeModel, Ns2ScriptedMobility -from core.service import ServiceManager, ServiceShim +from core.service import ServiceManager _ONE_DAY_IN_SECONDS = 60 * 60 * 24 @@ -99,108 +96,6 @@ def convert_link(session, link_data): ) -def send_objects(session): - time.sleep(1) - # find all nodes and links - nodes_data = [] - links_data = [] - with session._objects_lock: - for obj in session.objects.itervalues(): - node_data = obj.data(message_type=MessageFlags.ADD.value) - if node_data: - nodes_data.append(node_data) - - node_links = obj.all_link_data(flags=MessageFlags.ADD.value) - for link_data in node_links: - links_data.append(link_data) - - # send all nodes first, so that they will exist for any links - for node_data in nodes_data: - session.broadcast_node(node_data) - - for link_data in links_data: - session.broadcast_link(link_data) - - # send mobility model info - for node_id in session.mobility.nodes(): - for model_name, config in session.mobility.get_all_configs(node_id).iteritems(): - model_class = session.mobility.models[model_name] - logging.debug("mobility config: node(%s) class(%s) values(%s)", node_id, model_class, config) - config_data = ConfigShim.config_data(0, node_id, ConfigFlags.UPDATE.value, model_class, config) - session.broadcast_config(config_data) - - # send emane model info - for node_id in session.emane.nodes(): - for model_name, config in session.emane.get_all_configs(node_id).iteritems(): - model_class = session.emane.models[model_name] - logging.debug("emane config: node(%s) class(%s) values(%s)", node_id, model_class, config) - config_data = ConfigShim.config_data(0, node_id, ConfigFlags.UPDATE.value, model_class, config) - session.broadcast_config(config_data) - - # service customizations - service_configs = session.services.all_configs() - for node_id, service in service_configs: - opaque = "service:%s" % service.name - data_types = tuple(repeat(ConfigDataTypes.STRING.value, len(ServiceShim.keys))) - node = session.get_object(node_id) - values = ServiceShim.tovaluelist(node, service) - config_data = ConfigData( - message_type=0, - node=node_id, - object=session.services.name, - type=ConfigFlags.UPDATE.value, - data_types=data_types, - data_values=values, - session=str(session.session_id), - opaque=opaque - ) - session.broadcast_config(config_data) - - for file_name, config_data in session.services.all_files(service): - file_data = FileData( - message_type=MessageFlags.ADD.value, - node=node_id, - name=str(file_name), - type=opaque, - data=str(config_data) - ) - session.broadcast_file(file_data) - - # TODO: send location info - - # send hook scripts - for state in sorted(session._hooks.keys()): - for file_name, config_data in session._hooks[state]: - file_data = FileData( - message_type=MessageFlags.ADD.value, - name=str(file_name), - type="hook:%s" % state, - data=str(config_data) - ) - session.broadcast_file(file_data) - - # send session configuration - session_config = session.options.get_configs() - config_data = ConfigShim.config_data(0, None, ConfigFlags.UPDATE.value, session.options, session_config) - session.broadcast_config(config_data) - - # send session metadata - configs = session.metadata.get_configs() - if configs: - data_values = "|".join(["%s=%s" % item for item in configs.iteritems()]) - data_types = tuple(ConfigDataTypes.STRING.value for _ in session.metadata.get_configs()) - config_data = ConfigData( - message_type=0, - object=session.metadata.name, - type=ConfigFlags.NONE.value, - data_types=data_types, - data_values=data_values - ) - session.broadcast_config(config_data) - - logging.debug("informed GUI about %d nodes and %d links", len(nodes_data), len(links_data)) - - class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): def __init__(self, coreemu): super(CoreGrpcServer, self).__init__() diff --git a/daemon/proto/core.proto b/daemon/proto/core.proto index 9e85ba88..31ea353d 100644 --- a/daemon/proto/core.proto +++ b/daemon/proto/core.proto @@ -602,47 +602,47 @@ message OpenXmlResponse { // data structures for messages below enum MessageType { - NOTHING = 0; - ADD = 1; - DELETE = 2; - CRI = 4; - LOCAL = 8; - STRING = 16; - TEXT = 32; - TTY = 64; + MESSAGE_NONE = 0; + MESSAGE_ADD = 1; + MESSAGE_DELETE = 2; + MESSAGE_CRI = 4; + MESSAGE_LOCAL = 8; + MESSAGE_STRING = 16; + MESSAGE_TEXT = 32; + MESSAGE_TTY = 64; } enum SessionState { - NONE = 0; - DEFINITION = 1; - CONFIGURATION = 2; - INSTANTIATION = 3; - RUNTIME = 4; - DATACOLLECT = 5; - SHUTDOWN = 6; + STATE_NONE = 0; + STATE_DEFINITION = 1; + STATE_CONFIGURATION = 2; + STATE_INSTANTIATION = 3; + STATE_RUNTIME = 4; + STATE_DATACOLLECT = 5; + STATE_SHUTDOWN = 6; } enum NodeType { - DEFAULT = 0; - PHYSICAL = 1; - TBD = 3; - SWITCH = 4; - HUB = 5; - WIRELESS_LAN = 6; - RJ45 = 7; - TUNNEL = 8; - KTUNNEL = 9; - EMANE = 10; - TAP_BRIDGE = 11; - PEER_TO_PEER = 12; - CONTROL_NET = 13; - EMANE_NET = 14; + NODE_DEFAULT = 0; + NODE_PHYSICAL = 1; + NODE_TBD = 3; + NODE_SWITCH = 4; + NODE_HUB = 5; + NODE_WIRELESS_LAN = 6; + NODE_RJ45 = 7; + NODE_TUNNEL = 8; + NODE_KTUNNEL = 9; + NODE_EMANE = 10; + NODE_TAP_BRIDGE = 11; + NODE_PEER_TO_PEER = 12; + NODE_CONTROL_NET = 13; + NODE_EMANE_NET = 14; } enum ServiceValidationMode { - BLOCKING = 0; - NON_BLOCKING = 1; - TIMER = 2; + VALIDATION_BLOCKING = 0; + VALIDATION_NON_BLOCKING = 1; + VALIDATION_TIMER = 2; } enum ServiceAction { @@ -660,10 +660,10 @@ enum MobilityAction { enum ExceptionLevel { EXCEPTION_DEFAULT = 0; - FATAL = 1; - ERROR = 2; - WARNING = 3; - NOTICE = 4; + EXCEPTION_FATAL = 1; + EXCEPTION_ERROR = 2; + EXCEPTION_WARNING = 3; + EXCEPTION_NOTICE = 4; } message Hook { diff --git a/daemon/tests/test_grpc.py b/daemon/tests/test_grpc.py index 956117ad..74aeeec1 100644 --- a/daemon/tests/test_grpc.py +++ b/daemon/tests/test_grpc.py @@ -72,7 +72,7 @@ class TestGrpc: response = client.get_session(session.session_id) # then - assert response.session.state == core_pb2.DEFINITION + assert response.session.state == core_pb2.STATE_DEFINITION assert len(response.session.nodes) == 1 assert len(response.session.links) == 0 @@ -277,7 +277,7 @@ class TestGrpc: # then with client.context_connect(): - response = client.save_xml(session.session_id, str(tmp)) + client.save_xml(session.session_id, str(tmp)) # then assert tmp.exists() From 51d93db586bedcc1198944d9ed5e6d2c9a8ac133 Mon Sep 17 00:00:00 2001 From: bharnden Date: Mon, 25 Mar 2019 22:00:08 -0700 Subject: [PATCH 49/72] grpc updated get_node method for server and added some more test cases --- daemon/core/grpc/server.py | 14 +++++--- daemon/tests/test_grpc.py | 71 ++++++++++++++++++++++++++++++-------- 2 files changed, 66 insertions(+), 19 deletions(-) diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index 2511d9f8..2d1255a2 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -130,14 +130,14 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): def get_session(self, _id, context): session = self.coreemu.sessions.get(_id) if not session: - context.abort(grpc.StatusCode.NOT_FOUND, "session not found") + context.abort(grpc.StatusCode.NOT_FOUND, "session {} not found".format(_id)) return session def get_node(self, session, _id, context): - node = session.get_object(_id) - if not node: - context.abort(grpc.StatusCode.NOT_FOUND, "node not found") - return node + try: + return session.get_object(_id) + except KeyError: + context.abort(grpc.StatusCode.NOT_FOUND, "node {} not found".format(_id)) def CreateSession(self, request, context): logging.debug("create session: %s", request) @@ -509,6 +509,10 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): def AddLink(self, request, context): logging.debug("add link: %s", request) session = self.get_session(request.session, context) + + # validate node exist + self.get_node(session, request.link.node_one, context) + self.get_node(session, request.link.node_two, context) node_one = request.link.node_one node_two = request.link.node_two diff --git a/daemon/tests/test_grpc.py b/daemon/tests/test_grpc.py index 74aeeec1..af06fde9 100644 --- a/daemon/tests/test_grpc.py +++ b/daemon/tests/test_grpc.py @@ -2,6 +2,7 @@ import time from Queue import Queue +import grpc import pytest from core.conf import ConfigShim @@ -47,18 +48,24 @@ class TestGrpc: assert response.id == session_id assert session.session_id == session_id - def test_delete_session(self, grpc_server): + @pytest.mark.parametrize("session_id, expected", [ + (None, True), + (6013, False) + ]) + def test_delete_session(self, grpc_server, session_id, expected): # given client = CoreGrpcClient() session = grpc_server.coreemu.create_session() + if session_id is None: + session_id = session.session_id # then with client.context_connect(): - response = client.delete_session(session.session_id) + response = client.delete_session(session_id) # then - assert response.result is True - assert grpc_server.coreemu.sessions.get(session.session_id) is None + assert response.result is expected + assert grpc_server.coreemu.sessions.get(session_id) is None def test_get_session(self, grpc_server): # given @@ -203,7 +210,11 @@ class TestGrpc: # then assert response.node.id == node.objid - def test_edit_node(self, grpc_server): + @pytest.mark.parametrize("node_id, expected", [ + (1, True), + (2, False) + ]) + def test_edit_node(self, grpc_server, node_id, expected): # given client = CoreGrpcClient() session = grpc_server.coreemu.create_session() @@ -214,14 +225,19 @@ class TestGrpc: with client.context_connect(): node_options = NodeOptions() node_options.set_position(x, y) - response = client.edit_node(session.session_id, node.objid, node_options) + response = client.edit_node(session.session_id, node_id, node_options) # then - assert response.result is True - assert node.position.x == x - assert node.position.y == y + assert response.result is expected + if expected is True: + assert node.position.x == x + assert node.position.y == y - def test_delete_node(self, grpc_server): + @pytest.mark.parametrize("node_id, expected", [ + (1, True), + (2, False) + ]) + def test_delete_node(self, grpc_server, node_id, expected): # given client = CoreGrpcClient() session = grpc_server.coreemu.create_session() @@ -229,12 +245,13 @@ class TestGrpc: # then with client.context_connect(): - response = client.delete_node(session.session_id, node.objid) + response = client.delete_node(session.session_id, node_id) # then - assert response.result is True - with pytest.raises(KeyError): - assert session.get_object(node.objid) + assert response.result is expected + if expected is True: + with pytest.raises(KeyError): + assert session.get_object(node.objid) def test_get_hooks(self, grpc_server): # given @@ -313,6 +330,20 @@ class TestGrpc: # then assert len(response.links) == 1 + def test_get_node_links_exception(self, grpc_server, ip_prefixes): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + switch = session.add_node(_type=NodeTypes.SWITCH) + node = session.add_node() + interface = ip_prefixes.create_interface(node) + session.add_link(node.objid, switch.objid, interface) + + # then + with pytest.raises(grpc.RpcError): + with client.context_connect(): + client.get_node_links(session.session_id, 3) + def test_add_link(self, grpc_server, ip_prefixes): # given client = CoreGrpcClient() @@ -330,6 +361,18 @@ class TestGrpc: assert response.result is True assert len(switch.all_link_data(0)) == 1 + def test_add_link_exception(self, grpc_server, ip_prefixes): + # given + client = CoreGrpcClient() + session = grpc_server.coreemu.create_session() + node = session.add_node() + + # then + interface = ip_prefixes.create_interface(node) + with pytest.raises(grpc.RpcError): + with client.context_connect(): + client.add_link(session.session_id, 1, 3, interface) + def test_edit_link(self, grpc_server, ip_prefixes): # given client = CoreGrpcClient() From 088230515ee3d02b9149d6a6275050a218eae1f2 Mon Sep 17 00:00:00 2001 From: bharnden Date: Mon, 25 Mar 2019 22:14:56 -0700 Subject: [PATCH 50/72] grpc initial documentation for grpc client --- daemon/core/grpc/client.py | 56 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/daemon/core/grpc/client.py b/daemon/core/grpc/client.py index 026b1f22..b65cdeaf 100644 --- a/daemon/core/grpc/client.py +++ b/daemon/core/grpc/client.py @@ -1,3 +1,7 @@ +""" +gRpc client for interfacing with CORE, when gRPC mode is enabled. +""" + from __future__ import print_function import logging @@ -14,6 +18,13 @@ from core.grpc import core_pb2_grpc def stream_listener(stream, handler): + """ + Listen for stream events and provide them to the handler. + + :param stream: grpc stream that will provide events + :param handler: function that handles an event + :return: nothing + """ try: for event in stream: handler(event) @@ -25,29 +36,74 @@ def stream_listener(stream, handler): def start_streamer(stream, handler): + """ + Convenience method for starting a grpc stream thread for handling streamed events. + + :param stream: grpc stream that will provide events + :param handler: function that handles an event + :return: nothing + """ thread = threading.Thread(target=stream_listener, args=(stream, handler)) thread.daemon = True thread.start() class CoreGrpcClient(object): + """ + Provides convenience methods for interfacing with the CORE grpc server. + """ + def __init__(self, address="localhost:50051"): + """ + Creates a CoreGrpcClient instance. + + :param str address: grpc server address to connect to + """ self.address = address self.stub = None self.channel = None def create_session(self, _id=None): + """ + Create a session. + + :param int _id: id for session, defaults to None will be created for you + :return: response with created session id + :rtype: core_pb2.CreateSessionResponse + """ request = core_pb2.CreateSessionRequest(id=_id) return self.stub.CreateSession(request) def delete_session(self, _id): + """ + Delete a session. + + :param int _id: id of session to delete + :return: response with result of deletion success or failure + :rtype: core_pb2.DeleteSessionResponse + :raises grpc.RpcError: when session doesn't exist + """ request = core_pb2.DeleteSessionRequest(id=_id) return self.stub.DeleteSession(request) def get_sessions(self): + """ + Retrieves all currently known sessions. + + :return: response with a list of currently known session, their state and number of nodes + :rtype: core_pb2.GetSessionsResponse + """ return self.stub.GetSessions(core_pb2.GetSessionsRequest()) def get_session(self, _id): + """ + Retrieve a session. + + :param int _id: id of session to get data for + :return: response with sessions state, nodes, and links + :rtype: core_pb2.GetSessionResponse + :raises grpc.RpcError: when session doesn't exist + """ request = core_pb2.GetSessionRequest(id=_id) return self.stub.GetSession(request) From 0677a5c7184e33c06e191ddc459667aedcafb658 Mon Sep 17 00:00:00 2001 From: bharnden Date: Tue, 26 Mar 2019 08:58:29 -0700 Subject: [PATCH 51/72] updated makefiles to build/clean protobuf files --- .gitignore | 1 - configure.ac | 1 + daemon/Makefile.am | 4 +++- daemon/proto/Makefile | 5 ----- daemon/proto/Makefile.am | 5 +++++ 5 files changed, 9 insertions(+), 7 deletions(-) delete mode 100644 daemon/proto/Makefile create mode 100644 daemon/proto/Makefile.am diff --git a/.gitignore b/.gitignore index 7ff8c547..dbf9e4c3 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,6 @@ .version.date Makefile !kernel/**/Makefile -!daemon/proto/Makefile Makefile.in aclocal.m4 autom4te.cache diff --git a/configure.ac b/configure.ac index 10c1c245..79c5ecc0 100644 --- a/configure.ac +++ b/configure.ac @@ -240,6 +240,7 @@ AC_CONFIG_FILES([Makefile daemon/Makefile daemon/doc/Makefile daemon/doc/conf.py + daemon/proto/Makefile netns/Makefile netns/version.h ns3/Makefile],) diff --git a/daemon/Makefile.am b/daemon/Makefile.am index a5fe9cb9..769098bb 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -11,9 +11,11 @@ SETUPPY = setup.py SETUPPYFLAGS = -v if WANT_DOCS - SUBDIRS = doc + DOCS = doc endif +SUBDIRS = proto $(DOCS) + SCRIPT_FILES := $(notdir $(wildcard scripts/*)) MAN_FILES := $(notdir $(wildcard ../man/*.1)) diff --git a/daemon/proto/Makefile b/daemon/proto/Makefile deleted file mode 100644 index e4edb47a..00000000 --- a/daemon/proto/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -all: - python -m grpc_tools.protoc -I . --python_out=../core/grpc --grpc_python_out=../core/grpc core.proto - -clean: - rm core_pb2* diff --git a/daemon/proto/Makefile.am b/daemon/proto/Makefile.am new file mode 100644 index 00000000..bcf62a2c --- /dev/null +++ b/daemon/proto/Makefile.am @@ -0,0 +1,5 @@ +all: + $(PYTHON) -m grpc_tools.protoc -I . --python_out=../core/grpc --grpc_python_out=../core/grpc core.proto + +clean: + -rm -f ../core/grpc/core_pb2* From cb8630bb03c269aff854fda1cbd5ba7814abb8e9 Mon Sep 17 00:00:00 2001 From: bharnden Date: Tue, 26 Mar 2019 21:41:34 -0700 Subject: [PATCH 52/72] updated grpc client documentation --- daemon/core/grpc/client.py | 190 ++++++++++++++++++++++++++++++++++++- 1 file changed, 187 insertions(+), 3 deletions(-) diff --git a/daemon/core/grpc/client.py b/daemon/core/grpc/client.py index b65cdeaf..9c49cb8e 100644 --- a/daemon/core/grpc/client.py +++ b/daemon/core/grpc/client.py @@ -67,7 +67,7 @@ class CoreGrpcClient(object): """ Create a session. - :param int _id: id for session, defaults to None will be created for you + :param int _id: id for session, default is None and one will be created for you :return: response with created session id :rtype: core_pb2.CreateSessionResponse """ @@ -78,7 +78,7 @@ class CoreGrpcClient(object): """ Delete a session. - :param int _id: id of session to delete + :param int _id: id of session :return: response with result of deletion success or failure :rtype: core_pb2.DeleteSessionResponse :raises grpc.RpcError: when session doesn't exist @@ -99,7 +99,7 @@ class CoreGrpcClient(object): """ Retrieve a session. - :param int _id: id of session to get data for + :param int _id: id of session :return: response with sessions state, nodes, and links :rtype: core_pb2.GetSessionResponse :raises grpc.RpcError: when session doesn't exist @@ -108,57 +108,166 @@ class CoreGrpcClient(object): return self.stub.GetSession(request) def get_session_options(self, _id): + """ + Retrieve session options. + + :param int _id: id of session + :return: response with a list of configuration groups + :rtype: core_pb2.GetSessionOptionsResponse + :raises grpc.RpcError: when session doesn't exist + """ request = core_pb2.GetSessionOptionsRequest(id=_id) return self.stub.GetSessionOptions(request) def set_session_options(self, _id, config): + """ + Set options for a session. + + :param int _id: id of session + :param dict[str, str] config: configuration values to set + :return: response with result of success or failure + :rtype: core_pb2.SetSessionOptionsResponse + :raises grpc.RpcError: when session doesn't exist + """ request = core_pb2.SetSessionOptionsRequest(id=_id, config=config) return self.stub.SetSessionOptions(request) def get_session_location(self, _id): + """ + Get session location. + + :param int _id: id of session + :return: response with session position reference and scale + :rtype: core_pb2.GetSessionLocationResponse + :raises grpc.RpcError: when session doesn't exist + """ request = core_pb2.GetSessionLocationRequest(id=_id) return self.stub.GetSessionLocation(request) def set_session_location(self, _id, x=None, y=None, z=None, lat=None, lon=None, alt=None, scale=None): + """ + Set session location. + + :param int _id: id of session + :param float x: x position + :param float y: y position + :param float z: z position + :param float lat: latitude position + :param float lon: longitude position + :param float alt: altitude position + :param float scale: geo scale + :return: response with result of success or failure + :rtype: core_pb2.SetSessionLocationResponse + :raises grpc.RpcError: when session doesn't exist + """ position = core_pb2.Position(x=x, y=y, z=z, lat=lat, lon=lon, alt=alt) request = core_pb2.SetSessionLocationRequest(id=_id, position=position, scale=scale) return self.stub.SetSessionLocation(request) def set_session_state(self, _id, state): + """ + Set session state. + + :param int _id: id of session + :param EventTypes state: session state to transition to + :return: response with result of success or failure + :rtype: core_pb2.SetSessionStateResponse + :raises grpc.RpcError: when session doesn't exist + """ request = core_pb2.SetSessionStateRequest(id=_id, state=state.value) return self.stub.SetSessionState(request) def node_events(self, _id, handler): + """ + Listen for session node events. + + :param int _id: id of session + :param handler: handler for every event + :return: nothing + :raises grpc.RpcError: when session doesn't exist + """ request = core_pb2.NodeEventsRequest(id=_id) stream = self.stub.NodeEvents(request) start_streamer(stream, handler) def link_events(self, _id, handler): + """ + Listen for session link events. + + :param int _id: id of session + :param handler: handler for every event + :return: nothing + :raises grpc.RpcError: when session doesn't exist + """ request = core_pb2.LinkEventsRequest(id=_id) stream = self.stub.LinkEvents(request) start_streamer(stream, handler) def session_events(self, _id, handler): + """ + Listen for session events. + + :param int _id: id of session + :param handler: handler for every event + :return: nothing + :raises grpc.RpcError: when session doesn't exist + """ request = core_pb2.SessionEventsRequest(id=_id) stream = self.stub.SessionEvents(request) start_streamer(stream, handler) def config_events(self, _id, handler): + """ + Listen for session config events. + + :param int _id: id of session + :param handler: handler for every event + :return: nothing + :raises grpc.RpcError: when session doesn't exist + """ request = core_pb2.ConfigEventsRequest(id=_id) stream = self.stub.ConfigEvents(request) start_streamer(stream, handler) def exception_events(self, _id, handler): + """ + Listen for session exception events. + + :param int _id: id of session + :param handler: handler for every event + :return: nothing + :raises grpc.RpcError: when session doesn't exist + """ request = core_pb2.ExceptionEventsRequest(id=_id) stream = self.stub.ExceptionEvents(request) start_streamer(stream, handler) def file_events(self, _id, handler): + """ + Listen for session file events. + + :param int _id: id of session + :param handler: handler for every event + :return: nothing + :raises grpc.RpcError: when session doesn't exist + """ request = core_pb2.FileEventsRequest(id=_id) stream = self.stub.FileEvents(request) start_streamer(stream, handler) def add_node(self, session, _type=NodeTypes.DEFAULT, _id=None, node_options=None, emane=None): + """ + Add node to session. + + :param int session: session id + :param NodeTypes _type: type of node to create + :param int _id: id for node, defaults to None, which will generate an id + :param NodeOptions node_options: options for node including position, services, and model + :param str emane: emane model, if an emane node + :return: response with node id + :rtype: core_pb2.AddNodeResponse + :raises grpc.RpcError: when session doesn't exist + """ if not node_options: node_options = NodeOptions() position = core_pb2.Position( @@ -171,10 +280,29 @@ class CoreGrpcClient(object): return self.stub.AddNode(request) def get_node(self, session, _id): + """ + Get node details. + + :param int session: session id + :param int _id: node id + :return: response with node details + :rtype: core_pb2.GetNodeResponse + :raises grpc.RpcError: when session or node doesn't exist + """ request = core_pb2.GetNodeRequest(session=session, id=_id) return self.stub.GetNode(request) def edit_node(self, session, _id, node_options): + """ + Edit a node, currently only changes position. + + :param int session: session id + :param int _id: node id + :param NodeOptions node_options: options for node including position, services, and model + :return: response with result of success or failure + :rtype: core_pb2.EditNodeResponse + :raises grpc.RpcError: when session or node doesn't exist + """ position = core_pb2.Position( x=node_options.x, y=node_options.y, lat=node_options.lat, lon=node_options.lon, alt=node_options.alt) @@ -182,14 +310,45 @@ class CoreGrpcClient(object): return self.stub.EditNode(request) def delete_node(self, session, _id): + """ + Delete node from session. + + :param int session: session id + :param int _id: node id + :return: response with result of success or failure + :rtype: core_pb2.DeleteNodeResponse + :raises grpc.RpcError: when session doesn't exist + """ request = core_pb2.DeleteNodeRequest(session=session, id=_id) return self.stub.DeleteNode(request) def get_node_links(self, session, _id): + """ + Get current links for a node. + + :param int session: session id + :param int _id: node id + :return: response with a list of links + :rtype: core_pb2.GetNodeLinksResponse + :raises grpc.RpcError: when session or node doesn't exist + """ request = core_pb2.GetNodeLinksRequest(session=session, id=_id) return self.stub.GetNodeLinks(request) def add_link(self, session, node_one, node_two, interface_one=None, interface_two=None, link_options=None): + """ + Add a link between nodes. + + :param int session: session id + :param int node_one: node one id + :param int node_two: node two id + :param InterfaceData interface_one: node one interface data + :param InterfaceData interface_two: node two interface data + :param LinkOptions link_options: options for link (jitter, bandwidth, etc) + :return: response with result of success or failure + :rtype: core_pb2.AddLinkResponse + :raises grpc.RpcError: when session or one of the nodes don't exist + """ interface_one_proto = None if interface_one is not None: mac = interface_one.mac @@ -233,6 +392,19 @@ class CoreGrpcClient(object): return self.stub.AddLink(request) def edit_link(self, session, node_one, node_two, link_options, interface_one=None, interface_two=None): + """ + Edit a link between nodes. + + :param int session: session id + :param int node_one: node one id + :param int node_two: node two id + :param LinkOptions link_options: options for link (jitter, bandwidth, etc) + :param int interface_one: node one interface id + :param int interface_two: node two interface id + :return: response with result of success or failure + :rtype: core_pb2.EditLinkResponse + :raises grpc.RpcError: when session or one of the nodes don't exist + """ options = core_pb2.LinkOptions( delay=link_options.delay, bandwidth=link_options.bandwidth, @@ -252,6 +424,18 @@ class CoreGrpcClient(object): return self.stub.EditLink(request) def delete_link(self, session, node_one, node_two, interface_one=None, interface_two=None): + """ + Delete a link between nodes. + + :param int session: session id + :param int node_one: node one id + :param int node_two: node two id + :param int interface_one: node one interface id + :param int interface_two: node two interface id + :return: response with result of success or failure + :rtype: core_pb2.DeleteLinkResponse + :raises grpc.RpcError: when session doesn't exist + """ request = core_pb2.DeleteLinkRequest( session=session, node_one=node_one, node_two=node_two, interface_one=interface_one, interface_two=interface_two) From 5161e4812a37a4a1000ba50a74646f5cbf1c4c30 Mon Sep 17 00:00:00 2001 From: bharnden Date: Thu, 28 Mar 2019 21:47:30 -0700 Subject: [PATCH 53/72] grpc updates to use proto values from client, rather than trying to squeeze in existing data types --- daemon/core/grpc/client.py | 48 ++++++++++++++------------------------ daemon/core/grpc/server.py | 30 ++++++++++++++++-------- daemon/proto/core.proto | 22 +++++++---------- daemon/tests/test_grpc.py | 22 ++++------------- 4 files changed, 52 insertions(+), 70 deletions(-) diff --git a/daemon/core/grpc/client.py b/daemon/core/grpc/client.py index 9c49cb8e..3cf9881b 100644 --- a/daemon/core/grpc/client.py +++ b/daemon/core/grpc/client.py @@ -169,12 +169,12 @@ class CoreGrpcClient(object): Set session state. :param int _id: id of session - :param EventTypes state: session state to transition to + :param core_pb2.SessionState state: session state to transition to :return: response with result of success or failure :rtype: core_pb2.SetSessionStateResponse :raises grpc.RpcError: when session doesn't exist """ - request = core_pb2.SetSessionStateRequest(id=_id, state=state.value) + request = core_pb2.SetSessionStateRequest(id=_id, state=state) return self.stub.SetSessionState(request) def node_events(self, _id, handler): @@ -255,28 +255,17 @@ class CoreGrpcClient(object): stream = self.stub.FileEvents(request) start_streamer(stream, handler) - def add_node(self, session, _type=NodeTypes.DEFAULT, _id=None, node_options=None, emane=None): + def add_node(self, session, node): """ Add node to session. :param int session: session id - :param NodeTypes _type: type of node to create - :param int _id: id for node, defaults to None, which will generate an id - :param NodeOptions node_options: options for node including position, services, and model - :param str emane: emane model, if an emane node + :param core_pb2.Node: node to add :return: response with node id :rtype: core_pb2.AddNodeResponse :raises grpc.RpcError: when session doesn't exist """ - if not node_options: - node_options = NodeOptions() - position = core_pb2.Position( - x=node_options.x, y=node_options.y, - lat=node_options.lat, lon=node_options.lon, alt=node_options.alt) - request = core_pb2.AddNodeRequest( - session=session, type=_type.value, name=node_options.name, - model=node_options.model, icon=node_options.icon, services=node_options.services, - opaque=node_options.opaque, emane=emane, position=position) + request = core_pb2.AddNodeRequest(session=session, node=node) return self.stub.AddNode(request) def get_node(self, session, _id): @@ -446,7 +435,7 @@ class CoreGrpcClient(object): return self.stub.GetHooks(request) def add_hook(self, session, state, file_name, file_data): - hook = core_pb2.Hook(state=state.value, file=file_name, data=file_data) + hook = core_pb2.Hook(state=state, file=file_name, data=file_data) request = core_pb2.AddHookRequest(session=session, hook=hook) return self.stub.AddHook(request) @@ -525,16 +514,13 @@ class CoreGrpcClient(object): request = core_pb2.GetEmaneModelsRequest(session=session) return self.stub.GetEmaneModels(request) - def get_emane_model_config(self, session, _id, model, interface_id=None): - if interface_id is not None: - _id = _id * 1000 + interface_id - request = core_pb2.GetEmaneModelConfigRequest(session=session, id=_id, model=model) + def get_emane_model_config(self, session, _id, model, interface_id=-1): + request = core_pb2.GetEmaneModelConfigRequest(session=session, id=_id, model=model, interface=interface_id) return self.stub.GetEmaneModelConfig(request) - def set_emane_model_config(self, session, _id, model, config, interface_id=None): - if interface_id is not None: - _id = _id * 1000 + interface_id - request = core_pb2.SetEmaneModelConfigRequest(session=session, id=_id, model=model, config=config) + def set_emane_model_config(self, session, _id, model, config, interface_id=-1): + request = core_pb2.SetEmaneModelConfigRequest( + session=session, id=_id, model=model, config=config, interface=interface_id) return self.stub.SetEmaneModelConfig(request) def get_emane_model_configs(self, session): @@ -593,7 +579,7 @@ def main(): print("created session: {}".format(session_data)) print("default services: {}".format(client.get_service_defaults(session_data.id))) print("emane models: {}".format(client.get_emane_models(session_data.id))) - print("add hook: {}".format(client.add_hook(session_data.id, EventTypes.RUNTIME_STATE, "test", "echo hello"))) + print("add hook: {}".format(client.add_hook(session_data.id, core_pb2.STATE_RUNTIME, "test", "echo hello"))) print("hooks: {}".format(client.get_hooks(session_data.id))) response = client.get_sessions() @@ -618,10 +604,11 @@ def main(): print("get location: {}".format(client.get_session_location(session_data.id))) # change session state - print("set session state: {}".format(client.set_session_state(session_data.id, EventTypes.CONFIGURATION_STATE))) + print("set session state: {}".format(client.set_session_state(session_data.id, core_pb2.STATE_CONFIGURATION))) # create switch node - response = client.add_node(session_data.id, _type=NodeTypes.SWITCH) + switch = core_pb2.Node(type=core_pb2.NODE_SWITCH) + response = client.add_node(session_data.id, switch) print("created switch: {}".format(response)) switch_id = response.id @@ -629,7 +616,8 @@ def main(): prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16") for _ in xrange(2): - response = client.add_node(session_data.id) + node = core_pb2.Node() + response = client.add_node(session_data.id, node) print("created node: {}".format(response)) node_id = response.id node_options = NodeOptions() @@ -657,7 +645,7 @@ def main(): print("get node links: {}".format(client.get_node_links(session_data.id, node_id))) # change session state - print("set session state: {}".format(client.set_session_state(session_data.id, EventTypes.INSTANTIATION_STATE))) + print("set session state: {}".format(client.set_session_state(session_data.id, core_pb2.STATE_INSTANTIATION))) # import pdb; pdb.set_trace() # get session diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index 2d1255a2..58316d9c 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -57,6 +57,13 @@ def get_links(session, node): return links +def get_emane_model_id(_id, interface): + if interface >= 0: + return _id * 1000 + interface + else: + return _id + + def convert_link(session, link_data): interface_one = None if link_data.interface1_id is not None: @@ -427,24 +434,25 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): logging.debug("add node: %s", request) session = self.get_session(request.session, context) - node_id = request.id - node_type = request.type + node_proto = request.node + node_id = node_proto.id + node_type = node_proto.type if node_type is None: node_type = NodeTypes.DEFAULT.value node_type = NodeTypes(node_type) - node_options = NodeOptions(name=request.name, model=request.model) - node_options.icon = request.icon - node_options.opaque = request.opaque - node_options.services = request.services + node_options = NodeOptions(name=node_proto.name, model=node_proto.model) + node_options.icon = node_proto.icon + node_options.opaque = node_proto.opaque + node_options.services = node_proto.services - position = request.position + position = node_proto.position node_options.set_position(position.x, position.y) node_options.set_location(position.lat, position.lon, position.alt) node = session.add_node(_type=node_type, _id=node_id, node_options=node_options) # configure emane if provided - emane_model = request.emane + emane_model = node_proto.emane if emane_model: session.emane.set_model_config(node_id, emane_model) @@ -816,14 +824,16 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): logging.debug("get emane model config: %s", request) session = self.get_session(request.session, context) model = session.emane.models[request.model] - config = session.emane.get_model_config(request.id, request.model) + _id = get_emane_model_id(request.id, request.interface) + config = session.emane.get_model_config(_id, request.model) groups = get_config_groups(config, model) return core_pb2.GetEmaneModelConfigResponse(groups=groups) def SetEmaneModelConfig(self, request, context): logging.debug("set emane model config: %s", request) session = self.get_session(request.session, context) - session.emane.set_model_config(request.id, request.model, request.config) + _id = get_emane_model_id(request.id, request.interface) + session.emane.set_model_config(_id, request.model, request.config) return core_pb2.SetEmaneModelConfigResponse(result=True) def GetEmaneModelConfigs(self, request, context): diff --git a/daemon/proto/core.proto b/daemon/proto/core.proto index 31ea353d..375fdfc8 100644 --- a/daemon/proto/core.proto +++ b/daemon/proto/core.proto @@ -280,15 +280,7 @@ message FileEvent { message AddNodeRequest { int32 session = 1; - int32 id = 2; - NodeType type = 3; - string name = 4; - string model = 5; - string icon = 6; - repeated string services = 7; - Position position = 8; - string emane = 9; - string opaque = 10; + Node node = 2; } message AddNodeResponse { @@ -553,7 +545,8 @@ message GetEmaneModelsResponse { message GetEmaneModelConfigRequest { int32 session = 1; int32 id = 2; - string model = 3; + int32 interface = 3; + string model = 4; } message GetEmaneModelConfigResponse { @@ -563,8 +556,9 @@ message GetEmaneModelConfigResponse { message SetEmaneModelConfigRequest { int32 session = 1; int32 id = 2; - string model = 3; - map config = 4; + int32 interface = 3; + string model = 4; + map config = 5; } message SetEmaneModelConfigResponse { @@ -724,11 +718,13 @@ message SessionSummary { message Node { int32 id = 1; string name = 2; - int32 type = 3; + NodeType type = 3; string model = 4; Position position = 5; repeated string services = 6; string emane = 7; + string icon = 8; + string opaque = 9; } message Link { diff --git a/daemon/tests/test_grpc.py b/daemon/tests/test_grpc.py index af06fde9..04ef2fb5 100644 --- a/daemon/tests/test_grpc.py +++ b/daemon/tests/test_grpc.py @@ -14,19 +14,6 @@ from core.grpc import core_pb2 from core.grpc.client import CoreGrpcClient from core.mobility import BasicRangeModel, Ns2ScriptedMobility -MODELS = [ - "router", - "host", - "PC", - "mdr", -] - -NET_TYPES = [ - NodeTypes.SWITCH, - NodeTypes.HUB, - NodeTypes.WIRELESS_LAN -] - class TestGrpc: @pytest.mark.parametrize("session_id", [None, 6013]) @@ -178,11 +165,11 @@ class TestGrpc: # then with client.context_connect(): - response = client.set_session_state(session.session_id, EventTypes.DEFINITION_STATE) + response = client.set_session_state(session.session_id, core_pb2.STATE_DEFINITION) # then assert response.result is True - assert session.state == EventTypes.DEFINITION_STATE.value + assert session.state == core_pb2.STATE_DEFINITION def test_add_node(self, grpc_server): # given @@ -191,7 +178,8 @@ class TestGrpc: # then with client.context_connect(): - response = client.add_node(session.session_id) + node = core_pb2.Node() + response = client.add_node(session.session_id, node) # then assert response.id is not None @@ -281,7 +269,7 @@ class TestGrpc: file_name = "test" file_data = "echo hello" with client.context_connect(): - response = client.add_hook(session.session_id, EventTypes.RUNTIME_STATE, file_name, file_data) + response = client.add_hook(session.session_id, core_pb2.STATE_RUNTIME, file_name, file_data) # then assert response.result is True From ec1b82238e583eaa575b451264eff097de0d5749 Mon Sep 17 00:00:00 2001 From: bharnden Date: Thu, 28 Mar 2019 21:48:22 -0700 Subject: [PATCH 54/72] grpc fixed add node doc --- daemon/core/grpc/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daemon/core/grpc/client.py b/daemon/core/grpc/client.py index 3cf9881b..8f07148e 100644 --- a/daemon/core/grpc/client.py +++ b/daemon/core/grpc/client.py @@ -260,7 +260,7 @@ class CoreGrpcClient(object): Add node to session. :param int session: session id - :param core_pb2.Node: node to add + :param core_pb2.Node node: node to add :return: response with node id :rtype: core_pb2.AddNodeResponse :raises grpc.RpcError: when session doesn't exist From 5c47b0cc439707caeca9f6cc114bfffd96db26d1 Mon Sep 17 00:00:00 2001 From: bharnden Date: Fri, 29 Mar 2019 22:12:34 -0700 Subject: [PATCH 55/72] grpc updated client methods to use proto objects directly, added more docs --- daemon/core/grpc/client.py | 261 ++++++++++++++++++++++++++----------- daemon/proto/core.proto | 7 +- daemon/tests/conftest.py | 6 + daemon/tests/test_grpc.py | 17 +-- 4 files changed, 203 insertions(+), 88 deletions(-) diff --git a/daemon/core/grpc/client.py b/daemon/core/grpc/client.py index 8f07148e..6e7c5f96 100644 --- a/daemon/core/grpc/client.py +++ b/daemon/core/grpc/client.py @@ -11,10 +11,97 @@ from contextlib import contextmanager import grpc -from core.emulator.emudata import NodeOptions, IpPrefixes, InterfaceData, LinkOptions -from core.enumerations import NodeTypes, LinkTypes, EventTypes from core.grpc import core_pb2 from core.grpc import core_pb2_grpc +from core.misc.ipaddress import Ipv4Prefix, Ipv6Prefix, MacAddress + + +class InterfaceHelper(object): + """ + Convenience class to help generate IP4 and IP6 addresses for gRPC clients. + """ + + def __init__(self, ip4_prefix=None, ip6_prefix=None): + """ + Creates an InterfaceHelper object. + + :param str ip4_prefix: ip4 prefix to use for generation + :param str ip6_prefix: ip6 prefix to use for generation + :raises ValueError: when both ip4 and ip6 prefixes have not been provided + """ + if not ip4_prefix and not ip6_prefix: + raise ValueError("ip4 or ip6 must be provided") + + self.ip4 = None + if ip4_prefix: + self.ip4 = Ipv4Prefix(ip4_prefix) + self.ip6 = None + if ip6_prefix: + self.ip6 = Ipv6Prefix(ip6_prefix) + + def ip4_address(self, node_id): + """ + Convenience method to return the IP4 address for a node. + + :param int node_id: node id to get IP4 address for + :return: IP4 address or None + :rtype: str + """ + if not self.ip4: + raise ValueError("ip4 prefixes have not been set") + return str(self.ip4.addr(node_id)) + + def ip6_address(self, node_id): + """ + Convenience method to return the IP6 address for a node. + + :param int node_id: node id to get IP6 address for + :return: IP4 address or None + :rtype: str + """ + if not self.ip6: + raise ValueError("ip6 prefixes have not been set") + return str(self.ip6.addr(node_id)) + + def create_interface(self, node_id, interface_id, name=None, mac=None): + """ + Creates interface data for linking nodes, using the nodes unique id for generation, along with a random + mac address, unless provided. + + :param int node_id: node id to create interface for + :param int interface_id: interface id for interface + :param str name: name to set for interface, default is eth{id} + :param str mac: mac address to use for this interface, default is random generation + :return: new interface data for the provided node + :rtype: core_pb2.Interface + """ + # generate ip4 data + ip4 = None + ip4_mask = None + if self.ip4: + ip4 = str(self.ip4.addr(node_id)) + ip4_mask = self.ip4.prefixlen + + # generate ip6 data + ip6 = None + ip6_mask = None + if self.ip6: + ip6 = str(self.ip6.addr(node_id)) + ip6_mask = self.ip6.prefixlen + + # random mac + if not mac: + mac = MacAddress.random() + + return core_pb2.Interface( + id=interface_id, + name=name, + ip4=ip4, + ip4mask=ip4_mask, + ip6=ip6, + ip6mask=ip6_mask, + mac=str(mac) + ) def stream_listener(stream, handler): @@ -281,20 +368,17 @@ class CoreGrpcClient(object): request = core_pb2.GetNodeRequest(session=session, id=_id) return self.stub.GetNode(request) - def edit_node(self, session, _id, node_options): + def edit_node(self, session, _id, position): """ Edit a node, currently only changes position. :param int session: session id :param int _id: node id - :param NodeOptions node_options: options for node including position, services, and model + :param core_pb2.Position position: position to set node to :return: response with result of success or failure :rtype: core_pb2.EditNodeResponse :raises grpc.RpcError: when session or node doesn't exist """ - position = core_pb2.Position( - x=node_options.x, y=node_options.y, - lat=node_options.lat, lon=node_options.lon, alt=node_options.alt) request = core_pb2.EditNodeRequest(session=session, id=_id, position=position) return self.stub.EditNode(request) @@ -324,89 +408,40 @@ class CoreGrpcClient(object): request = core_pb2.GetNodeLinksRequest(session=session, id=_id) return self.stub.GetNodeLinks(request) - def add_link(self, session, node_one, node_two, interface_one=None, interface_two=None, link_options=None): + def add_link(self, session, node_one, node_two, interface_one=None, interface_two=None, options=None): """ Add a link between nodes. :param int session: session id :param int node_one: node one id :param int node_two: node two id - :param InterfaceData interface_one: node one interface data - :param InterfaceData interface_two: node two interface data - :param LinkOptions link_options: options for link (jitter, bandwidth, etc) + :param core_pb2.Interface interface_one: node one interface data + :param core_pb2.Interface interface_two: node two interface data + :param core_pb2.LinkOptions options: options for link (jitter, bandwidth, etc) :return: response with result of success or failure :rtype: core_pb2.AddLinkResponse :raises grpc.RpcError: when session or one of the nodes don't exist """ - interface_one_proto = None - if interface_one is not None: - mac = interface_one.mac - if mac is not None: - mac = str(mac) - interface_one_proto = core_pb2.Interface( - id=interface_one.id, name=interface_one.name, mac=mac, - ip4=interface_one.ip4, ip4mask=interface_one.ip4_mask, - ip6=interface_one.ip6, ip6mask=interface_one.ip6_mask) - - interface_two_proto = None - if interface_two is not None: - mac = interface_two.mac - if mac is not None: - mac = str(mac) - interface_two_proto = core_pb2.Interface( - id=interface_two.id, name=interface_two.name, mac=mac, - ip4=interface_two.ip4, ip4mask=interface_two.ip4_mask, - ip6=interface_two.ip6, ip6mask=interface_two.ip6_mask) - - options = None - if link_options is not None: - options = core_pb2.LinkOptions( - delay=link_options.delay, - bandwidth=link_options.bandwidth, - per=link_options.per, - dup=link_options.dup, - jitter=link_options.jitter, - mer=link_options.mer, - burst=link_options.burst, - mburst=link_options.mburst, - unidirectional=link_options.unidirectional, - key=link_options.key, - opaque=link_options.opaque - ) - link = core_pb2.Link( - node_one=node_one, node_two=node_two, type=LinkTypes.WIRED.value, - interface_one=interface_one_proto, interface_two=interface_two_proto, options=options) + node_one=node_one, node_two=node_two, type=core_pb2.LINK_WIRED, + interface_one=interface_one, interface_two=interface_two, options=options) request = core_pb2.AddLinkRequest(session=session, link=link) return self.stub.AddLink(request) - def edit_link(self, session, node_one, node_two, link_options, interface_one=None, interface_two=None): + def edit_link(self, session, node_one, node_two, options, interface_one=None, interface_two=None): """ Edit a link between nodes. :param int session: session id :param int node_one: node one id :param int node_two: node two id - :param LinkOptions link_options: options for link (jitter, bandwidth, etc) + :param core_pb2.LinkOptions options: options for link (jitter, bandwidth, etc) :param int interface_one: node one interface id :param int interface_two: node two interface id :return: response with result of success or failure :rtype: core_pb2.EditLinkResponse :raises grpc.RpcError: when session or one of the nodes don't exist """ - options = core_pb2.LinkOptions( - delay=link_options.delay, - bandwidth=link_options.bandwidth, - per=link_options.per, - dup=link_options.dup, - jitter=link_options.jitter, - mer=link_options.mer, - burst=link_options.burst, - mburst=link_options.mburst, - unidirectional=link_options.unidirectional, - key=link_options.key, - opaque=link_options.opaque - ) request = core_pb2.EditLinkRequest( session=session, node_one=node_one, node_two=node_two, options=options, interface_one=interface_one, interface_two=interface_two) @@ -431,39 +466,118 @@ class CoreGrpcClient(object): return self.stub.DeleteLink(request) def get_hooks(self, session): + """ + Get all hook scripts. + + :param int session: session id + :return: response with a list of hooks + :rtype: core_pb2.GetHooksResponse + :raises grpc.RpcError: when session doesn't exist + """ request = core_pb2.GetHooksRequest(session=session) return self.stub.GetHooks(request) def add_hook(self, session, state, file_name, file_data): + """ + Add hook scripts. + + :param int session: session id + :param core_pb2.SessionState state: state to trigger hook + :param str file_name: name of file for hook script + :param bytes file_data: hook script contents + :return: response with result of success or failure + :rtype: core_pb2.AddHookResponse + :raises grpc.RpcError: when session doesn't exist + """ hook = core_pb2.Hook(state=state, file=file_name, data=file_data) request = core_pb2.AddHookRequest(session=session, hook=hook) return self.stub.AddHook(request) def get_mobility_configs(self, session): + """ + Get all mobility configurations. + + :param int session: session id + :return: response with a dict of node ids to mobility configurations + :rtype: core_pb2.GetMobilityConfigsResponse + :raises grpc.RpcError: when session doesn't exist + """ request = core_pb2.GetMobilityConfigsRequest(session=session) return self.stub.GetMobilityConfigs(request) def get_mobility_config(self, session, _id): + """ + Get mobility configuration for a node. + + :param int session: session id + :param int _id: node id + :return: response with a list of configuration groups + :rtype: core_pb2.GetMobilityConfigResponse + :raises grpc.RpcError: when session or node doesn't exist + """ request = core_pb2.GetMobilityConfigRequest(session=session, id=_id) return self.stub.GetMobilityConfig(request) def set_mobility_config(self, session, _id, config): + """ + Set mobility configuration for a node. + + :param int session: session id + :param int _id: node id + :param dict[str, str] config: mobility configuration + :return: response with result of success or failure + :rtype: core_pb2.SetMobilityConfigResponse + :raises grpc.RpcError: when session or node doesn't exist + """ request = core_pb2.SetMobilityConfigRequest(session=session, id=_id, config=config) return self.stub.SetMobilityConfig(request) def mobility_action(self, session, _id, action): + """ + Send a mobility action for a node. + + :param int session: session id + :param int _id: node id + :param core_pb2.ServiceAction action: action to take + :return: response with result of success or failure + :rtype: core_pb2.MobilityActionResponse + :raises grpc.RpcError: when session or node doesn't exist + """ request = core_pb2.MobilityActionRequest(session=session, id=_id, action=action) return self.stub.MobilityAction(request) def get_services(self): + """ + Get all currently loaded services. + + :return: response with a list of services + :rtype: core_pb2.GetServicesResponse + """ request = core_pb2.GetServicesRequest() return self.stub.GetServices(request) def get_service_defaults(self, session): + """ + Get default services for different default node models. + + :param int session: session id + :return: response with a dict of node model to a list of services + :rtype: core_pb2.GetServiceDefaultsResponse + :raises grpc.RpcError: when session doesn't exist + """ request = core_pb2.GetServiceDefaultsRequest(session=session) return self.stub.GetServiceDefaults(request) def set_service_defaults(self, session, service_defaults): + """ + Set default services for node models. + + :param int session: session id + :param dict service_defaults: node models to lists of services + :return: response with result of success or failure + :rtype: core_pb2.SetServiceDefaultsResponse + :raises grpc.RpcError: when session doesn't exist + """ defaults = [] for node_type in service_defaults: services = service_defaults[node_type] @@ -591,7 +705,7 @@ def main(): # set session location response = client.set_session_location( session_data.id, - x=0, y=0, z=None, + x=0, y=0, lat=47.57917, lon=-122.13232, alt=3.0, scale=150000.0 ) @@ -613,17 +727,15 @@ def main(): switch_id = response.id # ip generator for example - prefixes = IpPrefixes(ip4_prefix="10.83.0.0/16") + interface_helper = InterfaceHelper(ip4_prefix="10.83.0.0/16") for _ in xrange(2): node = core_pb2.Node() response = client.add_node(session_data.id, node) print("created node: {}".format(response)) node_id = response.id - node_options = NodeOptions() - node_options.x = 5 - node_options.y = 5 - print("edit node: {}".format(client.edit_node(session_data.id, node_id, node_options))) + position = core_pb2.Position(x=5, y=5) + print("edit node: {}".format(client.edit_node(session_data.id, node_id, position))) print("get node: {}".format(client.get_node(session_data.id, node_id))) print("emane model config: {}".format( client.get_emane_model_config(session_data.id, node_id, "emane_tdma"))) @@ -631,14 +743,9 @@ def main(): print("node service: {}".format(client.get_node_service(session_data.id, node_id, "zebra"))) # create link - interface_one = InterfaceData( - _id=None, name=None, mac=None, - ip4=str(prefixes.ip4.addr(node_id)), ip4_mask=prefixes.ip4.prefixlen, - ip6=None, ip6_mask=None - ) + interface_one = interface_helper.create_interface(node_id, 0) print("created link: {}".format(client.add_link(session_data.id, node_id, switch_id, interface_one))) - link_options = LinkOptions() - link_options.per = 50 + link_options = core_pb2.LinkOptions(per=50) print("edit link: {}".format(client.edit_link( session_data.id, node_id, switch_id, link_options, interface_one=0))) diff --git a/daemon/proto/core.proto b/daemon/proto/core.proto index 375fdfc8..45fd597e 100644 --- a/daemon/proto/core.proto +++ b/daemon/proto/core.proto @@ -606,6 +606,11 @@ enum MessageType { MESSAGE_TTY = 64; } +enum LinkType { + LINK_WIRELESS = 0; + LINK_WIRED = 1; +} + enum SessionState { STATE_NONE = 0; STATE_DEFINITION = 1; @@ -730,7 +735,7 @@ message Node { message Link { int32 node_one = 1; int32 node_two = 2; - int32 type = 3; + LinkType type = 3; Interface interface_one = 4; Interface interface_two = 5; LinkOptions options = 6; diff --git a/daemon/tests/conftest.py b/daemon/tests/conftest.py index 5b98fe7c..2dc19184 100644 --- a/daemon/tests/conftest.py +++ b/daemon/tests/conftest.py @@ -28,6 +28,7 @@ from core.enumerations import LinkTypes from core.enumerations import MessageFlags from core.enumerations import NodeTlvs from core.enumerations import NodeTypes +from core.grpc.client import InterfaceHelper from core.grpc.server import CoreGrpcServer from core.misc import ipaddress from core.misc.ipaddress import MacAddress @@ -249,6 +250,11 @@ def ip_prefixes(): return IpPrefixes(ip4_prefix="10.83.0.0/16") +@pytest.fixture(scope="module") +def interface_helper(): + return InterfaceHelper(ip4_prefix="10.83.0.0/16") + + @pytest.fixture() def cored(): # create and return server diff --git a/daemon/tests/test_grpc.py b/daemon/tests/test_grpc.py index 04ef2fb5..c0f14ef4 100644 --- a/daemon/tests/test_grpc.py +++ b/daemon/tests/test_grpc.py @@ -8,7 +8,6 @@ import pytest from core.conf import ConfigShim from core.data import EventData from core.emane.ieee80211abg import EmaneIeee80211abgModel -from core.emulator.emudata import NodeOptions, LinkOptions from core.enumerations import NodeTypes, EventTypes, ConfigFlags, ExceptionLevels from core.grpc import core_pb2 from core.grpc.client import CoreGrpcClient @@ -211,9 +210,8 @@ class TestGrpc: # then x, y = 10, 10 with client.context_connect(): - node_options = NodeOptions() - node_options.set_position(x, y) - response = client.edit_node(session.session_id, node_id, node_options) + position = core_pb2.Position(x=x, y=y) + response = client.edit_node(session.session_id, node_id, position) # then assert response.result is expected @@ -332,7 +330,7 @@ class TestGrpc: with client.context_connect(): client.get_node_links(session.session_id, 3) - def test_add_link(self, grpc_server, ip_prefixes): + def test_add_link(self, grpc_server, interface_helper): # given client = CoreGrpcClient() session = grpc_server.coreemu.create_session() @@ -341,7 +339,7 @@ class TestGrpc: assert len(switch.all_link_data(0)) == 0 # then - interface = ip_prefixes.create_interface(node) + interface = interface_helper.create_interface(node.objid, 0) with client.context_connect(): response = client.add_link(session.session_id, node.objid, switch.objid, interface) @@ -349,14 +347,14 @@ class TestGrpc: assert response.result is True assert len(switch.all_link_data(0)) == 1 - def test_add_link_exception(self, grpc_server, ip_prefixes): + def test_add_link_exception(self, grpc_server, interface_helper): # given client = CoreGrpcClient() session = grpc_server.coreemu.create_session() node = session.add_node() # then - interface = ip_prefixes.create_interface(node) + interface = interface_helper.create_interface(node.objid, 0) with pytest.raises(grpc.RpcError): with client.context_connect(): client.add_link(session.session_id, 1, 3, interface) @@ -369,8 +367,7 @@ class TestGrpc: node = session.add_node() interface = ip_prefixes.create_interface(node) session.add_link(node.objid, switch.objid, interface) - options = LinkOptions() - options.bandwidth = 30000 + options = core_pb2.LinkOptions(bandwidth=30000) link = switch.all_link_data(0)[0] assert options.bandwidth != link.bandwidth From 69a4ea420cf821dcedb6d556c00f544d3f978320 Mon Sep 17 00:00:00 2001 From: bharnden Date: Fri, 29 Mar 2019 23:47:20 -0700 Subject: [PATCH 56/72] grpc futher documentation for client code --- daemon/core/grpc/client.py | 75 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/daemon/core/grpc/client.py b/daemon/core/grpc/client.py index 6e7c5f96..e0bac2b9 100644 --- a/daemon/core/grpc/client.py +++ b/daemon/core/grpc/client.py @@ -587,19 +587,65 @@ class CoreGrpcClient(object): return self.stub.SetServiceDefaults(request) def get_node_service(self, session, _id, service): + """ + Get service data for a node. + + :param int session: session id + :param int _id: node id + :param str service: service name + :return: response with node service data + :rtype: core_pb2.GetNodeServiceResponse + :raises grpc.RpcError: when session or node doesn't exist + """ request = core_pb2.GetNodeServiceRequest(session=session, id=_id, service=service) return self.stub.GetNodeService(request) def get_node_service_file(self, session, _id, service, file_name): + """ + Get a service file for a node. + + :param int session: session id + :param int _id: node id + :param str service: service name + :param str file_name: file name to get data for + :return: response with file data + :rtype: core_pb2.GetNodeServiceFileResponse + :raises grpc.RpcError: when session or node doesn't exist + """ request = core_pb2.GetNodeServiceFileRequest(session=session, id=_id, service=service, file=file_name) return self.stub.GetNodeServiceFile(request) def set_node_service(self, session, _id, service, startup, validate, shutdown): + """ + Set service data for a node. + + :param int session: session id + :param int _id: node id + :param str service: service name + :param list startup: startup commands + :param list validate: validation commands + :param list shutdown: shutdown commands + :return: response with result of success or failure + :rtype: core_pb2.SetNodeServiceResponse + :raises grpc.RpcError: when session or node doesn't exist + """ request = core_pb2.SetNodeServiceRequest( session=session, id=_id, service=service, startup=startup, validate=validate, shutdown=shutdown) return self.stub.SetNodeService(request) def set_node_service_file(self, session, _id, service, file_name, data): + """ + Set a service file for a node. + + :param int session: session id + :param int _id: node id + :param str service: service name + :param str file_name: file name to save + :param bytes data: data to save for file + :return: response with result of success or failure + :rtype: core_pb2.SetNodeServiceFileResponse + :raises grpc.RpcError: when session or node doesn't exist + """ request = core_pb2.SetNodeServiceFileRequest( session=session, id=_id, service=service, file=file_name, data=data) return self.stub.SetNodeServiceFile(request) @@ -642,28 +688,57 @@ class CoreGrpcClient(object): return self.stub.GetEmaneModelConfigs(request) def save_xml(self, session, file_path): + """ + Save the current scenario to an XML file. + + :param int session: session id + :param str file_path: local path to save scenario XML file to + :return: nothing + """ request = core_pb2.SaveXmlRequest(session=session) response = self.stub.SaveXml(request) with open(file_path, "wb") as xml_file: xml_file.write(response.data) def open_xml(self, file_path): + """ + Load a local scenario XML file to open as a new session. + + :param str file_path: path of scenario XML file + :return: response with opened session id + :rtype: core_pb2.OpenXmlResponse + """ with open(file_path, "rb") as xml_file: data = xml_file.read() request = core_pb2.OpenXmlRequest(data=data) return self.stub.OpenXml(request) def connect(self): + """ + Open connection to server, must be closed manually. + + :return: nothing + """ self.channel = grpc.insecure_channel(self.address) self.stub = core_pb2_grpc.CoreApiStub(self.channel) def close(self): + """ + Close currently opened server channel connection. + + :return: nothing + """ if self.channel: self.channel.close() self.channel = None @contextmanager def context_connect(self): + """ + Makes a context manager based connection to the server, will close after context ends. + + :return: nothing + """ try: self.connect() yield From 2b14865473d2adf8a5a764fdd6695b22f4c71a3e Mon Sep 17 00:00:00 2001 From: bharnden Date: Sat, 30 Mar 2019 18:51:01 -0700 Subject: [PATCH 57/72] grpc finished client doc --- daemon/core/grpc/client.py | 86 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/daemon/core/grpc/client.py b/daemon/core/grpc/client.py index e0bac2b9..ada41b07 100644 --- a/daemon/core/grpc/client.py +++ b/daemon/core/grpc/client.py @@ -651,39 +651,125 @@ class CoreGrpcClient(object): return self.stub.SetNodeServiceFile(request) def service_action(self, session, _id, service, action): + """ + Send an action to a service for a node. + + :param int session: session id + :param int _id: node id + :param str service: service name + :param core_pb2.ServiceAction action: action for service (start, stop, restart, validate) + :return: response with result of success or failure + :rtype: core_pb2.ServiceActionResponse + :raises grpc.RpcError: when session or node doesn't exist + """ request = core_pb2.ServiceActionRequest(session=session, id=_id, service=service, action=action) return self.stub.ServiceAction(request) def get_wlan_config(self, session, _id): + """ + Get wlan configuration for a node. + + :param int session: session id + :param int _id: node id + :return: response with a list of configuration groups + :rtype: core_pb2.GetWlanConfigResponse + :raises grpc.RpcError: when session doesn't exist + """ request = core_pb2.GetWlanConfigRequest(session=session, id=_id) return self.stub.GetWlanConfig(request) def set_wlan_config(self, session, _id, config): + """ + Set wlan configuration for a node. + + :param int session: session id + :param int _id: node id + :param dict[str, str] config: wlan configuration + :return: response with result of success or failure + :rtype: core_pb2.SetWlanConfigResponse + :raises grpc.RpcError: when session doesn't exist + """ request = core_pb2.SetWlanConfigRequest(session=session, id=_id, config=config) return self.stub.SetWlanConfig(request) def get_emane_config(self, session): + """ + Get session emane configuration. + + :param int session: session id + :return: response with a list of configuration groups + :rtype: core_pb2.GetEmaneConfigResponse + :raises grpc.RpcError: when session doesn't exist + """ request = core_pb2.GetEmaneConfigRequest(session=session) return self.stub.GetEmaneConfig(request) def set_emane_config(self, session, config): + """ + Set session emane configuration. + + :param int session: session id + :param dict[str, str] config: emane configuration + :return: response with result of success or failure + :rtype: core_pb2.SetEmaneConfigResponse + :raises grpc.RpcError: when session doesn't exist + """ request = core_pb2.SetEmaneConfigRequest(session=session, config=config) return self.stub.SetEmaneConfig(request) def get_emane_models(self, session): + """ + Get session emane models. + + :param int session: session id + :return: response with a list of emane models + :rtype: core_pb2.GetEmaneModelsResponse + :raises grpc.RpcError: when session doesn't exist + """ request = core_pb2.GetEmaneModelsRequest(session=session) return self.stub.GetEmaneModels(request) def get_emane_model_config(self, session, _id, model, interface_id=-1): + """ + Get emane model configuration for a node or a node's interface. + + :param int session: session id + :param int _id: node id + :param str model: emane model name + :param int interface_id: node interface id + :return: response with a list of configuration groups + :rtype: core_pb2.GetEmaneModelConfigResponse + :raises grpc.RpcError: when session doesn't exist + """ request = core_pb2.GetEmaneModelConfigRequest(session=session, id=_id, model=model, interface=interface_id) return self.stub.GetEmaneModelConfig(request) def set_emane_model_config(self, session, _id, model, config, interface_id=-1): + """ + Set emane model configuration for a node or a node's interface. + + :param int session: session id + :param int _id: node id + :param str model: emane model name + :param dict[str, str] config: emane model configuration + :param int interface_id: node interface id + :return: response with result of success or failure + :rtype: core_pb2.SetEmaneModelConfigResponse + :raises grpc.RpcError: when session doesn't exist + """ request = core_pb2.SetEmaneModelConfigRequest( session=session, id=_id, model=model, config=config, interface=interface_id) return self.stub.SetEmaneModelConfig(request) def get_emane_model_configs(self, session): + """ + Get all emane model configurations for a session. + + :param int session: session id + :return: response with a dictionary of node/interface ids to configurations + :rtype: core_pb2.GetEmaneModelConfigsResponse + :raises grpc.RpcError: when session doesn't exist + """ request = core_pb2.GetEmaneModelConfigsRequest(session=session) return self.stub.GetEmaneModelConfigs(request) From 27ea317a574af734aced77bae2ded6914e3019fd Mon Sep 17 00:00:00 2001 From: bharnden Date: Sat, 30 Mar 2019 19:01:11 -0700 Subject: [PATCH 58/72] create initial example client script for grpc and separated from within the client library --- daemon/core/grpc/client.py | 100 ----------------------------- daemon/examples/grpc/__init__.py | 0 daemon/examples/grpc/switch.py | 107 +++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+), 100 deletions(-) create mode 100644 daemon/examples/grpc/__init__.py create mode 100644 daemon/examples/grpc/switch.py diff --git a/daemon/core/grpc/client.py b/daemon/core/grpc/client.py index ada41b07..a6863e37 100644 --- a/daemon/core/grpc/client.py +++ b/daemon/core/grpc/client.py @@ -5,7 +5,6 @@ gRpc client for interfacing with CORE, when gRPC mode is enabled. from __future__ import print_function import logging -import os import threading from contextlib import contextmanager @@ -830,102 +829,3 @@ class CoreGrpcClient(object): yield finally: self.close() - - -def main(): - xml_file_name = "/tmp/core.xml" - - client = CoreGrpcClient() - with client.context_connect(): - if os.path.exists(xml_file_name): - response = client.open_xml(xml_file_name) - print("open xml: {}".format(response)) - - print("services: {}".format(client.get_services())) - - # create session - session_data = client.create_session() - client.exception_events(session_data.id, lambda x: print(x)) - client.node_events(session_data.id, lambda x: print(x)) - client.session_events(session_data.id, lambda x: print(x)) - client.link_events(session_data.id, lambda x: print(x)) - client.file_events(session_data.id, lambda x: print(x)) - client.config_events(session_data.id, lambda x: print(x)) - print("created session: {}".format(session_data)) - print("default services: {}".format(client.get_service_defaults(session_data.id))) - print("emane models: {}".format(client.get_emane_models(session_data.id))) - print("add hook: {}".format(client.add_hook(session_data.id, core_pb2.STATE_RUNTIME, "test", "echo hello"))) - print("hooks: {}".format(client.get_hooks(session_data.id))) - - response = client.get_sessions() - print("core client received: {}".format(response)) - - print("set emane config: {}".format(client.set_emane_config(session_data.id, {"otamanagerttl": "2"}))) - print("emane config: {}".format(client.get_emane_config(session_data.id))) - - # set session location - response = client.set_session_location( - session_data.id, - x=0, y=0, - lat=47.57917, lon=-122.13232, alt=3.0, - scale=150000.0 - ) - print("set location response: {}".format(response)) - - # get options - print("get options: {}".format(client.get_session_options(session_data.id))) - - # get location - print("get location: {}".format(client.get_session_location(session_data.id))) - - # change session state - print("set session state: {}".format(client.set_session_state(session_data.id, core_pb2.STATE_CONFIGURATION))) - - # create switch node - switch = core_pb2.Node(type=core_pb2.NODE_SWITCH) - response = client.add_node(session_data.id, switch) - print("created switch: {}".format(response)) - switch_id = response.id - - # ip generator for example - interface_helper = InterfaceHelper(ip4_prefix="10.83.0.0/16") - - for _ in xrange(2): - node = core_pb2.Node() - response = client.add_node(session_data.id, node) - print("created node: {}".format(response)) - node_id = response.id - position = core_pb2.Position(x=5, y=5) - print("edit node: {}".format(client.edit_node(session_data.id, node_id, position))) - print("get node: {}".format(client.get_node(session_data.id, node_id))) - print("emane model config: {}".format( - client.get_emane_model_config(session_data.id, node_id, "emane_tdma"))) - - print("node service: {}".format(client.get_node_service(session_data.id, node_id, "zebra"))) - - # create link - interface_one = interface_helper.create_interface(node_id, 0) - print("created link: {}".format(client.add_link(session_data.id, node_id, switch_id, interface_one))) - link_options = core_pb2.LinkOptions(per=50) - print("edit link: {}".format(client.edit_link( - session_data.id, node_id, switch_id, link_options, interface_one=0))) - - print("get node links: {}".format(client.get_node_links(session_data.id, node_id))) - - # change session state - print("set session state: {}".format(client.set_session_state(session_data.id, core_pb2.STATE_INSTANTIATION))) - # import pdb; pdb.set_trace() - - # get session - print("get session: {}".format(client.get_session(session_data.id))) - - # save xml - client.save_xml(session_data.id, xml_file_name) - - # delete session - print("delete session: {}".format(client.delete_session(session_data.id))) - - -if __name__ == "__main__": - logging.basicConfig(level=logging.DEBUG) - main() diff --git a/daemon/examples/grpc/__init__.py b/daemon/examples/grpc/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/daemon/examples/grpc/switch.py b/daemon/examples/grpc/switch.py new file mode 100644 index 00000000..3906dd9a --- /dev/null +++ b/daemon/examples/grpc/switch.py @@ -0,0 +1,107 @@ +import logging +import os + +from core.grpc import client +from core.grpc import core_pb2 + + +def log_event(event): + logging.info("event: %s", event) + + +def main(): + xml_file_name = "/tmp/core.xml" + core = client.CoreGrpcClient() + + with core.context_connect(): + if os.path.exists(xml_file_name): + response = core.open_xml(xml_file_name) + print("open xml: {}".format(response)) + + print("services: {}".format(core.get_services())) + + # create session + session = core.create_session() + core.exception_events(session.id, log_event) + core.node_events(session.id, log_event) + core.session_events(session.id, log_event) + core.link_events(session.id, log_event) + core.file_events(session.id, log_event) + core.config_events(session.id, log_event) + print("created session: {}".format(session)) + print("default services: {}".format(core.get_service_defaults(session.id))) + print("emane models: {}".format(core.get_emane_models(session.id))) + print("add hook: {}".format(core.add_hook(session.id, core_pb2.STATE_RUNTIME, "test", "echo hello"))) + print("hooks: {}".format(core.get_hooks(session.id))) + + response = core.get_sessions() + print("core client received: {}".format(response)) + + print("set emane config: {}".format(core.set_emane_config(session.id, {"otamanagerttl": "2"}))) + print("emane config: {}".format(core.get_emane_config(session.id))) + + # set session location + response = core.set_session_location( + session.id, x=0, y=0, + lat=47.57917, lon=-122.13232, alt=3.0, + scale=150000.0 + ) + print("set location response: {}".format(response)) + + # get options + print("get options: {}".format(core.get_session_options(session.id))) + + # get location + print("get location: {}".format(core.get_session_location(session.id))) + + # change session state + print("set session state: {}".format(core.set_session_state(session.id, core_pb2.STATE_CONFIGURATION))) + + # create switch node + switch = core_pb2.Node(type=core_pb2.NODE_SWITCH) + response = core.add_node(session.id, switch) + print("created switch: {}".format(response)) + switch_id = response.id + + # ip generator for example + interface_helper = client.InterfaceHelper(ip4_prefix="10.83.0.0/16") + + # create node nodes and link them to switch + for _ in xrange(2): + node = core_pb2.Node() + response = core.add_node(session.id, node) + print("created node: {}".format(response)) + node_id = response.id + position = core_pb2.Position(x=5, y=5) + print("edit node: {}".format(core.edit_node(session.id, node_id, position))) + print("get node: {}".format(core.get_node(session.id, node_id))) + print("emane model config: {}".format( + core.get_emane_model_config(session.id, node_id, "emane_tdma"))) + + print("node service: {}".format(core.get_node_service(session.id, node_id, "zebra"))) + + # create link + interface_one = interface_helper.create_interface(node_id, 0) + print("created link: {}".format(core.add_link(session.id, node_id, switch_id, interface_one))) + link_options = core_pb2.LinkOptions(per=50) + print("edit link: {}".format(core.edit_link( + session.id, node_id, switch_id, link_options, interface_one=0))) + + print("get node links: {}".format(core.get_node_links(session.id, node_id))) + + # change session state + print("set session state: {}".format(core.set_session_state(session.id, core_pb2.STATE_INSTANTIATION))) + + # get session + print("get session: {}".format(core.get_session(session.id))) + + # save xml + core.save_xml(session.id, xml_file_name) + + # delete session + print("delete session: {}".format(core.delete_session(session.id))) + + +if __name__ == "__main__": + logging.basicConfig(level=logging.DEBUG) + main() From 1bf53cfd03fe903b6723989eda784737cc44f0b8 Mon Sep 17 00:00:00 2001 From: bharnden Date: Sat, 30 Mar 2019 21:05:50 -0700 Subject: [PATCH 59/72] small cleanup to grpc switch example --- daemon/examples/grpc/switch.py | 79 +++++++--------------------------- 1 file changed, 16 insertions(+), 63 deletions(-) diff --git a/daemon/examples/grpc/switch.py b/daemon/examples/grpc/switch.py index 3906dd9a..354e7b66 100644 --- a/daemon/examples/grpc/switch.py +++ b/daemon/examples/grpc/switch.py @@ -1,5 +1,4 @@ import logging -import os from core.grpc import client from core.grpc import core_pb2 @@ -10,96 +9,50 @@ def log_event(event): def main(): - xml_file_name = "/tmp/core.xml" core = client.CoreGrpcClient() with core.context_connect(): - if os.path.exists(xml_file_name): - response = core.open_xml(xml_file_name) - print("open xml: {}".format(response)) - - print("services: {}".format(core.get_services())) - # create session session = core.create_session() + logging.info("created session: %s", session) + + # handle events session may broadcast core.exception_events(session.id, log_event) core.node_events(session.id, log_event) core.session_events(session.id, log_event) core.link_events(session.id, log_event) core.file_events(session.id, log_event) core.config_events(session.id, log_event) - print("created session: {}".format(session)) - print("default services: {}".format(core.get_service_defaults(session.id))) - print("emane models: {}".format(core.get_emane_models(session.id))) - print("add hook: {}".format(core.add_hook(session.id, core_pb2.STATE_RUNTIME, "test", "echo hello"))) - print("hooks: {}".format(core.get_hooks(session.id))) - - response = core.get_sessions() - print("core client received: {}".format(response)) - - print("set emane config: {}".format(core.set_emane_config(session.id, {"otamanagerttl": "2"}))) - print("emane config: {}".format(core.get_emane_config(session.id))) - - # set session location - response = core.set_session_location( - session.id, x=0, y=0, - lat=47.57917, lon=-122.13232, alt=3.0, - scale=150000.0 - ) - print("set location response: {}".format(response)) - - # get options - print("get options: {}".format(core.get_session_options(session.id))) - - # get location - print("get location: {}".format(core.get_session_location(session.id))) # change session state - print("set session state: {}".format(core.set_session_state(session.id, core_pb2.STATE_CONFIGURATION))) + response = core.set_session_state(session.id, core_pb2.STATE_CONFIGURATION) + logging.info("set session state: %s", response) # create switch node switch = core_pb2.Node(type=core_pb2.NODE_SWITCH) response = core.add_node(session.id, switch) - print("created switch: {}".format(response)) + logging.info("created switch: %s", response) switch_id = response.id - # ip generator for example + # helper to create interfaces interface_helper = client.InterfaceHelper(ip4_prefix="10.83.0.0/16") - # create node nodes and link them to switch - for _ in xrange(2): - node = core_pb2.Node() + for i in xrange(2): + # create node + position = core_pb2.Position(x=50 + 50 * i, y=50) + node = core_pb2.Node(position=position) response = core.add_node(session.id, node) - print("created node: {}".format(response)) + logging.info("created node: %s", response) node_id = response.id - position = core_pb2.Position(x=5, y=5) - print("edit node: {}".format(core.edit_node(session.id, node_id, position))) - print("get node: {}".format(core.get_node(session.id, node_id))) - print("emane model config: {}".format( - core.get_emane_model_config(session.id, node_id, "emane_tdma"))) - - print("node service: {}".format(core.get_node_service(session.id, node_id, "zebra"))) # create link interface_one = interface_helper.create_interface(node_id, 0) - print("created link: {}".format(core.add_link(session.id, node_id, switch_id, interface_one))) - link_options = core_pb2.LinkOptions(per=50) - print("edit link: {}".format(core.edit_link( - session.id, node_id, switch_id, link_options, interface_one=0))) - - print("get node links: {}".format(core.get_node_links(session.id, node_id))) + response = core.add_link(session.id, node_id, switch_id, interface_one) + logging.info("created link: %s", response) # change session state - print("set session state: {}".format(core.set_session_state(session.id, core_pb2.STATE_INSTANTIATION))) - - # get session - print("get session: {}".format(core.get_session(session.id))) - - # save xml - core.save_xml(session.id, xml_file_name) - - # delete session - print("delete session: {}".format(core.delete_session(session.id))) + response = core.set_session_state(session.id, core_pb2.STATE_INSTANTIATION) + logging.info("set session state: %s", response) if __name__ == "__main__": From 315e9b9cd34665299543f4d0ec587fbcce3b0adb Mon Sep 17 00:00:00 2001 From: bharnden Date: Sat, 30 Mar 2019 21:19:00 -0700 Subject: [PATCH 60/72] updated core url to reference github --- Makefile.am | 2 +- daemon/setup.py.in | 2 +- netns/setup.py.in | 2 +- ns3/setup.py.in | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile.am b/Makefile.am index 7626ced9..42dd4af6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -56,7 +56,7 @@ fpm -s dir -t $1 -n core-gui \ -m "$(PACKAGE_MAINTAINERS)" \ --license "BSD" \ --description "Common Open Research Emulator GUI front-end" \ - --url http://www.nrl.navy.mil/itd/ncs/products/core \ + --url https://github.com/coreemu/core \ --vendor "$(PACKAGE_VENDOR)" \ -p core-gui_VERSION_ARCH.$1 \ -v $(PACKAGE_VERSION) \ diff --git a/daemon/setup.py.in b/daemon/setup.py.in index efbb980d..89be649e 100644 --- a/daemon/setup.py.in +++ b/daemon/setup.py.in @@ -54,7 +54,7 @@ setup( data_files=data_files, scripts=glob.glob("scripts/*"), description="Python components of CORE", - url="http://www.nrl.navy.mil/itd/ncs/products/core", + url="https://github.com/coreemu/core", author="Boeing Research & Technology", author_email="core-dev@nrl.navy.mil", license="BSD", diff --git a/netns/setup.py.in b/netns/setup.py.in index ad53e41a..0866fe5c 100644 --- a/netns/setup.py.in +++ b/netns/setup.py.in @@ -36,7 +36,7 @@ setup( netns, vcmd ], - url="http://www.nrl.navy.mil/itd/ncs/products/core", + url="https://github.com/coreemu/core", author="Boeing Research & Technology", author_email="core-dev@nrl.navy.mil", license="BSD", diff --git a/ns3/setup.py.in b/ns3/setup.py.in index 31f154f2..8335d43f 100644 --- a/ns3/setup.py.in +++ b/ns3/setup.py.in @@ -12,7 +12,7 @@ setup( ], data_files=[(_EXAMPLES_DIR, glob.glob("examples/*"))], description="Python ns-3 components of CORE", - url="http://www.nrl.navy.mil/itd/ncs/products/core", + url="https://github.com/coreemu/core", author="Boeing Research & Technology", author_email="core-dev@nrl.navy.mil", license="GPLv2", From 34678ff48fe5b96df55f4a1bff63f8be5d22b38d Mon Sep 17 00:00:00 2001 From: bharnden Date: Thu, 4 Apr 2019 20:25:23 -0700 Subject: [PATCH 61/72] updated requirements.txt to add grpcio library --- daemon/requirements.txt | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/daemon/requirements.txt b/daemon/requirements.txt index ba137563..ae84c527 100644 --- a/daemon/requirements.txt +++ b/daemon/requirements.txt @@ -1,7 +1,5 @@ enum34==1.1.6 +futures==3.2.0 +grpcio==1.18.0 lxml==3.5.0 -mock==1.3.0 -pycco==0.5.1 -pytest==3.0.7 -pytest-cov==2.5.1 -pytest-runner==2.11.1 +six==1.12.0 From 51634318a31dc796953fc26a7f8c7081c8f5af0e Mon Sep 17 00:00:00 2001 From: bharnden Date: Thu, 4 Apr 2019 20:52:20 -0700 Subject: [PATCH 62/72] small codacy cleanup --- daemon/core/grpc/server.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index 58316d9c..55a72c4f 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -264,7 +264,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): def NodeEvents(self, request, context): session = self.get_session(request.id, context) queue = Queue() - session.node_handlers.append(lambda x: queue.put(x)) + session.node_handlers.append(queue.put) while self._is_running(context): try: @@ -284,7 +284,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): def LinkEvents(self, request, context): session = self.get_session(request.id, context) queue = Queue() - session.link_handlers.append(lambda x: queue.put(x)) + session.link_handlers.append(queue.put) while self._is_running(context): try: @@ -329,7 +329,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): def SessionEvents(self, request, context): session = self.get_session(request.id, context) queue = Queue() - session.event_handlers.append(lambda x: queue.put(x)) + session.event_handlers.append(queue.put) while self._is_running(context): try: @@ -354,7 +354,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): def ConfigEvents(self, request, context): session = self.get_session(request.id, context) queue = Queue() - session.config_handlers.append(lambda x: queue.put(x)) + session.config_handlers.append(queue.put) while self._is_running(context): try: @@ -384,7 +384,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): def ExceptionEvents(self, request, context): session = self.get_session(request.id, context) queue = Queue() - session.exception_handlers.append(lambda x: queue.put(x)) + session.exception_handlers.append(queue.put) while self._is_running(context): try: @@ -407,7 +407,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): def FileEvents(self, request, context): session = self.get_session(request.id, context) queue = Queue() - session.file_handlers.append(lambda x: queue.put(x)) + session.file_handlers.append(queue.put) while self._is_running(context): try: From 3c8b4ab0a564c80939bbe17e6cc05cd3a9cdf808 Mon Sep 17 00:00:00 2001 From: bharnden Date: Thu, 4 Apr 2019 21:32:50 -0700 Subject: [PATCH 63/72] #236 fix for missing loss/per values in xml and other cases --- daemon/core/corehandlers.py | 5 ++++- daemon/core/coreobj.py | 6 ++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/daemon/core/corehandlers.py b/daemon/core/corehandlers.py index 4a38bd3c..40b5cf6d 100644 --- a/daemon/core/corehandlers.py +++ b/daemon/core/corehandlers.py @@ -320,13 +320,16 @@ class CoreHandler(SocketServer.BaseRequestHandler): :return: nothing """ logging.debug("handling broadcast link: %s", link_data) + per = "" + if link_data.per is not None: + per = str(link_data.per) tlv_data = structutils.pack_values(coreapi.CoreLinkTlv, [ (LinkTlvs.N1_NUMBER, link_data.node1_id), (LinkTlvs.N2_NUMBER, link_data.node2_id), (LinkTlvs.DELAY, link_data.delay), (LinkTlvs.BANDWIDTH, link_data.bandwidth), - (LinkTlvs.PER, link_data.per), + (LinkTlvs.PER, per), (LinkTlvs.DUP, link_data.dup), (LinkTlvs.JITTER, link_data.jitter), (LinkTlvs.MER, link_data.mer), diff --git a/daemon/core/coreobj.py b/daemon/core/coreobj.py index 91492cd2..daf5d9da 100644 --- a/daemon/core/coreobj.py +++ b/daemon/core/coreobj.py @@ -570,7 +570,8 @@ class PyCoreNet(PyCoreObj): delay=netif.getparam("delay"), bandwidth=netif.getparam("bw"), dup=netif.getparam("duplicate"), - jitter=netif.getparam("jitter") + jitter=netif.getparam("jitter"), + per=netif.getparam("loss") ) all_links.append(link_data) @@ -587,7 +588,8 @@ class PyCoreNet(PyCoreObj): delay=netif.getparam("delay"), bandwidth=netif.getparam("bw"), dup=netif.getparam("duplicate"), - jitter=netif.getparam("jitter") + jitter=netif.getparam("jitter"), + per=netif.getparam("loss") ) netif.swapparams('_params_up') From 747c2792a13088b3bc8ff7e0b3e51535c3c10ec5 Mon Sep 17 00:00:00 2001 From: bharnden Date: Fri, 5 Apr 2019 21:25:46 -0700 Subject: [PATCH 64/72] fixed issue with grpc server saving mac addresses on links --- daemon/core/corehandlers.py | 22 ++++++++++++---------- daemon/core/emulator/emudata.py | 14 +++++++------- daemon/core/grpc/server.py | 5 +++++ 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/daemon/core/corehandlers.py b/daemon/core/corehandlers.py index 40b5cf6d..728eb970 100644 --- a/daemon/core/corehandlers.py +++ b/daemon/core/corehandlers.py @@ -1761,15 +1761,17 @@ class CoreHandler(SocketServer.BaseRequestHandler): self.session.broadcast_config(config_data) # send session metadata - data_values = "|".join(["%s=%s" % item for item in self.session.metadata.get_configs().iteritems()]) - data_types = tuple(ConfigDataTypes.STRING.value for _ in self.session.metadata.get_configs()) - config_data = ConfigData( - message_type=0, - object=self.session.metadata.name, - type=ConfigFlags.NONE.value, - data_types=data_types, - data_values=data_values - ) - self.session.broadcast_config(config_data) + metadata_configs = self.session.metadata.get_configs() + if metadata_configs: + data_values = "|".join(["%s=%s" % item for item in metadata_configs.iteritems()]) + data_types = tuple(ConfigDataTypes.STRING.value for _ in self.session.metadata.get_configs()) + config_data = ConfigData( + message_type=0, + object=self.session.metadata.name, + type=ConfigFlags.NONE.value, + data_types=data_types, + data_values=data_values + ) + self.session.broadcast_config(config_data) logging.info("informed GUI about %d nodes and %d links", len(nodes_data), len(links_data)) diff --git a/daemon/core/emulator/emudata.py b/daemon/core/emulator/emudata.py index e224209d..2d70d367 100644 --- a/daemon/core/emulator/emudata.py +++ b/daemon/core/emulator/emudata.py @@ -183,13 +183,13 @@ class InterfaceData(object): """ Creates an InterfaceData object. - :param int _id: - :param str name: - :param str mac: - :param str ip4: - :param int ip4_mask: - :param str ip6: - :param int ip6_mask: + :param int _id: interface id + :param str name: name for interface + :param core.misc.ipaddress.MacAddress mac: mac address + :param str ip4: ipv4 address + :param int ip4_mask: ipv4 bit mask + :param str ip6: ipv6 address + :param int ip6_mask: ipv6 bit mask """ self.id = _id self.name = name diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index 55a72c4f..4a50cfec 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -13,6 +13,7 @@ from core.enumerations import NodeTypes, EventTypes, LinkTypes from core.grpc import core_pb2 from core.grpc import core_pb2_grpc from core.misc import nodeutils +from core.misc.ipaddress import MacAddress from core.mobility import BasicRangeModel, Ns2ScriptedMobility from core.service import ServiceManager @@ -533,6 +534,8 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): mac = interface_one_data.mac if mac == "": mac = None + else: + mac = MacAddress.from_string(mac) interface_one = InterfaceData( _id=interface_one_data.id, name=name, @@ -552,6 +555,8 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): mac = interface_two_data.mac if mac == "": mac = None + else: + mac = MacAddress.from_string(mac) interface_two = InterfaceData( _id=interface_two_data.id, name=name, From 4ade93a5a6bc1aca7f63b6681f25323978745707 Mon Sep 17 00:00:00 2001 From: bharnden Date: Fri, 5 Apr 2019 22:35:38 -0700 Subject: [PATCH 65/72] fixed spacing in input file for core lib --- daemon/core/constants.py.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/daemon/core/constants.py.in b/daemon/core/constants.py.in index fc901893..f03a7676 100644 --- a/daemon/core/constants.py.in +++ b/daemon/core/constants.py.in @@ -9,9 +9,9 @@ QUAGGA_STATE_DIR = "@CORE_STATE_DIR@/run/quagga" def which(command): for path in os.environ["PATH"].split(os.pathsep): - command_path = os.path.join(path, command) - if os.path.isfile(command_path) and os.access(command_path, os.X_OK): - return command_path + command_path = os.path.join(path, command) + if os.path.isfile(command_path) and os.access(command_path, os.X_OK): + return command_path VNODED_BIN = which("vnoded") From b518105e5f88ac0036404fbd73279473597cbd64 Mon Sep 17 00:00:00 2001 From: bharnden Date: Sat, 6 Apr 2019 23:06:35 -0700 Subject: [PATCH 66/72] updated link test due to change on what get_node_count returns --- daemon/tests/test_links.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/daemon/tests/test_links.py b/daemon/tests/test_links.py index 75475388..27289014 100644 --- a/daemon/tests/test_links.py +++ b/daemon/tests/test_links.py @@ -124,7 +124,6 @@ class TestLinks: session.add_link(node_one.objid, node_two.objid, interface_one, interface_two) assert node_one.netif(interface_one.id) assert node_two.netif(interface_two.id) - assert session.get_node_count() == 3 # when session.delete_link(node_one.objid, node_two.objid, interface_one.id, interface_two.id) @@ -132,7 +131,6 @@ class TestLinks: # then assert not node_one.netif(interface_one.id) assert not node_two.netif(interface_two.id) - assert session.get_node_count() == 2 def test_link_bandwidth(self, session, ip_prefixes): """ From 2825ce423b4812258a75a15047abe52a7e9d969e Mon Sep 17 00:00:00 2001 From: bharnden Date: Sun, 7 Apr 2019 13:08:49 -0700 Subject: [PATCH 67/72] removed pydoc line, no longer needed --- daemon/core/mobility.py | 1 - 1 file changed, 1 deletion(-) diff --git a/daemon/core/mobility.py b/daemon/core/mobility.py index e8bb747d..4a9eb746 100644 --- a/daemon/core/mobility.py +++ b/daemon/core/mobility.py @@ -271,7 +271,6 @@ class WirelessModel(ConfigurableOptions): :param core.session.Session session: core session we are tied to :param int object_id: object id - :param dict config: values """ self.session = session self.object_id = object_id From 6672fd0f7a5f2311bdc8175f6890241eb8df7915 Mon Sep 17 00:00:00 2001 From: bharnden Date: Mon, 8 Apr 2019 09:49:37 -0700 Subject: [PATCH 68/72] updates to allow configuration of installed emane_prefix for default models, requires small tweaks to custom models --- daemon/core/emane/bypass.py | 5 +++++ daemon/core/emane/commeffect.py | 13 ++++++++---- daemon/core/emane/emanemanager.py | 3 +++ daemon/core/emane/emanemodel.py | 34 +++++++++++++++++++++++++++++-- daemon/core/emane/ieee80211abg.py | 16 +++++++++------ daemon/core/emane/rfpipe.py | 16 +++++++++------ daemon/core/emane/tdma.py | 33 ++++++++++++++++-------------- daemon/data/core.conf | 2 ++ daemon/tests/conftest.py | 2 +- daemon/tests/test_emane.py | 8 ++++++++ 10 files changed, 98 insertions(+), 34 deletions(-) diff --git a/daemon/core/emane/bypass.py b/daemon/core/emane/bypass.py index 42340f2b..91a01b37 100644 --- a/daemon/core/emane/bypass.py +++ b/daemon/core/emane/bypass.py @@ -29,6 +29,11 @@ class EmaneBypassModel(emanemodel.EmaneModel): phy_library = "bypassphylayer" phy_config = [] + @classmethod + def load(cls, emane_prefix): + # ignore default logic + pass + # override config groups @classmethod def config_groups(cls): diff --git a/daemon/core/emane/commeffect.py b/daemon/core/emane/commeffect.py index 37fe0f67..62676c16 100644 --- a/daemon/core/emane/commeffect.py +++ b/daemon/core/emane/commeffect.py @@ -37,13 +37,18 @@ class EmaneCommEffectModel(emanemodel.EmaneModel): name = "emane_commeffect" shim_library = "commeffectshim" - shim_xml = "/usr/share/emane/manifest/commeffectshim.xml" + shim_xml = "commeffectshim.xml" shim_defaults = {} - config_shim = emanemanifest.parse(shim_xml, shim_defaults) + config_shim = [] # comm effect does not need the default phy and external configurations - phy_config = () - external_config = () + phy_config = [] + external_config = [] + + @classmethod + def load(cls, emane_prefix): + shim_xml_path = os.path.join(emane_prefix, "share/emane/manifest", cls.shim_xml) + cls.config_shim = emanemanifest.parse(shim_xml_path, cls.shim_defaults) @classmethod def configurations(cls): diff --git a/daemon/core/emane/emanemanager.py b/daemon/core/emane/emanemanager.py index 4255b7de..a7c9b121 100644 --- a/daemon/core/emane/emanemanager.py +++ b/daemon/core/emane/emanemanager.py @@ -51,6 +51,7 @@ EMANE_MODELS = [ EmaneBypassModel, EmaneTdmaModel ] +DEFAULT_EMANE_PREFIX = "/usr" class EmaneManager(ModelManager): @@ -212,6 +213,8 @@ class EmaneManager(ModelManager): """ for emane_model in emane_models: logging.info("loading emane model: %s", emane_model.__name__) + emane_prefix = self.session.options.get_config("emane_prefix", default=DEFAULT_EMANE_PREFIX) + emane_model.load(emane_prefix) self.models[emane_model.name] = emane_model def add_node(self, emane_node): diff --git a/daemon/core/emane/emanemodel.py b/daemon/core/emane/emanemodel.py index 284ef84a..01bf1835 100644 --- a/daemon/core/emane/emanemodel.py +++ b/daemon/core/emane/emanemodel.py @@ -26,13 +26,13 @@ class EmaneModel(WirelessModel): # default phy configuration settings, using the universal model phy_library = None - phy_xml = "/usr/share/emane/manifest/emanephy.xml" + phy_xml = "emanephy.xml" phy_defaults = { "subid": "1", "propagationmodel": "2ray", "noisemode": "none" } - phy_config = emanemanifest.parse(phy_xml, phy_defaults) + phy_config = [] # support for external configurations external_config = [ @@ -43,12 +43,42 @@ class EmaneModel(WirelessModel): config_ignore = set() + @classmethod + def load(cls, emane_prefix): + """ + Called after being loaded within the EmaneManager. Provides configured emane_prefix for + parsing xml files. + + :param str emane_prefix: configured emane prefix path + :return: nothing + """ + manifest_path = "share/emane/manifest" + # load mac configuration + mac_xml_path = os.path.join(emane_prefix, manifest_path, cls.mac_xml) + cls.mac_config = emanemanifest.parse(mac_xml_path, cls.mac_defaults) + + # load phy configuration + phy_xml_path = os.path.join(emane_prefix, manifest_path, cls.phy_xml) + cls.phy_config = emanemanifest.parse(phy_xml_path, cls.phy_defaults) + @classmethod def configurations(cls): + """ + Returns the combination all all configurations (mac, phy, and external). + + :return: all configurations + :rtype: list[Configuration] + """ return cls.mac_config + cls.phy_config + cls.external_config @classmethod def config_groups(cls): + """ + Returns the defined configuration groups. + + :return: list of configuration groups. + :rtype: list[ConfigGroup] + """ mac_len = len(cls.mac_config) phy_len = len(cls.phy_config) + mac_len config_len = len(cls.configurations()) diff --git a/daemon/core/emane/ieee80211abg.py b/daemon/core/emane/ieee80211abg.py index 9b4e6ea6..e99ebe3b 100644 --- a/daemon/core/emane/ieee80211abg.py +++ b/daemon/core/emane/ieee80211abg.py @@ -1,8 +1,8 @@ """ ieee80211abg.py: EMANE IEEE 802.11abg model for CORE """ +import os -from core.emane import emanemanifest from core.emane import emanemodel @@ -12,8 +12,12 @@ class EmaneIeee80211abgModel(emanemodel.EmaneModel): # mac configuration mac_library = "ieee80211abgmaclayer" - mac_xml = "/usr/share/emane/manifest/ieee80211abgmaclayer.xml" - mac_defaults = { - "pcrcurveuri": "/usr/share/emane/xml/models/mac/ieee80211abg/ieee80211pcr.xml", - } - mac_config = emanemanifest.parse(mac_xml, mac_defaults) + mac_xml = "ieee80211abgmaclayer.xml" + + @classmethod + def load(cls, emane_prefix): + cls.mac_defaults["pcrcurveuri"] = os.path.join( + emane_prefix, + "share/emane/xml/models/mac/ieee80211abg/ieee80211pcr.xml" + ) + super(EmaneIeee80211abgModel, cls).load(emane_prefix) diff --git a/daemon/core/emane/rfpipe.py b/daemon/core/emane/rfpipe.py index 3c600ddd..4942d89e 100644 --- a/daemon/core/emane/rfpipe.py +++ b/daemon/core/emane/rfpipe.py @@ -1,8 +1,8 @@ """ rfpipe.py: EMANE RF-PIPE model for CORE """ +import os -from core.emane import emanemanifest from core.emane import emanemodel @@ -12,8 +12,12 @@ class EmaneRfPipeModel(emanemodel.EmaneModel): # mac configuration mac_library = "rfpipemaclayer" - mac_xml = "/usr/share/emane/manifest/rfpipemaclayer.xml" - mac_defaults = { - "pcrcurveuri": "/usr/share/emane/xml/models/mac/rfpipe/rfpipepcr.xml", - } - mac_config = emanemanifest.parse(mac_xml, mac_defaults) + mac_xml = "rfpipemaclayer.xml" + + @classmethod + def load(cls, emane_prefix): + cls.mac_defaults["pcrcurveuri"] = os.path.join( + emane_prefix, + "share/emane/xml/models/mac/rfpipe/rfpipepcr.xml" + ) + super(EmaneRfPipeModel, cls).load(emane_prefix) diff --git a/daemon/core/emane/tdma.py b/daemon/core/emane/tdma.py index 832f5d13..b599638d 100644 --- a/daemon/core/emane/tdma.py +++ b/daemon/core/emane/tdma.py @@ -7,7 +7,6 @@ import os from core import constants from core.conf import Configuration -from core.emane import emanemanifest from core.emane import emanemodel from core.enumerations import ConfigDataTypes from core.misc import utils @@ -19,26 +18,30 @@ class EmaneTdmaModel(emanemodel.EmaneModel): # mac configuration mac_library = "tdmaeventschedulerradiomodel" - mac_xml = "/usr/share/emane/manifest/tdmaeventschedulerradiomodel.xml" - mac_defaults = { - "pcrcurveuri": "/usr/share/emane/xml/models/mac/tdmaeventscheduler/tdmabasemodelpcr.xml", - } - mac_config = emanemanifest.parse(mac_xml, mac_defaults) + mac_xml = "tdmaeventschedulerradiomodel.xml" # add custom schedule options and ignore it when writing emane xml schedule_name = "schedule" default_schedule = os.path.join(constants.CORE_DATA_DIR, "examples", "tdma", "schedule.xml") - mac_config.insert( - 0, - Configuration( - _id=schedule_name, - _type=ConfigDataTypes.STRING, - default=default_schedule, - label="TDMA schedule file (core)" - ) - ) config_ignore = {schedule_name} + @classmethod + def load(cls, emane_prefix): + cls.mac_defaults["pcrcurveuri"] = os.path.join( + emane_prefix, + "share/emane/xml/models/mac/tdmaeventscheduler/tdmabasemodelpcr.xml" + ) + super(EmaneTdmaModel, cls).load(emane_prefix) + cls.mac_config.insert( + 0, + Configuration( + _id=cls.schedule_name, + _type=ConfigDataTypes.STRING, + default=cls.default_schedule, + label="TDMA schedule file (core)" + ) + ) + def post_startup(self): """ Logic to execute after the emane manager is finished with startup. diff --git a/daemon/data/core.conf b/daemon/data/core.conf index c4285779..11de4cca 100644 --- a/daemon/data/core.conf +++ b/daemon/data/core.conf @@ -55,3 +55,5 @@ emane_event_monitor = False # EMANE log level range [0,4] default: 2 #emane_log_level = 2 emane_realtime = True +# prefix used for emane installation +# emane_prefix = /usr diff --git a/daemon/tests/conftest.py b/daemon/tests/conftest.py index 2dc19184..2116b8fe 100644 --- a/daemon/tests/conftest.py +++ b/daemon/tests/conftest.py @@ -224,7 +224,7 @@ def grpc_server(): @pytest.fixture def session(): # use coreemu and create a session - coreemu = CoreEmu() + coreemu = CoreEmu(config={"emane_prefix": "/usr"}) session_fixture = coreemu.create_session() session_fixture.set_state(EventTypes.CONFIGURATION_STATE) assert os.path.exists(session_fixture.session_dir) diff --git a/daemon/tests/test_emane.py b/daemon/tests/test_emane.py index 7720e285..1c77d8ea 100644 --- a/daemon/tests/test_emane.py +++ b/daemon/tests/test_emane.py @@ -1,6 +1,7 @@ """ Unit tests for testing CORE EMANE networks. """ +import os import pytest @@ -19,6 +20,7 @@ _EMANE_MODELS = [ EmaneCommEffectModel, EmaneTdmaModel, ] +_DIR = os.path.dirname(os.path.abspath(__file__)) class TestEmane: @@ -39,6 +41,12 @@ class TestEmane: ) emane_network.setposition(x=80, y=50) + # configure tdma + if model == EmaneTdmaModel: + session.emane.set_model_config(emane_network.objid, EmaneTdmaModel.name, { + "schedule": os.path.join(_DIR, "../examples/tdma/schedule.xml") + }) + # create nodes node_options = NodeOptions() node_options.set_position(150, 150) From 73bc56e4c354a25473a6998bf1a108a8aaffd22f Mon Sep 17 00:00:00 2001 From: bharnden Date: Mon, 8 Apr 2019 10:39:36 -0700 Subject: [PATCH 69/72] renamed session.session_id to session.id --- daemon/core/broker.py | 6 +- daemon/core/corehandlers.py | 23 ++++---- daemon/core/emulator/coreemu.py | 8 +-- daemon/core/grpc/server.py | 8 +-- daemon/core/misc/utils.py | 2 +- daemon/core/netns/openvswitch.py | 2 +- daemon/core/netns/vnet.py | 2 +- daemon/core/session.py | 28 +++++----- daemon/tests/conftest.py | 2 +- daemon/tests/test_grpc.py | 94 ++++++++++++++++---------------- ns3/corens3/obj.py | 4 +- 11 files changed, 89 insertions(+), 90 deletions(-) diff --git a/daemon/core/broker.py b/daemon/core/broker.py index a1c45bd6..19d9713b 100644 --- a/daemon/core/broker.py +++ b/daemon/core/broker.py @@ -389,7 +389,7 @@ class CoreBroker(object): sid = self.session_id_master if sid is None: # this is the master session - sid = self.session.session_id + sid = self.session.id key = (sid << 16) ^ hash(n1num) ^ (hash(n2num) << 8) return key & 0xFFFFFFFF @@ -697,7 +697,7 @@ class CoreBroker(object): tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.DATA_TYPES.value, (ConfigDataTypes.STRING.value,)) tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.VALUES.value, "%s:%s:%s" % (server.name, server.host, server.port)) - tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.SESSION.value, "%s" % self.session.session_id) + tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.SESSION.value, "%s" % self.session.id) msg = coreapi.CoreConfMessage.pack(0, tlvdata) server.sock.send(msg) @@ -973,7 +973,7 @@ class CoreBroker(object): filename = os.path.join(self.session.session_dir, "servers") master = self.session_id_master if master is None: - master = self.session.session_id + master = self.session.id try: with open(filename, "w") as f: f.write("master=%s\n" % master) diff --git a/daemon/core/corehandlers.py b/daemon/core/corehandlers.py index 728eb970..7c4a377c 100644 --- a/daemon/core/corehandlers.py +++ b/daemon/core/corehandlers.py @@ -57,7 +57,6 @@ class CoreHandler(SocketServer.BaseRequestHandler): :param request: request object :param str client_address: client address :param CoreServer server: core server instance - :return: """ self.done = False self.message_handlers = { @@ -140,7 +139,7 @@ class CoreHandler(SocketServer.BaseRequestHandler): self.session.broker.session_clients.remove(self) if not self.session.broker.session_clients and not self.session.is_active(): logging.info("no session clients left and not active, initiating shutdown") - self.coreemu.delete_session(self.session.session_id) + self.coreemu.delete_session(self.session.id) return SocketServer.BaseRequestHandler.finish(self) @@ -160,9 +159,9 @@ class CoreHandler(SocketServer.BaseRequestHandler): num_sessions = 0 with self._sessions_lock: - for session_id, session in self.coreemu.sessions.iteritems(): + for _id, session in self.coreemu.sessions.iteritems(): num_sessions += 1 - id_list.append(str(session_id)) + id_list.append(str(_id)) name = session.name if not name: @@ -372,7 +371,7 @@ class CoreHandler(SocketServer.BaseRequestHandler): :return: register message data """ - logging.info("GUI has connected to session %d at %s", self.session.session_id, time.ctime()) + logging.info("GUI has connected to session %d at %s", self.session.id, time.ctime()) tlv_data = "" tlv_data += coreapi.CoreRegisterTlv.pack(RegisterTlvs.EXECUTE_SERVER.value, "core-daemon") @@ -538,7 +537,7 @@ class CoreHandler(SocketServer.BaseRequestHandler): # TODO: add shutdown handler for session self.session = self.coreemu.create_session(port, master=False) # self.session.shutdown_handlers.append(self.session_shutdown) - logging.debug("created new session for client: %s", self.session.session_id) + logging.debug("created new session for client: %s", self.session.id) # TODO: hack to associate this handler with this sessions broker for broadcasting # TODO: broker needs to be pulled out of session to the server/handler level @@ -592,7 +591,7 @@ class CoreHandler(SocketServer.BaseRequestHandler): :return: """ exception_data = ExceptionData( - session=str(self.session.session_id), + session=str(self.session.id), node=node, date=time.ctime(), level=level.value, @@ -850,7 +849,7 @@ class CoreHandler(SocketServer.BaseRequestHandler): try: session.open_xml(file_name, start=True) except: - self.coreemu.delete_session(session.session_id) + self.coreemu.delete_session(session.id) raise else: thread = threading.Thread( @@ -908,7 +907,7 @@ class CoreHandler(SocketServer.BaseRequestHandler): # find the session containing this client and set the session to master for session in self.coreemu.sessions.itervalues(): if self in session.broker.session_clients: - logging.debug("setting session to master: %s", session.session_id) + logging.debug("setting session to master: %s", session.id) session.master = True break @@ -1591,7 +1590,7 @@ class CoreHandler(SocketServer.BaseRequestHandler): logging.warn("session %s not found", session_id) continue - logging.info("request to modify to session: %s", session.session_id) + logging.info("request to modify to session: %s", session.id) if names is not None: session.name = names[index] @@ -1624,7 +1623,7 @@ class CoreHandler(SocketServer.BaseRequestHandler): self.remove_session_handlers() self.session.broker.session_clients.remove(self) if not self.session.broker.session_clients and not self.session.is_active(): - self.coreemu.delete_session(self.session.session_id) + self.coreemu.delete_session(self.session.id) # set session to join self.session = session @@ -1727,7 +1726,7 @@ class CoreHandler(SocketServer.BaseRequestHandler): type=ConfigFlags.UPDATE.value, data_types=data_types, data_values=values, - session=str(self.session.session_id), + session=str(self.session.id), opaque=opaque ) self.session.broadcast_config(config_data) diff --git a/daemon/core/emulator/coreemu.py b/daemon/core/emulator/coreemu.py index d0b562cb..9f7e128a 100644 --- a/daemon/core/emulator/coreemu.py +++ b/daemon/core/emulator/coreemu.py @@ -118,8 +118,8 @@ class IdGen(object): class EmuSession(Session): - def __init__(self, session_id, config=None, mkdir=True): - super(EmuSession, self).__init__(session_id, config, mkdir) + def __init__(self, _id, config=None, mkdir=True): + super(EmuSession, self).__init__(_id, config, mkdir) # object management self.node_id_gen = IdGen() @@ -620,7 +620,7 @@ class EmuSession(Session): :return: nothing """ - logging.info("session(%s) shutting down", self.session_id) + logging.info("session(%s) shutting down", self.id) self.set_state(EventTypes.DATACOLLECT_STATE, send_event=True) self.set_state(EventTypes.SHUTDOWN_STATE, send_event=True) super(EmuSession, self).shutdown() @@ -647,7 +647,7 @@ class EmuSession(Session): :return: True if active, False otherwise """ result = self.state in {EventTypes.RUNTIME_STATE.value, EventTypes.DATACOLLECT_STATE.value} - logging.info("session(%s) checking if active: %s", self.session_id, result) + logging.info("session(%s) checking if active: %s", self.id, result) return result def open_xml(self, file_name, start=False): diff --git a/daemon/core/grpc/server.py b/daemon/core/grpc/server.py index 4a50cfec..45c35fc8 100644 --- a/daemon/core/grpc/server.py +++ b/daemon/core/grpc/server.py @@ -153,7 +153,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): session.set_state(EventTypes.DEFINITION_STATE) session.location.setrefgeo(47.57917, -122.13232, 2.0) session.location.refscale = 150000.0 - return core_pb2.CreateSessionResponse(id=session.session_id, state=session.state) + return core_pb2.CreateSessionResponse(id=session.id, state=session.state) def DeleteSession(self, request, context): logging.debug("delete session: %s", request) @@ -344,7 +344,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): name=event.name, data=event.data, time=event_time, - session=session.session_id + session=session.id ) yield session_event except Empty: @@ -881,8 +881,8 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): try: session.open_xml(temp_path, start=True) - return core_pb2.OpenXmlResponse(session=session.session_id, result=True) + return core_pb2.OpenXmlResponse(session=session.id, result=True) except IOError: logging.exception("error opening session file") - self.coreemu.delete_session(session.session_id) + self.coreemu.delete_session(session.id) context.abort(grpc.StatusCode.INVALID_ARGUMENT, "invalid xml file") diff --git a/daemon/core/misc/utils.py b/daemon/core/misc/utils.py index 0aadd5fe..f2e578bf 100644 --- a/daemon/core/misc/utils.py +++ b/daemon/core/misc/utils.py @@ -319,7 +319,7 @@ def expand_corepath(pathname, session=None, node=None): """ if session is not None: pathname = pathname.replace("~", "/home/%s" % session.user) - pathname = pathname.replace("%SESSION%", str(session.session_id)) + pathname = pathname.replace("%SESSION%", str(session.id)) pathname = pathname.replace("%SESSION_DIR%", session.session_dir) pathname = pathname.replace("%SESSION_USER%", session.user) diff --git a/daemon/core/netns/openvswitch.py b/daemon/core/netns/openvswitch.py index c0b86d0a..d18471ea 100644 --- a/daemon/core/netns/openvswitch.py +++ b/daemon/core/netns/openvswitch.py @@ -648,7 +648,7 @@ class OvsGreTapBridge(OvsNet): OvsNet.__init__(self, session=session, objid=objid, name=name, policy=policy, start=False) self.grekey = key if self.grekey is None: - self.grekey = self.session.session_id ^ self.objid + self.grekey = self.session.id ^ self.objid self.localnum = None self.remotenum = None diff --git a/daemon/core/netns/vnet.py b/daemon/core/netns/vnet.py index 55dc855d..17bcada0 100644 --- a/daemon/core/netns/vnet.py +++ b/daemon/core/netns/vnet.py @@ -589,7 +589,7 @@ class GreTapBridge(LxBrNet): LxBrNet.__init__(self, session=session, objid=objid, name=name, policy=policy, start=False) self.grekey = key if self.grekey is None: - self.grekey = self.session.session_id ^ self.objid + self.grekey = self.session.id ^ self.objid self.localnum = None self.remotenum = None self.remoteip = remoteip diff --git a/daemon/core/session.py b/daemon/core/session.py index d9c02a5f..84f1d822 100644 --- a/daemon/core/session.py +++ b/daemon/core/session.py @@ -46,18 +46,18 @@ class Session(object): CORE session manager. """ - def __init__(self, session_id, config=None, mkdir=True): + def __init__(self, _id, config=None, mkdir=True): """ Create a Session instance. - :param int session_id: session id + :param int _id: session id :param dict config: session configuration :param bool mkdir: flag to determine if a directory should be made """ - self.session_id = session_id + self.id = _id # define and create session directory when desired - self.session_dir = os.path.join(tempfile.gettempdir(), "pycore.%s" % self.session_id) + self.session_dir = os.path.join(tempfile.gettempdir(), "pycore.%s" % self.id) if mkdir: os.mkdir(self.session_dir) @@ -207,12 +207,12 @@ class Session(object): state_name = state.name if self.state == state_value: - logging.info("session(%s) is already in state: %s, skipping change", self.session_id, state_name) + logging.info("session(%s) is already in state: %s, skipping change", self.id, state_name) return self.state = state_value self._state_time = time.time() - logging.info("changing session(%s) to state %s", self.session_id, state_name) + logging.info("changing session(%s) to state %s", self.id, state_name) self.write_state(state_value) self.run_hooks(state_value) @@ -397,7 +397,7 @@ class Session(object): :return: """ env = os.environ.copy() - env["SESSION"] = "%s" % self.session_id + env["SESSION"] = "%s" % self.id env["SESSION_SHORT"] = "%s" % self.short_session_id() env["SESSION_DIR"] = "%s" % self.session_dir env["SESSION_NAME"] = "%s" % self.name @@ -562,7 +562,7 @@ class Session(object): """ Log information about the session in its current state. """ - logging.info("session id=%s name=%s state=%s", self.session_id, self.name, self.state) + logging.info("session id=%s name=%s state=%s", self.id, self.name, self.state) logging.info("file=%s thumbnail=%s node_count=%s/%s", self.file_name, self.thumbnail, self.get_node_count(), len(self.objects)) @@ -579,7 +579,7 @@ class Session(object): exception_data = ExceptionData( node=object_id, - session=str(self.session_id), + session=str(self.id), level=level, source=source, date=time.ctime(), @@ -649,8 +649,8 @@ class Session(object): # this is called from instantiate() after receiving an event message # for the instantiation state, and from the broker when distributed # nodes have been started - logging.info("session(%s) checking if not in runtime state, current state: %s", self.session_id, - coreapi.state_name(self.state)) + logging.info("session(%s) checking if not in runtime state, current state: %s", self.id, + coreapi.state_name(self.state)) if self.state == EventTypes.RUNTIME_STATE.value: logging.info("valid runtime state found, returning") return @@ -696,7 +696,7 @@ class Session(object): and links remain. """ node_count = self.get_node_count() - logging.info("session(%s) checking shutdown: %s nodes remaining", self.session_id, node_count) + logging.info("session(%s) checking shutdown: %s nodes remaining", self.id, node_count) shutdown = False if node_count == 0: @@ -710,7 +710,7 @@ class Session(object): Return a shorter version of the session ID, appropriate for interface names, where length may be limited. """ - ssid = (self.session_id >> 8) ^ (self.session_id & ((1 << 8) - 1)) + ssid = (self.id >> 8) ^ (self.id & ((1 << 8) - 1)) return "%x" % ssid def boot_nodes(self): @@ -963,7 +963,7 @@ class Session(object): logging.exception("error retrieving control net object") return - header = "CORE session %s host entries" % self.session_id + header = "CORE session %s host entries" % self.id if remove: logging.info("Removing /etc/hosts file entries.") utils.file_demunge("/etc/hosts", header) diff --git a/daemon/tests/conftest.py b/daemon/tests/conftest.py index 2116b8fe..3bd32121 100644 --- a/daemon/tests/conftest.py +++ b/daemon/tests/conftest.py @@ -194,7 +194,7 @@ class CoreServerTest(object): # set services for host nodes message = CoreConfMessage.create(0, [ - (ConfigTlvs.SESSION, str(self.session.session_id)), + (ConfigTlvs.SESSION, str(self.session.id)), (ConfigTlvs.OBJECT, "services"), (ConfigTlvs.TYPE, 0), (ConfigTlvs.DATA_TYPES, (10, 10, 10)), diff --git a/daemon/tests/test_grpc.py b/daemon/tests/test_grpc.py index c0f14ef4..85a7910d 100644 --- a/daemon/tests/test_grpc.py +++ b/daemon/tests/test_grpc.py @@ -32,7 +32,7 @@ class TestGrpc: assert session.state == response.state if session_id is not None: assert response.id == session_id - assert session.session_id == session_id + assert session.id == session_id @pytest.mark.parametrize("session_id, expected", [ (None, True), @@ -43,7 +43,7 @@ class TestGrpc: client = CoreGrpcClient() session = grpc_server.coreemu.create_session() if session_id is None: - session_id = session.session_id + session_id = session.id # then with client.context_connect(): @@ -62,7 +62,7 @@ class TestGrpc: # then with client.context_connect(): - response = client.get_session(session.session_id) + response = client.get_session(session.id) # then assert response.session.state == core_pb2.STATE_DEFINITION @@ -81,7 +81,7 @@ class TestGrpc: # then found_session = None for current_session in response.sessions: - if current_session.id == session.session_id: + if current_session.id == session.id: found_session = current_session break assert len(response.sessions) == 1 @@ -94,7 +94,7 @@ class TestGrpc: # then with client.context_connect(): - response = client.get_session_options(session.session_id) + response = client.get_session_options(session.id) # then assert len(response.groups) > 0 @@ -106,7 +106,7 @@ class TestGrpc: # then with client.context_connect(): - response = client.get_session_location(session.session_id) + response = client.get_session_location(session.id) # then assert response.scale == 1.0 @@ -128,7 +128,7 @@ class TestGrpc: lat_lon_alt = (1, 1, 1) with client.context_connect(): response = client.set_session_location( - session.session_id, + session.id, x=xyz[0], y=xyz[1], z=xyz[2], lat=lat_lon_alt[0], lon=lat_lon_alt[1], alt=lat_lon_alt[2], scale=scale @@ -149,7 +149,7 @@ class TestGrpc: option = "enablerj45" value = "1" with client.context_connect(): - response = client.set_session_options(session.session_id, {option: value}) + response = client.set_session_options(session.id, {option: value}) # then assert response.result is True @@ -164,7 +164,7 @@ class TestGrpc: # then with client.context_connect(): - response = client.set_session_state(session.session_id, core_pb2.STATE_DEFINITION) + response = client.set_session_state(session.id, core_pb2.STATE_DEFINITION) # then assert response.result is True @@ -178,7 +178,7 @@ class TestGrpc: # then with client.context_connect(): node = core_pb2.Node() - response = client.add_node(session.session_id, node) + response = client.add_node(session.id, node) # then assert response.id is not None @@ -192,7 +192,7 @@ class TestGrpc: # then with client.context_connect(): - response = client.get_node(session.session_id, node.objid) + response = client.get_node(session.id, node.objid) # then assert response.node.id == node.objid @@ -211,7 +211,7 @@ class TestGrpc: x, y = 10, 10 with client.context_connect(): position = core_pb2.Position(x=x, y=y) - response = client.edit_node(session.session_id, node_id, position) + response = client.edit_node(session.id, node_id, position) # then assert response.result is expected @@ -231,7 +231,7 @@ class TestGrpc: # then with client.context_connect(): - response = client.delete_node(session.session_id, node_id) + response = client.delete_node(session.id, node_id) # then assert response.result is expected @@ -249,7 +249,7 @@ class TestGrpc: # then with client.context_connect(): - response = client.get_hooks(session.session_id) + response = client.get_hooks(session.id) # then assert len(response.hooks) == 1 @@ -267,7 +267,7 @@ class TestGrpc: file_name = "test" file_data = "echo hello" with client.context_connect(): - response = client.add_hook(session.session_id, core_pb2.STATE_RUNTIME, file_name, file_data) + response = client.add_hook(session.id, core_pb2.STATE_RUNTIME, file_name, file_data) # then assert response.result is True @@ -280,7 +280,7 @@ class TestGrpc: # then with client.context_connect(): - client.save_xml(session.session_id, str(tmp)) + client.save_xml(session.id, str(tmp)) # then assert tmp.exists() @@ -311,7 +311,7 @@ class TestGrpc: # then with client.context_connect(): - response = client.get_node_links(session.session_id, switch.objid) + response = client.get_node_links(session.id, switch.objid) # then assert len(response.links) == 1 @@ -328,7 +328,7 @@ class TestGrpc: # then with pytest.raises(grpc.RpcError): with client.context_connect(): - client.get_node_links(session.session_id, 3) + client.get_node_links(session.id, 3) def test_add_link(self, grpc_server, interface_helper): # given @@ -341,7 +341,7 @@ class TestGrpc: # then interface = interface_helper.create_interface(node.objid, 0) with client.context_connect(): - response = client.add_link(session.session_id, node.objid, switch.objid, interface) + response = client.add_link(session.id, node.objid, switch.objid, interface) # then assert response.result is True @@ -357,7 +357,7 @@ class TestGrpc: interface = interface_helper.create_interface(node.objid, 0) with pytest.raises(grpc.RpcError): with client.context_connect(): - client.add_link(session.session_id, 1, 3, interface) + client.add_link(session.id, 1, 3, interface) def test_edit_link(self, grpc_server, ip_prefixes): # given @@ -373,7 +373,7 @@ class TestGrpc: # then with client.context_connect(): - response = client.edit_link(session.session_id, node.objid, switch.objid, options) + response = client.edit_link(session.id, node.objid, switch.objid, options) # then assert response.result is True @@ -400,7 +400,7 @@ class TestGrpc: # then with client.context_connect(): response = client.delete_link( - session.session_id, node_one.objid, node_two.objid, interface_one.id, interface_two.id) + session.id, node_one.objid, node_two.objid, interface_one.id, interface_two.id) # then assert response.result is True @@ -414,7 +414,7 @@ class TestGrpc: # then with client.context_connect(): - response = client.get_wlan_config(session.session_id, wlan.objid) + response = client.get_wlan_config(session.id, wlan.objid) # then assert len(response.groups) > 0 @@ -429,7 +429,7 @@ class TestGrpc: # then with client.context_connect(): - response = client.set_wlan_config(session.session_id, wlan.objid, {range_key: range_value}) + response = client.set_wlan_config(session.id, wlan.objid, {range_key: range_value}) # then assert response.result is True @@ -443,7 +443,7 @@ class TestGrpc: # then with client.context_connect(): - response = client.get_emane_config(session.session_id) + response = client.get_emane_config(session.id) # then assert len(response.groups) > 0 @@ -457,7 +457,7 @@ class TestGrpc: # then with client.context_connect(): - response = client.set_emane_config(session.session_id, {config_key: config_value}) + response = client.set_emane_config(session.id, {config_key: config_value}) # then assert response.result is True @@ -479,7 +479,7 @@ class TestGrpc: # then with client.context_connect(): - response = client.get_emane_model_configs(session.session_id) + response = client.get_emane_model_configs(session.id) # then assert len(response.configs) == 1 @@ -499,7 +499,7 @@ class TestGrpc: # then with client.context_connect(): response = client.set_emane_model_config( - session.session_id, emane_network.objid, EmaneIeee80211abgModel.name, {config_key: config_value}) + session.id, emane_network.objid, EmaneIeee80211abgModel.name, {config_key: config_value}) # then assert response.result is True @@ -518,7 +518,7 @@ class TestGrpc: # then with client.context_connect(): response = client.get_emane_model_config( - session.session_id, emane_network.objid, EmaneIeee80211abgModel.name) + session.id, emane_network.objid, EmaneIeee80211abgModel.name) # then assert len(response.groups) > 0 @@ -530,7 +530,7 @@ class TestGrpc: # then with client.context_connect(): - response = client.get_emane_models(session.session_id) + response = client.get_emane_models(session.id) # then assert len(response.models) > 0 @@ -544,7 +544,7 @@ class TestGrpc: # then with client.context_connect(): - response = client.get_mobility_configs(session.session_id) + response = client.get_mobility_configs(session.id) # then assert len(response.configs) > 0 @@ -559,7 +559,7 @@ class TestGrpc: # then with client.context_connect(): - response = client.get_mobility_config(session.session_id, wlan.objid) + response = client.get_mobility_config(session.id, wlan.objid) # then assert len(response.groups) > 0 @@ -574,7 +574,7 @@ class TestGrpc: # then with client.context_connect(): - response = client.set_mobility_config(session.session_id, wlan.objid, {config_key: config_value}) + response = client.set_mobility_config(session.id, wlan.objid, {config_key: config_value}) # then assert response.result is True @@ -591,7 +591,7 @@ class TestGrpc: # then with client.context_connect(): - response = client.mobility_action(session.session_id, wlan.objid, core_pb2.MOBILITY_STOP) + response = client.mobility_action(session.id, wlan.objid, core_pb2.MOBILITY_STOP) # then assert response.result is True @@ -614,7 +614,7 @@ class TestGrpc: # then with client.context_connect(): - response = client.get_service_defaults(session.session_id) + response = client.get_service_defaults(session.id) # then assert len(response.defaults) > 0 @@ -628,7 +628,7 @@ class TestGrpc: # then with client.context_connect(): - response = client.set_service_defaults(session.session_id, {node_type: services}) + response = client.set_service_defaults(session.id, {node_type: services}) # then assert response.result is True @@ -642,7 +642,7 @@ class TestGrpc: # then with client.context_connect(): - response = client.get_node_service(session.session_id, node.objid, "IPForward") + response = client.get_node_service(session.id, node.objid, "IPForward") # then assert len(response.service.configs) > 0 @@ -655,7 +655,7 @@ class TestGrpc: # then with client.context_connect(): - response = client.get_node_service_file(session.session_id, node.objid, "IPForward", "ipforward.sh") + response = client.get_node_service_file(session.id, node.objid, "IPForward", "ipforward.sh") # then assert response.data is not None @@ -670,7 +670,7 @@ class TestGrpc: # then with client.context_connect(): - response = client.set_node_service(session.session_id, node.objid, service_name, (), validate, ()) + response = client.set_node_service(session.id, node.objid, service_name, (), validate, ()) # then assert response.result is True @@ -688,7 +688,7 @@ class TestGrpc: # then with client.context_connect(): - response = client.set_node_service_file(session.session_id, node.objid, service_name, file_name, file_data) + response = client.set_node_service_file(session.id, node.objid, service_name, file_name, file_data) # then assert response.result is True @@ -704,7 +704,7 @@ class TestGrpc: # then with client.context_connect(): - response = client.service_action(session.session_id, node.objid, service_name, core_pb2.SERVICE_STOP) + response = client.service_action(session.id, node.objid, service_name, core_pb2.SERVICE_STOP) # then assert response.result is True @@ -722,7 +722,7 @@ class TestGrpc: # then with client.context_connect(): - client.node_events(session.session_id, handle_event) + client.node_events(session.id, handle_event) time.sleep(0.1) session.broadcast_node(node_data) @@ -745,7 +745,7 @@ class TestGrpc: # then with client.context_connect(): - client.link_events(session.session_id, handle_event) + client.link_events(session.id, handle_event) time.sleep(0.1) session.broadcast_link(link_data) @@ -763,7 +763,7 @@ class TestGrpc: # then with client.context_connect(): - client.session_events(session.session_id, handle_event) + client.session_events(session.id, handle_event) time.sleep(0.1) event = EventData(event_type=EventTypes.RUNTIME_STATE.value, time="%s" % time.time()) session.broadcast_event(event) @@ -782,7 +782,7 @@ class TestGrpc: # then with client.context_connect(): - client.config_events(session.session_id, handle_event) + client.config_events(session.id, handle_event) time.sleep(0.1) session_config = session.options.get_configs() config_data = ConfigShim.config_data(0, None, ConfigFlags.UPDATE.value, session.options, session_config) @@ -802,7 +802,7 @@ class TestGrpc: # then with client.context_connect(): - client.exception_events(session.session_id, handle_event) + client.exception_events(session.id, handle_event) time.sleep(0.1) session.exception(ExceptionLevels.FATAL, "test", None, "exception message") @@ -821,7 +821,7 @@ class TestGrpc: # then with client.context_connect(): - client.file_events(session.session_id, handle_event) + client.file_events(session.id, handle_event) time.sleep(0.1) file_data = session.services.get_service_file(node, "IPForward", "ipforward.sh") session.broadcast_file(file_data) diff --git a/ns3/corens3/obj.py b/ns3/corens3/obj.py index acb56e1c..10e7e614 100644 --- a/ns3/corens3/obj.py +++ b/ns3/corens3/obj.py @@ -360,11 +360,11 @@ class Ns3Session(Session): A Session that starts an ns-3 simulation thread. """ - def __init__(self, session_id, persistent=False, duration=600): + def __init__(self, _id, persistent=False, duration=600): self.duration = duration self.nodes = ns.network.NodeContainer() self.mobhelper = ns.mobility.MobilityHelper() - Session.__init__(self, session_id) + Session.__init__(self, _id) def run(self, vis=False): """ From 70abb8cc1426ffceb53a03e84edc26f56f9ed4c0 Mon Sep 17 00:00:00 2001 From: bharnden <32446120+bharnden@users.noreply.github.com> Date: Mon, 8 Apr 2019 14:37:08 -0700 Subject: [PATCH 70/72] Update README.md removing NRL mailing lists, due to closure --- README.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/README.md b/README.md index 9db7e9ab..c5f7e182 100644 --- a/README.md +++ b/README.md @@ -32,12 +32,6 @@ GitHub integration. This allows for more dynamic conversations and the capability to respond faster. Feel free to join us at the link below. -You can also get help with questions, comments, or trouble, by using -the CORE mailing lists: - -* [core-users](https://pf.itd.nrl.navy.mil/mailman/listinfo/core-users) for general comments and questions -* [core-dev](https://pf.itd.nrl.navy.mil/mailman/listinfo/core-dev) for bugs, compile errors, and other development issues - ## Building CORE See [CORE Installation](http://coreemu.github.io/core/install.html) for detailed build instructions. From 2b82c2022ce825157a356c3e5bb57b618333ac57 Mon Sep 17 00:00:00 2001 From: apwiggins Date: Tue, 9 Apr 2019 17:53:32 -0300 Subject: [PATCH 71/72] Add FRR STATE DIR constant to FRR daemon --- daemon/core/constants.py.in | 1 + 1 file changed, 1 insertion(+) diff --git a/daemon/core/constants.py.in b/daemon/core/constants.py.in index fc901893..f4083518 100644 --- a/daemon/core/constants.py.in +++ b/daemon/core/constants.py.in @@ -5,6 +5,7 @@ CORE_STATE_DIR = "@CORE_STATE_DIR@" CORE_CONF_DIR = "@CORE_CONF_DIR@" CORE_DATA_DIR = "@CORE_DATA_DIR@" QUAGGA_STATE_DIR = "@CORE_STATE_DIR@/run/quagga" +FRR_STATE_DIR = "@CORE_STATE_DIR@/run/frr" def which(command): From a6874daba8abf714438eae052aa0ee2b727578f1 Mon Sep 17 00:00:00 2001 From: apwiggins Date: Tue, 9 Apr 2019 17:55:10 -0300 Subject: [PATCH 72/72] Add FRR service file --- daemon/core/services/frr.py | 639 ++++++++++++++++++++++++++++++++++++ 1 file changed, 639 insertions(+) create mode 100644 daemon/core/services/frr.py diff --git a/daemon/core/services/frr.py b/daemon/core/services/frr.py new file mode 100644 index 00000000..47e1b8d8 --- /dev/null +++ b/daemon/core/services/frr.py @@ -0,0 +1,639 @@ +""" +frr.py: defines routing services provided by FRRouting. +Assumes installation of FRR via https://deb.frrouting.org/ +""" + +from core import constants +from core.enumerations import LinkTypes, NodeTypes +from core.misc import ipaddress +from core.misc import nodeutils +from core.service import CoreService + + +class FRRZebra(CoreService): + name = "FRRzebra" + group = "FRR" + dirs = ( + "/usr/local/etc/frr", + "/var/run/frr", + "/var/log/frr", + ) + configs = ( + "/usr/local/etc/frr/frr.conf", + "frrboot.sh", + "/usr/local/etc/frr/vtysh.conf", + "/usr/local/etc/frr/daemons", + ) + startup = ("sh frrboot.sh zebra",) + shutdown = ("killall zebra",) + validate = ("pidof zebra",) + + @classmethod + def generate_config(cls, node, filename): + """ + Return the frr.conf or frrboot.sh file contents. + """ + if filename == cls.configs[0]: + return cls.generateFrrConf(node) + elif filename == cls.configs[1]: + return cls.generateFrrBoot(node) + elif filename == cls.configs[2]: + return cls.generateVtyshConf(node) + elif filename == cls.configs[3]: + return cls.generateFrrDaemons(node) + else: + raise ValueError("file name (%s) is not a known configuration: %s", filename, cls.configs) + + @classmethod + def generateVtyshConf(cls, node): + """ + Returns configuration file text. + """ + return "service integrated-vtysh-config\n" + + @classmethod + def generateFrrConf(cls, node): + """ + Returns configuration file text. Other services that depend on zebra + will have generatefrrifcconfig() and generatefrrconfig() + hooks that are invoked here. + """ + # we could verify here that filename == frr.conf + cfg = "" + for ifc in node.netifs(): + cfg += "interface %s\n" % ifc.name + # include control interfaces in addressing but not routing daemons + if hasattr(ifc, 'control') and ifc.control is True: + cfg += " " + cfg += "\n ".join(map(cls.addrstr, ifc.addrlist)) + cfg += "\n" + continue + cfgv4 = "" + cfgv6 = "" + want_ipv4 = False + want_ipv6 = False + for s in node.services: + if cls.name not in s.dependencies: + continue + ifccfg = s.generatefrrifcconfig(node, ifc) + if s.ipv4_routing: + want_ipv4 = True + if s.ipv6_routing: + want_ipv6 = True + cfgv6 += ifccfg + else: + cfgv4 += ifccfg + + if want_ipv4: + ipv4list = filter(lambda x: ipaddress.is_ipv4_address(x.split('/')[0]), ifc.addrlist) + cfg += " " + cfg += "\n ".join(map(cls.addrstr, ipv4list)) + cfg += "\n" + cfg += cfgv4 + if want_ipv6: + ipv6list = filter(lambda x: ipaddress.is_ipv6_address(x.split('/')[0]), ifc.addrlist) + cfg += " " + cfg += "\n ".join(map(cls.addrstr, ipv6list)) + cfg += "\n" + cfg += cfgv6 + cfg += "!\n" + + for s in node.services: + if cls.name not in s.dependencies: + continue + cfg += s.generatefrrconfig(node) + return cfg + + @staticmethod + def addrstr(x): + """ + helper for mapping IP addresses to zebra config statements + """ + if x.find(".") >= 0: + return "ip address %s" % x + elif x.find(":") >= 0: + return "ipv6 address %s" % x + else: + raise ValueError("invalid address: %s", x) + + @classmethod + def generateFrrBoot(cls, node): + """ + Generate a shell script used to boot the FRR daemons. + """ + frr_bin_search = node.session.options.get_config("frr_bin_search", + default='"/usr/local/bin /usr/bin /usr/lib/frr"') + frr_sbin_search = node.session.options.get_config('frr_sbin_search', + default='"/usr/local/sbin /usr/sbin /usr/lib/frr"') + return """\ +#!/bin/sh +# auto-generated by zebra service (frr.py) +FRR_CONF=%s +FRR_SBIN_SEARCH=%s +FRR_BIN_SEARCH=%s +FRR_STATE_DIR=%s + +searchforprog() +{ + prog=$1 + searchpath=$@ + ret= + for p in $searchpath; do + if [ -x $p/$prog ]; then + ret=$p + break + fi + done + echo $ret +} + +confcheck() +{ + CONF_DIR=`dirname $FRR_CONF` + # if /etc/frr exists, point /etc/frr/frr.conf -> CONF_DIR + if [ "$CONF_DIR" != "/etc/frr" ] && [ -d /etc/frr ] && [ ! -e /etc/frr/frr.conf ]; then + ln -s $CONF_DIR/frr.conf /etc/frr/frr.conf + fi + # if /etc/frr exists, point /etc/frr/vtysh.conf -> CONF_DIR + if [ "$CONF_DIR" != "/etc/frr" ] && [ -d /etc/frr ] && [ ! -e /etc/frr/vtysh.conf ]; then + ln -s $CONF_DIR/vtysh.conf /etc/frr/vtysh.conf + fi +} + +bootdaemon() +{ + FRR_SBIN_DIR=$(searchforprog $1 $FRR_SBIN_SEARCH) + if [ "z$FRR_SBIN_DIR" = "z" ]; then + echo "ERROR: FRR's '$1' daemon not found in search path:" + echo " $FRR_SBIN_SEARCH" + return 1 + fi + + flags="" + + if [ "$1" = "xpimd" ] && \\ + grep -E -q '^[[:space:]]*router[[:space:]]+pim6[[:space:]]*$' $FRR_CONF; then + flags="$flags -6" + fi + + + #force FRR to use CORE generated conf file + flags="$flags -d -f $FRR_CONF" + $FRR_SBIN_DIR/$1 $flags + + if [ "$?" != "0" ]; then + echo "ERROR: FRR's '$1' daemon failed to start!:" + return 1 + fi +} + +bootfrr() +{ + FRR_BIN_DIR=$(searchforprog 'vtysh' $FRR_BIN_SEARCH) + if [ "z$FRR_BIN_DIR" = "z" ]; then + echo "ERROR: FRR's 'vtysh' program not found in search path:" + echo " $FRR_BIN_SEARCH" + return 1 + fi + + # fix /var/run/frr permissions + id -u frr 2>/dev/null >/dev/null + if [ "$?" = "0" ]; then + chown frr $FRR_STATE_DIR + fi + + bootdaemon "zebra" + for r in rip ripng ospf6 ospf bgp babel; do + if grep -q "^router \<${r}\>" $FRR_CONF; then + bootdaemon "${r}d" + fi + done + + if grep -E -q '^[[:space:]]*router[[:space:]]+pim6?[[:space:]]*$' $FRR_CONF; then + bootdaemon "xpimd" + fi + + $FRR_BIN_DIR/vtysh -b +} + +if [ "$1" != "zebra" ]; then + echo "WARNING: '$1': all FRR daemons are launched by the 'zebra' service!" + exit 1 +fi +confcheck +bootfrr +""" % (cls.configs[0], frr_sbin_search, frr_bin_search, constants.FRR_STATE_DIR) + + @classmethod + def generateFrrDaemons(cls, node): + """ + Returns configuration file text. + """ + return """\ +# +# When activation a daemon at the first time, a config file, even if it is +# empty, has to be present *and* be owned by the user and group "frr", else +# the daemon will not be started by /etc/init.d/frr. The permissions should +# be u=rw,g=r,o=. +# When using "vtysh" such a config file is also needed. It should be owned by +# group "frrvty" and set to ug=rw,o= though. Check /etc/pam.d/frr, too. +# +# The watchfrr and zebra daemons are always started. +# +bgpd=yes +ospfd=yes +ospf6d=yes +ripd=yes +ripngd=yes +isisd=yes +pimd=yes +ldpd=yes +nhrpd=yes +eigrpd=yes +babeld=yes +sharpd=yes +pbrd=yes +bfdd=yes +fabricd=yes + +# +# If this option is set the /etc/init.d/frr script automatically loads +# the config via "vtysh -b" when the servers are started. +# Check /etc/pam.d/frr if you intend to use "vtysh"! +# +vtysh_enable=yes +zebra_options=" -A 127.0.0.1 -s 90000000" +bgpd_options=" -A 127.0.0.1" +ospfd_options=" -A 127.0.0.1" +ospf6d_options=" -A ::1" +ripd_options=" -A 127.0.0.1" +ripngd_options=" -A ::1" +isisd_options=" -A 127.0.0.1" +pimd_options=" -A 127.0.0.1" +ldpd_options=" -A 127.0.0.1" +nhrpd_options=" -A 127.0.0.1" +eigrpd_options=" -A 127.0.0.1" +babeld_options=" -A 127.0.0.1" +sharpd_options=" -A 127.0.0.1" +pbrd_options=" -A 127.0.0.1" +staticd_options="-A 127.0.0.1" +bfdd_options=" -A 127.0.0.1" +fabricd_options="-A 127.0.0.1" + +# The list of daemons to watch is automatically generated by the init script. +#watchfrr_options="" + +# for debugging purposes, you can specify a "wrap" command to start instead +# of starting the daemon directly, e.g. to use valgrind on ospfd: +# ospfd_wrap="/usr/bin/valgrind" +# or you can use "all_wrap" for all daemons, e.g. to use perf record: +# all_wrap="/usr/bin/perf record --call-graph -" +# the normal daemon command is added to this at the end. +""" + + + +class FrrService(CoreService): + """ + Parent class for FRR services. Defines properties and methods + common to FRR's routing daemons. + """ + name = None + group = "FRR" + dependencies = ("FRRzebra",) + dirs = () + configs = () + startup = () + shutdown = () + meta = "The config file for this service can be found in the Zebra service." + + ipv4_routing = False + ipv6_routing = False + + @staticmethod + def routerid(node): + """ + Helper to return the first IPv4 address of a node as its router ID. + """ + for ifc in node.netifs(): + if hasattr(ifc, 'control') and ifc.control is True: + continue + for a in ifc.addrlist: + if a.find(".") >= 0: + return a.split('/')[0] + # raise ValueError, "no IPv4 address found for router ID" + return "0.0.0.0" + + @staticmethod + def rj45check(ifc): + """ + Helper to detect whether interface is connected an external RJ45 + link. + """ + if ifc.net: + for peerifc in ifc.net.netifs(): + if peerifc == ifc: + continue + if nodeutils.is_node(peerifc, NodeTypes.RJ45): + return True + return False + + @classmethod + def generate_config(cls, node, filename): + return "" + + @classmethod + def generatefrrifcconfig(cls, node, ifc): + return "" + + @classmethod + def generatefrrconfig(cls, node): + return "" + + +class FRROspfv2(FrrService): + """ + The OSPFv2 service provides IPv4 routing for wired networks. It does + not build its own configuration file but has hooks for adding to the + unified frr.conf file. + """ + name = "FRROSPFv2" + startup = () + shutdown = ("killall ospfd",) + validate = ("pidof ospfd",) + ipv4_routing = True + + @staticmethod + def mtucheck(ifc): + """ + Helper to detect MTU mismatch and add the appropriate OSPF + mtu-ignore command. This is needed when e.g. a node is linked via a + GreTap device. + """ + if ifc.mtu != 1500: + # a workaround for PhysicalNode GreTap, which has no knowledge of + # the other nodes/nets + return " ip ospf mtu-ignore\n" + if not ifc.net: + return "" + for i in ifc.net.netifs(): + if i.mtu != ifc.mtu: + return " ip ospf mtu-ignore\n" + return "" + + @staticmethod + def ptpcheck(ifc): + """ + Helper to detect whether interface is connected to a notional + point-to-point link. + """ + if nodeutils.is_node(ifc.net, NodeTypes.PEER_TO_PEER): + return " ip ospf network point-to-point\n" + return "" + + @classmethod + def generatefrrconfig(cls, node): + cfg = "router ospf\n" + rtrid = cls.routerid(node) + cfg += " router-id %s\n" % rtrid + # network 10.0.0.0/24 area 0 + for ifc in node.netifs(): + if hasattr(ifc, 'control') and ifc.control is True: + continue + for a in ifc.addrlist: + if a.find(".") < 0: + continue + net = ipaddress.Ipv4Prefix(a) + cfg += " network %s area 0\n" % net + cfg += "!\n" + return cfg + + @classmethod + def generatefrrifcconfig(cls, node, ifc): + return cls.mtucheck(ifc) + # cfg = cls.mtucheck(ifc) + # external RJ45 connections will use default OSPF timers + # if cls.rj45check(ifc): + # return cfg + # cfg += cls.ptpcheck(ifc) + + # return cfg + """\ + + +# ip ospf hello-interval 2 +# ip ospf dead-interval 6 +# ip ospf retransmit-interval 5 +# """ + + +class FRROspfv3(FrrService): + """ + The OSPFv3 service provides IPv6 routing for wired networks. It does + not build its own configuration file but has hooks for adding to the + unified frr.conf file. + """ + name = "FRROSPFv3" + startup = () + shutdown = ("killall ospf6d",) + validate = ("pidof ospf6d",) + ipv4_routing = True + ipv6_routing = True + + @staticmethod + def minmtu(ifc): + """ + Helper to discover the minimum MTU of interfaces linked with the + given interface. + """ + mtu = ifc.mtu + if not ifc.net: + return mtu + for i in ifc.net.netifs(): + if i.mtu < mtu: + mtu = i.mtu + return mtu + + @classmethod + def mtucheck(cls, ifc): + """ + Helper to detect MTU mismatch and add the appropriate OSPFv3 + ifmtu command. This is needed when e.g. a node is linked via a + GreTap device. + """ + minmtu = cls.minmtu(ifc) + if minmtu < ifc.mtu: + return " ipv6 ospf6 ifmtu %d\n" % minmtu + else: + return "" + + @staticmethod + def ptpcheck(ifc): + """ + Helper to detect whether interface is connected to a notional + point-to-point link. + """ + if nodeutils.is_node(ifc.net, NodeTypes.PEER_TO_PEER): + return " ipv6 ospf6 network point-to-point\n" + return "" + + @classmethod + def generatefrrconfig(cls, node): + cfg = "router ospf6\n" + rtrid = cls.routerid(node) + cfg += " router-id %s\n" % rtrid + for ifc in node.netifs(): + if hasattr(ifc, 'control') and ifc.control is True: + continue + cfg += " interface %s area 0.0.0.0\n" % ifc.name + cfg += "!\n" + return cfg + + @classmethod + def generatefrrifcconfig(cls, node, ifc): + return cls.mtucheck(ifc) + # cfg = cls.mtucheck(ifc) + # external RJ45 connections will use default OSPF timers + # if cls.rj45check(ifc): + # return cfg + # cfg += cls.ptpcheck(ifc) + + # return cfg + """\ + + +# ipv6 ospf6 hello-interval 2 +# ipv6 ospf6 dead-interval 6 +# ipv6 ospf6 retransmit-interval 5 +# """ + + +class FRRBgp(FrrService): + """ + The BGP service provides interdomain routing. + Peers must be manually configured, with a full mesh for those + having the same AS number. + """ + name = "FRRBGP" + startup = () + shutdown = ("killall bgpd",) + validate = ("pidof bgpd",) + custom_needed = True + ipv4_routing = True + ipv6_routing = True + + @classmethod + def generatefrrconfig(cls, node): + cfg = "!\n! BGP configuration\n!\n" + cfg += "! You should configure the AS number below,\n" + cfg += "! along with this router's peers.\n!\n" + cfg += "router bgp %s\n" % node.objid + rtrid = cls.routerid(node) + cfg += " bgp router-id %s\n" % rtrid + cfg += " redistribute connected\n" + cfg += "! neighbor 1.2.3.4 remote-as 555\n!\n" + return cfg + + +class FRRRip(FrrService): + """ + The RIP service provides IPv4 routing for wired networks. + """ + name = "FRRRIP" + startup = () + shutdown = ("killall ripd",) + validate = ("pidof ripd",) + ipv4_routing = True + + @classmethod + def generatefrrconfig(cls, node): + cfg = """\ +router rip + redistribute static + redistribute connected + redistribute ospf + network 0.0.0.0/0 +! +""" + return cfg + + +class FRRRipng(FrrService): + """ + The RIP NG service provides IPv6 routing for wired networks. + """ + name = "FRRRIPNG" + startup = () + shutdown = ("killall ripngd",) + validate = ("pidof ripngd",) + ipv6_routing = True + + @classmethod + def generatefrrconfig(cls, node): + cfg = """\ +router ripng + redistribute static + redistribute connected + redistribute ospf6 + network ::/0 +! +""" + return cfg + + +class FRRBabel(FrrService): + """ + The Babel service provides a loop-avoiding distance-vector routing + protocol for IPv6 and IPv4 with fast convergence properties. + """ + name = "FRRBabel" + startup = () + shutdown = ("killall babeld",) + validate = ("pidof babeld",) + ipv6_routing = True + + @classmethod + def generatefrrconfig(cls, node): + cfg = "router babel\n" + for ifc in node.netifs(): + if hasattr(ifc, "control") and ifc.control is True: + continue + cfg += " network %s\n" % ifc.name + cfg += " redistribute static\n redistribute connected\n" + return cfg + + @classmethod + def generatefrrifcconfig(cls, node, ifc): + if ifc.net and ifc.net.linktype == LinkTypes.WIRELESS.value: + return " babel wireless\n no babel split-horizon\n" + else: + return " babel wired\n babel split-horizon\n" + + +class FRRXpimd(FrrService): + """ + PIM multicast routing based on XORP. + """ + name = 'FRRXpimd' + startup = () + shutdown = ('killall xpimd',) + validate = ('pidof xpimd',) + ipv4_routing = True + + @classmethod + def generatefrrconfig(cls, node): + ifname = 'eth0' + for ifc in node.netifs(): + if ifc.name != 'lo': + ifname = ifc.name + break + cfg = 'router mfea\n!\n' + cfg += 'router igmp\n!\n' + cfg += 'router pim\n' + cfg += ' !ip pim rp-address 10.0.0.1\n' + cfg += ' ip pim bsr-candidate %s\n' % ifname + cfg += ' ip pim rp-candidate %s\n' % ifname + cfg += ' !ip pim spt-threshold interval 10 bytes 80000\n' + return cfg + + @classmethod + def generatefrrifcconfig(cls, node, ifc): + return ' ip mfea\n ip igmp\n ip pim\n'