From 97604666d63e5eab39df4a0860fdaf3f28d8f98b Mon Sep 17 00:00:00 2001 From: Steve Leon Date: Tue, 24 Mar 2015 15:58:45 -0700 Subject: [PATCH] Making multiple fixes to the cue dashboard - Renaming column names - Adding endpoint URL into the table list - Username and password field in create cue form - Remove network widget and replace with dropdown - Add flavor details to create cluster - Update tooltips Change-Id: I16f5cde7109931b40c80108fa1dc76cd6597255a --- .gitignore | 2 +- cuedashboard/api.py | 9 +- cuedashboard/queues/tables.py | 11 +- cuedashboard/queues/views.py | 5 - cuedashboard/queues/workflows.py | 128 ++++++++++-------- .../templates/queues/_detail_overview.html | 2 +- .../queues/_launch_details_help.html | 22 ++- .../queues/_launch_network_help.html | 10 -- .../templates/queues/_launch_networks.html | 46 ------- 9 files changed, 103 insertions(+), 132 deletions(-) delete mode 100644 cuedashboard/templates/queues/_launch_network_help.html delete mode 100644 cuedashboard/templates/queues/_launch_networks.html diff --git a/.gitignore b/.gitignore index dd33ae6..67e2cdc 100644 --- a/.gitignore +++ b/.gitignore @@ -7,5 +7,5 @@ ChangeLog cue_dashboard.egg* build/ .tox/ -.egg/ +.eggs/ .idea/ diff --git a/cuedashboard/api.py b/cuedashboard/api.py index 0e30b9c..c0c94a9 100644 --- a/cuedashboard/api.py +++ b/cuedashboard/api.py @@ -67,10 +67,7 @@ def flavor(request, flavor_id): # This is needed because the cue client returns a dict # instead of a cluster object. def _to_cluster_object(cluster_dict): - endpoint = (cluster_dict['end_points'][0] if cluster_dict['end_points'] - else None) - if endpoint: - cluster_dict['url'] = "".join((endpoint['type'], - '://', - endpoint['uri'])) + endpoints = ["".join((endpoint['type'], '://', endpoint['uri'])) + for endpoint in cluster_dict['end_points']] + cluster_dict['url'] = endpoints return namedtuple('Cluster', cluster_dict)(**cluster_dict) diff --git a/cuedashboard/queues/tables.py b/cuedashboard/queues/tables.py index 3019c35..f3d5c98 100644 --- a/cuedashboard/queues/tables.py +++ b/cuedashboard/queues/tables.py @@ -20,10 +20,6 @@ from django.utils.translation import ungettext_lazy from django.utils.translation import ugettext as _ from horizon import tables from cuedashboard import api -import logging - - -LOG = logging.getLogger(__name__) class CreateCluster(tables.LinkAction): @@ -66,11 +62,18 @@ class UpdateRow(tables.Row): return api.cluster_get(request, cluster_id) +def format_endpoints(cluster): + if hasattr(cluster, "url"): + return ', '.join(cluster.url) + return "-" + + class ClusterTable(tables.DataTable): name = tables.Column("name", verbose_name=_("Name"), link='horizon:project:queues:detail') size = tables.Column("size", verbose_name=_("Cluster Size"),) + endpoint = tables.Column(format_endpoints, verbose_name=_("Endpoints")) status = tables.Column("status", verbose_name=_("Status")) class Meta: diff --git a/cuedashboard/queues/views.py b/cuedashboard/queues/views.py index 4065282..c274c4b 100644 --- a/cuedashboard/queues/views.py +++ b/cuedashboard/queues/views.py @@ -16,8 +16,6 @@ # Copyright [2014] Hewlett-Packard Development Company, L.P. # limitations under the License. -import logging - from cuedashboard import api from cuedashboard.queues.tables import ClusterTable from cuedashboard.queues.tabs import ClusterDetailTabs @@ -30,9 +28,6 @@ from horizon.utils import memoized from horizon import workflows -LOG = logging.getLogger(__name__) - - class IndexView(tables.DataTableView): table_class = ClusterTable template_name = 'queues/index.html' diff --git a/cuedashboard/queues/workflows.py b/cuedashboard/queues/workflows.py index ed07957..7b3cb66 100644 --- a/cuedashboard/queues/workflows.py +++ b/cuedashboard/queues/workflows.py @@ -13,14 +13,16 @@ # under the License. import logging - +import json from django.conf import settings from django.core.urlresolvers import reverse from django.utils.translation import ugettext_lazy as _ +from django.forms import ValidationError from horizon import exceptions from horizon import forms from horizon.utils import memoized +from horizon.utils import validators from horizon import workflows from openstack_dashboard import api from cuedashboard.api import cluster_create @@ -32,14 +34,58 @@ from openstack_dashboard.dashboards.project.instances \ LOG = logging.getLogger(__name__) +class PasswordMixin(forms.SelfHandlingForm): + password = forms.RegexField( + label=_("Password"), + widget=forms.PasswordInput(render_value=False), + regex=validators.password_validator(), + error_messages={'invalid': validators.password_validator_msg()}) + confirm_password = forms.CharField( + label=_("Confirm Password"), + widget=forms.PasswordInput(render_value=False)) + no_autocomplete = True + + def clean(self): + '''Check to make sure password fields match.''' + data = super(forms.Form, self).clean() + if 'password' in data: + if data['password'] != data.get('confirm_password', None): + raise ValidationError(_('Passwords do not match.')) + return data + + class SetInstanceDetailsAction(workflows.Action): name = forms.CharField(max_length=80, label=_("Cluster Name")) flavor = forms.ChoiceField(label=_("Flavor"), - help_text=_("Size of image to launch.")) + help_text=_("The amount of RAM and CPU included \ + in each node of the cluster.")) size = forms.IntegerField(label=_("Cluster Size"), min_value=1, initial=1, - help_text=_("Size of cluster.")) + help_text=_("The number of nodes that make up \ + the cluster.")) + network = forms.ChoiceField(label=_("Network"), + help_text=_("Network to attach to the \ + cluster.")) + username = forms.CharField(max_length=80, label=_("User Name"), + help_text=_("User name for logging into the \ + RabbitMQ Management UI.")) + password = forms.RegexField( + label=_("Password"), + widget=forms.PasswordInput(render_value=False), + regex=validators.password_validator(), + error_messages={'invalid': validators.password_validator_msg()}) + confirm_password = forms.CharField( + label=_("Confirm Password"), + widget=forms.PasswordInput(render_value=False)) + + def clean(self): + '''Check to make sure password fields match.''' + data = super(forms.Form, self).clean() + if 'password' in data: + if data['password'] != data.get('confirm_password', None): + raise ValidationError(_('Passwords do not match.')) + return data class Meta(object): name = _("Details") @@ -62,41 +108,8 @@ class SetInstanceDetailsAction(workflows.Action): return instance_utils.sort_flavor_list(request, flavors) return [] - -class SetClusterDetails(workflows.Step): - action_class = SetInstanceDetailsAction - contributes = ("name", "flavor", "size") - - -class SetNetworkAction(workflows.Action): - network = forms.MultipleChoiceField(label=_("Networks"), - widget=forms.CheckboxSelectMultiple(), - error_messages={ - 'required': _( - "At least one network must" - " be specified.")}, - help_text=_("Create cluster with" - " these networks")) - - def __init__(self, request, *args, **kwargs): - super(SetNetworkAction, self).__init__(request, *args, **kwargs) - network_list = self.fields["network"].choices - if len(network_list) == 1: - self.fields['network'].initial = [network_list[0][0]] - - class Meta(object): - name = _("Networking") - permissions = ('openstack.services.network',) - help_text = _("Select networks for your cluster.") - - def clean(self): - # Cue does not currently support attaching multiple networks. - if len(self.data.getlist("network", None)) > 1: - msg = _("You must select only one network.") - self._errors["network"] = self.error_class([msg]) - return self.cleaned_data - - def populate_network_choices(self, request, context): + @memoized.memoized_method + def networks(self, request): try: tenant_id = self.request.user.tenant_id networks = api.neutron.network_list_for_tenant(request, tenant_id) @@ -108,25 +121,25 @@ class SetNetworkAction(workflows.Action): _('Unable to retrieve networks.')) return network_list + def populate_network_choices(self, request, context): + return self.networks(request) -class SetNetwork(workflows.Step): - action_class = SetNetworkAction - template_name = "queues/_launch_networks.html" - contributes = ("network_id",) + def get_help_text(self, extra_context=None): + extra = {} if extra_context is None else dict(extra_context) + flavors = json.dumps([f._info for f in + instance_utils.flavor_list(self.request)]) + try: + extra['flavors'] = flavors - def contribute(self, data, context): - if data: - networks = self.workflow.request.POST.getlist("network") - # If no networks are explicitly specified, network list - # contains an empty string, so remove it. - networks = [n for n in networks if n != ''] - if networks: - # TODO - # Choosing the first networks until Cue - # supports more than one networks. - context['network_id'] = networks[0] + except Exception: + exceptions.handle(self.request, + _("Unable to retrieve quota information.")) + return super(SetInstanceDetailsAction, self).get_help_text(extra) - return context + +class SetClusterDetails(workflows.Step): + action_class = SetInstanceDetailsAction + contributes = ("name", "flavor", "size", "network") class CreateCluster(workflows.Workflow): @@ -136,8 +149,7 @@ class CreateCluster(workflows.Workflow): success_message = _('Created cluster named "%(name)s".') failure_message = _('Unable to create cluster named "%(name)s".') success_url = "horizon:project:queues:index" - default_steps = (SetClusterDetails, - SetNetwork) + default_steps = (SetClusterDetails,) def __init__(self, request=None, context_seed=None, entry_point=None, *args, **kwargs): @@ -155,9 +167,9 @@ class CreateCluster(workflows.Workflow): LOG.info("Launching message queue cluster with parameters " "{name=%s, flavor=%s, size=%s, nics=%s}", context['name'], context['flavor'], - context['size'], context['network_id']) + context['size'], context['network']) - cluster_create(request, context['name'], context['network_id'], + cluster_create(request, context['name'], context['network'], context['flavor'], context['size']) return True except Exception: diff --git a/cuedashboard/templates/queues/_detail_overview.html b/cuedashboard/templates/queues/_detail_overview.html index 07d270f..e82682f 100644 --- a/cuedashboard/templates/queues/_detail_overview.html +++ b/cuedashboard/templates/queues/_detail_overview.html @@ -17,6 +17,6 @@
{% trans "Cluster Size" %}
{{ cluster.size|default:_("-") }}
{% trans "Endpoint" %}
-
{{ cluster.url|default:_("-") }}
+
{{ cluster.url|join:", " |default:_("-") }}
diff --git a/cuedashboard/templates/queues/_launch_details_help.html b/cuedashboard/templates/queues/_launch_details_help.html index 17d5bbf..1f0e735 100644 --- a/cuedashboard/templates/queues/_launch_details_help.html +++ b/cuedashboard/templates/queues/_launch_details_help.html @@ -1,3 +1,23 @@ {% load i18n %} -

