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
This commit is contained in:
Erik Olof Gunnar Andersson
2023-09-21 05:28:45 -07:00
parent 79aac2b206
commit ecaff9e6d3
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
)