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
This commit is contained in:
parent
2d752d0cc6
commit
97604666d6
|
@ -7,5 +7,5 @@ ChangeLog
|
|||
cue_dashboard.egg*
|
||||
build/
|
||||
.tox/
|
||||
.egg/
|
||||
.eggs/
|
||||
.idea/
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -17,6 +17,6 @@
|
|||
<dt>{% trans "Cluster Size" %}</dt>
|
||||
<dd>{{ cluster.size|default:_("-") }}</dd>
|
||||
<dt>{% trans "Endpoint" %}</dt>
|
||||
<dd>{{ cluster.url|default:_("-") }}</dd>
|
||||
<dd>{{ cluster.url|join:", " |default:_("-") }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
|
|
@ -1,3 +1,23 @@
|
|||
{% load i18n %}
|
||||
|
||||
<p>{% blocktrans %}Specify the details for creating a message queue cluster.{% endblocktrans %}</p>
|
||||
{% block help_message %}
|
||||
<p>{% blocktrans %}Specify the details for launching an instance.{% endblocktrans %}
|
||||
{% blocktrans %}The chart below shows the flavor information.{% endblocktrans %}</p>
|
||||
{% endblock %}
|
||||
|
||||
<h4>{% trans "Flavor Details" %}</h4>
|
||||
<table class="flavor_table table-striped">
|
||||
<tbody>
|
||||
<tr><td class="flavor_name">{% trans "Name" %}</td><td><span id="flavor_name"></span></td></tr>
|
||||
<tr><td class="flavor_name">{% trans "VCPUs" %}</td><td><span id="flavor_vcpus"></span></td></tr>
|
||||
<tr><td class="flavor_name">{% trans "Root Disk" %}</td><td><span id="flavor_disk"> </span> {% trans "GB" %}</td></tr>
|
||||
<tr><td class="flavor_name">{% trans "Ephemeral Disk" %}</td><td><span id="flavor_ephemeral"></span> {% trans "GB" %}</td></tr>
|
||||
<tr><td class="flavor_name">{% trans "Total Disk" %}</td><td><span id="flavor_disk_total"></span> {% trans "GB" %}</td></tr>
|
||||
<tr><td class="flavor_name">{% trans "RAM" %}</td><td><span id="flavor_ram"></span> {% trans "MB" %}</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
horizon.Quota.initWithFlavors({{ flavors|safe|default:"{}" }});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
{% load i18n %}
|
||||
|
||||
<p>
|
||||
{% 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 %}
|
||||
</p>
|
||||
<p>{% blocktrans %}<strong>Please note:</strong> You can only choose one network per cluster.{% endblocktrans %}</p>
|
|
@ -1,46 +0,0 @@
|
|||
{% load i18n %}
|
||||
|
||||
<noscript><h3>{{ step }}</h3></noscript>
|
||||
<table class="table-fixed" id="networkListSortContainer">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="col-sm-6">
|
||||
<label id="selected_network_label">{% trans "Selected networks" %}</label>
|
||||
<ul id="selected_network" class="networklist">
|
||||
</ul>
|
||||
<label>{% trans "Available networks" %}</label>
|
||||
<ul id="available_network" class="networklist">
|
||||
</ul>
|
||||
</td>
|
||||
<td class="col-sm-6">
|
||||
{% include "queues/_launch_network_help.html" %}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table class="table-fixed" id="networkListIdContainer">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="actions">
|
||||
<div id="networkListId">
|
||||
{% include "horizon/common/_form_fields.html" %}
|
||||
</div>
|
||||
</td>
|
||||
<td class="help_text">
|
||||
{{ step.get_help_text }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
<script>
|
||||
if (typeof $ !== 'undefined') {
|
||||
horizon.instances.workflow_init($(".workflow"));
|
||||
} else {
|
||||
addHorizonLoadEvent(function() {
|
||||
horizon.instances.workflow_init($(".workflow"));
|
||||
});
|
||||
}
|
||||
</script>
|
Loading…
Reference in New Issue