[netdata] simplify tracking of lowpan context IDs (#8876)

This commit simplifies how the 6LoWPAN Context IDs are assigned and
tracked by `NetworkData::Leader` using a new nested class
`ContextIds`. A Context ID can be in one of the 3 states:
- It is unallocated.
- It is allocated and in-use.
- It is scheduled to be removed (after reuse delay interval).

This commit also adds a test-case `test-019-netdata-context-id.py`
which validates the assignment and removal of Context IDs under
different scenarios.
This commit is contained in:
Abtin Keshavarzian
2023-03-18 22:45:28 -07:00
committed by GitHub
parent af031f3b50
commit 8ad7b2bd64
4 changed files with 524 additions and 115 deletions
+97 -97
View File
@@ -61,6 +61,7 @@ RegisterLogModule("NetworkData");
Leader::Leader(Instance &aInstance)
: LeaderBase(aInstance)
, mWaitingForNetDataSync(false)
, mContextIds(aInstance)
, mTimer(aInstance)
{
Reset();
@@ -70,9 +71,7 @@ void Leader::Reset(void)
{
LeaderBase::Reset();
memset(reinterpret_cast<void *>(mContextLastUsed), 0, sizeof(mContextLastUsed));
mContextUsed = 0;
mContextIdReuseDelay = kContextIdReuseDelay;
mContextIds.Clear();
}
void Leader::Start(Mle::LeaderStartMode aStartMode)
@@ -852,10 +851,10 @@ Error Leader::AddBorderRouter(const BorderRouterTlv &aBorderRouter, PrefixTlv &a
if (dstContext == nullptr)
{
// Allocate a Context ID first. This ensure that if we cannot
// allocate, we fail and exit before potentially inserting a
// Border Router sub-TLV.
SuccessOrExit(error = AllocateContextId(contextId));
// Get a new Context ID first. This ensure that if we cannot
// get new Context ID, we fail and exit before potentially
// inserting a Border Router sub-TLV.
SuccessOrExit(error = mContextIds.GetUnallocatedId(contextId));
}
if (dstBorderRouter == nullptr)
@@ -894,7 +893,7 @@ Error Leader::AddBorderRouter(const BorderRouterTlv &aBorderRouter, PrefixTlv &a
}
dstContext->SetCompress();
StopContextReuseTimer(dstContext->GetContextId());
mContextIds.MarkAsInUse(dstContext->GetContextId());
VerifyOrExit(!ContainsMatchingEntry(dstBorderRouter, *entry));
@@ -974,58 +973,6 @@ const ServiceTlv *Leader::FindServiceById(uint8_t aServiceId) const
return service;
}
Error Leader::AllocateContextId(uint8_t &aContextId)
{
Error error = kErrorNotFound;
for (uint8_t contextId = kMinContextId; contextId < kMinContextId + kNumContextIds; contextId++)
{
if ((mContextUsed & (1 << contextId)) == 0)
{
mContextUsed |= (1 << contextId);
aContextId = contextId;
error = kErrorNone;
LogInfo("Allocated Context ID = %d", contextId);
break;
}
}
return error;
}
void Leader::FreeContextId(uint8_t aContextId)
{
LogInfo("Free Context Id = %d", aContextId);
RemoveContext(aContextId);
mContextUsed &= ~(1 << aContextId);
IncrementVersions(/* aIncludeStable */ true);
}
void Leader::StartContextReuseTimer(uint8_t aContextId)
{
// Start the reuse timer for `aContextId` if it is not already
// scheduled.
VerifyOrExit(mContextLastUsed[aContextId - kMinContextId].GetValue() == 0);
mContextLastUsed[aContextId - kMinContextId] = TimerMilli::GetNow();
if (mContextLastUsed[aContextId - kMinContextId].GetValue() == 0)
{
mContextLastUsed[aContextId - kMinContextId].SetValue(1);
}
if (!mTimer.IsRunning())
{
mTimer.Start(kStateUpdatePeriod);
}
exit:
return;
}
void Leader::StopContextReuseTimer(uint8_t aContextId) { mContextLastUsed[aContextId - kMinContextId].SetValue(0); }
void Leader::RemoveRloc(uint16_t aRloc16, MatchMode aMatchMode, ChangedFlags &aChangedFlags)
{
NetworkData excludeNetworkData(GetInstance()); // Empty network data.
@@ -1147,15 +1094,15 @@ void Leader::RemoveRlocInPrefix(PrefixTlv &aPrefix,
if ((context = aPrefix.FindSubTlv<ContextTlv>()) != nullptr)
{
if (aPrefix.GetSubTlvsLength() == sizeof(ContextTlv))
if (aPrefix.FindSubTlv<BorderRouterTlv>() == nullptr)
{
context->ClearCompress();
StartContextReuseTimer(context->GetContextId());
mContextIds.ScheduleToRemove(context->GetContextId());
}
else
{
context->SetCompress();
StopContextReuseTimer(context->GetContextId());
mContextIds.MarkAsInUse(context->GetContextId());
}
}
}
@@ -1263,6 +1210,8 @@ void Leader::RemoveContext(uint8_t aContextId)
start = prefix->GetNext();
}
IncrementVersions(/* aIncludeStable */ true);
}
void Leader::RemoveContext(PrefixTlv &aPrefix, uint8_t aContextId)
@@ -1303,55 +1252,26 @@ void Leader::HandleNetworkDataRestoredAfterReset(void)
continue;
}
mContextUsed |= 1 << context->GetContextId();
mContextIds.MarkAsInUse(context->GetContextId());
if (context->IsCompress())
if (!context->IsCompress())
{
StopContextReuseTimer(context->GetContextId());
}
else
{
StartContextReuseTimer(context->GetContextId());
mContextIds.ScheduleToRemove(context->GetContextId());
}
}
}
void Leader::HandleTimer(void)
{
bool contextsWaiting = false;
if (mWaitingForNetDataSync)
{
LogInfo("Timed out waiting for netdata on restoring leader role after reset");
IgnoreError(Get<Mle::MleRouter>().BecomeDetached());
ExitNow();
}
for (uint8_t i = 0; i < kNumContextIds; i++)
else
{
if (mContextLastUsed[i].GetValue() == 0)
{
continue;
}
if (TimerMilli::GetNow() - mContextLastUsed[i] >= Time::SecToMsec(mContextIdReuseDelay))
{
mContextLastUsed[i].SetValue(0);
FreeContextId(kMinContextId + i);
}
else
{
contextsWaiting = true;
}
mContextIds.HandleTimer();
}
if (contextsWaiting)
{
mTimer.Start(kStateUpdatePeriod);
}
exit:
return;
}
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
@@ -1393,6 +1313,86 @@ exit:
}
#endif
//---------------------------------------------------------------------------------------------------------------------
// Leader::ContextIds
void Leader::ContextIds::Clear(void)
{
for (uint8_t id = kMinId; id <= kMaxId; id++)
{
MarkAsUnallocated(id);
}
}
Error Leader::ContextIds::GetUnallocatedId(uint8_t &aId)
{
Error error = kErrorNotFound;
for (uint8_t id = kMinId; id <= kMaxId; id++)
{
if (IsUnallocated(id))
{
aId = id;
error = kErrorNone;
break;
}
}
return error;
}
void Leader::ContextIds::ScheduleToRemove(uint8_t aId)
{
VerifyOrExit(IsInUse(aId));
SetRemoveTime(aId, TimerMilli::GetNow() + Time::SecToMsec(mReuseDelay));
Get<Leader>().mTimer.FireAtIfEarlier(GetRemoveTime(aId));
exit:
return;
}
void Leader::ContextIds::SetRemoveTime(uint8_t aId, TimeMilli aTime)
{
uint32_t time = aTime.GetValue();
while ((time == kUnallocated) || (time == kInUse))
{
time++;
}
mRemoveTimes[aId - kMinId].SetValue(time);
}
void Leader::ContextIds::HandleTimer(void)
{
TimeMilli now = TimerMilli::GetNow();
TimeMilli nextTime = now.GetDistantFuture();
for (uint8_t id = kMinId; id <= kMaxId; id++)
{
if (IsUnallocated(id) || IsInUse(id))
{
continue;
}
if (now >= GetRemoveTime(id))
{
MarkAsUnallocated(id);
Get<Leader>().RemoveContext(id);
}
else
{
nextTime = Min(nextTime, GetRemoveTime(id));
}
}
if (nextTime != now.GetDistantFuture())
{
Get<Leader>().mTimer.FireAt(nextTime);
}
}
} // namespace NetworkData
} // namespace ot
+57 -18
View File
@@ -41,6 +41,7 @@
#include <stdint.h>
#include "common/non_copyable.hpp"
#include "common/numeric_limits.hpp"
#include "common/timer.hpp"
#include "net/ip6_address.hpp"
#include "thread/mle_router.hpp"
@@ -122,20 +123,20 @@ public:
/**
* This method returns CONTEXT_ID_RESUSE_DELAY value.
*
* @returns The CONTEXT_ID_REUSE_DELAY value.
* @returns The CONTEXT_ID_REUSE_DELAY value (in seconds).
*
*/
uint32_t GetContextIdReuseDelay(void) const { return mContextIdReuseDelay; }
uint32_t GetContextIdReuseDelay(void) const { return mContextIds.GetReuseDelay(); }
/**
* This method sets CONTEXT_ID_RESUSE_DELAY value.
*
* @warning This method should only be used for testing.
*
* @param[in] aDelay The CONTEXT_ID_REUSE_DELAY value.
* @param[in] aDelay The CONTEXT_ID_REUSE_DELAY value (in seconds).
*
*/
void SetContextIdReuseDelay(uint32_t aDelay) { mContextIdReuseDelay = aDelay; }
void SetContextIdReuseDelay(uint32_t aDelay) { mContextIds.SetReuseDelay(aDelay); }
/**
* This method removes Network Data entries matching with a given RLOC16.
@@ -177,6 +178,8 @@ public:
#endif
private:
static constexpr uint32_t kMaxNetDataSyncWait = 60 * 1000; // Maximum time to wait for netdata sync in msec.
class ChangedFlags
{
public:
@@ -206,6 +209,55 @@ private:
kTlvUpdated, // TLV stable flag is updated based on its sub TLVs.
};
class ContextIds : public InstanceLocator
{
public:
// This class tracks Context IDs. A Context ID can be in one
// of the 3 states: It is unallocated, or it is allocated
// and in-use, or it scheduled to be removed (after reuse delay
// interval is passed).
static constexpr uint8_t kInvalidId = NumericLimits<uint8_t>::kMax;
explicit ContextIds(Instance &aInstance)
: InstanceLocator(aInstance)
, mReuseDelay(kReuseDelay)
{
}
void Clear(void);
Error GetUnallocatedId(uint8_t &aId);
void MarkAsInUse(uint8_t aId) { mRemoveTimes[aId - kMinId].SetValue(kInUse); }
void ScheduleToRemove(uint8_t aId);
uint32_t GetReuseDelay(void) const { return mReuseDelay; }
void SetReuseDelay(uint32_t aDelay) { mReuseDelay = aDelay; }
void HandleTimer(void);
private:
static constexpr uint32_t kReuseDelay = 48 * 60 * 60; // in seconds
static constexpr uint8_t kMinId = 1;
static constexpr uint8_t kMaxId = 15;
// The `mRemoveTimes[id]` is used to track the state of a
// Context ID and its remove time. Two specific values
// `kUnallocated` and `kInUse` are used to indicate ID is in
// unallocated or in-use states. Other values indicate we
// are in remove state waiting to remove it at `mRemoveTime`.
static constexpr uint32_t kUnallocated = 0;
static constexpr uint32_t kInUse = 1;
bool IsUnallocated(uint8_t aId) const { return mRemoveTimes[aId - kMinId].GetValue() == kUnallocated; }
bool IsInUse(uint8_t aId) const { return mRemoveTimes[aId - kMinId].GetValue() == kInUse; }
TimeMilli GetRemoveTime(uint8_t aId) const { return mRemoveTimes[aId - kMinId]; }
void SetRemoveTime(uint8_t aId, TimeMilli aTime);
void MarkAsUnallocated(uint8_t aId) { mRemoveTimes[aId - kMinId].SetValue(kUnallocated); }
TimeMilli mRemoveTimes[kMaxId - kMinId + 1];
uint32_t mReuseDelay;
};
template <Uri kUri> void HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
void HandleTimer(void);
@@ -220,11 +272,6 @@ private:
Error AllocateServiceId(uint8_t &aServiceId) const;
Error AllocateContextId(uint8_t &aContextId);
void FreeContextId(uint8_t aContextId);
void StartContextReuseTimer(uint8_t aContextId);
void StopContextReuseTimer(uint8_t aContextId);
void RemoveContext(uint8_t aContextId);
void RemoveContext(PrefixTlv &aPrefix, uint8_t aContextId);
@@ -285,16 +332,8 @@ private:
using UpdateTimer = TimerMilliIn<Leader, &Leader::HandleTimer>;
static constexpr uint8_t kMinContextId = 1; // Minimum Context ID (0 is used for Mesh Local)
static constexpr uint8_t kNumContextIds = 15; // Maximum Context ID
static constexpr uint32_t kContextIdReuseDelay = 48 * 60 * 60; // in seconds
static constexpr uint32_t kStateUpdatePeriod = 60 * 1000; // State update period in milliseconds
static constexpr uint32_t kMaxNetDataSyncWait = 60 * 1000; // Maximum time to wait for netdata sync.
bool mWaitingForNetDataSync;
uint16_t mContextUsed;
TimeMilli mContextLastUsed[kNumContextIds];
uint32_t mContextIdReuseDelay;
ContextIds mContextIds;
UpdateTimer mTimer;
};
+369
View File
@@ -0,0 +1,369 @@
#!/usr/bin/env python3
#
# Copyright (c) 2023, The OpenThread Authors.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. Neither the name of the copyright holder nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from cli import verify
from cli import verify_within
import cli
import time
# -----------------------------------------------------------------------------------------------------------------------
# Test description: Network Data Context ID assignment and reuse delay.
#
# Network topology
#
# r1 ---- r2 ---- r3
#
test_name = __file__[:-3] if __file__.endswith('.py') else __file__
print('-' * 120)
print('Starting \'{}\''.format(test_name))
# -----------------------------------------------------------------------------------------------------------------------
# Creating `cli.Node` instances
speedup = 40
cli.Node.set_time_speedup_factor(speedup)
r1 = cli.Node()
r2 = cli.Node()
r3 = cli.Node()
# -----------------------------------------------------------------------------------------------------------------------
# Form topology
r1.allowlist_node(r2)
r2.allowlist_node(r1)
r2.allowlist_node(r3)
r3.allowlist_node(r2)
r1.form('netdata')
r2.join(r1)
r3.join(r1)
verify(r1.get_state() == 'leader')
verify(r2.get_state() == 'router')
verify(r3.get_state() == 'router')
# -----------------------------------------------------------------------------------------------------------------------
# Test Implementation
# Change the reuse delay on `r1 (leader) to 5 seconds.
r1.cli('contextreusedelay 5')
verify(int(r1.cli('contextreusedelay')[0]) == 5)
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Add an on-mesh prefix from each router `r1`, r2`, and `r3`.
# Validate that netdata is updated and Context IDs are assigned.
r1.add_prefix('fd00:1::/64', 'poas')
r1.register_netdata()
r2.add_prefix('fd00:2::/64', 'poas')
r2.register_netdata()
r3.add_prefix('fd00:3::/64', 'poas')
r3.add_route('fd00:beef::/64', 's')
r3.register_netdata()
def check_netdata_1():
global netdata
netdata = r1.get_netdata()
verify(len(netdata['prefixes']) == 3)
verify(len(netdata['routes']) == 1)
verify_within(check_netdata_1, 5)
contexts = netdata['contexts']
verify(len(contexts) == 3)
verify(any([context.startswith('fd00:1:0:0::/64') for context in contexts]))
verify(any([context.startswith('fd00:2:0:0::/64') for context in contexts]))
verify(any([context.startswith('fd00:3:0:0::/64') for context in contexts]))
for context in contexts:
verify(context.endswith('c'))
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Remove prefix on `r3`. Validate that Context compress flag
# is cleared for the removed prefix.
r3.remove_prefix('fd00:3::/64')
r3.register_netdata()
def check_netdata_2():
global netdata, versions
versions = r1.get_netdata_versions()
netdata = r1.get_netdata()
verify(len(netdata['prefixes']) == 2)
verify(len(netdata['routes']) == 1)
verify_within(check_netdata_2, 5)
contexts = netdata['contexts']
verify(len(contexts) == 3)
verify(any([context.startswith('fd00:1:0:0::/64') for context in contexts]))
verify(any([context.startswith('fd00:2:0:0::/64') for context in contexts]))
verify(any([context.startswith('fd00:3:0:0::/64') for context in contexts]))
for context in contexts:
if context.startswith('fd00:1:0:0::/64') or context.startswith('fd00:2:0:0::/64'):
verify(context.endswith('c'))
else:
verify(context.endswith('-'))
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Validate that the prefix context is removed within reuse delay
# time (which is set to 5 seconds on leader).
def check_netdata_3():
global netdata
netdata = r1.get_netdata()
verify(len(netdata['prefixes']) == 2)
verify(len(netdata['routes']) == 1)
verify(len(netdata['contexts']) == 2)
verify_within(check_netdata_3, 5)
old_versions = versions
versions = r1.get_netdata_versions()
verify(versions != old_versions)
# Make sure netdata does not change afterwards
time.sleep(5 * 3 / speedup)
verify(versions == r1.get_netdata_versions())
netdata = r1.get_netdata()
verify(len(netdata['prefixes']) == 2)
verify(len(netdata['routes']) == 1)
verify(len(netdata['contexts']) == 2)
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Add two new on-mesh prefixes from `r3`. Validate that they get
# assigned Context IDs.
r3.add_prefix('fd00:4::/64', 'poas')
r3.register_netdata()
r3.add_prefix('fd00:3::/64', 'poas')
r3.register_netdata()
def check_netdata_4():
global netdata
netdata = r1.get_netdata()
verify(len(netdata['prefixes']) == 4)
verify(len(netdata['routes']) == 1)
verify(len(netdata['contexts']) == 4)
verify_within(check_netdata_4, 5)
contexts = netdata['contexts']
verify(len(contexts) == 4)
verify(any([context.startswith('fd00:1:0:0::/64') for context in contexts]))
verify(any([context.startswith('fd00:2:0:0::/64') for context in contexts]))
verify(any([context.startswith('fd00:3:0:0::/64') for context in contexts]))
verify(any([context.startswith('fd00:4:0:0::/64') for context in contexts]))
for context in contexts:
verify(context.endswith('c'))
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Remove prefixes on `r1` and `r2` and re-add them back quickly both
# from `r2`. Validate that they get the same context IDs as before.
for context in contexts:
if context.startswith('fd00:1:0:0::/64'):
cid1 = int(context.split()[1])
elif context.startswith('fd00:2:0:0::/64'):
cid2 = int(context.split()[1])
r1.remove_prefix('fd00:1:0:0::/64')
r1.register_netdata()
r2.remove_prefix('fd00:2:0:0::/64')
r2.register_netdata()
def check_netdata_5():
global netdata
netdata = r1.get_netdata()
verify(len(netdata['prefixes']) == 2)
verify(len(netdata['routes']) == 1)
verify_within(check_netdata_5, 5)
contexts = netdata['contexts']
verify(len(contexts) == 4)
verify(any([context.startswith('fd00:1:0:0::/64') for context in contexts]))
verify(any([context.startswith('fd00:2:0:0::/64') for context in contexts]))
verify(any([context.startswith('fd00:3:0:0::/64') for context in contexts]))
verify(any([context.startswith('fd00:4:0:0::/64') for context in contexts]))
for context in contexts:
if context.startswith('fd00:1:0:0::/64') or context.startswith('fd00:2:0:0::/64'):
verify(context.endswith('-'))
else:
verify(context.endswith('c'))
# Re-add both prefixes (now from `r2`) before CID remove delay time
# is expired.
r2.add_prefix('fd00:1::/64', 'poas')
r2.add_prefix('fd00:2::/64', 'poas')
r2.register_netdata()
def check_netdata_6():
global netdata
netdata = r1.get_netdata()
verify(len(netdata['prefixes']) == 4)
verify(len(netdata['routes']) == 1)
verify_within(check_netdata_6, 5)
contexts = netdata['contexts']
verify(len(contexts) == 4)
verify(any([context.startswith('fd00:1:0:0::/64') for context in contexts]))
verify(any([context.startswith('fd00:2:0:0::/64') for context in contexts]))
verify(any([context.startswith('fd00:3:0:0::/64') for context in contexts]))
verify(any([context.startswith('fd00:4:0:0::/64') for context in contexts]))
for context in contexts:
verify(context.endswith('c'))
if context.startswith('fd00:1:0:0::/64'):
verify(int(context.split()[1]) == cid1)
elif context.startswith('fd00:2:0:0::/64'):
verify(int(context.split()[1]) == cid2)
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Remove two prefixes on `r3`. Add one back as an external route from
# `r1`. Also add a new prefix from `r1`. Validate that the context IDs
# for the removed on-mesh prefixes are removed after reuse delay time
# and that the new prefix gets a different Context ID.
# Remember the CID used.
for context in contexts:
if context.startswith('fd00:3:0:0::/64'):
cid3 = int(context.split()[1])
elif context.startswith('fd00:4:0:0::/64'):
cid4 = int(context.split()[1])
r3.remove_prefix('fd00:3:0:0::/64')
r3.remove_prefix('fd00:4:0:0::/64')
r3.register_netdata()
def check_netdata_7():
global netdata
netdata = r1.get_netdata()
verify(len(netdata['prefixes']) == 2)
verify(len(netdata['routes']) == 1)
verify_within(check_netdata_7, 5)
contexts = netdata['contexts']
verify(len(contexts) == 4)
verify(any([context.startswith('fd00:1:0:0::/64') for context in contexts]))
verify(any([context.startswith('fd00:2:0:0::/64') for context in contexts]))
verify(any([context.startswith('fd00:3:0:0::/64') for context in contexts]))
verify(any([context.startswith('fd00:4:0:0::/64') for context in contexts]))
for context in contexts:
if context.startswith('fd00:3:0:0::/64') or context.startswith('fd00:4:0:0::/64'):
verify(context.endswith('-'))
else:
verify(context.endswith('c'))
# Add first one removed as route and add a new prefix.
r1.add_route('fd00:3:0:0::/64', 's')
r1.add_prefix('fd00:5:0:0::/64', 'poas')
r1.register_netdata()
def check_netdata_8():
global netdata
netdata = r1.get_netdata()
verify(len(netdata['prefixes']) == 3)
verify(len(netdata['routes']) == 2)
verify(len(netdata['contexts']) == 3)
verify_within(check_netdata_8, 5)
contexts = netdata['contexts']
verify(len(contexts) == 3)
verify(any([context.startswith('fd00:1:0:0::/64') for context in contexts]))
verify(any([context.startswith('fd00:2:0:0::/64') for context in contexts]))
verify(any([context.startswith('fd00:5:0:0::/64') for context in contexts]))
for context in contexts:
verify(context.endswith('c'))
if context.startswith('fd00:5:0:0::/64'):
verify(not int(context.split()[1]) in [cid3, cid4])
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Remove a prefix on `r2`, wait one second and remove a second
# prefix. Validate the Context IDs for both are removed.
r2.remove_prefix('fd00:1::/64')
r2.register_netdata()
time.sleep(1 / speedup)
r2.remove_prefix('fd00:2::/64')
r2.register_netdata()
def check_netdata_9():
global netdata
netdata = r1.get_netdata()
verify(len(netdata['prefixes']) == 1)
verify(len(netdata['routes']) == 2)
verify(len(netdata['contexts']) == 1)
verify_within(check_netdata_9, 5)
contexts = netdata['contexts']
verify(len(contexts) == 1)
verify(contexts[0].startswith('fd00:5:0:0::/64'))
verify(contexts[0].endswith('c'))
# -----------------------------------------------------------------------------------------------------------------------
# Test finished
cli.Node.finalize_all_nodes()
print('\'{}\' passed.'.format(test_name))
+1
View File
@@ -183,6 +183,7 @@ if [ "$TORANJ_CLI" = 1 ]; then
run cli/test-016-child-mode-change.py
run cli/test-017-network-data-versions.py
run cli/test-018-next-hop-and-path-cost.py
run cli/test-019-netdata-context-id.py
run cli/test-400-srp-client-server.py
run cli/test-601-channel-manager-channel-change.py
# Skip the "channel-select" test on a TREL only radio link, since it