Add QuotaManagement Panel to Horizon.
Kingbird-dashboard is a horizon plugin and therefore this commit is to add kingbird as a panel to the existing panels. The features in this commit helps admin to update, delete and `sync quota` for tenants. Change-Id: I44de3b61e336e0ecfbfb74b90e791ed4d9a25650
This commit is contained in:
parent
7e3536cb8b
commit
f44da40ea5
|
@ -1,138 +1,77 @@
|
|||
# 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
|
||||
# Copyright 2018 - Ericsson AB.
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
# 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
|
||||
#
|
||||
# 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.
|
||||
# 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 horizon.utils import memoized
|
||||
|
||||
import kingbirdclient
|
||||
|
||||
from kingbirdclient.api import client as kb_client
|
||||
|
||||
SERVICE_TYPE = 'synchronization'
|
||||
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import logging
|
||||
|
||||
# enable following after client product implemented.
|
||||
# from kingbird_dashboardclient.v1 import client as kingbird_dashboard_client
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon.utils.memoized import memoized
|
||||
from openstack_dashboard.api import base
|
||||
|
||||
# for stab, should remove when use CLI API
|
||||
import copy
|
||||
from datetime import datetime
|
||||
import uuid
|
||||
@memoized.memoized
|
||||
def kingbird_dashboardclient(request):
|
||||
"""Kingbird Client for API calls."""
|
||||
return kb_client.client(
|
||||
username=request.user.username,
|
||||
auth_token=request.user.token.id,
|
||||
project_id=request.user.tenant_id,
|
||||
auth_url=getattr(settings, 'OPENSTACK_KEYSTONE_URL')
|
||||
)
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
ATTRIBUTES = ['name', 'description', 'enabled', 'size', 'temperature',
|
||||
'base', 'flavor', 'topping']
|
||||
|
||||
STUB_DATA = {}
|
||||
def list_defaults(request):
|
||||
"""Default Quota Limits."""
|
||||
return kingbird_dashboardclient(request).quota_manager.\
|
||||
list_defaults()
|
||||
|
||||
|
||||
# for stab, should be removed when use CLI API
|
||||
class StubResponse(object):
|
||||
|
||||
def __init__(self, info):
|
||||
self._info = info
|
||||
|
||||
def __repr__(self):
|
||||
reprkeys = sorted(k for k in self.__dict__.keys() if k[0] != '_')
|
||||
info = ", ".join("%s=%s" % (k, getattr(self, k)) for k in reprkeys)
|
||||
return "<%s %s>" % (self.__class__.__name__, info)
|
||||
|
||||
def to_dict(self):
|
||||
return copy.deepcopy(self._info)
|
||||
def global_limits(request, target_tenant_id):
|
||||
"""Global Quota Limits for any tenant."""
|
||||
return kingbird_dashboardclient(request).quota_manager.\
|
||||
global_limits(target_tenant_id)
|
||||
|
||||
|
||||
@memoized
|
||||
def apiclient(request):
|
||||
api_url = ""
|
||||
c = None
|
||||
def update_global_limits(request, target_tenant_id, **data):
|
||||
"""Update Global Limits for a tenant."""
|
||||
return kingbird_dashboardclient(request).quota_manager.\
|
||||
update_global_limits(target_tenant_id, **data)
|
||||
|
||||
|
||||
def sync_quota(request, target_tenant_id):
|
||||
"""On Demand Quota Sync."""
|
||||
return kingbird_dashboardclient(request).quota_manager.\
|
||||
sync_quota(target_tenant_id)
|
||||
|
||||
|
||||
def delete_quota(request, target_tenant_id):
|
||||
"""Delete Quota for a tenant."""
|
||||
try:
|
||||
api_url = base.url_for(request, 'kingbird')
|
||||
except exceptions.ServiceCatalogException:
|
||||
LOG.debug('No Kingbird Management service is configured.')
|
||||
return None
|
||||
|
||||
LOG.debug('kingbird_dashboardclient using the token "%s" and url'
|
||||
'"%s"' % (request.user.token.id, api_url))
|
||||
# enable following after client product implemented.
|
||||
# c = kingbird_dashboard_client.Client(
|
||||
# username=request.user.username,
|
||||
# project_id=request.user.tenant_id,
|
||||
# input_auth_token=request.user.token.id,
|
||||
# api_url=api_url)
|
||||
return c
|
||||
kingbird_dashboardclient(request).quota_manager.\
|
||||
delete_quota(target_tenant_id)
|
||||
return True
|
||||
except kingbirdclient.exceptions.APIException:
|
||||
raise
|
||||
|
||||
|
||||
def kingbird_create(request, **kwargs):
|
||||
args = {}
|
||||
for (key, value) in kwargs.items():
|
||||
if key in ATTRIBUTES:
|
||||
args[str(key)] = value
|
||||
else:
|
||||
raise exceptions.BadRequest(
|
||||
"Key must be in %s" % ",".join(ATTRIBUTES))
|
||||
# created = apiclient(request).kingbirds.create(**args)
|
||||
|
||||
# create dummy response
|
||||
args["uuid"] = uuid.uuid1().hex
|
||||
args["created_at"] = datetime.now().isoformat()
|
||||
created = StubResponse(args)
|
||||
for k in args:
|
||||
setattr(created, k, args[k])
|
||||
STUB_DATA[created.uuid] = created
|
||||
|
||||
return created
|
||||
|
||||
|
||||
def kingbird_update(request, id, **kwargs):
|
||||
args = {}
|
||||
for (key, value) in kwargs.items():
|
||||
if key in ATTRIBUTES:
|
||||
args[str(key)] = value
|
||||
else:
|
||||
raise exceptions.BadRequest(
|
||||
"Key must be in %s" % ",".join(ATTRIBUTES))
|
||||
# updated = apiclient(request).kingbird.update(id, **args)
|
||||
|
||||
# update dummy response
|
||||
args["uuid"] = id
|
||||
args["updated_at"] = datetime.now().isoformat()
|
||||
updated = StubResponse(args)
|
||||
for k in args:
|
||||
setattr(updated, k, args[k])
|
||||
STUB_DATA[updated.uuid] = updated
|
||||
|
||||
return updated
|
||||
|
||||
|
||||
def kingbird_delete(request, id):
|
||||
# deleted = apiclient(request).kingbirds.delete(id)
|
||||
deleted = STUB_DATA.pop(id)
|
||||
|
||||
return deleted
|
||||
|
||||
|
||||
def kingbird_list(
|
||||
request, limit=None, marker=None, sort_key=None,
|
||||
sort_dir=None, detail=True):
|
||||
|
||||
# list = apiclient(request).Kingbirds.list(limit, marker, sort_key,
|
||||
# sort_dir, detail)
|
||||
list = [STUB_DATA[data] for data in STUB_DATA]
|
||||
return list
|
||||
|
||||
|
||||
def kingbird_show(request, id):
|
||||
# show = apiclient(request).kingbirds.get(id)
|
||||
show = STUB_DATA.get(id)
|
||||
return show
|
||||
def detail_quota(request, target_tenant_id):
|
||||
"""Quota Information of a tenant."""
|
||||
try:
|
||||
return kingbird_dashboardclient(request).quota_manager.\
|
||||
quota_detail(target_tenant_id)
|
||||
except kingbirdclient.exceptions.APIException:
|
||||
raise
|
||||
|
|
|
@ -1,86 +0,0 @@
|
|||
# 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.views import generic
|
||||
|
||||
from kingbird_dashboard.api import client
|
||||
|
||||
from openstack_dashboard.api.rest import urls
|
||||
from openstack_dashboard.api.rest import utils as rest_utils
|
||||
|
||||
|
||||
def change_to_id(obj):
|
||||
"""Change key named 'uuid' to 'id'
|
||||
|
||||
API returns objects with a field called 'uuid' many of Horizons
|
||||
directives however expect objects to have a field called 'id'.
|
||||
"""
|
||||
obj['id'] = obj.pop('uuid')
|
||||
return obj
|
||||
|
||||
|
||||
@urls.register
|
||||
class Kingbird(generic.View):
|
||||
"""API for retrieving a single Kingbird"""
|
||||
url_regex = r'kingbird_dashboard/kingbirds/(?P<id>[^/]+)$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request, id):
|
||||
"""Get a specific kingbird"""
|
||||
return change_to_id(client.kingbird_show(request, id).to_dict())
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
def post(self, request, id):
|
||||
"""Update a Kingbird.
|
||||
|
||||
Returns the updated Kingbird object on success.
|
||||
"""
|
||||
kingbird = client.kingbird_update(request, id, **request.DATA)
|
||||
return rest_utils.CreatedResponse(
|
||||
'/api/kingbird_dashboard/kingbird/%s' % kingbird.uuid,
|
||||
kingbird.to_dict())
|
||||
|
||||
|
||||
@urls.register
|
||||
class Kingbirds(generic.View):
|
||||
"""API for Kingbirds"""
|
||||
url_regex = r'kingbird_dashboard/kingbirds/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request):
|
||||
"""Get a list of the Kingbirds for a project.
|
||||
|
||||
The returned result is an object with property 'items' and each
|
||||
item under this is a Kingbird.
|
||||
"""
|
||||
result = client.kingbird_list(request)
|
||||
return {'items': [change_to_id(n.to_dict()) for n in result]}
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
def delete(self, request):
|
||||
"""Delete one or more Kingbirds by id.
|
||||
|
||||
Returns HTTP 204 (no content) on successful deletion.
|
||||
"""
|
||||
for id in request.DATA:
|
||||
client.kingbird_delete(request, id)
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
def put(self, request):
|
||||
"""Create a new Kingbird.
|
||||
|
||||
Returns the new Kingbird object on success.
|
||||
"""
|
||||
kingbird = client.kingbird_create(request, **request.DATA)
|
||||
return rest_utils.CreatedResponse(
|
||||
'/api/kingbird_dashboard/kingbird/%s' % kingbird.uuid,
|
||||
kingbird.to_dict())
|
|
@ -1,3 +1,5 @@
|
|||
# Copyright 2018 Ericsson AB.
|
||||
#
|
||||
# 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
|
||||
|
@ -11,13 +13,21 @@
|
|||
# under the License.
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
import horizon
|
||||
|
||||
# This panel will be loaded from horizon, because specified in enabled file.
|
||||
# To register REST api, import below here.
|
||||
from kingbird_dashboard.api import rest_api # noqa: F401
|
||||
from kingbird_dashboard.default import panel
|
||||
|
||||
|
||||
class Kingbirds(horizon.Panel):
|
||||
name = _("Kingbirds")
|
||||
slug = "kingbirds"
|
||||
class Kingbird(horizon.Dashboard):
|
||||
name = _("Kingbird")
|
||||
slug = "kingbird"
|
||||
panels = (
|
||||
'default'
|
||||
'resource management',
|
||||
'quota management')
|
||||
default_panel = "default"
|
||||
|
||||
|
||||
horizon.register(Kingbird)
|
||||
Kingbird.register(panel.Default)
|
|
@ -0,0 +1,24 @@
|
|||
# Copyright 2018 Ericsson AB.
|
||||
#
|
||||
# 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'
|
||||
urls = 'kingbird_dashboard.quota_management.urls'
|
||||
nav = False
|
|
@ -0,0 +1,8 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block css %}
|
||||
{% include "_stylesheets.html" %}
|
||||
<link href='{{ STATIC_URL }}dashboard/kingbird_dashboard/css/style.css' type='text/css' media='screen' rel='stylesheet' />
|
||||
{% endblock %}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{% extends 'kingbird/default/base.html' %}
|
||||
|
||||
{% block main %}
|
||||
<div class="kingbird-wrapper">
|
||||
{{ table.render }}
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -22,5 +22,10 @@ PANEL = 'quota_management'
|
|||
# The slug of the dashboard the PANEL_GROUP associated with. Required.
|
||||
PANEL_DASHBOARD = 'kingbird'
|
||||
|
||||
# Python panel class of the PANEL to be added
|
||||
# Python panel class of the PANEL to be added.
|
||||
ADD_PANEL = 'kingbird_dashboard.quota_management.panel.QuotaManagement'
|
||||
|
||||
# Static CSS files to be added to Kingbird.
|
||||
ADD_SCSS_FILES = ['dashboard/kingbird_dashboard/css/style.css']
|
||||
|
||||
AUTO_DISCOVER_STATIC_FILES = True
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
# Copyright 2018 Ericsson AB.
|
||||
#
|
||||
# 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
|
|
@ -1,151 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
|
||||
module.exports = function (config) {
|
||||
// This tox venv is setup in the post-install npm step
|
||||
var toxPath = '../.tox/py27/lib/python2.7/site-packages/';
|
||||
|
||||
config.set({
|
||||
preprocessors: {
|
||||
// Used to collect templates for preprocessing.
|
||||
// NOTE: the templates must also be listed in the files section below.
|
||||
'./static/**/*.html': ['ng-html2js'],
|
||||
// Used to indicate files requiring coverage reports.
|
||||
'./static/**/!(*.spec).js': ['coverage'],
|
||||
},
|
||||
|
||||
// Sets up module to process templates.
|
||||
ngHtml2JsPreprocessor: {
|
||||
prependPrefix: '/',
|
||||
moduleName: 'templates'
|
||||
},
|
||||
|
||||
basePath: './',
|
||||
|
||||
// Contains both source and test files.
|
||||
files: [
|
||||
/*
|
||||
* shim, partly stolen from /i18n/js/horizon/
|
||||
* Contains expected items not provided elsewhere (dynamically by
|
||||
* Django or via jasmine template.
|
||||
*/
|
||||
'../test-shim.js',
|
||||
|
||||
// from jasmine.html
|
||||
toxPath + 'xstatic/pkg/jquery/data/jquery.js',
|
||||
toxPath + 'xstatic/pkg/angular/data/angular.js',
|
||||
toxPath + 'xstatic/pkg/angular/data/angular-route.js',
|
||||
toxPath + 'xstatic/pkg/angular/data/angular-mocks.js',
|
||||
toxPath + 'xstatic/pkg/angular/data/angular-cookies.js',
|
||||
toxPath + 'xstatic/pkg/angular_bootstrap/data/angular-bootstrap.js',
|
||||
toxPath + 'xstatic/pkg/angular_gettext/data/angular-gettext.js',
|
||||
toxPath + 'xstatic/pkg/angular/data/angular-sanitize.js',
|
||||
toxPath + 'xstatic/pkg/d3/data/d3.js',
|
||||
toxPath + 'xstatic/pkg/rickshaw/data/rickshaw.js',
|
||||
toxPath + 'xstatic/pkg/angular_smart_table/data/smart-table.js',
|
||||
toxPath + 'xstatic/pkg/angular_lrdragndrop/data/lrdragndrop.js',
|
||||
toxPath + 'xstatic/pkg/spin/data/spin.js',
|
||||
toxPath + 'xstatic/pkg/spin/data/spin.jquery.js',
|
||||
toxPath + 'xstatic/pkg/tv4/data/tv4.js',
|
||||
toxPath + 'xstatic/pkg/objectpath/data/ObjectPath.js',
|
||||
toxPath + 'xstatic/pkg/angular_schema_form/data/schema-form.js',
|
||||
toxPath + 'xstatic/pkg/angular_fileupload/data/ng-file-upload.js',
|
||||
|
||||
|
||||
// TODO: These should be mocked.
|
||||
toxPath + '/horizon/static/horizon/js/horizon.js',
|
||||
|
||||
/**
|
||||
* Include framework source code from horizon that we need.
|
||||
* Otherwise, karma will not be able to find them when testing.
|
||||
* These files should be mocked in the foreseeable future.
|
||||
*/
|
||||
toxPath + 'horizon/static/framework/**/*.module.js',
|
||||
toxPath + 'horizon/static/framework/**/!(*.spec|*.mock).js',
|
||||
toxPath + 'openstack_dashboard/static/**/*.module.js',
|
||||
toxPath + 'openstack_dashboard/static/**/!(*.spec|*.mock).js',
|
||||
toxPath + 'openstack_dashboard/dashboards/**/static/**/*.module.js',
|
||||
toxPath + 'openstack_dashboard/dashboards/**/static/**/!(*.spec|*.mock).js',
|
||||
|
||||
/**
|
||||
* First, list all the files that defines application's angular modules.
|
||||
* Those files have extension of `.module.js`. The order among them is
|
||||
* not significant.
|
||||
*/
|
||||
'./static/**/*.module.js',
|
||||
|
||||
/**
|
||||
* Followed by other JavaScript files that defines angular providers
|
||||
* on the modules defined in files listed above. And they are not mock
|
||||
* files or spec files defined below. The order among them is not
|
||||
* significant.
|
||||
*/
|
||||
'./static/**/!(*.spec|*.mock).js',
|
||||
|
||||
/**
|
||||
* Then, list files for mocks with `mock.js` extension. The order
|
||||
* among them should not be significant.
|
||||
*/
|
||||
toxPath + 'openstack_dashboard/static/**/*.mock.js',
|
||||
|
||||
/**
|
||||
* Finally, list files for spec with `spec.js` extension. The order
|
||||
* among them should not be significant.
|
||||
*/
|
||||
'./static/**/*.spec.js',
|
||||
|
||||
/**
|
||||
* Angular external templates
|
||||
*/
|
||||
'./static/**/*.html'
|
||||
],
|
||||
|
||||
autoWatch: true,
|
||||
|
||||
frameworks: ['jasmine'],
|
||||
|
||||
browsers: ['Chrome'],
|
||||
|
||||
browserNoActivityTimeout: 60000,
|
||||
|
||||
reporters: ['progress', 'coverage', 'threshold'],
|
||||
|
||||
plugins: [
|
||||
'karma-chrome-launcher',
|
||||
'karma-jasmine',
|
||||
'karma-ng-html2js-preprocessor',
|
||||
'karma-coverage',
|
||||
'karma-threshold-reporter'
|
||||
],
|
||||
|
||||
// Places coverage report in HTML format in the subdirectory below.
|
||||
coverageReporter: {
|
||||
type: 'html',
|
||||
dir: '../cover/karma/'
|
||||
},
|
||||
|
||||
// Coverage threshold values.
|
||||
thresholdReporter: {
|
||||
statements: 10, // target 100
|
||||
branches: 0, // target 100
|
||||
functions: 10, // target 100
|
||||
lines: 10 // target 100
|
||||
}
|
||||
});
|
||||
};
|
|
@ -0,0 +1,119 @@
|
|||
# Copyright 2018 - Ericsson AB.
|
||||
#
|
||||
# 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 kingbird_dashboard.api import client as kb_client
|
||||
|
||||
|
||||
INDEX_URL = "horizon:kingbird:quota_management:index"
|
||||
|
||||
|
||||
class UpdateForm(forms.SelfHandlingForm):
|
||||
ifcb_label = _("Injected File Content (Bytes)")
|
||||
ifpb_label = _("Length of Injected File Path")
|
||||
metadata_items = forms.IntegerField(min_value=-1,
|
||||
label=_("Metadata Items"))
|
||||
cores = forms.IntegerField(min_value=-1, label=_("VCPUs"))
|
||||
instances = forms.IntegerField(min_value=-1, label=_("Instances"))
|
||||
key_pairs = forms.IntegerField(min_value=-1, label=_("Key Pairs"))
|
||||
volumes = forms.IntegerField(min_value=-1, label=_("Volumes"))
|
||||
snapshots = forms.IntegerField(min_value=-1, label=_("Volume Snapshots"))
|
||||
gigabytes = forms.IntegerField(
|
||||
min_value=-1, label=_("Total Size of Volumes and Snapshots (GiB)"))
|
||||
backup_gigabytes = forms.IntegerField(
|
||||
min_value=-1, label=_(
|
||||
"Total Size of backup Volumes and Snapshots (GiB)"))
|
||||
backups = forms.IntegerField(
|
||||
min_value=-1, label=_(
|
||||
"Total Size of backup Volumes and Snapshots (GiB)"))
|
||||
ram = forms.IntegerField(min_value=-1, label=_("RAM (MB)"))
|
||||
floating_ips = forms.IntegerField(min_value=-1, label=_("Floating IPs"))
|
||||
fixed_ips = forms.IntegerField(min_value=-1, label=_("Fixed IPs"))
|
||||
security_groups = forms.IntegerField(min_value=-1,
|
||||
label=_("Security Groups"))
|
||||
security_group = forms.IntegerField(min_value=-1,
|
||||
label=_("Security Groups"))
|
||||
security_group_rule = forms.IntegerField(min_value=-1,
|
||||
label=_("Security Group Rules"))
|
||||
floatingip = forms.IntegerField(min_value=-1, label=_("Floating IPs"))
|
||||
network = forms.IntegerField(min_value=-1, label=_("Networks"))
|
||||
port = forms.IntegerField(min_value=-1, label=_("Ports"))
|
||||
router = forms.IntegerField(min_value=-1, label=_("Routers"))
|
||||
subnet = forms.IntegerField(min_value=-1, label=_("Subnets"))
|
||||
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(UpdateForm, self).__init__(request, *args,
|
||||
**kwargs)
|
||||
target_tenant_id = request.build_absolute_uri().split('/')[-2]
|
||||
quotas = kb_client.global_limits(request, target_tenant_id)
|
||||
result = {i._data: i._Limit for i in quotas}
|
||||
for field in result:
|
||||
if field in self.fields:
|
||||
self.fields[field].initial = result[field]
|
||||
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
target_tenant_id = request.build_absolute_uri().split('/')[-2]
|
||||
default_quota_obj = kb_client.global_limits(request,
|
||||
target_tenant_id)
|
||||
default_quota = {i._data: i._Limit for i in default_quota_obj}
|
||||
for resource in default_quota:
|
||||
if default_quota[resource] == data[resource]:
|
||||
del data[resource]
|
||||
if data:
|
||||
kb_client.update_global_limits(request, target_tenant_id,
|
||||
**data)
|
||||
msg = _('Quotas updated successfully for tenant "%s".') \
|
||||
% target_tenant_id
|
||||
messages.success(request, msg)
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
msg = _('Failed to update "%s".') % e
|
||||
redirect = reverse('horizon:kingbird:quota_management:index')
|
||||
exceptions.handle(request, msg, redirect=redirect)
|
||||
|
||||
|
||||
class SyncQuotaForm(forms.SelfHandlingForm):
|
||||
def handle(self, request, data=None):
|
||||
target_tenant = request.build_absolute_uri().split('/')[-2]
|
||||
try:
|
||||
kb_client.sync_quota(request, target_tenant)
|
||||
msg = _('On demand Quota sync has been triggered.')
|
||||
messages.success(request, msg)
|
||||
return True
|
||||
except Exception as e:
|
||||
msg = _('Failed to Sync Quota "%s".') % e
|
||||
|
||||
|
||||
class DeleteQuotaForm(forms.SelfHandlingForm):
|
||||
def handle(self, request, data=None):
|
||||
target_tenant = request.build_absolute_uri().split('/')[-2]
|
||||
msg = _('Request to delete quotas has been triggered.')
|
||||
err_msg = _('Failed to Delete Quota .')
|
||||
try:
|
||||
kb_client.delete_quota(request, target_tenant)
|
||||
messages.success(request, msg)
|
||||
return True
|
||||
except Exception as e:
|
||||
msg = _('Failed to delete quotas for the tenant "%s".') % e
|
||||
redirect = reverse('horizon:kingbird:quota_management:index')
|
||||
exceptions.handle(request, err_msg, redirect=redirect)
|
|
@ -1,3 +1,5 @@
|
|||
# Copyright 2018 Ericsson AB.
|
||||
#
|
||||
# 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
|
||||
|
@ -10,11 +12,15 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from django.conf.urls import url
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from horizon.browsers import views
|
||||
|
||||
title = _("Kingbirds")
|
||||
urlpatterns = [
|
||||
url('', views.AngularIndexView.as_view(title=title), name='index'),
|
||||
]
|
||||
import horizon
|
||||
|
||||
from kingbird_dashboard import dashboard
|
||||
|
||||
|
||||
class QuotaManagement(horizon.Panel):
|
||||
name = _("Quota Management")
|
||||
slug = 'quota_management'
|
||||
|
||||
dashboard.Kingbird.register(QuotaManagement)
|
|
@ -0,0 +1,63 @@
|
|||
# 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 UpdateQuota(tables.LinkAction):
|
||||
name = "update"
|
||||
verbose_name = _("Update")
|
||||
url = "horizon:kingbird:quota_management:update"
|
||||
classes = ("ajax-modal", "btn-edit")
|
||||
|
||||
|
||||
class QuotaSync(tables.LinkAction):
|
||||
name = "quota_sync"
|
||||
verbose_name = _("Sync Quota")
|
||||
url = "horizon:kingbird:quota_management:sync"
|
||||
classes = ("ajax-modal", "btn-edit")
|
||||
|
||||
|
||||
class DeleteQuota(tables.LinkAction):
|
||||
name = "delete_quota"
|
||||
verbose_name = _("Delete Quota")
|
||||
url = "horizon:kingbird:quota_management:delete"
|
||||
classes = ("ajax-modal", "btn-edit")
|
||||
|
||||
|
||||
class TenantsTable(tables.DataTable):
|
||||
name = tables.Column(
|
||||
"name",
|
||||
verbose_name=_("Name")
|
||||
)
|
||||
description = tables.Column(
|
||||
"description",
|
||||
verbose_name=_("Description")
|
||||
)
|
||||
id = tables.Column(
|
||||
"id",
|
||||
verbose_name=_("Project ID"),
|
||||
)
|
||||
enabled = tables.Column(
|
||||
"enabled",
|
||||
verbose_name=_("Enabled"),
|
||||
)
|
||||
|
||||
def get_object_id(self, datum):
|
||||
return datum.id
|
||||
|
||||
class Meta(object):
|
||||
name = "tenant_set"
|
||||
verbose_name = _("Quota Management")
|
||||
row_actions = (UpdateQuota, QuotaSync, DeleteQuota)
|
|
@ -0,0 +1,18 @@
|
|||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block form_id %}Delete Quota{% endblock %}
|
||||
{% block form_action %}{% url 'horizon:kingbird:quota_management:delete' project_id %}{% endblock %}
|
||||
|
||||
{% block modal-header %}{% trans "Delete Quota Sync" %}{% endblock %}
|
||||
|
||||
{% block modal-body %}
|
||||
<div class="left">
|
||||
<fieldset>
|
||||
{% include "horizon/common/_form_fields.html" %}
|
||||
</fieldset>
|
||||
<p align="left">Delete Quotas for this tenant.</p>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block form_id %}Sync Quota{% endblock %}
|
||||
{% block form_action %}{% url 'horizon:kingbird:quota_management:sync' project_id %}{% endblock %}
|
||||
|
||||
{% block modal-header %}{% trans "Trigger Quota Sync" %}{% endblock %}
|
||||
|
||||
{% block modal-body %}
|
||||
<div class="left">
|
||||
<fieldset>
|
||||
{% include "horizon/common/_form_fields.html" %}
|
||||
</fieldset>
|
||||
<p align="left">Click Sync to sync Quotas Dynamically. </p>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block form_id %}update_quota{% endblock %}
|
||||
{% block form_action %}{% url 'horizon:kingbird:quota_management:update' project_id %}{% endblock %}
|
||||
|
||||
{% block modal-header %}{% trans "Update Quotas" %}{% endblock %}
|
||||
|
||||
{% block modal-body %}
|
||||
<div class="left">
|
||||
<fieldset>
|
||||
{% include "horizon/common/_form_fields.html" %}
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="right">
|
||||
<h3>{% trans "Description:" %}</h3>
|
||||
<p>{% trans "From here you can update quotas." %}</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
{% extends 'kingbird/default/base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Delete Quota" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Delete Quota") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'kingbird/quota_management/_delete.html' %}
|
||||
{% endblock %}
|
|
@ -0,0 +1,11 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Quota Management" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_domain_page_header.html" with title=page_title %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{{ table.render }}
|
||||
{% endblock %}
|
|
@ -0,0 +1,11 @@
|
|||
{% extends 'kingbird/default/base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Update Quota" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Update Quota") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'kingbird/quota_management/_sync.html' %}
|
||||
{% endblock %}
|
|
@ -0,0 +1,11 @@
|
|||
{% extends 'kingbird/default/base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Update Quota" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Update Quota") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'kingbird/quota_management/_update.html' %}
|
||||
{% endblock %}
|
|
@ -1,3 +1,5 @@
|
|||
# Copyright 2018 Ericsson AB.
|
||||
#
|
||||
# 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
|
||||
|
@ -10,10 +12,17 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from openstack_dashboard.test import helpers as test
|
||||
from django.conf.urls import url
|
||||
|
||||
from kingbird_dashboard.quota_management import views
|
||||
|
||||
class KingbirdsTests(test.TestCase):
|
||||
# Unit tests for kingbird.
|
||||
def test_me(self):
|
||||
self.assertTrue(1 + 1 == 2)
|
||||
PROJECT_ID = r'^(?P<project_id>[^/]+)/%s$'
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', views.IndexView.as_view(), name='index'),
|
||||
url(PROJECT_ID % 'update', views.UpdateQuotaView.as_view(),
|
||||
name='update'),
|
||||
url(PROJECT_ID % 'sync', views.SyncQuotaView.as_view(), name='sync'),
|
||||
url(PROJECT_ID % 'delete', views.DeleteQuotaView.as_view(),
|
||||
name='delete'),
|
||||
]
|
|
@ -0,0 +1,144 @@
|
|||
# Copyright 2018 Ericsson AB.
|
||||
#
|
||||
# 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 exceptions
|
||||
from horizon import forms
|
||||
from horizon import messages
|
||||
from horizon import tables
|
||||
|
||||
from kingbird_dashboard.quota_management import forms as kb_forms
|
||||
from kingbird_dashboard.quota_management import tables as kb_tables
|
||||
|
||||
from openstack_dashboard import api as os_api
|
||||
from openstack_dashboard import policy
|
||||
from openstack_dashboard.utils import identity
|
||||
|
||||
|
||||
class IndexView(tables.DataTableView):
|
||||
table_id = "tenants"
|
||||
table_class = kb_tables.TenantsTable
|
||||
template_name = 'kingbird/quota_management/index.html'
|
||||
page_title = _("Quota Management")
|
||||
|
||||
def needs_filter_first(self, table):
|
||||
return self._needs_filter_first
|
||||
|
||||
def has_more_data(self, table):
|
||||
return self._more
|
||||
|
||||
def get_data(self):
|
||||
tenants = []
|
||||
marker = self.request.GET.get(
|
||||
kb_tables.TenantsTable._meta.pagination_param, None)
|
||||
self._more = False
|
||||
filters = self.get_filters()
|
||||
|
||||
self._needs_filter_first = False
|
||||
|
||||
if policy.check((("identity", "identity:list_projects"),),
|
||||
self.request):
|
||||
|
||||
# If filter_first is set and if there are not other filters
|
||||
# selected, then search criteria must be provided and
|
||||
# return an empty list
|
||||
filter_first = getattr(settings, 'FILTER_DATA_FIRST', {})
|
||||
if filter_first.get('identity.projects', False) and len(
|
||||
filters) == 0:
|
||||
self._needs_filter_first = True
|
||||
self._more = False
|
||||
return tenants
|
||||
|
||||
domain_id = identity.get_domain_id_for_operation(self.request)
|
||||
try:
|
||||
tenants, self._more = os_api.keystone.tenant_list(
|
||||
self.request,
|
||||
domain=domain_id,
|
||||
paginate=True,
|
||||
filters=filters,
|
||||
marker=marker)
|
||||
except Exception:
|
||||
exceptions.handle(self.request,
|
||||
_("Unable to retrieve project list."))
|
||||
elif policy.check((("identity", "identity:list_user_projects"),),
|
||||
self.request):
|
||||
try:
|
||||
tenants, self._more = os_api.keystone.tenant_list(
|
||||
self.request,
|
||||
user=self.request.user.id,
|
||||
paginate=True,
|
||||
marker=marker,
|
||||
filters=filters,
|
||||
admin=False)
|
||||
except Exception:
|
||||
exceptions.handle(self.request,
|
||||
_("Unable to retrieve project information."))
|
||||
else:
|
||||
msg = \
|
||||
_("Insufficient privilege level to view project information.")
|
||||
messages.info(self.request, msg)
|
||||
|
||||
if os_api.keystone.VERSIONS.active >= 3:
|
||||
domain_lookup = os_api.keystone.domain_lookup(self.request)
|
||||
for t in tenants:
|
||||
t.domain_name = domain_lookup.get(t.domain_id)
|
||||
return tenants
|
||||
|
||||
|
||||
class UpdateQuotaView(forms.ModalFormView):
|
||||
form_class = kb_forms.UpdateForm
|
||||
template_name = 'kingbird/quota_management/update.html'
|
||||
success_url = reverse_lazy("horizon:kingbird:quota_management:index")
|
||||
submit_label = _("Update")
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(UpdateQuotaView, self).get_context_data(**kwargs)
|
||||
context["project_id"] = self.kwargs['project_id']
|
||||
return context
|
||||
|
||||
def get_initial(self, **kwargs):
|
||||
return {'project_id': self.kwargs['project_id']}
|
||||
|
||||
|
||||
class SyncQuotaView(forms.ModalFormView):
|
||||
form_class = kb_forms.SyncQuotaForm
|
||||
template_name = 'kingbird/quota_management/sync.html'
|
||||
success_url = reverse_lazy("horizon:kingbird:quota_management:index")
|
||||
submit_label = _("Sync")
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(SyncQuotaView, self).get_context_data(**kwargs)
|
||||
context["project_id"] = self.kwargs['project_id']
|
||||
return context
|
||||
|
||||
def get_initial(self, **kwargs):
|
||||
return {'project_id': self.kwargs['project_id']}
|
||||
|
||||
|
||||
class DeleteQuotaView(forms.ModalFormView):
|
||||
form_class = kb_forms.DeleteQuotaForm
|
||||
template_name = 'kingbird/quota_management/delete.html'
|
||||
success_url = reverse_lazy("horizon:kingbird:quota_management:index")
|
||||
submit_label = _("Delete")
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(DeleteQuotaView, self).get_context_data(**kwargs)
|
||||
context["project_id"] = self.kwargs['project_id']
|
||||
return context
|
||||
|
||||
def get_initial(self, **kwargs):
|
||||
return {'project_id': self.kwargs['project_id']}
|
|
@ -1,40 +0,0 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc overview
|
||||
* @name horizon.dashboard.kingbird
|
||||
* @description
|
||||
* Dashboard module to host various kingbird panels.
|
||||
*/
|
||||
// fixme: if ngRoute and $routeProvider are unnecessary, remove them
|
||||
/* eslint-disable no-unused-vars */
|
||||
angular
|
||||
.module('horizon.dashboard.kingbird', [
|
||||
'horizon.dashboard.kingbird.kingbirds',
|
||||
'ngRoute'
|
||||
])
|
||||
.config(config);
|
||||
|
||||
config.$inject = ['$provide', '$windowProvider', '$routeProvider'];
|
||||
|
||||
function config($provide, $windowProvider, $routeProvider) {
|
||||
var path = $windowProvider.$get().STATIC_URL + 'dashboard/kingbird/';
|
||||
$provide.constant('horizon.dashboard.kingbird.basePath', path);
|
||||
}
|
||||
/* eslint-disable no-unused-vars */
|
||||
})();
|
|
@ -1,23 +0,0 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
describe('horizon.dashboard.kingbird', function() {
|
||||
it('should exist', function() {
|
||||
expect(angular.module('horizon.dashboard.kingbird')).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
})();
|
|
@ -1,8 +0,0 @@
|
|||
@import "kingbirds/kingbirds";
|
||||
|
||||
.batch-action {
|
||||
float: right;
|
||||
action-list {
|
||||
padding-left: 0.3em;
|
||||
}
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('horizon.app.core.openstack-service-api')
|
||||
.factory('horizon.app.core.openstack-service-api.kingbird_dashboard', API);
|
||||
|
||||
API.$inject = [
|
||||
'horizon.framework.util.http.service',
|
||||
'horizon.framework.widgets.toast.service',
|
||||
'horizon.framework.util.i18n.gettext'
|
||||
];
|
||||
|
||||
function API(apiService, toastService, gettext) {
|
||||
var service = {
|
||||
getKingbird: getKingbird,
|
||||
getKingbirds: getKingbirds,
|
||||
createKingbird: createKingbird,
|
||||
updateKingbird: updateKingbird,
|
||||
deleteKingbird: deleteKingbird
|
||||
};
|
||||
|
||||
return service;
|
||||
|
||||
///////////////////////////////
|
||||
// Kingbirds
|
||||
|
||||
function getKingbird(id) {
|
||||
return apiService.get('/api/kingbird_dashboard/kingbirds/' + id)
|
||||
.error(function() {
|
||||
var msg = gettext('Unable to retrieve the Kingbird with id: %(id)s.');
|
||||
toastService.add('error', interpolate(msg, {id: id}, true));
|
||||
});
|
||||
}
|
||||
|
||||
function getKingbirds() {
|
||||
return apiService.get('/api/kingbird_dashboard/kingbirds/')
|
||||
.error(function() {
|
||||
toastService.add('error', gettext('Unable to retrieve the Kingbirds.'));
|
||||
});
|
||||
}
|
||||
|
||||
function createKingbird(params) {
|
||||
return apiService.put('/api/kingbird_dashboard/kingbirds/', params)
|
||||
.error(function() {
|
||||
var msg = gettext('Unable to create the Kingbird with name: %(name)s');
|
||||
toastService.add('error', interpolate(msg, { name: params.name }, true));
|
||||
});
|
||||
}
|
||||
|
||||
function updateKingbird(id, params) {
|
||||
return apiService.post('/api/kingbird_dashboard/kingbirds/' + id, params)
|
||||
.error(function() {
|
||||
var msg = gettext('Unable to update the Kingbird with id: %(id)s');
|
||||
toastService.add('error', interpolate(msg, { id: params.id }, true));
|
||||
});
|
||||
}
|
||||
|
||||
function deleteKingbird(id, suppressError) {
|
||||
var promise = apiService.delete('/api/kingbird_dashboard/kingbirds/', [id]);
|
||||
return suppressError ? promise : promise.error(function() {
|
||||
var msg = gettext('Unable to delete the Kingbird with id: %(id)s');
|
||||
toastService.add('error', interpolate(msg, { id: id }, true));
|
||||
});
|
||||
}
|
||||
}
|
||||
}());
|
|
@ -1,87 +0,0 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc overview
|
||||
* @ngname horizon.dashboard.kingbird.kingbirds.actions
|
||||
*
|
||||
* @description
|
||||
* Provides all of the actions for Kingbirds.
|
||||
*/
|
||||
angular
|
||||
.module('horizon.dashboard.kingbird.kingbirds.actions', [
|
||||
'horizon.framework',
|
||||
'horizon.dashboard.kingbird'
|
||||
])
|
||||
.run(registerKingbirdActions);
|
||||
|
||||
registerKingbirdActions.$inject = [
|
||||
'horizon.framework.conf.resource-type-registry.service',
|
||||
'horizon.framework.util.i18n.gettext',
|
||||
'horizon.dashboard.kingbird.kingbirds.create.service',
|
||||
'horizon.dashboard.kingbird.kingbirds.update.service',
|
||||
'horizon.dashboard.kingbird.kingbirds.delete.service',
|
||||
'horizon.dashboard.kingbird.kingbirds.resourceType'
|
||||
];
|
||||
|
||||
function registerKingbirdActions (
|
||||
registry,
|
||||
gettext,
|
||||
createKingbirdService,
|
||||
updateKingbirdService,
|
||||
deleteKingbirdService,
|
||||
resourceType
|
||||
) {
|
||||
var kingbirdsResourceType = registry.getResourceType(resourceType);
|
||||
kingbirdsResourceType.globalActions
|
||||
.append({
|
||||
id: 'createKingbirdAction',
|
||||
service: createKingbirdService,
|
||||
template: {
|
||||
type: 'create',
|
||||
text: gettext('Create Kingbird')
|
||||
}
|
||||
});
|
||||
|
||||
kingbirdsResourceType.batchActions
|
||||
.append({
|
||||
id: 'batchDeleteKingbirdAction',
|
||||
service: deleteKingbirdService,
|
||||
template: {
|
||||
type: 'delete-selected',
|
||||
text: gettext('Delete Kingbirds')
|
||||
}
|
||||
});
|
||||
|
||||
kingbirdsResourceType.itemActions
|
||||
.append({
|
||||
id: 'updateKingbirdAction',
|
||||
service: updateKingbirdService,
|
||||
template: {
|
||||
text: gettext('Update Kingbird')
|
||||
}
|
||||
})
|
||||
.append({
|
||||
id: 'deleteKingbirdAction',
|
||||
service: deleteKingbirdService,
|
||||
template: {
|
||||
type: 'delete',
|
||||
text: gettext('Delete Kingbird')
|
||||
}
|
||||
});
|
||||
}
|
||||
})();
|
|
@ -1,103 +0,0 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc overview
|
||||
* @name horizon.dashboard.kingbird.kingbirds.create.service
|
||||
* @description Service for the kingbird create modal
|
||||
*/
|
||||
angular
|
||||
.module('horizon.dashboard.kingbird.kingbirds')
|
||||
.factory('horizon.dashboard.kingbird.kingbirds.create.service', createService);
|
||||
|
||||
createService.$inject = [
|
||||
'$location',
|
||||
'horizon.app.core.openstack-service-api.kingbird_dashboard',
|
||||
'horizon.app.core.openstack-service-api.policy',
|
||||
'horizon.framework.util.actions.action-result.service',
|
||||
'horizon.framework.util.i18n.gettext',
|
||||
'horizon.framework.util.q.extensions',
|
||||
'horizon.framework.widgets.toast.service',
|
||||
'horizon.dashboard.kingbird.kingbirds.events',
|
||||
'horizon.dashboard.kingbird.kingbirds.model',
|
||||
'horizon.dashboard.kingbird.kingbirds.resourceType',
|
||||
'horizon.dashboard.kingbird.kingbirds.workflow'
|
||||
];
|
||||
|
||||
function createService(
|
||||
$location, api, policy, actionResult, gettext, $qExtensions,
|
||||
toast, events, model, resourceType, workflow
|
||||
) {
|
||||
|
||||
var message = {
|
||||
success: gettext('Kingbird %s was successfully created.')
|
||||
};
|
||||
|
||||
var service = {
|
||||
initAction: initAction,
|
||||
perform: perform,
|
||||
allowed: allowed
|
||||
};
|
||||
|
||||
return service;
|
||||
|
||||
//////////////
|
||||
|
||||
// fixme: include this function in your service
|
||||
// if you plan to emit events to the parent controller,
|
||||
// otherwise remove it
|
||||
function initAction() {
|
||||
}
|
||||
|
||||
// fixme: if newScope is unnecessary, remove it
|
||||
/* eslint-disable no-unused-vars */
|
||||
function perform(selected, newScope) {
|
||||
// modal title, buttons
|
||||
var title, submitText, submitIcon;
|
||||
title = gettext("Create Kingbird");
|
||||
submitText = gettext("Create");
|
||||
submitIcon = "fa fa-check";
|
||||
model.init();
|
||||
|
||||
var result = workflow.init(title, submitText, submitIcon, model.spec);
|
||||
return result.then(submit);
|
||||
}
|
||||
|
||||
function allowed() {
|
||||
return $qExtensions.booleanAsPromise(true);
|
||||
// fixme: if you need to set policy, change as follow
|
||||
//return policy.ifAllowed({ rules: [['kingbird', 'create_kingbird']] });
|
||||
}
|
||||
|
||||
function submit() {
|
||||
model.cleanProperties();
|
||||
return api.createKingbird(model.spec).then(success);
|
||||
}
|
||||
|
||||
function success(response) {
|
||||
response.data.id = response.data.uuid;
|
||||
toast.add('success', interpolate(message.success, [response.data.id]));
|
||||
var result = actionResult.getActionResult()
|
||||
.created(resourceType, response.data.id);
|
||||
if (result.result.failed.length === 0 && result.result.created.length > 0) {
|
||||
$location.path('/kingbird/kingbirds');
|
||||
} else {
|
||||
return result.result;
|
||||
}
|
||||
}
|
||||
}
|
||||
})();
|
|
@ -1,156 +0,0 @@
|
|||
/**
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use self 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.
|
||||
*/
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngDoc factory
|
||||
* @name horizon.dashboard.kingbird.kingbirds.delete.service
|
||||
* @Description
|
||||
* Brings up the delete kingbirds confirmation modal dialog.
|
||||
* On submit, delete selected resources.
|
||||
* On cancel, do nothing.
|
||||
*/
|
||||
angular
|
||||
.module('horizon.dashboard.kingbird.kingbirds')
|
||||
.factory('horizon.dashboard.kingbird.kingbirds.delete.service', deleteService);
|
||||
|
||||
deleteService.$inject = [
|
||||
'$location',
|
||||
'$q',
|
||||
'horizon.app.core.openstack-service-api.kingbird_dashboard',
|
||||
'horizon.app.core.openstack-service-api.policy',
|
||||
'horizon.framework.util.actions.action-result.service',
|
||||
'horizon.framework.util.i18n.gettext',
|
||||
'horizon.framework.util.q.extensions',
|
||||
'horizon.framework.widgets.modal.deleteModalService',
|
||||
'horizon.framework.widgets.toast.service',
|
||||
'horizon.dashboard.kingbird.kingbirds.resourceType',
|
||||
'horizon.dashboard.kingbird.kingbirds.events'
|
||||
];
|
||||
|
||||
function deleteService(
|
||||
$location, $q, api, policy, actionResult, gettext, $qExtensions,
|
||||
deleteModal, toast, resourceType, events
|
||||
) {
|
||||
var scope;
|
||||
var context = {
|
||||
labels: null,
|
||||
deleteEntity: deleteEntity,
|
||||
successEvent: events.DELETE_SUCCESS
|
||||
};
|
||||
var service = {
|
||||
initAction: initAction,
|
||||
allowed: allowed,
|
||||
perform: perform
|
||||
};
|
||||
var notAllowedMessage =
|
||||
gettext("You are not allowed to delete kingbirds: %s");
|
||||
|
||||
return service;
|
||||
|
||||
//////////////
|
||||
|
||||
// fixme: include this function in your service
|
||||
// if you plan to emit events to the parent controller,
|
||||
// otherwise remove it
|
||||
function initAction() {
|
||||
}
|
||||
|
||||
function allowed() {
|
||||
return $qExtensions.booleanAsPromise(true);
|
||||
// fixme: if you need to set policy, change as follow
|
||||
//return policy.ifAllowed({ rules: [['kingbird', 'delete_kingbird']] });
|
||||
}
|
||||
|
||||
// delete selected resource objects
|
||||
function perform(selected, newScope) {
|
||||
scope = newScope;
|
||||
selected = angular.isArray(selected) ? selected : [selected];
|
||||
context.labels = labelize(selected.length);
|
||||
return $qExtensions.allSettled(selected.map(checkPermission)).then(afterCheck);
|
||||
}
|
||||
|
||||
function labelize(count) {
|
||||
return {
|
||||
title: ngettext('Confirm Delete Kingbird',
|
||||
'Confirm Delete Kingbirds', count),
|
||||
/* eslint-disable max-len */
|
||||
message: ngettext('You have selected "%s". Please confirm your selection. Deleted kingbird is not recoverable.',
|
||||
'You have selected "%s". Please confirm your selection. Deleted kingbirds are not recoverable.', count),
|
||||
/* eslint-enable max-len */
|
||||
submit: ngettext('Delete Kingbird',
|
||||
'Delete Kingbirds', count),
|
||||
success: ngettext('Deleted Kingbird: %s.',
|
||||
'Deleted Kingbirds: %s.', count),
|
||||
error: ngettext('Unable to delete Kingbird: %s.',
|
||||
'Unable to delete Kingbirds: %s.', count)
|
||||
};
|
||||
}
|
||||
|
||||
// for batch delete
|
||||
function checkPermission(selected) {
|
||||
return {promise: allowed(selected), context: selected};
|
||||
}
|
||||
|
||||
// for batch delete
|
||||
function afterCheck(result) {
|
||||
var outcome = $q.reject(); // Reject the promise by default
|
||||
if (result.fail.length > 0) {
|
||||
toast.add('error', getMessage(notAllowedMessage, result.fail));
|
||||
outcome = $q.reject(result.fail);
|
||||
}
|
||||
if (result.pass.length > 0) {
|
||||
outcome = deleteModal.open(scope, result.pass.map(getEntity), context).then(createResult);
|
||||
}
|
||||
return outcome;
|
||||
}
|
||||
|
||||
function createResult(deleteModalResult) {
|
||||
// To make the result of this action generically useful, reformat the return
|
||||
// from the deleteModal into a standard form
|
||||
var result = actionResult.getActionResult();
|
||||
deleteModalResult.pass.forEach(function markDeleted(item) {
|
||||
result.deleted(resourceType, getEntity(item).id);
|
||||
});
|
||||
deleteModalResult.fail.forEach(function markFailed(item) {
|
||||
result.failed(resourceType, getEntity(item).id);
|
||||
});
|
||||
if (result.result.failed.length === 0 && result.result.deleted.length > 0) {
|
||||
$location.path('/kingbird/kingbirds');
|
||||
} else {
|
||||
return result.result;
|
||||
}
|
||||
}
|
||||
|
||||
function getMessage(message, entities) {
|
||||
return interpolate(message, [entities.map(getName).join(", ")]);
|
||||
}
|
||||
|
||||
function getName(result) {
|
||||
return getEntity(result).name;
|
||||
}
|
||||
|
||||
// for batch delete
|
||||
function getEntity(result) {
|
||||
return result.context;
|
||||
}
|
||||
|
||||
// call delete REST API
|
||||
function deleteEntity(id) {
|
||||
return api.deleteKingbird(id, true);
|
||||
}
|
||||
}
|
||||
})();
|
|
@ -1,122 +0,0 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc overview
|
||||
* @name horizon.dashboard.kingbird.kingbirds.update.service
|
||||
* @description Service for the kingbird update modal
|
||||
*/
|
||||
angular
|
||||
.module('horizon.dashboard.kingbird.kingbirds')
|
||||
.factory('horizon.dashboard.kingbird.kingbirds.update.service', updateService);
|
||||
|
||||
updateService.$inject = [
|
||||
'$location',
|
||||
'horizon.app.core.openstack-service-api.kingbird_dashboard',
|
||||
'horizon.app.core.openstack-service-api.policy',
|
||||
'horizon.framework.util.actions.action-result.service',
|
||||
'horizon.framework.util.i18n.gettext',
|
||||
'horizon.framework.util.q.extensions',
|
||||
'horizon.framework.widgets.toast.service',
|
||||
'horizon.dashboard.kingbird.kingbirds.events',
|
||||
'horizon.dashboard.kingbird.kingbirds.model',
|
||||
'horizon.dashboard.kingbird.kingbirds.resourceType',
|
||||
'horizon.dashboard.kingbird.kingbirds.workflow'
|
||||
];
|
||||
|
||||
function updateService(
|
||||
$location, api, policy, actionResult, gettext, $qExtensions,
|
||||
toast, events, model, resourceType, workflow
|
||||
) {
|
||||
|
||||
var message = {
|
||||
success: gettext('Kingbird %s was successfully updated.')
|
||||
};
|
||||
|
||||
var service = {
|
||||
initAction: initAction,
|
||||
perform: perform,
|
||||
allowed: allowed
|
||||
};
|
||||
|
||||
var id;
|
||||
|
||||
return service;
|
||||
|
||||
//////////////
|
||||
|
||||
// fixme: include this function in your service
|
||||
// if you plan to emit events to the parent controller,
|
||||
// otherwise remove it
|
||||
function initAction() {
|
||||
}
|
||||
|
||||
// fixme: if newScope is unnecessary, remove it
|
||||
/* eslint-disable no-unused-vars */
|
||||
function perform(selected, newScope) {
|
||||
// modal title, buttons
|
||||
var title, submitText, submitIcon;
|
||||
title = gettext("Update Kingbird");
|
||||
submitText = gettext("Update");
|
||||
submitIcon = "fa fa-check";
|
||||
model.init();
|
||||
|
||||
// load current data
|
||||
id = selected.id;
|
||||
var deferred = api.getKingbird(id);
|
||||
deferred.then(onLoad);
|
||||
|
||||
function onLoad(response) {
|
||||
model.spec.id = response.data.id;
|
||||
model.spec.name = response.data.name;
|
||||
model.spec.description = response.data.description;
|
||||
model.spec.enabled = response.data.enabled;
|
||||
model.spec.size = response.data.size;
|
||||
model.spec.temperature = response.data.temperature;
|
||||
model.spec.base = response.data.base;
|
||||
model.spec.flavor = response.data.flavor;
|
||||
model.spec.topping = response.data.topping;
|
||||
}
|
||||
|
||||
var result = workflow.init(title, submitText, submitIcon, model.spec);
|
||||
return result.then(submit);
|
||||
}
|
||||
|
||||
function allowed() {
|
||||
return $qExtensions.booleanAsPromise(true);
|
||||
// fixme: if you need to set policy, change as follow
|
||||
//return policy.ifAllowed({ rules: [['kingbird', 'update_kingbird']] });
|
||||
}
|
||||
|
||||
function submit() {
|
||||
model.cleanProperties();
|
||||
return api.updateKingbird(id, model.spec).then(success);
|
||||
}
|
||||
|
||||
function success(response) {
|
||||
response.data.id = response.data.uuid;
|
||||
toast.add('success', interpolate(message.success, [response.data.id]));
|
||||
var result = actionResult.getActionResult()
|
||||
.updated(resourceType, response.data.id);
|
||||
if (result.result.failed.length === 0 && result.result.updated.length > 0) {
|
||||
$location.path('/kingbird/kingbirds');
|
||||
} else {
|
||||
return result.result;
|
||||
}
|
||||
}
|
||||
}
|
||||
})();
|
|
@ -1,57 +0,0 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc overview
|
||||
* @ngname horizon.dashboard.kingbird.kingbirds.details
|
||||
*
|
||||
* @description
|
||||
* Provides details features for Kingbird.
|
||||
*/
|
||||
angular
|
||||
.module('horizon.dashboard.kingbird.kingbirds.details', [
|
||||
'horizon.app.core',
|
||||
'horizon.framework.conf'
|
||||
])
|
||||
.run(registerDetails);
|
||||
|
||||
registerDetails.$inject = [
|
||||
'horizon.app.core.openstack-service-api.kingbird_dashboard',
|
||||
'horizon.dashboard.kingbird.kingbirds.basePath',
|
||||
'horizon.dashboard.kingbird.kingbirds.resourceType',
|
||||
'horizon.framework.conf.resource-type-registry.service'
|
||||
];
|
||||
|
||||
function registerDetails(
|
||||
api,
|
||||
basePath,
|
||||
resourceType,
|
||||
registry
|
||||
) {
|
||||
registry.getResourceType(resourceType)
|
||||
.setLoadFunction(loadFunction)
|
||||
.detailsViews.append({
|
||||
id: 'kingbirdDetailsOverview',
|
||||
name: gettext('Overview'),
|
||||
template: basePath + 'details/overview.html'
|
||||
});
|
||||
|
||||
function loadFunction(identifier) {
|
||||
return api.getKingbird(identifier);
|
||||
}
|
||||
}
|
||||
})();
|
|
@ -1,6 +0,0 @@
|
|||
<hz-resource-property-list
|
||||
resource-type-name="OS::kingbird_dashboard::Kingbird"
|
||||
item="item"
|
||||
property-groups="[['id'],
|
||||
['topping', 'created_at', 'updated_at']]">
|
||||
</hz-resource-property-list>
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
angular
|
||||
.module('horizon.dashboard.kingbird.kingbirds')
|
||||
.controller('horizon.dashboard.kingbird.kingbirds.OverviewController', controller);
|
||||
|
||||
controller.$inject = [
|
||||
'$scope'
|
||||
];
|
||||
|
||||
function controller(
|
||||
$scope
|
||||
) {
|
||||
var ctrl = this;
|
||||
ctrl.kingbird = {};
|
||||
|
||||
$scope.context.loadPromise.then(onGetKingbird);
|
||||
|
||||
function onGetKingbird(kingbird) {
|
||||
ctrl.kingbird = kingbird.data;
|
||||
}
|
||||
}
|
||||
})();
|
|
@ -1,16 +0,0 @@
|
|||
<div ng-controller="horizon.dashboard.kingbird.kingbirds.OverviewController as ctrl">
|
||||
<div class="row">
|
||||
<div class="col-md-12 detail">
|
||||
<h3 translate>Kingbird</h3>
|
||||
<hr>
|
||||
<hz-resource-property-list
|
||||
resource-type-name="OS::kingbird_dashboard::Kingbird"
|
||||
cls="dl-horizontal"
|
||||
item="ctrl.kingbird"
|
||||
property-groups="[['name', 'description', 'enabled'],
|
||||
['size', 'temperature', 'base', 'flavor', 'topping'],
|
||||
['id', 'created_at', 'updated_at']]">
|
||||
</hz-resource-property-list>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,169 +0,0 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc overview
|
||||
* @name horizon.dashboard.kingbird.kingbirds
|
||||
* @ngModule
|
||||
* @description
|
||||
* Provides all the services and widgets require to display the Kingbird
|
||||
* panel
|
||||
*/
|
||||
angular
|
||||
.module('horizon.dashboard.kingbird.kingbirds', [
|
||||
'ngRoute',
|
||||
'horizon.dashboard.kingbird.kingbirds.actions',
|
||||
'horizon.dashboard.kingbird.kingbirds.details'
|
||||
])
|
||||
.constant('horizon.dashboard.kingbird.kingbirds.events', events())
|
||||
.constant('horizon.dashboard.kingbird.kingbirds.resourceType', 'OS::kingbird_dashboard::Kingbird')
|
||||
.run(run)
|
||||
.config(config);
|
||||
|
||||
/**
|
||||
* @ngdoc constant
|
||||
* @name horizon.dashboard.kingbird.kingbirds.events
|
||||
* @description A list of events used by Kingbird
|
||||
* @returns {Object} events
|
||||
*/
|
||||
function events() {
|
||||
return {
|
||||
CREATE_SUCCESS: 'horizon.dashboard.kingbird.kingbirds.CREATE_SUCCESS',
|
||||
DELETE_SUCCESS: 'horizon.dashboard.kingbird.kingbirds.DELETE_SUCCESS'
|
||||
};
|
||||
}
|
||||
|
||||
run.$inject = [
|
||||
'horizon.framework.conf.resource-type-registry.service',
|
||||
'horizon.dashboard.kingbird.kingbirds.service',
|
||||
'horizon.dashboard.kingbird.kingbirds.basePath',
|
||||
'horizon.dashboard.kingbird.kingbirds.resourceType'
|
||||
];
|
||||
|
||||
function run(registry, service, basePath, resourceType) {
|
||||
registry.getResourceType(resourceType)
|
||||
.setNames(gettext('Kingbird'), gettext('Kingbirds'))
|
||||
// for detail summary view on table row
|
||||
.setSummaryTemplateUrl(basePath + 'details/drawer.html')
|
||||
// specify items for table row items, summary view and details view
|
||||
.setProperties(properties())
|
||||
// get items for table
|
||||
.setListFunction(service.getPromise)
|
||||
// specify table columns
|
||||
.tableColumns
|
||||
.append({
|
||||
id: 'name',
|
||||
priority: 1,
|
||||
sortDefault: true,
|
||||
filters: ['noName'],
|
||||
urlFunction: service.urlFunction
|
||||
})
|
||||
.append({
|
||||
id: 'size',
|
||||
priority: 1,
|
||||
filters: ['noValue']
|
||||
})
|
||||
.append({
|
||||
id: 'temperature',
|
||||
priority: 1,
|
||||
filters: ['noValue']
|
||||
})
|
||||
.append({
|
||||
id: 'base',
|
||||
priority: 1,
|
||||
filters: ['noValue']
|
||||
})
|
||||
.append({
|
||||
id: 'flavor',
|
||||
priority: 1,
|
||||
filters: ['noValue']
|
||||
})
|
||||
.append({
|
||||
id: 'topping',
|
||||
priority: 2,
|
||||
filters: ['noValue']
|
||||
})
|
||||
.append({
|
||||
id: 'created_at',
|
||||
priority: 2
|
||||
})
|
||||
.append({
|
||||
id: 'updated_at',
|
||||
priority: 2
|
||||
});
|
||||
// for magic-search
|
||||
registry.getResourceType(resourceType).filterFacets
|
||||
.append({
|
||||
'label': gettext('Name'),
|
||||
'name': 'name',
|
||||
'singleton': true
|
||||
})
|
||||
.append({
|
||||
'label': gettext('Base'),
|
||||
'name': 'base',
|
||||
'singleton': true
|
||||
})
|
||||
.append({
|
||||
'label': gettext('Flavor'),
|
||||
'name': 'flavor',
|
||||
'singleton': true
|
||||
})
|
||||
.append({
|
||||
'label': gettext('ID'),
|
||||
'name': 'id',
|
||||
'singleton': true
|
||||
});
|
||||
}
|
||||
|
||||
function properties() {
|
||||
return {
|
||||
id: { label: gettext('ID'), filters: ['noValue'] },
|
||||
name: { label: gettext('Name'), filters: ['noName'] },
|
||||
description: { label: gettext('Description'), filters: ['noValue'] },
|
||||
enabled: { label: gettext('Enabled'), filters: ['yesno'] },
|
||||
size: { label: gettext('Size'), filters: ['noValue'] },
|
||||
temperature: { label: gettext('Temperature'), filters: ['noValue'] },
|
||||
base: { label: gettext('Base'), filters: ['noValue'] },
|
||||
flavor: { label: gettext('Flavor'), filters: ['noValue'] },
|
||||
topping: { label: gettext('Topping'), filters: ['noValue'] },
|
||||
created_at: { label: gettext('Created'), filters: ['simpleDate', 'noValue'] },
|
||||
updated_at: { label: gettext('Updated'), filters: ['simpleDate', 'noValue'] }
|
||||
};
|
||||
}
|
||||
|
||||
config.$inject = [
|
||||
'$provide',
|
||||
'$windowProvider',
|
||||
'$routeProvider'
|
||||
];
|
||||
|
||||
/**
|
||||
* @name config
|
||||
* @param {Object} $provide
|
||||
* @param {Object} $windowProvider
|
||||
* @param {Object} $routeProvider
|
||||
* @description Routes used by this module.
|
||||
* @returns {undefined} Returns nothing
|
||||
*/
|
||||
function config($provide, $windowProvider, $routeProvider) {
|
||||
var path = $windowProvider.$get().STATIC_URL + 'dashboard/kingbird/kingbirds/';
|
||||
$provide.constant('horizon.dashboard.kingbird.kingbirds.basePath', path);
|
||||
$routeProvider.when('/kingbird/kingbirds', {
|
||||
templateUrl: path + 'panel.html'
|
||||
});
|
||||
}
|
||||
})();
|
|
@ -1,23 +0,0 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
describe('horizon.dashboard.kingbird.kingbirds', function() {
|
||||
it('should exist', function() {
|
||||
expect(angular.module('horizon.dashboard.kingbird.kingbirds')).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
})();
|
|
@ -1,63 +0,0 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
angular.module('horizon.dashboard.kingbird.kingbirds')
|
||||
.factory('horizon.dashboard.kingbird.kingbirds.service',
|
||||
service);
|
||||
|
||||
service.$inject = [
|
||||
'$filter',
|
||||
'horizon.app.core.detailRoute',
|
||||
'horizon.app.core.openstack-service-api.kingbird_dashboard'
|
||||
];
|
||||
|
||||
/*
|
||||
* @ngdoc factory
|
||||
* @name horizon.dashboard.kingbird.kingbirds.service
|
||||
*
|
||||
* @description
|
||||
* This service provides functions that are used through the Kingbirds
|
||||
* features. These are primarily used in the module registrations
|
||||
* but do not need to be restricted to such use. Each exposed function
|
||||
* is documented below.
|
||||
*/
|
||||
function service($filter, detailRoute, api) {
|
||||
return {
|
||||
getPromise: getPromise,
|
||||
urlFunction: urlFunction
|
||||
};
|
||||
|
||||
function getPromise(params) {
|
||||
return api.getKingbirds(params).then(modifyResponse);
|
||||
}
|
||||
|
||||
function modifyResponse(response) {
|
||||
return {data: {items: response.data.items.map(modifyItem)}};
|
||||
|
||||
function modifyItem(item) {
|
||||
var timestamp = item.updated_at ? item.updated_at : item.created_at;
|
||||
item.trackBy = item.id.concat(timestamp);
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
function urlFunction(item) {
|
||||
return detailRoute + 'OS::kingbird_dashboard::Kingbird/' + item.id;
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
describe('Kingbirds service', function() {
|
||||
var service;
|
||||
beforeEach(module('horizon.dashboard.kingbird.kingbirds'));
|
||||
beforeEach(inject(function($injector) {
|
||||
service = $injector.get('horizon.dashboard.kingbird.kingbirds.service');
|
||||
}));
|
||||
|
||||
describe('getPromise', function() {
|
||||
it("provides a promise", inject(function($q, $injector, $timeout) {
|
||||
var api = $injector.get('horizon.app.core.openstack-service-api.kingbird_dashboard');
|
||||
var deferred = $q.defer();
|
||||
spyOn(api, 'getKingbirds').and.returnValue(deferred.promise);
|
||||
var result = service.getPromise({});
|
||||
deferred.resolve({
|
||||
data:{
|
||||
items: [{id: 123, name: 'resource1'}]
|
||||
}
|
||||
});
|
||||
$timeout.flush();
|
||||
expect(api.getKingbirds).toHaveBeenCalled();
|
||||
expect(result.$$state.value.data.items[0].name).toBe('resource1');
|
||||
}));
|
||||
});
|
||||
|
||||
describe('urlFunction', function() {
|
||||
it("get url", inject(function() {
|
||||
var detailRoute = $injector.get('horizon.app.core.detailRoute');
|
||||
var result = service.urlFunction({id:"123abc"});
|
||||
expect(result).toBe(detailRoute + "OS::kingbird_dashboard::Kingbird/123abc");
|
||||
}));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
})();
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
<hz-resource-panel resource-type-name="OS::kingbird_dashboard::Kingbird">
|
||||
<hz-resource-table resource-type-name="OS::kingbird_dashboard::Kingbird"
|
||||
track-by="trackBy">
|
||||
</hz-resource-table>
|
||||
</hz-resource-panel>
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
<dl>
|
||||
<dt translate>Kingbird Name</dt>
|
||||
<dd translate>An arbitrary human-readable name</dd>
|
||||
</dl>
|
|
@ -1,63 +0,0 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc model
|
||||
* @name horizon.dashboard.kingbird.kingbirds.model
|
||||
* @description Service for the kingbird model
|
||||
*/
|
||||
angular
|
||||
.module('horizon.dashboard.kingbird.kingbirds')
|
||||
.factory('horizon.dashboard.kingbird.kingbirds.model', model);
|
||||
|
||||
model.$inject = [
|
||||
];
|
||||
|
||||
function model() {
|
||||
var model = {
|
||||
// params
|
||||
"spec": {},
|
||||
|
||||
// methods
|
||||
"init": init,
|
||||
"cleanProperties": cleanProperties
|
||||
};
|
||||
|
||||
function init() {
|
||||
// initialize model
|
||||
model.spec = {
|
||||
"id": "",
|
||||
"name": "", // text required
|
||||
"description": "", // textarea
|
||||
"enabled": true, // checkbox
|
||||
"size": "M", // radio
|
||||
"temperature": "H", // radio
|
||||
"base": "", // select
|
||||
"flavor": "", // select
|
||||
"topping": "" // checkboxes
|
||||
};
|
||||
}
|
||||
|
||||
function cleanProperties() {
|
||||
delete model.spec.id;
|
||||
delete model.spec.tabs;
|
||||
}
|
||||
|
||||
return model;
|
||||
}
|
||||
})();
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
<dl>
|
||||
<dt translate>Base</dt>
|
||||
<dd translate>Choose base drink.</dd>
|
||||
<dt translate>Other options</dt>
|
||||
<dd translate>Choose favorite options.</dd>
|
||||
</dl>
|
||||
|
|
@ -1,211 +0,0 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc workflow
|
||||
* @name horizon.dashboard.kingbird.kingbirds.workflow
|
||||
* @description Service for the create/update workflow
|
||||
*/
|
||||
angular
|
||||
.module('horizon.dashboard.kingbird.kingbirds')
|
||||
.factory('horizon.dashboard.kingbird.kingbirds.workflow', workflow);
|
||||
|
||||
workflow.$inject = [
|
||||
'horizon.dashboard.kingbird.basePath',
|
||||
'horizon.framework.util.i18n.gettext',
|
||||
'horizon.framework.widgets.form.ModalFormService'
|
||||
];
|
||||
|
||||
function workflow(basePath, gettext, modal) {
|
||||
var workflow = {
|
||||
init: init
|
||||
};
|
||||
|
||||
function init(title, submitText, submitIcon, model) {
|
||||
var schema, form;
|
||||
|
||||
// schema
|
||||
schema = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"title": gettext("Name"),
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"title": gettext("Description"),
|
||||
"type": "string"
|
||||
},
|
||||
"enabled": {
|
||||
"title": gettext("Enabled"),
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"size": {
|
||||
"title": gettext("Size"),
|
||||
"type": "string",
|
||||
"default": "M"
|
||||
},
|
||||
"temperature": {
|
||||
"title": gettext("Temperature"),
|
||||
"type": "string",
|
||||
"default": "H"
|
||||
},
|
||||
"base": {
|
||||
"title": gettext("Base"),
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"flavor": {
|
||||
"title": gettext("Flavor"),
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"topping": {
|
||||
"title": gettext("Topping")
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// form
|
||||
form = [
|
||||
{
|
||||
"type": "tabs",
|
||||
"tabs": [
|
||||
{
|
||||
"title": gettext("Info"),
|
||||
"help": basePath + "kingbirds/workflow/info.help.html",
|
||||
"items": [
|
||||
{
|
||||
"key": "name",
|
||||
"placeholder": gettext("Name of the kingbird."),
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"key": "description",
|
||||
"type": "textarea"
|
||||
},
|
||||
{
|
||||
"key": "enabled",
|
||||
"type": "checkbox"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": gettext("Recipe"),
|
||||
"help": basePath + "kingbirds/workflow/recipe.help.html",
|
||||
"items": [
|
||||
{
|
||||
"key": "size",
|
||||
"type": "radiobuttons",
|
||||
"titleMap": [
|
||||
{"value": "S", "name": gettext("Small")},
|
||||
{"value": "M", "name": gettext("Medium")},
|
||||
{"value": "L", "name": gettext("Large")},
|
||||
{"value": "XL", "name": gettext("Extra Large")}
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "temperature",
|
||||
"type": "radiobuttons",
|
||||
"titleMap": [
|
||||
{"value": "H", "name": gettext("Hot")},
|
||||
{"value": "I", "name": gettext("Ice")}
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "base",
|
||||
"type": "select",
|
||||
"titleMap": [
|
||||
{"value": "", "name": gettext("Choose base.")},
|
||||
{
|
||||
"value": "blend",
|
||||
"name": gettext("House Blend"),
|
||||
"group": gettext("Coffee")
|
||||
},
|
||||
{
|
||||
"value": "mandheling",
|
||||
"name": gettext("Mandheling"),
|
||||
"group": gettext("Coffee")},
|
||||
{
|
||||
"value": "colombia",
|
||||
"name": gettext("Colombia"),
|
||||
"group": gettext("Coffee")
|
||||
},
|
||||
{
|
||||
"value": "espresso",
|
||||
"name": gettext("Espresso"),
|
||||
"group": gettext("Coffee")
|
||||
},
|
||||
{
|
||||
"value": "earl_gray",
|
||||
"name": gettext("Earl Gray"),
|
||||
"group": gettext("Tea")
|
||||
},
|
||||
{
|
||||
"value": "darjeeling",
|
||||
"name": gettext("Darjeeling"),
|
||||
"group": gettext("Tea")},
|
||||
{
|
||||
"value": "orange_pekoe",
|
||||
"name": gettext("Orange Pekoe"),
|
||||
"group": gettext("Tea")
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "flavor",
|
||||
"type": "select",
|
||||
"titleMap": [
|
||||
{"value": "", "name": gettext("Choose flavor.")},
|
||||
{"value": "chocolate", "name": gettext("Chocolate")},
|
||||
{"value": "mocha", "name": gettext("Mocha")},
|
||||
{"value": "strawberry", "name": gettext("Strawberry")},
|
||||
{"value": "blueberry", "name": gettext("Blueberry")},
|
||||
{"value": "raspberry", "name": gettext("Raspberry")}
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "topping",
|
||||
"type": "checkboxes",
|
||||
"titleMap": [
|
||||
{"value": "clushed_nuts", "name": gettext("Clushed Nuts")},
|
||||
{"value": "whip_cream", "name": gettext("Whip Cream")},
|
||||
{"value": "mixed_serial", "name": gettext("Mixed Serial")}
|
||||
]
|
||||
}
|
||||
] // items
|
||||
} // tab
|
||||
] // tabs
|
||||
}
|
||||
]; // form
|
||||
|
||||
var config = {
|
||||
"title": title,
|
||||
"submitText": submitText,
|
||||
"schema": schema,
|
||||
"form": form,
|
||||
"model": model
|
||||
};
|
||||
|
||||
return modal.open(config);
|
||||
}
|
||||
|
||||
return workflow;
|
||||
}
|
||||
})();
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
# 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 pbr.version
|
||||
|
||||
version_info = pbr.version.VersionInfo('kingbird_dashboard')
|
Loading…
Reference in New Issue