From cf1d23c11e684fa99c631d83edf0280bc40a9bce Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Mon, 2 Mar 2026 18:03:14 -0800 Subject: [PATCH] [cli] add lease info to `srp server host` and `srp server service` (#12589) This commit adds lease and remaining lease information to the output of `srp server host` and `srp server service` CLI commands. The information includes: - `lease`: The total lease time in seconds. - `key-lease`: The total key lease time in seconds. - `remaining lease`: The remaining lease time in seconds (with millisecond precision). - `remaining key-lease`: The remaining key lease time in seconds (with millisecond precision). A new utility method `OutputMsecDurationInSec()` is added to `Utils` class to format durations in milliseconds as seconds with a fractional part. The SRP server host and service output parsers in `tests/scripts/thread-cert/node.py`, `tests/toranj/cli/cli.py`, and `tools/otci/otci/otci.py` are updated to correctly handle the new fields for both active and deleted entries. --- src/cli/README_SRP_SERVER.md | 12 ++++++ src/cli/cli_srp_server.cpp | 69 ++++++++++++++++++++++--------- src/cli/cli_srp_server.hpp | 1 + src/cli/cli_utils.cpp | 8 ++++ src/cli/cli_utils.hpp | 9 ++++ tests/scripts/thread-cert/node.py | 27 ++++++++++-- tests/toranj/cli/cli.py | 30 +++++++++++--- tools/otci/otci/otci.py | 6 +++ 8 files changed, 132 insertions(+), 30 deletions(-) diff --git a/src/cli/README_SRP_SERVER.md b/src/cli/README_SRP_SERVER.md index cd91f78b2..3e9395d0b 100644 --- a/src/cli/README_SRP_SERVER.md +++ b/src/cli/README_SRP_SERVER.md @@ -165,9 +165,17 @@ Print information of all registered hosts. srp-api-test-1.default.service.arpa. deleted: false addresses: [fdde:ad00:beef:0:0:ff:fe00:fc10] + lease: 7200 + key-lease: 1209600 + remaining lease: 6345.459 + remaining key-lease: 1208734.459 srp-api-test-0.default.service.arpa. deleted: false addresses: [fdde:ad00:beef:0:0:ff:fe00:fc10] + lease: 3600 + key-lease: 1209600 + remaining lease: 2600.012 + remaining key-lease: 1208600.012 Done ``` @@ -241,6 +249,8 @@ srp-api-test-1._ipps._tcp.default.service.arpa. ttl: 7200 lease: 7200 key-lease: 1209600 + remaining lease: 6345.459 + remaining key-lease: 1208734.459 TXT: [616263, xyz=585960] host: srp-api-test-1.default.service.arpa. addresses: [fdde:ad00:beef:0:0:ff:fe00:fc10] @@ -253,6 +263,8 @@ srp-api-test-0._ipps._tcp.default.service.arpa. ttl: 3600 lease: 3600 key-lease: 1209600 + remaining lease: 2600.012 + remaining key-lease: 1208600.012 TXT: [616263, xyz=585960] host: srp-api-test-0.default.service.arpa. addresses: [fdde:ad00:beef:0:0:ff:fe00:fc10] diff --git a/src/cli/cli_srp_server.cpp b/src/cli/cli_srp_server.cpp index 53ef1c579..cd25c0b9f 100644 --- a/src/cli/cli_srp_server.cpp +++ b/src/cli/cli_srp_server.cpp @@ -378,32 +378,35 @@ template <> otError SrpServer::Process(Arg aArgs[]) host = nullptr; while ((host = otSrpServerGetNextHost(GetInstancePtr(), host)) != nullptr) { - const otIp6Address *addresses; - uint8_t addressesNum; - bool isDeleted = otSrpServerHostIsDeleted(host); + const otIp6Address *addresses; + uint8_t addressesNum; + bool isDeleted = otSrpServerHostIsDeleted(host); + otSrpServerLeaseInfo leaseInfo; OutputLine("%s", otSrpServerHostGetFullName(host)); OutputLine(kIndentSize, "deleted: %s", isDeleted ? "true" : "false"); - if (isDeleted) + + if (!isDeleted) { - continue; - } + OutputSpaces(kIndentSize); + OutputFormat("addresses: ["); - OutputSpaces(kIndentSize); - OutputFormat("addresses: ["); + addresses = otSrpServerHostGetAddresses(host, &addressesNum); - addresses = otSrpServerHostGetAddresses(host, &addressesNum); - - for (uint8_t i = 0; i < addressesNum; ++i) - { - OutputIp6Address(addresses[i]); - if (i < addressesNum - 1) + for (uint8_t i = 0; i < addressesNum; ++i) { - OutputFormat(", "); + OutputIp6Address(addresses[i]); + if (i < addressesNum - 1) + { + OutputFormat(", "); + } } + + OutputLine("]"); } - OutputLine("]"); + otSrpServerHostGetLeaseInfo(host, &leaseInfo); + OutputLeaseInfo(leaseInfo, isDeleted); } exit: @@ -430,6 +433,27 @@ void SrpServer::OutputHostAddresses(const otSrpServerHost *aHost) OutputFormat("]"); } +void SrpServer::OutputLeaseInfo(const otSrpServerLeaseInfo &aLeaseInfo, bool aIsDeleted) +{ + if (!aIsDeleted) + { + OutputLine(kIndentSize, "lease: %lu", ToUlong(aLeaseInfo.mLease / 1000)); + } + + OutputLine(kIndentSize, "key-lease: %lu", ToUlong(aLeaseInfo.mKeyLease / 1000)); + + if (!aIsDeleted) + { + OutputFormat(kIndentSize, "remaining lease: "); + OutputMsecDurationInSec(aLeaseInfo.mRemainingLease); + OutputNewLine(); + } + + OutputFormat(kIndentSize, "remaining key-lease: "); + OutputMsecDurationInSec(aLeaseInfo.mRemainingKeyLease); + OutputNewLine(); +} + /** * @cli srp server service * @code @@ -443,6 +467,8 @@ void SrpServer::OutputHostAddresses(const otSrpServerHost *aHost) * ttl: 7200 * lease: 7200 * key-lease: 1209600 + * remaining lease: 6345.459 + * remaining key-lease: 1208734.459 * TXT: [616263, xyz=585960] * host: srp-api-test-1.default.service.arpa. * addresses: [fdde:ad00:beef:0:0:ff:fe00:fc10] @@ -455,6 +481,8 @@ void SrpServer::OutputHostAddresses(const otSrpServerHost *aHost) * ttl: 3600 * lease: 3600 * key-lease: 1209600 + * remaining lease: 2600.012 + * remaining key-lease: 1208600.012 * TXT: [616263, xyz=585960] * host: srp-api-test-0.default.service.arpa. * addresses: [fdde:ad00:beef:0:0:ff:fe00:fc10] @@ -493,13 +521,14 @@ template <> otError SrpServer::Process(Arg aArgs[]) OutputLine("%s", otSrpServerServiceGetInstanceName(service)); OutputLine(kIndentSize, "deleted: %s", isDeleted ? "true" : "false"); + otSrpServerServiceGetLeaseInfo(service, &leaseInfo); + if (isDeleted) { + OutputLeaseInfo(leaseInfo, isDeleted); continue; } - otSrpServerServiceGetLeaseInfo(service, &leaseInfo); - OutputFormat(kIndentSize, "subtypes: "); for (uint16_t index = 0;; index++) @@ -523,8 +552,8 @@ template <> otError SrpServer::Process(Arg aArgs[]) OutputLine(kIndentSize, "priority: %u", otSrpServerServiceGetPriority(service)); OutputLine(kIndentSize, "weight: %u", otSrpServerServiceGetWeight(service)); OutputLine(kIndentSize, "ttl: %lu", ToUlong(otSrpServerServiceGetTtl(service))); - OutputLine(kIndentSize, "lease: %lu", ToUlong(leaseInfo.mLease / 1000)); - OutputLine(kIndentSize, "key-lease: %lu", ToUlong(leaseInfo.mKeyLease / 1000)); + + OutputLeaseInfo(leaseInfo, isDeleted); txtData = otSrpServerServiceGetTxtData(service, &txtDataLength); OutputFormat(kIndentSize, "TXT: "); diff --git a/src/cli/cli_srp_server.hpp b/src/cli/cli_srp_server.hpp index c05fa92dc..86593029c 100644 --- a/src/cli/cli_srp_server.hpp +++ b/src/cli/cli_srp_server.hpp @@ -83,6 +83,7 @@ private: template otError Process(Arg aArgs[]); void OutputHostAddresses(const otSrpServerHost *aHost); + void OutputLeaseInfo(const otSrpServerLeaseInfo &aLeaseInfo, bool aIsDeleted); }; } // namespace Cli diff --git a/src/cli/cli_utils.cpp b/src/cli/cli_utils.cpp index 155139249..125768504 100644 --- a/src/cli/cli_utils.cpp +++ b/src/cli/cli_utils.cpp @@ -160,6 +160,14 @@ void Utils::OutputUint64Line(uint64_t aUint64) void Utils::OutputEnabledDisabledStatus(bool aEnabled) { OutputLine(aEnabled ? "Enabled" : "Disabled"); } +void Utils::OutputMsecDurationInSec(uint32_t aMsecDuration) +{ + uint32_t durInSec = aMsecDuration / 1000; + uint32_t remainder = aMsecDuration % 1000; + + OutputFormat("%lu.%03u", ToUlong(durInSec), static_cast(remainder)); +} + #if OPENTHREAD_FTD || OPENTHREAD_MTD void Utils::OutputIp6Address(const otIp6Address &aAddress) diff --git a/src/cli/cli_utils.hpp b/src/cli/cli_utils.hpp index b1441d2dc..dc22c8e0d 100644 --- a/src/cli/cli_utils.hpp +++ b/src/cli/cli_utils.hpp @@ -351,6 +351,15 @@ public: */ void OutputEnabledDisabledStatus(bool aEnabled); + /** + * Outputs a given duration interval in seconds including the msec remainder. + * + * The duration is outputted in seconds with msec remainder, e.g., 12.047. + * + * @param[in] aMsecDuration A duration interval in msec. + */ + void OutputMsecDurationInSec(uint32_t aMsecDuration); + #if OPENTHREAD_FTD || OPENTHREAD_MTD /** diff --git a/tests/scripts/thread-cert/node.py b/tests/scripts/thread-cert/node.py index dcf69cf63..f8602c09b 100755 --- a/tests/scripts/thread-cert/node.py +++ b/tests/scripts/thread-cert/node.py @@ -1157,7 +1157,11 @@ class NodeImpl: 'fullname': 'my-host.default.service.arpa.', 'name': 'my-host', 'deleted': 'false', - 'addresses': ['2001::1', '2001::2'] + 'addresses': ['2001::1', '2001::2'], + 'lease': '7200', + 'key-lease': '1209600', + 'remaining lease': '6345.459', + 'remaining key-lease': '1208734.459' }] """ @@ -1173,6 +1177,10 @@ class NodeImpl: host['deleted'] = lines.pop(0).strip().split(':')[1].strip() if host['deleted'] == 'true': + for _ in range(2): + # `key-lease` and `remaining key-lease` + key_value = lines.pop(0).strip().split(':') + host[key_value[0].strip()] = key_value[1].strip() host_list.append(host) continue @@ -1180,6 +1188,11 @@ class NodeImpl: map(str.strip, addresses) host['addresses'] = [addr.strip() for addr in addresses if addr] + for _ in range(4): + # `lease`, `key-lease`, `remaining lease` and `remaining key-lease` + key_value = lines.pop(0).strip().split(':') + host[key_value[0].strip()] = key_value[1].strip() + host_list.append(host) return host_list @@ -1210,7 +1223,9 @@ class NodeImpl: 'weight': '0', 'ttl': '7200', 'lease': '7200', - 'key-lease': '7200', + 'key-lease': '1209600', + 'remaining lease': '6345.459', + 'remaining key-lease': '1208734.459', 'TXT': ['abc=010203'], 'host_fullname': 'my-host.default.service.arpa.', 'host': 'my-host', @@ -1235,11 +1250,15 @@ class NodeImpl: service['deleted'] = lines.pop(0).strip().split(':')[1].strip() if service['deleted'] == 'true': + for _ in range(2): + key_value = lines.pop(0).strip().split(':') + service[key_value[0].strip()] = key_value[1].strip() service_list.append(service) continue - # 'subtypes', port', 'priority', 'weight', 'ttl', 'lease', and 'key-lease' - for i in range(0, 7): + # 'subtypes', port', 'priority', 'weight', 'ttl', 'lease', 'key-lease', + # 'remaining lease', and `remaining key-lease` + for i in range(0, 9): key_value = lines.pop(0).strip().split(':') service[key_value[0].strip()] = key_value[1].strip() diff --git a/tests/toranj/cli/cli.py b/tests/toranj/cli/cli.py index 183575baa..aec0b9dc8 100644 --- a/tests/toranj/cli/cli.py +++ b/tests/toranj/cli/cli.py @@ -715,15 +715,18 @@ class Node(object): self._cli_no_output('srp server lease', min_lease, max_lease, min_key_lease, max_key_lease) def srp_server_get_hosts(self): - """Returns the host list on the SRP server as a list of property - dictionary. + """Returns the host list on the SRP server as a list of property dictionary. Example output: [{ 'fullname': 'my-host.default.service.arpa.', 'name': 'my-host', 'deleted': 'false', - 'addresses': ['2001::1', '2001::2'] + 'addresses': ['2001::1', '2001::2'], + 'lease': '7200', + 'key-lease': '1209600', + 'remaining lease': '6345.459', + 'remaining key-lease': '1208734.459' }] """ outputs = self.cli('srp server host') @@ -734,11 +737,19 @@ class Node(object): host['name'] = host['fullname'].split('.')[0] host['deleted'] = outputs.pop(0).strip().split(':')[1].strip() if host['deleted'] == 'true': + for _ in range(2): + # `key-lease` and `remaining key-lease` + key_value = outputs.pop(0).strip().split(':') + host[key_value[0].strip()] = key_value[1].strip() host_list.append(host) continue addresses = outputs.pop(0).strip().split('[')[1].strip(' ]').split(',') map(str.strip, addresses) host['addresses'] = [addr for addr in addresses if addr] + for _ in range(4): + # `lease`, `key-lease`, `remaining lease`, and `remaining key-lease` + key_value = outputs.pop(0).strip().split(':') + host[key_value[0].strip()] = key_value[1].strip() host_list.append(host) return host_list @@ -767,7 +778,9 @@ class Node(object): 'weight': '0', 'ttl': '7200', 'lease': '7200', - 'key-lease', '1209600', + 'key-lease': '1209600', + 'remaining lease': '6322.418', + 'remaining key-lease': '1109587.418', 'TXT': ['abc=010203'], 'host_fullname': 'my-host.default.service.arpa.', 'host': 'my-host', @@ -786,10 +799,15 @@ class Node(object): service['name'] = '.'.join(name_labels[1:3]) service['deleted'] = outputs.pop(0).strip().split(':')[1].strip() if service['deleted'] == 'true': + for _ in range(2): + # `key-lease` and `remaining key-lease` + key_value = outputs.pop(0).strip().split(':') + service[key_value[0].strip()] = key_value[1].strip() service_list.append(service) continue - # 'subtypes', port', 'priority', 'weight', 'ttl', 'lease', 'key-lease' - for i in range(0, 7): + # 'subtypes', port', 'priority', 'weight', 'ttl', 'lease', 'key-lease', + # 'remaining lease', 'remaining key-lease'. + for i in range(0, 9): key_value = outputs.pop(0).strip().split(':') service[key_value[0].strip()] = key_value[1].strip() txt_entries = outputs.pop(0).strip().split('[')[1].strip(' ]').split(',') diff --git a/tools/otci/otci/otci.py b/tools/otci/otci/otci.py index e9848f369..1a3b432d8 100644 --- a/tools/otci/otci/otci.py +++ b/tools/otci/otci/otci.py @@ -1164,6 +1164,10 @@ class OTCI(object): v = v[1:-1] info['addresses'] = list(map(Ip6Addr, v.split(', '))) + + elif k in ('lease', 'key-lease', 'remaining lease', 'remaining key-lease'): + info[k] = v + else: raise UnexpectedCommandOutput(output) @@ -1195,6 +1199,8 @@ class OTCI(object): info[k] = list() if v == '(null)' else list(v.split(',')) elif k in ('port', 'weight', 'priority', 'ttl', 'lease', 'key-lease'): info[k] = int(v) + elif k in ('remaining lease', 'remaining key-lease'): + info[k] = v elif k in ('host',): info[k] = v elif k == 'TXT':