{% blocktrans %}Specify the details for creating a message queue cluster.{% endblocktrans %}

+{% block help_message %} +

{% blocktrans %}Specify the details for launching an instance.{% endblocktrans %} +{% blocktrans %}The chart below shows the flavor information.{% endblocktrans %}

+{% endblock %} + +

{% trans "Flavor Details" %}

+ + + + + + + + + +
{% trans "Name" %}
{% trans "VCPUs" %}
{% trans "Root Disk" %} {% trans "GB" %}
{% trans "Ephemeral Disk" %} {% trans "GB" %}
{% trans "Total Disk" %} {% trans "GB" %}
{% trans "RAM" %} {% trans "MB" %}
+ + + diff --git a/cuedashboard/templates/queues/_launch_network_help.html b/cuedashboard/templates/queues/_launch_network_help.html deleted file mode 100644 index e2a9229..0000000 --- a/cuedashboard/templates/queues/_launch_network_help.html +++ /dev/null @@ -1,10 +0,0 @@ -{% load i18n %} - -

- {% blocktrans %} - Move networks from 'Available Networks' to 'Selected Networks' by - clicking the button, or dragging and dropping. You can change the - NIC order by dragging and dropping as well. - {% endblocktrans %} -

-

{% blocktrans %}Please note: You can only choose one network per cluster.{% endblocktrans %}

diff --git a/cuedashboard/templates/queues/_launch_networks.html b/cuedashboard/templates/queues/_launch_networks.html deleted file mode 100644 index 3ac9a44..0000000 --- a/cuedashboard/templates/queues/_launch_networks.html +++ /dev/null @@ -1,46 +0,0 @@ -{% load i18n %} - - - - - - - - - -
- -
    -
- -
    -
-
- {% include "queues/_launch_network_help.html" %} -
- - - - - - - - -
-
- {% include "horizon/common/_form_fields.html" %} -
-
- {{ step.get_help_text }} -
- - - \ No newline at end of file