Removed deprecated v1 api dashboard

The v1 api support was removed from the designate client in Train,
and because of that we can no longer support the v1 api in
the Designate dashboard and add release-note-job to
designate-dashboard.

Change-Id: Iabf7d396ea6feb0cd7de1c5408a81a7a3ce1efbf
This commit is contained in:
Erik Olof Gunnar Andersson 2019-09-22 14:30:02 -07:00 committed by Vishal Manchanda
parent 9fecedf4df
commit fe68f0f1bc
34 changed files with 5 additions and 2311 deletions

View File

@ -1 +0,0 @@
from designatedashboard.api import designate # noqa

View File

@ -1,171 +0,0 @@
# Copyright 2013 Hewlett-Packard Development Company, L.P.
#
# 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 __future__ import absolute_import
from designateclient.v1 import Client # noqa
from designateclient.v1.domains import Domain # noqa
from designateclient.v1.records import Record # noqa
from django.conf import settings # noqa
from horizon import exceptions
from openstack_dashboard.api.base import url_for # noqa
from oslo_log import log as logging
LOG = logging.getLogger(__name__)
def designateclient(request):
designate_url = ""
try:
designate_url = url_for(request, 'dns')
except exceptions.ServiceCatalogException:
LOG.debug('no dns service configured.')
return None
insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False)
cacert = getattr(settings, 'OPENSTACK_SSL_CACERT', None)
return Client(endpoint=designate_url,
token=request.user.token.id,
username=request.user.username,
tenant_id=request.user.project_id,
insecure=insecure,
cacert=cacert)
def domain_get(request, domain_id):
d_client = designateclient(request)
if d_client is None:
return []
return d_client.domains.get(domain_id)
def domain_list(request):
d_client = designateclient(request)
if d_client is None:
return []
return d_client.domains.list()
def domain_create(request, name, email, ttl=None, description=None):
d_client = designateclient(request)
if d_client is None:
return None
options = {
'description': description,
}
# TTL needs to be optionally added as argument because the client
# won't accept a None value
if ttl is not None:
options['ttl'] = ttl
domain = Domain(name=name, email=email, **options)
return d_client.domains.create(domain)
def domain_update(request, domain_id, email, ttl, description=None):
d_client = designateclient(request)
if d_client is None:
return None
# A quirk of the designate client is that you need to start with a
# base record and then update individual fields in order to persist
# the data. The designate client will only send the 'changed' fields.
domain = Domain(id=domain_id, name='', email='')
domain.email = email
domain.ttl = ttl
domain.description = description
return d_client.domains.update(domain)
def domain_delete(request, domain_id):
d_client = designateclient(request)
if d_client is None:
return []
return d_client.domains.delete(domain_id)
def server_list(request, domain_id):
d_client = designateclient(request)
if d_client is None:
return []
return d_client.domains.list_domain_servers(domain_id)
def record_list(request, domain_id):
d_client = designateclient(request)
if d_client is None:
return []
return d_client.records.list(domain_id)
def record_get(request, domain_id, record_id):
d_client = designateclient(request)
if d_client is None:
return []
return d_client.records.get(domain_id, record_id)
def record_delete(request, domain_id, record_id):
d_client = designateclient(request)
if d_client is None:
return []
return d_client.records.delete(domain_id, record_id)
def record_create(request, domain_id, **kwargs):
d_client = designateclient(request)
if d_client is None:
return []
record = Record(**kwargs)
return d_client.records.create(domain_id, record)
def record_update(request, domain_id, record_id, **kwargs):
d_client = designateclient(request)
if d_client is None:
return []
# A quirk of the designate client is that you need to start with a
# base record and then update individual fields in order to persist
# the data. The designate client will only send the 'changed' fields.
record = Record(
id=record_id,
type='A',
name='',
data='')
record.type = kwargs.get('type', None)
record.name = kwargs.get('name', None)
record.data = kwargs.get('data', None)
record.priority = kwargs.get('priority', None)
record.ttl = kwargs.get('ttl', None)
record.description = kwargs.get('description', None)
return d_client.records.update(domain_id, record)
def quota_get(request, project_id=None):
if not project_id:
project_id = request.user.project_id
d_client = designateclient(request)
return d_client.quotas.get(project_id)

View File

