summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZuul <zuul@review.openstack.org>2018-11-12 11:13:58 +0000
committerGerrit Code Review <review@openstack.org>2018-11-12 11:13:58 +0000
commitbdc828f70ee0f97d10dc7f593e925c0fc3d64fa3 (patch)
tree5bb72d8cefcc993f453a810d5e84739415bec666
parent0e8431cbdfb3f3e821808001488ce8976e600ae8 (diff)
parent44d9c02c7c17dc8fcefc2d091090f8c5055ccdaf (diff)
downloaddesignate-bdc828f70ee0f97d10dc7f593e925c0fc3d64fa3.tar.gz
Merge "NAPTR DNS records"
-rw-r--r--contrib/archive/backends/impl_ipa/__init__.py3
-rw-r--r--designate/__init__.py2
-rw-r--r--designate/objects/__init__.py1
-rw-r--r--designate/objects/fields.py46
-rw-r--r--designate/objects/rrdata_naptr.py63
-rw-r--r--designate/storage/impl_sqlalchemy/migrate_repo/versions/101_support_naptr_records.py44
-rw-r--r--designate/storage/impl_sqlalchemy/tables.py2
-rw-r--r--designate/tests/test_central/test_service.py2
-rw-r--r--designate/tests/test_objects/test_naptr_object.py46
-rw-r--r--doc/source/contributor/sourcedoc/objects.rst6
10 files changed, 211 insertions, 4 deletions
diff --git a/contrib/archive/backends/impl_ipa/__init__.py b/contrib/archive/backends/impl_ipa/__init__.py
index 2e5bc5dd..20d7b954 100644
--- a/contrib/archive/backends/impl_ipa/__init__.py
+++ b/contrib/archive/backends/impl_ipa/__init__.py
@@ -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
diff --git a/designate/__init__.py b/designate/__init__.py
index 91afebd7..0239fe53 100644
--- a/designate/__init__.py
+++ b/designate/__init__.py
@@ -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
diff --git a/designate/objects/__init__.py b/designate/objects/__init__.py
index c1acb072..5176295a 100644
--- a/designate/objects/__init__.py
+++ b/designate/objects/__init__.py
@@ -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
diff --git a/designate/objects/fields.py b/designate/objects/fields.py
index 33d5cd38..39759424 100644
--- a/designate/objects/fields.py
+++ b/designate/objects/fields.py
@@ -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):
diff --git a/designate/objects/rrdata_naptr.py b/designate/objects/rrdata_naptr.py
new file mode 100644
index 00000000..a126f971
--- /dev/null
+++ b/designate/objects/rrdata_naptr.py
@@ -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'),
+ }
diff --git a/designate/storage/impl_sqlalchemy/migrate_repo/versions/101_support_naptr_records.py b/designate/storage/impl_sqlalchemy/migrate_repo/versions/101_support_naptr_records.py
new file mode 100644
index 00000000..4c03e6e8
--- /dev/null
+++ b/designate/storage/impl_sqlalchemy/migrate_repo/versions/101_support_naptr_records.py
@@ -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))
diff --git a/designate/storage/impl_sqlalchemy/tables.py b/designate/storage/impl_sqlalchemy/tables.py
index feca30f3..22bf5118 100644
--- a/designate/storage/impl_sqlalchemy/tables.py
+++ b/designate/storage/impl_sqlalchemy/tables.py
@@ -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']
diff --git a/designate/tests/test_central/test_service.py b/designate/tests/test_central/test_service.py
index 2b0e405c..5828c172 100644
--- a/designate/tests/test_central/test_service.py
+++ b/designate/tests/test_central/test_service.py
@@ -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')
diff --git a/designate/tests/test_objects/test_naptr_object.py b/designate/tests/test_objects/test_naptr_object.py
new file mode 100644
index 00000000..6266f93a
--- /dev/null
+++ b/designate/tests/test_objects/test_naptr_object.py
@@ -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)
diff --git a/doc/source/contributor/sourcedoc/objects.rst b/doc/source/contributor/sourcedoc/objects.rst
index 884c748a..d7c00583 100644
--- a/doc/source/contributor/sourcedoc/objects.rst
+++ b/doc/source/contributor/sourcedoc/objects.rst
@@ -180,3 +180,9 @@ Objects SSHFP Record
:show-inheritance:
+Objects NAPTR Record
+====================
+.. automodule:: designate.objects.rrdata_naptr
+ :members:
+ :undoc-members:
+ :show-inheritance: