Add angular Load Balancers V2 panel

This adds an angular version of the Load Balancers V2 panel,
including the Load Balancers table and config to get karma working.
The karma config assumes the horizon directory is a sibling of
neutron-lbaas-dashboard.

To use, copy the _1481_project_ng_loadbalancersv2_panel.py file from
neutron_lbaas_dashboard/enabled into openstack_dashboard/enabled.

Partially-Implements: blueprint horizon-lbaas-v2-ui
Change-Id: I99c4ec705433385c19256c887944706472642a25
This commit is contained in:
Justin Pomeroy 2015-11-04 14:33:49 -06:00
parent 2d5f4a29ae
commit 75fc881f7f
23 changed files with 1065 additions and 2 deletions

2
.gitignore vendored
View File

@ -28,6 +28,8 @@ pip-log.txt
nosetests.xml
.testrepository
.venv
node_modules
coverage*
# Translations
*.mo

View File

@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
@ -14,6 +12,9 @@
import pbr.version
# Register the REST API URLs so they can be called from the JavaScript files
import neutron_lbaas_dashboard.api.rest # noqa
__version__ = pbr.version.VersionInfo(
'neutron_lbaas_dashboard').version_string()

View File

@ -0,0 +1,25 @@
# Copyright 2015 IBM Corp.
#
# 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.
"""This package holds the REST API that supports the LBaaS v2 dashboard
Javascript code.
It is not intended to be used outside of Horizon, and makes no promises of
stability or fitness for purpose outside of that scope.
It does not promise to adhere to the general OpenStack API Guidelines set out
in https://wiki.openstack.org/wiki/APIChangeGuidelines.
"""
# import REST API modules here
from neutron_lbaas_dashboard.api.rest import lbaasv2 # noqa

View File

@ -0,0 +1,41 @@
# Copyright 2015 IBM Corp.
#
# 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.
"""API over the neutron LBaaS v2 service.
"""
from django.views import generic
from openstack_dashboard.api import neutron
from openstack_dashboard.api.rest import urls
from openstack_dashboard.api.rest import utils as rest_utils
neutronclient = neutron.neutronclient
@urls.register
class LoadBalancers(generic.View):
"""API for load balancers.
"""
url_regex = r'lbaas/loadbalancers/$'
@rest_utils.ajax()
def get(self, request):
"""List load balancers for current project.
The listing result is an object with property "items".
"""
tenant_id = request.user.project_id
result = neutronclient(request).list_loadbalancers(tenant_id=tenant_id)
return {'items': result.get('loadbalancers')}

View File

@ -0,0 +1,42 @@
# (c) Copyright 2015 Hewlett-Packard Development Company, L.P.
#
# 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 NGLoadBalancers(horizon.Panel):
name = _("Load Balancers")
slug = 'ngloadbalancersv2'
permissions = ('openstack.services.network',)
def allowed(self, context):
# TODO(jpomero) temporarily enabling panel for any user
# request = context['request']
# if not request.user.has_perms(self.permissions):
# return False
# try:
# if not neutron.is_service_enabled(request,
# config_name='enable_lb',
# ext_name='lbaas'):
# return False
# except Exception:
# LOG.error("Call to list enabled services failed. This is likely "
# "due to a problem communicating with the Neutron "
# "endpoint. Load Balancers panel will not be displayed")
# return False
# if not super(LoadBalancer, self).allowed(context):
# return False
return True

View File

@ -0,0 +1,11 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Load Balancers" %}{% endblock %}
{% block page_header %}
<hz-page-header header="'{% trans "Load Balancers" %}'"></hz-page-header>
{% endblock page_header %}
{% block main %}
<ng-include src="'{{ STATIC_URL }}dashboard/project/lbaasv2/loadbalancers/table.html'"></ng-include>
{% endblock %}

View File

