Skip to content

Commit

Permalink
[controller] add schedule migration API
Browse files Browse the repository at this point in the history
  • Loading branch information
Irving-cl committed Aug 15, 2024
1 parent 29a2095 commit c7499d8
Show file tree
Hide file tree
Showing 11 changed files with 184 additions and 3 deletions.
6 changes: 4 additions & 2 deletions src/dbus/server/dbus_thread_object_ncp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,10 @@ void DBusThreadObjectNcp::ScheduleMigrationHandler(DBusRequest &aRequest)

SuccessOrExit(error = agent::ThreadHelper::ProcessDatasetForMigration(pendingOpDatasetTlvs, delayInMilli));

// TODO: Change to use Migrate API
error = OT_ERROR_NOT_IMPLEMENTED;
mHost.ScheduleMigration(pendingOpDatasetTlvs, [aRequest](otError aError, const std::string &aErrorInfo) mutable {
OT_UNUSED_VARIABLE(aErrorInfo);
aRequest.ReplyOtResult(aError);
});

exit:
if (error != OT_ERROR_NONE)
Expand Down
19 changes: 19 additions & 0 deletions src/ncp/ncp_host.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,25 @@ void NcpHost::Leave(const AsyncResultReceiver &aReceiver)
task->Run();
}

void NcpHost::ScheduleMigration(const otOperationalDatasetTlvs &aPendingOpDatasetTlvs,
const AsyncResultReceiver aReceiver)
{
otDeviceRole role = GetDeviceRole();
otError error = OT_ERROR_NONE;
auto errorHandler = [aReceiver](otError aError, const std::string &aErrorInfo) { aReceiver(aError, aErrorInfo); };

VerifyOrExit(role != OT_DEVICE_ROLE_DISABLED && role != OT_DEVICE_ROLE_DETACHED, error = OT_ERROR_INVALID_STATE);

mNcpSpinel.DatasetMgmtSetPending(aPendingOpDatasetTlvs, std::make_shared<AsyncTask>(errorHandler));

exit:
if (error != OT_ERROR_NONE)
{
mTaskRunner.Post(
[aReceiver, error](void) { aReceiver(error, "Cannot schedule migration when this device is detached"); });
}
}

void NcpHost::Process(const MainloopContext &aMainloop)
{
mSpinelDriver.Process(&aMainloop);
Expand Down
3 changes: 3 additions & 0 deletions src/ncp/ncp_host.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ class NcpHost : public MainloopProcessor, public ThreadHost, public NcpNetworkPr
// ThreadHost methods
void Join(const otOperationalDatasetTlvs &aActiveOpDatasetTlvs, const AsyncResultReceiver &aReceiver) override;
void Leave(const AsyncResultReceiver &aReceiver) override;
void ScheduleMigration(const otOperationalDatasetTlvs &aPendingOpDatasetTlvs,
const AsyncResultReceiver aReceiver) override;
CoprocessorType GetCoprocessorType(void) override { return OT_COPROCESSOR_NCP; }
const char *GetCoprocessorVersion(void) override;
const char *GetInterfaceName(void) const override { return mConfig.mInterfaceName; }
Expand All @@ -102,6 +104,7 @@ class NcpHost : public MainloopProcessor, public ThreadHost, public NcpNetworkPr
ot::Spinel::SpinelDriver &mSpinelDriver;
otPlatformConfig mConfig;
NcpSpinel mNcpSpinel;
TaskRunner mTaskRunner;
};

} // namespace Ncp
Expand Down
42 changes: 42 additions & 0 deletions src/ncp/ncp_spinel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,25 @@ void NcpSpinel::DatasetSetActiveTlvs(const otOperationalDatasetTlvs &aActiveOpDa
}
}

void NcpSpinel::DatasetMgmtSetPending(const otOperationalDatasetTlvs &aPendingOpDatasetTlvs, AsyncTaskPtr aAsyncTask)
{
otError error = OT_ERROR_NONE;
EncodingFunc encodingFunc = [this, &aPendingOpDatasetTlvs] {
return mEncoder.WriteData(aPendingOpDatasetTlvs.mTlvs, aPendingOpDatasetTlvs.mLength);
};

VerifyOrExit(mDatasetMgmtSetPendingTask == nullptr, error = OT_ERROR_BUSY);

SuccessOrExit(error = SetProperty(SPINEL_PROP_THREAD_MGMT_SET_PENDING_DATASET_TLVS, encodingFunc));
mDatasetMgmtSetPendingTask = aAsyncTask;

exit:
if (error != OT_ERROR_NONE)
{
mTaskRunner.Post([aAsyncTask, error] { aAsyncTask->SetResult(error, "Failed to set pending dataset!"); });
}
}

