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__))
GRPCP_ISISD = 50054
GRPCP_OSPFD = 50055
GRPCP_PIMD = 50056
+GRPCP_MGMTD = 50057
pytestmark = [
pytest.mark.mgmtd,
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
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):
# 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)
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):
)
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)
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):