@ -0,0 +1,24 @@
# (c) Copyright 2015 Hewlett-Packard Development Company, L.P.
#
# 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 patterns
from django.conf.urls import url
from neutron_lbaas_dashboard.dashboards.project.ngloadbalancersv2 import views
urlpatterns = patterns(
'neutron_lbaas_dashboard.dashboards.project.ngloadbalancersv2.views',
url(r'^$', views.IndexView.as_view(), name='index'),
)

View File

@ -0,0 +1,19 @@
# (c) Copyright 2015 Hewlett-Packard Development Company, L.P.
#
# 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
class IndexView(generic.TemplateView):
template_name = 'project/ngloadbalancersv2/index.html'

View File

@ -0,0 +1,51 @@
# Copyright 2015 IBM Corp.
#
# 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.
# The slug of the panel to be added to HORIZON_CONFIG. Required.
PANEL = 'ngloadbalancersv2'
# The slug of the dashboard the PANEL associated with. Required.
PANEL_DASHBOARD = 'project'
# The slug of the panel group the PANEL is associated with.
PANEL_GROUP = 'network'
# Python panel class of the PANEL to be added.
ADD_PANEL = (
'neutron_lbaas_dashboard.dashboards.project.ngloadbalancersv2.panel'
'.NGLoadBalancers')
ADD_INSTALLED_APPS = ['neutron_lbaas_dashboard']
ADD_ANGULAR_MODULES = ['horizon.dashboard.project.lbaasv2']
# AUTO_DISCOVER_STATIC_FILES = True
ADD_JS_FILES = [
'app/core/openstack-service-api/lbaasv2.service.js',
'dashboard/project/lbaasv2/lbaasv2.module.js',
'dashboard/project/lbaasv2/loadbalancers/loadbalancers.module.js',
'dashboard/project/lbaasv2/loadbalancers/table.controller.js',
'dashboard/project/lbaasv2/loadbalancers/filters.js',
]
ADD_JS_SPEC_FILES = [
'app/core/openstack-service-api/lbaasv2.service.spec.js',
'dashboard/project/lbaasv2/lbaasv2.module.spec.js',
'dashboard/project/lbaasv2/loadbalancers/loadbalancers.module.spec.js',
'dashboard/project/lbaasv2/loadbalancers/table.controller.spec.js',
'dashboard/project/lbaasv2/loadbalancers/filters.spec.js',
]
#
# ADD_SCSS_FILES = [
#
# ]

View File

@ -0,0 +1,168 @@
/*
* Copyright 2015 IBM Corp.
*
* 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) {
var xstaticPath;
var basePaths = [
'../horizon/.venv'
];
for (var i = 0; i < basePaths.length; i++) {
var basePath = path.resolve(basePaths[i]);
if (fs.existsSync(basePath)) {
xstaticPath = basePath + '/lib/python2.7/site-packages/xstatic/pkg/';
break;
}
}
if (!xstaticPath) {
console.error('xStatic libraries not found, please set up venv');
process.exit(1);
}
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: '/static/',
moduleName: 'templates'
},
// Assumes you're in the top-level horizon directory.
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.
*/
'../../horizon/test-shim.js',
// from jasmine.html
xstaticPath + 'jquery/data/jquery.js',
xstaticPath + 'angular/data/angular.js',
xstaticPath + 'angular/data/angular-mocks.js',
xstaticPath + 'angular/data/angular-cookies.js',
xstaticPath + 'angular_bootstrap/data/angular-bootstrap.js',
xstaticPath + 'angular_gettext/data/angular-gettext.js',
xstaticPath + 'angular/data/angular-sanitize.js',
xstaticPath + 'd3/data/d3.js',
xstaticPath + 'rickshaw/data/rickshaw.js',
xstaticPath + 'angular_smart_table/data/smart-table.js',
xstaticPath + 'angular_lrdragndrop/data/lrdragndrop.js',
xstaticPath + 'spin/data/spin.js',
xstaticPath + 'spin/data/spin.jquery.js',
// TODO: These should be mocked.
'../../horizon/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.
*/
'../../horizon/horizon/static/framework/**/*.module.js',
'../../horizon/horizon/static/framework/**/!(*.spec|*.mock).js',
'../../horizon/openstack_dashboard/static/**/*.module.js',
'../../horizon/openstack_dashboard/static/**/!(*.spec|*.mock).js',
'../../horizon/openstack_dashboard/dashboards/**/static/**/*.module.js',
'../../horizon/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.
*/
'../../horizon/openstack_dashboard/static/**/*.mock.js',
//'./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: ['PhantomJS'],
phantomjsLauncher: {
// Have phantomjs exit if a ResourceError is encountered
// (useful if karma exits without killing phantom)
exitOnResourceError: true
},
reporters: ['progress', 'coverage', 'threshold'],
plugins: [
'karma-phantomjs-launcher',
'karma-jasmine',
'karma-ng-html2js-preprocessor',
'karma-coverage',
'karma-threshold-reporter'
],
coverageReporter: {
type: 'html',
dir: '../coverage-karma/'
},
// Coverage threshold values.
thresholdReporter: {
statements: 100,
branches: 100,
functions: 100,
lines: 100
}
});
};

