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)\