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:
Steve Leon 2015-03-12 22:47:05 -07:00
parent 76225d2aae
commit ab15cbca8d
19 changed files with 183 additions and 55 deletions

2
.gitignore vendored
View File

@ -6,4 +6,6 @@ ChangeLog
.DS_Store
cue_dashboard.egg*
build/
.tox/
.egg/
.idea/

View File

@ -1,5 +1,5 @@
from django.utils.translation import ugettext_lazy as _
PANEL_GROUP = 'queuesgroup'
PANEL_GROUP_NAME = _('Message Queues')
PANEL_GROUP_DASHBOARD = 'project'
PANEL_GROUP_NAME = _('Messaging')
PANEL_GROUP_DASHBOARD = 'project'

View File

@ -21,14 +21,14 @@ from cueclient.v1 import client
from keystoneclient import session as ksc_session
from keystoneclient.auth.identity import v2
from collections import namedtuple
from openstack_dashboard.api import base
from openstack_dashboard import api
from horizon.utils.memoized import memoized # noqa
@memoized
def cueclient(request):
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,
tenant_id=request.user.tenant_id,
tenant_name=request.user.tenant_name)
@ -50,17 +50,27 @@ def cluster_get(request, cluster_id):
return cluster
def cluster_create(request, name, nic, flavor, size, volume_size):
return cueclient(request).clusters.create(name, nic,flavor,
size, volume_size)
def cluster_create(request, name, nic, flavor, size):
return cueclient(request).clusters.create(name, nic[0],
flavor, size, 0)
def delete_cluster(request, cluster_id):
return cueclient(request).clusters.delete(cluster_id)
#todo
#This is needed because the cue client returns a dict
#instead of a cluster object.
def flavor(request, flavor_id):
return api.nova.flavor_get(request, flavor_id)
# todo
# This is needed because the cue client returns a dict
# instead of a cluster object.
def _to_cluster_object(cluster_dict):
return namedtuple('Cluster', cluster_dict)(**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)

View File

@ -24,4 +24,3 @@ class CuePanel(horizon.Panel):
name = _("Clusters")
slug = 'queues'
permissions = ('openstack.services.message_queue',)

View File

@ -20,6 +20,10 @@ 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):
@ -55,16 +59,23 @@ class DeleteCluster(tables.BatchAction):
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):
name = tables.Column("name",
verbose_name=_("Name"),
link='horizon:project:queues:detail')
size = tables.Column("size", verbose_name=_("Size"),)
flavor = tables.Column("flavor", verbose_name=_("Flavor"),)
size = tables.Column("size", verbose_name=_("Cluster Size"),)
status = tables.Column("status", verbose_name=_("Status"))
class Meta:
name = "clusters"
verbose_name = _("Clusters")
row_class = UpdateRow
table_actions = (CreateCluster, DeleteCluster,)
row_actions = (DeleteCluster,)

View File

@ -14,4 +14,4 @@ class OverviewTab(tabs.Tab):
class ClusterDetailTabs(tabs.TabGroup):
slug = "cluster_details"
tabs = (OverviewTab,)
sticky = True
sticky = True

View File

@ -26,6 +26,8 @@ CLUSTERS = r'^(?P<cluster_id>[^/]+)/%s$'
urlpatterns = patterns('',
url(r'^$', views.IndexView.as_view(),
name='index'),
url(CLUSTERS % '', views.DetailView.as_view(), name='detail'),
url(r'^create$', views.CreateClusterView.as_view(), name='create'),
)
url(CLUSTERS % '', views.DetailView.as_view(),
name='detail'),
url(r'^create$', views.CreateClusterView.as_view(),
name='create'),
)

View File

@ -36,6 +36,7 @@ LOG = logging.getLogger(__name__)
class IndexView(tables.DataTableView):
table_class = ClusterTable
template_name = 'queues/index.html'
page_title = _("Clusters")
def get_data(self):
return api.clusters_list(self.request)
@ -57,6 +58,8 @@ class DetailView(horizon_tabs.TabbedTableView):
cluster = self.get_data()
table = ClusterTable(self.request)
context["cluster"] = cluster
flavor = api.flavor(self.request, cluster.flavor)
context["flavor"] = flavor.name
context["url"] = self.get_redirect_url()
context["actions"] = table.render_row_actions(cluster)
return context
@ -66,16 +69,17 @@ class DetailView(horizon_tabs.TabbedTableView):
cluster_id = self.kwargs['cluster_id']
cluster = api.cluster_get(self.request, cluster_id)
LOG.info('hlahaha')
LOG.info(cluster)
LOG.info(type(cluster))
return cluster
def get_tabs(self, request, *args, **kwargs):
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
def get_redirect_url():
return reverse('horizon:project:queues:index')
return reverse('horizon:project:queues:index')

View File