View File

@ -0,0 +1,60 @@
/*
* Copyright 2015 IBM Corp.
*
* 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.lbaasv2', lbaasv2API);
lbaasv2API.$inject = [
'horizon.framework.util.http.service',
'horizon.framework.widgets.toast.service'
];
/**
* @ngdoc service
* @name horizon.app.core.openstack-service-api.loadbalancers
* @description Provides direct pass through to neutron LBaaS v2 with NO abstraction.
*/
function lbaasv2API(apiService, toastService) {
var service = {
getLoadBalancers: getLoadBalancers
};
return service;
///////////////
// Load Balancers
/**
* @name horizon.app.core.openstack-service-api.lbaasv2.getLoadBalancers
* @description
* Get a list of load balancers.
*
* The listing result is an object with property "items". Each item is
* a load balancer.
*/
function getLoadBalancers() {
return apiService.get('/api/lbaas/loadbalancers/')
.error(function () {
toastService.add('error', gettext('Unable to retrieve load balancers.'));
});
}
}
}());

View File

@ -0,0 +1,57 @@
/*
* Copyright 2015 IBM Corp.
*
* 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('LBaaS v2 API', function() {
var testCall, service;
var apiService = {};
var toastService = {};
beforeEach(module('horizon.mock.openstack-service-api', function($provide, initServices) {
testCall = initServices($provide, apiService, toastService);
}));
beforeEach(module('horizon.app.core.openstack-service-api'));
beforeEach(inject(['horizon.app.core.openstack-service-api.lbaasv2', function(lbaasv2API) {
service = lbaasv2API;
}]));
it('defines the service', function() {
expect(service).toBeDefined();
});
var tests = [
{
"func": "getLoadBalancers",
"method": "get",
"path": "/api/lbaas/loadbalancers/",
"error": "Unable to retrieve load balancers."
}
];
// Iterate through the defined tests and apply as Jasmine specs.
angular.forEach(tests, function(params) {
it('defines the ' + params.func + ' call properly', function() {
var callParams = [apiService, service, toastService, params];
testCall.apply(this, callParams);
});
});
});
})();

View File

@ -0,0 +1,32 @@
/*
* Copyright 2015 IBM Corp.
*
* 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.project.lbaasv2
* @description
*
* # horizon.dashboard.project.lbaasv2
*
* The LBaaS v2 dashboard's top level module.
*/
angular
.module('horizon.dashboard.project.lbaasv2', [
'horizon.dashboard.project.lbaasv2.loadbalancers'
]);
}());

View File

