ONOS-628 - Download and extract node binaries for the GUI build step
- Added gulp build task the:
- Bundles Vendor JS files
- Bundles ONOS JS Files
- Bundles ONOS CSS Files
- Added SourceMaps to JS bundles - Helps with debugging during development
- Added Bundles to index.js and removed old references
- Git Ignored any generated files
- Ensured the build step is able to build without a local copy of node installed
- Added BUCK genrules (provided by Viswa)
- Added BUCK Dependency to GUI
- Buck Rule to run when src changes
- Node/NPM downloaded using BUCK remote_file
Change-Id: Ia6ca3b952ff801850ade7469c23aac76c8520400
diff --git a/.gitignore b/.gitignore
index f45ab60..307c56c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,10 +21,14 @@
/bin/
lib/yang
+/tools/gui/bin
+/tools/gui/node_modules
+/tools/gui/package-lock.json
web/gui/src/main/webapp/tests/node_modules
web/gui/src/test/_karma/node_modules
web/gui/src/main/webapp/node_modules/
+web/gui/src/main/webapp/dist/
npm-debug.log
sonar-project.properties
diff --git a/bucklets/node.bucklet b/bucklets/node.bucklet
new file mode 100644
index 0000000..3239e46
--- /dev/null
+++ b/bucklets/node.bucklet
@@ -0,0 +1,34 @@
+NODE_VERSION = "v8.1.2"
+NODE_RELEASE_BASE_URL = "https://nodejs.org/dist/"
+
+NODE_SHA1S = {
+ "node-v8.1.2-linux-x64.tar.gz":"61a609c83e2d3458cc2301a63b212a97e6b9f809",
+ "node-v8.1.2-darwin-x64.tar.gz":"a8b31fd645480661a8a777d9b4466dca0e6deb33"
+}
+
+def get_system_arch():
+ import platform
+ os = platform.system().lower()
+ return os
+
+def fetch_node(version):
+ file_name = "node-%s-%s-x64" % (version, get_system_arch())
+ file_fullname = "node-%s-%s-x64.tar.gz" % (version, get_system_arch())
+ if file_fullname not in NODE_SHA1S:
+ raise Exception("Cannot download %s, architecture or version not supported" % file_name)
+
+ remote_file(
+ name = 'node-release-' + version,
+ url = NODE_RELEASE_BASE_URL + version + '/' + file_fullname,
+ sha1 = NODE_SHA1S[file_fullname],
+ )
+
+ genrule(
+ name = 'node-bin-' + version,
+ bash = 'tar -xf $(location :node-release-' + version + ') && ' +
+ 'mv ' + file_name + ' $OUT && ' +
+ 'chmod +x $OUT',
+ out = 'node-binaries',
+ executable = False,
+ visibility = [ "PUBLIC" ],
+ )
diff --git a/tools/gui/gulp-tasks/bundles/bundle-css/index.js b/tools/gui/gulp-tasks/bundles/bundle-css/index.js
new file mode 100644
index 0000000..0c5c8d6
--- /dev/null
+++ b/tools/gui/gulp-tasks/bundles/bundle-css/index.js
@@ -0,0 +1,20 @@
+import gulp from 'gulp';
+import concat from 'gulp-concat';
+import BundleResources from '../helpers/bundleResources';
+
+const GUI_BASE = '../../web/gui/src/main/webapp/';
+const bundleFiles = [
+ 'app/onos.css',
+ 'app/onos-theme.css',
+ 'app/common.css',
+ 'app/fw/**/*.css',
+ 'app/view/**/*.css',
+];
+
+const task = gulp.task('bundle-css', function () {
+ return gulp.src(BundleResources(GUI_BASE, bundleFiles))
+ .pipe(concat('onos.css'))
+ .pipe(gulp.dest(GUI_BASE + '/dist/'));
+});
+
+export default task;
\ No newline at end of file
diff --git a/tools/gui/gulp-tasks/bundles/bundle-js/index.js b/tools/gui/gulp-tasks/bundles/bundle-js/index.js
new file mode 100644
index 0000000..4af3432
--- /dev/null
+++ b/tools/gui/gulp-tasks/bundles/bundle-js/index.js
@@ -0,0 +1,51 @@
+import gulp from 'gulp';
+import concat from 'gulp-concat';
+import strip from 'gulp-strip-comments';
+import uglyfy from 'gulp-uglify';
+import sourceMaps from 'gulp-sourcemaps';
+import BundleResources from '../helpers/bundleResources';
+
+
+const GUI_BASE = '../../web/gui/src/main/webapp/';
+const bundleFiles = [
+ // NOTE: Bundle the important files first
+ 'app/directives.js',
+ 'app/fw/util/util.js',
+ 'app/fw/mast/mast.js',
+ 'app/fw/nav/nav.js',
+ 'app/fw/svg/svg.js',
+ 'app/fw/remote/remote.js',
+ 'app/fw/widget/widget.js',
+ 'app/fw/layer/layer.js',
+
+ // NOTE: bundle everything else
+ 'app/fw/**/*.js',
+ 'app/view/**/*.js'
+];
+
+const vendor = [
+ 'tp/angular.js',
+ 'tp/angular-route.js',
+ 'tp/angular-cookies.js',
+ 'tp/d3.js',
+ 'tp/topojson.v1.min.js',
+ 'tp/Chart.min.js',
+ 'tp/lodash.min.js',
+];
+
+function bundle(files, exportName) {
+ return gulp.src(BundleResources(GUI_BASE, files))
+ .pipe(sourceMaps.init())
+ .pipe(strip())
+ .pipe(uglyfy())
+ .pipe(concat(exportName))
+ .pipe(sourceMaps.write('source-map'))
+ .pipe(gulp.dest(GUI_BASE + '/dist/'));
+}
+
+const tasks = function () {
+ gulp.task('bundle-vendor', () => bundle(vendor, 'vendor.js'));
+ gulp.task('bundle-js', () => bundle(bundleFiles, 'onos.js'));
+};
+
+export default tasks();
\ No newline at end of file
diff --git a/tools/gui/gulp-tasks/bundles/helpers/bundleResources.js b/tools/gui/gulp-tasks/bundles/helpers/bundleResources.js
new file mode 100644
index 0000000..0ee0898
--- /dev/null
+++ b/tools/gui/gulp-tasks/bundles/helpers/bundleResources.js
@@ -0,0 +1,5 @@
+export default function(prefix, fileArray) {
+ return fileArray.map((file) => {
+ return prefix + file;
+ });
+}
\ No newline at end of file
diff --git a/tools/gui/gulp-tasks/index.js b/tools/gui/gulp-tasks/index.js
new file mode 100644
index 0000000..3bef084
--- /dev/null
+++ b/tools/gui/gulp-tasks/index.js
@@ -0,0 +1,2 @@
+export { default as BundleJS } from './bundles/bundle-js';
+export { default as BundleCSS } from './bundles/bundle-css';
\ No newline at end of file
diff --git a/tools/gui/gulpfile.babel.js b/tools/gui/gulpfile.babel.js
new file mode 100644
index 0000000..0d39b04
--- /dev/null
+++ b/tools/gui/gulpfile.babel.js
@@ -0,0 +1,8 @@
+import gulp from 'gulp';
+import * as Tasks from './gulp-tasks/';
+
+gulp.task('build', ['bundle-css', 'bundle-vendor', 'bundle-js']);
+
+gulp.task('default', function() {
+ // Do stuff
+});
\ No newline at end of file
diff --git a/tools/gui/package.json b/tools/gui/package.json
new file mode 100644
index 0000000..bd76963
--- /dev/null
+++ b/tools/gui/package.json
@@ -0,0 +1,28 @@
+{
+ "name": "onos-gui-build",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1",
+ "build": "gulp build"
+ },
+ "author": "",
+ "license": "ISC",
+ "dependencies": {},
+ "devDependencies": {
+ "babel": "^6.23.0",
+ "babel-core": "^6.25.0",
+ "babel-preset-es2015": "^6.24.1",
+ "gulp": "^3.9.1",
+ "gulp-concat": "^2.6.1",
+ "gulp-sourcemaps": "^2.6.0",
+ "gulp-strip-comments": "^2.4.5",
+ "gulp-uglify": "^3.0.0"
+ },
+ "babel": {
+ "presets": [
+ "es2015"
+ ]
+ }
+}
diff --git a/web/gui/BUCK b/web/gui/BUCK
index 88181de..e15ce91 100644
--- a/web/gui/BUCK
+++ b/web/gui/BUCK
@@ -1,3 +1,5 @@
+NODE_VERSION = '8.0.1'
+
COMPILE_DEPS = [
'//lib:CORE_DEPS',
'//lib:javax.ws.rs-api',
@@ -12,6 +14,7 @@
'//incubator/net:onos-incubator-net',
'//utils/rest:onlab-rest',
'//core/store/serializers:onos-core-serializers',
+ ':onos-tools-gui'
]
TEST_DEPS = [
@@ -30,10 +33,33 @@
'WEB-INF/classes/raw': 'src/main/webapp/raw',
}
+include_defs('//bucklets/node.bucklet')
+sh_src='onos-gui-build'
+
+fetch_node(version = 'v8.1.2')
+
+export_file (
+ name = 'onos-tools-gui-exe',
+ src = sh_src,
+ visibility = [ 'PUBLIC' ],
+)
+
+genrule(
+ name = 'onos-tools-gui',
+ srcs = ['src/main/webapp'],
+ bash = '$(location :onos-tools-gui-exe) '
+ + '$(location //web/gui:node-release-v8.1.2) '
+ + '$(location //web/gui:node-bin-v8.1.2) > $OUT',
+ cmd_exe = '',
+ out = 'onos-tools-gui.log',
+ visibility = [ 'PUBLIC' ],
+)
+
osgi_jar_with_tests (
name = 'onos-gui',
deps = COMPILE_DEPS,
test_deps = TEST_DEPS,
include_resources = RESOURCES,
web_context = '/onos/ui',
+ do_javadocs = False,
)
diff --git a/web/gui/onos-gui-build b/web/gui/onos-gui-build
new file mode 100755
index 0000000..b913f5b
--- /dev/null
+++ b/web/gui/onos-gui-build
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+ONOS_INSTALL_LOCATION=$2
+echo ${ONOS_INSTALL_LOCATION}
+
+export NODEJS_HOME=${ONOS_INSTALL_LOCATION}/
+export PATH=$NODEJS_HOME/bin:$PATH
+
+cd $ONOS_ROOT/tools/gui
+# Install NPM Global packages
+ONOS_NPM_GLOBAL_DEPENDENCIES="gulp-cli gulp"
+echo "Installing NPM Global dependencies: $ONOS_NPM_GLOBAL_DEPENDENCIES"
+npm install -g ${ONOS_NPM_GLOBAL_DEPENDENCIES} --loglevel=error
+
+# Install Project Dependencies
+echo "Installing project dependencies"
+npm install --loglevel=error
+
+# Build the GUI Project
+echo "Packaging JavaScript and CSS"
+npm run build
\ No newline at end of file
diff --git a/web/gui/src/main/webapp/app/fw/mast/mast.css b/web/gui/src/main/webapp/app/fw/mast/mast.css
index 24eff91..265d789 100644
--- a/web/gui/src/main/webapp/app/fw/mast/mast.css
+++ b/web/gui/src/main/webapp/app/fw/mast/mast.css
@@ -88,7 +88,7 @@
height: 7px;
width: 9px;
margin-left: 10px;
- background: url('../../../data/img/dropdown-icon.png') no-repeat;
+ background: url('/onos/ui/data/img/dropdown-icon.png') no-repeat;
}
#mast .dropdown {
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2.js b/web/gui/src/main/webapp/app/view/topo2/topo2.js
index cf13d11..db64e14 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2.js
@@ -22,7 +22,7 @@
(function () {
'use strict';
-
+
// references to injected services
var $scope, $log, fs, mast, ks, wss,
gs, sus, ps, t2es, t2fs, t2is, t2bcs, t2kcs, t2ms, t2mcs, t2zs;
diff --git a/web/gui/src/main/webapp/index.html b/web/gui/src/main/webapp/index.html
index 07395cb..e74329b 100644
--- a/web/gui/src/main/webapp/index.html
+++ b/web/gui/src/main/webapp/index.html
@@ -30,222 +30,22 @@
<title>ONOS</title>
<!-- Third party library code included here -->
- <!--TODO: use minified versions, once debugging is complete -->
- <script src="tp/angular.js"></script>
- <script src="tp/angular-route.js"></script>
- <script src="tp/angular-cookies.js"></script>
-
- <script src="tp/d3.js"></script>
- <script src="tp/topojson.v1.min.js"></script>
-
- <script src="tp/Chart.min.js"></script>
- <script src="tp/angular-chart.min.js"></script>
- <script src="tp/lodash.min.js"></script>
+ <script src="dist/vendor.js"></script>
<!-- {INJECTED-USER-START} -->
<!-- {INJECTED-USER-END} -->
<!-- ONOS UI Framework included here -->
- <!-- TODO: use a single catenated-minified file here -->
<script src="onos.js"></script>
- <script src="app/directives.js"></script>
-
- <script src="app/fw/util/util.js"></script>
- <script src="app/fw/util/ee.js"></script>
- <script src="app/fw/util/fn.js"></script>
- <script src="app/fw/util/random.js"></script>
- <script src="app/fw/util/theme.js"></script>
- <script src="app/fw/util/keys.js"></script>
- <script src="app/fw/util/prefs.js"></script>
- <script src="app/fw/util/lion.js"></script>
-
- <script src="app/fw/mast/mast.js"></script>
- <script src="app/fw/nav/nav.js"></script>
-
- <script src="app/fw/svg/svg.js"></script>
- <script src="app/fw/svg/glyph.js"></script>
- <script src="app/fw/svg/glyphData.js"></script>
- <script src="app/fw/svg/icon.js"></script>
- <script src="app/fw/svg/geodata.js"></script>
- <script src="app/fw/svg/map.js"></script>
- <script src="app/fw/svg/zoom.js"></script>
- <script src="app/fw/svg/svgUtil.js"></script>
- <script src="app/fw/svg/sprite.js"></script>
- <script src="app/fw/svg/spriteData.js"></script>
-
- <script src="app/fw/remote/remote.js"></script>
- <script src="app/fw/remote/urlfn.js"></script>
- <script src="app/fw/remote/rest.js"></script>
- <script src="app/fw/remote/wsock.js"></script>
- <script src="app/fw/remote/websocket.js"></script>
-
- <script src="app/fw/widget/widget.js"></script>
- <script src="app/fw/widget/table.js"></script>
- <script src="app/fw/widget/toolbar.js"></script>
- <script src="app/fw/widget/tooltip.js"></script>
- <script src="app/fw/widget/button.js"></script>
- <script src="app/fw/widget/tableDetail.js"></script>
- <script src="app/fw/widget/tableBuilder.js"></script>
- <script src="app/fw/widget/chartBuilder.js"></script>
- <script src="app/fw/widget/listBuilder.js"></script>
-
- <script src="app/fw/layer/layer.js"></script>
- <script src="app/fw/layer/panel.js"></script>
- <script src="app/fw/layer/dialog.js"></script>
- <script src="app/fw/layer/flash.js"></script>
- <script src="app/fw/layer/quickhelp.js"></script>
- <script src="app/fw/layer/veil.js"></script>
- <script src="app/fw/layer/loading.js"></script>
+ <script src="dist/onos.js"></script>
<!-- Framework and library stylesheets included here -->
- <!-- TODO: use a single catenated-minified file here -->
- <link rel="stylesheet" href="app/onos.css">
- <link rel="stylesheet" href="app/onos-theme.css">
- <link rel="stylesheet" href="app/common.css">
- <link rel="stylesheet" href="app/fw/mast/mast.css">
- <link rel="stylesheet" href="app/fw/mast/mast-theme.css">
- <link rel="stylesheet" href="app/fw/svg/glyph.css">
- <link rel="stylesheet" href="app/fw/svg/glyph-theme.css">
- <link rel="stylesheet" href="app/fw/svg/sprite-theme.css">
- <link rel="stylesheet" href="app/fw/svg/icon.css">
- <link rel="stylesheet" href="app/fw/svg/icon-theme.css">
- <link rel="stylesheet" href="app/fw/layer/panel.css">
- <link rel="stylesheet" href="app/fw/layer/panel-theme.css">
- <link rel="stylesheet" href="app/fw/layer/dialog.css">
- <link rel="stylesheet" href="app/fw/layer/dialog-theme.css">
- <link rel="stylesheet" href="app/fw/layer/flash.css">
- <link rel="stylesheet" href="app/fw/layer/flash-theme.css">
- <link rel="stylesheet" href="app/fw/layer/quickhelp.css">
- <link rel="stylesheet" href="app/fw/layer/quickhelp-theme.css">
- <link rel="stylesheet" href="app/fw/layer/veil.css">
- <link rel="stylesheet" href="app/fw/layer/veil-theme.css">
- <link rel="stylesheet" href="app/fw/layer/loading.css">
- <link rel="stylesheet" href="app/fw/nav/nav.css">
- <link rel="stylesheet" href="app/fw/nav/nav-theme.css">
- <link rel="stylesheet" href="app/fw/widget/button.css">
- <link rel="stylesheet" href="app/fw/widget/button-theme.css">
- <link rel="stylesheet" href="app/fw/widget/toolbar.css">
- <link rel="stylesheet" href="app/fw/widget/toolbar-theme.css">
- <link rel="stylesheet" href="app/fw/widget/tooltip.css">
- <link rel="stylesheet" href="app/fw/widget/tooltip-theme.css">
- <link rel="stylesheet" href="app/fw/widget/table.css">
- <link rel="stylesheet" href="app/fw/widget/table-theme.css">
-
- <!-- Under development for Region support. -->
- <script src="app/view/topo2/topo2.js"></script>
- <script src="app/view/topo2/topo2Breadcrumb.js"></script>
- <script src="app/view/topo2/topo2Background.js"></script>
- <script src="app/view/topo2/topo2Badge.js"></script>
- <script src="app/view/topo2/topo2Collection.js"></script>
- <script src="app/view/topo2/topo2DetailsPanel.js"></script>
- <script src="app/view/topo2/topo2Device.js"></script>
- <script src="app/view/topo2/topo2DeviceDetailsPanel.js"></script>
- <script src="app/view/topo2/topo2Event.js"></script>
- <script src="app/view/topo2/topo2Force.js"></script>
- <script src="app/view/topo2/topo2Host.js"></script>
- <script src="app/view/topo2/topo2HostsPanel.js"></script>
- <script src="app/view/topo2/topo2Instance.js"></script>
- <script src="app/view/topo2/topo2KeyCommands.js"></script>
- <script src="app/view/topo2/topo2Label.js"></script>
- <script src="app/view/topo2/topo2LabelCollection.js"></script>
- <script src="app/view/topo2/topo2Layout.js"></script>
- <script src="app/view/topo2/topo2Link.js"></script>
- <script src="app/view/topo2/topo2LinkLabel.js"></script>
- <script src="app/view/topo2/topo2LinkPanel.js"></script>
- <script src="app/view/topo2/topo2Map.js"></script>
- <script src="app/view/topo2/topo2MapConfig.js"></script>
- <script src="app/view/topo2/topo2Mastership.js"></script>
- <script src="app/view/topo2/topo2Model.js"></script>
- <script src="app/view/topo2/topo2NodeModel.js"></script>
- <script src="app/view/topo2/topo2NodePosition.js"></script>
- <script src="app/view/topo2/topo2NoDevicesConnected.js"></script>
- <script src="app/view/topo2/topo2Overlay.js"></script>
- <script src="app/view/topo2/topo2Panel.js"></script>
- <script src="app/view/topo2/topo2PeerRegion.js"></script>
- <script src="app/view/topo2/topo2Prefs.js"></script>
- <script src="app/view/topo2/topo2Region.js"></script>
- <script src="app/view/topo2/topo2RegionNavigation.js"></script>
- <script src="app/view/topo2/topo2Select.js"></script>
- <script src="app/view/topo2/topo2SpriteLayer.js"></script>
- <script src="app/view/topo2/topo2SummaryPanel.js"></script>
- <script src="app/view/topo2/topo2SubRegion.js"></script>
- <script src="app/view/topo2/topo2SubRegionPanel.js"></script>
- <script src="app/view/topo2/topo2Toolbar.js"></script>
- <script src="app/view/topo2/topo2Traffic.js"></script>
- <script src="app/view/topo2/topo2TrafficOverlay.js"></script>
- <script src="app/view/topo2/topo2View.js"></script>
- <script src="app/view/topo2/topo2ViewController.js"></script>
- <script src="app/view/topo2/topo2Zoom.js"></script>
- <script src="app/view/topo2/uiView.js"></script>
- <link rel="stylesheet" href="app/view/topo2/topo2.css">
- <link rel="stylesheet" href="app/view/topo2/topo2-theme.css">
-
- <!-- Builtin views javascript. -->
- <script src="app/view/topo/topo.js"></script>
- <script src="app/view/topo/topoD3.js"></script>
- <script src="app/view/topo/topoEvent.js"></script>
- <script src="app/view/topo/topoDialog.js"></script>
- <script src="app/view/topo/topoFilter.js"></script>
- <script src="app/view/topo/topoForce.js"></script>
- <script src="app/view/topo/topoInst.js"></script>
- <script src="app/view/topo/topoLink.js"></script>
- <script src="app/view/topo/topoModel.js"></script>
- <script src="app/view/topo/topoOblique.js"></script>
- <script src="app/view/topo/topoOverlay.js"></script>
- <script src="app/view/topo/topoPanel.js"></script>
- <script src="app/view/topo/topoSelect.js"></script>
- <script src="app/view/topo/topoMap.js"></script>
- <script src="app/view/topo/topoSprite.js"></script>
- <script src="app/view/topo/topoTraffic.js"></script>
- <script src="app/view/topo/topoTrafficNew.js"></script>
- <script src="app/view/topo/topoProtectedIntent.js"></script>
- <script src="app/view/topo/topoProtectedIntentOverlay.js"></script>
- <script src="app/view/topo/topoToolbar.js"></script>
- <script src="app/view/device/device.js"></script>
- <script src="app/view/flow/flow.js"></script>
- <script src="app/view/port/port.js"></script>
- <script src="app/view/group/group.js"></script>
- <script src="app/view/meter/meter.js"></script>
- <script src="app/view/partition/partition.js"></script>
- <script src="app/view/link/link.js"></script>
- <script src="app/view/host/host.js"></script>
- <script src="app/view/intent/intent.js"></script>
- <script src="app/view/app/app.js"></script>
- <script src="app/view/settings/settings.js"></script>
- <script src="app/view/cluster/cluster.js"></script>
- <script src="app/view/processor/processor.js"></script>
- <script src="app/view/tunnel/tunnel.js"></script>
+ <link rel="stylesheet" href="dist/onos.css">
<!-- Contributed javascript injected here -->
<!-- {INJECTED-JAVASCRIPT-START} -->
<!-- {INJECTED-JAVASCRIPT-END} -->
- <!-- Builtin views stylesheets. -->
- <link rel="stylesheet" href="app/view/topo/topo.css">
- <link rel="stylesheet" href="app/view/topo/topo-theme.css">
- <link rel="stylesheet" href="app/view/topo/protected-intents.css">
- <link rel="stylesheet" href="app/view/device/device.css">
- <link rel="stylesheet" href="app/view/device/device-theme.css">
- <link rel="stylesheet" href="app/view/flow/flow.css">
- <link rel="stylesheet" href="app/view/flow/flow-theme.css">
- <link rel="stylesheet" href="app/view/port/port.css">
- <link rel="stylesheet" href="app/view/group/group.css">
- <link rel="stylesheet" href="app/view/group/group-theme.css">
- <link rel="stylesheet" href="app/view/meter/meter.css">
- <link rel="stylesheet" href="app/view/meter/meter-theme.css">
- <link rel="stylesheet" href="app/view/partition/partition.css">
- <link rel="stylesheet" href="app/view/link/link.css">
- <link rel="stylesheet" href="app/view/host/host.css">
- <link rel="stylesheet" href="app/view/intent/intent.css">
- <link rel="stylesheet" href="app/view/intent/intent-theme.css">
- <link rel="stylesheet" href="app/view/app/app.css">
- <link rel="stylesheet" href="app/view/app/app-theme.css">
- <link rel="stylesheet" href="app/view/settings/settings.css">
- <link rel="stylesheet" href="app/view/settings/settings-theme.css">
- <link rel="stylesheet" href="app/view/cluster/cluster.css">
- <link rel="stylesheet" href="app/view/processor/processor.css">
- <link rel="stylesheet" href="app/view/tunnel/tunnel.css">
-
<!-- Contributed stylesheets injected here -->
<!-- {INJECTED-STYLESHEETS-START} -->
<!-- {INJECTED-STYLESHEETS-END} -->