Adding skeletal structure for the ONLP gNMI GUI demo.
Change-Id: I6796ebf200e20a51bdc098fcc3696b78d7c1132e
diff --git a/apps/onlp-demo/BUILD b/apps/onlp-demo/BUILD
new file mode 100644
index 0000000..0bf3560
--- /dev/null
+++ b/apps/onlp-demo/BUILD
@@ -0,0 +1,25 @@
+COMPILE_DEPS = CORE_DEPS + JACKSON + [
+ "@com_google_protobuf//:protobuf_java",
+ "@io_grpc_grpc_java//core",
+ "@io_grpc_grpc_java//netty",
+ "@io_grpc_grpc_java//stub",
+ "//core/store/serializers:onos-core-serializers",
+ "//protocols/gnmi/stub:onos-protocols-gnmi-stub",
+ "//protocols/gnmi/api:onos-protocols-gnmi-api",
+ "//protocols/grpc/api:onos-protocols-grpc-api",
+ "//protocols/grpc/proto:onos-protocols-grpc-proto",
+]
+
+osgi_jar_with_tests(
+ deps = COMPILE_DEPS,
+)
+
+onos_app(
+ category = "GUI",
+ description = "Provides a GUI overlay for displaying ONLP device management information.",
+ required_apps = [
+ "org.onosproject.protocols.gnmi",
+ ],
+ title = "ONLP device demo",
+ url = "http://onosproject.org",
+)
diff --git a/apps/onlp-demo/src/main/java/org/onosproject/onlpdemo/OnlpDemoManager.java b/apps/onlp-demo/src/main/java/org/onosproject/onlpdemo/OnlpDemoManager.java
new file mode 100644
index 0000000..6085bbc
--- /dev/null
+++ b/apps/onlp-demo/src/main/java/org/onosproject/onlpdemo/OnlpDemoManager.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright 2018-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.
+ */
+
+package org.onosproject.onlpdemo;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Maps;
+import com.google.common.util.concurrent.Futures;
+import gnmi.Gnmi;
+import org.onlab.util.SharedExecutors;
+import org.onosproject.gnmi.api.GnmiClient;
+import org.onosproject.gnmi.api.GnmiController;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Port;
+import org.onosproject.net.config.NetworkConfigService;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.ui.UiExtension;
+import org.onosproject.ui.UiExtensionService;
+import org.onosproject.ui.UiMessageHandlerFactory;
+import org.onosproject.ui.UiTopoOverlay;
+import org.onosproject.ui.UiTopoOverlayFactory;
+import org.onosproject.ui.UiView;
+import org.onosproject.ui.UiViewHidden;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+import java.util.Map;
+import java.util.TimerTask;
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * Extends the ONOS GUI to display various ONLP device data.
+ */
+@Component(immediate = true, service = OnlpDemoManager.class)
+public class OnlpDemoManager {
+
+ private Logger log = LoggerFactory.getLogger(getClass());
+
+ private static final String EXTENSION_ID = "onlpdemo";
+ private static final String OVERLAY_ID = "od-overlay";
+
+ private static final String OVERLAY_VIEW_ID = "odTopov";
+ private static final String TABLE_VIEW_ID = "onlp";
+
+ // List of application views
+ private final List<UiView> uiViews = ImmutableList.of(
+ new UiViewHidden(OVERLAY_VIEW_ID),
+ new UiViewHidden(TABLE_VIEW_ID)
+ );
+
+ // Factory for UI message handlers
+ private final UiMessageHandlerFactory messageHandlerFactory =
+ () -> ImmutableList.of(new OnlpDemoViewMessageHandler(new GnmiOnlpDataSource()));
+
+ // Factory for UI topology overlays
+ private final UiTopoOverlayFactory topoOverlayFactory =
+ () -> ImmutableList.of(new UiTopoOverlay(OVERLAY_ID));
+
+ // Application UI extension
+ protected UiExtension extension =
+ new UiExtension.Builder(getClass().getClassLoader(), uiViews)
+ .resourcePath(EXTENSION_ID)
+ .messageHandlerFactory(messageHandlerFactory)
+ .topoOverlayFactory(topoOverlayFactory)
+ .build();
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected NetworkConfigService networkConfigService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected DeviceService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected UiExtensionService uiExtensionService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected GnmiController gnmiController;
+
+ @Activate
+ protected void activate() {
+ uiExtensionService.register(extension);
+ log.info("Started");
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ uiExtensionService.unregister(extension);
+ log.info("Stopped");
+ }
+
+
+ public class OnlpData {
+ String id;
+ String presence;
+ String vendor;
+ String modelNumber;
+ String serialNumber;
+ String formFactor;
+
+ OnlpData(String id, String presence, String vendor, String modelNumber,
+ String serialNumber, String formFactor) {
+ this.id = id;
+ this.presence = presence;
+ this.vendor = vendor;
+ this.modelNumber = modelNumber;
+ this.serialNumber = serialNumber;
+ this.formFactor = formFactor;
+ }
+ }
+
+ public interface OnlpDataSource {
+ List<OnlpData> getData(DeviceId deviceId);
+ }
+
+
+ public class GnmiOnlpDataSource implements OnlpDataSource {
+
+ private Map<DeviceId, List<OnlpData>> cache = Maps.newConcurrentMap();
+
+ GnmiOnlpDataSource() {
+ SharedExecutors.getTimer().schedule(new TimerTask() {
+ @Override
+ public void run() {
+ cache.keySet().forEach(GnmiOnlpDataSource.this::fetchData);
+ }
+ }, 5000, 5000);
+ }
+
+ @Override
+ public List<OnlpData> getData(DeviceId deviceId) {
+ return cache.computeIfAbsent(deviceId, k -> ImmutableList.of());
+ }
+
+ private void fetchData(DeviceId deviceId) {
+ ImmutableList.Builder<OnlpData> builder = ImmutableList.builder();
+ GnmiClient gnmiClient = gnmiController.getClient(deviceId);
+ deviceService.getPorts(deviceId)
+ .forEach(port -> builder.add(getOnlpData(gnmiClient, port)));
+ cache.put(deviceId, builder.build());
+ }
+
+ private OnlpData getOnlpData(GnmiClient gnmiClient, Port port) {
+ CompletableFuture<Gnmi.GetResponse> prReq = gnmiClient.get(fieldRequest(port, "present"));
+ CompletableFuture<Gnmi.GetResponse> veReq = gnmiClient.get(fieldRequest(port, "vendor"));
+ CompletableFuture<Gnmi.GetResponse> snReq = gnmiClient.get(fieldRequest(port, "serial-no"));
+ CompletableFuture<Gnmi.GetResponse> vpReq = gnmiClient.get(fieldRequest(port, "vendor-part"));
+ CompletableFuture<Gnmi.GetResponse> ffReq = gnmiClient.get(fieldRequest(port, "form-factor"));
+
+ return new OnlpData("sfp-" + port.number().name().replaceFirst("/[0-9]", ""),
+ value(prReq).equals("PRESENT") ? "*" : "",
+ value(veReq), value(vpReq), value(snReq), value(ffReq));
+ }
+
+ private String value(CompletableFuture<Gnmi.GetResponse> req) {
+ Gnmi.GetResponse response = Futures.getUnchecked(req);
+ return response.getNotificationList().isEmpty() ?
+ "" : response.getNotification(0).getUpdate(0).getVal().getStringVal().trim();
+ }
+
+ private Gnmi.GetRequest fieldRequest(Port port, String field) {
+ Gnmi.Path path = Gnmi.Path.newBuilder()
+ .addElem(Gnmi.PathElem.newBuilder().setName("components").build())
+ .addElem(Gnmi.PathElem.newBuilder().setName("component").putKey("name",
+ port.number().name()).build())
+ .addElem(Gnmi.PathElem.newBuilder().setName("transceiver").build())
+ .addElem(Gnmi.PathElem.newBuilder().setName("state").build())
+ .addElem(Gnmi.PathElem.newBuilder().setName(field).build())
+ .build();
+ return Gnmi.GetRequest.newBuilder()
+ .addPath(path)
+ .setType(Gnmi.GetRequest.DataType.ALL)
+ .setEncoding(Gnmi.Encoding.PROTO)
+ .build();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/apps/onlp-demo/src/main/java/org/onosproject/onlpdemo/OnlpDemoViewMessageHandler.java b/apps/onlp-demo/src/main/java/org/onosproject/onlpdemo/OnlpDemoViewMessageHandler.java
new file mode 100644
index 0000000..3a37481
--- /dev/null
+++ b/apps/onlp-demo/src/main/java/org/onosproject/onlpdemo/OnlpDemoViewMessageHandler.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2019-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.
+ */
+
+package org.onosproject.onlpdemo;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableSet;
+import org.onosproject.net.DeviceId;
+import org.onosproject.onlpdemo.OnlpDemoManager.OnlpData;
+import org.onosproject.onlpdemo.OnlpDemoManager.OnlpDataSource;
+import org.onosproject.ui.RequestHandler;
+import org.onosproject.ui.UiMessageHandler;
+import org.onosproject.ui.table.TableModel;
+import org.onosproject.ui.table.TableRequestHandler;
+import org.onosproject.ui.table.cell.AbstractCellComparator;
+
+import java.util.Collection;
+
+
+/**
+ * Message handler for ONLP view related messages.
+ */
+public class OnlpDemoViewMessageHandler extends UiMessageHandler {
+
+ private static final String ONLP_DATA_REQ = "onlpDataRequest";
+ private static final String ONLP_DATA_RESP = "onlpDataResponse";
+ private static final String ONLPS = "onlps";
+
+ private static final String ID = "id";
+ private static final String PRESENCE = "presence";
+ private static final String VENDOR = "vendor";
+ private static final String SERIAL_NO = "serial_number";
+ private static final String MODEL_NO = "model_number";
+ private static final String FORM_FACTOR = "form_factor";
+
+
+ private static final String[] COL_IDS = {
+ ID, PRESENCE, VENDOR, MODEL_NO, SERIAL_NO, FORM_FACTOR,
+ };
+
+ private final OnlpDataSource onlpDataSource;
+
+ public OnlpDemoViewMessageHandler(OnlpDataSource onlpDataSource) {
+ super();
+ this.onlpDataSource = onlpDataSource;
+ }
+
+ @Override
+ protected Collection<RequestHandler> createRequestHandlers() {
+ return ImmutableSet.of(
+ new OnlpDataRequest()
+ );
+ }
+
+ // handler for port table requests
+ private final class OnlpDataRequest extends TableRequestHandler {
+
+ private static final String NO_ROWS_MESSAGE = "No data available yet";
+
+ private OnlpDataRequest() {
+ super(ONLP_DATA_REQ, ONLP_DATA_RESP, ONLPS);
+ }
+
+ @Override
+ protected String[] getColumnIds() {
+ return COL_IDS;
+ }
+
+ @Override
+ protected String noRowsMessage(ObjectNode payload) {
+ return NO_ROWS_MESSAGE;
+ }
+
+ @Override
+ protected TableModel createTableModel() {
+ TableModel tm = super.createTableModel();
+ tm.setComparator(ID, new SfpIdCellComparator());
+ return tm;
+ }
+
+ @Override
+ protected void populateTable(TableModel tm, ObjectNode payload) {
+ String uri = string(payload, "devId");
+ if (!Strings.isNullOrEmpty(uri)) {
+ for (OnlpData data : onlpDataSource.getData(DeviceId.deviceId(uri))) {
+ populateRow(tm.addRow(), data);
+ }
+ }
+ }
+
+ private void populateRow(TableModel.Row row, OnlpData data) {
+ row.cell(ID, data.id)
+ .cell(PRESENCE, data.presence)
+ .cell(VENDOR, data.vendor)
+ .cell(MODEL_NO, data.modelNumber)
+ .cell(SERIAL_NO, data.serialNumber)
+ .cell(FORM_FACTOR, data.formFactor);
+ }
+ }
+
+ private static class SfpIdCellComparator extends AbstractCellComparator {
+ @Override
+ protected int nonNullCompare(Object o1, Object o2) {
+ return index((String) o2) - index((String) o1);
+ }
+
+ private int index(String s) {
+ int i = s.indexOf("-");
+ try {
+ return i > 0 ? Integer.parseInt(s.substring(i)) : 0;
+ } catch (NumberFormatException e) {
+ return Integer.MAX_VALUE;
+ }
+ }
+ }
+
+}
diff --git a/apps/onlp-demo/src/main/java/org/onosproject/onlpdemo/package-info.java b/apps/onlp-demo/src/main/java/org/onosproject/onlpdemo/package-info.java
new file mode 100644
index 0000000..c049790
--- /dev/null
+++ b/apps/onlp-demo/src/main/java/org/onosproject/onlpdemo/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 20199-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.
+ */
+
+/**
+ * Demo GUI for displaying ONLP device data obtained via gNMI.
+ */
+package org.onosproject.onlpdemo;
\ No newline at end of file
diff --git a/apps/onlp-demo/src/main/resources/app/view/odTopov/odTopov.css b/apps/onlp-demo/src/main/resources/app/view/odTopov/odTopov.css
new file mode 100644
index 0000000..8aa18f6
--- /dev/null
+++ b/apps/onlp-demo/src/main/resources/app/view/odTopov/odTopov.css
@@ -0,0 +1,2 @@
+/* css for layout topology overlay */
+
diff --git a/apps/onlp-demo/src/main/resources/app/view/odTopov/odTopov.html b/apps/onlp-demo/src/main/resources/app/view/odTopov/odTopov.html
new file mode 100644
index 0000000..377b3b0
--- /dev/null
+++ b/apps/onlp-demo/src/main/resources/app/view/odTopov/odTopov.html
@@ -0,0 +1,4 @@
+<!-- partial HTML -->
+<div id="ov-od-topov">
+ <p>This is a hidden view .. just a placeholder to house the javascript</p>
+</div>
diff --git a/apps/onlp-demo/src/main/resources/app/view/odTopov/odTopov.js b/apps/onlp-demo/src/main/resources/app/view/odTopov/odTopov.js
new file mode 100644
index 0000000..d78c009
--- /dev/null
+++ b/apps/onlp-demo/src/main/resources/app/view/odTopov/odTopov.js
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2015-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.
+ */
+
+/*
+ Module containing the "business logic" for the layout topology overlay.
+ */
+
+(function () {
+ 'use strict';
+
+ // injected refs
+ var $log, flash, wss;
+
+ function doFoo(type, description) {
+ flash.flash(description);
+ wss.sendEvent('doFoo', {
+ type: type
+ });
+ }
+
+ function clear() {
+ // Nothing to do?
+ }
+
+ angular.module('ovOdTopov', [])
+ .factory('OnlpDemoTopovService',
+ ['$log', 'FlashService', 'WebSocketService',
+
+ function (_$log_, _flash_, _wss_) {
+ $log = _$log_;
+ flash = _flash_;
+ wss = _wss_;
+
+ return {
+ doFoo: doFoo,
+ clear: clear
+ };
+ }]);
+}());
diff --git a/apps/onlp-demo/src/main/resources/app/view/odTopov/odTopovOverlay.js b/apps/onlp-demo/src/main/resources/app/view/odTopov/odTopovOverlay.js
new file mode 100644
index 0000000..682ea17
--- /dev/null
+++ b/apps/onlp-demo/src/main/resources/app/view/odTopov/odTopovOverlay.js
@@ -0,0 +1,59 @@
+// path painter topology overlay - client side
+//
+// This is the glue that binds our business logic (in ppTopov.js)
+// to the overlay framework.
+
+(function () {
+ 'use strict';
+
+ // injected refs
+ var $log, tov, ns, lts, sel;
+
+ // our overlay definition
+ var overlay = {
+ overlayId: 'od-overlay',
+ glyphId: 'm_disjointPaths',
+ tooltip: 'ONLP Data Overlay',
+
+ activate: function () {
+ $log.debug("ONLP data topology overlay ACTIVATED");
+ },
+ deactivate: function () {
+ lts.clear();
+ $log.debug("ONLP data topology overlay DEACTIVATED");
+ },
+
+ // detail panel button definitions
+ buttons: {
+ showOnlpView: {
+ gid: 'chain',
+ tt: 'ONLP data',
+ cb: function (data) {
+ $log.debug('ONLP action invoked on selection:', sel);
+ ns.navTo('onlp', { devId: sel.id });
+ }
+ }
+ },
+
+ hooks: {
+ // hooks for when the selection changes...
+ single: function (data) {
+ $log.debug('selection data:', data);
+ sel = data;
+ tov.addDetailButton('showOnlpView');
+ }
+ }
+ };
+
+ // invoke code to register with the overlay service
+ angular.module('ovOdTopov')
+ .run(['$log', 'TopoOverlayService', 'NavService', 'OnlpDemoTopovService',
+
+ function (_$log_, _tov_, _ns_, _lts_) {
+ $log = _$log_;
+ tov = _tov_;
+ ns = _ns_;
+ lts = _lts_;
+ tov.register(overlay);
+ }]);
+}());
diff --git a/apps/onlp-demo/src/main/resources/app/view/onlp/onlp.css b/apps/onlp-demo/src/main/resources/app/view/onlp/onlp.css
new file mode 100644
index 0000000..60e1c58
--- /dev/null
+++ b/apps/onlp-demo/src/main/resources/app/view/onlp/onlp.css
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2015-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.
+ */
+
+/*
+ ONOS GUI -- Port View (layout) -- CSS file
+ */
+
+#ov-onlp h2 {
+ display: inline-block;
+}
+
+#ov-onlp div.ctrl-btns {
+}
+
+#ov-onlp td {
+ text-align: center;
+}
+
+#ov-onlp td.left {
+ text-align: left;
+}
+
+#ov-onlp tr.left {
+ text-align: left;
+}
+
+#ov-onlp tr.no-data td {
+ text-align: center;
+}
diff --git a/apps/onlp-demo/src/main/resources/app/view/onlp/onlp.html b/apps/onlp-demo/src/main/resources/app/view/onlp/onlp.html
new file mode 100644
index 0000000..6972f40
--- /dev/null
+++ b/apps/onlp-demo/src/main/resources/app/view/onlp/onlp.html
@@ -0,0 +1,91 @@
+<!-- Port partial HTML -->
+<div id="ov-onlp">
+ <div class="tabular-header">
+ <h2>ONLP Data for {{devId || "(No device selected)"}}</h2>
+
+ <div class="ctrl-btns">
+ <div class="refresh" ng-class="{active: autoRefresh}"
+ icon icon-size="42" icon-id="refresh"
+ tooltip tt-msg="autoRefreshTip"
+ ng-click="toggleRefresh()"></div>
+
+ <div class="separator"></div>
+
+ <div class="refresh" ng-class="{active: isNZ()}"
+ icon icon-size="42" icon-id="nonzero"
+ tooltip tt-msg="toggleNZTip"
+ ng-click="toggleNZ()"></div>
+
+ <div class="refresh" ng-class="{active: isDelta()}"
+ icon icon-size="42" icon-id="delta"
+ tooltip tt-msg="toggleDeltaTip"
+ ng-click="toggleDelta()"></div>
+
+ <div class="separator"></div>
+
+ <div class="active"
+ icon icon-id="deviceTable" icon-size="42"
+ tooltip tt-msg="deviceTip"
+ ng-click="nav('device')"></div>
+
+ <div class="active"
+ icon icon-id="flowTable" icon-size="42"
+ tooltip tt-msg="flowTip"
+ ng-click="nav('flow')"></div>
+
+ <div class="current-view"
+ icon icon-id="portTable" icon-size="42"></div>
+
+ <div class="active"
+ icon icon-id="groupTable" icon-size="42"
+ tooltip tt-msg="groupTip"
+ ng-click="nav('group')"></div>
+
+ <div class="active"
+ icon icon-id="meterTable" icon-size="42"
+ tooltip tt-msg="meterTip"
+ ng-click="nav('meter')"></div>
+
+ <div class="active"
+ icon icon-id="pipeconfTable" icon-size="42"
+ tooltip tt-msg="pipeconfTip"
+ ng-click="nav('pipeconf')"></div>
+ </div>
+ </div>
+
+
+ <div class="summary-list" onos-table-resize>
+ <div class="table-header" onos-sortable-header>
+ <table>
+ <tr>
+ <td colId="id" col-width="60px" sortable>SFP ID </td>
+ <td class="left" colId="id" col-width="30px" sortable> </td>
+ <td class="left" colId="vendor" sortable>Vendor </td>
+ <td class="left" colId="model_number" sortable>Model # </td>
+ <td class="left" colId="serial_number" sortable>Serial # </td>
+ <td class="left" colId="form_factor" sortable>Form Factor </td>
+ </tr>
+ </table>
+ </div>
+
+ <div class="table-body">
+ <table id-prop="id">
+ <tr ng-if="!tableData.length" class="no-data">
+ <td colspan="6">{{annots.no_rows_msg}}</td>
+ </tr>
+
+ <tr ng-repeat="onlp in tableData"
+ ng-class="{selected: onlp.id === selId}"
+ ng-repeat-complete row-id="{{onlp.id}}">
+ <td>{{onlp.id}}</td>
+ <td class="left">{{onlp.presence}}</td>
+ <td class="left">{{onlp.vendor}}</td>
+ <td class="left">{{onlp.model_number}}</td>
+ <td class="left">{{onlp.serial_number}}</td>
+ <td class="left">{{onlp.form_factor}}</td>
+ </tr>
+ </table>
+ </div>
+ </div>
+
+</div>
diff --git a/apps/onlp-demo/src/main/resources/app/view/onlp/onlp.js b/apps/onlp-demo/src/main/resources/app/view/onlp/onlp.js
new file mode 100644
index 0000000..81625d2
--- /dev/null
+++ b/apps/onlp-demo/src/main/resources/app/view/onlp/onlp.js
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2015-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.
+ */
+
+/*
+ ONOS GUI -- Onlp View Module
+ */
+
+(function () {
+ 'use strict';
+
+ // injected references
+ var $log, $scope, $location, tbs, fs, mast, wss, ns, prefs, is, ps;
+
+ // internal state
+ var nzFilter = true,
+ showDelta = false,
+ pStartY,
+ pHeight,
+ wSize;
+
+ var keyBindings = {
+ };
+
+ var defaultOnlpPrefsState = {
+ };
+
+ var prefsState = {};
+
+ function updatePrefsState(what, b) {
+ prefsState[what] = b ? 1 : 0;
+ prefs.setPrefs('onlp_prefs', prefsState);
+ }
+
+ function restoreConfigFromPrefs() {
+ prefsState = prefs.asNumbers(
+ prefs.getPrefs('onlp_prefs', defaultOnlpPrefsState)
+ );
+
+ $log.debug('ONLP - Prefs State:', prefsState);
+ }
+
+ angular.module('ovOnlp', [])
+ .controller('OvOnlpCtrl', [
+ '$log', '$scope', '$location',
+ 'TableBuilderService', 'FnService', 'MastService', 'WebSocketService',
+ 'NavService', 'PrefsService', 'IconService',
+ 'PanelService',
+
+ function (_$log_, _$scope_, _$location_,
+ _tbs_, _fs_, _mast_, _wss_,
+ _ns_, _prefs_, _is_, _ps_) {
+ var params;
+ var tableApi;
+ $log = _$log_;
+ $scope = _$scope_;
+ $location = _$location_;
+ tbs = _tbs_;
+ fs = _fs_;
+ mast = _mast_;
+ wss = _wss_;
+ ns = _ns_;
+ prefs = _prefs_;
+ is = _is_;
+ ps = _ps_;
+
+ params = $location.search();
+
+ $scope.deviceTip = 'Show device table';
+ $scope.flowTip = 'Show flow view for this device';
+ $scope.groupTip = 'Show group view for this device';
+ $scope.meterTip = 'Show meter view for selected device';
+ $scope.pipeconfTip = 'Show pipeconf view for selected device';
+
+ if (params.hasOwnProperty('devId')) {
+ $scope.devId = params['devId'];
+ }
+
+ $scope.payloadParams = {
+ nzFilter: nzFilter,
+ showDelta: showDelta,
+ };
+
+ tableApi = tbs.buildTable({
+ scope: $scope,
+ tag: 'onlp',
+ query: params,
+ });
+
+ function filterToggleState() {
+ return {
+ nzFilter: nzFilter,
+ showDelta: showDelta,
+ };
+ }
+
+ $scope.nav = function (path) {
+ if ($scope.devId) {
+ ns.navTo(path, { devId: $scope.devId });
+ }
+ };
+
+ function getOperatorFromQuery(query) {
+
+ var operator = query.split(' '),
+ opFunc = null;
+
+ if (operator[0] === '>') {
+ opFunc = _.gt;
+ } else if (operator[0] === '>=') {
+ opFunc = _.gte;
+ } else if (operator[0] === '<') {
+ opFunc = _.lt;
+ } else if (operator[0] === '<=') {
+ opFunc = _.lte;
+ } else {
+ return {
+ operator: opFunc,
+ searchText: query,
+ };
+ }
+
+ return {
+ operator: opFunc,
+ searchText: operator[1],
+ };
+ }
+
+ $scope.customFilter = function (prop, val) {
+ if (!val) {
+ return;
+ }
+
+ var search = getOperatorFromQuery(val),
+ operator = search.operator,
+ searchText = search.searchText;
+
+ if (operator) {
+ return function (row) {
+ var queryBy = $scope.queryBy || '$';
+
+ if (queryBy !== '$') {
+ var rowValue = parseInt(row[$scope.queryBy].replace(/,/g, ''));
+ return operator(rowValue, parseInt(searchText)) ? row : null;
+ } else {
+ var keys = _.keysIn(row);
+
+ for (var i = 0, l = keys.length; i < l; i++) {
+ var rowValue = parseInt(row[keys[i]].replace(/,/g, ''));
+ if (operator(rowValue, parseInt(searchText))) {
+ return row;
+ }
+ }
+ }
+ };
+ } else {
+ var out = {};
+ out[$scope.queryBy || '$'] = $scope.query;
+ return out;
+ }
+ };
+
+ restoreConfigFromPrefs();
+
+ $scope.$on('$destroy', function () {
+ dps.destroy();
+ });
+
+ $log.log('OvOnlpCtrl has been created');
+ }]);
+}());
diff --git a/apps/onlp-demo/src/main/resources/onlpdemo/css.html b/apps/onlp-demo/src/main/resources/onlpdemo/css.html
new file mode 100644
index 0000000..ace7ac3
--- /dev/null
+++ b/apps/onlp-demo/src/main/resources/onlpdemo/css.html
@@ -0,0 +1,18 @@
+<!--
+ ~ Copyright 2019-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.
+ -->
+
+<link rel="stylesheet" href="app/view/odTopov/odTopov.css">
+<link rel="stylesheet" href="app/view/onlp/onlp.css">
\ No newline at end of file
diff --git a/apps/onlp-demo/src/main/resources/onlpdemo/js.html b/apps/onlp-demo/src/main/resources/onlpdemo/js.html
new file mode 100644
index 0000000..6a45bad
--- /dev/null
+++ b/apps/onlp-demo/src/main/resources/onlpdemo/js.html
@@ -0,0 +1,19 @@
+<!--
+ ~ Copyright 2019-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.
+ -->
+
+<script src="app/view/onlp/onlp.js"></script>
+<script src="app/view/odTopov/odTopov.js"></script>
+<script src="app/view/odTopov/odTopovOverlay.js"></script>