[cli] netdata show to support filtering by RLOC16 (#9969)

This commit updates the CLI `netdata show` command to allow an
optional RLOC16 as input. Providing an RLOC16 filters the output to
display prefix, route, and service entries associated specifically
with the specified RLOC16. This is helpful for checking entries added
by a specific border router.

`test-011-network-data-timeout.py` is updated to validate the new CLI
functionality.
This commit is contained in:
Abtin Keshavarzian
2024-03-27 15:25:02 -07:00
committed by GitHub
parent 34a0b0dd7e
commit 58b230fef0
5 changed files with 81 additions and 28 deletions
+16 -1
View File
@@ -334,10 +334,12 @@ Done
### show
Usage: `netdata show [local] [-x]`
Usage: `netdata show [local] [-x] [\<rloc16\>]`
Print entries in Network Data, on-mesh prefixes, external routes, services, and 6LoWPAN context information.
If the optional `rloc16` input is specified, prints the entries associated with the given RLOC16 only. The RLOC16 filtering can be used when `-x` or `local` are not used.
On-mesh prefixes are listed under `Prefixes` header:
- The on-mesh prefix
@@ -406,6 +408,19 @@ Commissioning:
Done
```
Print Network Data entries from the Leader associated with `0xa00` RLOC16.
```bash
> netdata show 0xa00
Prefixes:
fd00:dead:beef:cafe::/64 paros med a000
Routes:
fd00:1234:0:0::/64 s med a000
Services:
44970 5d fddead00beef00007bad0069ce45948504d2 s a000
Done
```
Print Network Data received from the Leader as hex-encoded TLVs.
```bash
+34 -10
View File
@@ -564,7 +564,7 @@ otError NetworkData::GetNextPrefix(otNetworkDataIterator *aIterator, otBorderRou
return error;
}
void NetworkData::OutputNetworkData(bool aLocal)
void NetworkData::OutputNetworkData(bool aLocal, uint16_t aRloc16)
{
otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
otBorderRouterConfig prefix;
@@ -577,7 +577,10 @@ void NetworkData::OutputNetworkData(bool aLocal)
while (GetNextPrefix(&iterator, &prefix, aLocal) == OT_ERROR_NONE)
{
OutputPrefix(prefix);
if ((aRloc16 == kAnyRloc16) || (aRloc16 == prefix.mRloc16))
{
OutputPrefix(prefix);
}
}
OutputLine("Routes:");
@@ -585,7 +588,10 @@ void NetworkData::OutputNetworkData(bool aLocal)
while (GetNextRoute(&iterator, &route, aLocal) == OT_ERROR_NONE)
{
OutputRoute(route);
if ((aRloc16 == kAnyRloc16) || (aRloc16 == route.mRloc16))
{
OutputRoute(route);
}
}
OutputLine("Services:");
@@ -593,10 +599,14 @@ void NetworkData::OutputNetworkData(bool aLocal)
while (GetNextService(&iterator, &service, aLocal) == OT_ERROR_NONE)
{
OutputService(service);
if ((aRloc16 == kAnyRloc16) || (aRloc16 == service.mServerConfig.mRloc16))
{
OutputService(service);
}
}
VerifyOrExit(!aLocal);
VerifyOrExit(aRloc16 == kAnyRloc16);
OutputLine("Contexts:");
iterator = OT_NETWORK_DATA_ITERATOR_INIT;
@@ -716,8 +726,16 @@ exit:
* 08040b02174703140040fd00deadbeefcafe0504dc00330007021140
* Done
* @endcode
* @cparam netdata show [@ca{-x}]
* @code
* netdata show 0xdc00
* Prefixes:
* fd00:dead:beef:cafe::/64 paros med dc00
* Routes:
* Services:
* Done
* @cparam netdata show [@ca{-x}|@ca{rloc16}]
* * The optional `-x` argument gets Network Data as hex-encoded TLVs.
* * The optional `rloc16` argument gets all prefix/route/service entries associated with a given RLOC16.
* @par
* `netdata show` from OT CLI gets full Network Data received from the Leader. This command uses several
* API functions to combine prefixes, routes, and services, including #otNetDataGetNextOnMeshPrefix,
@@ -774,9 +792,10 @@ exit:
*/
template <> otError NetworkData::Process<Cmd("show")>(Arg aArgs[])
{
otError error = OT_ERROR_INVALID_ARGS;
bool local = false;
bool binary = false;
otError error = OT_ERROR_INVALID_ARGS;
uint16_t rloc16 = kAnyRloc16;
bool local = false;
bool binary = false;
for (uint8_t i = 0; !aArgs[i].IsEmpty(); i++)
{
@@ -811,17 +830,22 @@ template <> otError NetworkData::Process<Cmd("show")>(Arg aArgs[])
}
else
{
ExitNow(error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = aArgs[i].ParseAsUint16(rloc16));
}
}
if (local || binary)
{
VerifyOrExit(rloc16 == kAnyRloc16, error = OT_ERROR_INVALID_ARGS);
}
if (binary)
{
error = OutputBinary(local);
}
else
{
OutputNetworkData(local);
OutputNetworkData(local, rloc16);
error = OT_ERROR_NONE;
}
+3 -1
View File
@@ -130,6 +130,8 @@ public:
private:
using Command = CommandEntry<NetworkData>;
static constexpr uint16_t kAnyRloc16 = 0xffff;
template <CommandId kCommandId> otError Process(Arg aArgs[]);
otError GetNextPrefix(otNetworkDataIterator *aIterator, otBorderRouterConfig *aConfig, bool aLocal);
@@ -137,7 +139,7 @@ private:
otError GetNextService(otNetworkDataIterator *aIterator, otServiceConfig *aConfig, bool aLocal);
otError OutputBinary(bool aLocal);
void OutputNetworkData(bool aLocal);
void OutputNetworkData(bool aLocal, uint16_t aRloc16);
#if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
static void HandleNetdataFull(void *aContext) { static_cast<NetworkData *>(aContext)->HandleNetdataFull(); }
+11 -7
View File
@@ -398,19 +398,23 @@ class Node(object):
#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# netdata
def get_netdata(self):
outputs = self.cli('netdata show')
def get_netdata(self, rloc16=None):
outputs = self.cli('netdata show', rloc16)
outputs = [line.strip() for line in outputs]
routes_index = outputs.index('Routes:')
services_index = outputs.index('Services:')
contexts_index = outputs.index('Contexts:')
commissioning_index = outputs.index('Commissioning:')
if rloc16 is None:
contexts_index = outputs.index('Contexts:')
commissioning_index = outputs.index('Commissioning:')
result = {}
result['prefixes'] = outputs[1:routes_index]
result['routes'] = outputs[routes_index + 1:services_index]
result['services'] = outputs[services_index + 1:contexts_index]
result['contexts'] = outputs[contexts_index + 1:commissioning_index]
result['commissioning'] = outputs[commissioning_index + 1:]
if rloc16 is None:
result['services'] = outputs[services_index + 1:contexts_index]
result['contexts'] = outputs[contexts_index + 1:commissioning_index]
result['commissioning'] = outputs[commissioning_index + 1:]
else:
result['services'] = outputs[services_index + 1:]
return result
@@ -89,11 +89,6 @@ nodes = [r1, r2, c2]
# -----------------------------------------------------------------------------------------------------------------------
# Test Implementation
common_prefix = 'fd00:cafe::'
prefix1 = 'fd00:1::'
prefix2 = 'fd00:2::'
prefix3 = 'fd00:3::'
# Each node adds its own prefix.
r1.add_prefix('fd00:1::/64', 'paros', 'med')
r2.add_prefix('fd00:2::/64', 'paros', 'med')
@@ -104,6 +99,9 @@ r1.add_prefix('fd00:abba::/64', 'paros', 'high')
r2.add_prefix('fd00:abba::/64', 'paros', 'med')
c2.add_prefix('fd00:abba::/64', 'paros', 'low')
r1.add_route('fd00:cafe::/64', 's', 'med')
r2.add_route('fd00:cafe::/64', 's', 'med')
r1.register_netdata()
r2.register_netdata()
c2.register_netdata()
@@ -112,12 +110,19 @@ c2.register_netdata()
def check_netdata_on_all_nodes():
for node in nodes:
netdata = node.get_netdata()
prefixes = netdata['prefixes']
verify(len(prefixes) == 6)
verify(len(netdata['prefixes']) == 6)
verify(len(netdata['routes']) == 2)
verify_within(check_netdata_on_all_nodes, 10)
# Check netdata filtering for r1 entries only.
r1_rloc = int(r1.get_rloc16(), 16)
netdata = r1.get_netdata(r1_rloc)
verify(len(netdata['prefixes']) == 2)
verify(len(netdata['routes']) == 1)
# Remove `r2`. This should trigger all the prefixes added by it or its
# child to timeout and be removed.
@@ -127,8 +132,11 @@ r2.interface_down()
def check_netdata_on_r1():
netdata = r1.get_netdata()
prefixes = netdata['prefixes']
verify(len(prefixes) == 2)
verify(len(netdata['prefixes']) == 2)
verify(len(netdata['routes']) == 1)
netdata = r1.get_netdata(r1_rloc)
verify(len(netdata['prefixes']) == 2)
verify(len(netdata['routes']) == 1)
verify_within(check_netdata_on_r1, 120)