@ -1,549 +0,0 @@
# Copyright 2013 Hewlett-Packard Development Company, L.P.
#
# 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 functools
import re
import six
from designateclient import exceptions as designate_exceptions
from django.core.exceptions import ValidationError # noqa
from django.core import validators
from django.template.loader import render_to_string
from django.utils.translation import ugettext_lazy as _ # noqa
from horizon import forms
from horizon import messages
from designatedashboard import api
from designatedashboard.dashboards.project.dns_domains.utils\
import limit_records_to_fips
from oslo_log import log as logging
LOG = logging.getLogger(__name__)
MAX_TTL = 2147483647
# These regexes were given to me by Kiall Mac Innes here:
# https://gerrit.hpcloud.net/#/c/25300/2/
DOMAIN_NAME_REGEX = r'^(?!.{255,})(?:(?!\-)[A-Za-z0-9_\-]{1,63}(?<!\-)\.)+$'
WILDCARD_DOMAIN_NAME_REGEX = r'^(?!.{255,})(?:(^\*|(?!\-)[A-Za-z0-9_\-]{1,63})(?<!\-)\.)+$' # noqa
SRV_NAME_REGEX = r'^(?:_[A-Za-z0-9_\-]{1,62}\.){2}'
SRV_DATA_REGEX = r'^(?:(?:6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}|[1-5][0-9]{4}|[1-9][0-9]{1,3}|[0-9])\s){2}(?!.{255,})((?!\-)[A-Za-z0-9_\-]{1,63}(?<!\-)\.)+$' # noqa
SSHFP_DATA_REGEX = r'^[1-4]\s[1-2]\s\b([0-9a-fA-F]{5,40}|[0-9a-fA-F]{64})\b$'
# The max length for a dns label
NAME_MAX_LENGTH = 63
def handle_exc(func):
@functools.wraps(func)
def wrapped(form, request, *args, **kwargs):
try:
return func(form, request, *args, **kwargs)
except designate_exceptions.RemoteError as ex:
msg = ""
data = {}
if six.text_type(ex) is not None:
data['message'] = six.text_type(ex)
msg += "Error: %(message)s"
else:
data["type"] = ex.type
msg += "Error Type: %(type)s"
if ex.code >= 500:
msg += " (Request ID: %(request_id)s"
data["request_id"] = ex.request_id
form.api_error(_(msg) % data) # noqa
return False
except Exception:
messages.error(request, form.exc_message)
return True
return wrapped
class DomainForm(forms.SelfHandlingForm):
'''Base class for DomainCreate and DomainUpdate forms.
Sets-up all of the common form fields.
'''
name = forms.RegexField(
label=_("Domain Name"),
regex=DOMAIN_NAME_REGEX,
error_messages={'invalid': _('Enter a valid domain name.')},
)
email = forms.EmailField(
label=_("Email"),
max_length=255,
)
ttl = forms.IntegerField(
label=_("TTL (seconds)"),
min_value=1,
max_value=MAX_TTL,
required=False,
)
description = forms.CharField(
label=_("Description"),
required=False,
max_length=160,
widget=forms.Textarea(),
)
class DomainCreate(DomainForm):
'''Form for creating new domain records.
Name and email address are
required.
'''
exc_message = _("Unable to create domain.")
@handle_exc
def handle(self, request, data):
domain = api.designate.domain_create(
request,
name=data['name'],
email=data['email'],
ttl=data['ttl'],
description=data['description'])
messages.success(request,
_('Domain %(name)s created.') %
{"name": domain.name})
return domain
class DomainUpdate(DomainForm):
'''Form for displaying domain record details and updating them.'''
exc_message = _('Unable to update domain.')
id = forms.CharField(
required=False,
widget=forms.HiddenInput()
)
serial = forms.CharField(
label=_("Serial"),
required=False,
widget=forms.TextInput(attrs={'readonly': 'readonly'}),
)
created_at = forms.CharField(
label=_("Created At"),
required=False,
widget=forms.TextInput(attrs={'readonly': 'readonly'}),
)
updated_at = forms.CharField(
label=_("Updated At"),
required=False,
widget=forms.TextInput(attrs={'readonly': 'readonly'}),
)
def __init__(self, request, *args, **kwargs):
super(DomainUpdate, self).__init__(request, *args, **kwargs)
# Mark name as read-only
self.fields['name'].required = False
self.fields['name'].widget.attrs['readonly'] = 'readonly'
self.fields['ttl'].required = True
# Customize display order for fields
self.fields.keyOrder = [
'id',
'name',
'serial',
'email',
'ttl',
'description',
'created_at',
'updated_at',
]
@handle_exc
def handle(self, request, data):
domain = api.designate.domain_update(
request,
domain_id=data['id'],
email=data['email'],
ttl=data['ttl'],
description=data['description'])
messages.success(request,
_('Domain %(name)s updated.') %
{"name": domain.name})
return domain
class PrefixWidget(forms.TextInput):
def render(self, name, value, attrs=None):
template_name = 'project/dns_domains/prefix_html_widget.html'
result = super(PrefixWidget, self).render(name, value, attrs)
view_data = {'input': result,
'suffix': getattr(self, "suffix", '')}
return render_to_string(template_name, view_data)
class RecordForm(forms.SelfHandlingForm):
'''Base class for RecordCreate and RecordUpdate forms.
Sets-up all of
the form fields and implements the complex validation logic.
'''
domain_id = forms.CharField(
widget=forms.HiddenInput())
domain_name = forms.CharField(
widget=forms.HiddenInput())
type = forms.ChoiceField(
label=_("Record Type"),
required=False,
choices=[
('a', _('A - Address record')),
('aaaa', _('AAAA - IPv6 address record')),
('cname', _('CNAME - Canonical name record')),
('mx', _('MX - Mail exchange record')),
('ptr', _('PTR - Pointer record')),
('spf', _('SPF - Sender Policy Framework')),
('srv', _('SRV - Service locator')),
('sshfp', _('SSHFP - SSH Public Key Fingerprint')),
('txt', _('TXT - Text record')),
],
widget=forms.Select(attrs={
'class': 'switchable',
'data-slug': 'record_type',
}),
)
name = forms.CharField(
required=False,
widget=PrefixWidget(attrs={
'class': 'switched',
'data-switch-on': 'record_type',
'data-record_type-a': _('Name'),
'data-record_type-aaaa': _('Name'),
'data-record_type-cname': _('Name'),
'data-record_type-mx': _('Name'),
'data-record_type-ns': _('Name'),
'data-record_type-ptr': _('Name'),
'data-record_type-soa': _('Name'),
'data-record_type-spf': _('Name'),
'data-record_type-srv': _('Name'),
'data-record_type-sshfp': _('Name'),
'data-record_type-txt': _('Name'),
}),
)
data = forms.CharField(
required=False,
widget=forms.TextInput(attrs={
'class': 'switched',
'data-switch-on': 'record_type',
'data-record_type-a': _('IP Address'),
'data-record_type-aaaa': _('IP Address'),
'data-record_type-cname': _('Canonical Name'),
'data-record_type-ns': _('Name Server'),
'data-record_type-mx': _('Mail Server'),
'data-record_type-ptr': _('PTR Domain Name'),
'data-record_type-soa': _('Value'),
'data-record_type-srv': _('Value'),
}),
)
ip_addr = forms.ChoiceField(
required=False,
widget=forms.Select(attrs={
'class': 'switched',
'data-switch-on': 'record_type',
'data-record_type-a': _('IP Address'),
'data-record_type-aaaa': _('IP Address'),
}),
)
txt = forms.CharField(
label=_('TXT'),
required=False,
widget=forms.Textarea(attrs={
'class': 'switched',
'data-switch-on': 'record_type',
'data-record_type-txt': _('Text'),
'data-record_type-spf': _('Text'),
'data-record_type-sshfp': _('Text'),
}),
)
priority = forms.IntegerField(
min_value=0,
max_value=65535,
required=False,
widget=forms.TextInput(attrs={
'class': 'switched',
'data-switch-on': 'record_type',
'data-record_type-mx': _('Priority'),
'data-record_type-srv': _('Priority'),
}),
)
ttl = forms.IntegerField(
label=_('TTL'),
min_value=1,
max_value=MAX_TTL,
required=False,
widget=forms.TextInput(attrs={
'class': 'switched',
'data-switch-on': 'record_type',
'data-record_type-a': _('TTL'),
'data-record_type-aaaa': _('TTL'),
'data-record_type-cname': _('TTL'),
'data-record_type-mx': _('TTL'),
'data-record_type-ptr': _('TTL'),
'data-record_type-soa': _('TTL'),
'data-record_type-spf': _('TTL'),
'data-record_type-srv': _('TTL'),
'data-record_type-sshfp': _('TTL'),
'data-record_type-txt': _('TTL'),
}),
)
description = forms.CharField(
label=_("Description"),
required=False,
max_length=160,
widget=forms.Textarea(),
)
def __init__(self, request, *args, **kwargs):
super(RecordForm, self).__init__(request, *args, **kwargs)
initial = kwargs.get('initial', {})
domain_suffix = "." + initial['domain_name']
self.fields['name'].widget.suffix = domain_suffix
self.fields['name'].max_length = min(NAME_MAX_LENGTH,
255 - len(domain_suffix))
if limit_records_to_fips():
del self.fields['data'].widget.attrs['data-record_type-a']
del self.fields['data'].widget.attrs['data-record_type-aaaa']
self.fields['ip_addr'].choices = \
self.populate_ip_addr_choices(request,
initial)
else:
del self.fields['ip_addr']
def _generate_fip_list(self, fips, instances):
instance_dict = {instance.id: instance for instance in instances}
for fip in fips:
instance_name = _("Unknown instance name")
if getattr(fip, "instance_id", "None") in instance_dict:
instance_name = instance_dict[getattr(fip, "instance_id")].name
yield (fip.ip, "%s (%s)" % (fip.ip, instance_name))
def populate_ip_addr_choices(self, request, initial):
results = [(None, _('Select an IP')), ]
if (initial.get('ip_addr') and
initial['ip_addr'] not in [fip.ip for fip in initial['fips']]):
"""The record is currently using an ip not in the list
of fips - this can happen when instance goes away or in
multi region setups
"""
results.append((initial['ip_addr'], initial['ip_addr']))
results.extend(self._generate_fip_list(initial['fips'],
initial['instances']))
if len(results) == 1:
messages.warning(request, _("There are no floating IP addresses "
"currently in use to select from."))
return results
def clean_type(self):
'''Type value needs to be uppercased before it is sent to the API.'''
return self.cleaned_data['type'].upper()
def clean(self):
'''Handles the validation logic for the domain record form.
Validation gets pretty complicated due to the fact that the different
record types (A, AAAA, MX, etc) have different requirements for
each of the fields.
'''
cleaned_data = super(RecordForm, self).clean()
record_type = cleaned_data['type']
domain_name = cleaned_data['domain_name']
if limit_records_to_fips():
ip_addr = cleaned_data.pop('ip_addr')
if (record_type in ['AAAA', 'A'] and limit_records_to_fips()):
cleaned_data['data'] = ip_addr
# Name field
if self._is_field_blank(cleaned_data, 'name'):
if record_type in ['CNAME', 'SRV']:
self._add_required_field_error('name')
elif record_type in ['MX', 'A', 'AAAA', 'TXT', 'PTR']:
cleaned_data['name'] = domain_name
else:
if record_type == 'SRV':
if not re.match(SRV_NAME_REGEX, cleaned_data['name']):
self._add_field_error('name', _('Enter a valid SRV name'))
else:
cleaned_data['name'] += domain_name
else:
cleaned_data['name'] += "." + domain_name
if not re.match(WILDCARD_DOMAIN_NAME_REGEX,
cleaned_data['name']):
self._add_field_error('name',
_('Enter a valid hostname. The '
'hostname should contain letters '
'and numbers, and be no more than '
'63 characters.'))
# Data field
if self._is_field_blank(cleaned_data, 'data'):
if record_type in ['A', 'AAAA', 'CNAME', 'MX', 'SRV']:
self._add_required_field_error('data')
else:
if record_type == 'A':
try:
validators.validate_ipv4_address(cleaned_data['data'])
except ValidationError:
self._add_field_error('data',
_('Enter a valid IPv4 address'))
elif record_type == 'AAAA':
try:
validators.validate_ipv6_address(cleaned_data['data'])
except ValidationError:
self._add_field_error('data',
_('Enter a valid IPv6 address'))
elif record_type in ['CNAME', 'MX', 'PTR']:
if not re.match(DOMAIN_NAME_REGEX, cleaned_data['data']):
self._add_field_error('data', _('Enter a valid hostname'))
elif record_type == 'SRV':
if not re.match(SRV_DATA_REGEX, cleaned_data['data']):
self._add_field_error('data',
_('Enter a valid SRV record'))
# Txt field
if self._is_field_blank(cleaned_data, 'txt'):
if record_type == 'TXT':
self._add_required_field_error('txt')
else:
if record_type == 'TXT':
cleaned_data['data'] = cleaned_data['txt']
if record_type == 'SSHFP':
if not re.match(SSHFP_DATA_REGEX, cleaned_data['txt']):
self._add_field_error('txt',
_('Enter a valid SSHFP record'))
cleaned_data['data'] = cleaned_data['txt']
cleaned_data.pop('txt')
# Priority field
# Check against '' instead of using _is_field_blank because we need to
# allow a valud of 0.
if ('priority' not in cleaned_data or
cleaned_data['priority'] == '' or
cleaned_data['priority'] is None):
if record_type in ['MX', 'SRV']:
self._add_required_field_error('priority')
# Rename 'id' to 'record_id'
if 'id' in cleaned_data:
cleaned_data['record_id'] = cleaned_data.pop('id')
# Remove domain_name
cleaned_data.pop('domain_name')
return cleaned_data
def _add_required_field_error(self, field):
'''Set a required field error on the specified field.'''
self._add_field_error(field, _('This field is required'))
def _add_field_error(self, field, msg):
'''Set the specified msg as an error on the field.'''
self._errors[field] = self.error_class([msg])
def _is_field_blank(self, cleaned_data, field):
'''Returns a flag indicating whether the specified field is blank.'''
return field in cleaned_data and not cleaned_data[field]
class RecordCreate(RecordForm):
'''Form for creating a new domain record.'''
exc_message = _('Unable to create record.')
@handle_exc
def handle(self, request, data):
record = api.designate.record_create(request, **data)
messages.success(request,
_('Domain record %(name)s created.') %
{"name": record.name})
return record
class RecordUpdate(RecordForm):
'''Form for editing a domain record.'''
exc_message = _('Unable to create record.')
id = forms.CharField(widget=forms.HiddenInput())
def __init__(self, request, *args, **kwargs):
super(RecordUpdate, self).__init__(request, *args, **kwargs)
# Force the type field to be read-only
self.fields['type'].widget.attrs['readonly'] = 'readonly'
if self['type'].value() in ('soa', 'ns'):
self.fields['type'].choices.append(('ns', _('NS')))
self.fields['type'].choices.append(('soa', _('SOA')))
self.fields['name'].widget.attrs['readonly'] = 'readonly'
self.fields['data'].widget.attrs['readonly'] = 'readonly'
self.fields['description'].widget.attrs['readonly'] = 'readonly'
self.fields['ttl'].widget.attrs['readonly'] = 'readonly'
# Filter the choice list so that it only contains the type for
# the current record. Ideally, we would just disable the select
# field, but that has the unfortunate side-effect of breaking
# the 'selectable' javascript code.
self.fields['type'].choices = (
[choice for choice in self.fields['type'].choices
if choice[0] == self.initial['type']])
@handle_exc
def handle(self, request, data):
if data['type'] in ('SOA', 'NS'):
return True
record = api.designate.record_update(request, **data)
messages.success(request,
_('Domain record %(name)s updated.') %
{"name": record.name})
return record

