-
Notifications
You must be signed in to change notification settings - Fork 1.5k
/
driver.py
173 lines (150 loc) · 7.02 KB
/
driver.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# Copyright (c) 2016 IBM
# All Rights Reserved.
#
# 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.
import netaddr
from designateclient import exceptions as d_exc
from designateclient.v2 import client as d_client
from keystoneauth1.identity.generic import password
from keystoneauth1 import loading
from keystoneauth1 import token_endpoint
from neutron_lib import constants
from oslo_config import cfg
from neutron.conf.services import extdns_designate_driver
from neutron.extensions import dns
from neutron.services.externaldns import driver
IPV4_PTR_ZONE_PREFIX_MIN_SIZE = 8
IPV4_PTR_ZONE_PREFIX_MAX_SIZE = 24
IPV6_PTR_ZONE_PREFIX_MIN_SIZE = 4
IPV6_PTR_ZONE_PREFIX_MAX_SIZE = 124
_SESSION = None
CONF = cfg.CONF
extdns_designate_driver.register_designate_opts()
def get_clients(context):
global _SESSION
if not _SESSION:
_SESSION = loading.load_session_from_conf_options(
CONF, 'designate')
auth = token_endpoint.Token(CONF.designate.url, context.auth_token)
client = d_client.Client(session=_SESSION, auth=auth)
if CONF.designate.auth_type:
admin_auth = loading.load_auth_from_conf_options(
CONF, 'designate')
else:
admin_auth = password.Password(
auth_url=CONF.designate.admin_auth_url,
username=CONF.designate.admin_username,
password=CONF.designate.admin_password,
tenant_name=CONF.designate.admin_tenant_name,
tenant_id=CONF.designate.admin_tenant_id)
admin_client = d_client.Client(session=_SESSION, auth=admin_auth)
return client, admin_client
class Designate(driver.ExternalDNSService):
"""Driver for Designate."""
def __init__(self):
ipv4_ptr_zone_size = CONF.designate.ipv4_ptr_zone_prefix_size
ipv6_ptr_zone_size = CONF.designate.ipv6_ptr_zone_prefix_size
if (ipv4_ptr_zone_size < IPV4_PTR_ZONE_PREFIX_MIN_SIZE or
ipv4_ptr_zone_size > IPV4_PTR_ZONE_PREFIX_MAX_SIZE or
(ipv4_ptr_zone_size % 8) != 0):
raise dns.InvalidPTRZoneConfiguration(
parameter='ipv4_ptr_zone_size', number='8',
maximum=str(IPV4_PTR_ZONE_PREFIX_MAX_SIZE),
minimum=str(IPV4_PTR_ZONE_PREFIX_MIN_SIZE))
if (ipv6_ptr_zone_size < IPV6_PTR_ZONE_PREFIX_MIN_SIZE or
ipv6_ptr_zone_size > IPV6_PTR_ZONE_PREFIX_MAX_SIZE or
(ipv6_ptr_zone_size % 4) != 0):
raise dns.InvalidPTRZoneConfiguration(
parameter='ipv6_ptr_zone_size', number='4',
maximum=str(IPV6_PTR_ZONE_PREFIX_MAX_SIZE),
minimum=str(IPV6_PTR_ZONE_PREFIX_MIN_SIZE))
def create_record_set(self, context, dns_domain, dns_name, records):
designate, designate_admin = get_clients(context)
v4, v6 = self._classify_records(records)
try:
if v4:
designate.recordsets.create(dns_domain, dns_name, 'A', v4)
if v6:
designate.recordsets.create(dns_domain, dns_name, 'AAAA', v6)
except d_exc.NotFound:
raise dns.DNSDomainNotFound(dns_domain=dns_domain)
except d_exc.Conflict:
raise dns.DuplicateRecordSet(dns_name=dns_name)
if not CONF.designate.allow_reverse_dns_lookup:
return
# Set up the PTR records
recordset_name = '%s.%s' % (dns_name, dns_domain)
ptr_zone_email = 'admin@%s' % dns_domain[:-1]
if CONF.designate.ptr_zone_email:
ptr_zone_email = CONF.designate.ptr_zone_email
for record in records:
in_addr_name = netaddr.IPAddress(record).reverse_dns
in_addr_zone_name = self._get_in_addr_zone_name(in_addr_name)
in_addr_zone_description = (
'An %s zone for reverse lookups set up by Neutron.' %
'.'.join(in_addr_name.split('.')[-3:]))
try:
# Since we don't delete in-addr zones, assume it already
# exists. If it doesn't, create it
designate_admin.recordsets.create(in_addr_zone_name,
in_addr_name, 'PTR',
[recordset_name])
except d_exc.NotFound:
designate_admin.zones.create(
in_addr_zone_name, email=ptr_zone_email,
description=in_addr_zone_description)
designate_admin.recordsets.create(in_addr_zone_name,
in_addr_name, 'PTR',
[recordset_name])
def _classify_records(self, records):
v4 = []
v6 = []
for record in records:
if netaddr.IPAddress(record).version == 4:
v4.append(record)
else:
v6.append(record)
return v4, v6
def _get_in_addr_zone_name(self, in_addr_name):
units = self._get_bytes_or_nybles_to_skip(in_addr_name)
return '.'.join(in_addr_name.split('.')[units:])
def _get_bytes_or_nybles_to_skip(self, in_addr_name):
if 'in-addr.arpa' in in_addr_name:
return int((constants.IPv4_BITS -
CONF.designate.ipv4_ptr_zone_prefix_size) / 8)
return int((constants.IPv6_BITS -
CONF.designate.ipv6_ptr_zone_prefix_size) / 4)
def delete_record_set(self, context, dns_domain, dns_name, records):
designate, designate_admin = get_clients(context)
ids_to_delete = self._get_ids_ips_to_delete(
dns_domain, '%s.%s' % (dns_name, dns_domain), records, designate)
for _id in ids_to_delete:
designate.recordsets.delete(dns_domain, _id)
if not CONF.designate.allow_reverse_dns_lookup:
return
for record in records:
in_addr_name = netaddr.IPAddress(record).reverse_dns
in_addr_zone_name = self._get_in_addr_zone_name(in_addr_name)
designate_admin.recordsets.delete(in_addr_zone_name, in_addr_name)
def _get_ids_ips_to_delete(self, dns_domain, name, records,
designate_client):
try:
recordsets = designate_client.recordsets.list(
dns_domain, criterion={"name": "%s" % name})
except d_exc.NotFound:
raise dns.DNSDomainNotFound(dns_domain=dns_domain)
ids = [rec['id'] for rec in recordsets]
ips = [str(ip) for rec in recordsets for ip in rec['records']]
if set(ips) != set(records):
raise dns.DuplicateRecordSet(dns_name=name)
return ids