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:
parent
9fecedf4df
commit
fe68f0f1bc
@ -1 +0,0 @@
|
|||||||
from designatedashboard.api import designate # noqa
|
|
@ -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)
|
|
@ -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
|
|
@ -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)
|
|
@ -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
|
|
@ -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 %}
|
|
@ -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 %}
|
|
@ -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>
|
|
@ -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>
|
|
@ -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 %}
|
|
@ -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 %}
|
|
||||||
|
|
@ -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 %}
|
|
@ -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 %}
|
|
@ -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 %}
|
|
@ -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 %}
|
|
@ -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>
|
|
@ -1,2 +0,0 @@
|
|||||||
<label class="form_field_suffix">{{ suffix }}</label>
|
|
||||||
<span class="form_field_prefix">{{ input }}</span>
|
|
@ -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 %}
|
|
@ -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 }} →
|
|
||||||
{% trans "Records" %}
|
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
<div class="table_actions">
|
|
||||||
<a href="{% url 'horizon:project:dns_domains:index' %}" class="close">×</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 %}
|
|
@ -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 %}
|
|
@ -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 %}
|
|
@ -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'),
|
|
||||||
]
|
|
@ -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)
|
|
@ -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
|
|
@ -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
|
|
@ -20,10 +20,6 @@ import os
|
|||||||
import fixtures
|
import fixtures
|
||||||
import testtools
|
import testtools
|
||||||
|
|
||||||
from openstack_dashboard.test import helpers as test
|
|
||||||
|
|
||||||
from designatedashboard.dashboards.project.dns_domains import forms
|
|
||||||
|
|
||||||
|
|
||||||
_TRUE_VALUES = ('True', 'true', '1', 'yes')
|
_TRUE_VALUES = ('True', 'true', '1', 'yes')
|
||||||
|
|
||||||
@ -56,45 +52,3 @@ class TestCase(testtools.TestCase):
|
|||||||
self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr))
|
self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr))
|
||||||
|
|
||||||
self.log_fixture = self.useFixture(fixtures.FakeLogger())
|
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)
|
|
||||||
|
@ -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'])
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
upgrade:
|
||||||
|
- |
|
||||||
|
Removed the long deprecated v1 API dashboard.
|
@ -139,7 +139,7 @@ pygments_style = 'sphinx'
|
|||||||
# relative to this directory. They are copied after the builtin static files,
|
# relative to this directory. They are copied after the builtin static files,
|
||||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
# 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
|
# Add any extra paths that contain custom files (such as robots.txt or
|
||||||
# .htaccess) here, relative to this directory. These files are copied
|
# .htaccess) here, relative to this directory. These files are copied
|
||||||
|
Loading…
x
Reference in New Issue
Block a user