View File

@ -1,26 +0,0 @@
# Copyright 2013 Hewlett-Packard Development Company, L.P.
#
# 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 django.utils.translation import ugettext_lazy as _ # noqa
import horizon
from openstack_dashboard.dashboards.project import dashboard
class DNSDomains(horizon.Panel):
name = _("Domains")
slug = 'dns_domains'
permissions = ('openstack.services.dns',)
dashboard.Project.register(DNSDomains)

View File

@ -1,265 +0,0 @@
# Copyright 2013 Hewlett-Packard Development Company, L.P.
#
# 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 django import urls
from django.utils.translation import ugettext_lazy as _ # noqa
from django.utils.translation import ungettext_lazy
from horizon import messages
from horizon import tables
from horizon.utils import memoized
from designatedashboard import api
from openstack_dashboard import policy
from oslo_log import log as logging
LOG = logging.getLogger(__name__)
EDITABLE_RECORD_TYPES = (
"A",
"AAAA",
"CNAME",
"MX",
"PTR",
"SPF",
"SRV",
"SSHFP",
"TXT",
)
class CreateDomain(tables.LinkAction):
'''Link action for navigating to the CreateDomain view.'''
name = "create_domain"
verbose_name = _("Create Domain")
url = "horizon:project:dns_domains:create_domain"
classes = ("ajax-modal", "btn-create")
policy_rules = (("dns", "create_domain"),)
@memoized.memoized_method
def allowed(self, request, datum):
if policy.check((("dns", "get_quota"),), request):
try:
if self.table:
quota = api.designate.quota_get(request)
return quota['domains'] > len(self.table.data)
except Exception:
msg = _("The quotas could not be retrieved.")
messages.warning(request, msg)
return True
class EditDomain(tables.LinkAction):
'''Link action for navigating to the UpdateDomain view.'''
name = "edit_domain"
verbose_name = _("Edit Domain")
url = "horizon:project:dns_domains:update_domain"
classes = ("ajax-modal", "btn-edit")
policy_rules = (("dns", "update_domain"),)
class ManageRecords(tables.LinkAction):
'''Link action for navigating to the ManageRecords view.'''
name = "manage_records"
verbose_name = _("Manage Records")
url = "horizon:project:dns_domains:records"
classes = ("btn-edit")
policy_rules = (("dns", "get_records"),)
class DeleteDomain(tables.BatchAction):
'''Batch action for deleting domains.'''
name = "delete"
classes = ('btn-danger', 'btn-delete')
policy_rules = (("dns", "delete_domain"),)
@staticmethod
def action_present(count):
return ungettext_lazy(
u"Delete Domain",
u"Delete Domains",
count
)
@staticmethod
def action_past(count):
return ungettext_lazy(
u"Deleted Domain",
u"Deleted Domains",
count
)
def action(self, request, domain_id):
api.designate.domain_delete(request, domain_id)
class CreateRecord(tables.LinkAction):
'''Link action for navigating to the CreateRecord view.'''
name = "create_record"
verbose_name = _("Create Record")
classes = ("ajax-modal", "btn-create")
policy_rules = (("dns", "create_record"),)
def get_link_url(self, datum=None):
url = "horizon:project:dns_domains:create_record"
return urls.reverse(url, kwargs=self.table.kwargs)
class EditRecord(tables.LinkAction):
'''Link action for navigating to the UpdateRecord view.'''
name = "edit_record"
verbose_name = _("Edit Record")
classes = ("ajax-modal", "btn-edit")
policy_rules = (("dns", "update_record"),)
def get_link_url(self, datum=None):
url = "horizon:project:dns_domains:update_record"
kwargs = {
'domain_id': datum.domain_id,
'record_id': datum.id,
}
return urls.reverse(url, kwargs=kwargs)
def allowed(self, request, record=None):
return record.type in EDITABLE_RECORD_TYPES
class DeleteRecord(tables.DeleteAction):
'''Link action for navigating to the UpdateRecord view.'''
policy_rules = (("dns", "delete_record"),)
@staticmethod
def action_present(count):
return ungettext_lazy(
u"Delete Record",
u"Delete Records",
count
)
@staticmethod
def action_past(count):
return ungettext_lazy(
u"Deleted Record",
u"Deleted Records",
count
)
def delete(self, request, record_id):
domain_id = self.table.kwargs['domain_id']
return api.designate.record_delete(request, domain_id, record_id)
def allowed(self, request, record=None):
return record.type in EDITABLE_RECORD_TYPES
class BatchDeleteRecord(tables.BatchAction):
'''Batch action for deleting domain records.'''
name = "delete"
classes = ('btn-danger', 'btn-delete')
policy_rules = (("dns", "delete_record"),)
@staticmethod
def action_present(count):
return ungettext_lazy(
u"Delete Record",
u"Delete Records",
count
)
@staticmethod
def action_past(count):
return ungettext_lazy(
u"Deleted Record",
u"Deleted Records",
count
)
def action(self, request, record_id):
domain_id = self.table.kwargs['domain_id']
api.designate.record_delete(request, domain_id, record_id)
class DomainsTable(tables.DataTable):
'''Data table for displaying domain summary information.'''
name = tables.Column("name",
verbose_name=_("Name"),
link=("horizon:project:dns_domains:domain_detail"))
email = tables.Column("email",
verbose_name=_("Email"))
ttl = tables.Column("ttl",
verbose_name=_("TTL"))
serial = tables.Column("serial",
verbose_name=_("Serial"))
class Meta(object):
name = "domains"
verbose_name = _("Domains")
table_actions = (CreateDomain, DeleteDomain,)
row_actions = (ManageRecords, EditDomain, DeleteDomain,)
def record__details_link(record):
'''Returns a link to the view for updating DNS records.'''
return urls.reverse(
"horizon:project:dns_domains:view_record",
args=(record.domain_id, record.id))
class RecordsTable(tables.DataTable):
'''Data table for displaying summary information for a domains records.'''
name = tables.Column("name",
verbose_name=_("Name"),
link=record__details_link,
)
type = tables.Column("type",
verbose_name=_("Type")
)
data = tables.Column("data",
verbose_name=_("Data")
)
priority = tables.Column("priority",
verbose_name=_("Priority"),
)
ttl = tables.Column("ttl",
verbose_name=_("TTL")
)
class Meta(object):
name = "records"
verbose_name = _("Records")
table_actions = (CreateRecord,)
row_actions = (EditRecord, DeleteRecord,)
multi_select = False

View File

@ -1,38 +0,0 @@
{% extends "horizon/common/_modal_form.html" %}
{% load i18n horizon humanize %}
{% block form_id %}{% endblock %}
{% block form_action %}{% url 'horizon:project:dns_domains:create_domain' %}{% endblock %}
{% block modal_id %}create_domain_modal{% endblock %}
{% block modal-header %}{% trans "Create Domain" %}{% endblock %}
{% block modal-body %}
<div class="left">
<fieldset>
{% include "horizon/common/_form_fields.html" %}
</fieldset>
</div>
<div class="right quota-dynamic">
<h3>{% trans "Description" %}:</h3>
<p>{% blocktrans trimmed %}
The Name field should contain a full-qualified domain name (with
trailing period).
{% endblocktrans %}</p>
<p>{% blocktrans trimmed %}
The Email field should contain a valid email address to be associated
with the domain.
{% endblocktrans %}</p>
<p>{% blocktrans trimmed %}
The optional TTL field can be any value between 1 and 2147483647
seconds.
{% endblocktrans %}</p>
</div>
{% endblock %}
{% block modal-footer %}
<input class="btn btn-primary pull-right" type="submit" value="{% trans "Create Domain" %}" />
<a href="{% url 'horizon:project:dns_domains:index' %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>
{% endblock %}

View File

@ -1,37 +0,0 @@
{% extends "horizon/common/_modal_form.html" %}
{% load i18n horizon humanize %}
{% block form_id %}{% endblock %}
{% block form_action %}{% url 'horizon:project:dns_domains:create_record' domain.id %}{% endblock %}
{% block modal_id %}create_record_modal{% endblock %}
{% block modal-header %}{% trans "Create Record for" %} {{ domain.name }}{% endblock %}
{% block modal-body %}
<div id="scoped-content">
{% include 'project/dns_domains/prefix_field_style.html' %}
<fieldset>
{% include "horizon/common/_form_fields.html" %}
</fieldset>
</div>
{% blocktrans trimmed %}
<p>
<strong>TTL</strong>
The TTL is the time-to-live for the record, in seconds.
</p>
<p>
See <a href="https://en.wikipedia.org/wiki/List_of_DNS_record_types" target="_designate_record_defs">more info</a> on record types.
</p>
{% endblocktrans %}
<script type="text/javascript">
// Empty hidden form fields when the record type is switched
// https://bugs.launchpad.net/designate/+bug/1525199
$("select#id_type.form-control.switchable").eq(0).change(function() {
$(this).closest('fieldset')
.find("input[type=text], textarea").filter(":hidden")
.val("");
})
</script>
{% endblock %}

View File

@ -1,32 +0,0 @@
{% load i18n sizeformat %}
<h3>{% trans "Domain Overview" %}</h3>
<div class="info detail">
<dl class="dl-horizontal">
<dt>{% trans "ID" %}</dt>
<dd>{{ domain.id|default:_("None") }}</dd>
<dt>{% trans "Name" %}</dt>
<dd>{{ domain.name|default:_("None") }}</dd>
<dt>{% trans "Description" %}</dt>
<dd>{{ domain.description|default:_("None") }}</dd>
<dt>{% trans "Serial" %}</dt>
<dd>{{ domain.serial|yesno|capfirst }}</dd>
<dt>{% trans "Email" %}</dt>
<dd>{{ domain.email|default:_("Unknown") }}</dd>
<dt>{% trans "TTL" %}</dt>
<dd>{{ domain.ttl|default:_("Unknown") }}</dd>
<dt>{% trans "Created" %}</dt>
{% if domain.created_at %}
<dd>{{ domain.created_at|parse_isotime }}</dd>
{% else %}
<dd>{% trans "Unknown" %}</dd>
{% endif %}
<dt>{% trans "Updated" %}</dt>
{% if domain.updated_at %}
<dd>{{ domain.updated_at|parse_isotime }}</dd>
{% else %}
<dd>{% trans "Unknown" %}</dd>
{% endif %}
</dl>
</div>

View File

@ -1,36 +0,0 @@
{% load i18n sizeformat %}
<h3><a href="{% url 'horizon:project:dns_domains:records' domain_id %}">{% trans "All Records" %}</a></h3>
<h4>{{ record.name|default:_("None") }}</h4>
<div class="info detail">
<dl class="dl-horizontal">
<dt>{% trans "Name" %}</dt>
<dd>{{ record.name|default:_("None") }}</dd>
<dt>{% trans "ID" %}</dt>
<dd>{{ record.id|default:_("None") }}</dd>
<dt>{% trans "Type" %}</dt>
<dd>{{ record.type|default:_("Unknown") }}</dd>
<dt>{% trans "Description" %}</dt>
<dd>{{ record.description|default:_("None") }}</dd>
<dt>{% trans "Record Data" %}</dt>
<dd>{{ record.data|default:_("None") }}</dd>
<dt>{% trans "Priority" %}</dt>
<dd>{{ record.priority|yesno|capfirst }}</dd>
<dt>{% trans "TTL" %}</dt>
<dd>{{ record.ttl|default:_("None") }}</dd>
<dt>{% trans "Created" %}</dt>
{% if record.created_at %}
<dd>{{ record.created_at|parse_isotime }}</dd>
{% else %}
<dd>{% trans "Unknown" %}</dd>
{% endif %}
<dt>{% trans "Updated" %}</dt>
{% if record.updated_at %}
<dd>{{ record.updated_at|parse_isotime }}</dd>
{% else %}
<dd>{% trans "Unknown" %}</dd>
{% endif %}
</dl>
</div>

View File

@ -1,35 +0,0 @@
{% extends "horizon/common/_modal_form.html" %}
{% load i18n %}
{% block form_id %}update_domain_form{% endblock %}
{% block form_action %}{% url 'horizon:project:dns_domains:update_domain' domain.id %}{% endblock %}
{% block modal-header %}{% trans "Update Domain" %}{% endblock %}
{% block modal-body %}
<div class="left">
<fieldset>
{% include "horizon/common/_form_fields.html" %}
</fieldset>
</div>
<div class="right">
<h3>{% trans "Description" %}:</h3>
<p>{% blocktrans %}
From here you can edit the email address and TTL associated with a domain.
{% endblocktrans %}</p>
<p>{% blocktrans trimmed %}
The Email field should contain a valid email address to be associated
with the domain.
{% endblocktrans %}</p>
<p>{% blocktrans trimmed %}
The optional TTL field can be any value between 1 and 2147483647
seconds.
{% endblocktrans %}</p>
</div>
{% endblock %}
{% block modal-footer %}
<input class="btn btn-primary pull-right" type="submit" value="{% trans "Update Domain" %}" />
<a href="{% url 'horizon:project:dns_domains:index' %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>
{% endblock %}

View File

@ -1,8 +0,0 @@
{% extends "project/dns_domains/_create_record.html" %}
{% load i18n %}
{% block form_id %}update_record_form{% endblock %}
{% block form_action %}{% url 'horizon:project:dns_domains:update_record' record.domain_id record.id %}{% endblock %}
{% block modal-header %}{% trans "Update Domain Record" %}{% endblock %}

View File

@ -1,11 +0,0 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Create Domain" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Create Domain") %}
{% endblock page_header %}
{% block main %}
{% include 'project/dns_domains/_create_domain.html' %}
{% endblock %}

View File

@ -1,11 +0,0 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Create Domain Record" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Create Domain Record") %}
{% endblock page_header %}
{% block main %}
{% include 'project/dns_domains/_create_record.html' %}
{% endblock %}

View File

@ -1,11 +0,0 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans 'Domain Detail' %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Domain") %}
{% endblock page_header %}
{% block main %}
{% include 'project/dns_domains/_domain_detail.html' %}
{% endblock %}

View File

@ -1,11 +0,0 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Domains" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Domains") %}
{% endblock page_header %}
{% block main %}
{{ table.render }}
{% endblock %}

View File

@ -1,4 +0,0 @@
<style type = "text/css" scoped>
.form_field_suffix { float: right; padding-top:10px; }
.form_field_prefix { display: block; overflow: hidden; }
</style>

View File

@ -1,2 +0,0 @@
<label class="form_field_suffix">{{ suffix }}</label>
<span class="form_field_prefix">{{ input }}</span>

View File

@ -1,11 +0,0 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans 'Record Detail' %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title="Record Detail" %}
{% endblock page_header %}
{% block main %}
{% include 'project/dns_domains/_record_detail.html' %}
{% endblock %}

View File

@ -1,32 +0,0 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans 'Domain Records' %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Domain Records") %}
{% endblock page_header %}
{% block main %}
<div class="sub-content grid-content">
<div class="page_title table_header">
<div>
<h3>
<a href="{% url 'horizon:project:dns_domains:index' %}">{% trans "Domains" %}</a> : {{ domain.name }} &rarr;
{% trans "Records" %}
</h3>
</div>
<div class="table_actions">
<a href="{% url 'horizon:project:dns_domains:index' %}" class="close">&times;</a>
</div>
</div>
<div class="nameservers_wrapper">
<h3>{% trans "Nameservers" %}</h3>
<ul>
{% for server in servers %}
<li>{{ server.name }}</li>
{% endfor %}
</ul>
</div>
{{ table.render }}
{% endblock %}

View File

@ -1,11 +0,0 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans 'Update Domain' %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title="Domain" %}
{% endblock page_header %}
{% block main %}
{% include 'project/dns_domains/_update_domain.html' %}
{% endblock %}

View File

@ -1,11 +0,0 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans 'Update Domain Record' %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title="Domain Record" %}
{% endblock page_header %}
{% block main %}
{% include 'project/dns_domains/_update_record.html' %}
{% endblock %}

View File

@ -1,51 +0,0 @@
# Copyright 2013 Hewlett-Packard Development Company, L.P.
#
# 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 django.conf.urls import url # noqa
from .views import CreateDomainView # noqa
from .views import CreateRecordView # noqa
from .views import DomainDetailView # noqa
from .views import IndexView # noqa
from .views import RecordsView # noqa
from .views import UpdateDomainView # noqa
from .views import UpdateRecordView # noqa
from .views import ViewRecordDetailsView # noqa
urlpatterns = [
url(r'^$',
IndexView.as_view(),
name='index'),
url(r'^create/$',
CreateDomainView.as_view(),
name='create_domain'),
url(r'^(?P<domain_id>[^/]+)/update$',
UpdateDomainView.as_view(),
name='update_domain'),
url(r'^(?P<domain_id>[^/]+)$',
DomainDetailView.as_view(),
name='domain_detail'),
url(r'^(?P<domain_id>[^/]+)/records$',
RecordsView.as_view(),
name='records'),
url(r'^(?P<domain_id>[^/]+)/records/create$',
CreateRecordView.as_view(),
name='create_record'),
url(r'^(?P<domain_id>[^/]+)/records/(?P<record_id>[^/]+)/update$',
UpdateRecordView.as_view(),
name='update_record'),
url(r'^(?P<domain_id>[^/]+)/records/(?P<record_id>[^/]+)/$',
ViewRecordDetailsView.as_view(),
name='view_record'),
]

View File

@ -1,20 +0,0 @@
# 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 django.conf import settings
def limit_records_to_fips():
# This method checks the settings to determine if the
# record creation / update screen should limit the ip input
# to be a dropdown of floating ips
return getattr(settings, "DESIGNATE",
{}).get("records_use_fips", False)

View File

@ -1,243 +0,0 @@
# Copyright 2013 Hewlett-Packard Development Company, L.P.
#
# 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 django.urls import reverse
from django.urls import reverse_lazy
from django.utils.translation import ugettext_lazy as _ # noqa
from horizon import exceptions
from horizon import forms
from horizon import tables
from horizon.views import HorizonTemplateView # noqa
from openstack_dashboard.api.neutron import tenant_floating_ip_list
from openstack_dashboard.api.nova import server_list
from designatedashboard import api
from .forms import DomainCreate # noqa
from .forms import DomainUpdate # noqa
from .forms import RecordCreate # noqa
from .forms import RecordUpdate # noqa
from .tables import DomainsTable # noqa
from .tables import RecordsTable # noqa
from .utils import limit_records_to_fips # noqa
class IndexView(tables.DataTableView):
table_class = DomainsTable
template_name = 'project/dns_domains/index.html'
def get_data(self):
try:
return api.designate.domain_list(self.request)
except Exception:
exceptions.handle(self.request,
_('Unable to retrieve domain list.'))
return []
class CreateDomainView(forms.ModalFormView):
form_class = DomainCreate
template_name = 'project/dns_domains/create_domain.html'
success_url = reverse_lazy('horizon:project:dns_domains:index')
def get_object_display(self, obj):
return obj.ip
class DomainDetailView(HorizonTemplateView):
template_name = 'project/dns_domains/domain_detail.html'
def get_context_data(self, **kwargs):
context = super(DomainDetailView, self).get_context_data(**kwargs)
domain_id = self.kwargs['domain_id']
try:
context["domain"] = api.designate.domain_get(self.request,
domain_id)
table = DomainsTable(self.request)
context["actions"] = table.render_row_actions(context["domain"])
except Exception:
redirect = reverse('horizon:project:dns_domains:index')
exceptions.handle(self.request,
_('Unable to retrieve domain record.'),
redirect=redirect)
return context
class UpdateDomainView(forms.ModalFormView):
form_class = DomainUpdate
template_name = 'project/dns_domains/update_domain.html'
success_url = reverse_lazy('horizon:project:dns_domains:index')
def get_object(self):
domain_id = self.kwargs['domain_id']
try:
return api.designate.domain_get(self.request, domain_id)
except Exception:
redirect = reverse('horizon:project:dns_domains:index')
exceptions.handle(self.request,
_('Unable to retrieve domain record.'),
redirect=redirect)
def get_initial(self):
self.domain = self.get_object()
return self.domain
def get_context_data(self, **kwargs):
context = super(UpdateDomainView, self).get_context_data(**kwargs)
context["domain"] = self.domain
return context
class RecordsView(tables.DataTableView):
table_class = RecordsTable
template_name = 'project/dns_domains/records.html'
def get_data(self):
domain_id = self.kwargs['domain_id']
records = []
try:
self.domain = api.designate.domain_get(self.request, domain_id)
self.servers = api.designate.server_list(self.request, domain_id)
records = api.designate.record_list(self.request, domain_id)
except Exception:
redirect = reverse('horizon:project:dns_domains:index')
exceptions.handle(self.request,
_('Unable to retrieve record list.'),
redirect=redirect)
return records
def get_context_data(self, **kwargs):
context = super(RecordsView, self).get_context_data(**kwargs)
context['domain'] = self.domain
context['servers'] = self.servers
return context
class BaseRecordFormView(forms.ModalFormView):
cancel_label = _("Cancel")
def get_success_url(self):
return reverse('horizon:project:dns_domains:records',
args=(self.kwargs['domain_id'],))
def get_domain(self):
domain_id = self.kwargs['domain_id']
try:
return api.designate.domain_get(self.request, domain_id)
except Exception:
redirect = reverse('horizon:project:dns_domains:records',
args=(self.kwargs['domain_id'],))
exceptions.handle(self.request,
('Unable to retrieve domain record.'),
redirect=redirect)
# NotAuthorized errors won't be redirected automatically. Need
# to force the issue
raise exceptions.Http302(redirect)
def get_initial(self):
self.domain = self.get_domain()
results = {'domain_id': self.domain.id,
'domain_name': self.domain.name, }
if limit_records_to_fips():
results.update({'fips': tenant_floating_ip_list(self.request),
'instances': server_list(self.request)[0]})
return results
def get_context_data(self, **kwargs):
"""Set the cancel url
the cancel_url needs a variable in it
so we cannot do this with a simple class attr
this is critical to perform before the super.get_context_data
"""
self.cancel_url = reverse('horizon:project:dns_domains:records',
args=(self.kwargs['domain_id'],))
context = super(BaseRecordFormView, self).get_context_data(**kwargs)
context['domain'] = self.domain
return context
class CreateRecordView(BaseRecordFormView):
form_class = RecordCreate
submit_label = _("Create Record")
template_name = 'project/dns_domains/create_record.html'
class ViewRecordDetailsView(HorizonTemplateView):
template_name = 'project/dns_domains/record_detail.html'
def get_record(self):
domain_id = self.kwargs['domain_id']
record_id = self.kwargs['record_id']
try:
return api.designate.record_get(self.request, domain_id, record_id)
except Exception:
redirect = reverse('horizon:project:dns_domains:records',
args=(self.kwargs['domain_id'],))
exceptions.handle(self.request,
_('Unable to retrieve domain record.'),
redirect=redirect)
def get_context_data(self, **kwargs):
context = super(ViewRecordDetailsView, self).get_context_data(**kwargs)
self.record = self.get_record()
context["record"] = self.record
context["domain_id"] = self.kwargs['domain_id']
return context
class UpdateRecordView(BaseRecordFormView):
form_class = RecordUpdate
submit_label = _("Update Record")
template_name = 'project/dns_domains/update_record.html'
def get_record(self):
domain_id = self.kwargs['domain_id']
record_id = self.kwargs['record_id']
try:
return api.designate.record_get(self.request, domain_id, record_id)
except Exception:
redirect = reverse('horizon:project:dns_domains:records',
args=(self.kwargs['domain_id'],))
exceptions.handle(self.request,
_('Unable to retrieve domain record.'),
redirect=redirect)
def get_initial(self):
initial = super(UpdateRecordView, self).get_initial()
self.record = self.get_record()
initial.update({
'id': self.record.id,
'name': self.record.name.replace("." + initial['domain_name'], ''),
'data': self.record.data,
'txt': self.record.data,
'priority': self.record.priority,
'ttl': self.record.ttl,
'type': self.record.type.lower(),
'description': self.record.description,
})
if limit_records_to_fips():
initial.update({'ip_addr': self.record.data})
return initial
def get_context_data(self, **kwargs):
context = super(UpdateRecordView, self).get_context_data(**kwargs)
context["record"] = self.record
return context

