mirror of
https://github.com/kernelkit/infix.git
synced 2026-08-02 22:03:01 +02:00
+10
-2
@@ -3,6 +3,15 @@ Change Log
|
||||
|
||||
All notable changes to the project are documented in this file.
|
||||
|
||||
[v24.12.0][UNRELEASED]
|
||||
-------------------------
|
||||
|
||||
### Changes
|
||||
|
||||
- NTP client status is now availible in YANG.
|
||||
|
||||
### Fixes
|
||||
|
||||
|
||||
[v24.11.1][] - 2024-11-29
|
||||
-------------------------
|
||||
@@ -52,7 +61,6 @@ All notable changes to the project are documented in this file.
|
||||
forwarded, when the underlying ports are simultaneously attached to
|
||||
a VLAN filtering bridge.
|
||||
|
||||
|
||||
[v24.11.0][] - 2024-11-20
|
||||
-------------------------
|
||||
|
||||
@@ -221,7 +229,7 @@ renamed to ease maintenance, more info below.
|
||||
with `custom-phys-address` to allow for constructing more free-form
|
||||
MAC addresses based on the chassis MAC (a.k.a., base MAC) address.
|
||||
For more information, see the YANG model, a few examples are listed in
|
||||
the updated documentation.
|
||||
the updated documentation.
|
||||
The syntax will be automatically updated in the `startup-config` and
|
||||
`factory-config` -- make sure to verify the changes and update any
|
||||
static `factory-config` used for your products
|
||||
|
||||
+29
-35
@@ -24,14 +24,14 @@ available in the system authentication configuration context.
|
||||
```
|
||||
admin@host:/config/> edit system authentication user admin
|
||||
admin@host:/config/system/authentication/user/admin/> change password
|
||||
New password:
|
||||
Retype password:
|
||||
New password:
|
||||
Retype password:
|
||||
admin@host:/config/system/authentication/user/admin/> leave
|
||||
```
|
||||
|
||||
The `change password` command starts an interactive dialogue that asks
|
||||
for the new password, with a confirmation, and then salts and encrypts
|
||||
the password with sha512crypt.
|
||||
the password with sha512crypt.
|
||||
|
||||
It is also possible to use the `set password ...` command. This allows
|
||||
setting an already hashed password. To manually hash a password, use
|
||||
@@ -55,7 +55,7 @@ With SSH keys in place it is possible to disable password login, just
|
||||
remember to verify SSH login and network connectivity before doing so.
|
||||
|
||||
```
|
||||
admin@host:/config/> edit system authentication user admin
|
||||
admin@host:/config/> edit system authentication user admin
|
||||
admin@host:/config/system/authentication/user/admin/> edit authorized-key example@host
|
||||
admin@host:/config/system/authentication/user/admin/authorized-key/example@host/> set algorithm ssh-rsa
|
||||
admin@host:/config/system/authentication/user/admin/authorized-key/example@host/> set key-data AAAAB3NzaC1yc2EAAAADAQABAAABgQC8iBL42yeMBioFay7lty1C4ZDTHcHyo739gc91rTTH8SKvAE4g8Rr97KOz/8PFtOObBrE9G21K7d6UBuPqmd0RUF2CkXXN/eN2PBSHJ50YprRFt/z/304bsBYkDdflKlPDjuSmZ/+OMp4pTsq0R0eNFlX9wcwxEzooIb7VPEdvWE7AYoBRUdf41u3KBHuvjGd1M6QYJtbFLQMMTiVe5IUfyVSZ1RCxEyAB9fR9CBhtVheTVsY3iG0fZc9eCEo89ErDgtGUTJK4Hxt5yCNwI88YaVmkE85cNtw8YwubWQL3/tGZHfbbQ0fynfB4kWNloyRHFr7E1kDxuX5+pbv26EqRdcOVGucNn7hnGU6C1+ejLWdBD7vgsoilFrEaBWF41elJEPKDzpszEijQ9gTrrWeYOQ+x++lvmOdssDu4KvGmj2K/MQTL2jJYrMJ7GDzsUu3XikChRL7zNfS2jYYQLzovboUCgqfPUsVba9hqeX3U67GsJo+hy5MG9RSry4+ucHs=
|
||||
@@ -145,7 +145,7 @@ is committed by issuing the `leave` command.
|
||||
admin@host:/config/> edit system
|
||||
admin@host:/config/system/> set hostname example
|
||||
admin@host:/config/system/> leave
|
||||
admin@host:/>
|
||||
admin@host:/>
|
||||
```
|
||||
|
||||
The hostname is advertised over mDNS-SD in the `.local` domain. If
|
||||
@@ -203,8 +203,8 @@ admin@host:/>
|
||||
|
||||
## NTP Client Configuration
|
||||
|
||||
Below is an example configuration for enabling NTP
|
||||
with a specific server and the `iburst` option for faster initial
|
||||
Below is an example configuration for enabling NTP
|
||||
with a specific server and the `iburst` option for faster initial
|
||||
synchronization.
|
||||
|
||||
```
|
||||
@@ -216,23 +216,37 @@ admin@host:/config/> set system ntp server ntp-pool iburst
|
||||
admin@host:/config/> set system ntp server ntp-pool prefer
|
||||
```
|
||||
|
||||
This configuration enables the NTP client and sets the NTP server to
|
||||
`pool.ntp.org` with the `iburst` and `prefer` options. The `iburst`
|
||||
option ensures faster initial synchronization, and the `prefer` option
|
||||
This configuration enables the NTP client and sets the NTP server to
|
||||
`pool.ntp.org` with the `iburst` and `prefer` options. The `iburst`
|
||||
option ensures faster initial synchronization, and the `prefer` option
|
||||
designates this server as preferred.
|
||||
|
||||
* `prefer false`: The NTP client will choose the best available source
|
||||
based on several factors, such as network delay, stratum, and other
|
||||
based on several factors, such as network delay, stratum, and other
|
||||
metrics (default config).
|
||||
* `prefer true`: The NTP client will try to use the preferred server
|
||||
* `prefer true`: The NTP client will try to use the preferred server
|
||||
as the primary source unless it becomes unreachable or unusable.
|
||||
|
||||
### Show NTP Sources
|
||||
|
||||
The status for NTP sources is availble in YANG and accessable with
|
||||
CLI/NETCONF/RESTCONF.
|
||||
|
||||
To view the sources being used by the NTP client, run:
|
||||
```
|
||||
admin@target:/> show ntp
|
||||
ADDRESS MODE STATE STRATUM POLL-INTERVAL
|
||||
192.168.1.1 server candidate 1 6
|
||||
192.168.2.1 server candidate 1 6
|
||||
192.168.3.1 server selected 1 6
|
||||
```
|
||||
|
||||
### Show NTP Status
|
||||
To check the status of NTP synchronization (only availble in CLI), use the following command:
|
||||
|
||||
To check the status of NTP synchronization, use the following command:
|
||||
|
||||
```
|
||||
admin@host:/> show ntp
|
||||
admin@host:/> show ntp tracking
|
||||
Reference ID : C0248F86 (192.36.143.134)
|
||||
Stratum : 2
|
||||
Ref time (UTC) : Mon Oct 21 10:06:45 2024
|
||||
@@ -249,29 +263,9 @@ Leap status : Normal
|
||||
admin@host:/>
|
||||
```
|
||||
|
||||
This output provides detailed information about the NTP status, including
|
||||
This output provides detailed information about the NTP status, including
|
||||
reference ID, stratum, time offsets, frequency, and root delay.
|
||||
|
||||
### Show NTP Sources
|
||||
|
||||
To view the sources being used by the NTP client, run:
|
||||
|
||||
```
|
||||
admin@host:/> show ntp sources
|
||||
|
||||
.-- Source mode '^' = server, '=' = peer, '#' = local clock.
|
||||
/ .- Source state '*' = current best, '+' = combined, '-' = not combined,
|
||||
| / 'x' = may be in error, '~' = too variable, '?' = unusable.
|
||||
|| .- xxxx [ yyyy ] +/- zzzz
|
||||
|| Reachability register (octal) -. | xxxx = adjusted offset,
|
||||
|| Log2(Polling interval) --. | | yyyy = measured offset,
|
||||
|| \ | | zzzz = estimated error.
|
||||
|| | | \
|
||||
MS Name/IP address Stratum Poll Reach LastRx Last sample
|
||||
===============================================================================
|
||||
^* 192.36.143.134 1 6 177 9 +278ms[ -3845s] +/- 514ms
|
||||
admin@host:/>
|
||||
```
|
||||
|
||||
> The system uses `chronyd` for Network Time Protocol (NTP)
|
||||
> synchronization. The output shown here is best explained in the
|
||||
|
||||
@@ -32,7 +32,7 @@ MODULES=(
|
||||
"infix-lldp@2023-08-23.yang"
|
||||
"infix-dhcp-client@2024-09-20.yang"
|
||||
"infix-meta@2024-10-18.yang"
|
||||
"infix-system@2024-09-13.yang"
|
||||
"infix-system@2024-11-27.yang"
|
||||
"infix-services@2024-05-30.yang"
|
||||
"ieee802-ethernet-interface@2019-06-21.yang"
|
||||
"infix-ethernet-interface@2024-02-27.yang"
|
||||
|
||||
@@ -10,12 +10,22 @@ module infix-system {
|
||||
prefix iana-tz;
|
||||
}
|
||||
|
||||
import ietf-inet-types {
|
||||
prefix inet;
|
||||
reference
|
||||
"RFC 6991: Common YANG Data Types";
|
||||
}
|
||||
|
||||
include infix-system-software;
|
||||
|
||||
organization "KernelKit";
|
||||
contact "kernelkit@googlegroups.com";
|
||||
description "Infix augments and deviations to ietf-system.";
|
||||
|
||||
revision 2024-11-27 {
|
||||
description "Add NTP status";
|
||||
reference "internal";
|
||||
}
|
||||
revision 2024-09-13 {
|
||||
description "Add some informative help about different shells and security.";
|
||||
reference "internal";
|
||||
@@ -115,6 +125,42 @@ module infix-system {
|
||||
base shell-type;
|
||||
}
|
||||
|
||||
typedef source-state {
|
||||
reference "rfc5905: Network Time Protocol Version 4: Protocol and Algorithms Specification";
|
||||
type enumeration {
|
||||
enum selected {
|
||||
description "Selected NTP server for synchronization";
|
||||
}
|
||||
enum candidate {
|
||||
description "Valid for synchronization, is used together with thes elected best server and also acting as backup";
|
||||
}
|
||||
enum outlier {
|
||||
description "Valid timesource but discarded by the clustering algorithm";
|
||||
}
|
||||
enum unusable {
|
||||
description "Unreachable or unresponsable";
|
||||
}
|
||||
enum falseticker {
|
||||
description "suspected of providing incorrect time";
|
||||
}
|
||||
enum unstable {
|
||||
description "Too variable or unstable source";
|
||||
}
|
||||
}
|
||||
}
|
||||
typedef source-mode {
|
||||
type enumeration {
|
||||
enum server {
|
||||
description "Normal NTP server";
|
||||
}
|
||||
enum peer {
|
||||
description "Symmetric peer";
|
||||
}
|
||||
enum local-clock {
|
||||
description "Reference clock";
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Typedefs
|
||||
*/
|
||||
@@ -256,6 +302,37 @@ module infix-system {
|
||||
}
|
||||
}
|
||||
|
||||
augment "/sys:system-state" {
|
||||
container ntp {
|
||||
description "NTP status";
|
||||
container sources {
|
||||
list source {
|
||||
key address;
|
||||
leaf address {
|
||||
type inet:ip-address;
|
||||
description "Address to NTP server";
|
||||
}
|
||||
leaf mode {
|
||||
type source-mode;
|
||||
description "Source mode";
|
||||
}
|
||||
leaf state {
|
||||
type source-state;
|
||||
description "Source state";
|
||||
}
|
||||
leaf stratum {
|
||||
type uint8;
|
||||
description "Stratum of NTP server";
|
||||
}
|
||||
leaf poll {
|
||||
type uint8;
|
||||
description "Interval between transmitted NTP messages, expressed as a power of two in seconds";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
deviation "/sys:system/sys:hostname" {
|
||||
deviate replace {
|
||||
type infix-sys:hostname;
|
||||
|
||||
@@ -279,10 +279,14 @@
|
||||
</COMMAND>
|
||||
|
||||
<COMMAND name="ntp" help="Show NTP (client) status">
|
||||
<ACTION sym="script">doas ntp tracking</ACTION>
|
||||
<ACTION sym="script">
|
||||
sysrepocfg -d operational -X -f json -x \
|
||||
/ietf-system:system-state/infix-system:ntp | \
|
||||
/usr/libexec/statd/cli-pretty "show-ntp"
|
||||
</ACTION>
|
||||
<SWITCH name="optional" min="0">
|
||||
<COMMAND name="sources" help="Show NTP sources">
|
||||
<ACTION sym="script">doas ntp sources</ACTION>
|
||||
<COMMAND name="tracking" help="Show NTP tracking">
|
||||
<ACTION sym="script">doas ntp tracking</ACTION>
|
||||
</COMMAND>
|
||||
</SWITCH>
|
||||
</COMMAND>
|
||||
|
||||
@@ -54,6 +54,12 @@ class PadUsbPort:
|
||||
name = 20
|
||||
state = 10
|
||||
|
||||
class PadNtpSource:
|
||||
address = 16
|
||||
mode = 13
|
||||
state = 13
|
||||
stratum = 11
|
||||
poll = 14
|
||||
|
||||
class Decore():
|
||||
@staticmethod
|
||||
@@ -719,6 +725,26 @@ def show_hardware(json):
|
||||
port = USBport(component)
|
||||
port.print()
|
||||
|
||||
def show_ntp(json):
|
||||
if not json.get("ietf-system:system-state"):
|
||||
print(f"Error, top level \"ietf-system:system-state\" missing")
|
||||
sys.exit(1)
|
||||
hdr = (f"{'ADDRESS':<{PadNtpSource.address}}"
|
||||
f"{'MODE':<{PadNtpSource.mode}}"
|
||||
f"{'STATE':<{PadNtpSource.state}}"
|
||||
f"{'STRATUM':>{PadNtpSource.stratum}}"
|
||||
f"{'POLL-INTERVAL':>{PadNtpSource.poll}}"
|
||||
)
|
||||
print(Decore.invert(hdr))
|
||||
sources = get_json_data({}, json, 'ietf-system:system-state', 'infix-system:ntp', 'sources', 'source')
|
||||
for source in sources:
|
||||
row = f"{source['address']:<{PadNtpSource.address}}"
|
||||
row += f"{source['mode']:<{PadNtpSource.mode}}"
|
||||
row += f"{source['state'] if source['state'] != 'not-combined' else 'not combined':<{PadNtpSource.state}}"
|
||||
row += f"{source['stratum']:>{PadNtpSource.stratum}}"
|
||||
row += f"{source['poll']:>{PadNtpSource.poll}}"
|
||||
print(row)
|
||||
|
||||
def main():
|
||||
global UNIT_TEST
|
||||
|
||||
@@ -749,6 +775,8 @@ def main():
|
||||
|
||||
parser_show_routing_table = subparsers.add_parser('show-hardware', help='Show USB ports')
|
||||
|
||||
parser_show_ntp_sources = subparsers.add_parser('show-ntp', help='Show NTP sources')
|
||||
|
||||
args = parser.parse_args()
|
||||
UNIT_TEST = args.test
|
||||
|
||||
@@ -762,6 +790,8 @@ def main():
|
||||
show_bridge_mdb(json_data)
|
||||
elif args.command == "show-hardware":
|
||||
show_hardware(json_data)
|
||||
elif args.command == "show-ntp":
|
||||
show_ntp(json_data)
|
||||
else:
|
||||
print(f"Error, unknown command {args.command}")
|
||||
sys.exit(1)
|
||||
|
||||
@@ -1053,6 +1053,36 @@ def add_interface(ifname, yang_ifaces):
|
||||
|
||||
add_container_ifaces(yang_ifaces)
|
||||
|
||||
def add_system_ntp(out):
|
||||
data = run_cmd(["chronyc", "-c", "sources"], "chronyc-sources.txt", "")
|
||||
source = []
|
||||
state_mode_map = {
|
||||
"^": "server",
|
||||
"=": "peer",
|
||||
"#": "local-clock"
|
||||
}
|
||||
source_state_map = {
|
||||
"*": "selected",
|
||||
"+": "candidate",
|
||||
"-": "outlier",
|
||||
"?": "unusable",
|
||||
"x": "falseticker",
|
||||
"~": "unstable"
|
||||
}
|
||||
for line in data:
|
||||
src = {}
|
||||
line = line.split(',')
|
||||
src["address"] = line[2]
|
||||
src["mode"] = state_mode_map[line[0]]
|
||||
src["state"] = source_state_map[line[1]]
|
||||
src["stratum"] = int(line[3])
|
||||
src["poll"] = int(line[4])
|
||||
source.append(src)
|
||||
|
||||
insert(out, "infix-system:ntp", "sources", "source", source)
|
||||
def add_system(yang_data):
|
||||
add_system_ntp(yang_data)
|
||||
|
||||
def main():
|
||||
global TESTPATH
|
||||
global logger
|
||||
@@ -1133,6 +1163,12 @@ def main():
|
||||
}
|
||||
add_container(yang_data['infix-containers:containers']['container'])
|
||||
|
||||
elif args.model == 'ietf-system':
|
||||
yang_data = {
|
||||
"ietf-system:system-state": {
|
||||
}
|
||||
}
|
||||
add_system(yang_data['ietf-system:system-state'])
|
||||
else:
|
||||
logger.warning(f"Unsupported model {args.model}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#define XPATH_ROUTING_BASE "/ietf-routing:routing/control-plane-protocols/control-plane-protocol"
|
||||
#define XPATH_ROUTING_TABLE "/ietf-routing:routing/ribs"
|
||||
#define XPATH_HARDWARE_BASE "/ietf-hardware:hardware"
|
||||
#define XPATH_SYSTEM_BASE "/ietf-system:system-state"
|
||||
#define XPATH_ROUTING_OSPF XPATH_ROUTING_BASE "/ospf"
|
||||
#define XPATH_CONTAIN_BASE "/infix-containers:containers"
|
||||
|
||||
@@ -340,6 +341,8 @@ static int subscribe_to_all(struct statd *statd)
|
||||
return SR_ERR_INTERNAL;
|
||||
if (subscribe(statd, "ietf-hardware", XPATH_HARDWARE_BASE, sr_generic_cb))
|
||||
return SR_ERR_INTERNAL;
|
||||
if (subscribe(statd, "ietf-system", XPATH_SYSTEM_BASE, sr_generic_cb))
|
||||
return SR_ERR_INTERNAL;
|
||||
#ifdef CONTAINERS
|
||||
if (subscribe(statd, "infix-containers", XPATH_CONTAIN_BASE, sr_generic_cb))
|
||||
return SR_ERR_INTERNAL;
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
[7mADDRESS MODE STATE STRATUM POLL-INTERVAL[0m
|
||||
185.125.190.56 peer candidate 2 8
|
||||
185.125.190.58 server unstable 2 7
|
||||
185.125.190.57 server falseticker 2 8
|
||||
91.189.91.157 local-clock outlier 2 6
|
||||
192.121.108.100 server candidate 3 8
|
||||
85.24.237.72 server selected 2 8
|
||||
162.159.200.1 server unusable 3 7
|
||||
85.24.237.71 server candidate 2 8
|
||||
+13
-1
@@ -59,6 +59,8 @@ if [ $# -eq 2 ] && [ $1 = "update" ]; then
|
||||
"$SR_EMULATOR_TOOL" | "$CLI_PRETTY_TOOL" "-t" "show-routing-table" -i "ipv6" > "$CLI_OUTPUT_PATH/show-routes-ipv6.txt"
|
||||
elif [ $2 = "show-bridge-mdb" ]; then
|
||||
"$SR_EMULATOR_TOOL" | "$CLI_PRETTY_TOOL" "show-bridge-mdb" > "$CLI_OUTPUT_PATH/show-bridge-mdb.txt"
|
||||
elif [ $2 = "show-ntp" ]; then
|
||||
"$SR_EMULATOR_TOOL" | "$CLI_PRETTY_TOOL" "show-ntp" > "$CLI_OUTPUT_PATH/show-ntp.txt"
|
||||
else
|
||||
echo "Unsupported cli-pretty command $2"
|
||||
exit 1
|
||||
@@ -67,7 +69,7 @@ if [ $# -eq 2 ] && [ $1 = "update" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "1..10"
|
||||
echo "1..11"
|
||||
echo "# Running:"
|
||||
|
||||
# Show interfaces
|
||||
@@ -89,6 +91,16 @@ if ! diff -u "$CLI_OUTPUT_PATH/show-bridge-mdb.txt" "$CLI_OUTPUT_FILE"; then
|
||||
fi
|
||||
ok "\"show bridge mdb\" output looks intact"
|
||||
|
||||
# Show NTP
|
||||
echo "# $SR_EMULATOR_TOOL | $CLI_PRETTY_TOOL show-ntp"
|
||||
"$SR_EMULATOR_TOOL" | "$CLI_PRETTY_TOOL" "show-ntp" > "$CLI_OUTPUT_FILE"
|
||||
|
||||
if ! diff -u "$CLI_OUTPUT_PATH/show-ntp.txt" "$CLI_OUTPUT_FILE"; then
|
||||
print_update_txt
|
||||
fail "\"show ntp\" output has changed"
|
||||
fi
|
||||
ok "\"show ntp\" output looks intact"
|
||||
|
||||
# Show ipv4 routes
|
||||
echo "# $SR_EMULATOR_TOOL | $CLI_PRETTY_TOOL -t show-routing-table -i ipv4"
|
||||
"$SR_EMULATOR_TOOL" | "$CLI_PRETTY_TOOL" "-t" "show-routing-table" -i "ipv4" > "$CLI_OUTPUT_FILE"
|
||||
|
||||
@@ -7,9 +7,11 @@ YANGER_TOOL="$ROOT_PATH/src/statd/python/yanger/yanger.py"
|
||||
|
||||
INTERFACES_OUTPUT_FILE="$(mktemp)"
|
||||
ROUTES_OUTPUT_FILE="$(mktemp)"
|
||||
SYSTEM_OUTPUT_FILE="$(mktemp)"
|
||||
cleanup() {
|
||||
rm -f "$INTERFACES_OUTPUT_FILE"
|
||||
rm -f "$ROUTES_OUTPUT_FILE"
|
||||
rm -f "$SYSTEM_OUTPUT_FILE"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
@@ -26,5 +28,7 @@ fi
|
||||
|
||||
$YANGER_TOOL "ietf-routing" -t "$SCRIPT_PATH/system-output/" > "$ROUTES_OUTPUT_FILE"
|
||||
|
||||
$YANGER_TOOL "ietf-system" -t "$SCRIPT_PATH/system-output/" > "$SYSTEM_OUTPUT_FILE"
|
||||
|
||||
# Merge all module files
|
||||
jq -s '.[0] * .[1]' $ROUTES_OUTPUT_FILE $INTERFACES_OUTPUT_FILE
|
||||
jq -s '.[0] * .[1] * .[2]' $ROUTES_OUTPUT_FILE $INTERFACES_OUTPUT_FILE $SYSTEM_OUTPUT_FILE
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
=,+,185.125.190.56,2,8,377,166,0.005469983,0.005822986,0.033335429
|
||||
^,~,185.125.190.58,2,7,377,37,0.003233160,0.003233160,0.028441129
|
||||
^,x,185.125.190.57,2,8,377,167,-0.000418652,-0.000065722,0.027703809
|
||||
#,-,91.189.91.157,2,6,377,488,0.000970410,0.001490889,0.077348366
|
||||
^,+,192.121.108.100,3,8,377,101,0.005757879,0.006114979,0.024731735
|
||||
^,*,85.24.237.72,2,8,377,37,0.006444688,0.006805896,0.017862743
|
||||
^,?,162.159.200.1,3,7,377,34,-0.000447366,-0.000447366,0.009887429
|
||||
^,+,85.24.237.71,2,8,377,169,0.003875532,0.004228305,0.015323908
|
||||
@@ -16,3 +16,5 @@ include::timezone_utc_offset/Readme.adoc[]
|
||||
include::ssh_key_authentication/Readme.adoc[]
|
||||
|
||||
include::upgrade/Readme.adoc[]
|
||||
|
||||
include::ntp_client/Readme.adoc[]
|
||||
|
||||
@@ -16,3 +16,6 @@
|
||||
|
||||
- name: ssh_key_authentication
|
||||
case: ssh_key_authentication/test.py
|
||||
|
||||
- name: ntp_client
|
||||
case: ntp_client/test.py
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
=== Basic NTP client test
|
||||
==== Description
|
||||
Verify NTP client with multiple servers, ensure one get selected.
|
||||
|
||||
==== Topology
|
||||
ifdef::topdoc[]
|
||||
image::../../test/case/ietf_system/ntp_client/topology.svg[Basic NTP client test topology]
|
||||
endif::topdoc[]
|
||||
ifndef::topdoc[]
|
||||
ifdef::testgroup[]
|
||||
image::ntp_client/topology.svg[Basic NTP client test topology]
|
||||
endif::testgroup[]
|
||||
ifndef::testgroup[]
|
||||
image::topology.svg[Basic NTP client test topology]
|
||||
endif::testgroup[]
|
||||
endif::topdoc[]
|
||||
==== Test sequence
|
||||
. Set up topology and attach to target DUT
|
||||
. Configure NTP client on 'target'
|
||||
. Verify one source is in 'selected' state on 'target'
|
||||
. Verify three sources exist in NTP client on 'target'
|
||||
|
||||
|
||||
<<<
|
||||
|
||||
Executable
+109
@@ -0,0 +1,109 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Basic NTP client test
|
||||
|
||||
Verify NTP client with multiple servers, ensure one get selected.
|
||||
"""
|
||||
|
||||
import infamy
|
||||
import infamy.ntp_server as ntp_server
|
||||
import infamy.ntp as ntp
|
||||
import infamy.util as util
|
||||
def config_target(dut, data1, data2, data3):
|
||||
dut.put_config_dicts({
|
||||
"ietf-interfaces": {
|
||||
"interfaces": {
|
||||
"interface": [
|
||||
{
|
||||
"name": data1,
|
||||
"enabled": True,
|
||||
"ipv4": {
|
||||
"address": [{
|
||||
"ip": "192.168.1.2",
|
||||
"prefix-length": 24
|
||||
}]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": data2,
|
||||
"enabled": True,
|
||||
"ipv4": {
|
||||
"address": [{
|
||||
"ip": "192.168.2.2",
|
||||
"prefix-length": 24
|
||||
}]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": data3,
|
||||
"enabled": True,
|
||||
"ipv4": {
|
||||
"address": [{
|
||||
|
||||
"ip": "192.168.3.2",
|
||||
"prefix-length": 24
|
||||
}]
|
||||
}
|
||||
}]
|
||||
}
|
||||
},
|
||||
"ietf-system": {
|
||||
"system": {
|
||||
"ntp": {
|
||||
"enabled": True,
|
||||
"server": [{
|
||||
"name": "Server1",
|
||||
"udp": {
|
||||
"address": "192.168.1.1"
|
||||
},
|
||||
"iburst": True
|
||||
},{
|
||||
"name": "Server2",
|
||||
"udp": {
|
||||
"address": "192.168.2.1"
|
||||
},
|
||||
"iburst": True
|
||||
},{
|
||||
"name": "Server3",
|
||||
"udp": {
|
||||
"address": "192.168.3.1"
|
||||
},
|
||||
"iburst": True
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
with infamy.Test() as test:
|
||||
with test.step("Set up topology and attach to target DUT"):
|
||||
env = infamy.Env()
|
||||
target = env.attach("target", "mgmt")
|
||||
|
||||
with test.step("Configure NTP client on 'target'"):
|
||||
_, data1 = env.ltop.xlate("target", "data1")
|
||||
_, data2 = env.ltop.xlate("target", "data2")
|
||||
_, data3 = env.ltop.xlate("target", "data3")
|
||||
|
||||
config_target(target, data1, data2, data3)
|
||||
|
||||
_, hport1 = env.ltop.xlate("host", "data1")
|
||||
_, hport2 = env.ltop.xlate("host", "data2")
|
||||
_, hport3 = env.ltop.xlate("host", "data3")
|
||||
|
||||
with infamy.IsolatedMacVlan(hport1) as ns1, \
|
||||
infamy.IsolatedMacVlan(hport2) as ns2, \
|
||||
infamy.IsolatedMacVlan(hport3) as ns3:
|
||||
ns1.addip("192.168.1.1")
|
||||
ns2.addip("192.168.2.1")
|
||||
ns3.addip("192.168.3.1")
|
||||
|
||||
with ntp_server.Server(ns1) as ntp1, \
|
||||
ntp_server.Server(ns2) as ntp2, \
|
||||
ntp_server.Server(ns3) as ntp3:
|
||||
with test.step("Verify one source is in 'selected' state on 'target'"):
|
||||
util.until(lambda: ntp.any_source_selected(target), attempts=200)
|
||||
with test.step("Verify three sources exist in NTP client on 'target'"):
|
||||
assert(ntp.number_of_sources(target) == 3)
|
||||
|
||||
test.succeed()
|
||||
@@ -0,0 +1 @@
|
||||
../../../infamy/topologies/1x4.dot
|
||||
@@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
|
||||
<!-- Title: 1x4 Pages: 1 -->
|
||||
<svg width="440pt" height="101pt"
|
||||
viewBox="0.00 0.00 440.03 101.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 97)">
|
||||
<title>1x4</title>
|
||||
<polygon fill="white" stroke="transparent" points="-4,4 -4,-97 436.03,-97 436.03,4 -4,4"/>
|
||||
<!-- host -->
|
||||
<g id="node1" class="node">
|
||||
<title>host</title>
|
||||
<polygon fill="none" stroke="black" points="0,-0.5 0,-92.5 108,-92.5 108,-0.5 0,-0.5"/>
|
||||
<text text-anchor="middle" x="25" y="-42.8" font-family="DejaVu Sans Mono, Book" font-size="14.00">host</text>
|
||||
<polyline fill="none" stroke="black" points="50,-0.5 50,-92.5 "/>
|
||||
<text text-anchor="middle" x="79" y="-77.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
|
||||
<polyline fill="none" stroke="black" points="50,-69.5 108,-69.5 "/>
|
||||
<text text-anchor="middle" x="79" y="-54.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">data1</text>
|
||||
<polyline fill="none" stroke="black" points="50,-46.5 108,-46.5 "/>
|
||||
<text text-anchor="middle" x="79" y="-31.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">data2</text>
|
||||
<polyline fill="none" stroke="black" points="50,-23.5 108,-23.5 "/>
|
||||
<text text-anchor="middle" x="79" y="-8.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">data3</text>
|
||||
</g>
|
||||
<!-- target -->
|
||||
<g id="node2" class="node">
|
||||
<title>target</title>
|
||||
<polygon fill="none" stroke="black" points="308.03,-0.5 308.03,-92.5 432.03,-92.5 432.03,-0.5 308.03,-0.5"/>
|
||||
<text text-anchor="middle" x="337.03" y="-77.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
|
||||
<polyline fill="none" stroke="black" points="308.03,-69.5 366.03,-69.5 "/>
|
||||
<text text-anchor="middle" x="337.03" y="-54.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">data1</text>
|
||||
<polyline fill="none" stroke="black" points="308.03,-46.5 366.03,-46.5 "/>
|
||||
<text text-anchor="middle" x="337.03" y="-31.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">data2</text>
|
||||
<polyline fill="none" stroke="black" points="308.03,-23.5 366.03,-23.5 "/>
|
||||
<text text-anchor="middle" x="337.03" y="-8.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">data3</text>
|
||||
<polyline fill="none" stroke="black" points="366.03,-0.5 366.03,-92.5 "/>
|
||||
<text text-anchor="middle" x="399.03" y="-42.8" font-family="DejaVu Sans Mono, Book" font-size="14.00">target</text>
|
||||
</g>
|
||||
<!-- host--target -->
|
||||
<g id="edge1" class="edge">
|
||||
<title>host:mgmt--target:mgmt</title>
|
||||
<path fill="none" stroke="cornflowerblue" stroke-width="2" d="M108,-81.5C108,-81.5 308.03,-81.5 308.03,-81.5"/>
|
||||
</g>
|
||||
<!-- host--target -->
|
||||
<g id="edge2" class="edge">
|
||||
<title>host:data1--target:data1</title>
|
||||
<path fill="none" stroke="black" stroke-width="2" d="M108,-58.5C108,-58.5 308.03,-58.5 308.03,-58.5"/>
|
||||
</g>
|
||||
<!-- host--target -->
|
||||
<g id="edge3" class="edge">
|
||||
<title>host:data2--target:data2</title>
|
||||
<path fill="none" stroke="black" stroke-width="2" d="M108,-34.5C108,-34.5 308.03,-34.5 308.03,-34.5"/>
|
||||
</g>
|
||||
<!-- host--target -->
|
||||
<g id="edge4" class="edge">
|
||||
<title>host:data3--target:data3</title>
|
||||
<path fill="none" stroke="black" stroke-width="2" d="M108,-11.5C108,-11.5 308.03,-11.5 308.03,-11.5"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
@@ -0,0 +1,38 @@
|
||||
"""
|
||||
NTP client helper
|
||||
"""
|
||||
|
||||
|
||||
def _get_ntp(target):
|
||||
xpath = "/ietf-system:system-state/infix-system:ntp"
|
||||
data = target.get_data(xpath)
|
||||
|
||||
if data is None:
|
||||
return None
|
||||
|
||||
return data["system-state"].get("infix-system:ntp", None) or data["system-state"].get("ntp", None)
|
||||
|
||||
|
||||
def _get_ntp_sources(target):
|
||||
ntp = _get_ntp(target)
|
||||
|
||||
if ntp is None:
|
||||
return []
|
||||
|
||||
return ntp["sources"]["source"]
|
||||
|
||||
|
||||
def any_source_selected(target):
|
||||
sources = _get_ntp_sources(target)
|
||||
|
||||
for source in sources:
|
||||
if source["state"] == "selected":
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def number_of_sources(target):
|
||||
sources = _get_ntp_sources(target)
|
||||
|
||||
return len(sources)
|
||||
@@ -0,0 +1,24 @@
|
||||
"""Start NTP server in the background"""
|
||||
import subprocess
|
||||
|
||||
class Server:
|
||||
def __init__(self, netns, iface="iface"):
|
||||
self.iface = iface
|
||||
self.process = None
|
||||
self.netns = netns
|
||||
|
||||
def __enter__(self):
|
||||
self.start()
|
||||
|
||||
def __exit__(self, _, __, ___):
|
||||
self.stop()
|
||||
|
||||
def start(self):
|
||||
cmd=f"ntpd -w -n -l -I {self.iface}"
|
||||
self.process = self.netns.popen(cmd.split(" "),stderr=subprocess.DEVNULL)
|
||||
|
||||
def stop(self):
|
||||
if self.process:
|
||||
self.process.terminate()
|
||||
self.process.wait()
|
||||
self.process = None
|
||||
@@ -7,20 +7,20 @@ graph "1x4" {
|
||||
edge [color="cornflowerblue", penwidth="2", fontname="DejaVu Serif, Book"];
|
||||
|
||||
host [
|
||||
label="host | { <tgt> tgt | <data0> data0 | <data1> data1 | <data2> data2 }",
|
||||
label="host | { <mgmt> mgmt | <data1> data1 | <data2> data2 | <data3> data3 }",
|
||||
pos="0,12!",
|
||||
kind="controller",
|
||||
];
|
||||
|
||||
target [
|
||||
label="{ <mgmt> mgmt | <data0> data0 | <data1> data1 | <data2> data2 } | target",
|
||||
label="{ <mgmt> mgmt | <data1> data1 | <data2> data2 | <data3> data3 } | target",
|
||||
pos="10,12!",
|
||||
|
||||
kind="infix",
|
||||
];
|
||||
|
||||
host:tgt -- target:mgmt [kind=mgmt]
|
||||
host:data0 -- target:data0 [color=black]
|
||||
host:mgmt -- target:mgmt [kind=mgmt]
|
||||
host:data1 -- target:data1 [color=black]
|
||||
host:data2 -- target:data2 [color=black]
|
||||
host:data3 -- target:data3 [color=black]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user