Adding tox file for gate testing and some other fixes
- Renaming some labels - Adding title to the cluster page - Also removing some debuging logs - Fix code to pass pep8 - Remove volume size - Display flavor name instead of ID in detail page Change-Id: Ia3351eace341797f48275c94b8bc352fd83791c4
This commit is contained in:
parent
76225d2aae
commit
ab15cbca8d
|
@ -6,4 +6,6 @@ ChangeLog
|
||||||
.DS_Store
|
.DS_Store
|
||||||
cue_dashboard.egg*
|
cue_dashboard.egg*
|
||||||
build/
|
build/
|
||||||
|
.tox/
|
||||||
|
.egg/
|
||||||
.idea/
|
.idea/
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
PANEL_GROUP = 'queuesgroup'
|
PANEL_GROUP = 'queuesgroup'
|
||||||
PANEL_GROUP_NAME = _('Message Queues')
|
PANEL_GROUP_NAME = _('Messaging')
|
||||||
PANEL_GROUP_DASHBOARD = 'project'
|
PANEL_GROUP_DASHBOARD = 'project'
|
|
@ -21,14 +21,14 @@ from cueclient.v1 import client
|
||||||
from keystoneclient import session as ksc_session
|
from keystoneclient import session as ksc_session
|
||||||
from keystoneclient.auth.identity import v2
|
from keystoneclient.auth.identity import v2
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from openstack_dashboard.api import base
|
from openstack_dashboard import api
|
||||||
from horizon.utils.memoized import memoized # noqa
|
from horizon.utils.memoized import memoized # noqa
|
||||||
|
|
||||||
|
|
||||||
@memoized
|
@memoized
|
||||||
def cueclient(request):
|
def cueclient(request):
|
||||||
cacert = getattr(settings, 'OPENSTACK_SSL_CACERT', None)
|
cacert = getattr(settings, 'OPENSTACK_SSL_CACERT', None)
|
||||||
auth_url = base.url_for(request, 'identity')
|
auth_url = api.base.url_for(request, 'identity')
|
||||||
auth = v2.Token(auth_url, request.user.token.id,
|
auth = v2.Token(auth_url, request.user.token.id,
|
||||||
tenant_id=request.user.tenant_id,
|
tenant_id=request.user.tenant_id,
|
||||||
tenant_name=request.user.tenant_name)
|
tenant_name=request.user.tenant_name)
|
||||||
|
@ -50,17 +50,27 @@ def cluster_get(request, cluster_id):
|
||||||
return cluster
|
return cluster
|
||||||
|
|
||||||
|
|
||||||
def cluster_create(request, name, nic, flavor, size, volume_size):
|
def cluster_create(request, name, nic, flavor, size):
|
||||||
return cueclient(request).clusters.create(name, nic,flavor,
|
return cueclient(request).clusters.create(name, nic[0],
|
||||||
size, volume_size)
|
flavor, size, 0)
|
||||||
|
|
||||||
|
|
||||||
def delete_cluster(request, cluster_id):
|
def delete_cluster(request, cluster_id):
|
||||||
return cueclient(request).clusters.delete(cluster_id)
|
return cueclient(request).clusters.delete(cluster_id)
|
||||||
|
|
||||||
|
|
||||||
#todo
|
def flavor(request, flavor_id):
|
||||||
#This is needed because the cue client returns a dict
|
return api.nova.flavor_get(request, flavor_id)
|
||||||
#instead of a cluster object.
|
|
||||||
|
|
||||||
|
# todo
|
||||||
|
# This is needed because the cue client returns a dict
|
||||||
|
# instead of a cluster object.
|
||||||
def _to_cluster_object(cluster_dict):
|
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']))
|
||||||
return namedtuple('Cluster', cluster_dict)(**cluster_dict)
|
return namedtuple('Cluster', cluster_dict)(**cluster_dict)
|
|
@ -24,4 +24,3 @@ class CuePanel(horizon.Panel):
|
||||||
name = _("Clusters")
|
name = _("Clusters")
|
||||||
slug = 'queues'
|
slug = 'queues'
|
||||||
permissions = ('openstack.services.message_queue',)
|
permissions = ('openstack.services.message_queue',)
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,10 @@ from django.utils.translation import ungettext_lazy
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from horizon import tables
|
from horizon import tables
|
||||||
from cuedashboard import api
|
from cuedashboard import api
|
||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class CreateCluster(tables.LinkAction):
|
class CreateCluster(tables.LinkAction):
|
||||||
|
@ -55,16 +59,23 @@ class DeleteCluster(tables.BatchAction):
|
||||||
api.delete_cluster(request, obj_id)
|
api.delete_cluster(request, obj_id)
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateRow(tables.Row):
|
||||||
|
ajax = True
|
||||||
|
|
||||||
|
def get_data(self, request, cluster_id):
|
||||||
|
return api.cluster_get(request, cluster_id)
|
||||||
|
|
||||||
|
|
||||||
class ClusterTable(tables.DataTable):
|
class ClusterTable(tables.DataTable):
|
||||||
name = tables.Column("name",
|
name = tables.Column("name",
|
||||||
verbose_name=_("Name"),
|
verbose_name=_("Name"),
|
||||||
link='horizon:project:queues:detail')
|
link='horizon:project:queues:detail')
|
||||||
size = tables.Column("size", verbose_name=_("Size"),)
|
size = tables.Column("size", verbose_name=_("Cluster Size"),)
|
||||||
flavor = tables.Column("flavor", verbose_name=_("Flavor"),)
|
|
||||||
status = tables.Column("status", verbose_name=_("Status"))
|
status = tables.Column("status", verbose_name=_("Status"))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
name = "clusters"
|
name = "clusters"
|
||||||
verbose_name = _("Clusters")
|
verbose_name = _("Clusters")
|
||||||
|
row_class = UpdateRow
|
||||||
table_actions = (CreateCluster, DeleteCluster,)
|
table_actions = (CreateCluster, DeleteCluster,)
|
||||||
row_actions = (DeleteCluster,)
|
row_actions = (DeleteCluster,)
|
||||||
|
|
|
@ -26,6 +26,8 @@ CLUSTERS = r'^(?P<cluster_id>[^/]+)/%s$'
|
||||||
urlpatterns = patterns('',
|
urlpatterns = patterns('',
|
||||||
url(r'^$', views.IndexView.as_view(),
|
url(r'^$', views.IndexView.as_view(),
|
||||||
name='index'),
|
name='index'),
|
||||||
url(CLUSTERS % '', views.DetailView.as_view(), name='detail'),
|
url(CLUSTERS % '', views.DetailView.as_view(),
|
||||||
url(r'^create$', views.CreateClusterView.as_view(), name='create'),
|
name='detail'),
|
||||||
)
|
url(r'^create$', views.CreateClusterView.as_view(),
|
||||||
|
name='create'),
|
||||||
|
)
|
||||||
|
|
|
@ -36,6 +36,7 @@ LOG = logging.getLogger(__name__)
|
||||||
class IndexView(tables.DataTableView):
|
class IndexView(tables.DataTableView):
|
||||||
table_class = ClusterTable
|
table_class = ClusterTable
|
||||||
template_name = 'queues/index.html'
|
template_name = 'queues/index.html'
|
||||||
|
page_title = _("Clusters")
|
||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
return api.clusters_list(self.request)
|
return api.clusters_list(self.request)
|
||||||
|
@ -57,6 +58,8 @@ class DetailView(horizon_tabs.TabbedTableView):
|
||||||
cluster = self.get_data()
|
cluster = self.get_data()
|
||||||
table = ClusterTable(self.request)
|
table = ClusterTable(self.request)
|
||||||
context["cluster"] = cluster
|
context["cluster"] = cluster
|
||||||
|
flavor = api.flavor(self.request, cluster.flavor)
|
||||||
|
context["flavor"] = flavor.name
|
||||||
context["url"] = self.get_redirect_url()
|
context["url"] = self.get_redirect_url()
|
||||||
context["actions"] = table.render_row_actions(cluster)
|
context["actions"] = table.render_row_actions(cluster)
|
||||||
return context
|
return context
|
||||||
|
@ -66,15 +69,16 @@ class DetailView(horizon_tabs.TabbedTableView):
|
||||||
|
|
||||||
cluster_id = self.kwargs['cluster_id']
|
cluster_id = self.kwargs['cluster_id']
|
||||||
cluster = api.cluster_get(self.request, cluster_id)
|
cluster = api.cluster_get(self.request, cluster_id)
|
||||||
LOG.info('hlahaha')
|
|
||||||
LOG.info(cluster)
|
|
||||||
LOG.info(type(cluster))
|
|
||||||
return cluster
|
return cluster
|
||||||
|
|
||||||
|
|
||||||
def get_tabs(self, request, *args, **kwargs):
|
def get_tabs(self, request, *args, **kwargs):
|
||||||
cluster = self.get_data()
|
cluster = self.get_data()
|
||||||
return self.tab_group_class(request, cluster=cluster, **kwargs)
|
# Get flavor name
|
||||||
|
flavor = api.flavor(self.request, cluster.flavor)
|
||||||
|
return self.tab_group_class(request,
|
||||||
|
cluster=flavor,
|
||||||
|
flavor=flavor.name,
|
||||||
|
**kwargs)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_redirect_url():
|
def get_redirect_url():
|
||||||
|
|
|
@ -36,20 +36,15 @@ class SetInstanceDetailsAction(workflows.Action):
|
||||||
name = forms.CharField(max_length=80, label=_("Cluster Name"))
|
name = forms.CharField(max_length=80, label=_("Cluster Name"))
|
||||||
flavor = forms.ChoiceField(label=_("Flavor"),
|
flavor = forms.ChoiceField(label=_("Flavor"),
|
||||||
help_text=_("Size of image to launch."))
|
help_text=_("Size of image to launch."))
|
||||||
size = forms.IntegerField(label=_("Size"),
|
size = forms.IntegerField(label=_("Cluster Size"),
|
||||||
min_value=0,
|
min_value=1,
|
||||||
initial=1,
|
initial=1,
|
||||||
help_text=_("Size of cluster."))
|
help_text=_("Size of cluster."))
|
||||||
volume = forms.IntegerField(label=_("Volume Size"),
|
|
||||||
min_value=0,
|
|
||||||
initial=1,
|
|
||||||
help_text=_("Size of the volume in GB."))
|
|
||||||
|
|
||||||
class Meta(object):
|
class Meta(object):
|
||||||
name = _("Details")
|
name = _("Details")
|
||||||
help_text_template = "queues/_launch_details_help.html"
|
help_text_template = "queues/_launch_details_help.html"
|
||||||
|
|
||||||
|
|
||||||
@memoized.memoized_method
|
@memoized.memoized_method
|
||||||
def flavors(self, request):
|
def flavors(self, request):
|
||||||
try:
|
try:
|
||||||
|
@ -70,7 +65,7 @@ class SetInstanceDetailsAction(workflows.Action):
|
||||||
|
|
||||||
class SetClusterDetails(workflows.Step):
|
class SetClusterDetails(workflows.Step):
|
||||||
action_class = SetInstanceDetailsAction
|
action_class = SetInstanceDetailsAction
|
||||||
contributes = ("name", "volume", "flavor", "size")
|
contributes = ("name", "flavor", "size")
|
||||||
|
|
||||||
|
|
||||||
class SetNetworkAction(workflows.Action):
|
class SetNetworkAction(workflows.Action):
|
||||||
|
@ -94,6 +89,13 @@ class SetNetworkAction(workflows.Action):
|
||||||
permissions = ('openstack.services.network',)
|
permissions = ('openstack.services.network',)
|
||||||
help_text = _("Select networks for your cluster.")
|
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):
|
def populate_network_choices(self, request, context):
|
||||||
try:
|
try:
|
||||||
tenant_id = self.request.user.tenant_id
|
tenant_id = self.request.user.tenant_id
|
||||||
|
@ -119,9 +121,9 @@ class SetNetwork(workflows.Step):
|
||||||
# contains an empty string, so remove it.
|
# contains an empty string, so remove it.
|
||||||
networks = [n for n in networks if n != '']
|
networks = [n for n in networks if n != '']
|
||||||
if networks:
|
if networks:
|
||||||
#TODO
|
# TODO
|
||||||
#Choosing the first networks until Cue
|
# Choosing the first networks until Cue
|
||||||
#supports more than one networks.
|
# supports more than one networks.
|
||||||
context['network_id'] = networks[0]
|
context['network_id'] = networks[0]
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
@ -151,12 +153,12 @@ class CreateCluster(workflows.Workflow):
|
||||||
def handle(self, request, context):
|
def handle(self, request, context):
|
||||||
try:
|
try:
|
||||||
LOG.info("Launching message queue cluster with parameters "
|
LOG.info("Launching message queue cluster with parameters "
|
||||||
"{name=%s, volume=%s, flavor=%s, size=%s, nics=%s}",
|
"{name=%s, flavor=%s, size=%s, nics=%s}",
|
||||||
context['name'], context['volume'], context['flavor'],
|
context['name'], context['flavor'],
|
||||||
context['size'], context['network_id'])
|
context['size'], context['network_id'])
|
||||||
|
|
||||||
cluster_create(request, context['name'], context['network_id'],
|
cluster_create(request, context['name'], context['network_id'],
|
||||||
context['flavor'], context['size'], context['volume'])
|
context['flavor'], context['size'])
|
||||||
return True
|
return True
|
||||||
except Exception:
|
except Exception:
|
||||||
exceptions.handle(request)
|
exceptions.handle(request)
|
||||||
|
|
|
@ -13,12 +13,10 @@
|
||||||
<dt>{% trans "Network" %}</dt>
|
<dt>{% trans "Network" %}</dt>
|
||||||
<dd>{{ cluster.network_id|default:_("-") }}</dd>
|
<dd>{{ cluster.network_id|default:_("-") }}</dd>
|
||||||
<dt>{% trans "Flavor" %}</dt>
|
<dt>{% trans "Flavor" %}</dt>
|
||||||
<dd>{{ cluster.flavor|default:_("-") }}</dd>
|
<dd>{{ flavor|default:_("-") }}</dd>
|
||||||
<dt>{% trans "Size" %}</dt>
|
<dt>{% trans "Cluster Size" %}</dt>
|
||||||
<dd>{{ cluster.size|default:_("-") }}</dd>
|
<dd>{{ cluster.size|default:_("-") }}</dd>
|
||||||
<dt>{% trans "Volume Size" %}</dt>
|
<dt>{% trans "Endpoint" %}</dt>
|
||||||
<dd>{{ cluster.volume_size|default:_("-") }}</dd>
|
<dd>{{ cluster.url|default:_("-") }}</dd>
|
||||||
<dt>{% trans "Endpoints" %}</dt>
|
|
||||||
<dd>{{ cluster.end_points|default:_("-") }}</dd>
|
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
|
@ -1,4 +1,3 @@
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
<p>{% blocktrans %}Specify the details for creating a message queue cluster.{% endblocktrans %}</p>
|
<p>{% blocktrans %}Specify the details for creating a message queue cluster.{% endblocktrans %}</p>
|
||||||
<p>{% blocktrans %}<strong>Please note:</strong> The cluster size must be at least 3{% endblocktrans %}</p>
|
|
|
@ -7,3 +7,4 @@
|
||||||
NIC order by dragging and dropping as well.
|
NIC order by dragging and dropping as well.
|
||||||
{% endblocktrans %}
|
{% endblocktrans %}
|
||||||
</p>
|
</p>
|
||||||
|
<p>{% blocktrans %}<strong>Please note:</strong> You can only choose one network per cluster.{% endblocktrans %}</p>
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<table class="table-fixed" id="networkListSortContainer">
|
<table class="table-fixed" id="networkListSortContainer">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="actions">
|
<td class="col-sm-6">
|
||||||
<label id="selected_network_label">{% trans "Selected networks" %}</label>
|
<label id="selected_network_label">{% trans "Selected networks" %}</label>
|
||||||
<ul id="selected_network" class="networklist">
|
<ul id="selected_network" class="networklist">
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -12,7 +12,7 @@
|
||||||
<ul id="available_network" class="networklist">
|
<ul id="available_network" class="networklist">
|
||||||
</ul>
|
</ul>
|
||||||
</td>
|
</td>
|
||||||
<td class="help_text">
|
<td class="col-sm-6">
|
||||||
{% include "queues/_launch_network_help.html" %}
|
{% include "queues/_launch_network_help.html" %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}{% trans "Queues" %}{% endblock %}
|
{% block title %}{% trans "Clusters" %}{% endblock %}
|
||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
{{ table.render }}
|
{{ table.render }}
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
# -*- 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 os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.abspath('../..'))
|
||||||
|
# -- General configuration ----------------------------------------------------
|
||||||
|
|
||||||
|
# Add any Sphinx extension module names here, as strings. They can be
|
||||||
|
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||||
|
extensions = [
|
||||||
|
'sphinx.ext.autodoc',
|
||||||
|
'oslosphinx',
|
||||||
|
]
|
||||||
|
|
||||||
|
# autodoc generation is a bit aggressive and a nuisance when doing heavy
|
||||||
|
# text edit cycles.
|
||||||
|
# execute "export SPHINX_DEBUG=1" in your terminal to disable
|
||||||
|
|
||||||
|
# The suffix of source filenames.
|
||||||
|
source_suffix = '.rst'
|
||||||
|
|
||||||
|
# The master toctree document.
|
||||||
|
master_doc = 'index'
|
||||||
|
|
||||||
|
# General information about the project.
|
||||||
|
project = u'cue-dashboard'
|
||||||
|
copyright = u'2015, OpenStack Foundation'
|
||||||
|
|
||||||
|
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||||
|
add_function_parentheses = True
|
||||||
|
|
||||||
|
# If true, the current module name will be prepended to all description
|
||||||
|
# unit titles (such as .. function::).
|
||||||
|
add_module_names = True
|
||||||
|
|
||||||
|
# The name of the Pygments (syntax highlighting) style to use.
|
||||||
|
pygments_style = 'sphinx'
|
||||||
|
|
||||||
|
# -- Options for HTML output --------------------------------------------------
|
||||||
|
|
||||||
|
# The theme to use for HTML and HTML Help pages. Major themes that come with
|
||||||
|
# Sphinx are currently 'default' and 'sphinxdoc'.
|
||||||
|
# html_theme_path = ["."]
|
||||||
|
# html_theme = '_theme'
|
||||||
|
# html_static_path = []
|
||||||
|
|
||||||
|
# Output file base name for HTML help builder.
|
||||||
|
htmlhelp_basename = '%sdoc' % project
|
||||||
|
|
||||||
|
# -- Options for manual page output -------------------------------------------
|
||||||
|
|
||||||
|
# One entry per manual page. List of tuples
|
||||||
|
# (source start file, name, description, authors, manual section).
|
||||||
|
man_pages = []
|
||||||
|
|
||||||
|
# If true, show URL addresses after external links.
|
||||||
|
man_show_urls = True
|
|
@ -0,0 +1 @@
|
||||||
|
.. include:: ../../README.rst
|
|
@ -1,2 +1,2 @@
|
||||||
Django>=1.4,<1.7
|
Django>=1.4,<1.7
|
||||||
python-cueclient
|
-e git+https://github.com/stackforge/python-cueclient.git#egg=python-cueclient
|
|
@ -0,0 +1,4 @@
|
||||||
|
pep8==1.5.7
|
||||||
|
flake8==2.2.3
|
||||||
|
sphinx>=1.1.2,!=1.2.0,<1.3
|
||||||
|
oslosphinx>=2.2.0 # Apache-2.0
|
|
@ -0,0 +1,25 @@
|
||||||
|
[tox]
|
||||||
|
envlist = pep8
|
||||||
|
minversion = 1.6
|
||||||
|
skipsdist = True
|
||||||
|
|
||||||
|
[testenv]
|
||||||
|
usedevelop = True
|
||||||
|
setenv = VIRTUAL_ENV={envdir}
|
||||||
|
deps = -r{toxinidir}/requirements.txt
|
||||||
|
-r{toxinidir}/test-requirements.txt
|
||||||
|
|
||||||
|
[testenv:pep8]
|
||||||
|
commands = flake8 {posargs}
|
||||||
|
|
||||||
|
[testenv:venv]
|
||||||
|
commands = {posargs}
|
||||||
|
|
||||||
|
[testenv:docs]
|
||||||
|
commands = python setup.py build_sphinx
|
||||||
|
|
||||||
|
[flake8]
|
||||||
|
show-source = true
|
||||||
|
ignore = H101,H302,H803
|
||||||
|
builtins = _
|
||||||
|
exclude = .venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,tools,local_settings.py
|
Loading…
Reference in New Issue