View File

@ -1,36 +0,0 @@
# Copyright 2013 Hewlett-Packard Development Company, L.P.
#
# 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 designatedashboard import exceptions
# The name of the panel to be added to HORIZON_CONFIG. Required.
PANEL = 'domains'
# The name of the dashboard the PANEL associated with. Required.
PANEL_DASHBOARD = 'project'
# The name of the panel group the PANEL is associated with.
PANEL_GROUP = 'dns'
ADD_INSTALLED_APPS = ['designatedashboard']
ADD_EXCEPTIONS = {
'recoverable': exceptions.RECOVERABLE,
'not_found': exceptions.NOT_FOUND,
'unauthorized': exceptions.UNAUTHORIZED,
}
# Python panel class of the PANEL to be added.
ADD_PANEL = (
'designatedashboard.dashboards.project.dns_domains.panel.DNSDomains')
DISABLED = True

View File

@ -20,10 +20,6 @@ import os
import fixtures
import testtools
from openstack_dashboard.test import helpers as test
from designatedashboard.dashboards.project.dns_domains import forms
_TRUE_VALUES = ('True', 'true', '1', 'yes')
@ -56,45 +52,3 @@ class TestCase(testtools.TestCase):
self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr))
self.log_fixture = self.useFixture(fixtures.FakeLogger())
class BaseRecordFormCleanTests(test.TestCase):
DOMAIN_NAME = 'foo.com.'
HOSTNAME = 'www'
MSG_FIELD_REQUIRED = 'This field is required'
MSG_INVALID_HOSTNAME = 'Enter a valid hostname. The '\
'hostname should contain letters '\
'and numbers, and be no more than '\
'63 characters.'
MSG_INVALID_HOSTNAME_SHORT = 'Enter a valid hostname'
def setUp(self):
super(BaseRecordFormCleanTests, self).setUp()
# Request object with messages support
self.request = self.factory.get('', {})
# Set-up form instance
kwargs = {}
kwargs['initial'] = {'domain_name': self.DOMAIN_NAME}
self.form = forms.RecordCreate(self.request, **kwargs)
self.form._errors = {}
self.form.cleaned_data = {
'domain_name': self.DOMAIN_NAME,
'name': '',
'data': '',
'txt': '',
'priority': None,
'ttl': None,
}
def assert_no_errors(self):
self.assertEqual(self.form._errors, {})
def assert_error(self, field, msg):
self.assertIn(msg, self.form._errors[field])
def assert_required_error(self, field):
self.assert_error(field, self.MSG_FIELD_REQUIRED)

View File

