Akihiro Motoki ddaf10204a Django 2.0 support
Replace django.core.urlresolves with django.urls

(In Django 2.0) The django.core.urlresolvers module is removed
in favor of its new location, django.urls.
It was deprecated in Django 1.10:
https://docs.djangoproject.com/en/2.0/releases/1.10/#id3

The arguments of include() has been changed in Django 1.9
and the older style was dropped in Django 2.0.
https://docs.djangoproject.com/en/2.0/releases/1.9/#passing-a-3-tuple-or-an-app-name-to-include

Add py35dj20 job to test Django 2.0 integration.
Also drops older Django unit tests from tox.ini
as horizon dropped Django <=1.10 support in Rocky.

Depends-On: https://review.openstack.org/#/c/571061/
Change-Id: I122f8ad81807386517149f37aa8d63c76daac533
2018-06-08 13:27:11 +09:00

436 lines
15 KiB
Python

# Copyright (c) 2014 eBay Software Foundation
# Copyright 2015 HP Software, LLC
# 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 django import shortcuts
from django.template.defaultfilters import title # noqa
from django import urls
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ungettext_lazy
import six
from horizon import messages
from horizon import tables
from horizon.templatetags import sizeformat
from horizon.utils import filters
from horizon.utils import functions
from horizon.utils import memoized
from oslo_log import log as logging
from trove_dashboard import api
from trove_dashboard.content.database_clusters import cluster_manager
from trove_dashboard.content.databases import db_capability
LOG = logging.getLogger(__name__)
ACTIVE_STATES = ("ACTIVE",)
class DeleteCluster(tables.BatchAction):
name = "delete"
icon = "remove"
classes = ('btn-danger',)
help_text = _("Deleted cluster is not recoverable.")
@staticmethod
def action_present(count):
return ungettext_lazy(
u"Delete Cluster",
u"Delete Clusters",
count
)
@staticmethod
def action_past(count):
return ungettext_lazy(
u"Scheduled deletion of Cluster",
u"Scheduled deletion of Clusters",
count
)
def action(self, request, obj_id):
api.trove.cluster_delete(request, obj_id)
class LaunchLink(tables.LinkAction):
name = "launch"
verbose_name = _("Launch Cluster")
url = "horizon:project:database_clusters:launch"
classes = ("btn-launch", "ajax-modal")
icon = "cloud-upload"
class ClusterGrow(tables.LinkAction):
name = "cluster_grow"
verbose_name = _("Grow Cluster")
url = "horizon:project:database_clusters:cluster_grow_details"
def allowed(self, request, cluster=None):
if (cluster and cluster.task["name"] == 'NONE' and
db_capability.can_modify_cluster(cluster.datastore['type'])):
return True
return False
class ClusterShrink(tables.LinkAction):
name = "cluster_shrink"
verbose_name = _("Shrink Cluster")
url = "horizon:project:database_clusters:cluster_shrink_details"
def allowed(self, request, cluster=None):
if (cluster and cluster.task["name"] == 'NONE' and
db_capability.can_modify_cluster(cluster.datastore['type'])):
return True
return False
class ResetPassword(tables.LinkAction):
name = "reset_password"
verbose_name = _("Reset Root Password")
url = "horizon:project:database_clusters:reset_password"
classes = ("ajax-modal",)
def allowed(self, request, cluster=None):
if (cluster and cluster.task["name"] == 'NONE' and
db_capability.is_vertica_datastore(cluster.datastore['type'])):
return True
return False
def get_link_url(self, datum):
cluster_id = self.table.get_object_id(datum)
return urls.reverse(self.url, args=[cluster_id])
class UpdateRow(tables.Row):
ajax = True
@memoized.memoized_method
def get_data(self, request, cluster_id):
cluster = api.trove.cluster_get(request, cluster_id)
try:
# TODO(michayu): assumption that cluster is homogeneous
flavor_id = cluster.instances[0]['flavor']['id']
cluster.full_flavor = api.trove.flavor_get(request, flavor_id)
except Exception:
pass
return cluster
def get_datastore(cluster):
return cluster.datastore["type"]
def get_datastore_version(cluster):
return cluster.datastore["version"]
def get_size(cluster):
if db_capability.is_vertica_datastore(cluster.datastore['type']):
return "3"
if hasattr(cluster, "full_flavor"):
size_string = _("%(name)s | %(RAM)s RAM | %(instances)s instances")
vals = {'name': cluster.full_flavor.name,
'RAM': sizeformat.mbformat(cluster.full_flavor.ram),
'instances': len(cluster.instances)}
return size_string % vals
elif hasattr(cluster, "instances"):
return "%s instances" % len(cluster.instances)
return _("Not available")
def get_task(cluster):
return cluster.task["name"]
class ClustersTable(tables.DataTable):
TASK_CHOICES = (
("none", True),
)
name = tables.Column("name",
link=("horizon:project:database_clusters:detail"),
verbose_name=_("Cluster Name"))
datastore = tables.Column(get_datastore,
verbose_name=_("Datastore"))
datastore_version = tables.Column(get_datastore_version,
verbose_name=_("Datastore Version"))
size = tables.Column(get_size,
verbose_name=_("Cluster Size"),
attrs={'data-type': 'size'})
task = tables.Column(get_task,
filters=(title, filters.replace_underscores),
verbose_name=_("Current Task"),
status=True,
status_choices=TASK_CHOICES)
class Meta(object):
name = "clusters"
verbose_name = _("Clusters")
status_columns = ["task"]
row_class = UpdateRow
table_actions = (LaunchLink, DeleteCluster)
row_actions = (ClusterGrow, ClusterShrink, ResetPassword,
DeleteCluster)
def get_instance_size(instance):
if hasattr(instance, "full_flavor"):
size_string = _("%(name)s | %(RAM)s RAM")
vals = {'name': instance.full_flavor.name,
'RAM': sizeformat.mbformat(instance.full_flavor.ram)}
return size_string % vals
return _("Not available")
def get_instance_type(instance):
if hasattr(instance, "type"):
return instance.type
return _("Not available")
def get_host(instance):
if hasattr(instance, "hostname"):
return instance.hostname
elif hasattr(instance, "ip") and instance.ip:
return instance.ip[0]
return _("Not Assigned")
class InstancesTable(tables.DataTable):
name = tables.Column("name",
verbose_name=_("Name"))
type = tables.Column(get_instance_type,
verbose_name=_("Type"))
host = tables.Column(get_host,
verbose_name=_("Host"))
size = tables.Column(get_instance_size,
verbose_name=_("Size"),
attrs={'data-type': 'size'})
status = tables.Column("status",
filters=(title, filters.replace_underscores),
verbose_name=_("Status"))
class Meta(object):
name = "instances"
verbose_name = _("Instances")
class ClusterShrinkAction(tables.BatchAction):
name = "cluster_shrink_action"
icon = "remove"
classes = ('btn-danger',)
success_url = 'horizon:project:database_clusters:index'
help_text = _("Shrinking a cluster is not recoverable.")
@staticmethod
def action_present(count):
return ungettext_lazy(
u"Shrink Cluster",
u"Shrink Cluster",
count
)
@staticmethod
def action_past(count):
return ungettext_lazy(
u"Scheduled Shrinking of Cluster",
u"Scheduled Shrinking of Cluster",
count
)
def handle(self, table, request, obj_ids):
datum_display_objs = []
for datum_id in obj_ids:
datum = table.get_object_by_id(datum_id)
datum_display = table.get_object_display(datum) or datum_id
datum_display_objs.append(datum_display)
display_str = functions.lazy_join(", ", datum_display_objs)
try:
cluster_id = table.kwargs['cluster_id']
data = [{'id': instance_id} for instance_id in obj_ids]
api.trove.cluster_shrink(request, cluster_id, data)
LOG.info('%s: "%s"' %
(self._get_action_name(past=True),
display_str))
msg = _('Removed instances from cluster.')
messages.info(request, msg)
except Exception as ex:
LOG.error('Action %(action)s failed with %(ex)s for %(data)s' %
{'action': self._get_action_name(past=True).lower(),
'ex': six.text_type(ex),
'data': display_str})
msg = _('Unable to remove instances from cluster: %s')
messages.error(request, msg % six.text_type(ex))
return shortcuts.redirect(self.get_success_url(request))
class ClusterShrinkInstancesTable(tables.DataTable):
name = tables.Column("name",
verbose_name=_("Name"))
status = tables.Column("status",
filters=(title, filters.replace_underscores),
verbose_name=_("Status"))
class Meta(object):
name = "shrink_cluster_table"
verbose_name = _("Instances")
table_actions = (ClusterShrinkAction,)
row_actions = (ClusterShrinkAction,)
class ClusterGrowAddInstance(tables.LinkAction):
name = "cluster_grow_add_instance"
verbose_name = _("Add Instance")
url = "horizon:project:database_clusters:add_instance"
classes = ("ajax-modal",)
def get_link_url(self):
return urls.reverse(
self.url, args=[self.table.kwargs['cluster_id']])
class ClusterGrowRemoveInstance(tables.BatchAction):
name = "cluster_grow_remove_instance"
@staticmethod
def action_present(count):
return ungettext_lazy(
u"Remove Instance",
u"Remove Instances",
count
)
@staticmethod
def action_past(count):
return ungettext_lazy(
u"Removed Instance",
u"Removed Instances",
count
)
def action(self, request, datum_id):
manager = cluster_manager.get(self.table.kwargs['cluster_id'])
manager.delete_instance(datum_id)
def handle(self, table, request, obj_ids):
action_success = []
action_failure = []
action_not_allowed = []
for datum_id in obj_ids:
datum = table.get_object_by_id(datum_id)
datum_display = table.get_object_display(datum) or datum_id
if not table._filter_action(self, request, datum):
action_not_allowed.append(datum_display)
LOG.warning('Permission denied to %s: "%s"' %
(self._get_action_name(past=True).lower(),
datum_display))
continue
try:
self.action(request, datum_id)
# Call update to invoke changes if needed
self.update(request, datum)
action_success.append(datum_display)
self.success_ids.append(datum_id)
LOG.info('%s: "%s"' %
(self._get_action_name(past=True), datum_display))
except Exception as ex:
# Handle the exception but silence it since we'll display
# an aggregate error message later. Otherwise we'd get
# multiple error messages displayed to the user.
action_failure.append(datum_display)
action_description = (
self._get_action_name(past=True).lower(), datum_display)
LOG.error(
'Action %(action)s Failed for %(reason)s', {
'action': action_description, 'reason': ex})
if action_not_allowed:
msg = _('You are not allowed to %(action)s: %(objs)s')
params = {"action":
self._get_action_name(action_not_allowed).lower(),
"objs": functions.lazy_join(", ", action_not_allowed)}
messages.error(request, msg % params)
if action_failure:
msg = _('Unable to %(action)s: %(objs)s')
params = {"action": self._get_action_name(action_failure).lower(),
"objs": functions.lazy_join(", ", action_failure)}
messages.error(request, msg % params)
return shortcuts.redirect(self.get_success_url(request))
class ClusterGrowAction(tables.Action):
name = "grow_cluster_action"
verbose_name = _("Grow Cluster")
verbose_name_plural = _("Grow Cluster")
requires_input = False
icon = "plus"
def handle(self, table, request, obj_ids):
if not table.data:
msg = _("Cannot grow cluster. No instances specified.")
messages.info(request, msg)
return shortcuts.redirect(request.build_absolute_uri())
datum_display_objs = []
for instance in table.data:
msg = _("[flavor=%(flavor)s, volume=%(volume)s, name=%(name)s, "
"type=%(type)s, related_to=%(related_to)s, "
"nics=%(nics)s]")
params = {"flavor": instance.flavor_id, "volume": instance.volume,
"name": instance.name, "type": instance.type,
"related_to": instance.related_to, "nics": instance.nics}
datum_display_objs.append(msg % params)
display_str = functions.lazy_join(", ", datum_display_objs)
cluster_id = table.kwargs['cluster_id']
try:
api.trove.cluster_grow(request, cluster_id, table.data)
LOG.info('%s: "%s"' % (_("Grow Cluster"), display_str))
msg = _('Scheduled growing of cluster.')
messages.success(request, msg)
except Exception as ex:
LOG.error('Action grow cluster failed with %(ex)s for %(data)s' %
{'ex': six.text_type(ex),
'data': display_str})
msg = _('Unable to grow cluster: %s')
messages.error(request, msg % six.text_type(ex))
finally:
cluster_manager.delete(cluster_id)
return shortcuts.redirect(urls.reverse(
"horizon:project:database_clusters:index"))
class ClusterGrowInstancesTable(tables.DataTable):
id = tables.Column("id", hidden=True)
name = tables.Column("name", verbose_name=_("Name"))
flavor = tables.Column("flavor", verbose_name=_("Flavor"))
flavor_id = tables.Column("flavor_id", hidden=True)
volume = tables.Column("volume", verbose_name=_("Volume"))
type = tables.Column("type", verbose_name=_("Instance Type"))
related_to = tables.Column("related_to", verbose_name=_("Related To"))
nics = tables.Column("nics", verbose_name=_("Network"))
class Meta(object):
name = "cluster_grow_instances_table"
verbose_name = _("Instances")
table_actions = (ClusterGrowAddInstance, ClusterGrowRemoveInstance,
ClusterGrowAction)
row_actions = (ClusterGrowRemoveInstance,)