void NcpSpinel::Ip6SetEnabled(bool aEnable, AsyncTaskPtr aAsyncTask)
{
otError error = OT_ERROR_NONE;
Expand Down Expand Up @@ -327,6 +346,15 @@ void NcpSpinel::HandleValueIs(spinel_prop_key_t aKey, const uint8_t *aBuffer, ui
break;
}

case SPINEL_PROP_THREAD_MGMT_SET_PENDING_DATASET_TLVS:
{
spinel_status_t status = SPINEL_STATUS_OK;

SuccessOrExit(error = SpinelDataUnpack(aBuffer, aLength, SPINEL_DATATYPE_UINT_PACKED_S, &status));
CallAndClear(mDatasetMgmtSetPendingTask, ot::Spinel::SpinelStatusToOtError(status));
break;
}

default:
otbrLogWarning("Received uncognized key: %u", aKey);
break;
Expand Down Expand Up @@ -364,6 +392,20 @@ otbrError NcpSpinel::HandleResponseForPropSet(spinel_tid_t aTid,
CallAndClear(mThreadSetEnabledTask, OT_ERROR_NONE);
break;

case SPINEL_PROP_THREAD_MGMT_SET_PENDING_DATASET_TLVS:
if (aKey == SPINEL_PROP_LAST_STATUS)
{ // Failed case
spinel_status_t status = SPINEL_STATUS_OK;

SuccessOrExit(error = SpinelDataUnpack(aData, aLength, SPINEL_DATATYPE_UINT_PACKED_S, &status));
CallAndClear(mDatasetMgmtSetPendingTask, ot::Spinel::SpinelStatusToOtError(status));
}
else if (aKey != SPINEL_PROP_THREAD_MGMT_SET_PENDING_DATASET_TLVS)
{
ExitNow(error = OTBR_ERROR_INVALID_STATE);
}
break;

default:
VerifyOrExit(aKey == mWaitingKeyTable[aTid], error = OTBR_ERROR_INVALID_STATE);
break;
Expand Down
13 changes: 13 additions & 0 deletions src/ncp/ncp_spinel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,18 @@ class NcpSpinel
*/
void DatasetSetActiveTlvs(const otOperationalDatasetTlvs &aActiveOpDatasetTlvs, AsyncTaskPtr aAsyncTask);

/**
* This method instructs the NCP to send a MGMT_SET to set Thread Pending Operational Dataset.
*
* If this method is called again before the previous call completed, no action will be taken.
* The new receiver @p aAsyncTask will be set a result OT_ERROR_BUSY.
*
* @param[in] aPendingOpDatasetTlvs A reference to the pending operational dataset of the Thread network.
* @param[in] aAsyncTask A pointer to an async result to receive the result of this operation.
*
*/
void DatasetMgmtSetPending(const otOperationalDatasetTlvs &aPendingOpDatasetTlvs, AsyncTaskPtr aAsyncTask);

/**
* This method enableds/disables the IP6 on the NCP.
*
Expand Down Expand Up @@ -241,6 +253,7 @@ class NcpSpinel
PropsObserver *mPropsObserver;

AsyncTaskPtr mDatasetSetActiveTask;
AsyncTaskPtr mDatasetMgmtSetPendingTask;
AsyncTaskPtr mIp6SetEnabledTask;
AsyncTaskPtr mThreadSetEnabledTask;
AsyncTaskPtr mThreadDetachGracefullyTask;
Expand Down
9 changes: 9 additions & 0 deletions src/ncp/rcp_host.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,15 @@ void RcpHost::Leave(const AsyncResultReceiver &aReceiver)
mTaskRunner.Post([aReceiver](void) { aReceiver(OT_ERROR_NOT_IMPLEMENTED, "Not implemented!"); });
}

void RcpHost::ScheduleMigration(const otOperationalDatasetTlvs &aPendingOpDatasetTlvs,
const AsyncResultReceiver aReceiver)
{
OT_UNUSED_VARIABLE(aPendingOpDatasetTlvs);

// TODO: Implement ScheduleMigration under RCP mode.
mTaskRunner.Post([aReceiver](void) { aReceiver(OT_ERROR_NOT_IMPLEMENTED, "Not implemented!"); });
}

/*
* Provide, if required an "otPlatLog()" function
*/
Expand Down
2 changes: 2 additions & 0 deletions src/ncp/rcp_host.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,8 @@ class RcpHost : public MainloopProcessor, public ThreadHost, public OtNetworkPro
// Thread Control virtual methods
void Join(const otOperationalDatasetTlvs &aActiveOpDatasetTlvs, const AsyncResultReceiver &aRecevier) override;
void Leave(const AsyncResultReceiver &aRecevier) override;
void ScheduleMigration(const otOperationalDatasetTlvs &aPendingOpDatasetTlvs,
const AsyncResultReceiver aReceiver) override;

CoprocessorType GetCoprocessorType(void) override
{
Expand Down
10 changes: 10 additions & 0 deletions src/ncp/thread_host.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,16 @@ class ThreadHost : virtual public NetworkProperties
*/
virtual void Leave(const AsyncResultReceiver &aRecevier) = 0;

/**
* This method migrates this device to the new network specified by @p aPendingOpDatasetTlvs.
*
* @param[in] aPendingOpDatasetTlvs A reference to the pending operational dataset of the Thread network.
* @param[in] aReceiver A receiver to get the async result of this operation.
*
*/
virtual void ScheduleMigration(const otOperationalDatasetTlvs &aPendingOpDatasetTlvs,
const AsyncResultReceiver aReceiver) = 0;

/**
* Returns the co-processor type.
*
Expand Down
80 changes: 80 additions & 0 deletions tests/scripts/expect/ncp_schedule_migration.exp
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#!/usr/bin/expect -f
#
# Copyright (c) 2024, 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.
#

source "tests/scripts/expect/_common.exp"

# Dataset of the initial Thread network
set dataset "0e080000000000010000000300001435060004001fffe002087d61eb42cdc48d6a0708fd0d07fca1b9f0500510ba088fc2bd6c3b3897f7a10f58263ff3030f4f70656e5468726561642d353234660102524f04109dc023ccd447b12b50997ef68020f19e0c0402a0f7f8"
set dataset_dbus "0x0e,0x08,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x03,0x00,0x00,0x14,0x35,0x06,0x00,0x04,0x00,0x1f,0xff,0xe0,0x02,0x08,0x7d,0x61,0xeb,0x42,0xcd,0xc4,0x8d,0x6a,0x07,0x08,0xfd,0x0d,0x07,0xfc,0xa1,0xb9,0xf0,0x50,0x05,0x10,0xba,0x08,0x8f,0xc2,0xbd,0x6c,0x3b,0x38,0x97,0xf7,0xa1,0x0f,0x58,0x26,0x3f,0xf3,0x03,0x0f,0x4f,0x70,0x65,0x6e,0x54,0x68,0x72,0x65,0x61,0x64,0x2d,0x35,0x32,0x34,0x66,0x01,0x02,0x52,0x4f,0x04,0x10,0x9d,0xc0,0x23,0xcc,0xd4,0x47,0xb1,0x2b,0x50,0x99,0x7e,0xf6,0x80,0x20,0xf1,0x9e,0x0c,0x04,0x02,0xa0,0xf7,0xf8"

# Dataset of the Thread network to migrate to
# (Only updates active timestamp and panid, panid is set to 0x9999)
set dataset1 "0e080000000000020000000300001435060004001fffe002087d61eb42cdc48d6a0708fd0d07fca1b9f0500510ba088fc2bd6c3b3897f7a10f58263ff3030f4f70656e5468726561642d353234660102999904109dc023ccd447b12b50997ef68020f19e0c0402a0f7f8"
set dataset1_dbus "0x0e,0x08,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x14,0x35,0x06,0x00,0x04,0x00,0x1f,0xff,0xe0,0x02,0x08,0x7d,0x61,0xeb,0x42,0xcd,0xc4,0x8d,0x6a,0x07,0x08,0xfd,0x0d,0x07,0xfc,0xa1,0xb9,0xf0,0x50,0x05,0x10,0xba,0x08,0x8f,0xc2,0xbd,0x6c,0x3b,0x38,0x97,0xf7,0xa1,0x0f,0x58,0x26,0x3f,0xf3,0x03,0x0f,0x4f,0x70,0x65,0x6e,0x54,0x68,0x72,0x65,0x61,0x64,0x2d,0x35,0x32,0x34,0x66,0x01,0x02,0x99,0x99,0x04,0x10,0x9d,0xc0,0x23,0xcc,0xd4,0x47,0xb1,0x2b,0x50,0x99,0x7e,0xf6,0x80,0x20,0xf1,0x9e,0x0c,0x04,0x02,0xa0,0xf7,0xf8"

# Step 1. Start otbr-agent with a NCP and join the network by dbus join method
spawn_node 1 otbr $::env(EXP_OT_NCP_PATH)
sleep 1
spawn dbus-send --system --dest=io.openthread.BorderRouter.wpan0 --type=method_call --print-reply /io/openthread/BorderRouter/wpan0 io.openthread.BorderRouter.Join "array:byte:${dataset_dbus}"
expect eof

# Step 2. Wait 10 seconds, check if the otbr-agent has attached successfully
sleep 10
spawn dbus-send --system --dest=io.openthread.BorderRouter.wpan0 --print-reply --reply-timeout=1000 /io/openthread/BorderRouter/wpan0 org.freedesktop.DBus.Properties.Get string:io.openthread.BorderRouter string:DeviceRole
expect -re {leader} {
} timeout {
puts "timeout!"
exit 1
}
expect eof

# Step 3. Start a Thread node and create a Thread network
spawn_node 2 cli $::env(EXP_OT_CLI_PATH)

send "dataset set active ${dataset}\n"
expect_line "Done"
send "mode rn\n"
expect_line "Done"
send "ifconfig up\n"
expect_line "Done"
send "thread start\n"
expect_line "Done"
wait_for "state" "child"
expect_line "Done"

# Step 4. Call ScheduleMigration method to migrate to another Thread network after 30s
spawn dbus-send --system --dest=io.openthread.BorderRouter.wpan0 --type=method_call --print-reply /io/openthread/BorderRouter/wpan0 io.openthread.BorderRouter.ScheduleMigration "array:byte:${dataset1_dbus}" "uint32:0x7530"
expect eof

# Step 5. Wait 31 seconds, check if the otbr-agent has migrated successfully by checking child's panid
sleep 31
switch_node 2
send "panid\n"
expect_line "0x9999"
expect_line "Done"
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ spawn_node 1 otbr $::env(EXP_OT_NCP_PATH)
sleep 1

spawn dbus-send --system --dest=io.openthread.BorderRouter.wpan0 --type=method_call --print-reply /io/openthread/BorderRouter/wpan0 io.openthread.BorderRouter.ScheduleMigration "array:byte:${dataset_valid}" "uint32:0x7530"
expect Error.NotImplemented
expect Error.InvalidState
expect eof

spawn dbus-send --system --dest=io.openthread.BorderRouter.wpan0 --type=method_call --print-reply /io/openthread/BorderRouter/wpan0 io.openthread.BorderRouter.ScheduleMigration "array:byte:${dataset_has_pending_timestamp}" "uint32:0x7530"
Expand Down
1 change: 1 addition & 0 deletions tests/scripts/ncp_mode
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ main()
otbr_exec_expect_script "${EXPECT_SCRIPT_DIR}/ncp_get_device_role.exp" || die "ncp expect script failed!"
otbr_exec_expect_script "${EXPECT_SCRIPT_DIR}/ncp_join_leave.exp" || die "ncp expect script failed!"
otbr_exec_expect_script "${EXPECT_SCRIPT_DIR}/ncp_test_schedule_migration_dbus_api.exp" || die "ncp expect script failed!"
otbr_exec_expect_script "${EXPECT_SCRIPT_DIR}/ncp_schedule_migration.exp" || die "ncp expect script failed!"
fi
}

Expand Down

0 comments on commit c7499d8

Please sign in to comment.