@ -36,20 +36,15 @@ 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."))
size = forms.IntegerField(label=_("Size"),
min_value=0,
initial=1,
help_text=_("Size of cluster."))
volume = forms.IntegerField(label=_("Volume Size"),
min_value=0,
initial=1,
help_text=_("Size of the volume in GB."))
size = forms.IntegerField(label=_("Cluster Size"),
min_value=1,
initial=1,
help_text=_("Size of cluster."))
class Meta(object):
name = _("Details")
help_text_template = "queues/_launch_details_help.html"
@memoized.memoized_method
def flavors(self, request):
try:
@ -70,7 +65,7 @@ class SetInstanceDetailsAction(workflows.Action):
class SetClusterDetails(workflows.Step):
action_class = SetInstanceDetailsAction
contributes = ("name", "volume", "flavor", "size")
contributes = ("name", "flavor", "size")
class SetNetworkAction(workflows.Action):
@ -94,6 +89,13 @@ class SetNetworkAction(workflows.Action):
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):
try:
tenant_id = self.request.user.tenant_id
@ -119,9 +121,9 @@ class SetNetwork(workflows.Step):
# 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.
# TODO
# Choosing the first networks until Cue
# supports more than one networks.
context['network_id'] = networks[0]
return context
@ -140,7 +142,7 @@ class CreateCluster(workflows.Workflow):
def __init__(self, request=None, context_seed=None, entry_point=None,
*args, **kwargs):
super(CreateCluster, self).__init__(request, context_seed,
entry_point, *args, **kwargs)
entry_point, *args, **kwargs)
self.attrs['autocomplete'] = (
settings.HORIZON_CONFIG.get('password_autocomplete'))
@ -151,13 +153,13 @@ class CreateCluster(workflows.Workflow):
def handle(self, request, context):
try:
LOG.info("Launching message queue cluster with parameters "
"{name=%s, volume=%s, flavor=%s, size=%s, nics=%s}",
context['name'], context['volume'], context['flavor'],
"{name=%s, flavor=%s, size=%s, nics=%s}",
context['name'], context['flavor'],
context['size'], context['network_id'])
cluster_create(request, context['name'], context['network_id'],
context['flavor'], context['size'], context['volume'])
context['flavor'], context['size'])
return True
except Exception:
exceptions.handle(request)
return False
return False

View File

@ -13,12 +13,10 @@
<dt>{% trans "Network" %}</dt>
<dd>{{ cluster.network_id|default:_("-") }}</dd>
<dt>{% trans "Flavor" %}</dt>
<dd>{{ cluster.flavor|default:_("-") }}</dd>
<dt>{% trans "Size" %}</dt>
<dd>{{ flavor|default:_("-") }}</dd>
<dt>{% trans "Cluster Size" %}</dt>
<dd>{{ cluster.size|default:_("-") }}</dd>
<dt>{% trans "Volume Size" %}</dt>
<dd>{{ cluster.volume_size|default:_("-") }}</dd>
<dt>{% trans "Endpoints" %}</dt>
<dd>{{ cluster.end_points|default:_("-") }}</dd>
<dt>{% trans "Endpoint" %}</dt>
<dd>{{ cluster.url|default:_("-") }}</dd>
</dl>
</div>
</div>

View File

@ -1,4 +1,3 @@
{% load i18n %}
<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>

View File

@ -6,4 +6,5 @@
clicking the button, or dragging and dropping. You can change the
NIC order by dragging and dropping as well.
{% endblocktrans %}
</p>
</p>
<p>{% blocktrans %}<strong>Please note:</strong> You can only choose one network per cluster.{% endblocktrans %}</p>

View File

@ -4,7 +4,7 @@
<table class="table-fixed" id="networkListSortContainer">
<tbody>
<tr>
<td class="actions">
<td class="col-sm-6">
<label id="selected_network_label">{% trans "Selected networks" %}</label>
<ul id="selected_network" class="networklist">
</ul>
@ -12,7 +12,7 @@
<ul id="available_network" class="networklist">
</ul>
</td>
<td class="help_text">
<td class="col-sm-6">
{% include "queues/_launch_network_help.html" %}
</td>
</tr>

View File

@ -1,6 +1,6 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Queues" %}{% endblock %}
{% block title %}{% trans "Clusters" %}{% endblock %}
{% block main %}
{{ table.render }}

70
doc/source/conf.py Normal file
View File

@ -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

1
doc/source/index.rst Normal file
View File

@ -0,0 +1 @@
.. include:: ../../README.rst

View File

@ -1,2 +1,2 @@
Django>=1.4,<1.7
python-cueclient
-e git+https://github.com/stackforge/python-cueclient.git#egg=python-cueclient

4
test-requirements.txt Normal file
View File

@ -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

25
tox.ini Normal file
View File

@ -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