Fix AXFR loop when updating secondary zone

When a secondary zone is created or updated we trigger an AXFR,
this AXFR then goes ahead and updates the zone. This then causes
the AXFR code to get triggered again as it thinks the masters
has changed.

We fix this by ensuring that when we update the zone from the AXFR
code that we always reset the masters field.

Partial-Bug: #1856442
Change-Id: Ifc2efb5a6d1091ff16373f3cbe3846f5e4371272
(cherry picked from commit ecaff9e6d3)
This commit is contained in:
Erik Olof Gunnar Andersson 2023-09-21 05:28:45 -07:00 committed by Dr. Jens Harbott
parent 0577a1df72
commit b1ecf814a8
4 changed files with 34 additions and 40 deletions

View File

@ -819,9 +819,7 @@ class Service(service.RPCService):
self.worker_api.create_zone(context, zone)
if zone.type == constants.ZONE_SECONDARY:
xfr_zone = copy.deepcopy(zone)
xfr_zone.obj_reset_changes(recursive=True)
self.worker_api.perform_zone_xfr(context, xfr_zone)
self.worker_api.perform_zone_xfr(context, zone)
# If zone is a superzone, update subzones
# with new parent IDs

View File

@ -1622,42 +1622,6 @@ class CentralZoneTestCase(CentralBasic):
refresh_time = central_service._generate_soa_refresh_interval()
self.assertEqual(3563, refresh_time)
@patch.object(objects.Zone, 'obj_reset_changes')
def test_create_secondary_zone(self, mock_obj_reset_changes):
self.service._enforce_zone_quota = mock.Mock()
self.service._create_zone_in_storage = mock.Mock(
return_value=objects.Zone(name='example.com.', type='SECONDARY'))
self.service._is_valid_zone_name = mock.Mock()
self.service._is_valid_ttl = mock.Mock()
self.service._is_subzone = mock.Mock(return_value=False)
self.service._is_superzone = mock.Mock(return_value=[])
self.service.storage.get_pool.return_value = RoObject(
ns_records=[RoObject()])
self.useFixture(
fixtures.MockPatchObject(
self.service.storage,
'find_pools',
return_value=objects.PoolList.from_list(
[
{'id': '94ccc2c1-d751-44fe-b57f-8894c9f5c842'}
]
)
)
)
output = self.service.create_zone(
self.context,
objects.Zone(
tenant_id='1',
name='example.com.',
ttl=60,
pool_id=CentralZoneTestCase.pool__id,
refresh=0,
type='SECONDARY'
)
)
self.assertEqual('example.com.', output.name)
mock_obj_reset_changes.assert_called_once_with(recursive=True)
class IsSubzoneTestCase(CentralBasic):
def setUp(self):

View File

@ -60,6 +60,38 @@ class TestXfr(oslotest.base.BaseTestCase):
self.assertIn('transferred_at', zone.obj_what_changed())
self.assertNotIn('name', zone.obj_what_changed())
@mock.patch.object(dnsutils, 'do_axfr', mock.Mock())
def test_zone_sync_not_change_masters(self):
recordset = objects.RecordSet(
name='www.example.test.', type='TXT',
records=objects.RecordList(objects=[
objects.Record(data='foo bar'),
])
)
zone = objects.Zone(
id='7592878e-4ade-40de-8b8d-699b871ee6fa',
name='example.com.',
serial=1,
masters=objects.ZoneMasterList.from_list(
[{'host': '127.0.0.1', 'port': 53}, ]
),
type=constants.ZONE_SECONDARY,
recordsets=objects.RecordSetList(objects=[]),
)
self.xfr = worker_zone.ZoneXfr(mock.Mock(), self.context, zone)
self.xfr._central_api = mock.Mock()
with mock.patch.object(dnsutils, 'from_dnspython_zone') as mock_dns:
zone.recordsets = objects.RecordSetList(objects=[recordset])
zone.masters = objects.ZoneMasterList()
mock_dns.return_value = zone
self.xfr()
self.assertIn('transferred_at', zone.obj_what_changed())
self.assertNotIn('masters', zone.obj_what_changed())
@mock.patch.object(dnsutils, 'do_axfr', mock.Mock())
def test_zone_sync_using_list_of_servers(self):
zone = objects.Zone(

View File

@ -183,7 +183,7 @@ class ZoneXfr(base.Task):
self.zone.update(dnsutils.from_dnspython_zone(dnspython_zone))
self.zone.transferred_at = timeutils.utcnow()
self.zone.obj_reset_changes(['name'])
self.zone.obj_reset_changes(['name', 'masters'], recursive=True)
self.central_api.update_zone(
self.context, self.zone, increment_serial=False
)