@ -1,354 +0,0 @@
# Copyright 2012 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Copyright 2012 Nebula, Inc.
#
# 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 __future__ import unicode_literals
from designatedashboard.tests import base
DOMAIN_ID = '123'
class ARecordFormTests(base.BaseRecordFormCleanTests):
IPV4 = '1.1.1.1'
MSG_INVALID_IPV4 = 'Enter a valid IPv4 address'
def setUp(self):
super(ARecordFormTests, self).setUp()
self.form.cleaned_data['type'] = 'A'
self.form.cleaned_data['name'] = self.HOSTNAME
self.form.cleaned_data['data'] = self.IPV4
def test_valid_field_values(self):
self.form.clean()
self.assert_no_errors()
def test_valid_name_field_wild_card(self):
self.form.cleaned_data['name'] = '*'
self.form.clean()
self.assert_no_errors()
def test_missing_name_field(self):
self.form.cleaned_data['name'] = ''
self.form.clean()
self.assert_no_errors()
self.assertIsNotNone(self.form.cleaned_data['name'])
def test_missing_data_field(self):
self.form.cleaned_data['data'] = ''
self.form.clean()
self.assert_required_error('data')
def test_invalid_name_field(self):
self.form.cleaned_data['name'] = '$#%foo!!'
self.form.clean()
self.assert_error('name', self.MSG_INVALID_HOSTNAME)
def test_invalid_name_field_starting_dash(self):
self.form.cleaned_data['name'] = '-ww'
self.form.clean()
self.assert_error('name', self.MSG_INVALID_HOSTNAME)
def test_invalid_name_field_trailing_dash(self):
self.form.cleaned_data['name'] = 'co-'
self.form.clean()
self.assert_error('name', self.MSG_INVALID_HOSTNAME)
def test_invalid_name_field_bad_wild_card(self):
self.form.cleaned_data['name'] = 'derp.*.' + self.DOMAIN_NAME
self.form.clean()
self.assert_error('name', self.MSG_INVALID_HOSTNAME)
def test_outside_of_domain_name_field(self):
self.form.cleaned_data['name'] = 'www.bar.com.'
self.form.clean()
self.assert_error('name', self.MSG_INVALID_HOSTNAME)
def test_invalid_data_field(self):
self.form.cleaned_data['data'] = 'foo'
self.form.clean()
self.assert_error('data', self.MSG_INVALID_IPV4)
class AAAARecordFormTests(base.BaseRecordFormCleanTests):
IPV6 = '1111:1111:1111:11::1'
MSG_INVALID_IPV6 = 'Enter a valid IPv6 address'
def setUp(self):
super(AAAARecordFormTests, self).setUp()
self.form.cleaned_data['type'] = 'AAAA'
self.form.cleaned_data['name'] = self.HOSTNAME
self.form.cleaned_data['data'] = self.IPV6
def test_valid_field_values(self):
self.form.clean()
self.assert_no_errors()
def test_valid_name_field_wild_card(self):
self.form.cleaned_data['name'] = '*'
self.form.clean()
self.assert_no_errors()
def test_missing_name_field(self):
self.form.cleaned_data['name'] = ''
self.form.clean()
self.assert_no_errors()
self.assertIsNotNone(self.form.cleaned_data['name'])
def test_missing_data_field(self):
self.form.cleaned_data['data'] = ''
self.form.clean()
self.assert_required_error('data')
def test_invalid_name_field(self):
self.form.cleaned_data['name'] = '#@$foo!!'
self.form.clean()
self.assert_error('name', self.MSG_INVALID_HOSTNAME)
def test_invalid_name_field_starting_dash(self):
self.form.cleaned_data['name'] = '-ww.foo.com'
self.form.clean()
self.assert_error('name', self.MSG_INVALID_HOSTNAME)
def test_invalid_name_field_trailing_dash(self):
self.form.cleaned_data['name'] = 'www.foo.co-'
self.form.clean()
self.assert_error('name', self.MSG_INVALID_HOSTNAME)
def test_invalid_name_field_bad_wild_card(self):
self.form.cleaned_data['name'] = 'derp.*.' + self.DOMAIN_NAME
self.form.clean()
self.assert_error('name', self.MSG_INVALID_HOSTNAME)
def test_outside_of_domain_name_field(self):
self.form.cleaned_data['name'] = 'www.bar.com.'
self.form.clean()
self.assert_error('name', self.MSG_INVALID_HOSTNAME)
def test_invalid_data_field(self):
self.form.cleaned_data['data'] = 'foo'
self.form.clean()
self.assert_error('data', self.MSG_INVALID_IPV6)
class CNAMERecordFormTests(base.BaseRecordFormCleanTests):
CNAME = 'bar.foo.com.'
def setUp(self):
super(CNAMERecordFormTests, self).setUp()
self.form.cleaned_data['type'] = 'CNAME'
self.form.cleaned_data['name'] = self.HOSTNAME
self.form.cleaned_data['data'] = self.CNAME
def test_valid_field_values(self):
self.form.clean()
self.assert_no_errors()
def test_valid_name_field_wild_card(self):
self.form.cleaned_data['name'] = '*'
self.form.clean()
self.assert_no_errors()
def test_missing_name_field(self):
self.form.cleaned_data['name'] = ''
self.form.clean()
self.assert_required_error('name')
def test_missing_data_field(self):
self.form.cleaned_data['data'] = ''
self.form.clean()
self.assert_required_error('data')
def test_invalid_name_field(self):
self.form.cleaned_data['name'] = '$#%#$foo!!!'
self.form.clean()
self.assert_error('name', self.MSG_INVALID_HOSTNAME)
def test_invalid_name_field_starting_dash(self):
self.form.cleaned_data['name'] = '-ww.foo.com'
self.form.clean()
self.assert_error('name', self.MSG_INVALID_HOSTNAME)
def test_invalid_name_field_trailing_dash(self):
self.form.cleaned_data['name'] = 'www.foo.co-'
self.form.clean()
self.assert_error('name', self.MSG_INVALID_HOSTNAME)
def test_invalid_name_field_bad_wild_card(self):
self.form.cleaned_data['name'] = 'derp.*.' + self.DOMAIN_NAME
self.form.clean()
self.assert_error('name', self.MSG_INVALID_HOSTNAME)
def test_outside_of_domain_name_field(self):
self.form.cleaned_data['name'] = 'www.bar.com.'
self.form.clean()
self.assert_error('name', self.MSG_INVALID_HOSTNAME)
def test_invalid_data_field(self):
self.form.cleaned_data['data'] = 'foo'
self.form.clean()
self.assert_error('data', self.MSG_INVALID_HOSTNAME_SHORT)
class MXRecordFormTests(base.BaseRecordFormCleanTests):
MAIL_SERVER = 'mail.foo.com.'
PRIORITY = 10
def setUp(self):
super(MXRecordFormTests, self).setUp()
self.form.cleaned_data['type'] = 'MX'
self.form.cleaned_data['data'] = self.MAIL_SERVER
self.form.cleaned_data['priority'] = self.PRIORITY
def test_valid_field_values(self):
self.form.clean()
self.assert_no_errors()
def test_missing_data_field(self):
self.form.cleaned_data['data'] = ''
self.form.clean()
self.assert_required_error('data')
def test_missing_priority_field(self):
self.form.cleaned_data['priority'] = None
self.form.clean()
self.assert_required_error('priority')
def test_invalid_data_field(self):
self.form.cleaned_data['data'] = 'foo'
self.form.clean()
self.assert_error('data', self.MSG_INVALID_HOSTNAME_SHORT)
def test_default_assignment_name_field(self):
self.form.clean()
self.assertEqual(self.DOMAIN_NAME, self.form.cleaned_data['name'])
class TXTRecordFormTests(base.BaseRecordFormCleanTests):
TEXT = 'Lorem ipsum'
def setUp(self):
super(TXTRecordFormTests, self).setUp()
self.form.cleaned_data['type'] = 'TXT'
self.form.cleaned_data['name'] = self.HOSTNAME
self.form.cleaned_data['txt'] = self.TEXT
def test_valid_field_values(self):
self.form.clean()
self.assert_no_errors()
def test_valid_name_field_wild_card(self):
self.form.cleaned_data['name'] = '*'
self.form.clean()
self.assert_no_errors()
def test_missing_name_field(self):
self.form.cleaned_data['name'] = ''
self.form.clean()
self.assert_no_errors()
self.assertIsNotNone(self.form.cleaned_data['name'])
def test_missing_txt_field(self):
self.form.cleaned_data['txt'] = ''
self.form.clean()
self.assert_required_error('txt')
def test_invalid_name_field(self):
self.form.cleaned_data['name'] = 'foo-'
self.form.clean()
self.assert_error('name', self.MSG_INVALID_HOSTNAME)
def test_invalid_name_field_starting_dash(self):
self.form.cleaned_data['name'] = '-ww.foo.com'
self.form.clean()
self.assert_error('name', self.MSG_INVALID_HOSTNAME)
def test_invalid_name_field_trailing_dash(self):
self.form.cleaned_data['name'] = 'www.foo.co-'
self.form.clean()
self.assert_error('name', self.MSG_INVALID_HOSTNAME)
def test_invalid_name_field_bad_wild_card(self):
self.form.cleaned_data['name'] = 'derp.*'
self.form.clean()
self.assert_error('name', self.MSG_INVALID_HOSTNAME)
def test_outside_of_domain_name_field(self):
self.form.cleaned_data['name'] = 'www.bar.com.'
self.form.clean()
self.assert_error('name', self.MSG_INVALID_HOSTNAME)
def test_default_assignment_data_field(self):
self.form.clean()
self.assertEqual(self.TEXT, self.form.cleaned_data['data'])
class SRVRecordFormTests(base.BaseRecordFormCleanTests):
SRV_NAME = '_foo._tcp.'
SRV_DATA = '1 1 srv.foo.com.'
PRIORITY = 10
MSG_INVALID_SRV_NAME = 'Enter a valid SRV name'
MSG_INVALID_SRV_DATA = 'Enter a valid SRV record'
def setUp(self):
super(SRVRecordFormTests, self).setUp()
self.form.cleaned_data['type'] = 'SRV'
self.form.cleaned_data['name'] = self.SRV_NAME
self.form.cleaned_data['data'] = self.SRV_DATA
self.form.cleaned_data['priority'] = self.PRIORITY
def test_valid_field_values(self):
self.form.clean()
self.assert_no_errors()
def test_missing_name_field(self):
self.form.cleaned_data['name'] = ''
self.form.clean()
self.assert_required_error('name')
def test_missing_data_field(self):
self.form.cleaned_data['data'] = ''
self.form.clean()
self.assert_required_error('data')
def test_missing_priority_field(self):
self.form.cleaned_data['priority'] = None
self.form.clean()
self.assert_required_error('priority')
def test_invalid_name_field(self):
self.form.cleaned_data['name'] = 'foo'
self.form.clean()
self.assert_error('name', self.MSG_INVALID_SRV_NAME)
def test_invalid_data_field(self):
self.form.cleaned_data['data'] = 'foo'
self.form.clean()
self.assert_error('data', self.MSG_INVALID_SRV_DATA)
def test_default_assignment_name_field(self):
self.form.clean()
self.assertEqual(self.SRV_NAME + self.DOMAIN_NAME,
self.form.cleaned_data['name'])