@ -0,0 +1,25 @@
/*
* Copyright 2015 IBM Corp.
*
* 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('LBaaS v2 Module', function () {
it('should be defined', function () {
expect(angular.module('horizon.dashboard.project.lbaasv2')).toBeDefined();
});
});
})();

View File

@ -0,0 +1,73 @@
/*
* Copyright 2015 IBM Corp.
*
* 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.project.lbaasv2.loadbalancers')
.filter('operatingStatus', operatingStatusFilter)
.filter('provisioningStatus', provisioningStatusFilter);
operatingStatusFilter.$inject = [
'horizon.framework.util.i18n.gettext'
];
provisioningStatusFilter.$inject = [
'horizon.framework.util.i18n.gettext'
];
/**
* @ngdoc filter
* @name operatingStatusFilter
* @description
* Takes raw load balancer operating status from the API and returns the user friendly status.
*/
function operatingStatusFilter(gettext) {
var statuses = {
'ONLINE': gettext('Online'),
'OFFLINE': gettext('Offline'),
'DEGRADED': gettext('Degraded'),
'ERROR': gettext('Error')
};
return function (input) {
var result = statuses[input];
return angular.isDefined(result) ? result : input;
};
}
/**
* @ngdoc filter
* @name provisioningStatusFilter
* @description
* Takes raw load balancer provisioning status from the API and returns the user friendly status.
*/
function provisioningStatusFilter(gettext) {
var statuses = {
'ACTIVE': gettext('Active'),
'PENDING_CREATE': gettext('Pending Create'),
'PENDING_UPDATE': gettext('Pending Update'),
'PENDING_DELETE': gettext('Pending Delete'),
'ERROR': gettext('Error')
};
return function (input) {
var result = statuses[input];
return angular.isDefined(result) ? result : input;
};
}
}());

View File

@ -0,0 +1,54 @@
/*
* Copyright 2015 IBM Corp.
*
* 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('LBaaS v2 Load Balancers Filters', function () {
beforeEach(module('horizon.framework.util.i18n'));
beforeEach(module('horizon.dashboard.project.lbaasv2.loadbalancers'));
describe('operatingStatus', function () {
var operatingStatusFilter;
beforeEach(inject(function (_operatingStatusFilter_) {
operatingStatusFilter = _operatingStatusFilter_;
}));
it('Returns value when key is present', function () {
expect(operatingStatusFilter('ONLINE')).toBe('Online');
});
it('Returns input when key is not present', function () {
expect(operatingStatusFilter('unknown')).toBe('unknown');
});
});
describe('provisioningStatus', function () {
var provisioningStatusFilter;
beforeEach(inject(function (_provisioningStatusFilter_) {
provisioningStatusFilter = _provisioningStatusFilter_;
}));
it('Returns value when key is present', function () {
expect(provisioningStatusFilter('ACTIVE')).toBe('Active');
});
it('Returns input when key is not present', function () {
expect(provisioningStatusFilter('unknown')).toBe('unknown');
});
});
});
})();

View File

@ -0,0 +1,41 @@
/*
* Copyright 2015 IBM Corp.
*
* 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.project.lbaasv2.loadbalancers
*
* @description
* Provides the services and widgets required to support and display the project load
* balancers v2 panel.
*/
angular
.module('horizon.dashboard.project.lbaasv2.loadbalancers', [])
.config(config);
config.$inject = [
'$provide',
'$windowProvider'
];
function config($provide, $windowProvider) {
var path = $windowProvider.$get().STATIC_URL + 'dashboard/project/lbaasv2/loadbalancers/';
$provide.constant('horizon.dashboard.project.lbaasv2.loadbalancers.basePath', path);
}
})();

View File

@ -0,0 +1,45 @@
/*
* Copyright 2015 IBM Corp.
*
* 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('LBaaS v2 Load Balancers Module', function() {
it('should exist', function() {
expect(angular.module('horizon.dashboard.project.lbaasv2.loadbalancers')).toBeDefined();
});
});
describe('LBaaS v2 Load Balancers Module Base Path', function () {
var basePath, staticUrl;
beforeEach(module('horizon.dashboard.project'));
beforeEach(module('horizon.dashboard.project.lbaasv2'));
beforeEach(module('horizon.dashboard.project.lbaasv2.loadbalancers'));
beforeEach(inject(function ($injector) {
basePath = $injector.get('horizon.dashboard.project.lbaasv2.loadbalancers.basePath');
staticUrl = $injector.get('$window').STATIC_URL;
}));
it('should be defined', function () {
expect(basePath).toBeDefined();
});
it('should be correct', function () {
expect(basePath).toEqual(staticUrl + 'dashboard/project/lbaasv2/loadbalancers/');
});
});
})();

View File

@ -0,0 +1,58 @@
/*
* Copyright 2015 IBM Corp.
*
* 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.project.lbaasv2.loadbalancers')
.controller('loadBalancersTableController', LoadBalancersTableController);
LoadBalancersTableController.$inject = [
'horizon.dashboard.project.lbaasv2.loadbalancers.basePath',
'horizon.app.core.openstack-service-api.lbaasv2'
];
/**
* @ngdoc controller
* @name LoadBalancersTableController
*
* @description
* Controller for the LBaaS v2 load balancers table.
* Serves as the focal point for table actions.
*/
function LoadBalancersTableController(basepath, api) {
var ctrl = this;
ctrl.items = [];
ctrl.src = [];
ctrl.checked = {};
ctrl.path = basepath;
init();
////////////////////////////////
function init() {
api.getLoadBalancers().success(success);
}
function success(response) {
ctrl.src = response.items;
}
}
})();

View File

@ -0,0 +1,68 @@
/*
* Copyright 2015 IBM Corp.
*
* 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('LBaaS v2 Load Balancers Table Controller', function() {
var controller, lbaasv2API, staticUrl, items = [];
function fakeAPI() {
return {
success: function(callback) {
callback({ items: items });
}
};
}
///////////////////////
beforeEach(module('horizon.framework.util.http'));
beforeEach(module('horizon.framework.widgets.toast'));
beforeEach(module('horizon.app.core.openstack-service-api'));
beforeEach(module('horizon.dashboard.project.lbaasv2.loadbalancers'));
beforeEach(inject(function($injector) {
lbaasv2API = $injector.get('horizon.app.core.openstack-service-api.lbaasv2');
controller = $injector.get('$controller');
staticUrl = $injector.get('$window').STATIC_URL;
spyOn(lbaasv2API, 'getLoadBalancers').and.callFake(fakeAPI);
}));
function createController() {
return controller('loadBalancersTableController', {
lbaasv2API: lbaasv2API
});
}
it('should initialize correctly', function() {
var ctrl = createController();
expect(ctrl.items).toEqual([]);
expect(ctrl.src).toEqual(items);
expect(ctrl.checked).toEqual({});
});
it('should set path properly', function() {
var path = staticUrl + 'dashboard/project/lbaasv2/loadbalancers/';
expect(createController().path).toEqual(path);
});
it('should invoke lbaasv2 apis', function() {
createController();
expect(lbaasv2API.getLoadBalancers).toHaveBeenCalled();
});
});
})();

View File

@ -0,0 +1,138 @@
<table ng-controller="loadBalancersTableController as table"
hz-table ng-cloak
st-table="table.items"
st-safe-src="table.src"
default-sort="name"
default-sort-reverse="false"
class="table-striped table-rsp table-detail modern">
<!--
TODO(jpomero): This table pattern does not allow for extensibility and should be revisited
once horizon implements a better one.
-->
<thead>
<tr>
<!--
Table-batch-actions:
This is where batch actions like searching, creating, and deleting.
-->
<th colspan="100" class="search-header">
<hz-search-bar group-classes="input-group-sm" icon-classes="fa-search">
</hz-search-bar>
</th>
</tr>
<tr>
<!--
Table-column-headers:
This is where we declaratively define the table column headers.
Include select-col if you want to select all.
Include expander if you want to inline details.
Include action-col if you want to perform actions.
-->
<th class="select-col">
<input type="checkbox" hz-select-all="table.items">
</th>
<th class="expander"></th>
<th class="rsp-p1" st-sort="name" st-sort-default="name" translate>Name</th>
<th class="rsp-p1" st-sort="description" translate>Description</th>
<th class="rsp-p1" st-sort="operating_status" translate>Operating Status</th>
<th class="rsp-p1" st-sort="provisioning_status" translate>Provisioning Status</th>
<th class="rsp-p2" st-sort="vip_address" translate>IP Address</th>
<th class="rsp-p2" st-sort="listeners.length" translate>Listeners</th>
</tr>
</thead>
<tbody>
<!--
Table-rows:
This is where we declaratively define the table columns.
Include select-col if you want to select all.
Include expander if you want to inline details.
Include action-col if you want to perform actions.
rsp-p1 rsp-p2 are responsive priority as user resizes window.
-->
<tr ng-repeat-start="item in table.items track by item.id"
ng-class="{'st-selected': checked[item.id]}">
<td class="select-col">
<input type="checkbox"
ng-model="selected[item.id].checked"
hz-select="item">
</td>
<td class="expander">
<i class="fa fa-chevron-right"
hz-expand-detail
duration="200">
</i>
</td>
<td class="rsp-p1">{$ item.name $}</td>
<td class="rsp-p1">{$ item.description | noValue $}</td>
<td class="rsp-p1">{$ item.operating_status | operatingStatus $}</td>
<td class="rsp-p1">{$ item.provisioning_status | provisioningStatus $}</td>
<td class="rsp-p2">{$ item.vip_address $}</td>
<td class="rsp-p2">{$ item.listeners.length $}</td>
</tr>
<tr ng-repeat-end class="detail-row">
<!--
Detail-row:
Contains detailed information on this item.
Can be toggled using the chevron button.
Ensure colspan is greater or equal to number of column-headers.
-->
<td class="detail" colspan="100">
<!--
The responsive columns that disappear typically should reappear here
with the same responsive priority that they disappear.
E.g. table header with rsp-p2 should be here with rsp-alt-p2
-->
<div class="row">
<span class="rsp-alt-p2">
<dl class="col-sm-2">
<dt translate>IP Address</dt>
<dd>{$ item.vip_address $}</dd>
</dl>
<dl class="col-sm-2">
<dt translate>Listeners</dt>
<dd>{$ item.listeners.length $}</dd>
</dl>
</span>
</div>
<div class="row">
<dl class="col-sm-2">
<dt translate>Provider</dt>
<dd>{$ item.provider $}</dd>
</dl>
<dl class="col-sm-2">
<dt translate>Admin State Up</dt>
<dd>{$ item.admin_state_up | yesno $}</dd>
</dl>
<dl class="col-sm-2">
<dt translate>ID</dt>
<dd>{$ item.id $}</dd>
</dl>
<dl class="col-sm-2">
<dt translate>Subnet ID</dt>
<dd>{$ item.vip_subnet_id $}</dd>
</dl>
<dl class="col-sm-2">
<dt translate>Port ID</dt>
<dd>{$ item.vip_port_id $}</dd>
</dl>
</div>
</td>
</tr>
</tbody>
<!--
Table-footer:
This is where we display number of items and pagination controls.
-->
<tfoot hz-table-footer items="table.items"></tfoot>
</table>

28
package.json Normal file
View File

@ -0,0 +1,28 @@
{
"version": "0.0.0",
"private": true,
"name": "neutron-lbaas-dashboard",
"description": "Neutron LBaaS v2 Dashboard",
"repository": "none",
"license": "Apache 2.0",
"devDependencies": {
"eslint": "1.2.0",
"eslint-config-openstack": "1.2.2",
"eslint-plugin-angular": "0.4.0",
"jasmine-core": "2.2.0",
"karma": "0.12.31",
"karma-chrome-launcher": "0.1.8",
"karma-cli": "0.0.4",
"karma-coverage": "0.3.1",
"karma-jasmine": "0.3.5",
"karma-ng-html2js-preprocessor": "0.1.2",
"karma-phantomjs-launcher": "0.2.0",
"karma-threshold-reporter": "0.1.15",
"phantomjs": "1.9.17"
},
"scripts": {
"test": "karma start neutron_lbaas_dashboard/karma.conf.js --single-run",
"lint": "eslint --no-color neutron_lbaas_dashboard/static"
},
"dependencies": {}
}