diff --git a/package/confd/confd.mk b/package/confd/confd.mk index 64bb2d91..caa806fb 100644 --- a/package/confd/confd.mk +++ b/package/confd/confd.mk @@ -4,7 +4,7 @@ # ################################################################################ -CONFD_VERSION = 1.3 +CONFD_VERSION = 1.4 CONFD_SITE_METHOD = local CONFD_SITE = $(BR2_EXTERNAL_INFIX_PATH)/src/confd CONFD_LICENSE = BSD-3-Clause diff --git a/src/confd/configure.ac b/src/confd/configure.ac index 1e4aceeb..ec6e01c1 100644 --- a/src/confd/configure.ac +++ b/src/confd/configure.ac @@ -1,6 +1,6 @@ AC_PREREQ(2.61) # confd version is same as system YANG model version, step on breaking changes -AC_INIT([confd], [1.3], [https://github.com/kernelkit/infix/issues]) +AC_INIT([confd], [1.4], [https://github.com/kernelkit/infix/issues]) AM_INIT_AUTOMAKE(1.11 foreign subdir-objects) AM_SILENT_RULES(yes) @@ -17,6 +17,7 @@ AC_CONFIG_FILES([ share/migrate/1.1/Makefile share/migrate/1.2/Makefile share/migrate/1.3/Makefile + share/migrate/1.4/Makefile src/Makefile yang/Makefile ]) diff --git a/src/confd/share/migrate/1.4/10-dhcp-client-option-name.sh b/src/confd/share/migrate/1.4/10-dhcp-client-option-name.sh new file mode 100755 index 00000000..5b1d4cb3 --- /dev/null +++ b/src/confd/share/migrate/1.4/10-dhcp-client-option-name.sh @@ -0,0 +1,15 @@ +#!/bin/sh +# Rename DHCP client option attribute 'name' -> 'id' to make DHCP server. + +file=$1 +temp=${file}.tmp + +jq '( + .["infix-dhcp-client:dhcp-client"]?."client-if"[]?.option[]? |= + if has("name") then + { "id": .name } + . | del(.name) + else + . + end +)' "$file" > "$temp" && + mv "$temp" "$file" diff --git a/src/confd/share/migrate/1.4/Makefile.am b/src/confd/share/migrate/1.4/Makefile.am new file mode 100644 index 00000000..9c4e0ec2 --- /dev/null +++ b/src/confd/share/migrate/1.4/Makefile.am @@ -0,0 +1,2 @@ +migratedir = $(pkgdatadir)/migrate/1.4 +dist_migrate_DATA = 10-dhcp-client-option-name.sh diff --git a/src/confd/share/migrate/Makefile.am b/src/confd/share/migrate/Makefile.am index 6fb10b5e..8c546823 100644 --- a/src/confd/share/migrate/Makefile.am +++ b/src/confd/share/migrate/Makefile.am @@ -1,2 +1,2 @@ -SUBDIRS = 1.0 1.1 1.2 1.3 +SUBDIRS = 1.0 1.1 1.2 1.3 1.4 migratedir = $(pkgdatadir)/migrate diff --git a/src/confd/src/infix-dhcp-client.c b/src/confd/src/infix-dhcp-client.c index 4f6ee22f..3710cabf 100644 --- a/src/confd/src/infix-dhcp-client.c +++ b/src/confd/src/infix-dhcp-client.c @@ -102,12 +102,12 @@ static char *os_name_version(char *str, size_t len) return str; } -static char *compose_option(const char *ifname, const char *name, const char *value, +static char *compose_option(const char *ifname, const char *id, const char *value, char *option, size_t len) { if (value) { - if (isdigit(name[0])) { - unsigned long opt = strtoul(name, NULL, 0); + if (isdigit(id[0])) { + unsigned long opt = strtoul(id, NULL, 0); switch (opt) { case 81: @@ -116,25 +116,25 @@ static char *compose_option(const char *ifname, const char *name, const char *va break; } - snprintf(option, len, "-x %s:%s ", name, value); + snprintf(option, len, "-x %s:%s ", id, value); } else { - if (!strcmp(name, "fqdn")) + if (!strcmp(id, "fqdn")) fqdn(value, option, len); - else if (!strcmp(name, "hostname")) - snprintf(option, len, "-x %s:%s ", name, value); + else if (!strcmp(id, "hostname")) + snprintf(option, len, "-x %s:%s ", id, value); else - snprintf(option, len, "-x %s:'\"%s\"' ", name, value); + snprintf(option, len, "-x %s:'\"%s\"' ", id, value); } } else { - struct { char *name; char *(*cb)(const char *, char *, size_t); } opt[] = { + struct { char *id; char *(*cb)(const char *, char *, size_t); } opt[] = { { "hostname", hostname }, { "address", ip_cache }, { "fqdn", NULL }, { NULL, NULL } }; - for (size_t i = 0; opt[i].name; i++) { - if (strcmp(name, opt[i].name)) + for (size_t i = 0; opt[i].id; i++) { + if (strcmp(id, opt[i].id)) continue; if (!opt[i].cb || !opt[i].cb(ifname, option, len)) @@ -143,17 +143,17 @@ static char *compose_option(const char *ifname, const char *name, const char *va return option; } - snprintf(option, len, "-O %s ", name); + snprintf(option, len, "-O %s ", id); } return option; } -static char *compose_options(const char *ifname, char **options, const char *name, const char *value) +static char *compose_options(const char *ifname, char **options, const char *id, const char *value) { char opt[300]; - compose_option(ifname, name, value, opt, sizeof(opt)); + compose_option(ifname, id, value, opt, sizeof(opt)); if (*options) { char *opts; @@ -177,10 +177,10 @@ static char *dhcp_options(const char *ifname, struct lyd_node *cfg) char *options = NULL; LYX_LIST_FOR_EACH(lyd_child(cfg), option, "option") { - const char *value = lydx_get_cattr(option, "value"); - const char *name = lydx_get_cattr(option, "name"); + const char *id = lydx_get_cattr(option, "id"); + const char *val = lydx_get_cattr(option, "value"); - options = compose_options(ifname, &options, name, value); + options = compose_options(ifname, &options, id, val); } if (!options) { @@ -339,7 +339,7 @@ static void infer_options(sr_session_ctx_t *session, const char *xpath) size_t i; for (i = 0; i < NELEMS(opt); i++) - srx_set_item(session, NULL, 0, "%s/option[name='%s']", xpath, opt[i]); + srx_set_item(session, NULL, 0, "%s/option[id='%s']", xpath, opt[i]); } static int cand(sr_session_ctx_t *session, uint32_t sub_id, const char *module, diff --git a/src/confd/yang/confd.inc b/src/confd/yang/confd.inc index 19567ee7..82cdb925 100644 --- a/src/confd/yang/confd.inc +++ b/src/confd/yang/confd.inc @@ -30,7 +30,7 @@ MODULES=( "infix-routing@2024-11-27.yang" "ieee802-dot1ab-lldp@2022-03-15.yang" "infix-lldp@2025-01-08.yang" - "infix-dhcp-client@2024-09-20.yang" + "infix-dhcp-client@2025-01-20.yang" "infix-dhcp-server@2024-08-18.yang" "infix-meta@2024-10-18.yang" "infix-system@2024-11-27.yang" diff --git a/src/confd/yang/infix-dhcp-client.yang b/src/confd/yang/infix-dhcp-client.yang index f5749f93..67f2fa63 100644 --- a/src/confd/yang/infix-dhcp-client.yang +++ b/src/confd/yang/infix-dhcp-client.yang @@ -10,6 +10,10 @@ module infix-dhcp-client { contact "kernelkit@googlegroups.com"; description "This module implements an IPv4 DHCP client"; + revision 2025-01-20 { + description "Rename option 'name' -> 'id' and rename internal type."; + reference "internal"; + } revision 2024-09-20 { description "Routes are installed in Frr (staticd), clarify preference vs metric and adjust default preference 100 -> 5."; @@ -133,13 +137,13 @@ module infix-dhcp-client { description "ARP for lease to check for IP address collisions (slow)."; } list option { - key "name"; + key "id"; description "List of DHCP options to request (and accept). The default is an empty list, meaning all supported options. To restrict the client to only get IP address and default route, set this to: 'subnet router'"; - leaf name { + leaf id { type dhcp-client-options; description "DHCP option to request from, or inform server of."; } @@ -149,7 +153,7 @@ module infix-dhcp-client { Example: option:hostname, value:xyzzy option:clientid, value:01:02:03:04:05:06:07:08:09:0a option:0x51, value:xyzzy.example.com"; - must "../name != 'hostname' or re-match(., '[a-zA-Z0-9\\-_]{1,64}')"; + must "../id != 'hostname' or re-match(., '[a-zA-Z0-9\\-_]{1,64}')"; } } leaf route-preference { diff --git a/src/confd/yang/infix-dhcp-client@2024-09-20.yang b/src/confd/yang/infix-dhcp-client@2025-01-20.yang similarity index 100% rename from src/confd/yang/infix-dhcp-client@2024-09-20.yang rename to src/confd/yang/infix-dhcp-client@2025-01-20.yang diff --git a/test/case/ietf_routing/route_pref_dhcp/test.py b/test/case/ietf_routing/route_pref_dhcp/test.py index e044715d..c6b4eacf 100755 --- a/test/case/ietf_routing/route_pref_dhcp/test.py +++ b/test/case/ietf_routing/route_pref_dhcp/test.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 """ -Route preference: DHCP vs Static +Route preference: DHCP vs Static This test configures a device with both a DHCP-acquired route on a dedicated interface and a static route to the same destination on @@ -70,13 +70,13 @@ def config_target1(target, data1, data2, link): "if-name": data1, "enabled": True, "option": [ - {"name": "broadcast"}, - {"name": "dns"}, - {"name": "domain"}, - {"name": "hostname"}, - {"name": "ntpsrv"}, - {"name": "router"}, - {"name": "subnet"} + {"id": "broadcast"}, + {"id": "dns"}, + {"id": "domain"}, + {"id": "hostname"}, + {"id": "ntpsrv"}, + {"id": "router"}, + {"id": "subnet"} ], "route-preference": 5 } diff --git a/test/case/infix_dhcp/dhcp_basic/test.py b/test/case/infix_dhcp/dhcp_basic/test.py index 0c2a37c8..afe0a391 100755 --- a/test/case/infix_dhcp/dhcp_basic/test.py +++ b/test/case/infix_dhcp/dhcp_basic/test.py @@ -1,10 +1,10 @@ #!/usr/bin/env python3 -""" -DHCP Basic +"""DHCP Basic This is a very basic DHCP test that requests an IPv4 lease from a DHCP server and checks that the lease is set on the interface. + """ import infamy, infamy.dhcp @@ -13,6 +13,7 @@ from infamy.util import until with infamy.Test() as test: ADDRESS = '10.0.0.42' + with test.step("Initialize"): env = infamy.Env() client = env.attach("client", "mgmt") @@ -31,7 +32,7 @@ with infamy.Test() as test: } client.put_config_dict("infix-dhcp-client", config) - with test.step("Verify client get DHCP lease for 10.0.0.42 on client:data"): + with test.step("Verify client lease for 10.0.0.42"): until(lambda: iface.address_exist(client, port, ADDRESS)) test.succeed() diff --git a/test/case/infix_dhcp/dhcp_router/test.py b/test/case/infix_dhcp/dhcp_router/test.py index e64763c4..df574dbe 100755 --- a/test/case/infix_dhcp/dhcp_router/test.py +++ b/test/case/infix_dhcp/dhcp_router/test.py @@ -1,9 +1,8 @@ #!/usr/bin/env python3 -""" -DHCP router +"""DHCP router -Verify that the DHCP client receives default gateway (DHCP option 3, router) -and that route exists in operational datastore. +Verify that the DHCP client receives default gateway (DHCP option 3, +router) and that route exists in operational datastore. """ import infamy, infamy.dhcp @@ -11,7 +10,8 @@ import infamy.route as route from infamy.util import until with infamy.Test() as test: - ROUTER = '192.168.0.254' + ROUTER = '192.168.0.254' + with test.step("Initialize"): env = infamy.Env() client = env.attach("client", "mgmt") @@ -26,14 +26,14 @@ with infamy.Test() as test: "client-if": [{ "if-name": f"{port}", "option": [ - { "name": "router" } + {"id": "router"} ] }] } } client.put_config_dict("infix-dhcp-client", config) - with test.step("Verify client to set up default route via 192.168.0.254"): + with test.step("Verify client has default route via 192.168.0.254"): until(lambda: route.ipv4_route_exist(client, "0.0.0.0/0", ROUTER)) test.succeed() diff --git a/test/case/infix_dhcp/dhcp_routes/test.py b/test/case/infix_dhcp/dhcp_routes/test.py index 394f9a8e..26b25e99 100755 --- a/test/case/infix_dhcp/dhcp_routes/test.py +++ b/test/case/infix_dhcp/dhcp_routes/test.py @@ -64,8 +64,8 @@ with infamy.Test() as test: "client-if": [{ "if-name": f"{port}", "option": [ - {"name": "router"}, - {"name": "staticroutes"} + {"id": "router"}, + {"id": "staticroutes"} ] }] } @@ -75,17 +75,17 @@ with infamy.Test() as test: netns.addip("192.168.0.1") print(f"Start DHCP server {ROUTER} with option 3 and 121") with infamy.dhcp.Server(netns, prefix=PREFIX, router=ROUTER): - with test.step("Verify 'client' has a route 10.0.0.0/24 via 192.168.0.254 (option 121)"): + with test.step("Verify client has route 10.0.0.0/24 via 192.168.0.254 (option 121)"): print("Verify client use classless routes, option 121") until(lambda: route.ipv4_route_exist(client, PREFIX, ROUTER)) - with test.step("Verify 'client' has default route via 192.168.0.254 (not use option 3)"): + with test.step("Verify client has default route via 192.168.0.254 (not use option 3)"): print("Verify client did *not* use option 3") if route.ipv4_route_exist(client, "0.0.0.0/0", ROUTER): test.fail() - with test.step("Verify 'client' still has canary route to 20.0.0.0/24 via 192.168.0.2"): + with test.step("Verify client still has canary route to 20.0.0.0/24 via 192.168.0.2"): until(lambda: route.ipv4_route_exist(client, CANARY, - CANHOP, pref=250)) + CANHOP, pref=250)) test.succeed()