View File

@ -1,85 +0,0 @@
# Copyright 2015 NEC Corporation. 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.
from designatedashboard.tests import base
class PTRRecordFormTests(base.BaseRecordFormCleanTests):
PTR = "6.0.0.10.in-addr.arpa."
def setUp(self):
super(PTRRecordFormTests, self).setUp()
self.form.cleaned_data['type'] = 'PTR'
self.form.cleaned_data['name'] = self.HOSTNAME
self.form.cleaned_data['data'] = self.PTR
def test_valid_field_values(self):
self.form.clean()
self.assert_no_errors()
def test_valid_name_field_wild_card(self):
self.form.cleaned_data['name'] = '*'
self.form.clean()
self.assert_no_errors()
def test_missing_name_field(self):
self.form.cleaned_data['name'] = ''
self.form.clean()
self.assert_no_errors()
self.assertIsNotNone(self.form.cleaned_data['name'])
def test_invalid_name_field(self):
self.form.cleaned_data['name'] = '#@$foo!!'
self.form.clean()
self.assert_error('name', self.MSG_INVALID_HOSTNAME)
def test_invalid_name_field_starting_dash(self):
self.form.cleaned_data['name'] = '-ww.foo.com'
self.form.clean()
self.assert_error('name', self.MSG_INVALID_HOSTNAME)
def test_invalid_name_field_trailing_dash(self):
self.form.cleaned_data['name'] = 'www.foo.co-'
self.form.clean()
self.assert_error('name', self.MSG_INVALID_HOSTNAME)
def test_invalid_name_field_bad_wild_card(self):
self.form.cleaned_data['name'] = 'derp.*.' + self.DOMAIN_NAME
self.form.clean()
self.assert_error('name', self.MSG_INVALID_HOSTNAME)
def test_outside_of_domain_name_field(self):
self.form.cleaned_data['name'] = 'www.bar.com.'
self.form.clean()
def test_invalid_data_field(self):
self.form.cleaned_data['data'] = '#@$' + self.PTR + '!!'
self.form.clean()
self.assert_error('data', self.MSG_INVALID_HOSTNAME_SHORT)
def test_invalid_data_field_starting_dash(self):
self.form.cleaned_data['data'] = '-' + self.PTR
self.form.clean()
self.assert_error('data', self.MSG_INVALID_HOSTNAME_SHORT)
def test_invalid_data_field_trailing_dash(self):
self.form.cleaned_data['data'] = self.PTR + '-'
self.form.clean()
self.assert_error('data', self.MSG_INVALID_HOSTNAME_SHORT)
def test_invalid_data_field_bad_wild_card(self):
self.form.cleaned_data['data'] = 'derp.*.' + self.PTR
self.form.clean()
self.assert_error('data', self.MSG_INVALID_HOSTNAME_SHORT)

View File

@ -1,70 +0,0 @@
# Copyright 2015 NEC Corporation. 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.
from designatedashboard.tests import base
class SPFRecordFormTests(base.BaseRecordFormCleanTests):
TEXT = 'v=spf1 +all'
def setUp(self):
super(SPFRecordFormTests, self).setUp()
self.form.cleaned_data['type'] = 'SPF'
self.form.cleaned_data['name'] = self.HOSTNAME
self.form.cleaned_data['txt'] = self.TEXT
def test_valid_field_values(self):
self.form.clean()
self.assert_no_errors()
def test_valid_name_field_wild_card(self):
self.form.cleaned_data['name'] = '*'
self.form.clean()
self.assert_no_errors()
def test_missing_name_field(self):
self.form.cleaned_data['name'] = ''
self.form.clean()
self.assert_no_errors()
def test_missing_txt_field(self):
self.form.cleaned_data['txt'] = ''
self.form.clean()
self.assert_no_errors()
def test_invalid_name_field(self):
self.form.cleaned_data['name'] = 'foo-'
self.form.clean()
self.assert_error('name', self.MSG_INVALID_HOSTNAME)
def test_invalid_name_field_starting_dash(self):
self.form.cleaned_data['name'] = '-ww.foo.com'
self.form.clean()
self.assert_error('name', self.MSG_INVALID_HOSTNAME)
def test_invalid_name_field_trailing_dash(self):
self.form.cleaned_data['name'] = 'www.foo.co-'
self.form.clean()
self.assert_error('name', self.MSG_INVALID_HOSTNAME)
def test_invalid_name_field_bad_wild_card(self):
self.form.cleaned_data['name'] = 'derp.*'
self.form.clean()
self.assert_error('name', self.MSG_INVALID_HOSTNAME)
def test_outside_of_domain_name_field(self):
self.form.cleaned_data['name'] = 'www.bar.com.'
self.form.clean()
self.assert_error('name', self.MSG_INVALID_HOSTNAME)

View File

@ -1,92 +0,0 @@
# Copyright 2015 NEC Corporation. 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.
from designatedashboard.tests import base
class SSHFPRecordFormTests(base.BaseRecordFormCleanTests):
TEXT = '2 1 d1eb0d876ec69d18bcefc4263ae43ec33ae14f4c'
MSG_INVALID_RECORD = "Enter a valid SSHFP record"
def setUp(self):
super(SSHFPRecordFormTests, self).setUp()
self.form.cleaned_data['type'] = 'SSHFP'
self.form.cleaned_data['name'] = self.HOSTNAME
self.form.cleaned_data['txt'] = self.TEXT
def test_valid_field_values(self):
self.form.clean()
self.assert_no_errors()
def test_valid_name_field_wild_card(self):
self.form.cleaned_data['name'] = '*'
self.form.clean()
self.assert_no_errors()
def test_missing_txt_field(self):
self.form.cleaned_data['txt'] = ''
self.form.clean()
self.assert_error('txt', self.MSG_INVALID_RECORD)
def test_invalid_txt_field(self):
self.form.cleaned_data['txt'] = 'foo'
self.form.clean()
self.assert_error('txt', self.MSG_INVALID_RECORD)
def test_invalid_text_field_starting_dash(self):
self.form.cleaned_data['txt'] = '-2 1 d1eb0d876ec69d18bcef\
c4263ae43ec33ae14f4c'
self.form.clean()
self.assert_error('txt', self.MSG_INVALID_RECORD)
def test_invalid_text_field_trailing_dash(self):
self.form.cleaned_data['txt'] = '2 1 d1eb0d876ec69d18bcef\
c4263ae43ec33ae14f4c-'
self.form.clean()
self.assert_error('txt', self.MSG_INVALID_RECORD)
def test_invalid_text_field_bad_wild_card(self):
self.form.cleaned_data['txt'] = '1 2 e0d5320e7e36dea8e369b*'
self.form.clean()
self.assert_error('txt', self.MSG_INVALID_RECORD)
def test_default_assignment_data_field(self):
self.form.clean()
self.assertEqual(self.TEXT, self.form.cleaned_data['data'])
def test_invalid_name_field(self):
self.form.cleaned_data['name'] = 'foo-'
self.form.clean()
self.assert_error('name', self.MSG_INVALID_HOSTNAME)
def test_invalid_name_field_starting_dash(self):
self.form.cleaned_data['name'] = '-ww.foo.com'
self.form.clean()
self.assert_error('name', self.MSG_INVALID_HOSTNAME)
def test_invalid_name_field_trailing_dash(self):
self.form.cleaned_data['name'] = 'www.foo.co-'
self.form.clean()
self.assert_error('name', self.MSG_INVALID_HOSTNAME)
def test_invalid_name_field_bad_wild_card(self):
self.form.cleaned_data['name'] = 'derp.*'
self.form.clean()
self.assert_error('name', self.MSG_INVALID_HOSTNAME)
def test_outside_of_domain_name_field(self):
self.form.cleaned_data['name'] = 'www.bar.com.'
self.form.clean()
self.assert_error('name', self.MSG_INVALID_HOSTNAME)

View File

@ -0,0 +1,4 @@
---
upgrade:
- |
Removed the long deprecated v1 API dashboard.

View File

@ -139,7 +139,7 @@ pygments_style = 'sphinx'
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# html_static_path = ['_static']
# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied