grpc updated client methods to use proto objects directly, added more docs

This commit is contained in:
bharnden 2019-03-29 22:12:34 -07:00
parent ec1b82238e
commit 5c47b0cc43
4 changed files with 203 additions and 88 deletions

View file

@ -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)))

View file

@ -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;

View file

@ -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

View file

@ -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