[CORD-2030] Subscriber dashboard
Change-Id: Ie12cc13537975fd5e5ca8628d4ad14ce4810a925
diff --git a/xos/gui/src/app/services/graph.extension.ts b/xos/gui/src/app/services/graph.extension.ts
index 56310f2..0d90963 100644
--- a/xos/gui/src/app/services/graph.extension.ts
+++ b/xos/gui/src/app/services/graph.extension.ts
@@ -36,11 +36,6 @@
}
public setup() {
- this.XosServiceGraphExtender.register('finegrained', 'ecord-local', (graph): any => {
- graph = this.positionFineGrainedNodes(graph);
- return graph;
- });
-
this.XosServiceGraphExtender.register('coarse', 'ecord-local', (graph: any): any => {
graph.nodes = this.positionCoarseNodes(graph.nodes);
return {
@@ -60,7 +55,7 @@
private positionCoarseNodes(nodes: any[]): any[] {
// getting distance between nodes
const hStep = this.getSvgDimensions('xos-coarse-tenancy-graph').width / 4;
- const vStep = this.getSvgDimensions('xos-coarse-tenancy-graph').height / 4;
+ const vStep = this.getSvgDimensions('xos-coarse-tenancy-graph').height / 5;
const vtr = _.find(nodes, {label: 'vtr'});
if (vtr) {
@@ -69,6 +64,13 @@
vtr.fixed = true;
}
+ const rcord = _.find(nodes, {label: 'rcord'});
+ if (rcord) {
+ rcord.x = hStep * 0.5;
+ rcord.y = vStep * 2;
+ rcord.fixed = true;
+ }
+
const volt = _.find(nodes, {label: 'volt'});
if (volt) {
volt.x = hStep * 1;
@@ -90,6 +92,13 @@
vrouter.fixed = true;
}
+ const addressmanager = _.find(nodes, {label: 'addressmanager'});
+ if (addressmanager) {
+ addressmanager.x = hStep * 2.5;
+ addressmanager.y = vStep * 1.5;
+ addressmanager.fixed = true;
+ }
+
const oc = _.find(nodes, {label: 'ONOS_CORD'});
if (oc) {
oc.x = hStep + (hStep / 2);
@@ -97,117 +106,34 @@
oc.fixed = true;
}
+ const vtn = _.find(nodes, {label: 'vtn'});
+ if (vtn) {
+ vtn.x = hStep * 1.5;
+ vtn.y = vStep * 4;
+ vtn.fixed = true;
+ }
+
const of = _.find(nodes, {label: 'ONOS_Fabric'});
if (of) {
- of.x = (hStep * 2) + (hStep / 2);
+ of.x = hStep * 2.5;
of.y = vStep * 3;
of.fixed = true;
}
+ const fabric = _.find(nodes, {label: 'fabric'});
+ if (fabric) {
+ fabric.x = hStep * 2.5;
+ fabric.y = vStep * 4;
+ fabric.fixed = true;
+ }
+
+ const exampleservice = _.find(nodes, {label: 'exampleservice'});
+ if (exampleservice) {
+ exampleservice.x = hStep * 3.5;
+ exampleservice.y = vStep * 4.5;
+ exampleservice.fixed = true;
+ }
+
return nodes;
}
-
- private positionFineGrainedNodes(graph: any): any[] {
-
- let subscriberPosition = 0;
- let networkPosition = 0;
-
- const positionSubscriberNode = (node: any, hStep: number, vStep: number): any => {
- subscriberPosition = subscriberPosition + 1;
- node.x = hStep;
- node.y = vStep * (3 + subscriberPosition );
- node.fixed = true;
- return node;
- };
-
- const positionServiceNode = (node: any, hStep: number, vStep: number, vLength: number): any => {
- if (node.label === 'ONOS_Fabric') {
- node.x = hStep * 4;
- node.y = vStep;
- }
- if (node.label === 'volt' || node.label === 'vsg' || node.label === 'vrouter') {
- node.y = vStep * 3;
- }
- if (node.label === 'volt') {
- node.x = hStep * 2;
- }
- if (node.label === 'vsg') {
- node.x = hStep * 3;
- }
- if (node.label === 'vrouter') {
- node.x = hStep * 4;
- }
- if (node.label === 'ONOS_CORD' || node.label === 'vtr') {
- node.y = vStep * (vLength -1);
- }
- if (node.label === 'ONOS_CORD') {
- node.x = hStep * 2;
- }
- if (node.label === 'vtr') {
- node.x = hStep * 3;
- }
-
- node.fixed = true;
- return node;
- };
-
- const positionNetworkNode = (node: any, hStep: number, vStep: number): any => {
- networkPosition = networkPosition + 1;
- node.x = hStep * 5;
- node.y = vStep * (3 + networkPosition );
- node.fixed = true;
- return node;
- };
-
- const findSubscriberElementY = (nodes: any[], node: any): any => {
- if (node.model.subscriber_root_id) {
- console.log(node.model.subscriber_root_id);
- const subscriber = _.find(nodes, n => {
- return n.id === `tenantroot~${node.model.subscriber_root_id}`
- });
- debugger;
- // console.log(subscriber.y);
- return subscriber.y;
- }
- };
-
- const positionTenantNode = (nodes: any[], node: any, hStep: number, vStep: number): any => {
- if (node.model.kind === 'vOLT') {
- node.x = hStep * 2;
- }
- if (node.model.kind === 'vCPE') {
- node.x = hStep * 3;
- }
- if (node.model.kind === 'vROUTER') {
- node.x = hStep * 4;
- }
-
- return node;
- };
-
- let subscribers = _.filter(graph.nodes, n => n.type === 'subscriber' || n.type === 'tenantroot');
-
- const vLength = 5 + subscribers.length;
-
- const hStep = this.getSvgDimensions('xos-fine-grained-tenancy-graph').width / 6;
- const vStep = this.getSvgDimensions('xos-fine-grained-tenancy-graph').height / vLength;
-
- graph.nodes = _.map(graph.nodes, n => {
- if (n.type === 'subscriber' || n.type === 'tenantroot') {
- n = positionSubscriberNode(n, hStep, vStep);
- }
- if (n.type === 'service') {
- n = positionServiceNode(n, hStep, vStep, vLength);
- }
- if (n.type === 'network') {
- n = positionNetworkNode(n, hStep, vStep);
- }
- if (n.type === 'tenant') {
- n = positionTenantNode(graph.nodes, n, hStep, vStep);
- }
- // n.fixed = true;
- return n;
- });
- return graph;
- }
}
diff --git a/xos/gui/src/app/subscriber-dashboard/subscriber-dashboard.component.ts b/xos/gui/src/app/subscriber-dashboard/subscriber-dashboard.component.ts
new file mode 100644
index 0000000..fb98e4c
--- /dev/null
+++ b/xos/gui/src/app/subscriber-dashboard/subscriber-dashboard.component.ts
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+
+ * 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 * as _ from 'lodash';
+import './subscriber-dashboard.scss';
+
+class rcordSubscriberDashboardCtrl {
+
+ static $inject = [
+ 'toastr',
+ 'XosModelStore',
+ 'XosModeldefsCache'
+ ];
+
+ public levelOptions = [];
+ public subscribers = [];
+ public statusFieldOptions = [];
+ public slider = {
+ floor: 0,
+ ceil: 1000000000,
+ translate: (value) => {
+ return Math.floor(value / 1000000);
+ }
+ };
+
+ private subscriberDef;
+
+ constructor (
+ private toastr: any,
+ private XosModelStore: any,
+ private XosModeldefsCache: any
+ ) {
+
+ }
+
+ $onInit() {
+ this.XosModelStore.query('CordSubscriberRoot', '/rcord/cordsubscriberroots')
+ .subscribe(
+ res => {
+ this.subscribers = this.parseSubscribers(res);
+ }
+ );
+
+ this.levelOptions = [
+ `G`,
+ `PG`,
+ `PG_13`,
+ `R`,
+ `X`
+ ];
+
+ this.subscriberDef = this.XosModeldefsCache.get('CordSubscriberRoot');
+ this.statusFieldOptions = _.find(this.subscriberDef.fields, {name: 'status'}).options;
+ }
+
+ public addDevice(subscriber) {
+ subscriber.service_specific_attribute.devices.push({
+ name: '',
+ mac: '',
+ level: 'PG_13'
+ })
+ }
+
+ public removeDevice(subscriber, device) {
+ _.remove(subscriber.service_specific_attribute.devices, {name:device.name})
+ }
+
+ public save(subscriber) {
+ console.log(subscriber);
+ const item: any = angular.copy(subscriber);
+ item.service_specific_attribute = JSON.stringify(item.service_specific_attribute);
+ item.$save()
+ .then(() => {
+ this.toastr.success(`Subscriber successfully saved`);
+ });
+ }
+
+ private parseSubscribers(subscribers) {
+ return _.map(subscribers, (s) => {
+ if (angular.isString(s.service_specific_attribute)) {
+ s.service_specific_attribute = JSON.parse(s.service_specific_attribute);
+ }
+ return s;
+ })
+ }
+}
+
+export const rcordSubscriberDashboard: angular.IComponentOptions = {
+ template: require('./subscriber-dashboard.html'),
+ controllerAs: 'vm',
+ controller: rcordSubscriberDashboardCtrl
+};
\ No newline at end of file
diff --git a/xos/gui/src/app/subscriber-dashboard/subscriber-dashboard.html b/xos/gui/src/app/subscriber-dashboard/subscriber-dashboard.html
new file mode 100644
index 0000000..0b49316
--- /dev/null
+++ b/xos/gui/src/app/subscriber-dashboard/subscriber-dashboard.html
@@ -0,0 +1,106 @@
+<!--
+Copyright 2017-present Open Networking Foundation
+
+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.
+-->
+
+<div class="row" ng-if="!vm.selectedSubscriber.status">
+ <div class="col-xs-12">
+ <h2>Select Subscriber:</h2>
+ <input
+ type="text"
+ ng-model="vm.selectedSubscriber"
+ uib-typeahead="s as s.name for s in vm.subscribers | filter:$viewValue | limitTo:8"
+ class="form-control">
+ </div>
+</div>
+
+<div class="row" ng-if="vm.selectedSubscriber.status">
+ <div class="col-xs-12">
+ <form>
+ <div class="form-group">
+ <h2>Status:</h2>
+ </div>
+ <div class="form-group">
+ <div class="row">
+ <div class="col-xs-3" ng-repeat="s in vm.statusFieldOptions">
+ <a
+ class="btn btn-default btn-block"
+ ng-click="vm.selectedSubscriber.status = s.id"
+ ng-class="{'active': s.id === vm.selectedSubscriber.status}">
+ {{s.label}}
+ </a>
+ </div>
+ </div>
+ </div>
+ <div class="form-group">
+ <h2>Bandwith:</h2>
+ </div>
+ <div class="form-group">
+ <div class="row">
+ <div class="col-xs-6">
+ <label>Uplink speed:</label>
+ <rzslider
+ rz-slider-model="vm.selectedSubscriber.uplink_speed"
+ rz-slider-options="vm.slider"></rzslider>
+ </div>
+ <div class="col-xs-6">
+ <label>Downlink speed:</label>
+ <rzslider
+ rz-slider-model="vm.selectedSubscriber.downlink_speed"
+ rz-slider-options="vm.slider"></rzslider>
+ </div>
+ </div>
+ </div>
+ <div class="form-group">
+ <h2>
+ Devices:
+ <a ng-click="vm.addDevice(vm.selectedSubscriber)">
+ <small class="label label-default">
+ <i class="fa fa-plus"></i>
+ </small>
+ </a>
+ </h2>
+ </div>
+ <div class="row">
+ <div class="col-md-3 col-sm-6" ng-repeat="d in vm.selectedSubscriber.service_specific_attribute.devices">
+ <div class="panel panel-filled">
+ <div class="panel-body">
+ <div class="form-group">
+ <a ng-click="vm.removeDevice(vm.selectedSubscriber, d)">
+ <i class="fa fa-remove pull-right"></i>
+ </a>
+ </div>
+ <div class="form-group">
+ <label>Name:</label>
+ <input class="form-control" type="text" ng-model="d.name"/>
+ </div>
+ <div class="form-group">
+ <label>MAC Address:</label>
+ <input class="form-control" type="text" ng-model="d.mac"/>
+ </div>
+ <div class="form-group">
+ <label>Filter level:</label>
+ <select class="form-control" ng-model="d.level" ng-options="l for l in vm.levelOptions"></select>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="form-group">
+ <button class="btn btn-success" ng-click="vm.save(vm.selectedSubscriber)">Save</button>
+ <button class="btn btn-info" ng-click="vm.selectedSubscriber = null">Reset</button>
+ </div>
+ </form>
+ </div>
+</div>
\ No newline at end of file
diff --git a/xos/gui/src/app/subscriber-dashboard/subscriber-dashboard.scss b/xos/gui/src/app/subscriber-dashboard/subscriber-dashboard.scss
new file mode 100644
index 0000000..e65add3
--- /dev/null
+++ b/xos/gui/src/app/subscriber-dashboard/subscriber-dashboard.scss
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+
+ * 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.
+ */
+
+.rzslider .rz-pointer {
+ background-color: #f6a821;
+ width: 20px;
+ height: 20px;
+ top: -8px;
+
+ &:after {
+ top: 6px;
+ left: 6px;
+ }
+}
\ No newline at end of file
diff --git a/xos/gui/src/index.html b/xos/gui/src/index.html
index 57bd4f5..fb32573 100644
--- a/xos/gui/src/index.html
+++ b/xos/gui/src/index.html
@@ -21,15 +21,16 @@
<head>
<meta charset="UTF-8">
<title>Document</title>
- <link href="/spa/loader.css" rel="stylesheet">
- <link href="/spa/app.css" rel="stylesheet">
+ <link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous">
+ <link href="/xos/loader.css" rel="stylesheet">
+ <link href="/xos/app.css" rel="stylesheet">
</head>
<body>
<div ui-view></div>
- <script src="/spa/vendor.js"></script>
- <script src="/spa/app.js"></script>
- <script src="/spa/loader.js"></script>
- <script src="/spa/app.config.js"></script>
- <script src="/spa/style.config.js"></script>
+ <script src="/xos/vendor.js"></script>
+ <script src="/xos/app.js"></script>
+ <script src="/xos/loader.js"></script>
+ <script src="/xos/app.config.js"></script>
+ <script src="/xos/style.config.js"></script>
</body>
</html>
\ No newline at end of file
diff --git a/xos/gui/src/index.ts b/xos/gui/src/index.ts
index 85d3a34..511a69a 100644
--- a/xos/gui/src/index.ts
+++ b/xos/gui/src/index.ts
@@ -22,14 +22,41 @@
import 'angular-ui-router';
import 'angular-resource';
import 'angular-cookies';
+import 'angularjs-slider';
+import '../node_modules/angularjs-slider/dist/rzslider.scss';
+
import {RCordGraphReducer, IRCordGraphReducer} from './app/services/graph.extension';
+import {rcordSubscriberDashboard} from './app/subscriber-dashboard/subscriber-dashboard.component';
angular.module('xos-rcord-gui-extension', [
'ui.router',
- 'app'
+ 'app',
+ 'rzModule'
])
.service('RCordGraphReducer', RCordGraphReducer)
- .run(function($log: ng.ILogService, RCordGraphReducer: IRCordGraphReducer) {
+ .component('rcordSubscriberDashboard', rcordSubscriberDashboard)
+ .run(function($log: ng.ILogService, RCordGraphReducer: IRCordGraphReducer, XosNavigationService: any, XosRuntimeStates: any, $state: ng.ui.IStateService) {
$log.info('[xos-rcord-graph-gui-extension] App is running');
RCordGraphReducer.setup();
+
+ XosRuntimeStates.addState(`xos.rcord`, {
+ url: 'rcord',
+ parent: 'xos',
+ abstract: true,
+ template: '<div ui-view></div>'
+ });
+
+ XosRuntimeStates.addState(`xos.rcord.dashboard`, {
+ url: '/dashboard',
+ parent: 'xos.rcord',
+ component: 'rcordSubscriberDashboard'
+ });
+
+ window.setTimeout(() => {
+ XosNavigationService.add({
+ label: 'Dashboard',
+ state: 'xos.rcord.dashboard',
+ parent: 'xos.rcord'
+ });
+ }, 5000);
});