Add segment panel
Added segment panel and implemented list and create segment functionality.Added test cases that actually not covering the line of code but tested the list and create functionally. Partial-Implements: blueprint masakari-dashboard Change-Id: I1366bfdc188f4e0d53fa46f2a6ea3790c9f295fc
This commit is contained in:
parent
b09b094a2b
commit
c429554def
@ -53,7 +53,9 @@ Install Masakari dashboard with all dependencies in your virtual environment::
|
||||
|
||||
And enable it in Horizon::
|
||||
|
||||
ln -s ../masakari-dashboard/masakaridashboard/enabled/_90_project_MasakariDashboard.py openstack_dashboard/local/enabled
|
||||
ln -s ../masakari-dashboard/masakaridashboard/local/enabled/_50_masakaridashboard.py openstack_dashboard/local/enabled
|
||||
ln -s ../masakari-dashboard/masakaridashboard/local/local_settings.d/_50_masakari.py openstack_dashboard/local/local_settings.d
|
||||
ln -s ../masakari-dashboard/masakaridashboard/conf/masakari_policy.json openstack_dashboard/conf
|
||||
|
||||
To run horizon with the newly enabled Masakari dashboard plugin run::
|
||||
|
||||
|
13
doc/requirements.txt
Normal file
13
doc/requirements.txt
Normal file
@ -0,0 +1,13 @@
|
||||
# The order of packages is significant, because pip processes them in the order
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
# Order matters to the pip dependency resolver, so sorting this file
|
||||
# changes how packages are installed. New dependencies should be
|
||||
# added in alphabetical order, however, some dependencies may need to
|
||||
# be installed in a specific order.
|
||||
|
||||
# Docs Requirements
|
||||
sphinx!=1.6.6,!=1.6.7,>=1.6.2 # BSD
|
||||
oslosphinx>=4.7.0 # Apache-2.0
|
||||
reno>=2.5.0 # Apache-2.0
|
||||
docutils>=0.11 # OSI-Approved Open Source, Public Domain
|
@ -53,7 +53,9 @@ Install Masakari dashboard with all dependencies in your virtual environment::
|
||||
|
||||
And enable it in Horizon::
|
||||
|
||||
ln -s ../masakari-dashboard/masakaridashboard/enabled/_90_project_MasakariDashboard.py openstack_dashboard/local/enabled
|
||||
ln -s ../masakari-dashboard/masakaridashboard/local/enabled/_50_masakaridashboard.py openstack_dashboard/local/enabled
|
||||
ln -s ../masakari-dashboard/masakaridashboard/local/local_settings.d/_50_masakari.py openstack_dashboard/local/local_settings.d
|
||||
ln -s ../masakari-dashboard/masakaridashboard/conf/masakari_policy.json openstack_dashboard/conf
|
||||
|
||||
To run horizon with the newly enabled Masakari dashboard plugin run::
|
||||
|
||||
|
112
masakaridashboard/api/api.py
Normal file
112
masakaridashboard/api/api.py
Normal file
@ -0,0 +1,112 @@
|
||||
# Copyright (C) 2018 NTT DATA
|
||||
# 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.
|
||||
|
||||
import itertools
|
||||
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon.utils import functions as utils
|
||||
from horizon.utils import memoized
|
||||
from keystoneauth1.identity.generic import token
|
||||
from keystoneauth1 import session as ks_session
|
||||
from openstack import connection
|
||||
|
||||
from masakaridashboard.handle_errors import handle_errors
|
||||
|
||||
|
||||
@memoized.memoized
|
||||
def openstack_connection(request):
|
||||
auth = token.Token(
|
||||
auth_url=getattr(settings, 'OPENSTACK_KEYSTONE_URL'),
|
||||
token=request.user.token.id,
|
||||
project_name=request.user.project_name,
|
||||
project_id=request.user.tenant_id)
|
||||
session = ks_session.Session(auth=auth)
|
||||
conn = connection.Connection(session=session)
|
||||
|
||||
return conn.instance_ha
|
||||
|
||||
|
||||
@handle_errors(_("Unable to retrieve segments"), [])
|
||||
def get_segment_list(request, marker='', paginate=False, filters=None):
|
||||
"""Returns segments as per page size."""
|
||||
page_size = utils.get_page_size(request)
|
||||
client = openstack_connection(request)
|
||||
kwargs = get_request_param(marker, paginate, filters, page_size)
|
||||
entities_iter = client.segments(**kwargs)
|
||||
has_prev_data = has_more_data = False
|
||||
if paginate:
|
||||
entities, has_more_data, has_prev_data = pagination_process(
|
||||
entities_iter, kwargs['limit'], page_size, marker)
|
||||
else:
|
||||
entities = list(entities_iter)
|
||||
|
||||
return entities, has_more_data, has_prev_data
|
||||
|
||||
|
||||
def get_request_param(marker, paginate, filters, page_size):
|
||||
limit = getattr(settings, 'API_RESULT_LIMIT', 100)
|
||||
|
||||
if paginate:
|
||||
request_size = page_size + 1
|
||||
else:
|
||||
request_size = limit
|
||||
|
||||
kwargs = {"marker": marker,
|
||||
"limit": request_size
|
||||
}
|
||||
if filters is not None:
|
||||
kwargs.update(filters)
|
||||
return kwargs
|
||||
|
||||
|
||||
def pagination_process(data, request_size, page_size, marker):
|
||||
"""Retrieve a listing of specific entity and handles pagination.
|
||||
|
||||
:param request: Request data
|
||||
:param marker: Pagination marker for large data sets: entity id
|
||||
:param paginate: If true will perform pagination based on settings.
|
||||
Default:False
|
||||
"""
|
||||
prev_data = more_data = False
|
||||
entities = list(itertools.islice(data, request_size))
|
||||
# first and middle page condition
|
||||
if len(entities) > page_size:
|
||||
entities.pop()
|
||||
more_data = True
|
||||
# middle page condition
|
||||
if marker is not None:
|
||||
prev_data = True
|
||||
elif marker is not None:
|
||||
prev_data = True
|
||||
|
||||
return entities, more_data, prev_data
|
||||
|
||||
|
||||
@handle_errors(_("Unable to retrieve segments"), [])
|
||||
def segment_list(request):
|
||||
return list(openstack_connection(request).segments())
|
||||
|
||||
|
||||
def segment_create(request, data):
|
||||
"""Create segment."""
|
||||
return openstack_connection(request).create_segment(**data)
|
||||
|
||||
|
||||
@handle_errors(_("Unable to retrieve segment"), [])
|
||||
def get_segment(request, segment_id):
|
||||
"""Returns segment by id"""
|
||||
return openstack_connection(request).get_segment(segment_id)
|
10
masakaridashboard/conf/masakari_policy.json
Normal file
10
masakaridashboard/conf/masakari_policy.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"admin_api": "is_admin:True",
|
||||
"context_is_admin": "role:admin",
|
||||
"admin_or_owner": "is_admin:True or project_id:%(project_id)s",
|
||||
"default": "rule:admin_api",
|
||||
"os_masakari_api:extensions": "rule:admin_api",
|
||||
"os_masakari_api:segments": "rule:admin_api",
|
||||
"os_masakari_api:os-hosts": "rule:admin_api",
|
||||
"os_masakari_api:notifications": "rule:admin_api"
|
||||
}
|
32
masakaridashboard/dashboard.py
Normal file
32
masakaridashboard/dashboard.py
Normal file
@ -0,0 +1,32 @@
|
||||
# Copyright (C) 2018 NTT DATA
|
||||
# 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.utils.translation import ugettext_lazy as _
|
||||
|
||||
import horizon
|
||||
|
||||
from masakaridashboard.default import panel
|
||||
|
||||
|
||||
class MasakariDashboard(horizon.Dashboard):
|
||||
slug = "masakaridashboard"
|
||||
name = _("Instance-ha")
|
||||
panels = ('default', 'segments')
|
||||
default_panel = 'default'
|
||||
policy_rules = (('instance-ha', 'context_is_admin'),)
|
||||
|
||||
|
||||
horizon.register(MasakariDashboard)
|
||||
MasakariDashboard.register(panel.Default)
|
24
masakaridashboard/default/panel.py
Normal file
24
masakaridashboard/default/panel.py
Normal file
@ -0,0 +1,24 @@
|
||||
# Copyright (C) 2018 NTT DATA
|
||||
# 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.utils.translation import ugettext_lazy as _
|
||||
|
||||
import horizon
|
||||
|
||||
|
||||
class Default(horizon.Panel):
|
||||
name = _("Default")
|
||||
slug = 'default'
|
||||
nav = False
|
7
masakaridashboard/default/templates/default/base.html
Normal file
7
masakaridashboard/default/templates/default/base.html
Normal file
@ -0,0 +1,7 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block css %}
|
||||
{% include "_stylesheets.html" %}
|
||||
<link href='{{ STATIC_URL }}masakaridashboard/css/style.css' type='text/css' media='screen' rel='stylesheet' />
|
||||
{% endblock %}
|
7
masakaridashboard/default/templates/default/table.html
Normal file
7
masakaridashboard/default/templates/default/table.html
Normal file
@ -0,0 +1,7 @@
|
||||
{% extends 'masakaridashboard/default/base.html' %}
|
||||
|
||||
{% block main %}
|
||||
<div class="masakari-wrapper">
|
||||
{{ table.render }}
|
||||
</div>
|
||||
{% endblock %}
|
75
masakaridashboard/handle_errors.py
Normal file
75
masakaridashboard/handle_errors.py
Normal file
@ -0,0 +1,75 @@
|
||||
# Copyright (C) 2018 NTT DATA
|
||||
# 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.
|
||||
|
||||
import functools
|
||||
import inspect
|
||||
|
||||
import horizon.exceptions
|
||||
|
||||
|
||||
def handle_errors(error_message, error_default=None, request_arg=None):
|
||||
"""A decorator for adding default error handling to API calls.
|
||||
|
||||
It wraps the original method in a try-except block, with horizon's
|
||||
error handling added.
|
||||
|
||||
Note: it should only be used on functions or methods that take request as
|
||||
their argument (it has to be named "request", or ``request_arg`` has to be
|
||||
provided, indicating which argument is the request).
|
||||
|
||||
The decorated method accepts a number of additional parameters:
|
||||
|
||||
:param _error_handle: whether to handle the errors in this call
|
||||
:param _error_message: override the error message
|
||||
:param _error_default: override the default value returned on error
|
||||
:param _error_redirect: specify a redirect url for errors
|
||||
:param _error_ignore: ignore known errors
|
||||
"""
|
||||
def decorator(func):
|
||||
if request_arg is None:
|
||||
_request_arg = 'request'
|
||||
if _request_arg not in inspect.getargspec(func).args:
|
||||
raise RuntimeError(
|
||||
"The handle_errors decorator requires 'request' as "
|
||||
"an argument of the function or method being decorated")
|
||||
else:
|
||||
_request_arg = request_arg
|
||||
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
_error_handle = kwargs.pop('_error_handle', True)
|
||||
_error_message = kwargs.pop('_error_message', error_message)
|
||||
_error_default = kwargs.pop('_error_default', error_default)
|
||||
_error_redirect = kwargs.pop('_error_redirect', None)
|
||||
_error_ignore = kwargs.pop('_error_ignore', False)
|
||||
|
||||
if not _error_handle:
|
||||
return func(*args, **kwargs)
|
||||
try:
|
||||
return func(*args, **kwargs)
|
||||
except Exception as e:
|
||||
callargs = inspect.getcallargs(func, *args, **kwargs)
|
||||
request = callargs[_request_arg]
|
||||
_error_message += ': ' + str(e)
|
||||
horizon.exceptions.handle(request, _error_message,
|
||||
ignore=_error_ignore,
|
||||
redirect=_error_redirect)
|
||||
return _error_default
|
||||
|
||||
wrapper.wrapped = func
|
||||
|
||||
return wrapper
|
||||
|
||||
return decorator
|
25
masakaridashboard/local/enabled/_50_masakaridashboard.py
Normal file
25
masakaridashboard/local/enabled/_50_masakaridashboard.py
Normal file
@ -0,0 +1,25 @@
|
||||
# Copyright (C) 2018 NTT DATA
|
||||
# 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 openstack_dashboard import exceptions
|
||||
|
||||
DASHBOARD = 'masakaridashboard'
|
||||
ADD_INSTALLED_APPS = ['masakaridashboard']
|
||||
|
||||
ADD_EXCEPTIONS = {
|
||||
'recoverable': exceptions.RECOVERABLE,
|
||||
'not_found': exceptions.NOT_FOUND,
|
||||
'unauthorized': exceptions.UNAUTHORIZED,
|
||||
}
|
0
masakaridashboard/local/enabled/__init__.py
Normal file
0
masakaridashboard/local/enabled/__init__.py
Normal file
19
masakaridashboard/local/local_settings.d/_50_masakari.py
Normal file
19
masakaridashboard/local/local_settings.d/_50_masakari.py
Normal file
@ -0,0 +1,19 @@
|
||||
# Copyright (C) 2018 NTT DATA
|
||||
# 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 openstack_dashboard.settings import POLICY_FILES
|
||||
|
||||
|
||||
POLICY_FILES.update({'instance-ha': 'masakari_policy.json'})
|
0
masakaridashboard/segments/__init__.py
Normal file
0
masakaridashboard/segments/__init__.py
Normal file
69
masakaridashboard/segments/forms.py
Normal file
69
masakaridashboard/segments/forms.py
Normal file
@ -0,0 +1,69 @@
|
||||
# Copyright (C) 2018 NTT DATA
|
||||
# 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.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import messages
|
||||
|
||||
from masakaridashboard.api import api
|
||||
|
||||
|
||||
class CreateSegmentForm(forms.SelfHandlingForm):
|
||||
name = forms.CharField(
|
||||
label=_('Segment Name'),
|
||||
widget=forms.TextInput(attrs={'maxlength': 255}),
|
||||
help_text=_('The segment name.'))
|
||||
recovery_method = forms.ChoiceField(
|
||||
label=_('Recovery Method'),
|
||||
choices=[('auto', 'auto'),
|
||||
('auto_priority', 'auto_priority'),
|
||||
('reserved_host', 'reserved_host'),
|
||||
('rh_priority', 'rh_priority')],
|
||||
widget=forms.Select(
|
||||
attrs={'class': 'switchable',
|
||||
'data-slug': 'recovery_method'}),
|
||||
required=True,
|
||||
help_text=_('Type of recovery if any host in this segment goes down.')
|
||||
)
|
||||
service_type = forms.CharField(
|
||||
label=_('Service Type'),
|
||||
help_text=_('The name of service which will be deployed in this'
|
||||
' segment. As of now user can mention COMPUTE as service'
|
||||
' type.'),
|
||||
widget=forms.TextInput(attrs={
|
||||
'readonly': 'readonly', 'value': 'compute'}))
|
||||
description = forms.CharField(
|
||||
label=_("Description"), widget=forms.Textarea(
|
||||
attrs={'rows': 4}), required=False)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CreateSegmentForm, self).__init__(*args, **kwargs)
|
||||
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
api.segment_create(request, data)
|
||||
msg = _('Successfully created segment')
|
||||
messages.success(request, msg)
|
||||
except Exception as exc:
|
||||
if exc.status_code == 409:
|
||||
msg = _('Segment with name "%s" already exists') % data["name"]
|
||||
else:
|
||||
msg = _('Failed to create segment')
|
||||
redirect = reverse('horizon:masakaridashboard:segments:index')
|
||||
exceptions.handle(request, msg, redirect=redirect)
|
||||
return True
|
28
masakaridashboard/segments/panel.py
Normal file
28
masakaridashboard/segments/panel.py
Normal file
@ -0,0 +1,28 @@
|
||||
# Copyright (C) 2018 NTT DATA
|
||||
# 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.utils.translation import ugettext_lazy as _
|
||||
|
||||
import horizon
|
||||
|
||||
from masakaridashboard import dashboard
|
||||
|
||||
|
||||
class Segment(horizon.Panel):
|
||||
name = _("Segments")
|
||||
slug = 'segments'
|
||||
|
||||
|
||||
dashboard.MasakariDashboard.register(Segment)
|
58
masakaridashboard/segments/tables.py
Normal file
58
masakaridashboard/segments/tables.py
Normal file
@ -0,0 +1,58 @@
|
||||
# Copyright (C) 2018 NTT DATA
|
||||
# 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.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import tables
|
||||
|
||||
|
||||
class CreateSegment(tables.LinkAction):
|
||||
name = "create"
|
||||
verbose_name = _("Create Segment")
|
||||
url = "horizon:masakaridashboard:segments:create_segment"
|
||||
classes = ("ajax-modal",)
|
||||
icon = "plus"
|
||||
|
||||
|
||||
SEGMENT_FILTER_CHOICES = (
|
||||
('recovery_method', _("Recovery Method ="), True),
|
||||
('service_type', _("Service Type ="), True),
|
||||
)
|
||||
|
||||
|
||||
class SegmentFilterAction(tables.FilterAction):
|
||||
filter_type = "server"
|
||||
filter_choices = SEGMENT_FILTER_CHOICES
|
||||
|
||||
|
||||
class FailoverSegmentTable(tables.DataTable):
|
||||
|
||||
name = tables.WrappingColumn('name', verbose_name=_("Name"), truncate=40)
|
||||
uuid = tables.Column('uuid', verbose_name=_("UUID"))
|
||||
recovery_method = tables.Column(
|
||||
'recovery_method', verbose_name=_("Recovery Method"))
|
||||
service_type = tables.Column(
|
||||
'service_type', verbose_name=_("Service Type"))
|
||||
description = tables.WrappingColumn(
|
||||
'description', verbose_name=_("Description"),
|
||||
truncate=40)
|
||||
|
||||
def get_object_id(self, datum):
|
||||
return datum.uuid
|
||||
|
||||
class Meta(object):
|
||||
name = "failover_segment"
|
||||
verbose_name = _("FailoverSegment")
|
||||
table_actions = (CreateSegment, SegmentFilterAction)
|
18
masakaridashboard/segments/templates/segments/_create.html
Normal file
18
masakaridashboard/segments/templates/segments/_create.html
Normal file
@ -0,0 +1,18 @@
|
||||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
{% block form_attrs %}enctype="multipart/form-data"{% endblock %}
|
||||
{% block modal-body-right %}
|
||||
<h3>{% trans "Description:" %}</h3>
|
||||
<p>{% trans "Create a failover segment of hypervisor hosts." %}</p>
|
||||
<p>
|
||||
<strong>{% trans "Recovery methods:" %}</strong>
|
||||
</p>
|
||||
<p>{% trans "auto: Nova selects the new compute host for evacuation of instances running on a failed compute host" %}</p>
|
||||
<p>{% trans "reserved_host : One of the reserved host configured in the segment will be used for evacuation of instances running on a failed compute host" %}</p>
|
||||
<p>{% trans "auto_priority: First it will try 'auto' recovery method, if it's fails, then it will try using 'reserved_host' recovery method." %}</p>
|
||||
<p>{% trans "rh_priority: It is exactly opposite of 'auto_priority' recovery method." %}</p>
|
||||
<p>
|
||||
<strong>{% trans "Please note: " %}</strong>
|
||||
{% trans "Service Type is presently not used by Masakari but it's a mandatory field so the default value is set to 'compute' and it cannot be changed." %}
|
||||
</p>
|
||||
{% endblock %}
|
@ -0,0 +1,7 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Validate Segment" %}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'masakaridashboard/segments/_create.html' %}
|
||||
{% endblock %}
|
7
masakaridashboard/segments/templates/segments/index.html
Normal file
7
masakaridashboard/segments/templates/segments/index.html
Normal file
@ -0,0 +1,7 @@
|
||||
{% extends 'masakaridashboard/default/table.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Segments" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Segments") %}
|
||||
{% endblock page_header %}
|
151
masakaridashboard/segments/tests.py
Normal file
151
masakaridashboard/segments/tests.py
Normal file
@ -0,0 +1,151 @@
|
||||
# Copyright (C) 2018 NTT DATA
|
||||
# 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.conf import settings
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.test.utils import override_settings
|
||||
from django.utils.http import urlunquote
|
||||
import mock
|
||||
|
||||
from masakaridashboard.segments import tables as segment_table
|
||||
from masakaridashboard.test import helpers as test
|
||||
|
||||
|
||||
INDEX_URL = reverse('horizon:masakaridashboard:segments:index')
|
||||
CREATE_URL = reverse('horizon:masakaridashboard:segments:create_segment')
|
||||
|
||||
|
||||
class SegmentTest(test.TestCase):
|
||||
|
||||
def test_index(self):
|
||||
with mock.patch(
|
||||
'masakaridashboard.api.api.get_segment_list',
|
||||
return_value=[self.masakari_segment.list(),
|
||||
False, False]) as mock_get_segment_list:
|
||||
res = self.client.get(INDEX_URL)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertTemplateUsed(res, 'masakaridashboard/segments/index.html')
|
||||
mock_get_segment_list.assert_called_once_with(
|
||||
filters={}, marker=None, paginate=True, request=mock.ANY)
|
||||
segments = res.context['failover_segment_table'].data
|
||||
self.assertItemsEqual(segments, self.masakari_segment.list())
|
||||
|
||||
def test_create_get(self):
|
||||
res = self.client.get(CREATE_URL)
|
||||
self.assertTemplateUsed(res, 'masakaridashboard/segments/create.html')
|
||||
|
||||
def test_create_post(self):
|
||||
segment = self.masakari_segment.list()[0]
|
||||
form_data = {
|
||||
'name': segment.name,
|
||||
'recovery_method': segment.recovery_method,
|
||||
'service_type': segment.service_type,
|
||||
'description': segment.description
|
||||
}
|
||||
with mock.patch('masakaridashboard.api.api.segment_create',
|
||||
return_value=segment) as mocked_create:
|
||||
res = self.client.post(CREATE_URL, form_data)
|
||||
self.assertNoFormErrors(res)
|
||||
self.assertEqual(res.status_code, 302)
|
||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||
|
||||
mocked_create.assert_called_once_with(
|
||||
mock.ANY,
|
||||
form_data
|
||||
)
|
||||
|
||||
def _test_segments_index_paginated(
|
||||
self, filters, marker, segments, url, has_more, has_prev):
|
||||
|
||||
with mock.patch(
|
||||
'masakaridashboard.api.api.get_segment_list',
|
||||
return_value=[segments,
|
||||
has_more, has_prev]) as mock_get_segment_list:
|
||||
res = self.client.get(urlunquote(url))
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertTemplateUsed(res, 'masakaridashboard/segments/index.html')
|
||||
mock_get_segment_list.assert_called_once_with(
|
||||
filters=filters, marker=marker, paginate=True, request=mock.ANY)
|
||||
|
||||
return res
|
||||
|
||||
@override_settings(API_RESULT_PAGE_SIZE=1)
|
||||
@mock.patch('masakaridashboard.api.api.get_segment')
|
||||
def test_segments_index_paginated(self, mock_get_segment):
|
||||
mock_get_segment.return_value = self.masakari_segment.list()[0]
|
||||
segment_list = self.masakari_segment.list()
|
||||
size = settings.API_RESULT_PAGE_SIZE
|
||||
base_url = INDEX_URL
|
||||
next = segment_table.FailoverSegmentTable._meta.pagination_param
|
||||
|
||||
# get first page
|
||||
expected_segments = segment_list[:size]
|
||||
res = self._test_segments_index_paginated(filters={}, marker=None,
|
||||
segments=expected_segments,
|
||||
url=base_url, has_more=True,
|
||||
has_prev=False)
|
||||
segments = res.context['failover_segment_table'].data
|
||||
self.assertItemsEqual(segments, expected_segments)
|
||||
|
||||
# get second page
|
||||
expected_segments = segment_list[size:2 * size]
|
||||
marker = expected_segments[0].id
|
||||
|
||||
url = base_url + "?%s=%s" % (next, marker)
|
||||
res = self._test_segments_index_paginated(filters={}, marker=marker,
|
||||
segments=expected_segments,
|
||||
url=url, has_more=True,
|
||||
has_prev=True)
|
||||
segments = res.context['failover_segment_table'].data
|
||||
self.assertItemsEqual(segments, expected_segments)
|
||||
|
||||
# get last page
|
||||
expected_segments = segment_list[-size:]
|
||||
marker = expected_segments[0].id
|
||||
url = base_url + "?%s=%s" % (next, marker)
|
||||
res = self._test_segments_index_paginated(filters={}, marker=marker,
|
||||
segments=expected_segments,
|
||||
url=url, has_more=False,
|
||||
has_prev=True)
|
||||
segments = res.context['failover_segment_table'].data
|
||||
self.assertItemsEqual(segments, expected_segments)
|
||||
|
||||
@override_settings(API_RESULT_PAGE_SIZE=1)
|
||||
def test_segments_index_paginated_prev_page(self):
|
||||
segment_list = self.masakari_segment.list()
|
||||
size = settings.API_RESULT_PAGE_SIZE
|
||||
base_url = INDEX_URL
|
||||
prev = segment_table.FailoverSegmentTable._meta.prev_pagination_param
|
||||
|
||||
# prev from some page
|
||||
expected_segments = segment_list[size:2 * size]
|
||||
marker = expected_segments[0].id
|
||||
url = base_url + "?%s=%s" % (prev, marker)
|
||||
res = self._test_segments_index_paginated(filters={}, marker=marker,
|
||||
segments=expected_segments,
|
||||
url=url, has_more=True,
|
||||
has_prev=True)
|
||||
segments = res.context['failover_segment_table'].data
|
||||
self.assertItemsEqual(segments, expected_segments)
|
||||
|
||||
# back to first page
|
||||
expected_segments = segment_list[:size]
|
||||
marker = expected_segments[0].id
|
||||
url = base_url + "?%s=%s" % (prev, marker)
|
||||
res = self._test_segments_index_paginated(
|
||||
filters={}, marker=marker, segments=expected_segments,
|
||||
url=url, has_more=True, has_prev=False)
|
||||
segments = res.context['failover_segment_table'].data
|
||||
self.assertItemsEqual(segments, expected_segments)
|
26
masakaridashboard/segments/urls.py
Normal file
26
masakaridashboard/segments/urls.py
Normal file
@ -0,0 +1,26 @@
|
||||
# Copyright (C) 2018 NTT DATA
|
||||
# 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.conf.urls import url
|
||||
|
||||
from masakaridashboard.segments import views
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', views.IndexView.as_view(), name='index'),
|
||||
url(r'^create_segment$',
|
||||
views.CreateSegmentView.as_view(),
|
||||
name='create_segment'),
|
||||
]
|
94
masakaridashboard/segments/views.py
Normal file
94
masakaridashboard/segments/views.py
Normal file
@ -0,0 +1,94 @@
|
||||
# Copyright (C) 2018 NTT DATA
|
||||
# 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.conf import settings
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import tables
|
||||
from masakaridashboard.api import api
|
||||
from masakaridashboard.segments import tables as masakari_tab
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from masakaridashboard.segments import forms as segment_forms
|
||||
|
||||
|
||||
class IndexView(tables.DataTableView):
|
||||
table_class = masakari_tab.FailoverSegmentTable
|
||||
template_name = 'masakaridashboard/segments/index.html'
|
||||
page_title = _("Segments")
|
||||
|
||||
_more = False
|
||||
_prev = False
|
||||
|
||||
def needs_filter_first(self, table):
|
||||
return self._needs_filter_first
|
||||
|
||||
def has_more_data(self, table):
|
||||
return self._more
|
||||
|
||||
def has_prev_data(self, table):
|
||||
return self._prev
|
||||
|
||||
def get_data(self):
|
||||
segments = []
|
||||
marker = self.request.GET.get(
|
||||
masakari_tab.FailoverSegmentTable._meta.pagination_param,
|
||||
None
|
||||
)
|
||||
if marker is not None:
|
||||
segment = api.get_segment(self.request, marker)
|
||||
marker = segment.id
|
||||
filters = self.get_filters()
|
||||
self._needs_filter_first = True
|
||||
|
||||
filter_first = getattr(settings, 'FILTER_DATA_FIRST', {})
|
||||
if filter_first.get('masakaridashboard.segments', False) and len(
|
||||
filters) == 0:
|
||||
self._needs_filter_first = True
|
||||
self._more = False
|
||||
return segments
|
||||
|
||||
try:
|
||||
segments, self._more, self._prev = api.get_segment_list(
|
||||
request=self.request,
|
||||
marker=marker,
|
||||
filters=filters,
|
||||
paginate=True
|
||||
)
|
||||
except Exception:
|
||||
self._prev = False
|
||||
self._more = False
|
||||
msg = _('Unable to retrieve segment list.')
|
||||
exceptions.handle(self.request, msg)
|
||||
|
||||
return segments
|
||||
|
||||
|
||||
class CreateSegmentView(forms.ModalFormView):
|
||||
template_name = 'masakaridashboard/segments/create.html'
|
||||
modal_header = _("Create Segment")
|
||||
form_id = "create_segment"
|
||||
form_class = segment_forms.CreateSegmentForm
|
||||
submit_label = _("Create")
|
||||
submit_url = reverse_lazy(
|
||||
"horizon:masakaridashboard:segments:create_segment")
|
||||
success_url = reverse_lazy("horizon:masakaridashboard:segments:index")
|
||||
page_title = _("Create Segment")
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super(CreateSegmentView, self).get_form_kwargs()
|
||||
return kwargs
|
48
masakaridashboard/static/masakaridashboard/css/style.css
Normal file
48
masakaridashboard/static/masakaridashboard/css/style.css
Normal file
@ -0,0 +1,48 @@
|
||||
.masakari-wrapper.list{
|
||||
list-style: inherit;
|
||||
}
|
||||
|
||||
.masakari-wrapper #actions{
|
||||
width:100%;
|
||||
}
|
||||
|
||||
.masakari-wrapper #actions a.btn{
|
||||
width:initial;
|
||||
}
|
||||
|
||||
.masakari-wrapper.detail-screen .page-breadcrumb ol li{
|
||||
max-width: inherit;
|
||||
}
|
||||
|
||||
.masakari-wrapper.detail-screen .page-breadcrumb li:last-child{
|
||||
display:none;
|
||||
}
|
||||
|
||||
.masakari-wrapper .navbar-brand{
|
||||
padding: 6px 10px;
|
||||
}
|
||||
|
||||
.boolfield{
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.boolfield i{
|
||||
padding-right: .2em;
|
||||
}
|
||||
|
||||
.boolfield i.green{
|
||||
color: green;
|
||||
}
|
||||
|
||||
.boolfield i.red{
|
||||
color: red;
|
||||
}
|
||||
|
||||
.line-space{
|
||||
margin: .3em 0;
|
||||
}
|
||||
|
||||
.line-space dd{
|
||||
display:inline-block;
|
||||
margin-left: 1.5em;
|
||||
}
|
28
masakaridashboard/test/helpers.py
Normal file
28
masakaridashboard/test/helpers.py
Normal file
@ -0,0 +1,28 @@
|
||||
# Copyright (C) 2018 NTT DATA
|
||||
# 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 openstack_dashboard.test import helpers
|
||||
|
||||
from masakaridashboard.test.test_data import utils
|
||||
|
||||
|
||||
class MasakariTestsMixin(object):
|
||||
def _setup_test_data(self):
|
||||
super(MasakariTestsMixin, self)._setup_test_data()
|
||||
utils.load_test_data(self)
|
||||
|
||||
|
||||
class TestCase(MasakariTestsMixin, helpers.TestCase):
|
||||
pass
|
@ -1,4 +1,5 @@
|
||||
# Copyright (c) 2018 NTT DATA
|
||||
# Copyright (C) 2018 NTT DATA
|
||||
# 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
|
||||
@ -22,13 +23,13 @@ HORIZON_CONFIG.pop('dashboards', None)
|
||||
HORIZON_CONFIG.pop('default_dashboard', None)
|
||||
|
||||
# Update the dashboards with masakaridashboard
|
||||
import masakaridashboard.enabled
|
||||
from masakaridashboard.local import enabled
|
||||
import openstack_dashboard.enabled
|
||||
from openstack_dashboard.utils import settings
|
||||
|
||||
settings.update_dashboards(
|
||||
[
|
||||
masakaridashboard.enabled,
|
||||
enabled,
|
||||
openstack_dashboard.enabled,
|
||||
],
|
||||
HORIZON_CONFIG,
|
||||
@ -36,4 +37,9 @@ settings.update_dashboards(
|
||||
)
|
||||
|
||||
# Ensure any duplicate apps are removed after the update_dashboards call
|
||||
|
||||
INSTALLED_APPS = list(set(INSTALLED_APPS))
|
||||
NOSE_ARGS = ['--nocapture',
|
||||
'--nologcapture',
|
||||
'--cover-package=masakaridashboard',
|
||||
'--cover-inclusive']
|
||||
|
0
masakaridashboard/test/test_data/__init__.py
Normal file
0
masakaridashboard/test/test_data/__init__.py
Normal file
38
masakaridashboard/test/test_data/masakari_data.py
Normal file
38
masakaridashboard/test/test_data/masakari_data.py
Normal file
@ -0,0 +1,38 @@
|
||||
# Copyright (C) 2018 NTT DATA
|
||||
# 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 openstack.instance_ha.v1 import segment
|
||||
|
||||
from openstack_dashboard.test.test_data import utils as test_data_utils
|
||||
|
||||
from masakaridashboard.test import uuidsentinel
|
||||
|
||||
|
||||
def data(TEST):
|
||||
TEST.masakari_segment = test_data_utils.TestDataContainer()
|
||||
|
||||
segment1 = segment.Segment(uuid=uuidsentinel.segment1, name='test',
|
||||
recovery_method='auto',
|
||||
service_type='service', description='demo')
|
||||
segment2 = segment.Segment(uuid=uuidsentinel.segment2,
|
||||
name='test2', recovery_method='auto',
|
||||
service_type='service', description='demo')
|
||||
segment3 = segment.Segment(uuid=uuidsentinel.segment3, name='test3',
|
||||
recovery_method='auto',
|
||||
service_type='service', description='demo')
|
||||
|
||||
TEST.masakari_segment.add(segment1)
|
||||
TEST.masakari_segment.add(segment2)
|
||||
TEST.masakari_segment.add(segment3)
|
33
masakaridashboard/test/test_data/utils.py
Normal file
33
masakaridashboard/test/test_data/utils.py
Normal file
@ -0,0 +1,33 @@
|
||||
# Copyright (C) 2018 NTT DATA
|
||||
# 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 openstack_dashboard.test.test_data import utils
|
||||
|
||||
|
||||
def load_test_data(load_onto=None):
|
||||
from masakaridashboard.test.test_data import masakari_data
|
||||
from openstack_dashboard.test.test_data import exceptions
|
||||
|
||||
# The order of these loaders matters, some depend on others.
|
||||
loaders = (
|
||||
exceptions.data,
|
||||
masakari_data.data,
|
||||
)
|
||||
if load_onto:
|
||||
for data_func in loaders:
|
||||
data_func(load_onto)
|
||||
return load_onto
|
||||
else:
|
||||
return utils.TestData(*loaders)
|
33
masakaridashboard/test/uuidsentinel.py
Normal file
33
masakaridashboard/test/uuidsentinel.py
Normal file
@ -0,0 +1,33 @@
|
||||
# Copyright (C) 2018 NTT DATA
|
||||
# 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.
|
||||
|
||||
import sys
|
||||
|
||||
|
||||
class UUIDSentinels(object):
|
||||
def __init__(self):
|
||||
from oslo_utils import uuidutils
|
||||
self._uuid_module = uuidutils
|
||||
self._sentinels = {}
|
||||
|
||||
def __getattr__(self, name):
|
||||
if name.startswith('_'):
|
||||
raise ValueError('Sentinels must not start with _')
|
||||
if name not in self._sentinels:
|
||||
self._sentinels[name] = self._uuid_module.generate_uuid()
|
||||
return self._sentinels[name]
|
||||
|
||||
|
||||
sys.modules[__name__] = UUIDSentinels()
|
@ -1,16 +1,18 @@
|
||||
# Copyright (c) 2018 NTT DATA
|
||||
# Copyright (C) 2018 NTT DATA
|
||||
# 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
|
||||
# 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
|
||||
# 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.
|
||||
# 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 pbr.version
|
||||
|
||||
version_info = pbr.version.VersionInfo('masakaridashboard')
|
||||
|
@ -7,11 +7,9 @@
|
||||
# be installed in a specific order.
|
||||
#
|
||||
# PBR should always appear first
|
||||
pbr>=2.0.0,!=2.1.0 # Apache-2.0
|
||||
pbr!=2.1.0,>=2.0.0 # Apache-2.0
|
||||
|
||||
Babel>=2.3.4,!=2.4.0 # BSD
|
||||
Django>=1.8,<2.0 # BSD
|
||||
django-babel>=0.5.1 # BSD
|
||||
django-compressor>=2.0 # MIT
|
||||
django-pyscss>=2.0.2 # BSD License (2 clause)
|
||||
# Horizon Core Requirements
|
||||
horizon>=13.0.0 # Apache-2.0
|
||||
openstacksdk>=0.13.0
|
||||
PyYAML>=3.12 # MIT
|
||||
|
@ -7,22 +7,18 @@
|
||||
# be installed in a specific order.
|
||||
#
|
||||
# Hacking should appear first in case something else depends on pep8
|
||||
hacking>=0.12.0,!=0.13.0,<0.14 # Apache-2.0
|
||||
#
|
||||
coverage>=4.0,!=4.4 # Apache-2.0
|
||||
django-nose>=1.4.4 # BSD
|
||||
mock>=2.0.0 # BSD
|
||||
hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0
|
||||
|
||||
# Testing Requirements
|
||||
coverage!=4.4,>=4.0 # Apache-2.0
|
||||
django-nose>=1.4.4 # BSD
|
||||
mock>=2.0.0 # BSD
|
||||
mox3>=0.20.0 # Apache-2.0
|
||||
nodeenv>=0.9.4 # BSD
|
||||
nose>=1.3.7 # LGPL
|
||||
nose-exclude>=0.3.0 # LGPL
|
||||
nosehtmloutput>=0.0.3 # Apache-2.0
|
||||
nosexcover>=1.0.10 # BSD
|
||||
openstack.nose-plugin>=0.7 # Apache-2.0
|
||||
openstackdocstheme>=1.18.1 # Apache-2.0
|
||||
reno>=2.5.0 # Apache-2.0
|
||||
selenium>=2.50.1 # Apache-2.0
|
||||
sphinx>=1.6.2,!=1.6.6,!=1.6.7 # BSD
|
||||
testtools>=2.2.0 # MIT
|
||||
# This also needs xvfb library installed on your OS
|
||||
xvfbwrapper>=0.1.3 #license: MIT
|
||||
nodeenv>=0.9.4 # BSD
|
||||
nose>=1.3.7 # LGPL
|
||||
nose-exclude>=0.3.0 # LGPL
|
||||
nosexcover>=1.0.10 # BSD
|
||||
openstack.nose-plugin>=0.7 # Apache-2.0
|
||||
nosehtmloutput>=0.0.3 # Apache-2.0
|
||||
selenium>=2.50.1 # Apache-2.0
|
||||
xvfbwrapper>=0.1.3 #license: MIT
|
||||
|
20
tox.ini
20
tox.ini
@ -17,7 +17,7 @@ deps =
|
||||
-c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt}
|
||||
-r{toxinidir}/requirements.txt
|
||||
-r{toxinidir}/test-requirements.txt
|
||||
commands = python manage.py test {posargs} --settings=masakaridashboard.test.settings
|
||||
commands = python manage.py test {posargs} --settings=masakaridashboard.test.settings --ignore-files=masakaridashboard/test/uuidsentinel.py
|
||||
|
||||
[testenv:pep8]
|
||||
commands = flake8 {posargs}
|
||||
@ -36,7 +36,7 @@ commands =
|
||||
basepython = python2.7
|
||||
commands =
|
||||
pip install django>=1.8,<1.9
|
||||
python manage.py test {posargs} --settings=masakaridashboard.test.settings
|
||||
python manage.py test {posargs} --settings=masakaridashboard.test.settings --ignore-files=masakaridashboard/test/uuidsentinel.py
|
||||
|
||||
[testenv:eslint]
|
||||
whitelist_externals = npm
|
||||
@ -59,10 +59,22 @@ commands =
|
||||
echo "nexecute `npm run test`"
|
||||
|
||||
[testenv:docs]
|
||||
commands = python setup.py build_sphinx
|
||||
# We need to install horizon dependencies to build module references
|
||||
deps =
|
||||
-c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt}
|
||||
-r{toxinidir}/requirements.txt
|
||||
-r{toxinidir}/doc/requirements.txt
|
||||
commands =
|
||||
sphinx-build -W -b html doc/source doc/build/html
|
||||
|
||||
[testenv:releasenotes]
|
||||
commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html
|
||||
# There is no need to install horizon.
|
||||
usedevelop = False
|
||||
deps =
|
||||
-c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt}
|
||||
-r{toxinidir}/doc/requirements.txt
|
||||
commands =
|
||||
sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html
|
||||
|
||||
[flake8]
|
||||
exclude = .venv,.git,.tox,dist,*lib/python*,*egg,build,node_modules
|
||||
|
Loading…
x
Reference in New Issue
Block a user