Commit a41e5ca7 authored by Kiall Mac Innes's avatar Kiall Mac Innes

Designate Dashboard now has it's own repo

The dashboard has been moved to openstack/designate-dashboard, a
new repo.

Change-Id: I36dcfc7ac0446c8fe4c637892b2864c116528fdb
Depends-On: Icd53afc063d3c7afbc04769285f7538cd27fdef7
parent 2debd0cc
If you would like to contribute to the development of OpenStack,
you must follow the steps in the "If you're a developer, start here"
section of this page:
http://wiki.openstack.org/HowToContribute
Once those steps have been completed, changes to OpenStack
should be submitted for review via the Gerrit tool, following
the workflow documented at:
http://wiki.openstack.org/GerritWorkflow
Pull requests submitted through GitHub will be ignored.
Bugs should be filed on Launchpad, not GitHub:
https://bugs.launchpad.net/designatedashboard
\ No newline at end of file
designatedashboard Style Commandments
===============================================
Read the OpenStack Style Commandments http://docs.openstack.org/developer/hacking/
\ No newline at end of file
This diff is collapsed.
include AUTHORS
include ChangeLog
include README.rst
recursive-include designatedashboard *.html
exclude .gitignore
exclude .gitreview
global-exclude *.pyc
===============================
designatedashboard
===============================
Designate Horizon UI bits
* Free software: Apache license
Features
--------
* TODO
Howto
-----
1. Package the designatedashboard by running::
python setup.py sdist
This will create a python egg in the dist folder, which can be used to install
on the horizon machine or within horizon's python virtual environment.
2. Modify horizon's settings file to enabled designatedashboard, note the two lines to add below::
import designatedashboard.enabled # ADD THIS LINE
...
INSTALLED_APPS = list(INSTALLED_APPS) # Make sure it's mutable
settings.update_dashboards([
openstack_dashboard.enabled,
openstack_dashboard.local.enabled,
designatedashboard.enabled, # ADD THIS LINE TOO
], HORIZON_CONFIG, INSTALLED_APPS)
3. (Optional) Copy the designate policy file into horizon's policy files folder, and add this config::
'dns': 'designate_policy.json',
# -*- coding: utf-8 -*-
# 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 pbr.version
__version__ = pbr.version.VersionInfo(
'designatedashboard').version_string()
from designatedashboard.api import designate # noqa
# 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
import logging
from openstack_dashboard.api.base import url_for # noqa
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
LOG.debug('designateclient connection created using token "%s"'
'and url "%s"' % (request.user.token.id, designate_url))
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)
# 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)
# 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 logging
from django.core import urlresolvers
from django.utils.translation import ugettext_lazy as _ # noqa
from horizon import messages
from horizon import tables
from horizon.utils import memoized
from designatedashboard import api
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):
try:
if self.table:
quota = api.designate.quota_get(request)
return quota['domains'] > len(self.table.data)
except:
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"
action_present = _("Delete")
action_past = _("Deleted")
data_type_singular = _("Domain")
data_type_plural = _("Domains")
classes = ('btn-danger', 'btn-delete')
policy_rules = (("dns", "delete_domain"),)
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 urlresolvers.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 urlresolvers.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.'''
data_type_singular = _("Record")
policy_rules = (("dns", "delete_record"),)
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"
action_present = _("Delete")
action_past = _("Deleted")
data_type_singular = _("Record")
classes = ('btn-danger', 'btn-delete')
policy_rules = (("dns", "delete_record"),)
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:
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 urlresolvers.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:
name = "records"
verbose_name = _("Records")
table_actions = (CreateRecord,)
row_actions = (EditRecord, DeleteRecord,)
multi_select = False
{% 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 %}
The Name field should contain a full-qualified domain name (with
trailing period).
{% endblocktrans %}</p>
<p>{% blocktrans %}
The Email field should contain a valid email address to be associated
with the domain.
{% endblocktrans %}</p>
<p>{% blocktrans %}
The optional TTL field can be any value between 0 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 %}
{% 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-right %}
<h3>{% trans "Description:" %}</h3>
{% blocktrans %}
<p>
<strong>Please note:</strong>
For record types, the name <strong>must</strong> end with a period.
</p>
<p>
<strong>TTL</strong>
The TTL is the time-to-live for the record, in seconds.
</p>
<p>
See <a href="http://en.wikipedia.org/wiki/List_of_DNS_record_types" target="_designate_record_defs">more info</a> on record types.
</p>
{% endblocktrans %}
{% endblock %}
{% 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>
{% 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>