Merge "NAPTR DNS records"

This commit is contained in:
Zuul 2018-11-12 11:13:58 +00:00 committed by Gerrit Code Review
commit bdc828f70e
10 changed files with 211 additions and 4 deletions

View File

@ -62,7 +62,8 @@ rectype2iparectype = {'A': ('arecord', '%(data)s'),
'NS': ('nsrecord', '%(data)s'),
'PTR': ('ptrrecord', '%(data)s'),
'SPF': ('spfrecord', '%(data)s'),
'SSHFP': ('sshfprecord', '%(data)s')}
'SSHFP': ('sshfprecord', '%(data)s'),
'NAPTR': ('naptrrecord', '%(data)s')}
IPA_INVALID_DATA = 3009
IPA_NOT_FOUND = 4001

View File

@ -69,7 +69,7 @@ designate_opts = [
# Supported record types
cfg.ListOpt('supported-record-type', help='Supported record types',
default=['A', 'AAAA', 'CNAME', 'MX', 'SRV', 'TXT', 'SPF', 'NS',
'PTR', 'SSHFP', 'SOA']),
'PTR', 'SSHFP', 'SOA', 'NAPTR']),
]
# Set some Oslo Log defaults

View File

@ -51,6 +51,7 @@ from designate.objects.rrdata_a import A, AList # noqa
from designate.objects.rrdata_aaaa import AAAA, AAAAList # noqa
from designate.objects.rrdata_cname import CNAME, CNAMEList # noqa
from designate.objects.rrdata_mx import MX, MXList # noqa
from designate.objects.rrdata_naptr import NAPTR, NAPTRList # noqa
from designate.objects.rrdata_ns import NS, NSList # noqa
from designate.objects.rrdata_ptr import PTR, PTRList # noqa
from designate.objects.rrdata_soa import SOA, SOAList # noqa

View File

@ -98,6 +98,9 @@ class StringFields(ovoo_fields.StringField):
RE_SSHFP_FINGERPRINT = r'^([0-9A-Fa-f]{10,40}|[0-9A-Fa-f]{64})\Z'
RE_TLDNAME = r'^(?!.{255,})(?:(?!\-)[A-Za-z0-9_\-]{1,63}(?<!\-))' \
r'(?:\.(?:(?!\-)[A-Za-z0-9_\-]{1,63}(?<!\-)))*\Z'
RE_NAPTR_FLAGS = r'^(?!.*(.).*\1)[APSU]+$'
RE_NAPTR_SERVICE = r'^([A-Za-z]([A-Za-z0-9]*)(\+[A-Za-z]([A-Za-z0-9]{0,31}))*)?' # noqa
RE_NAPTR_REGEXP = r'^([^0-9i\\])(.*)\1((.+)|(\\[1-9]))\1(i?)'
def __init__(self, nullable=False, read_only=False,
default=ovoo_fields.UnspecifiedDefault, description='',
@ -291,6 +294,49 @@ class TldField(StringFields):
return value
class NaptrFlagsField(StringFields):
def __init__(self, **kwargs):
super(NaptrFlagsField, self).__init__(**kwargs)
def coerce(self, obj, attr, value):
value = super(NaptrFlagsField, self).coerce(obj, attr, value)
if (len(value) > 255):
raise ValueError("NAPTR record flags field cannot be longer than"
" 255 characters" % value)
if not re.match(self.RE_NAPTR_FLAGS, "%s" % value):
raise ValueError("NAPTR record flags can be S, A, U and P" % value)
return value
class NaptrServiceField(StringFields):
def __init__(self, **kwargs):
super(NaptrServiceField, self).__init__(**kwargs)
def coerce(self, obj, attr, value):
value = super(NaptrServiceField, self).coerce(obj, attr, value)
if (len(value) > 255):
raise ValueError("NAPTR record service field cannot be longer than"
" 255 characters" % value)
if not re.match(self.RE_NAPTR_SERVICE, "%s" % value):
raise ValueError("%s NAPTR record service does not match" % value)
return value
class NaptrRegexpField(StringFields):
def __init__(self, **kwargs):
super(NaptrRegexpField, self).__init__(**kwargs)
def coerce(self, obj, attr, value):
value = super(NaptrRegexpField, self).coerce(obj, attr, value)
if (len(value) > 255):
raise ValueError("NAPTR record regexp field cannot be longer than"
" 255 characters" % value)
if value:
if not re.match(self.RE_NAPTR_REGEXP, "%s" % value):
raise ValueError("%s is not a NAPTR record regexp" % value)
return value
class Any(ovoo_fields.FieldType):
@staticmethod
def coerce(obj, attr, value):

View File

@ -0,0 +1,63 @@
# Copyright 2018 Canonical Ltd.
#
# Author: Tytus Kurek <tytus.kurek@canonical.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from designate.objects.record import Record
from designate.objects.record import RecordList
from designate.objects import base
from designate.objects import fields
@base.DesignateRegistry.register
class NAPTR(Record):
"""
NAPTR Resource Record Type
Defined in: RFC2915
"""
fields = {
'order': fields.IntegerFields(minimum=0, maximum=65535),
'preference': fields.IntegerFields(minimum=0, maximum=65535),
'flags': fields.NaptrFlagsField(),
'service': fields.NaptrServiceField(),
'regexp': fields.NaptrRegexpField(),
'replacement': fields.DomainField(maxLength=255)
}
def _to_string(self):
return ("%(order)s %(preference)s %(flags)s %(service)s %(regexp)s "
"%(replacement)s" % self)
def _from_string(self, v):
order, preference, flags, service, regexp, replacement = v.split(' ')
self.order = int(order)
self.preference = int(preference)
self.flags = flags
self.service = service
self.regexp = regexp
self.replacement = replacement
# The record type is defined in the RFC. This will be used when the record
# is sent by mini-dns.
RECORD_TYPE = 35
@base.DesignateRegistry.register
class NAPTRList(RecordList):
LIST_ITEM_TYPE = NAPTR
fields = {
'objects': fields.ListOfObjectsField('NAPTR'),
}

View File

@ -0,0 +1,44 @@
# Copyright 2018 Canonical Ltd.
#
# Author: Tytus Kurek <tytus.kurek@canonical.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from sqlalchemy import MetaData, Table, Enum
meta = MetaData()
def upgrade(migrate_engine):
meta.bind = migrate_engine
RECORD_TYPES = ['A', 'AAAA', 'CNAME', 'MX', 'SRV', 'TXT', 'SPF', 'NS',
'PTR', 'SSHFP', 'SOA', 'NAPTR']
records_table = Table('recordsets', meta, autoload=True)
records_table.columns.type.alter(name='type', type=Enum(*RECORD_TYPES))
def downgrade(migrate_engine):
meta.bind = migrate_engine
RECORD_TYPES = ['A', 'AAAA', 'CNAME', 'MX', 'SRV', 'TXT', 'SPF', 'NS',
'PTR', 'SSHFP', 'SOA']
records_table = Table('recordsets', meta, autoload=True)
# Delete all NAPTR records
records_table.filter_by(name='type', type='NAPTR').delete()
# Remove CAA from the ENUM
records_table.columns.type.alter(type=Enum(*RECORD_TYPES))

View File

@ -29,7 +29,7 @@ CONF = cfg.CONF
RESOURCE_STATUSES = ['ACTIVE', 'PENDING', 'DELETED', 'ERROR']
RECORD_TYPES = ['A', 'AAAA', 'CNAME', 'MX', 'SRV', 'TXT', 'SPF', 'NS', 'PTR',
'SSHFP', 'SOA']
'SSHFP', 'SOA', 'NAPTR']
TASK_STATUSES = ['ACTIVE', 'PENDING', 'DELETED', 'ERROR', 'COMPLETE']
TSIG_ALGORITHMS = ['hmac-md5', 'hmac-sha1', 'hmac-sha224', 'hmac-sha256',
'hmac-sha384', 'hmac-sha512']

View File

@ -1823,7 +1823,7 @@ class CentralServiceTest(CentralTestCase):
def test_update_recordset_immutable_type(self):
zone = self.create_zone()
# ['A', 'AAAA', 'CNAME', 'MX', 'SRV', 'TXT', 'SPF', 'NS', 'PTR',
# 'SSHFP', 'SOA']
# 'SSHFP', 'SOA', 'NAPTR']
# Create a recordset
recordset = self.create_recordset(zone)
cname_recordset = self.create_recordset(zone, type='CNAME')

View File

@ -0,0 +1,46 @@
# Copyright 2018 Canonical Ltd.
#
# Author: Tytus Kurek <tytus.kurek@canonical.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_log import log as logging
import oslotest.base
from designate import objects
LOG = logging.getLogger(__name__)
def debug(*a, **kw):
for v in a:
LOG.debug(repr(v))
for k in sorted(kw):
LOG.debug("%s: %s", k, repr(kw[k]))
class NAPTRRecordTest(oslotest.base.BaseTestCase):
def test_parse_naptr(self):
naptr_record = objects.NAPTR()
naptr_record._from_string(
'0 0 S SIP+D2U !^.*$!sip:customer-service@example.com! _sip._udp.example.com.') # noqa
self.assertEqual(0, naptr_record.order)
self.assertEqual(0, naptr_record.preference)
self.assertEqual('S', naptr_record.flags)
self.assertEqual('SIP+D2U', naptr_record.service)
self.assertEqual('!^.*$!sip:customer-service@example.com!',
naptr_record.regexp)
self.assertEqual('_sip._udp.example.com.', naptr_record.replacement)

View File

@ -180,3 +180,9 @@ Objects SSHFP Record
:show-inheritance:
Objects NAPTR Record
====================
.. automodule:: designate.objects.rrdata_naptr
:members:
:undoc-members:
:show-inheritance: