Add the get_routes function as an attribute of the SlaveMethods, and
expose the functionality to the recipe via the Controller.Task.HostAPI.
The function returns two lists: one for directly connected routes and one
for next-hop routes. Each route is represented via a dictionary of the
form:
{
"prefix" : route prefix,
"flags" : extra route flags,
"nexthops" : a list of nexthops - only for nexthop routes,
"dev" : The output device - only for directly connected routes
}
Signed-off-by: Yotam Gigi <yotamg(a)mellanox.com>
---
lnst/Controller/Machine.py | 4 ++++
lnst/Controller/Task.py | 3 +++
lnst/Slave/NetTestSlave.py | 58 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 65 insertions(+)
diff --git a/lnst/Controller/Machine.py b/lnst/Controller/Machine.py
index 74bd64e..fe196ad 100644
--- a/lnst/Controller/Machine.py
+++ b/lnst/Controller/Machine.py
@@ -568,6 +568,10 @@ class Machine(object):
def del_netns(self, netns):
return self._rpc_call("del_namespace", netns)
+ def get_routes(self, routes_filter, ns):
+ routes = self._rpc_call_x(ns, "get_routes", routes_filter)
+ return routes
+
def del_namespaces(self):
for netns in self._namespaces:
self.del_netns(netns)
diff --git a/lnst/Controller/Task.py b/lnst/Controller/Task.py
index af1db96..a5fb0d3 100644
--- a/lnst/Controller/Task.py
+++ b/lnst/Controller/Task.py
@@ -318,6 +318,9 @@ class HostAPI(object):
else:
raise TaskError("No device with name '%s' found." %
str(name))
+ def get_routes(self, routes_filter = "", netns = None):
+ return self._m.get_routes(routes_filter, netns)
+
@deprecated
def get_devname(self, if_id):
"""
diff --git a/lnst/Slave/NetTestSlave.py b/lnst/Slave/NetTestSlave.py
index f11ca8b..ddbfe72 100644
--- a/lnst/Slave/NetTestSlave.py
+++ b/lnst/Slave/NetTestSlave.py
@@ -19,6 +19,7 @@ import datetime
import socket
import ctypes
import multiprocessing
+import re
from time import sleep, time
from xmlrpclib import Binary
from tempfile import NamedTemporaryFile
@@ -756,6 +757,63 @@ class SlaveMethods:
del self._net_namespaces[netns]
return True
+ def get_routes(self, route_filter):
+ try:
+ route_output, _ = exec_cmd("ip route show %s" % route_filter)
+ except:
+ return {}
+
+ dc_routes = []
+ nh_routes = []
+ ip_re = "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}"
+ prefix_re = "^(" + ip_re + "(?:/\d{1,3})?)"
+
+ # parse directly connected routes
+ dc_route_re = prefix_re + " dev (\w+) (.*)"
+ dc_matchs = re.findall(dc_route_re, route_output, re.M)
+
+ for dc_match in dc_matchs:
+ dc_route = { "prefix" : dc_match[0],
+ "dev" : dc_match[1],
+ "flags" : dc_match[2] }
+ dc_routes.append(dc_route)
+
+ # parse nexthop routes
+ nh_re = " via (" + ip_re + ").*dev (\w+) (.*)"
+ nh_route_re = prefix_re + nh_re
+ nh_route_matchs = re.findall(nh_route_re, route_output, re.M)
+
+ for nh_route_match in nh_route_matchs:
+ nexthop = { "ip" : nh_route_match[1],
+ "dev" : nh_route_match[2],
+ "flags" : ""}
+ nh_route = {"prefix" : nh_route_match[0],
+ "nexthops": [ nexthop ],
+ "flags" : nh_route_match[3] }
+ nh_routes.append(nh_route)
+
+ # parse ECMP routes
+ ecmp_route_re = prefix_re + "(.*)\n((?:.*nexthop .*\n?)+)"
+ ecmp_matchs = re.findall(ecmp_route_re, route_output, re.M)
+
+ for ecmp_match in ecmp_matchs:
+ # parse each nexthop
+ nexthops = []
+ nh_matchs = re.findall(nh_re, ecmp_match[2])
+
+ for nh_match in nh_matchs:
+ nexthop = { "ip" : nh_match[0],
+ "dev" : nh_match[1],
+ "flags" : nh_match[2]}
+ nexthops.append(nexthop)
+
+ ecmp_route = {"prefix" : ecmp_match[0],
+ "nexthops": nexthops,
+ "flags" : ecmp_match[1] }
+ nh_routes.append(ecmp_route)
+
+ return dc_routes, nh_routes
+
def set_if_netns(self, if_id, netns):
netns_pid = self._net_namespaces[netns]["pid"]
--
2.8.4