Skip to content

Commit

Permalink
Merge pull request FRRouting#16012 from LabNConsulting/chopps/improve…
Browse files Browse the repository at this point in the history
…-grpc-test

tests: improve the grpc query client and topotest
  • Loading branch information
donaldsharp authored May 17, 2024
2 parents e3fceef + 9dc02dd commit e41b4a7
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 32 deletions.
94 changes: 77 additions & 17 deletions tests/topotests/grpc_basic/test_basic_grpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,18 @@
test_basic_grpc.py: Test Basic gRPC.
"""

import json
import logging
import os
import re
import sys

import pytest

from lib.common_config import step
from lib.micronet import commander
from lib.topogen import Topogen, TopoRouter
from lib.topolog import logger
from lib.topotest import json_cmp

CWD = os.path.dirname(os.path.realpath(__file__))

Expand All @@ -28,6 +30,7 @@
GRPCP_ISISD = 50054
GRPCP_OSPFD = 50055
GRPCP_PIMD = 50056
GRPCP_MGMTD = 50057

pytestmark = [
pytest.mark.mgmtd,
Expand Down Expand Up @@ -59,12 +62,15 @@ def tgen(request):

for rname, router in router_list.items():
router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf", f"-M grpc:{GRPCP_ZEBRA}")
router.load_config(TopoRouter.RD_STATIC, None, f"-M grpc:{GRPCP_STATICD}")
# router.load_config(TopoRouter.RD_BFD, None, f"-M grpc:{GRPCP_BFDD}")
router.load_config(TopoRouter.RD_STATIC, "", f"-M grpc:{GRPCP_STATICD}")
# router.load_config(TopoRouter.RD_BFDD, "", f"-M grpc:{GRPCP_BFDD}")
# router.load_config(TopoRouter.RD_ISIS, None, f"-M grpc:{GRPCP_ISISD}")
# router.load_config(TopoRouter.RD_OSPF, None, f"-M grpc:{GRPCP_OSPFD}")
# router.load_config(TopoRouter.RD_PIM, None, f"-M grpc:{GRPCP_PIMD}")

# This doesn't work yet...
# router.load_config(TopoRouter.RD_MGMTD, "", f"-M grpc:{GRPCP_MGMTD}")

tgen.start_router()
yield tgen

Expand Down Expand Up @@ -94,40 +100,94 @@ def run_grpc_client(r, port, commands):


def test_connectivity(tgen):
r1 = tgen.gears["r1"]
output = r1.cmd_raises("ping -c1 192.168.1.2")
logging.info("ping output: %s", output)
tgen.gears["r1"].cmd_raises("ping -c1 192.168.1.2")


def test_capabilities(tgen):
r1 = tgen.gears["r1"]
output = run_grpc_client(r1, GRPCP_ZEBRA, "GETCAP")
logging.info("grpc output: %s", output)
output = run_grpc_client(r1, GRPCP_STATICD, "GETCAP")
logging.debug("grpc output: %s", output)

modules = sorted(re.findall('name: "([^"]+)"', output))
expected = ["frr-interface", "frr-routing", "frr-staticd", "frr-vrf"]
assert modules == expected

encodings = sorted(re.findall("supported_encodings: (.*)", output))
expected = ["JSON", "XML"]
assert encodings == expected


def test_get_config(tgen):
nrepeat = 5
r1 = tgen.gears["r1"]

step("'GET' interface config 10 times, once per invocation")
step("'GET' interface config and state 10 times, once per invocation")

for i in range(0, nrepeat):
output = run_grpc_client(r1, GRPCP_ZEBRA, "GET,/frr-interface:lib")
logging.info("[iteration %s]: grpc GET output: %s", i, output)
output = run_grpc_client(r1, GRPCP_ZEBRA, "GET-CONFIG,/frr-interface:lib")
logging.debug("[iteration %s]: grpc GET output: %s", i, output)

step(f"'GET' YANG {nrepeat} times in one invocation")
commands = ["GET,/frr-interface:lib" for _ in range(0, 10)]
commands = ["GET-CONFIG,/frr-interface:lib" for _ in range(0, 10)]
output = run_grpc_client(r1, GRPCP_ZEBRA, commands)
logging.info("grpc GET*{%d} output: %s", nrepeat, output)
logging.debug("grpc GET*{%d} output: %s", nrepeat, output)

output = run_grpc_client(r1, GRPCP_ZEBRA, commands[0])
out_json = json.loads(output)
expect = json.loads(
"""{
"frr-interface:lib": {
"interface": [
{
"name": "r1-eth0",
"frr-zebra:zebra": {
"ipv4-addrs": [
{
"ip": "192.168.1.1",
"prefix-length": 24
}
],
"evpn-mh": {},
"ipv6-router-advertisements": {}
}
}
]
},
"frr-zebra:zebra": {
"import-kernel-table": {}
}
} """
)
result = json_cmp(out_json, expect, exact=True)
assert result is None


def test_get_vrf_config(tgen):
r1 = tgen.gears["r1"]

step("'GET' get VRF config")

output = run_grpc_client(r1, GRPCP_ZEBRA, "GET,/frr-vrf:lib")
logging.info("grpc GET /frr-vrf:lib output: %s", output)
step("'GET' VRF config and state")

output = run_grpc_client(r1, GRPCP_STATICD, "GET,/frr-vrf:lib")
logging.debug("grpc GET /frr-vrf:lib output: %s", output)
out_json = json.loads(output)
expect = json.loads(
"""{
"frr-vrf:lib": {
"vrf": [
{
"name": "default",
"state": {
"id": 0,
"active": true
}
}
]
}
}
"""
)
result = json_cmp(out_json, expect, exact=True)
assert result is None


def test_shutdown_checks(tgen):
Expand Down
44 changes: 29 additions & 15 deletions tests/topotests/lib/grpc-query.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,16 @@
# This is painful but works if you have installed grpc and grpc_tools would be *way*
# better if we actually built and installed these but ... python packaging.
try:
import grpc
import grpc_tools

import grpc

sys.path.append(os.path.dirname(CWD))
from munet.base import commander

commander.cmd_raises(f"cp {CWD}/../../../grpc/frr-northbound.proto .")
commander.cmd_raises(
f"python3 -m grpc_tools.protoc --python_out=. --grpc_python_out=. -I . frr-northbound.proto"
"python3 -m grpc_tools.protoc --python_out=. --grpc_python_out=. -I . frr-northbound.proto"
)
except Exception as error:
logging.error("can't create proto definition modules %s", error)
Expand Down Expand Up @@ -57,16 +58,16 @@ def get_capabilities(self):
logging.debug("GRPC Capabilities: %s", response)
return response

def get(self, xpath):
def get(self, xpath, encoding, gtype):
request = frr_northbound_pb2.GetRequest()
request.path.append(xpath)
request.type = frr_northbound_pb2.GetRequest.ALL
request.encoding = frr_northbound_pb2.XML
xml = ""
request.type = gtype
request.encoding = encoding
result = ""
for r in self.stub.Get(request):
logging.info('GRPC Get path: "%s" value: %s', request.path, r)
xml += str(r.data.data)
return xml
logging.debug('GRPC Get path: "%s" value: %s', request.path, r)
result += str(r.data.data)
return result


def next_action(action_list=None):
Expand Down Expand Up @@ -95,6 +96,7 @@ def main(*args):
)
parser.add_argument("-v", "--verbose", action="store_true", help="be verbose")
parser.add_argument("--check", action="store_true", help="check runable")
parser.add_argument("--xml", action="store_true", help="encode XML instead of JSON")
parser.add_argument("actions", nargs="*", help="GETCAP|GET,xpath")
args = parser.parse_args(*args)

Expand All @@ -107,20 +109,32 @@ def main(*args):
if args.check:
sys.exit(0)

encoding = frr_northbound_pb2.XML if args.xml else frr_northbound_pb2.JSON

c = GRPCClient(args.server, args.port)

for action in next_action(args.actions):
action = action.casefold()
logging.info("GOT ACTION: %s", action)
logging.debug("GOT ACTION: %s", action)
if action == "getcap":
caps = c.get_capabilities()
print("Capabilities:", caps)
print(caps)
elif action.startswith("get,"):
# Print Interface State and Config
# Get and print config and state
_, xpath = action.split(",", 1)
logging.debug("Get XPath: %s", xpath)
print(c.get(xpath, encoding, gtype=frr_northbound_pb2.GetRequest.ALL))
elif action.startswith("get-config,"):
# Get and print config
_, xpath = action.split(",", 1)
logging.debug("Get Config XPath: %s", xpath)
print(c.get(xpath, encoding, gtype=frr_northbound_pb2.GetRequest.CONFIG))
# for _ in range(0, 1):
elif action.startswith("get-state,"):
# Get and print state
_, xpath = action.split(",", 1)
print("Get XPath: ", xpath)
xml = c.get(xpath)
print("{}: {}".format(xpath, xml))
logging.debug("Get State XPath: %s", xpath)
print(c.get(xpath, encoding, gtype=frr_northbound_pb2.GetRequest.STATE))
# for _ in range(0, 1):


Expand Down

0 comments on commit e41b4a7

Please sign in to comment.