From c6008c7bd08c85491806a830b874852748b85ba7 Mon Sep 17 00:00:00 2001 From: Thomas Mieslinger Date: Tue, 13 Oct 2026 12:35:27 +0200 Subject: [PATCH] fix(ndcli): accept layer3domain for show rr / modify rr "modify rr NAME a IP" and "show rr NAME a IP" had no layer3domain argument and ignored NDCLI_LAYER3DOMAIN / layer3domain= in ~/.ndclirc. When IP exists in more than one layer3domain the server cannot pick one and answered "A layer3domain is needed" with no way for the user to supply it. They now take "layer3domain NAME" and the configured default, exactly like "create rr" and "delete rr". The server drops layer3domain after using it to resolve the ipblock, as rr_delete() already does, since it is not part of the record value. ITOUDP-8041 Co-Authored-By: Claude Opus 5.5 --- dim-testsuite/t/layer3domain-rr-modify.t | 76 ++++++++++++++++++++++++ dim/CHANGES | 1 + dim/dim/rpc.py | 2 + ndcli/CHANGES | 1 + ndcli/dimcli/__init__.py | 9 +++ 5 files changed, 89 insertions(+) create mode 100644 dim-testsuite/t/layer3domain-rr-modify.t diff --git a/dim-testsuite/t/layer3domain-rr-modify.t b/dim-testsuite/t/layer3domain-rr-modify.t new file mode 100644 index 0000000..c40dc2d --- /dev/null +++ b/dim-testsuite/t/layer3domain-rr-modify.t @@ -0,0 +1,76 @@ +# ITOUDP-8041: "show rr" and "modify rr" for A/AAAA/PTR must accept a +# layer3domain like "create rr" and "delete rr" do, including the default from +# NDCLI_LAYER3DOMAIN / layer3domain= in ~/.ndclirc. +$ ndcli create layer3domain two type vrf rd 0:2 +$ ndcli create zone a.de +WARNING - Creating zone a.de without profile +WARNING - Primary NS for this Domain is now localhost. +$ ndcli create rr a.de. a 10.0.0.1 layer3domain default +INFO - Marked IP 10.0.0.1 from layer3domain default as static +INFO - Creating RR @ A 10.0.0.1 in zone a.de +INFO - No zone found for 1.0.0.10.in-addr.arpa. +WARNING - No reverse zone found. Only creating forward entry. +$ ndcli create rr b.a.de. a 10.0.0.1 layer3domain two --allow-overlap +WARNING - 10.0.0.1 in layer3domain two overlaps with 10.0.0.1 in layer3domain default +INFO - Marked IP 10.0.0.1 from layer3domain two as static +INFO - Creating RR b A 10.0.0.1 in zone a.de +INFO - No zone found for 1.0.0.10.in-addr.arpa. +WARNING - No reverse zone found. Only creating forward entry. + +# 10.0.0.1 exists in two layer3domains: without one the address is ambiguous, +# exactly as for "delete rr" +$ ndcli modify rr a.de. a 10.0.0.1 --ttl 60 +ERROR - A layer3domain is needed +$ ndcli show rr a.de. a 10.0.0.1 +ERROR - A layer3domain is needed + +$ ndcli modify rr a.de. a 10.0.0.1 layer3domain default --ttl 60 +$ ndcli show rr a.de. a 10.0.0.1 layer3domain default +created:2026-10-13 12:00:00 +created_by:admin +modified:2026-10-13 12:00:00 +modified_by:admin +rr:@ 60 A 10.0.0.1 +ttl:60 +zone:a.de + +$ ndcli modify rr b.a.de. a 10.0.0.1 layer3domain two -c "in two" +comment:in two +created:2026-10-13 12:00:00 +created_by:admin +modified:2026-10-13 12:00:00 +modified_by:admin +rr:b A 10.0.0.1 +zone:a.de + +# the layer3domain must exist +$ ndcli modify rr a.de. a 10.0.0.1 layer3domain nosuch --ttl 60 +ERROR - Layer3domain 'nosuch' does not exist + +# the reporter's case: layer3domain only set in the environment / ~/.ndclirc +# (shell lines can not carry expected output in this harness, so check with show) +$ NDCLI_CONFIG=/dev/null NDCLI_LAYER3DOMAIN=default ndcli modify rr a.de. a 10.0.0.1 --ttl 120 +$ ndcli show rr a.de. a 10.0.0.1 layer3domain default +created:2026-10-13 12:00:00 +created_by:admin +modified:2026-10-13 12:00:00 +modified_by:admin +rr:@ 120 A 10.0.0.1 +ttl:120 +zone:a.de + +# addresses that exist only once still need no layer3domain +$ ndcli create rr c.a.de. a 10.0.0.3 layer3domain default +INFO - Marked IP 10.0.0.3 from layer3domain default as static +INFO - Creating RR c A 10.0.0.3 in zone a.de +INFO - No zone found for 3.0.0.10.in-addr.arpa. +WARNING - No reverse zone found. Only creating forward entry. +$ ndcli modify rr c.a.de. a 10.0.0.3 --ttl 300 +$ ndcli show rr c.a.de. a 10.0.0.3 +created:2026-10-13 12:00:00 +created_by:admin +modified:2026-10-13 12:00:00 +modified_by:admin +rr:c 300 A 10.0.0.3 +ttl:300 +zone:a.de diff --git a/dim/CHANGES b/dim/CHANGES index 0bb36d3..923b173 100644 --- a/dim/CHANGES +++ b/dim/CHANGES @@ -1,5 +1,6 @@ to be released ------ +* accept `layer3domain` in rr_get_attrs / rr_set_attrs / rr_set_comment for A, AAAA and PTR records (ITOUDP-8041) * added ldap pseudo user sync to manage_dim (introduces is_pseudo column, LDAP_PSEUDO_USER_BASE, LDAP_BIND_DN, and LDAP_BIND_PASSWORD configurations) * added automated database schema mismatch verification check on application startup * reject attribute names containing colons during deletion to prevent silent failure from incorrect key-value input diff --git a/dim/dim/rpc.py b/dim/dim/rpc.py index 6a3f454..d2b2b18 100644 --- a/dim/dim/rpc.py +++ b/dim/dim/rpc.py @@ -3421,6 +3421,8 @@ def _get_rr_helper(name, view=None, type=None, **kwargs): if 'type' in kwargs: self._check_rr_parameters(kwargs, creating=False) self._parse_ip_from_rr_parameters(kwargs, zone=None, creating=False) + # layer3domain was only needed to find the ipblock, it is not part of the rr value + kwargs.pop('layer3domain', None) if kwargs['type'] == 'PTR' and 'ip' in kwargs: kwargs['name'] = dim.dns.get_ptr_name(kwargs['ip']) del kwargs['ip'] diff --git a/ndcli/CHANGES b/ndcli/CHANGES index dd11299..db85639 100644 --- a/ndcli/CHANGES +++ b/ndcli/CHANGES @@ -1,5 +1,6 @@ unreleased ---------- +* `show rr` and `modify rr` for A, AAAA and PTR accept `layer3domain` and use the default from NDCLI_LAYER3DOMAIN / ~/.ndclirc like `create rr` and `delete rr` (ITOUDP-8041) * add support for ALIAS resource records (create, delete, modify, show) * add support for DNAME resource records (create, delete, modify, show, and zone import) * add tree view and filtering rendering mode to `list containers` command (`-T`/`--tree`, `-p`/`--pattern`, `-c`/`--case-sensitive`, `--no-color`) diff --git a/ndcli/dimcli/__init__.py b/ndcli/dimcli/__init__.py index b65a650..fe5994b 100644 --- a/ndcli/dimcli/__init__.py +++ b/ndcli/dimcli/__init__.py @@ -650,6 +650,8 @@ def show_rr(self, args): options = OptionDict() options.set_if(type=rr_type, view=args.get('view', None)) + if rr_type in ('A', 'AAAA', 'PTR'): + options.set_if(layer3domain=get_layer3domain(args.get('layer3domain', None))) _fill_rr_options(options, rr_type, params, args) _print_attributes(self.client.rr_get_attrs(**options), script=False) @@ -662,6 +664,8 @@ def modify_rr(self, args): options.set_if(type=rr_type, view=args.view, comment=args.comment) + if rr_type in ('A', 'AAAA', 'PTR'): + options.set_if(layer3domain=get_layer3domain(args.get('layer3domain', None))) if args.ttl is not None: if args.ttl.lower() == 'default': args.ttl = None @@ -2406,6 +2410,11 @@ def modify_zone_dnssec_new_zsk(self, args): delete_arguments = properties.get('delete_arguments', properties['arguments']) delete_rr_options = [Group(*delete_arguments, **{'nargs': '?', 'stop_at': 'view'}), rr_view_group] show_rr_options = [Group(*delete_arguments, **{'nargs': '?', 'stop_at': 'view'}), rr_single_view_group] + if rr in ('a', 'aaaa', 'ptr'): + # the address may exist in more than one layer3domain, like for "delete rr" + show_rr_options = [Group(*delete_arguments, **{'nargs': '?', 'stop_at': ['layer3domain', 'view']}), + rr_single_view_group, + layer3domain_group] cmd.register('modify zone create rr ' + rr, description=create_rr_description, *modify_zone_create_rr_options)\