mirror of
https://github.com/opennetworkinglab/onos.git
synced 2026-05-05 04:06:49 +02:00
Merge remote-tracking branch 'origin/master' into merge-master
Change-Id: I4608093c4400a313b253508ac6bc8a84ecba5c7e
This commit is contained in:
commit
df521294ce
@ -1134,7 +1134,8 @@ public class Dhcp4HandlerImpl implements DhcpHandler, HostProvider {
|
||||
DhcpServerInfo foundServerInfo = findServerInfoFromServer(directConnFlag, inPort);
|
||||
|
||||
if (foundServerInfo == null) {
|
||||
log.warn("Cannot find server info");
|
||||
log.warn("Cannot find server info for {} server, inPort {}",
|
||||
directConnFlag ? "direct" : "indirect", inPort);
|
||||
return null;
|
||||
} else {
|
||||
if (Dhcp4HandlerUtil.isServerIpEmpty(foundServerInfo)) {
|
||||
@ -1517,7 +1518,14 @@ public class Dhcp4HandlerImpl implements DhcpHandler, HostProvider {
|
||||
// sent by ONOS or circuit Id can't be parsed
|
||||
// TODO: remove relay store from this method
|
||||
MacAddress dstMac = valueOf(dhcpPayload.getClientHardwareAddress());
|
||||
Optional<DhcpRecord> dhcpRecord = dhcpRelayStore.getDhcpRecord(HostId.hostId(dstMac, originalPacketVlanId));
|
||||
VlanId filteredVlanId = getVlanIdFromDhcpRecord(dstMac, originalPacketVlanId);
|
||||
// Get the vlan from the dhcp record
|
||||
if (filteredVlanId == null) {
|
||||
log.debug("not find the matching DHCP record for mac: {} and vlan: {}", dstMac, originalPacketVlanId);
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
Optional<DhcpRecord> dhcpRecord = dhcpRelayStore.getDhcpRecord(HostId.hostId(dstMac, filteredVlanId));
|
||||
ConnectPoint clientConnectPoint = dhcpRecord
|
||||
.map(DhcpRecord::locations)
|
||||
.orElse(Collections.emptySet())
|
||||
@ -1534,12 +1542,51 @@ public class Dhcp4HandlerImpl implements DhcpHandler, HostProvider {
|
||||
if (clientConnectPoint != null) {
|
||||
return interfaceService.getInterfacesByPort(clientConnectPoint)
|
||||
.stream()
|
||||
.filter(iface -> interfaceContainsVlan(iface, originalPacketVlanId))
|
||||
.filter(iface -> interfaceContainsVlan(iface, filteredVlanId))
|
||||
.findFirst();
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required vlanId in case the DCHP record has more than one vlanId for a given MAC.
|
||||
*
|
||||
* @param mac MAC address of the DHCP client
|
||||
* @param vlan Expected vlan of the DHCP client
|
||||
*/
|
||||
private VlanId getVlanIdFromDhcpRecord(MacAddress mac, VlanId vlan) {
|
||||
// Get all the DHCP records matching with the mac address
|
||||
// If only one entry is present then pick the vlan of that entry
|
||||
// If more then one entry is present then look for an entry with matching vlan
|
||||
// else return null
|
||||
Collection<DhcpRecord> records = dhcpRelayStore.getDhcpRecords();
|
||||
List<DhcpRecord> filteredRecords = new ArrayList<>();
|
||||
for (DhcpRecord e: records) {
|
||||
if (e.macAddress().equals(mac)) {
|
||||
filteredRecords.add(e);
|
||||
}
|
||||
}
|
||||
log.debug("getVlanIdFromDhcpRecord mac: {} vlan: {}", mac, vlan);
|
||||
log.debug("filteredRecords are: {}", filteredRecords);
|
||||
if (filteredRecords.size() == 1) {
|
||||
log.debug("Only one DHCP record entry. Returning back the vlan of that DHCP record: {}", filteredRecords);
|
||||
return filteredRecords.get(0).vlanId();
|
||||
}
|
||||
// Check in the DHCP filtered record for matching vlan
|
||||
for (DhcpRecord e: filteredRecords) {
|
||||
if (e.vlanId().equals(vlan)) {
|
||||
log.debug("Found a matching vlan entry in the DHCP record:{}", e);
|
||||
return vlan;
|
||||
}
|
||||
}
|
||||
// Found nothing return null
|
||||
log.debug("Returning null as no matching or more than one matching entry found");
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Send the response DHCP to the requester host.
|
||||
*
|
||||
@ -1924,9 +1971,6 @@ public class Dhcp4HandlerImpl implements DhcpHandler, HostProvider {
|
||||
log.debug("ServerInfo found for Rcv port {} Server Connect Point {} for {}",
|
||||
inPort, serverInfo.getDhcpServerConnectPoint(), directConnFlag ? "direct" : "indirect");
|
||||
break;
|
||||
} else {
|
||||
log.warn("Rcv port {} not the same as Server Connect Point {} for {}",
|
||||
inPort, serverInfo.getDhcpServerConnectPoint(), directConnFlag ? "direct" : "indirect");
|
||||
}
|
||||
}
|
||||
return foundServerInfo;
|
||||
|
||||
@ -1211,7 +1211,8 @@ public class Dhcp6HandlerImpl implements DhcpHandler, HostProvider {
|
||||
DhcpServerInfo foundServerInfo = findServerInfoFromServer(directConnFlag, inPort);
|
||||
|
||||
if (foundServerInfo == null) {
|
||||
log.warn("Cannot find server info");
|
||||
log.warn("Cannot find server info for {} server, inPort {}",
|
||||
directConnFlag ? "direct" : "indirect", inPort);
|
||||
dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_SERVER_INFO);
|
||||
return null;
|
||||
} else {
|
||||
@ -1846,9 +1847,6 @@ public class Dhcp6HandlerImpl implements DhcpHandler, HostProvider {
|
||||
log.debug("ServerInfo found for Rcv port {} Server Connect Point {} for {}",
|
||||
inPort, serverInfo.getDhcpServerConnectPoint(), directConnFlag ? "direct" : "indirect");
|
||||
break;
|
||||
} else {
|
||||
log.warn("Rcv port {} not the same as Server Connect Point {} for {}",
|
||||
inPort, serverInfo.getDhcpServerConnectPoint(), directConnFlag ? "direct" : "indirect");
|
||||
}
|
||||
}
|
||||
return foundServerInfo;
|
||||
|
||||
13
apps/faultmanagement/fm-gui2-lib/.editorconfig
Normal file
13
apps/faultmanagement/fm-gui2-lib/.editorconfig
Normal file
@ -0,0 +1,13 @@
|
||||
# Editor configuration, see http://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
||||
42
apps/faultmanagement/fm-gui2-lib/.gitignore
vendored
Normal file
42
apps/faultmanagement/fm-gui2-lib/.gitignore
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# compiled output
|
||||
/dist
|
||||
/tmp
|
||||
/out-tsc
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
|
||||
# IDEs and editors
|
||||
/.idea
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# IDE - VSCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
|
||||
# misc
|
||||
/.sass-cache
|
||||
/connect.lock
|
||||
/coverage
|
||||
/libpeerconnection.log
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
testem.log
|
||||
/typings
|
||||
|
||||
# System Files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# ONOS temp CSS files
|
||||
/projects/fm-gui2-lib/fw
|
||||
291
apps/faultmanagement/fm-gui2-lib/BUILD
Normal file
291
apps/faultmanagement/fm-gui2-lib/BUILD
Normal file
@ -0,0 +1,291 @@
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
|
||||
"""
|
||||
Rules to build the Fault Management (FM) GUI app for GUI 2
|
||||
|
||||
|
||||
"""
|
||||
|
||||
COMPILE_DEPS = CORE_DEPS + JACKSON + KRYO + [
|
||||
"@javax_ws_rs_api//jar",
|
||||
"@servlet_api//jar",
|
||||
"@jetty_websocket//jar",
|
||||
"@jetty_util//jar",
|
||||
"@jersey_media_multipart//jar",
|
||||
"@jersey_server//jar",
|
||||
"//utils/rest:onlab-rest",
|
||||
"//core/store/serializers:onos-core-serializers",
|
||||
]
|
||||
|
||||
TEST_DEPS = TEST + [
|
||||
"//core/api:onos-api-tests",
|
||||
"//drivers/default:onos-drivers-default",
|
||||
]
|
||||
|
||||
"""
|
||||
Files that get put at the top level of the tar ball
|
||||
"""
|
||||
|
||||
filegroup(
|
||||
name = "_root_level_files",
|
||||
srcs =
|
||||
[
|
||||
":angular.json",
|
||||
":package.json",
|
||||
":package-lock.json",
|
||||
":tsconfig.json",
|
||||
":tslint.json",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "_e2e_test_files",
|
||||
srcs = [
|
||||
":e2e/protractor.conf.js",
|
||||
":e2e/src/app.e2e-spec.ts",
|
||||
":e2e/src/app.po.ts",
|
||||
":e2e/tsconfig.e2e.json",
|
||||
],
|
||||
)
|
||||
|
||||
"""
|
||||
Run ng build to create FM GUI 2 library in production mode
|
||||
The output file fm-gui2-lib-ver.tgz is in the form that can be uploaded directly to https://www.npmjs.com/
|
||||
See bazel-genfiles/apps/faultmanagement/fm-gui2-lib/fm-gui2-lib.log for details of the Angular CLI output
|
||||
"""
|
||||
|
||||
genrule(
|
||||
name = "fm-gui2-lib-build",
|
||||
srcs = [
|
||||
"@nodejs//:bin/npm",
|
||||
"@nodejs//:bin/node",
|
||||
"@nodejs//:bin/node.js",
|
||||
"@nodejs//:bin/nodejs/bin/node",
|
||||
"@nodejs//:bin/nodejs/bin/npm",
|
||||
"//web/gui2-fw-lib:onos-gui2-fw-npm-install",
|
||||
"//web/gui2-fw-lib:onos-gui2-fw-ng-build",
|
||||
"//web/gui2-fw-lib:gui2_fw_lib_ext_css",
|
||||
":_root_level_files",
|
||||
":_fm_gui2_lib_src",
|
||||
":_fm-gui2_app_files",
|
||||
],
|
||||
outs = [
|
||||
"fm-gui2-lib.log",
|
||||
"fm-gui2-lib-ver.tgz",
|
||||
],
|
||||
cmd = "ROOT=`pwd` &&" +
|
||||
" export HOME=. &&" +
|
||||
" export XDG_CONFIG_HOME=$(@D)/config &&" +
|
||||
" NODE=$(location @nodejs//:bin/node) &&" +
|
||||
" INSTALL_FILES=($(locations //web/gui2-fw-lib:onos-gui2-fw-npm-install)) &&" + # An array of filenames - sorted by time created
|
||||
" FWLIB_FILES=($(locations //web/gui2-fw-lib:onos-gui2-fw-ng-build)) &&" + # An array of filenames - sorted by time created
|
||||
" mkdir -p apps/faultmanagement/fm-gui2-lib &&" +
|
||||
" cd apps/faultmanagement/fm-gui2-lib &&" +
|
||||
" jar xf $$ROOT/$${INSTALL_FILES[0]} &&" +
|
||||
" tar xf $$ROOT/$${FWLIB_FILES[0]} &&" +
|
||||
" mv package/ node_modules/gui2-fw-lib/ &&" +
|
||||
" mkdir -p projects/fm-gui2-lib/fw &&" +
|
||||
" (cd projects/fm-gui2-lib/fw &&" +
|
||||
" jar xf $$ROOT/$(location //web/gui2-fw-lib:gui2_fw_lib_ext_css)) &&" +
|
||||
" chmod +x node_modules/@angular/cli/bin/ng &&" +
|
||||
" export PATH=$$ROOT/$$(dirname $${NODE}):$$ROOT/apps/faultmanagement/fm-gui2-lib/node_modules/@angular/cli/bin:$$PATH &&" +
|
||||
" ng build --prod fm-gui2-lib >> $$ROOT/$(location fm-gui2-lib.log) 2>&1 ||" +
|
||||
" if [ $$? -eq 0 ]; then echo 'Successfully ran build';" +
|
||||
" else " +
|
||||
" echo 'Error running \'ng build fm-gui2-lib\' on \'//apps/faultmanagement/fm-gui2-lib:fm-gui2-lib-build\'. \\\n" +
|
||||
" See bazel-genfiles/apps/faultmanagement/fm-gui2-lib/fm-gui2-lib.log for more details' >&2;" +
|
||||
#" tail -n 100 ../../$(location onos-gui2-ng-test.log) >&2;" +
|
||||
" exit 1;" +
|
||||
" fi;" +
|
||||
" cd dist/fm-gui2-lib && " +
|
||||
" npm pack >> $$ROOT/$(location fm-gui2-lib.log) 2>&1 &&" +
|
||||
" mv fm-gui2-lib-*.tgz $$ROOT/$(location fm-gui2-lib-ver.tgz) &&" +
|
||||
" touch $$ROOT/$(location fm-gui2-lib.log)", # to get the log always as the 2nd file,
|
||||
message = "Angular FM GUI2 build",
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
"""
|
||||
Run 'ng test' to run Angular test and 'ng lint' for checkstyle
|
||||
See bazel-genfiles/apps/faultmanagement/fm-gui2-lib/fm-gui2-lib-lint.log or
|
||||
bazel-genfiles/apps/faultmanagement/fm-gui2-lib/fm-gui2-lib-test.log for details of the Angular CLI output
|
||||
"""
|
||||
|
||||
genrule(
|
||||
name = "_fm-gui2-lib-test-genrule",
|
||||
srcs = [
|
||||
"@nodejs//:bin/npm",
|
||||
"@nodejs//:bin/node",
|
||||
"@nodejs//:bin/node.js",
|
||||
"@nodejs//:bin/nodejs/bin/node",
|
||||
"@nodejs//:bin/nodejs/bin/npm",
|
||||
"//web/gui2-fw-lib:onos-gui2-fw-npm-install",
|
||||
"//web/gui2-fw-lib:onos-gui2-fw-ng-build",
|
||||
"//web/gui2-fw-lib:gui2_fw_lib_ext_css",
|
||||
":_root_level_files",
|
||||
":_fm_gui2_lib_src",
|
||||
":_fm_gui2_lib_src_tests",
|
||||
],
|
||||
outs = [
|
||||
"fm-gui2-lib-ver.log",
|
||||
"fm-gui2-lib-lint.log",
|
||||
"fm-gui2-lib-test.log",
|
||||
],
|
||||
cmd = " ROOT=`pwd` &&" +
|
||||
" export HOME=. &&" +
|
||||
" export XDG_CONFIG_HOME=$(@D)/config &&" +
|
||||
" NODE=$(location @nodejs//:bin/node) &&" +
|
||||
" INSTALL_FILES=($(locations //web/gui2-fw-lib:onos-gui2-fw-npm-install)) &&" + # An array of filenames - sorted by time created
|
||||
" FWLIB_FILES=($(locations //web/gui2-fw-lib:onos-gui2-fw-ng-build)) &&" + # An array of filenames - sorted by time created
|
||||
" mkdir -p apps/faultmanagement/fm-gui2-lib &&" +
|
||||
" cd apps/faultmanagement/fm-gui2-lib &&" +
|
||||
" jar xf ../../../$(location :_fm_gui2_lib_src_tests) &&" +
|
||||
" jar xf $$ROOT/$${INSTALL_FILES[0]} &&" +
|
||||
" tar xf $$ROOT/$${FWLIB_FILES[0]} &&" +
|
||||
" mv package/ node_modules/gui2-fw-lib/ &&" +
|
||||
" mkdir -p projects/fm-gui2-lib/fw &&" +
|
||||
" (cd projects/fm-gui2-lib/fw &&" +
|
||||
" jar xf $$ROOT/$(location //web/gui2-fw-lib:gui2_fw_lib_ext_css)) &&" +
|
||||
" chmod +x $$ROOT/apps/faultmanagement/fm-gui2-lib/node_modules/@angular/cli/bin/ng &&" +
|
||||
" export PATH=$$ROOT/$$(dirname $${NODE}):node_modules/@angular/cli/bin:$$PATH &&" +
|
||||
" node -v > ../../../$(location fm-gui2-lib-ver.log) &&" +
|
||||
" npm -v >> ../../../$(location fm-gui2-lib-ver.log) &&" +
|
||||
" ng -v >> ../../../$(location fm-gui2-lib-ver.log) &&" +
|
||||
" ng lint fm-gui2-lib > ../../../$(location fm-gui2-lib-lint.log);" +
|
||||
" if [ -f /usr/bin/chromium-browser ]; then " + # Add to this for Mac and Chrome
|
||||
" export CHROME_BIN=/usr/bin/chromium-browser; " +
|
||||
" elif [ -f /opt/google/chrome/chrome ]; then " +
|
||||
" export CHROME_BIN=/opt/google/chrome/chrome; " +
|
||||
" else " +
|
||||
" MSG='Warning: Step fm-gui2-lib-tests skipped because \\n" +
|
||||
" no binary for ChromeHeadless browser was found at /usr/bin/chromium-browser. \\n" +
|
||||
" Install Google Chrome or Chromium Browser to allow this step to run.';" +
|
||||
" echo -e $$MSG >&2;" +
|
||||
" echo -e $$MSG > ../../../$(location fm-gui2-lib-test.log);" +
|
||||
" exit 0;" +
|
||||
" fi;" +
|
||||
" ng test --preserve-symlinks --code-coverage --browsers=ChromeHeadless" +
|
||||
" --watch=false fm-gui2-lib > ../../../$(location fm-gui2-lib-test.log) 2>&1 ||" +
|
||||
" if [ $$? -eq 0 ]; then echo 'Successfully ran tests';" +
|
||||
" else " +
|
||||
" echo 'Error running \'ng test fm-gui2-lib\' on \'//apps/faultmanagement/fm-gui2-lib:_fm-gui2-lib-test-genrule\'. \\\n" +
|
||||
" See bazel-genfiles/apps/faultmanagement/fm-gui2-lib/fm-gui2-lib-test.log for more details' >&2;" +
|
||||
" exit 1;" +
|
||||
" fi;",
|
||||
message = "Angular FM GUI2 Lib lint and test",
|
||||
)
|
||||
|
||||
"""
|
||||
Make a group of all the webapp files.
|
||||
"""
|
||||
|
||||
filegroup(
|
||||
name = "_fm_gui2_lib_src",
|
||||
srcs = glob(
|
||||
[
|
||||
"projects/fm-gui2-lib/**/*",
|
||||
],
|
||||
exclude = [
|
||||
"projects/fm-gui2-lib/**/*.spec.*", # Don't track tests here
|
||||
"projects/fm-gui2-lib/karma.conf.js",
|
||||
"projects/fm-gui2-lib/src/test.ts",
|
||||
"projects/fm-gui2-lib/fw/**/*",
|
||||
],
|
||||
),
|
||||
)
|
||||
|
||||
"""
|
||||
Make a group of all the webapp qpp files.
|
||||
"""
|
||||
|
||||
filegroup(
|
||||
name = "_fm-gui2_app_files",
|
||||
srcs = glob(
|
||||
[
|
||||
"src/**/*",
|
||||
],
|
||||
),
|
||||
)
|
||||
|
||||
"""
|
||||
Make a jar file of all the webapp test (*.spec.ts) files.
|
||||
"""
|
||||
|
||||
genrule(
|
||||
name = "_fm_gui2_lib_src_tests",
|
||||
srcs = glob(
|
||||
[
|
||||
"projects/fm-gui2-lib/karma.conf.js",
|
||||
"projects/fm-gui2-lib/src/test.ts",
|
||||
"projects/fm-gui2-lib/tsconfig.spec.json",
|
||||
"projects/fm-gui2-lib/**/*.spec.ts",
|
||||
],
|
||||
exclude = [
|
||||
"projects/fm-gui2-lib/ng-package.json",
|
||||
"projects/fm-gui2-lib/ng-package.prod.json",
|
||||
"projects/fm-gui2-lib/package.json",
|
||||
"projects/fm-gui2-lib/tsconfig.lib.json",
|
||||
"projects/fm-gui2-lib/tslint.json",
|
||||
"projects/fm-gui2-lib/src/public_api.ts",
|
||||
],
|
||||
),
|
||||
outs = ["fm_gui2_lib_src_tests.jar"],
|
||||
cmd = "cd apps/faultmanagement/fm-gui2-lib &&" +
|
||||
" jar Mcf ../../../$@ .",
|
||||
)
|
||||
|
||||
"""
|
||||
Make a jar file of all the webapp test (*.spec.ts) files.
|
||||
"""
|
||||
|
||||
genrule(
|
||||
name = "_fm_gui2_lib_tests",
|
||||
srcs = glob(
|
||||
[
|
||||
"projects/fm-gui2-lib/**/*.spec.ts",
|
||||
"projects/fm-gui2-lib/**/*.spec.json",
|
||||
],
|
||||
exclude = [
|
||||
"src/main/webapp/tests/**",
|
||||
"src/main/webapp/node_modules/**",
|
||||
"src/main/webapp/dist/**",
|
||||
"src/main/webapp/doc/**",
|
||||
],
|
||||
),
|
||||
outs = ["fm_gui2_lib_tests.jar"],
|
||||
cmd = "cd apps/faultmanagement/fm-gui2-lib &&" +
|
||||
" find projects/fm-gui2-lib/src/lib -type f -exec touch -t 201808280000 {} \; &&" +
|
||||
" jar Mcf ../../../$@ projects/fm-gui2-lib/src/lib",
|
||||
)
|
||||
|
||||
"""
|
||||
Wrap the genrule for testing in a test
|
||||
"""
|
||||
|
||||
sh_test(
|
||||
name = "fm-gui2-lib-tests",
|
||||
size = "small",
|
||||
srcs = [
|
||||
":ng-test.sh",
|
||||
],
|
||||
data = [
|
||||
":_fm-gui2-lib-test-genrule",
|
||||
],
|
||||
deps = [
|
||||
"@bazel_tools//tools/bash/runfiles",
|
||||
],
|
||||
)
|
||||
85
apps/faultmanagement/fm-gui2-lib/README.md
Normal file
85
apps/faultmanagement/fm-gui2-lib/README.md
Normal file
@ -0,0 +1,85 @@
|
||||
# FmGui2LibApp
|
||||
|
||||
This project contains the FM GUI2 web application as an NPM library module
|
||||
|
||||
It is a new approach to GUI extensions in GUI2, where each extension is implemented in its own
|
||||
library module as an Angular module containing one or more Angular components, services or directives.
|
||||
It benefits from the lazy loading approach of the ONOS GUI2 application, and so does not have injected
|
||||
js and css files as the old approach had.
|
||||
|
||||
It contains the component AlarmTableComponent and AlarmDetailsComponent, and is built on top of
|
||||
classes from the GUI2 framework library (gui2-fw-lib).
|
||||
|
||||
This is integrated in to the main ONOS GUI2 web application by adding it to the
|
||||
"onos-routing.module.ts" in web/gui2/src/main/webapp/app and in to nav.html
|
||||
|
||||
```
|
||||
|
||||
===============================
|
||||
== ONOS GUI2 Web Application ==
|
||||
===============================
|
||||
||
|
||||
|| Lazy loads
|
||||
\/
|
||||
===================================
|
||||
== FM GUI2 LIB module (incl ...) ==
|
||||
== Alarm Table Component ==
|
||||
== Alarm Details Component ==
|
||||
===================================
|
||||
||
|
||||
|| Depends
|
||||
\/
|
||||
=============================
|
||||
== GUI2 FW Lib ==
|
||||
== Web Sockets, LION etc ==
|
||||
=============================
|
||||
|
||||
```
|
||||
|
||||
|
||||
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 6.0.0.
|
||||
|
||||
A couple of good articles on the creation and use of __libraries__ in Angular 6 is given in:
|
||||
|
||||
[The Angular Library Series - Creating a Library with the Angular CLI](https://blog.angularindepth.com/creating-a-library-in-angular-6-87799552e7e5)
|
||||
|
||||
and
|
||||
|
||||
[The Angular Library Series - Building and Packaging](https://blog.angularindepth.com/creating-a-library-in-angular-6-part-2-6e2bc1e14121)
|
||||
|
||||
The Bazel build of this library handles the building and packaging of the library
|
||||
so that other projects and libraries can use it.
|
||||
|
||||
## Development server
|
||||
|
||||
To build the library project using Angular CLI run 'ng build --prod fm-gui2-lib'
|
||||
inside the ~/onos/apps/faultmanagement/fm-gui2-lib folder
|
||||
|
||||
To make the library in to an NPM package use 'npm pack' inside the dist/fm-gui2-lib folder
|
||||
|
||||
To build the app that surrounds the library run 'ng build'. This app is not
|
||||
part of the ONOS GUI and is there as a placeholder for testing the library
|
||||
|
||||
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`.
|
||||
The app will automatically reload if you change any of the source files.
|
||||
__NOTE__ If you make changes to files in the library, the app will not pick them up until you build the library again
|
||||
|
||||
## Code scaffolding
|
||||
|
||||
Run `ng generate component <component-name> --project=fm-gui2-lib` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
|
||||
|
||||
## Build
|
||||
|
||||
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
||||
|
||||
## Running end-to-end tests
|
||||
|
||||
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
|
||||
|
||||
## Further help
|
||||
|
||||
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
|
||||
162
apps/faultmanagement/fm-gui2-lib/angular.json
Normal file
162
apps/faultmanagement/fm-gui2-lib/angular.json
Normal file
@ -0,0 +1,162 @@
|
||||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"fm-gui2-lib-app": {
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"projectType": "application",
|
||||
"prefix": "app",
|
||||
"schematics": {},
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
"options": {
|
||||
"outputPath": "dist/fm-gui2-lib-app",
|
||||
"index": "src/index.html",
|
||||
"main": "src/main.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "src/tsconfig.app.json",
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.css"
|
||||
],
|
||||
"scripts": []
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.prod.ts"
|
||||
}
|
||||
],
|
||||
"optimization": true,
|
||||
"outputHashing": "all",
|
||||
"sourceMap": false,
|
||||
"extractCss": true,
|
||||
"namedChunks": false,
|
||||
"aot": true,
|
||||
"extractLicenses": true,
|
||||
"vendorChunk": false,
|
||||
"buildOptimizer": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"options": {
|
||||
"browserTarget": "fm-gui2-lib-app:build"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"browserTarget": "fm-gui2-lib-app:build:production"
|
||||
}
|
||||
}
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"browserTarget": "fm-gui2-lib-app:build"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"main": "src/test.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "src/tsconfig.spec.json",
|
||||
"karmaConfig": "src/karma.conf.js",
|
||||
"styles": [
|
||||
"styles.css"
|
||||
],
|
||||
"scripts": [],
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
]
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"src/tsconfig.app.json",
|
||||
"src/tsconfig.spec.json"
|
||||
],
|
||||
"exclude": [
|
||||
"**/node_modules/**"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"fm-gui2-lib-app-e2e": {
|
||||
"root": "e2e/",
|
||||
"projectType": "application",
|
||||
"architect": {
|
||||
"e2e": {
|
||||
"builder": "@angular-devkit/build-angular:protractor",
|
||||
"options": {
|
||||
"protractorConfig": "e2e/protractor.conf.js",
|
||||
"devServerTarget": "fm-gui2-lib-app:serve"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": "e2e/tsconfig.e2e.json",
|
||||
"exclude": [
|
||||
"**/node_modules/**"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"fm-gui2-lib": {
|
||||
"root": "projects/fm-gui2-lib",
|
||||
"sourceRoot": "projects/fm-gui2-lib/src",
|
||||
"projectType": "library",
|
||||
"prefix": "onos",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-ng-packagr:build",
|
||||
"options": {
|
||||
"tsConfig": "projects/fm-gui2-lib/tsconfig.lib.json",
|
||||
"project": "projects/fm-gui2-lib/ng-package.json"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"project": "projects/fm-gui2-lib/ng-package.prod.json"
|
||||
}
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"main": "projects/fm-gui2-lib/src/test.ts",
|
||||
"tsConfig": "projects/fm-gui2-lib/tsconfig.spec.json",
|
||||
"karmaConfig": "projects/fm-gui2-lib/karma.conf.js"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"projects/fm-gui2-lib/tsconfig.lib.json",
|
||||
"projects/fm-gui2-lib/tsconfig.spec.json"
|
||||
],
|
||||
"exclude": [
|
||||
"**/node_modules/**"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultProject": "fm-gui2-lib-app"
|
||||
}
|
||||
28
apps/faultmanagement/fm-gui2-lib/e2e/protractor.conf.js
Normal file
28
apps/faultmanagement/fm-gui2-lib/e2e/protractor.conf.js
Normal file
@ -0,0 +1,28 @@
|
||||
// Protractor configuration file, see link for more information
|
||||
// https://github.com/angular/protractor/blob/master/lib/config.ts
|
||||
|
||||
const { SpecReporter } = require('jasmine-spec-reporter');
|
||||
|
||||
exports.config = {
|
||||
allScriptsTimeout: 11000,
|
||||
specs: [
|
||||
'./src/**/*.e2e-spec.ts'
|
||||
],
|
||||
capabilities: {
|
||||
'browserName': 'chrome'
|
||||
},
|
||||
directConnect: true,
|
||||
baseUrl: 'http://localhost:4200/',
|
||||
framework: 'jasmine',
|
||||
jasmineNodeOpts: {
|
||||
showColors: true,
|
||||
defaultTimeoutInterval: 30000,
|
||||
print: function() {}
|
||||
},
|
||||
onPrepare() {
|
||||
require('ts-node').register({
|
||||
project: require('path').join(__dirname, './tsconfig.e2e.json')
|
||||
});
|
||||
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
|
||||
}
|
||||
};
|
||||
14
apps/faultmanagement/fm-gui2-lib/e2e/src/app.e2e-spec.ts
Normal file
14
apps/faultmanagement/fm-gui2-lib/e2e/src/app.e2e-spec.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { AppPage } from './app.po';
|
||||
|
||||
describe('workspace-project App', () => {
|
||||
let page: AppPage;
|
||||
|
||||
beforeEach(() => {
|
||||
page = new AppPage();
|
||||
});
|
||||
|
||||
it('should display welcome message', () => {
|
||||
page.navigateTo();
|
||||
expect(page.getParagraphText()).toEqual('Welcome to app!');
|
||||
});
|
||||
});
|
||||
11
apps/faultmanagement/fm-gui2-lib/e2e/src/app.po.ts
Normal file
11
apps/faultmanagement/fm-gui2-lib/e2e/src/app.po.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { browser, by, element } from 'protractor';
|
||||
|
||||
export class AppPage {
|
||||
navigateTo() {
|
||||
return browser.get('/');
|
||||
}
|
||||
|
||||
getParagraphText() {
|
||||
return element(by.css('app-root h1')).getText();
|
||||
}
|
||||
}
|
||||
13
apps/faultmanagement/fm-gui2-lib/e2e/tsconfig.e2e.json
Normal file
13
apps/faultmanagement/fm-gui2-lib/e2e/tsconfig.e2e.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../out-tsc/app",
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"types": [
|
||||
"jasmine",
|
||||
"jasminewd2",
|
||||
"node"
|
||||
]
|
||||
}
|
||||
}
|
||||
29
apps/faultmanagement/fm-gui2-lib/ng-test.sh
Executable file
29
apps/faultmanagement/fm-gui2-lib/ng-test.sh
Executable file
@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
# --- begin runfiles.bash initialization ---
|
||||
if [[ ! -d "${RUNFILES_DIR:-/dev/null}" && ! -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then
|
||||
if [[ -f "$0.runfiles_manifest" ]]; then
|
||||
export RUNFILES_MANIFEST_FILE="$0.runfiles_manifest"
|
||||
elif [[ -f "$0.runfiles/MANIFEST" ]]; then
|
||||
export RUNFILES_MANIFEST_FILE="$0.runfiles/MANIFEST"
|
||||
elif [[ -f "$0.runfiles/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then
|
||||
export RUNFILES_DIR="$0.runfiles"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -f "${RUNFILES_DIR:-/dev/null}/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then
|
||||
source "${RUNFILES_DIR}/bazel_tools/tools/bash/runfiles/runfiles.bash"
|
||||
elif [[ -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then
|
||||
source "$(grep -m1 "^bazel_tools/tools/bash/runfiles/runfiles.bash " \
|
||||
"$RUNFILES_MANIFEST_FILE" | cut -d ' ' -f 2-)"
|
||||
else
|
||||
echo >&2 "ERROR: cannot find @bazel_tools//tools/bash/runfiles:runfiles.bash"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- end runfiles.bash initialization ---
|
||||
echo "Test results for Angular CLI commands run in fm-gui2-lib-tests outputs at:"
|
||||
ls $(rlocation org_onosproject_onos/apps/faultmanagement/fm-gui2-lib/fw-gui2-lib-ver.log)
|
||||
ls $(rlocation org_onosproject_onos/apps/faultmanagement/fm-gui2-lib/fw-gui2-lib-lint.log)
|
||||
ls $(rlocation org_onosproject_onos/apps/faultmanagement/fm-gui2-lib/fw-gui2-lib-test.log)
|
||||
|
||||
11641
apps/faultmanagement/fm-gui2-lib/package-lock.json
generated
Normal file
11641
apps/faultmanagement/fm-gui2-lib/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
47
apps/faultmanagement/fm-gui2-lib/package.json
Normal file
47
apps/faultmanagement/fm-gui2-lib/package.json
Normal file
@ -0,0 +1,47 @@
|
||||
{
|
||||
"name": "fm-gui2-lib-app",
|
||||
"version": "2.0.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^6.0.0",
|
||||
"@angular/common": "^6.0.0",
|
||||
"@angular/compiler": "^6.0.0",
|
||||
"@angular/core": "^6.0.0",
|
||||
"@angular/forms": "^6.0.0",
|
||||
"@angular/http": "^6.0.0",
|
||||
"@angular/platform-browser": "^6.0.0",
|
||||
"@angular/platform-browser-dynamic": "^6.0.0",
|
||||
"@angular/router": "^6.0.0",
|
||||
"core-js": "^2.5.4",
|
||||
"d3": "^5.2.0",
|
||||
"gui2-fw-lib": "file:../../../web/gui2-fw-lib/dist/gui2-fw-lib/gui2-fw-lib-0.14.0.tgz",
|
||||
"rxjs": "^6.0.0",
|
||||
"zone.js": "^0.8.26"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular/compiler-cli": "^6.0.0",
|
||||
"@angular-devkit/build-ng-packagr": "~0.6.8",
|
||||
"@angular-devkit/build-angular": "~0.6.0",
|
||||
"ng-packagr": "^3.0.0-rc.2",
|
||||
"tsickle": ">=0.25.5",
|
||||
"tslib": "^1.7.1",
|
||||
"typescript": "~2.7.2",
|
||||
"@angular/cli": "~6.0.0",
|
||||
"@angular/language-service": "^6.0.0",
|
||||
"@types/jasmine": "~2.8.6",
|
||||
"@types/jasminewd2": "~2.0.3",
|
||||
"@types/node": "~8.9.4",
|
||||
"codelyzer": "~4.2.1",
|
||||
"jasmine-core": "~2.99.1",
|
||||
"jasmine-spec-reporter": "~4.2.1",
|
||||
"karma": "~1.7.1",
|
||||
"karma-chrome-launcher": "~2.2.0",
|
||||
"karma-coverage-istanbul-reporter": "~1.4.2",
|
||||
"karma-firefox-launcher": "^1.1.0",
|
||||
"karma-jasmine": "~1.1.1",
|
||||
"karma-jasmine-html-reporter": "^0.2.2",
|
||||
"protractor": "~5.3.0",
|
||||
"ts-node": "~5.0.1",
|
||||
"tslint": "~5.9.1"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
basePath: '',
|
||||
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||
plugins: [
|
||||
require('karma-jasmine'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-firefox-launcher'),
|
||||
require('karma-jasmine-html-reporter'),
|
||||
require('karma-coverage-istanbul-reporter'),
|
||||
require('@angular-devkit/build-angular/plugins/karma')
|
||||
],
|
||||
client: {
|
||||
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
||||
},
|
||||
coverageIstanbulReporter: {
|
||||
dir: require('path').join(__dirname, '../../coverage'),
|
||||
reports: ['html', 'lcovonly'],
|
||||
fixWebpackSourcePaths: true
|
||||
},
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome', 'Firefox'],
|
||||
singleRun: false
|
||||
});
|
||||
};
|
||||
@ -0,0 +1,8 @@
|
||||
{
|
||||
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
|
||||
"dest": "../../dist/fm-gui2-lib",
|
||||
"deleteDestPath": false,
|
||||
"lib": {
|
||||
"entryFile": "src/public_api.ts"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
{
|
||||
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
|
||||
"dest": "../../dist/fm-gui2-lib",
|
||||
"lib": {
|
||||
"entryFile": "src/public_api.ts"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "fm-gui2-lib",
|
||||
"version": "1.15.0",
|
||||
"peerDependencies": {
|
||||
"@angular/common": "^6.0.0-rc.0 || ^6.0.0",
|
||||
"@angular/core": "^6.0.0-rc.0 || ^6.0.0"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* css for alarm details table view */
|
||||
|
||||
/* Panel Styling */
|
||||
#ov-alarm-table-item-details-panel.floatpanel {
|
||||
z-index: 0;
|
||||
font-size: 10pt;
|
||||
top: 185px;
|
||||
}
|
||||
|
||||
#ov-alarm-table-item-details-panel .container {
|
||||
padding: 0 30px;
|
||||
}
|
||||
|
||||
#ov-alarm-table-item-details-panel .close-btn {
|
||||
position: absolute;
|
||||
right:5px;
|
||||
top: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.light #ov-alarm-table-item-details-panel.floatpanel {
|
||||
background-color: rgb(229, 234, 237);
|
||||
}
|
||||
.dark #ov-alarm-table-item-details-panel.floatpanel {
|
||||
background-color: #3A4042;
|
||||
}
|
||||
|
||||
#ov-alarm-table-item-details-panel h2 {
|
||||
display: inline-block;
|
||||
margin: 8px 0;
|
||||
font-weight: bold;
|
||||
font-size: 16pt;
|
||||
}
|
||||
|
||||
#ov-alarm-table-item-details-panel h3 {
|
||||
margin: 0;
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
#ov-alarm-table-item-details-panel h4 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#ov-alarm-table-item-details-panel td {
|
||||
padding: 5px;
|
||||
}
|
||||
#ov-alarm-table-item-details-panel td.label {
|
||||
font-weight: bold;
|
||||
text-align: right;
|
||||
padding-right: 6px;
|
||||
}
|
||||
@ -0,0 +1,82 @@
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<div id="ov-alarm-table-item-details-panel" class="floatpanel" [@alarmDetailsState]="id!=='' && !closed">
|
||||
<div class="container">
|
||||
<div class="top">
|
||||
<div class="close-btn">
|
||||
<onos-icon class="close-btn" classes="active-close" iconId="close" iconSize="20" (click)="close()"></onos-icon>
|
||||
</div>
|
||||
<h2 class="editable clickable">{{detailsData.id}}</h2>
|
||||
<div class="top-content">
|
||||
<div class="top-tables">
|
||||
<div class="left">
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="label" width="110">Description:</td>
|
||||
<td class="value" width="80">{{detailsData.alarmDesc}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label" width="110">Device ID :</td>
|
||||
<td class="value" width="80">{{detailsData.alarmDeviceId}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label" width="110">Alarm source :</td>
|
||||
<td class="value" width="80">{{detailsData.alarmSource}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label" width="110">Time raised :</td>
|
||||
<td class="value" width="80">{{detailsData.alarmTimeRaised}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label" width="110">Time updated :</td>
|
||||
<td class="value" width="80">{{detailsData.alarmTimeUpdated}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label" width="110">Cleared :</td>
|
||||
<td class="value" width="80">{{detailsData.alarmCleared}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label" width="110">Time cleared :</td>
|
||||
<td class="value" width="80">{{detailsData.alarmTimeCleared}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label" width="110">Severity :</td>
|
||||
<td class="value" width="80">{{detailsData.alarmSeverity}}</td>
|
||||
</tr>
|
||||
<hr>
|
||||
<tr>
|
||||
<td class="label" width="110">Service affecting :</td>
|
||||
<td class="value" width="80">{{detailsData.alarmServiceAffecting}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label" width="110">Acknowledged :</td>
|
||||
<td class="value" width="80">{{detailsData.alarmAcknowledged}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label" width="110">Assigned User :</td>
|
||||
<td class="value" width="80">{{detailsData.alarmAssignedUser}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { AlarmDetailsComponent } from './alarmdetails.component';
|
||||
import { ActivatedRoute, Params } from '@angular/router';
|
||||
import { of } from 'rxjs/index';
|
||||
import {
|
||||
FnService,
|
||||
IconService,
|
||||
LogService,
|
||||
WebSocketService,
|
||||
IconComponent
|
||||
} from 'gui2-fw-lib';
|
||||
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { DebugElement } from '@angular/core';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { } from 'jasmine';
|
||||
|
||||
class MockActivatedRoute extends ActivatedRoute {
|
||||
constructor(params: Params) {
|
||||
super();
|
||||
this.queryParams = of(params);
|
||||
}
|
||||
}
|
||||
|
||||
class MockIconService {
|
||||
classes = 'active-close';
|
||||
loadIconDef() { }
|
||||
}
|
||||
|
||||
class MockWebSocketService {
|
||||
createWebSocket() { }
|
||||
isConnected() { return false; }
|
||||
unbindHandlers() { }
|
||||
bindHandlers() { }
|
||||
}
|
||||
|
||||
describe('AlarmDetailsComponent', () => {
|
||||
let fs: FnService;
|
||||
let ar: MockActivatedRoute;
|
||||
let windowMock: Window;
|
||||
let logServiceSpy: jasmine.SpyObj<LogService>;
|
||||
let component: AlarmDetailsComponent;
|
||||
let fixture: ComponentFixture<AlarmDetailsComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
const logSpy = jasmine.createSpyObj('LogService', ['info', 'debug', 'warn', 'error']);
|
||||
ar = new MockActivatedRoute({ 'debug': 'panel' });
|
||||
windowMock = <any>{
|
||||
location: <any>{
|
||||
hostname: 'foo',
|
||||
host: 'foo',
|
||||
port: '80',
|
||||
protocol: 'http',
|
||||
search: { debug: 'true' },
|
||||
href: 'ws://foo:123/onos/ui/websock/path',
|
||||
absUrl: 'ws://foo:123/onos/ui/websock/path'
|
||||
}
|
||||
};
|
||||
fs = new FnService(ar, logSpy, windowMock);
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
imports: [BrowserAnimationsModule],
|
||||
declarations: [ AlarmDetailsComponent, IconComponent ],
|
||||
providers: [
|
||||
{ provide: FnService, useValue: fs },
|
||||
{ provide: IconService, useClass: MockIconService },
|
||||
{ provide: LogService, useValue: logSpy },
|
||||
{ provide: WebSocketService, useClass: MockWebSocketService },
|
||||
{ provide: 'Window', useValue: windowMock },
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
logServiceSpy = TestBed.get(LogService);
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(AlarmDetailsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
import { Component, Input, OnInit, OnDestroy, OnChanges } from '@angular/core';
|
||||
import { trigger, state, style, animate, transition } from '@angular/animations';
|
||||
import {
|
||||
FnService,
|
||||
IconService,
|
||||
LoadingService,
|
||||
LogService,
|
||||
DetailsPanelBaseImpl,
|
||||
WebSocketService,
|
||||
} from 'gui2-fw-lib';
|
||||
|
||||
/**
|
||||
* ONOS GUI -- Alarm Details Component extends TableBaseImpl
|
||||
* The details view when an alarm row is clicked from the Alarm view
|
||||
*
|
||||
* This is expected to be passed an 'id' and it makes a call
|
||||
* to the WebSocket with an alarmDetailsRequest, and gets back an
|
||||
* alarmDetailsResponse.
|
||||
*
|
||||
* The animated fly-in is controlled by the animation below
|
||||
* The alarmDetailsState is attached to alarm-details-panel
|
||||
* and is false (flies out) when id='' and true (flies in) when
|
||||
* id has a value
|
||||
*/
|
||||
@Component({
|
||||
selector: 'onos-alarmdetails',
|
||||
templateUrl: './alarmdetails.component.html',
|
||||
styleUrls: ['./alarmdetails.component.css',
|
||||
'../../../fw/widget/panel.css', '../../../fw/widget/panel-theme.css'
|
||||
],
|
||||
animations: [
|
||||
trigger('alarmDetailsState', [
|
||||
state('true', style({
|
||||
transform: 'translateX(-100%)',
|
||||
opacity: '100'
|
||||
})),
|
||||
state('false', style({
|
||||
transform: 'translateX(0%)',
|
||||
opacity: '0'
|
||||
})),
|
||||
transition('0 => 1', animate('100ms ease-in')),
|
||||
transition('1 => 0', animate('100ms ease-out'))
|
||||
])
|
||||
]
|
||||
})
|
||||
export class AlarmDetailsComponent extends DetailsPanelBaseImpl implements OnInit, OnDestroy, OnChanges {
|
||||
@Input() id: string;
|
||||
|
||||
constructor(
|
||||
protected fs: FnService,
|
||||
protected ls: LoadingService,
|
||||
protected log: LogService,
|
||||
protected is: IconService,
|
||||
protected wss: WebSocketService
|
||||
) {
|
||||
super(fs, ls, log, wss, 'alarmTable');
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.init();
|
||||
this.log.debug('Alarm Table Details Component initialized:', this.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop listening to alarmTableDetailsResponse on WebSocket
|
||||
*/
|
||||
ngOnDestroy() {
|
||||
this.destroy();
|
||||
this.log.debug('Alarm Table Details Component destroyed');
|
||||
}
|
||||
|
||||
/**
|
||||
* Details Panel Data Request on row selection changes
|
||||
* Should be called whenever id changes
|
||||
* If id is empty, no request is made
|
||||
*/
|
||||
ngOnChanges() {
|
||||
if (this.id === '') {
|
||||
return '';
|
||||
} else {
|
||||
const query = {
|
||||
'id': this.id
|
||||
};
|
||||
this.requestDetailsPanelData(query);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* css for alarm table view */
|
||||
|
||||
#ov-alarm-table h2 {
|
||||
display: inline-block;
|
||||
}
|
||||
@ -0,0 +1,87 @@
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
<div id="ov-alarm-table">
|
||||
<div class="tabular-header">
|
||||
<h2>Alarms for {{ "all devices."}} ({{ tableData.length }} total)</h2>
|
||||
<div class="ctrl-btns">
|
||||
<div class="refresh" (click)="toggleRefresh()">
|
||||
<!-- See icon.theme.css for the defintions of the classes active and refresh-->
|
||||
<onos-icon classes="{{ autoRefresh?'active refresh':'refresh' }}" iconId="refresh" iconSize="36" toolTip="{{ autoRefreshTip }}"></onos-icon>
|
||||
</div>
|
||||
<!--div class="separator"></div>
|
||||
<div (click)="(!!selId) ? confirmAction(AlarmActionEnum.ACKNOWLEDGE) : ''">
|
||||
<onos-icon classes="{{ ctrlBtnState.acknowledged?'active-rect play':'play' }}"
|
||||
iconId="play" iconSize="42" toolTip="{{ acknowledgeTip }}"></onos-icon>
|
||||
</div>
|
||||
<div (click)="(!!selId) ? confirmAction(AlarmActionEnum.CLEAR) : ''">
|
||||
<onos-icon classes="{{ ctrlBtnState.cleared?'active-rect stop':'stop' }}"
|
||||
iconId="stop" iconSize="42" toolTip="{{ clearTip }}"></onos-icon>
|
||||
</div-->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="summary-list" class="summary-list" onosTableResize>
|
||||
|
||||
<div class="table-header">
|
||||
<table>
|
||||
<tr>
|
||||
<td colId="alarmId" (click)="onSort('id')">Id
|
||||
<onos-icon classes="active-sort" [iconSize]="10" [iconId]="sortIcon('id')"></onos-icon>
|
||||
</td>
|
||||
<td colId="alarmDeviceId" (click)="onSort('alarmDeviceId')">Device
|
||||
<onos-icon classes="active-sort" [iconSize]="10" [iconId]="sortIcon('alarmDeviceId')"></onos-icon>
|
||||
</td>
|
||||
<td colId="alarmDesc" (click)="onSort('alarmDesc')">Description
|
||||
<onos-icon classes="active-sort" [iconSize]="10" [iconId]="sortIcon('alarmDesc')"></onos-icon>
|
||||
</td>
|
||||
<td colId="alarmSource" (click)="onSort('alarmSource')">Source
|
||||
<onos-icon classes="active-sort" [iconSize]="10" [iconId]="sortIcon('alarmSource')"></onos-icon>
|
||||
</td>
|
||||
<td colId="alarmTimeRaised" (click)="onSort('alarmTimeRaised')">Time Raised
|
||||
<onos-icon classes="active-sort" [iconSize]="10" [iconId]="sortIcon('alarmTimeRaised')"></onos-icon>
|
||||
</td>
|
||||
<td colId="alarmSeverity" (click)="onSort('alarmSeverity')">Severity
|
||||
<onos-icon classes="active-sort" [iconSize]="10" [iconId]="sortIcon('alarmSeverity')"></onos-icon>
|
||||
</td>
|
||||
<td colId="alarmAcknowledged" (click)="onSort('alarmAcknowledged')">Acknowledged
|
||||
<onos-icon classes="active-sort" [iconSize]="10" [iconId]="sortIcon('alarmAcknowledged')"></onos-icon>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="table-body">
|
||||
<table>
|
||||
<tr class="table-body" *ngIf="tableData.length === 0" class="no-data">
|
||||
<td colspan="9">{{ annots.noRowsMsg }}</td>
|
||||
</tr>
|
||||
<tr *ngFor="let item of tableData | filter : tableDataFilter" (click)="selectCallback($event, item)" [ngClass]="{selected: item.id === selId, 'data-change': isChanged(item.id)}">
|
||||
<td>{{ item.id }}</td>
|
||||
<td>{{ item.alarmDeviceId }}</td>
|
||||
<td>{{ item.alarmDesc }}</td>
|
||||
<td>{{ item.alarmSource }}</td>
|
||||
<td>{{ item.alarmTimeRaised }}</td>
|
||||
<td>{{ item.alarmSeverity }}</td>
|
||||
<td class="table-icon">
|
||||
<onos-icon [iconId]="ackIcon(item.alarmAcknowledged)"></onos-icon>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<onos-alarmdetails class="floatpanels" id="{{ selId }}" (closeEvent)="deselectRow($event)"></onos-alarmdetails>
|
||||
</div>
|
||||
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ActivatedRoute, Params } from '@angular/router';
|
||||
import { DebugElement } from '@angular/core';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { AlarmTableComponent } from './alarmtable.component';
|
||||
import { AlarmDetailsComponent } from '../alarmdetails/alarmdetails.component';
|
||||
import {
|
||||
FnService,
|
||||
IconService,
|
||||
GlyphService,
|
||||
IconComponent,
|
||||
LoadingService,
|
||||
LogService,
|
||||
NavService,
|
||||
MastService,
|
||||
TableFilterPipe,
|
||||
ThemeService,
|
||||
WebSocketService
|
||||
} from 'gui2-fw-lib';
|
||||
|
||||
import { of } from 'rxjs';
|
||||
import { } from 'jasmine';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
|
||||
class MockActivatedRoute extends ActivatedRoute {
|
||||
constructor(params: Params) {
|
||||
super();
|
||||
this.queryParams = of(params);
|
||||
}
|
||||
}
|
||||
|
||||
class MockIconService {
|
||||
loadIconDef() { }
|
||||
}
|
||||
|
||||
class MockLoadingService {
|
||||
startAnim() { }
|
||||
stop() { }
|
||||
waiting() { }
|
||||
}
|
||||
|
||||
describe('AlarmTableComponent', () => {
|
||||
let fs: FnService;
|
||||
let ar: MockActivatedRoute;
|
||||
let windowMock: Window;
|
||||
let logServiceSpy: jasmine.SpyObj<LogService>;
|
||||
let component: AlarmTableComponent;
|
||||
let fixture: ComponentFixture<AlarmTableComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
const logSpy = jasmine.createSpyObj('LogService', ['info', 'debug', 'warn', 'error']);
|
||||
ar = new MockActivatedRoute({ 'debug': 'txrx' });
|
||||
|
||||
windowMock = <any>{
|
||||
location: <any>{
|
||||
hostname: 'foo',
|
||||
host: 'foo',
|
||||
port: '80',
|
||||
protocol: 'http',
|
||||
search: { debug: 'true' },
|
||||
href: 'ws://foo:123/onos/ui/websock/path',
|
||||
absUrl: 'ws://foo:123/onos/ui/websock/path'
|
||||
}
|
||||
};
|
||||
fs = new FnService(ar, logSpy, windowMock);
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
imports: [BrowserAnimationsModule, FormsModule, RouterTestingModule],
|
||||
declarations: [
|
||||
AlarmTableComponent, AlarmDetailsComponent,
|
||||
IconComponent, TableFilterPipe
|
||||
],
|
||||
providers: [
|
||||
{ provide: FnService, useValue: fs },
|
||||
{ provide: LogService, useValue: logSpy },
|
||||
{ provide: LoadingService, useClass: MockLoadingService },
|
||||
{ provide: IconService, useClass: MockIconService },
|
||||
{ provide: 'Window', useValue: windowMock },
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
logServiceSpy = TestBed.get(LogService);
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(AlarmTableComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,185 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
import { Component, Input, Inject, OnInit, OnDestroy } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import {
|
||||
FnService,
|
||||
LoadingService,
|
||||
LogService,
|
||||
WebSocketService,
|
||||
SortDir, TableBaseImpl, TableResponse
|
||||
} from 'gui2-fw-lib';
|
||||
|
||||
/**
|
||||
* Model of the response from the WebSocket
|
||||
*/
|
||||
export interface AlarmTableResponse extends TableResponse {
|
||||
alarmTables: Alarm[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Model of the alarms returned from the WebSocket
|
||||
*/
|
||||
export interface Alarm {
|
||||
id: string;
|
||||
alarmDesc: string;
|
||||
alarmDeviceId: string;
|
||||
alarmSource: string;
|
||||
alarmTimeRaised: string;
|
||||
alarmTimeUpdated: string;
|
||||
alarmTimeCleared: string;
|
||||
alarmSeverity: string;
|
||||
}
|
||||
|
||||
export enum AlarmAction {
|
||||
NONE = 0,
|
||||
ACKNOWLEDGE = 1,
|
||||
CLEAR = 2,
|
||||
}
|
||||
|
||||
/**
|
||||
* Model of the Control Button
|
||||
*/
|
||||
export interface CtrlBtnState {
|
||||
acknowledged: boolean;
|
||||
cleared: boolean;
|
||||
selection: string;
|
||||
}
|
||||
|
||||
const ACKNOWLEDGED = 'ACKNOWLEDGED';
|
||||
const CLEARED = 'CLEARED';
|
||||
|
||||
/**
|
||||
* ONOS GUI -- Alarm Table Component extends TableBaseImpl
|
||||
*/
|
||||
@Component({
|
||||
selector: 'onos-alarmtable',
|
||||
templateUrl: './alarmtable.component.html',
|
||||
styleUrls: ['./alarmtable.component.css', '../../../fw/widget/table.css', '../../../fw/widget/table.theme.css']
|
||||
})
|
||||
export class AlarmTableComponent extends TableBaseImpl implements OnInit, OnDestroy {
|
||||
devId: string;
|
||||
selRowAlarmId: string;
|
||||
// TODO Add in LION translations
|
||||
acknowledgeTip: string = 'Acknowledge';
|
||||
clearTip: string = 'Clear';
|
||||
AlarmActionEnum: any = AlarmAction;
|
||||
alarmAction: AlarmAction = AlarmAction.NONE;
|
||||
confirmMsg: string = '';
|
||||
ctrlBtnState: CtrlBtnState;
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
@Inject('Window') private w: any,
|
||||
protected log: LogService,
|
||||
protected ls: LoadingService,
|
||||
protected fs: FnService,
|
||||
protected wss: WebSocketService,
|
||||
) {
|
||||
super(fs, ls, log, wss, 'alarmTable');
|
||||
|
||||
this.route.queryParams.subscribe(params => {
|
||||
this.devId = params['devId'];
|
||||
|
||||
});
|
||||
|
||||
this.payloadParams = {
|
||||
devId: this.devId
|
||||
};
|
||||
|
||||
this.responseCallback = this.alarmResponseCb;
|
||||
|
||||
this.sortParams = {
|
||||
firstCol: 'alarmTimeRaised',
|
||||
firstDir: SortDir.desc,
|
||||
secondCol: 'alarmDeviceId',
|
||||
secondDir: SortDir.asc,
|
||||
};
|
||||
|
||||
this.ctrlBtnState = <CtrlBtnState>{
|
||||
acknowledged: false,
|
||||
cleared: false
|
||||
};
|
||||
this.log.debug('AlarmTableComponent constructed');
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.init();
|
||||
this.log.debug('AlarmTableComponent initialized');
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.destroy();
|
||||
this.log.debug('AlarmTableComponent destroyed');
|
||||
}
|
||||
|
||||
alarmResponseCb(data: AlarmTableResponse) {
|
||||
this.log.debug('Alarm response received for ', data.alarmTables.length, 'alarm');
|
||||
}
|
||||
|
||||
rowSelection(event: any, selRow: any) {
|
||||
this.ctrlBtnState.acknowledged = this.selId && selRow && selRow.state === ACKNOWLEDGED;
|
||||
this.ctrlBtnState.cleared = this.selId && selRow && selRow.cleared === CLEARED;
|
||||
this.ctrlBtnState.selection = this.selId;
|
||||
this.selRowAlarmId = selRow.appId;
|
||||
this.log.debug('Row ', this.selId, 'selected', this.ctrlBtnState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform one of the alarm actions - acknowledge or clear
|
||||
* Raises a dialog which calls back the dOk() below
|
||||
*/
|
||||
confirmAction(action: AlarmAction): void {
|
||||
this.alarmAction = action;
|
||||
const alarmActionLc = (<string>AlarmAction[this.alarmAction]).toLowerCase();
|
||||
|
||||
this.confirmMsg = alarmActionLc + ' ' + this.selId + '?';
|
||||
|
||||
this.log.debug('Initiating', this.alarmAction, 'of', this.selId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback when the Confirm dialog is shown and a choice is made
|
||||
*/
|
||||
dOk(choice: boolean) {
|
||||
const alarmActionLc = (<string>AlarmAction[this.alarmAction]).toLowerCase();
|
||||
if (choice) {
|
||||
this.log.debug('Confirmed', alarmActionLc, 'on', this.selId);
|
||||
|
||||
/** commented out until backend is implemented
|
||||
this.wss.sendEvent(APPMGMTREQ, {
|
||||
action: alarmActionLc,
|
||||
name: this.selId,
|
||||
sortCol: this.sortParams.firstCol,
|
||||
sortDir: SortDir[this.sortParams.firstDir],
|
||||
});
|
||||
|
||||
this.wss.sendEvent(DETAILSREQ, { id: this.selId });
|
||||
*/
|
||||
} else {
|
||||
this.log.debug('Cancelled', alarmActionLc, 'on', this.selId);
|
||||
}
|
||||
this.confirmMsg = '';
|
||||
}
|
||||
|
||||
ackIcon(acknowledged: string): string {
|
||||
if (acknowledged === 'true') {
|
||||
return 'active';
|
||||
} else {
|
||||
return 'appInactive';
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
import { AlarmTableComponent } from './alarmtable/alarmtable.component';
|
||||
|
||||
const fmRoutes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: AlarmTableComponent
|
||||
}
|
||||
];
|
||||
|
||||
/**
|
||||
* ONOS GUI -- FM GUI Routing Module - allows it to be lazy loaded
|
||||
*
|
||||
* See https://angular.io/guide/lazy-loading-ngmodules
|
||||
*/
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(fmRoutes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class FmGui2LibRoutingModule { }
|
||||
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { FmGui2LibRoutingModule } from './fm-gui2-lib-routing.module';
|
||||
import { Gui2FwLibModule, ConsoleLoggerService, LogService } from 'gui2-fw-lib';
|
||||
import { AlarmTableComponent } from './alarmtable/alarmtable.component';
|
||||
import { AlarmDetailsComponent } from './alarmdetails/alarmdetails.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
FmGui2LibRoutingModule,
|
||||
Gui2FwLibModule
|
||||
],
|
||||
declarations: [ AlarmTableComponent, AlarmDetailsComponent ],
|
||||
exports: [ AlarmTableComponent, AlarmDetailsComponent ],
|
||||
})
|
||||
export class FmGui2LibModule { }
|
||||
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Public API Surface of fm-gui2-lib
|
||||
*/
|
||||
|
||||
export * from './lib/alarmtable/alarmtable.component';
|
||||
export * from './lib/alarmdetails/alarmdetails.component';
|
||||
export * from './lib/fm-gui2-lib.module';
|
||||
export * from './lib/fm-gui2-lib-routing.module';
|
||||
@ -0,0 +1,22 @@
|
||||
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
|
||||
|
||||
import 'core-js/es7/reflect';
|
||||
import 'zone.js/dist/zone';
|
||||
import 'zone.js/dist/zone-testing';
|
||||
import { getTestBed } from '@angular/core/testing';
|
||||
import {
|
||||
BrowserDynamicTestingModule,
|
||||
platformBrowserDynamicTesting
|
||||
} from '@angular/platform-browser-dynamic/testing';
|
||||
|
||||
declare const require: any;
|
||||
|
||||
// First, initialize the Angular testing environment.
|
||||
getTestBed().initTestEnvironment(
|
||||
BrowserDynamicTestingModule,
|
||||
platformBrowserDynamicTesting()
|
||||
);
|
||||
// Then we find all the tests.
|
||||
const context = require.context('./', true, /\.spec\.ts$/);
|
||||
// And load the modules.
|
||||
context.keys().map(context);
|
||||
@ -0,0 +1,33 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../out-tsc/lib",
|
||||
"target": "es2015",
|
||||
"module": "es2015",
|
||||
"moduleResolution": "node",
|
||||
"declaration": true,
|
||||
"sourceMap": true,
|
||||
"inlineSources": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"importHelpers": true,
|
||||
"types": [],
|
||||
"lib": [
|
||||
"dom",
|
||||
"es2015"
|
||||
]
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"annotateForClosureCompiler": true,
|
||||
"skipTemplateCodegen": true,
|
||||
"strictMetadataEmit": true,
|
||||
"fullTemplateTypeCheck": true,
|
||||
"strictInjectionParameters": true,
|
||||
"flatModuleId": "AUTOGENERATED",
|
||||
"flatModuleOutFile": "AUTOGENERATED"
|
||||
},
|
||||
"exclude": [
|
||||
"src/test.ts",
|
||||
"**/*.spec.ts"
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../out-tsc/spec",
|
||||
"types": [
|
||||
"jasmine",
|
||||
"node"
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
"src/test.ts"
|
||||
],
|
||||
"include": [
|
||||
"**/*.spec.ts",
|
||||
"**/*.d.ts"
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
{
|
||||
"extends": "../../tslint.json",
|
||||
"rules": {
|
||||
"directive-selector": [
|
||||
true,
|
||||
"attribute",
|
||||
"onos",
|
||||
"camelCase"
|
||||
],
|
||||
"component-selector": [
|
||||
true,
|
||||
"element",
|
||||
"onos",
|
||||
"kebab-case"
|
||||
]
|
||||
}
|
||||
}
|
||||
47
apps/faultmanagement/fm-gui2-lib/src/app/app.component.html
Normal file
47
apps/faultmanagement/fm-gui2-lib/src/app/app.component.html
Normal file
@ -0,0 +1,47 @@
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
<div style="text-align:center">
|
||||
<onos-icon iconId="nav_apps" iconSize="200" toolTip="Test app"></onos-icon>
|
||||
<h1>
|
||||
Welcome to {{ title }}!
|
||||
</h1>
|
||||
</div>
|
||||
<h2>Test App - Do not use or extend</h2>
|
||||
<p>This app is just a wrapper around the FM GUI2 Library and is
|
||||
necessary only to provide a base for that library.
|
||||
It has a use in validating that the library can be loaded.
|
||||
|
||||
The library is linked in here by the addition of the paths statement in
|
||||
tsconfig.json. This is fine for accessing a local library, but for another
|
||||
project it should be added in through package.json, using the 'file' locator.
|
||||
Then run "npm install" in that target project and the tar will be expanded in
|
||||
to it's node_modules folder.
|
||||
|
||||
A good article on the creation and use of libraries in Angular 6 is given in<br />
|
||||
<a href="https://blog.angularindepth.com/creating-a-library-in-angular-6-87799552e7e5">The Angular Library Series - Creating a Library with the Angular CLI</a><br />
|
||||
and <br />
|
||||
<a href="https://blog.angularindepth.com/creating-a-library-in-angular-6-part-2-6e2bc1e14121">The Angular Library Series - Building and Packaging</a><br />
|
||||
|
||||
This "app" component is not built by Bazel - it's only the library that is built
|
||||
by bazel which in turn calls "ng build --prod fm-gui2-lib" and then "npm pack"
|
||||
resulting in a tar file that can be used as an NPM package anywhere.
|
||||
<br />
|
||||
Note: Please remember that in Angular 6 rebuilding of libraries is not automatic.
|
||||
If you change anything in the library, you will have to build it again before
|
||||
it is picked up in this app.
|
||||
</p>
|
||||
<onos-alarmtable></onos-alarmtable>
|
||||
|
||||
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { TestBed, async } from '@angular/core/testing';
|
||||
import { AppComponent } from './app.component';
|
||||
describe('AppComponent', () => {
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [
|
||||
AppComponent
|
||||
],
|
||||
}).compileComponents();
|
||||
}));
|
||||
it('should create the app', async(() => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.debugElement.componentInstance;
|
||||
expect(app).toBeTruthy();
|
||||
}));
|
||||
it(`should have as title 'app'`, async(() => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.debugElement.componentInstance;
|
||||
expect(app.title).toEqual('app');
|
||||
}));
|
||||
it('should render title in a h1 tag', async(() => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
fixture.detectChanges();
|
||||
const compiled = fixture.debugElement.nativeElement;
|
||||
expect(compiled.querySelector('h1').textContent).toContain('Welcome to app!');
|
||||
}));
|
||||
});
|
||||
26
apps/faultmanagement/fm-gui2-lib/src/app/app.component.ts
Normal file
26
apps/faultmanagement/fm-gui2-lib/src/app/app.component.ts
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.css']
|
||||
})
|
||||
export class AppComponent {
|
||||
title = 'app';
|
||||
}
|
||||
44
apps/faultmanagement/fm-gui2-lib/src/app/app.module.ts
Normal file
44
apps/faultmanagement/fm-gui2-lib/src/app/app.module.ts
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { FmGui2LibModule } from 'fm-gui2-lib';
|
||||
import { AppComponent } from './app.component';
|
||||
import { Gui2FwLibModule, ConsoleLoggerService, LogService } from 'gui2-fw-lib';
|
||||
|
||||
const appRoutes: Routes = [
|
||||
{ path: '**', component: AppComponent }
|
||||
]
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent
|
||||
],
|
||||
imports: [
|
||||
RouterModule.forRoot(appRoutes),
|
||||
BrowserModule,
|
||||
FmGui2LibModule,
|
||||
Gui2FwLibModule
|
||||
],
|
||||
providers: [
|
||||
{ provide: 'Window', useValue: window },
|
||||
{ provide: LogService, useClass: ConsoleLoggerService },
|
||||
],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
export class AppModule { }
|
||||
9
apps/faultmanagement/fm-gui2-lib/src/browserslist
Normal file
9
apps/faultmanagement/fm-gui2-lib/src/browserslist
Normal file
@ -0,0 +1,9 @@
|
||||
# This file is currently used by autoprefixer to adjust CSS to support the below specified browsers
|
||||
# For additional information regarding the format and rule options, please see:
|
||||
# https://github.com/browserslist/browserslist#queries
|
||||
# For IE 9-11 support, please uncomment the last line of the file and adjust as needed
|
||||
> 0.5%
|
||||
last 2 versions
|
||||
Firefox ESR
|
||||
not dead
|
||||
# IE 9-11
|
||||
@ -0,0 +1,3 @@
|
||||
export const environment = {
|
||||
production: true
|
||||
};
|
||||
@ -0,0 +1,15 @@
|
||||
// This file can be replaced during build by using the `fileReplacements` array.
|
||||
// `ng build ---prod` replaces `environment.ts` with `environment.prod.ts`.
|
||||
// The list of file replacements can be found in `angular.json`.
|
||||
|
||||
export const environment = {
|
||||
production: false
|
||||
};
|
||||
|
||||
/*
|
||||
* In development mode, to ignore zone related error stack frames such as
|
||||
* `zone.run`, `zoneDelegate.invokeTask` for easier debugging, you can
|
||||
* import the following file, but please comment it out in production mode
|
||||
* because it will have performance impact when throw error
|
||||
*/
|
||||
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.
|
||||
BIN
apps/faultmanagement/fm-gui2-lib/src/favicon.ico
Normal file
BIN
apps/faultmanagement/fm-gui2-lib/src/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.3 KiB |
14
apps/faultmanagement/fm-gui2-lib/src/index.html
Normal file
14
apps/faultmanagement/fm-gui2-lib/src/index.html
Normal file
@ -0,0 +1,14 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>FmGui2LibApp</title>
|
||||
<base href="/">
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||
</head>
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
</body>
|
||||
</html>
|
||||
31
apps/faultmanagement/fm-gui2-lib/src/karma.conf.js
Normal file
31
apps/faultmanagement/fm-gui2-lib/src/karma.conf.js
Normal file
@ -0,0 +1,31 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
basePath: '',
|
||||
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||
plugins: [
|
||||
require('karma-jasmine'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-jasmine-html-reporter'),
|
||||
require('karma-coverage-istanbul-reporter'),
|
||||
require('@angular-devkit/build-angular/plugins/karma')
|
||||
],
|
||||
client: {
|
||||
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
||||
},
|
||||
coverageIstanbulReporter: {
|
||||
dir: require('path').join(__dirname, '../coverage'),
|
||||
reports: ['html', 'lcovonly'],
|
||||
fixWebpackSourcePaths: true
|
||||
},
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
singleRun: false
|
||||
});
|
||||
};
|
||||
12
apps/faultmanagement/fm-gui2-lib/src/main.ts
Normal file
12
apps/faultmanagement/fm-gui2-lib/src/main.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { enableProdMode } from '@angular/core';
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
|
||||
import { AppModule } from './app/app.module';
|
||||
import { environment } from './environments/environment';
|
||||
|
||||
if (environment.production) {
|
||||
enableProdMode();
|
||||
}
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule)
|
||||
.catch(err => console.log(err));
|
||||
80
apps/faultmanagement/fm-gui2-lib/src/polyfills.ts
Normal file
80
apps/faultmanagement/fm-gui2-lib/src/polyfills.ts
Normal file
@ -0,0 +1,80 @@
|
||||
/**
|
||||
* This file includes polyfills needed by Angular and is loaded before the app.
|
||||
* You can add your own extra polyfills to this file.
|
||||
*
|
||||
* This file is divided into 2 sections:
|
||||
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
|
||||
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
|
||||
* file.
|
||||
*
|
||||
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
|
||||
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
|
||||
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
|
||||
*
|
||||
* Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
|
||||
*/
|
||||
|
||||
/***************************************************************************************************
|
||||
* BROWSER POLYFILLS
|
||||
*/
|
||||
|
||||
/** IE9, IE10 and IE11 requires all of the following polyfills. **/
|
||||
// import 'core-js/es6/symbol';
|
||||
// import 'core-js/es6/object';
|
||||
// import 'core-js/es6/function';
|
||||
// import 'core-js/es6/parse-int';
|
||||
// import 'core-js/es6/parse-float';
|
||||
// import 'core-js/es6/number';
|
||||
// import 'core-js/es6/math';
|
||||
// import 'core-js/es6/string';
|
||||
// import 'core-js/es6/date';
|
||||
// import 'core-js/es6/array';
|
||||
// import 'core-js/es6/regexp';
|
||||
// import 'core-js/es6/map';
|
||||
// import 'core-js/es6/weak-map';
|
||||
// import 'core-js/es6/set';
|
||||
|
||||
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
|
||||
// import 'classlist.js'; // Run `npm install --save classlist.js`.
|
||||
|
||||
/** IE10 and IE11 requires the following for the Reflect API. */
|
||||
// import 'core-js/es6/reflect';
|
||||
|
||||
|
||||
/** Evergreen browsers require these. **/
|
||||
// Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove.
|
||||
import 'core-js/es7/reflect';
|
||||
|
||||
|
||||
/**
|
||||
* Web Animations `@angular/platform-browser/animations`
|
||||
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
|
||||
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
|
||||
**/
|
||||
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
|
||||
|
||||
/**
|
||||
* By default, zone.js will patch all possible macroTask and DomEvents
|
||||
* user can disable parts of macroTask/DomEvents patch by setting following flags
|
||||
*/
|
||||
|
||||
// (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
|
||||
// (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
|
||||
// (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
|
||||
|
||||
/*
|
||||
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
|
||||
* with the following flag, it will bypass `zone.js` patch for IE/Edge
|
||||
*/
|
||||
// (window as any).__Zone_enable_cross_context_check = true;
|
||||
|
||||
/***************************************************************************************************
|
||||
* Zone JS is required by default for Angular itself.
|
||||
*/
|
||||
import 'zone.js/dist/zone'; // Included with Angular CLI.
|
||||
|
||||
|
||||
|
||||
/***************************************************************************************************
|
||||
* APPLICATION IMPORTS
|
||||
*/
|
||||
1
apps/faultmanagement/fm-gui2-lib/src/styles.css
Normal file
1
apps/faultmanagement/fm-gui2-lib/src/styles.css
Normal file
@ -0,0 +1 @@
|
||||
/* You can add global styles to this file, and also import other style files */
|
||||
20
apps/faultmanagement/fm-gui2-lib/src/test.ts
Normal file
20
apps/faultmanagement/fm-gui2-lib/src/test.ts
Normal file
@ -0,0 +1,20 @@
|
||||
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
|
||||
|
||||
import 'zone.js/dist/zone-testing';
|
||||
import { getTestBed } from '@angular/core/testing';
|
||||
import {
|
||||
BrowserDynamicTestingModule,
|
||||
platformBrowserDynamicTesting
|
||||
} from '@angular/platform-browser-dynamic/testing';
|
||||
|
||||
declare const require: any;
|
||||
|
||||
// First, initialize the Angular testing environment.
|
||||
getTestBed().initTestEnvironment(
|
||||
BrowserDynamicTestingModule,
|
||||
platformBrowserDynamicTesting()
|
||||
);
|
||||
// Then we find all the tests.
|
||||
const context = require.context('./', true, /\.spec\.ts$/);
|
||||
// And load the modules.
|
||||
context.keys().map(context);
|
||||
12
apps/faultmanagement/fm-gui2-lib/src/tsconfig.app.json
Normal file
12
apps/faultmanagement/fm-gui2-lib/src/tsconfig.app.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../out-tsc/app",
|
||||
"module": "es2015",
|
||||
"types": []
|
||||
},
|
||||
"exclude": [
|
||||
"src/test.ts",
|
||||
"**/*.spec.ts"
|
||||
]
|
||||
}
|
||||
19
apps/faultmanagement/fm-gui2-lib/src/tsconfig.spec.json
Normal file
19
apps/faultmanagement/fm-gui2-lib/src/tsconfig.spec.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../out-tsc/spec",
|
||||
"module": "commonjs",
|
||||
"types": [
|
||||
"jasmine",
|
||||
"node"
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
"test.ts",
|
||||
"polyfills.ts"
|
||||
],
|
||||
"include": [
|
||||
"**/*.spec.ts",
|
||||
"**/*.d.ts"
|
||||
]
|
||||
}
|
||||
17
apps/faultmanagement/fm-gui2-lib/src/tslint.json
Normal file
17
apps/faultmanagement/fm-gui2-lib/src/tslint.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"extends": "../tslint.json",
|
||||
"rules": {
|
||||
"directive-selector": [
|
||||
true,
|
||||
"attribute",
|
||||
"app",
|
||||
"camelCase"
|
||||
],
|
||||
"component-selector": [
|
||||
true,
|
||||
"element",
|
||||
"app",
|
||||
"kebab-case"
|
||||
]
|
||||
}
|
||||
}
|
||||
28
apps/faultmanagement/fm-gui2-lib/tsconfig.json
Normal file
28
apps/faultmanagement/fm-gui2-lib/tsconfig.json
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"outDir": "./dist/out-tsc",
|
||||
"sourceMap": true,
|
||||
"declaration": false,
|
||||
"moduleResolution": "node",
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"target": "es6",
|
||||
"typeRoots": [
|
||||
"node_modules/@types"
|
||||
],
|
||||
"lib": [
|
||||
"es2017",
|
||||
"dom"
|
||||
],
|
||||
"paths": {
|
||||
"fm-gui2-lib": [
|
||||
"dist/fm-gui2-lib"
|
||||
],
|
||||
"fm-gui2-lib/*": [
|
||||
"dist/fm-gui2-lib/*"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
130
apps/faultmanagement/fm-gui2-lib/tslint.json
Normal file
130
apps/faultmanagement/fm-gui2-lib/tslint.json
Normal file
@ -0,0 +1,130 @@
|
||||
{
|
||||
"rulesDirectory": [
|
||||
"node_modules/codelyzer"
|
||||
],
|
||||
"rules": {
|
||||
"arrow-return-shorthand": true,
|
||||
"callable-types": true,
|
||||
"class-name": true,
|
||||
"comment-format": [
|
||||
true,
|
||||
"check-space"
|
||||
],
|
||||
"curly": true,
|
||||
"deprecation": {
|
||||
"severity": "warn"
|
||||
},
|
||||
"eofline": true,
|
||||
"forin": true,
|
||||
"import-blacklist": [
|
||||
true,
|
||||
"rxjs/Rx"
|
||||
],
|
||||
"import-spacing": true,
|
||||
"indent": [
|
||||
true,
|
||||
"spaces"
|
||||
],
|
||||
"interface-over-type-literal": true,
|
||||
"label-position": true,
|
||||
"max-line-length": [
|
||||
true,
|
||||
140
|
||||
],
|
||||
"member-access": false,
|
||||
"member-ordering": [
|
||||
true,
|
||||
{
|
||||
"order": [
|
||||
"static-field",
|
||||
"instance-field",
|
||||
"static-method",
|
||||
"instance-method"
|
||||
]
|
||||
}
|
||||
],
|
||||
"no-arg": true,
|
||||
"no-bitwise": true,
|
||||
"no-console": [
|
||||
true,
|
||||
"debug",
|
||||
"info",
|
||||
"time",
|
||||
"timeEnd",
|
||||
"trace"
|
||||
],
|
||||
"no-construct": true,
|
||||
"no-debugger": true,
|
||||
"no-duplicate-super": true,
|
||||
"no-empty": false,
|
||||
"no-empty-interface": true,
|
||||
"no-eval": true,
|
||||
"no-inferrable-types": [
|
||||
false,
|
||||
"ignore-params"
|
||||
],
|
||||
"no-misused-new": true,
|
||||
"no-non-null-assertion": true,
|
||||
"no-shadowed-variable": true,
|
||||
"no-string-literal": false,
|
||||
"no-string-throw": true,
|
||||
"no-switch-case-fall-through": true,
|
||||
"no-trailing-whitespace": true,
|
||||
"no-unnecessary-initializer": true,
|
||||
"no-unused-expression": true,
|
||||
"no-use-before-declare": true,
|
||||
"no-var-keyword": true,
|
||||
"object-literal-sort-keys": false,
|
||||
"one-line": [
|
||||
true,
|
||||
"check-open-brace",
|
||||
"check-catch",
|
||||
"check-else",
|
||||
"check-whitespace"
|
||||
],
|
||||
"prefer-const": true,
|
||||
"quotemark": [
|
||||
true,
|
||||
"single"
|
||||
],
|
||||
"radix": true,
|
||||
"semicolon": [
|
||||
true,
|
||||
"always"
|
||||
],
|
||||
"triple-equals": [
|
||||
true,
|
||||
"allow-null-check"
|
||||
],
|
||||
"typedef-whitespace": [
|
||||
true,
|
||||
{
|
||||
"call-signature": "nospace",
|
||||
"index-signature": "nospace",
|
||||
"parameter": "nospace",
|
||||
"property-declaration": "nospace",
|
||||
"variable-declaration": "nospace"
|
||||
}
|
||||
],
|
||||
"unified-signatures": true,
|
||||
"variable-name": false,
|
||||
"whitespace": [
|
||||
true,
|
||||
"check-branch",
|
||||
"check-decl",
|
||||
"check-operator",
|
||||
"check-separator",
|
||||
"check-type"
|
||||
],
|
||||
"no-output-on-prefix": true,
|
||||
"use-input-property-decorator": true,
|
||||
"use-output-property-decorator": true,
|
||||
"use-host-property-decorator": true,
|
||||
"no-input-rename": true,
|
||||
"no-output-rename": true,
|
||||
"use-life-cycle-interface": true,
|
||||
"use-pipe-transform-interface": true,
|
||||
"component-class-suffix": true,
|
||||
"directive-class-suffix": true
|
||||
}
|
||||
}
|
||||
@ -7,6 +7,7 @@ COMPILE_DEPS = [
|
||||
'//lib:javax.ws.rs-api',
|
||||
'//utils/osgi:onlab-osgi',
|
||||
'//core/store/serializers:onos-core-serializers',
|
||||
'//apps/faultmanagement/fmmgr:onos-apps-faultmanagement-fmmgr'
|
||||
]
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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.faultmanagement.alarms.cli;
|
||||
|
||||
import org.apache.karaf.shell.api.action.Argument;
|
||||
import org.apache.karaf.shell.api.action.Command;
|
||||
import org.apache.karaf.shell.api.action.lifecycle.Service;
|
||||
import org.onosproject.cli.AbstractShellCommand;
|
||||
import org.onosproject.faultmanagement.api.AlarmStore;
|
||||
import org.onosproject.incubator.net.faultmanagement.alarm.Alarm;
|
||||
import org.onosproject.incubator.net.faultmanagement.alarm.AlarmId;
|
||||
import org.onosproject.incubator.net.faultmanagement.alarm.DefaultAlarm;
|
||||
import org.onosproject.net.DeviceId;
|
||||
import org.onosproject.net.device.DeviceService;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* Creates a default alarm on a device.
|
||||
*/
|
||||
@Service
|
||||
@Command(scope = "onos", name = "alarm-create",
|
||||
description = "Creates an alarm")
|
||||
public class CreateAlarm extends AbstractShellCommand {
|
||||
|
||||
@Argument(index = 0, name = "deviceId", description = "Device identity",
|
||||
required = true, multiValued = false)
|
||||
String deviceIdStr = null;
|
||||
|
||||
@Argument(index = 1, name = "severity", description = "Severity level",
|
||||
required = true, multiValued = false)
|
||||
String severityStr = null;
|
||||
|
||||
@Argument(index = 2, name = "alarmId", description = "Unique alarm id",
|
||||
required = true, multiValued = false)
|
||||
String alarmId = null;
|
||||
|
||||
@Argument(index = 3, name = "desc", description = "Alarm description",
|
||||
required = true, multiValued = false)
|
||||
String desc = null;
|
||||
|
||||
private AlarmStore alarmStore = AbstractShellCommand.get(AlarmStore.class);
|
||||
|
||||
private DeviceService deviceManager = AbstractShellCommand.get(DeviceService.class);
|
||||
|
||||
@Override
|
||||
protected void doExecute() {
|
||||
DeviceId deviceId = DeviceId.deviceId(deviceIdStr);
|
||||
if (!deviceManager.isAvailable(deviceId)) {
|
||||
throw new IllegalArgumentException("Device " + deviceIdStr + " is not available");
|
||||
}
|
||||
|
||||
Alarm.SeverityLevel severityLevel = Alarm.SeverityLevel.valueOf(severityStr);
|
||||
|
||||
Alarm newAlarm = new DefaultAlarm.Builder(
|
||||
AlarmId.alarmId(deviceId, alarmId),
|
||||
deviceId,
|
||||
desc,
|
||||
severityLevel,
|
||||
Instant.now().toEpochMilli())
|
||||
.build();
|
||||
alarmStore.createOrUpdateAlarm(newAlarm);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.faultmanagement.alarms.cli;
|
||||
|
||||
import org.apache.karaf.shell.api.action.Argument;
|
||||
import org.apache.karaf.shell.api.action.Command;
|
||||
import org.onosproject.cli.AbstractShellCommand;
|
||||
import org.onosproject.faultmanagement.api.AlarmStore;
|
||||
import org.onosproject.incubator.net.faultmanagement.alarm.AlarmId;
|
||||
|
||||
/**
|
||||
* Remove an alarm from the Alarm Store.
|
||||
*/
|
||||
@Command(scope = "onos", name = "alarm-remove",
|
||||
description = "Removes an alarm")
|
||||
public class RemoveAlarm extends AbstractShellCommand {
|
||||
@Argument(index = 0, name = "alarmId", description = "Unique alarm id",
|
||||
required = true, multiValued = false)
|
||||
String alarmId = null;
|
||||
|
||||
private AlarmStore alarmStore = AbstractShellCommand.get(AlarmStore.class);
|
||||
|
||||
@Override
|
||||
protected void doExecute() {
|
||||
alarmStore.removeAlarm(AlarmId.alarmId(alarmId));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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.faultmanagement.alarms.cli;
|
||||
|
||||
import org.apache.karaf.shell.api.action.Argument;
|
||||
import org.apache.karaf.shell.api.action.Command;
|
||||
import org.apache.karaf.shell.api.action.lifecycle.Service;
|
||||
import org.onosproject.cli.AbstractShellCommand;
|
||||
import org.onosproject.faultmanagement.api.AlarmStore;
|
||||
import org.onosproject.incubator.net.faultmanagement.alarm.Alarm;
|
||||
import org.onosproject.incubator.net.faultmanagement.alarm.AlarmEntityId;
|
||||
import org.onosproject.incubator.net.faultmanagement.alarm.AlarmId;
|
||||
import org.onosproject.incubator.net.faultmanagement.alarm.DefaultAlarm;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* Updates an existing alarm.
|
||||
*/
|
||||
@Command(scope = "onos", name = "alarm-update",
|
||||
description = "Updates an alarm")
|
||||
@Service
|
||||
public class UpdateAlarm extends AbstractShellCommand {
|
||||
|
||||
@Argument(index = 0, name = "alarmId", description = "Unique alarm id",
|
||||
required = true, multiValued = false)
|
||||
String alarmId = null;
|
||||
|
||||
@Argument(index = 1, name = "desc", description = "Alarm field",
|
||||
required = true, multiValued = false)
|
||||
String alarmField = null;
|
||||
|
||||
@Argument(index = 2, name = "value", description = "The new value of the chosen Alarm field.",
|
||||
required = true, multiValued = false)
|
||||
String alarmFieldValue = null;
|
||||
|
||||
private AlarmStore alarmStore = AbstractShellCommand.get(AlarmStore.class);
|
||||
|
||||
@Override
|
||||
protected void doExecute() {
|
||||
Alarm existing = alarmStore.getAlarm(AlarmId.alarmId(alarmId));
|
||||
|
||||
DefaultAlarm.Builder newAlarmBuilder = new DefaultAlarm.Builder(existing);
|
||||
UpdateAlarm.AlarmField field = UpdateAlarm.AlarmField.valueOf(alarmField);
|
||||
switch (field) {
|
||||
case SOURCE:
|
||||
AlarmEntityId sourceId = AlarmEntityId.alarmEntityId(alarmFieldValue);
|
||||
newAlarmBuilder.forSource(sourceId);
|
||||
break;
|
||||
case ASSIGNED_USER:
|
||||
newAlarmBuilder.withAssignedUser(alarmFieldValue);
|
||||
break;
|
||||
case ACKNOWLEDGED:
|
||||
newAlarmBuilder.withAcknowledged("TRUE".equalsIgnoreCase(alarmFieldValue));
|
||||
break;
|
||||
case MANUALLY_CLEARABLE:
|
||||
newAlarmBuilder.withManuallyClearable("TRUE".equalsIgnoreCase(alarmFieldValue));
|
||||
break;
|
||||
case SERVICE_AFFECTING:
|
||||
newAlarmBuilder.withServiceAffecting("TRUE".equalsIgnoreCase(alarmFieldValue));
|
||||
break;
|
||||
case TIME_CLEARED:
|
||||
newAlarmBuilder.clear();
|
||||
newAlarmBuilder.withTimeCleared(Instant.parse(alarmFieldValue).toEpochMilli());
|
||||
break;
|
||||
case TIME_UPDATED:
|
||||
default:
|
||||
newAlarmBuilder.withTimeUpdated(Instant.parse(alarmFieldValue).toEpochMilli());
|
||||
break;
|
||||
}
|
||||
alarmStore.createOrUpdateAlarm(newAlarmBuilder.build());
|
||||
}
|
||||
|
||||
public enum AlarmField {
|
||||
SOURCE,
|
||||
ASSIGNED_USER,
|
||||
ACKNOWLEDGED,
|
||||
MANUALLY_CLEARABLE,
|
||||
SERVICE_AFFECTING,
|
||||
TIME_CLEARED,
|
||||
TIME_UPDATED
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.faultmanagement.alarms.cli.completer;
|
||||
|
||||
import org.onosproject.cli.AbstractChoicesCompleter;
|
||||
import org.onosproject.faultmanagement.alarms.cli.UpdateAlarm;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* CLI completer for Alarm Severity.
|
||||
*/
|
||||
public class AlarmFieldCompleter extends AbstractChoicesCompleter {
|
||||
@Override
|
||||
public List<String> choices() {
|
||||
List<String> choices = new ArrayList<>();
|
||||
|
||||
Arrays.asList(UpdateAlarm.AlarmField.values()).forEach(field -> choices.add(field.toString()));
|
||||
|
||||
return choices;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.faultmanagement.alarms.cli.completer;
|
||||
|
||||
import org.apache.karaf.shell.api.action.lifecycle.Service;
|
||||
import org.onosproject.cli.AbstractChoicesCompleter;
|
||||
import org.onosproject.faultmanagement.alarms.cli.UpdateAlarm;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* CLI completer for Alarm Field values.
|
||||
*/
|
||||
@Service
|
||||
public class AlarmFieldValueCompleter extends AbstractChoicesCompleter {
|
||||
@Override
|
||||
protected List<String> choices() {
|
||||
|
||||
List<String> choices = new ArrayList<>();
|
||||
UpdateAlarm.AlarmField field = UpdateAlarm.AlarmField.valueOf(commandLine.getArguments()[2]);
|
||||
|
||||
switch (field) {
|
||||
case ACKNOWLEDGED:
|
||||
case MANUALLY_CLEARABLE:
|
||||
case SERVICE_AFFECTING:
|
||||
choices.add("TRUE");
|
||||
choices.add("FALSE");
|
||||
return choices;
|
||||
case TIME_CLEARED:
|
||||
case TIME_UPDATED:
|
||||
choices.add(Instant.now().toString());
|
||||
default:
|
||||
return choices;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.faultmanagement.alarms.cli.completer;
|
||||
|
||||
import org.onosproject.cli.AbstractChoicesCompleter;
|
||||
import static org.onosproject.cli.AbstractShellCommand.get;
|
||||
import org.onosproject.faultmanagement.api.AlarmStore;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* CLI completer for Alarm Id.
|
||||
*/
|
||||
public class AlarmIdCompleter extends AbstractChoicesCompleter {
|
||||
@Override
|
||||
public List<String> choices() {
|
||||
List<String> choices = new ArrayList<>();
|
||||
|
||||
AlarmStore alarmStore = get(AlarmStore.class);
|
||||
alarmStore.getAlarms().forEach((alarm -> choices.add(alarm.id().toString())));
|
||||
|
||||
return choices;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.faultmanagement.alarms.cli.completer;
|
||||
|
||||
import org.onosproject.cli.AbstractChoicesCompleter;
|
||||
import org.onosproject.incubator.net.faultmanagement.alarm.Alarm;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* CLI completer for Alarm Severity.
|
||||
*/
|
||||
public class AlarmSeverityCompleter extends AbstractChoicesCompleter {
|
||||
@Override
|
||||
public List<String> choices() {
|
||||
List<String> severityList = new ArrayList<>();
|
||||
|
||||
Arrays.asList(Alarm.SeverityLevel.values()).forEach(s -> severityList.add(s.toString()));
|
||||
|
||||
return severityList;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* CLI completers for alarms.
|
||||
*/
|
||||
package org.onosproject.faultmanagement.alarms.cli.completer;
|
||||
@ -29,8 +29,33 @@
|
||||
<ref component-id="deviceIdCompleter"/>
|
||||
</completers>
|
||||
</command>
|
||||
<command>
|
||||
<action class="org.onosproject.faultmanagement.alarms.cli.CreateAlarm"/>
|
||||
<completers>
|
||||
<ref component-id="deviceIdCompleter"/>
|
||||
<ref component-id="alarmSeverityCompleter"/>
|
||||
</completers>
|
||||
</command>
|
||||
<command>
|
||||
<action class="org.onosproject.faultmanagement.alarms.cli.UpdateAlarm"/>
|
||||
<completers>
|
||||
<ref component-id="alarmIdCompleter"/>
|
||||
<ref component-id="alarmFieldCompleter"/>
|
||||
<ref component-id="alarmFieldValueCompleter"/>
|
||||
</completers>
|
||||
</command>
|
||||
<command>
|
||||
<action class="org.onosproject.faultmanagement.alarms.cli.RemoveAlarm"/>
|
||||
<completers>
|
||||
<ref component-id="alarmIdCompleter"/>
|
||||
</completers>
|
||||
</command>
|
||||
</command-bundle>
|
||||
|
||||
<bean id="deviceIdCompleter" class="org.onosproject.cli.net.DeviceIdCompleter"/>
|
||||
<bean id="alarmIdCompleter" class="org.onosproject.faultmanagement.alarms.cli.completer.AlarmIdCompleter"/>
|
||||
<bean id="alarmSeverityCompleter" class="org.onosproject.faultmanagement.alarms.cli.completer.AlarmSeverityCompleter"/>
|
||||
<bean id="alarmFieldCompleter" class="org.onosproject.faultmanagement.alarms.cli.completer.AlarmFieldCompleter"/>
|
||||
<bean id="alarmFieldValueCompleter" class="org.onosproject.faultmanagement.alarms.cli.completer.AlarmFieldValueCompleter"/>
|
||||
|
||||
</blueprint>
|
||||
|
||||
@ -55,6 +55,11 @@ public class AlarmTableMessageHandler extends UiMessageHandler {
|
||||
private static final String TIME_UPDATED = "alarmTimeUpdated";
|
||||
private static final String TIME_CLEARED = "alarmTimeCleared";
|
||||
private static final String SEVERITY = "alarmSeverity";
|
||||
private static final String SERVICE_AFFECTING = "alarmServiceAffecting";
|
||||
private static final String ACKNOWLEDGED = "alarmAcknowledged";
|
||||
private static final String CLEARED = "alarmCleared";
|
||||
private static final String MANUALLY_CLEARABLE = "alarmManuallyClearable";
|
||||
private static final String ASSIGNED_USER = "alarmAssignedUser";
|
||||
private static final String RESULT = "result";
|
||||
|
||||
// TODO No need to show id column in ONOS-GUI
|
||||
@ -63,7 +68,8 @@ public class AlarmTableMessageHandler extends UiMessageHandler {
|
||||
// e.g. red=critical, green=cleared etc
|
||||
|
||||
private static final String[] COLUMN_IDS = {
|
||||
ID, DEVICE_ID_STR, DESCRIPTION, SOURCE, TIME_RAISED, SEVERITY
|
||||
ID, DEVICE_ID_STR, DESCRIPTION, SOURCE, TIME_RAISED, SEVERITY,
|
||||
MANUALLY_CLEARABLE, CLEARED, ACKNOWLEDGED
|
||||
};
|
||||
|
||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
||||
@ -127,7 +133,10 @@ public class AlarmTableMessageHandler extends UiMessageHandler {
|
||||
.cell(DESCRIPTION, alarm.description())
|
||||
.cell(SOURCE, alarm.source())
|
||||
.cell(TIME_RAISED, Instant.ofEpochMilli(alarm.timeRaised()))
|
||||
.cell(SEVERITY, alarm.severity());
|
||||
.cell(SEVERITY, alarm.severity())
|
||||
.cell(MANUALLY_CLEARABLE, alarm.manuallyClearable())
|
||||
.cell(CLEARED, alarm.cleared())
|
||||
.cell(ACKNOWLEDGED, alarm.acknowledged());
|
||||
}
|
||||
}
|
||||
|
||||
@ -166,6 +175,14 @@ public class AlarmTableMessageHandler extends UiMessageHandler {
|
||||
data.put(TIME_UPDATED, formatTime(alarm.timeUpdated()));
|
||||
data.put(TIME_CLEARED, formatTime(alarm.timeCleared()));
|
||||
data.put(SEVERITY, alarm.severity().toString());
|
||||
// TODO: The following should be change to %yes% and %no% if LION is added to Alarm View
|
||||
data.put(SERVICE_AFFECTING,
|
||||
alarm.serviceAffecting() ? "Yes" : "No");
|
||||
data.put(ACKNOWLEDGED,
|
||||
alarm.acknowledged() ? "Yes" : "No");
|
||||
data.put(CLEARED,
|
||||
alarm.cleared() ? "Yes" : "No");
|
||||
data.put(ASSIGNED_USER, alarm.assignedUser());
|
||||
}
|
||||
log.debug("send = {}", rootNode);
|
||||
|
||||
|
||||
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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.openstacknetworking.cli;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.apache.karaf.shell.api.action.Command;
|
||||
import org.apache.karaf.shell.api.action.lifecycle.Service;
|
||||
import org.onosproject.cli.AbstractShellCommand;
|
||||
import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
|
||||
import org.openstack4j.model.network.Subnet;
|
||||
import org.openstack4j.openstack.networking.domain.NeutronSubnet;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.modelEntityToJson;
|
||||
import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.prettyJson;
|
||||
|
||||
/**
|
||||
* Lists OpenStack subnets.
|
||||
*/
|
||||
@Service
|
||||
@Command(scope = "onos", name = "openstack-subnets",
|
||||
description = "Lists all OpenStack subnets")
|
||||
public class OpenstackSubnetListCommand extends AbstractShellCommand {
|
||||
|
||||
private static final String FORMAT = "%-40s%-20s%-20s%-20s%-40s%-20s%-8s";
|
||||
|
||||
@Override
|
||||
protected void doExecute() {
|
||||
OpenstackNetworkService service = AbstractShellCommand.get(OpenstackNetworkService.class);
|
||||
List<Subnet> subnets = Lists.newArrayList(service.subnets());
|
||||
subnets.sort(Comparator.comparing(Subnet::getName));
|
||||
|
||||
if (outputJson()) {
|
||||
print("%s", json(subnets));
|
||||
} else {
|
||||
print(FORMAT, "ID", "Name", "CIDR", "GatewayIp", "NetworkId", "NetworkName", "HostRoutes");
|
||||
|
||||
for (Subnet subnet: subnets) {
|
||||
print(FORMAT,
|
||||
subnet.getId(),
|
||||
subnet.getName(),
|
||||
subnet.getCidr(),
|
||||
subnet.getGateway(),
|
||||
subnet.getNetworkId(),
|
||||
service.network(subnet.getNetworkId()).getName(),
|
||||
subnet.getHostRoutes());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String json(List<Subnet> subnets) {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
ArrayNode result = mapper.createArrayNode();
|
||||
for (Subnet net: subnets) {
|
||||
result.add(modelEntityToJson(net, NeutronSubnet.class));
|
||||
}
|
||||
return prettyJson(mapper, result.toString());
|
||||
}
|
||||
}
|
||||
@ -216,7 +216,8 @@ public final class OpenstackNetworkingUtil {
|
||||
if (Strings.isNullOrEmpty(fip.getFloatingIpAddress())) {
|
||||
continue;
|
||||
}
|
||||
if (fip.getFixedIpAddress().equals(port.ipAddress().toString())) {
|
||||
if (fip.getFixedIpAddress().equals(port.ipAddress().toString()) &&
|
||||
fip.getPortId().equals(port.portId())) {
|
||||
return fip;
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,6 +18,9 @@
|
||||
<command>
|
||||
<action class="org.onosproject.openstacknetworking.cli.OpenstackNetworkListCommand"/>
|
||||
</command>
|
||||
<command>
|
||||
<action class="org.onosproject.openstacknetworking.cli.OpenstackSubnetListCommand"/>
|
||||
</command>
|
||||
<command>
|
||||
<action class="org.onosproject.openstacknetworking.cli.OpenstackPortListCommand"/>
|
||||
</command>
|
||||
|
||||
@ -3,6 +3,19 @@ GRPC_VER = '1.3.1'
|
||||
BUNDLES = [
|
||||
'//lib:kafka-clients',
|
||||
'//lib:influxdb-java',
|
||||
'//lib:simpleclient',
|
||||
'//lib:simpleclient_common',
|
||||
'//lib:simpleclient_hotspot',
|
||||
'//lib:simpleclient_servlet',
|
||||
'//lib:jetty-http',
|
||||
'//lib:jetty-servlet',
|
||||
'//lib:jetty-server',
|
||||
'//lib:jetty-security',
|
||||
'//lib:jetty-util',
|
||||
'//lib:jetty-io',
|
||||
'//lib:servlet-api',
|
||||
'//lib:javax.ws.rs-api',
|
||||
'//lib:jetty-websocket',
|
||||
'//lib:commons-codec',
|
||||
'//lib:retrofit',
|
||||
'//lib:okhttp',
|
||||
|
||||
@ -42,6 +42,10 @@ public final class Constants {
|
||||
public static final String DEFAULT_INFLUXDB_MEASUREMENT = "sonaflow";
|
||||
public static final boolean DEFAULT_INFLUXDB_ENABLE_BATCH = true;
|
||||
|
||||
// default configuration variables for Promethetus exporter
|
||||
public static final String DEFAULT_PROMETHEUS_EXPORTER_IP = "0.0.0.0";
|
||||
public static final int DEFAULT_PROMETHEUS_EXPORTER_PORT = 9501;
|
||||
|
||||
// default configuration variables for Kafka
|
||||
public static final String DEFAULT_KAFKA_SERVER_IP = DEFAULT_SERVER_IP;
|
||||
public static final int DEFAULT_KAFKA_SERVER_PORT = 9092;
|
||||
|
||||
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.openstacktelemetry.api;
|
||||
|
||||
/**
|
||||
* Admin service API for publishing openstack telemetry through Prometheus producer.
|
||||
*/
|
||||
public interface PrometheusTelemetryAdminService
|
||||
extends TelemetryAdminService, PrometheusTelemetryService {
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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.openstacktelemetry.api;
|
||||
|
||||
/**
|
||||
* Configuration service API for publishing openstack telemetry through Prometheus producer.
|
||||
*/
|
||||
public interface PrometheusTelemetryConfigService extends TelemetryConfigService {
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.openstacktelemetry.api;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Service API for publishing openstack telemetry through Prometheus producer.
|
||||
*/
|
||||
public interface PrometheusTelemetryService extends TelemetryService {
|
||||
/**
|
||||
* Publishes openstack telemetry to Prometheus server.
|
||||
*
|
||||
* @param flowInfos a network metric to be published
|
||||
*/
|
||||
void publish(Set<FlowInfo> flowInfos);
|
||||
}
|
||||
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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.openstacktelemetry.api.config;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface PrometheusTelemetryConfig extends TelemetryConfig {
|
||||
|
||||
/**
|
||||
* Obtains prometheus exporter IP address.
|
||||
*
|
||||
* @return IP address which prometheus exporter binds
|
||||
*/
|
||||
String address();
|
||||
|
||||
/**
|
||||
* Obtains prometheus exporter port number.
|
||||
*
|
||||
* @return prometheus exporter port number
|
||||
*/
|
||||
int port();
|
||||
|
||||
/**
|
||||
* Obtains prometheus config maps.
|
||||
*
|
||||
* @return prometheus config map
|
||||
*/
|
||||
Map<String, Object> configMap();
|
||||
|
||||
|
||||
/**
|
||||
* Builder class of PrometheusTelemetryConfig.
|
||||
*/
|
||||
interface Builder extends TelemetryConfig.Builder {
|
||||
|
||||
/**
|
||||
* Sets prometheus exporter IP address.
|
||||
*
|
||||
* @param address prometheus exporter IP
|
||||
* @return builder instance
|
||||
*/
|
||||
Builder withAddress(String address);
|
||||
|
||||
/**
|
||||
* Sets prometheus exporter port number.
|
||||
*
|
||||
* @param port prometheus exporter port
|
||||
* @return builder instance
|
||||
*/
|
||||
Builder withPort(int port);
|
||||
|
||||
// TODO add authentication.
|
||||
|
||||
/**
|
||||
* Sets other prometheus configuration map.
|
||||
*
|
||||
* @param configMap prometheus configuration map
|
||||
* @return builder instance
|
||||
*/
|
||||
Builder withConfigMap(Map<String, Object> configMap);
|
||||
|
||||
/**
|
||||
* Creates a prometheus telemetry config instance.
|
||||
*
|
||||
* @return prometheus telemetry config instance
|
||||
*/
|
||||
PrometheusTelemetryConfig build();
|
||||
}
|
||||
}
|
||||
@ -15,6 +15,18 @@ COMPILE_DEPS = [
|
||||
'//apps/openstacktelemetry/api:onos-apps-openstacktelemetry-api',
|
||||
'//lib:kafka-clients',
|
||||
'//lib:influxdb-java',
|
||||
'//lib:simpleclient',
|
||||
'//lib:simpleclient_common',
|
||||
'//lib:simpleclient_hotspot',
|
||||
'//lib:simpleclient_servlet',
|
||||
'//lib:jetty-http',
|
||||
'//lib:jetty-servlet',
|
||||
'//lib:jetty-server',
|
||||
'//lib:jetty-security',
|
||||
'//lib:jetty-websocket',
|
||||
'//lib:jetty-util',
|
||||
'//lib:jetty-io',
|
||||
'//lib:servlet-api',
|
||||
'//lib:protobuf-java-3.2.0',
|
||||
'//lib:GRPC_1.3',
|
||||
'//incubator/grpc-dependencies:grpc-core-repkg-' + GRPC_VER,
|
||||
|
||||
@ -2,6 +2,16 @@ COMPILE_DEPS = CORE_DEPS + JACKSON + KRYO + REST + CLI + [
|
||||
"@kafka_clients//jar",
|
||||
"@jersey_client//jar",
|
||||
"@influxdb_java//jar",
|
||||
"@simpleclient//jar",
|
||||
"@simpleclient_common//jar",
|
||||
"@simpleclient_hotspot//jar",
|
||||
"@simpleclient_servlet//jar",
|
||||
"@jetty_servlet//jar",
|
||||
"@jetty_http//jar",
|
||||
"@jetty_server//jar",
|
||||
"@jetty_util//jar",
|
||||
"@jetty_websocket//jar",
|
||||
"@servlet_api//jar",
|
||||
"@io_grpc_grpc_java//core",
|
||||
"@io_grpc_grpc_java//protobuf-lite",
|
||||
"//core/store/serializers:onos-core-serializers",
|
||||
|
||||
@ -0,0 +1,131 @@
|
||||
/*
|
||||
* 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.openstacktelemetry.config;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Maps;
|
||||
import org.onosproject.openstacktelemetry.api.config.PrometheusTelemetryConfig;
|
||||
import org.onosproject.openstacktelemetry.api.config.TelemetryConfig;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import static com.google.common.base.MoreObjects.toStringHelper;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* A configuration file contains Prometheus telemetry parameters.
|
||||
*/
|
||||
public final class DefaultPrometheusTelemetryConfig implements PrometheusTelemetryConfig {
|
||||
|
||||
private final String address;
|
||||
private final int port;
|
||||
private final Map<String, Object> configMap;
|
||||
|
||||
private DefaultPrometheusTelemetryConfig(String address,
|
||||
int port,
|
||||
Map<String, Object> configMap) {
|
||||
this.address = address;
|
||||
this.port = port;
|
||||
this.configMap = configMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String address() {
|
||||
return address;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int port() {
|
||||
return port;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> configMap() {
|
||||
if (configMap != null) {
|
||||
return ImmutableMap.copyOf(configMap);
|
||||
} else {
|
||||
return Maps.newConcurrentMap();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj instanceof DefaultPrometheusTelemetryConfig) {
|
||||
final DefaultPrometheusTelemetryConfig other = (DefaultPrometheusTelemetryConfig) obj;
|
||||
return Objects.equals(this.address, other.address) &&
|
||||
Objects.equals(this.port, other.port) &&
|
||||
Objects.equals(this.configMap, other.configMap);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(address, port, configMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toStringHelper(this)
|
||||
.add("address", address)
|
||||
.add("port", port)
|
||||
.add("configMap", configMap)
|
||||
.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TelemetryConfig.Builder createBuilder() {
|
||||
return new DefaultBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder class of DefaultPrometheusTelemetryConfig.
|
||||
*/
|
||||
public static final class DefaultBuilder implements Builder {
|
||||
private String address;
|
||||
private int port;
|
||||
private Map<String, Object> configMap;
|
||||
|
||||
@Override
|
||||
public Builder withAddress(String address) {
|
||||
this.address = address;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder withPort(int port) {
|
||||
this.port = port;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder withConfigMap(Map<String, Object> configMap) {
|
||||
this.configMap = configMap;
|
||||
return this;
|
||||
}
|
||||
@Override
|
||||
public PrometheusTelemetryConfig build() {
|
||||
checkNotNull(address, "Prometheus exporter binding address cannot be null");
|
||||
checkNotNull(configMap, "Config map cannot be null");
|
||||
return new DefaultPrometheusTelemetryConfig(address, port, configMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -56,8 +56,6 @@ public class InfluxDbTelemetryManager implements InfluxDbTelemetryAdminService {
|
||||
private static final String SRC_PORT = "srcPort";
|
||||
private static final String DST_PORT = "dstPort";
|
||||
private static final String PROTOCOL = "protocol";
|
||||
private static final String SRC_MAC = "srcMac";
|
||||
private static final String DST_MAC = "dstMac";
|
||||
|
||||
private static final String STARTUP_TIME = "startupTime";
|
||||
private static final String FST_PKT_ARR_TIME = "fstPktArrTime";
|
||||
@ -128,7 +126,7 @@ public class InfluxDbTelemetryManager implements InfluxDbTelemetryAdminService {
|
||||
producer = null;
|
||||
}
|
||||
|
||||
log.info("InfluxDB producer has Stopped");
|
||||
log.info("InfluxDB producer has stopped");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -24,6 +24,7 @@ import org.onosproject.openstacktelemetry.api.GrpcTelemetryService;
|
||||
import org.onosproject.openstacktelemetry.api.InfluxDbTelemetryService;
|
||||
import org.onosproject.openstacktelemetry.api.KafkaTelemetryService;
|
||||
import org.onosproject.openstacktelemetry.api.OpenstackTelemetryService;
|
||||
import org.onosproject.openstacktelemetry.api.PrometheusTelemetryService;
|
||||
import org.onosproject.openstacktelemetry.api.RestTelemetryService;
|
||||
import org.onosproject.openstacktelemetry.api.TelemetryService;
|
||||
import org.onosproject.openstacktelemetry.codec.TinaMessageByteBufferCodec;
|
||||
@ -94,6 +95,12 @@ public class OpenstackTelemetryManager implements OpenstackTelemetryService {
|
||||
invokeInfluxDbPublisher((InfluxDbTelemetryService) service, flowInfos);
|
||||
}
|
||||
|
||||
if (service instanceof PrometheusTelemetryManager &&
|
||||
getPropertyValueAsBoolean(componentConfigService.getProperties(
|
||||
PrometheusTelemetryConfigManager.class.getName()), ENABLE_SERVICE)) {
|
||||
invokePrometheusPublisher((PrometheusTelemetryService) service, flowInfos);
|
||||
}
|
||||
|
||||
if (service instanceof KafkaTelemetryManager &&
|
||||
getPropertyValueAsBoolean(componentConfigService.getProperties(
|
||||
KafkaTelemetryConfigManager.class.getName()), ENABLE_SERVICE)) {
|
||||
@ -125,6 +132,10 @@ public class OpenstackTelemetryManager implements OpenstackTelemetryService {
|
||||
service.publish(influxRecord);
|
||||
}
|
||||
|
||||
private void invokePrometheusPublisher(PrometheusTelemetryService service, Set<FlowInfo> flowInfos) {
|
||||
service.publish(flowInfos);
|
||||
}
|
||||
|
||||
private void invokeKafkaPublisher(KafkaTelemetryService service, Set<FlowInfo> flowInfos) {
|
||||
TinaMessageByteBufferCodec codec = new TinaMessageByteBufferCodec();
|
||||
ByteBuffer buffer = codec.encode(flowInfos);
|
||||
|
||||
@ -0,0 +1,136 @@
|
||||
/*
|
||||
* 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.openstacktelemetry.impl;
|
||||
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.Modified;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
import org.osgi.service.component.annotations.ReferenceCardinality;
|
||||
import org.onlab.util.Tools;
|
||||
import org.onosproject.cfg.ComponentConfigService;
|
||||
import org.onosproject.openstacktelemetry.api.PrometheusTelemetryAdminService;
|
||||
import org.onosproject.openstacktelemetry.api.PrometheusTelemetryConfigService;
|
||||
import org.onosproject.openstacktelemetry.api.config.TelemetryConfig;
|
||||
import org.onosproject.openstacktelemetry.config.DefaultPrometheusTelemetryConfig;
|
||||
import org.osgi.service.component.ComponentContext;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Dictionary;
|
||||
|
||||
import static org.onosproject.openstacktelemetry.api.Constants.DEFAULT_DISABLE;
|
||||
import static org.onosproject.openstacktelemetry.api.Constants.DEFAULT_ENABLE;
|
||||
import static org.onosproject.openstacktelemetry.api.Constants.DEFAULT_PROMETHEUS_EXPORTER_IP;
|
||||
import static org.onosproject.openstacktelemetry.api.Constants.DEFAULT_PROMETHEUS_EXPORTER_PORT;
|
||||
import static org.onosproject.openstacktelemetry.util.OpenstackTelemetryUtil.getBooleanProperty;
|
||||
import static org.onosproject.openstacktelemetry.util.OpenstackTelemetryUtil.initTelemetryService;
|
||||
|
||||
/**
|
||||
* Prometheus exporter configuration manager for publishing openstack telemetry.
|
||||
*/
|
||||
@Component(immediate = true, service = PrometheusTelemetryConfigService.class)
|
||||
public class PrometheusTelemetryConfigManager implements PrometheusTelemetryConfigService {
|
||||
|
||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private static final String ENABLE_SERVICE = "enableService";
|
||||
private static final String ADDRESS = "address";
|
||||
private static final String PORT = "port";
|
||||
|
||||
@Reference(cardinality = ReferenceCardinality.MANDATORY)
|
||||
protected ComponentConfigService componentConfigService;
|
||||
|
||||
@Reference(cardinality = ReferenceCardinality.MANDATORY)
|
||||
protected PrometheusTelemetryAdminService prometheusTelemetryAdminService;
|
||||
|
||||
//@Property(name = ADDRESS, value = DEFAULT_PROMETHEUS_EXPORTER_IP,
|
||||
// label = "Default IP address of prometheus exporter")
|
||||
protected String address = DEFAULT_PROMETHEUS_EXPORTER_IP;
|
||||
|
||||
//@Property(name = PORT, intValue = DEFAULT_PROMETHEUS_EXPORTER_PORT,
|
||||
// label = "Default port number of prometheus exporter")
|
||||
protected Integer port = DEFAULT_PROMETHEUS_EXPORTER_PORT;
|
||||
|
||||
//@Property(name = ENABLE_SERVICE, boolValue = DEFAULT_ENABLE,
|
||||
// label = "Specify the default behavior of telemetry service")
|
||||
protected Boolean enableService = DEFAULT_ENABLE;
|
||||
|
||||
@Activate
|
||||
protected void activate(ComponentContext context) {
|
||||
componentConfigService.registerProperties(getClass());
|
||||
if (enableService) {
|
||||
prometheusTelemetryAdminService.start(getConfig());
|
||||
}
|
||||
log.info("Started");
|
||||
}
|
||||
|
||||
@Deactivate
|
||||
protected void deactivate() {
|
||||
componentConfigService.unregisterProperties(getClass(), false);
|
||||
if (enableService) {
|
||||
prometheusTelemetryAdminService.stop();
|
||||
}
|
||||
log.info("Stopped");
|
||||
}
|
||||
|
||||
@Modified
|
||||
private void modified(ComponentContext context) {
|
||||
readComponentConfiguration(context);
|
||||
initTelemetryService(prometheusTelemetryAdminService, getConfig(), enableService);
|
||||
log.info("Modified");
|
||||
}
|
||||
|
||||
@Override
|
||||
public TelemetryConfig getConfig() {
|
||||
return new DefaultPrometheusTelemetryConfig.DefaultBuilder()
|
||||
.withAddress(address)
|
||||
.withPort(port)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts properties from the component configuration context.
|
||||
*
|
||||
* @param context the component context
|
||||
*/
|
||||
private void readComponentConfiguration(ComponentContext context) {
|
||||
Dictionary<?, ?> properties = context.getProperties();
|
||||
|
||||
String addressStr = Tools.get(properties, ADDRESS);
|
||||
address = addressStr != null ? addressStr : DEFAULT_PROMETHEUS_EXPORTER_IP;
|
||||
log.info("Configured. Prometheus exporter address is {}", address);
|
||||
|
||||
Integer portConfigured = Tools.getIntegerProperty(properties, PORT);
|
||||
if (portConfigured == null) {
|
||||
port = DEFAULT_PROMETHEUS_EXPORTER_PORT;
|
||||
log.info("Prometheus exporter port is NOT configured, default value is {}", port);
|
||||
} else {
|
||||
port = portConfigured;
|
||||
log.info("Configured. Prometheus exporter port is {}", port);
|
||||
}
|
||||
|
||||
Boolean enableServiceConfigured = getBooleanProperty(properties, ENABLE_SERVICE);
|
||||
if (enableServiceConfigured == null) {
|
||||
enableService = DEFAULT_DISABLE;
|
||||
log.info("Prometheus service enable flag is NOT " +
|
||||
"configured, default value is {}", enableService);
|
||||
} else {
|
||||
enableService = enableServiceConfigured;
|
||||
log.info("Configured. Prometheus service enable flag is {}", enableService);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,195 @@
|
||||
/*
|
||||
* 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.openstacktelemetry.impl;
|
||||
|
||||
import org.onosproject.openstacktelemetry.api.FlowInfo;
|
||||
import org.onosproject.openstacktelemetry.api.OpenstackTelemetryService;
|
||||
import org.onosproject.openstacktelemetry.api.PrometheusTelemetryAdminService;
|
||||
import org.onosproject.openstacktelemetry.api.PrometheusTelemetryService;
|
||||
import org.onosproject.openstacktelemetry.api.config.PrometheusTelemetryConfig;
|
||||
import org.onosproject.openstacktelemetry.api.config.TelemetryConfig;
|
||||
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 io.prometheus.client.Counter;
|
||||
import io.prometheus.client.exporter.MetricsServlet;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Prometheus telemetry manager.
|
||||
*/
|
||||
@Component(immediate = true, service = PrometheusTelemetryService.class)
|
||||
public class PrometheusTelemetryManager implements PrometheusTelemetryAdminService {
|
||||
|
||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private Server prometheusExporter;
|
||||
|
||||
private static final String BYTE_VM2VM = "byte_vm2vm";
|
||||
private static final String BYTE_DEVICE = "byte_device";
|
||||
private static final String BYTE_SRC_IP = "byte_src_ip";
|
||||
private static final String BYTE_DST_IP = "byte_dst_ip";
|
||||
|
||||
private static final String PKT_VM2VM = "pkt_vm2vm";
|
||||
private static final String PKT_DEVICE = "pkt_device";
|
||||
private static final String PKT_SRC_IP = "pkt_src_ip";
|
||||
private static final String PKT_DST_IP = "pkt_dst_ip";
|
||||
|
||||
private static final String PKT_ERROR = "pkt_error";
|
||||
private static final String PKT_DROP = "pkt_drop";
|
||||
|
||||
private static final String LABEL_IP_5_TUPLE = "IP_5_TUPLE";
|
||||
private static final String LABEL_DEV_ID = "DEVICE_ID";
|
||||
private static final String LABEL_SRC_IP = "SOURCE_IP";
|
||||
private static final String LABEL_DST_IP = "DESTINATION_IP";
|
||||
|
||||
private static final String HELP_MSG = "SONA Flow statistics";
|
||||
|
||||
private static Counter byteVM2VM = Counter.build().name(BYTE_VM2VM)
|
||||
.help(HELP_MSG)
|
||||
.labelNames(LABEL_IP_5_TUPLE).register();
|
||||
|
||||
private static Counter byteDevice = Counter.build().name(BYTE_DEVICE)
|
||||
.help(HELP_MSG)
|
||||
.labelNames(LABEL_DEV_ID).register();
|
||||
|
||||
private static Counter byteSrcIp = Counter.build().name(BYTE_SRC_IP)
|
||||
.help(HELP_MSG)
|
||||
.labelNames(LABEL_SRC_IP).register();
|
||||
|
||||
private static Counter byteDstIp = Counter.build().name(BYTE_DST_IP)
|
||||
.help(HELP_MSG)
|
||||
.labelNames(LABEL_DST_IP).register();
|
||||
|
||||
private static Counter pktVM2VM = Counter.build().name(PKT_VM2VM)
|
||||
.help(HELP_MSG)
|
||||
.labelNames(LABEL_IP_5_TUPLE).register();
|
||||
|
||||
private static Counter pktDevice = Counter.build().name(PKT_DEVICE)
|
||||
.help(HELP_MSG)
|
||||
.labelNames(LABEL_DEV_ID).register();
|
||||
|
||||
private static Counter pktSrcIp = Counter.build().name(PKT_SRC_IP)
|
||||
.help(HELP_MSG)
|
||||
.labelNames(LABEL_SRC_IP).register();
|
||||
|
||||
private static Counter pktDstIp = Counter.build().name(PKT_DST_IP)
|
||||
.help(HELP_MSG)
|
||||
.labelNames(LABEL_DST_IP).register();
|
||||
|
||||
private static Counter pktError = Counter.build().name(PKT_ERROR)
|
||||
.help(HELP_MSG)
|
||||
.register();
|
||||
private static Counter pktDrop = Counter.build().name(PKT_DROP)
|
||||
.help(HELP_MSG)
|
||||
.register();
|
||||
|
||||
@Reference(cardinality = ReferenceCardinality.MANDATORY)
|
||||
protected OpenstackTelemetryService openstackTelemetryService;
|
||||
|
||||
@Activate
|
||||
protected void activate() {
|
||||
openstackTelemetryService.addTelemetryService(this);
|
||||
log.info("Started");
|
||||
}
|
||||
|
||||
@Deactivate
|
||||
protected void deactivate() {
|
||||
stop();
|
||||
openstackTelemetryService.removeTelemetryService(this);
|
||||
log.info("Stopped");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(TelemetryConfig config) {
|
||||
log.info("Prometheus exporter starts.");
|
||||
|
||||
PrometheusTelemetryConfig prometheusConfig = (PrometheusTelemetryConfig) config;
|
||||
|
||||
try {
|
||||
// TODO Offer a 'Authentication'
|
||||
prometheusExporter = new Server(prometheusConfig.port());
|
||||
ServletContextHandler context = new ServletContextHandler();
|
||||
context.setContextPath("/");
|
||||
prometheusExporter.setHandler(context);
|
||||
context.addServlet(new ServletHolder(new MetricsServlet()), "/metrics");
|
||||
|
||||
log.info("Prometeus server start");
|
||||
|
||||
prometheusExporter.start();
|
||||
} catch (Exception ex) {
|
||||
log.warn("Exception: {}", ex.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
try {
|
||||
prometheusExporter.stop();
|
||||
} catch (Exception ex) {
|
||||
log.warn("Exception: {}", ex.toString());
|
||||
}
|
||||
log.info("Prometheus exporter has stopped");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restart(TelemetryConfig config) {
|
||||
stop();
|
||||
start(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publish(Set<FlowInfo> flowInfos) {
|
||||
if (flowInfos.size() == 0) {
|
||||
log.debug("No record to publish");
|
||||
return;
|
||||
}
|
||||
|
||||
long flowByte;
|
||||
int flowPkt;
|
||||
for (FlowInfo flowInfo: flowInfos) {
|
||||
flowByte = flowInfo.statsInfo().currAccBytes() - flowInfo.statsInfo().prevAccBytes();
|
||||
flowPkt = flowInfo.statsInfo().currAccPkts() - flowInfo.statsInfo().prevAccPkts();
|
||||
|
||||
byteVM2VM.labels(flowInfo.uniqueFlowInfoKey()).inc(flowByte);
|
||||
byteDevice.labels(flowInfo.deviceId().toString()).inc(flowByte);
|
||||
byteSrcIp.labels(flowInfo.srcIp().toString()).inc(flowByte);
|
||||
byteDstIp.labels(flowInfo.dstIp().toString()).inc(flowByte);
|
||||
|
||||
pktVM2VM.labels(flowInfo.uniqueFlowInfoKey()).inc(flowPkt);
|
||||
pktDevice.labels(flowInfo.deviceId().toString()).inc(flowPkt);
|
||||
pktSrcIp.labels(flowInfo.srcIp().toString()).inc(flowPkt);
|
||||
pktDstIp.labels(flowInfo.dstIp().toString()).inc(flowPkt);
|
||||
|
||||
pktError.inc(flowInfo.statsInfo().errorPkts());
|
||||
pktDrop.inc(flowInfo.statsInfo().dropPkts());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
log.info("Prometheus Exporter State: {}", prometheusExporter.isRunning());
|
||||
return prometheusExporter.isRunning();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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.openstacktelemetry.config;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.testing.EqualsTester;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.onosproject.openstacktelemetry.api.config.PrometheusTelemetryConfig;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
|
||||
import static org.onosproject.openstacktelemetry.api.Constants.DEFAULT_PROMETHEUS_EXPORTER_IP;
|
||||
import static org.onosproject.openstacktelemetry.api.Constants.DEFAULT_PROMETHEUS_EXPORTER_PORT;
|
||||
|
||||
/**
|
||||
* Unit tests for DefaultPrometheusTelemetryConfig class.
|
||||
*/
|
||||
public class DefaultPrometheusTelemetryConfigTest {
|
||||
|
||||
private static final String IP_ADDRESS_1 = DEFAULT_PROMETHEUS_EXPORTER_IP;
|
||||
private static final String IP_ADDRESS_2 = "10.10.1.2";
|
||||
|
||||
private static final int PORT_1 = DEFAULT_PROMETHEUS_EXPORTER_PORT;
|
||||
private static final int PORT_2 = DEFAULT_PROMETHEUS_EXPORTER_PORT + 1;
|
||||
|
||||
private static final Map<String, Object> CONFIG_MAP_1 =
|
||||
ImmutableMap.of("key1", "value1");
|
||||
private static final Map<String, Object> CONFIG_MAP_2 =
|
||||
ImmutableMap.of("key2", "value2");
|
||||
|
||||
private PrometheusTelemetryConfig config1;
|
||||
private PrometheusTelemetryConfig sameAsConfig1;
|
||||
private PrometheusTelemetryConfig config2;
|
||||
|
||||
/**
|
||||
* Initial setup for this unit test.
|
||||
*/
|
||||
@Before
|
||||
public void setup() {
|
||||
|
||||
PrometheusTelemetryConfig.Builder builder1 =
|
||||
new DefaultPrometheusTelemetryConfig.DefaultBuilder();
|
||||
PrometheusTelemetryConfig.Builder builder2 =
|
||||
new DefaultPrometheusTelemetryConfig.DefaultBuilder();
|
||||
PrometheusTelemetryConfig.Builder builder3 =
|
||||
new DefaultPrometheusTelemetryConfig.DefaultBuilder();
|
||||
|
||||
config1 = builder1
|
||||
.withAddress(IP_ADDRESS_1)
|
||||
.withPort(PORT_1)
|
||||
.withConfigMap(CONFIG_MAP_1)
|
||||
.build();
|
||||
|
||||
sameAsConfig1 = builder2
|
||||
.withAddress(IP_ADDRESS_1)
|
||||
.withPort(PORT_1)
|
||||
.withConfigMap(CONFIG_MAP_1)
|
||||
.build();
|
||||
|
||||
config2 = builder3
|
||||
.withAddress(IP_ADDRESS_2)
|
||||
.withPort(PORT_2)
|
||||
.withConfigMap(CONFIG_MAP_2)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests class immutability.
|
||||
*/
|
||||
@Test
|
||||
public void testImmutability() {
|
||||
assertThatClassIsImmutable(DefaultPrometheusTelemetryConfig.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests object equality.
|
||||
*/
|
||||
@Test
|
||||
public void testEquality() {
|
||||
new EqualsTester()
|
||||
.addEqualityGroup(config1, sameAsConfig1)
|
||||
.addEqualityGroup(config2).testEquals();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests object construction.
|
||||
*/
|
||||
@Test
|
||||
public void testConstruction() {
|
||||
PrometheusTelemetryConfig config = config1;
|
||||
|
||||
assertThat(config.address(), is(IP_ADDRESS_1));
|
||||
assertThat(config.port(), is(PORT_1));
|
||||
assertThat(config.configMap(), is(CONFIG_MAP_1));
|
||||
}
|
||||
}
|
||||
@ -23,10 +23,10 @@ import java.util.concurrent.ExecutorService;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.onlab.packet.IpAddress;
|
||||
import org.onlab.packet.IpPrefix;
|
||||
import org.onosproject.cluster.NodeId;
|
||||
import org.onlab.util.KryoNamespace;
|
||||
import org.onosproject.routeservice.InternalRouteEvent;
|
||||
import org.onosproject.routeservice.Route;
|
||||
@ -50,7 +50,12 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||
public class DefaultRouteTable implements RouteTable {
|
||||
|
||||
private final RouteTableId id;
|
||||
private final ConsistentMultimap<IpPrefix, Route> routes;
|
||||
|
||||
// The route map stores RawRoute instead of Route to translate the polymorphic IpPrefix and IpAddress types
|
||||
// into monomorphic types (specifically String). Using strings in the stored RawRoute is necessary to ensure
|
||||
// the serialized bytes are consistent whether e.g. IpAddress or Ip4Address is used when storing a route.
|
||||
private final ConsistentMultimap<String, RawRoute> routes;
|
||||
|
||||
private final RouteStoreDelegate delegate;
|
||||
private final ExecutorService executor;
|
||||
private final RouteTableListener listener = new RouteTableListener();
|
||||
@ -89,13 +94,14 @@ public class DefaultRouteTable implements RouteTable {
|
||||
new InternalRouteEvent(InternalRouteEvent.Type.ROUTE_ADDED, routeSet)));
|
||||
}
|
||||
|
||||
private ConsistentMultimap<IpPrefix, Route> buildRouteMap(StorageService storageService) {
|
||||
private ConsistentMultimap<String, RawRoute> buildRouteMap(StorageService storageService) {
|
||||
KryoNamespace routeTableSerializer = KryoNamespace.newBuilder()
|
||||
.register(KryoNamespaces.API)
|
||||
.register(Route.class)
|
||||
.register(Route.Source.class)
|
||||
.register(RawRoute.class)
|
||||
.build();
|
||||
return storageService.<IpPrefix, Route>consistentMultimapBuilder()
|
||||
return storageService.<String, RawRoute>consistentMultimapBuilder()
|
||||
.withName("onos-routes-" + id.name())
|
||||
.withRelaxedReadConsistency()
|
||||
.withSerializer(Serializer.using(routeTableSerializer))
|
||||
@ -121,36 +127,37 @@ public class DefaultRouteTable implements RouteTable {
|
||||
|
||||
@Override
|
||||
public void update(Route route) {
|
||||
routes.put(route.prefix(), route);
|
||||
routes.put(route.prefix().toString(), new RawRoute(route));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(Route route) {
|
||||
routes.remove(route.prefix(), route);
|
||||
routes.remove(route.prefix().toString(), new RawRoute(route));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replace(Route route) {
|
||||
routes.replaceValues(route.prefix(), Sets.newHashSet(route));
|
||||
routes.replaceValues(route.prefix().toString(), Sets.newHashSet(new RawRoute(route)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<RouteSet> getRoutes() {
|
||||
return routes.stream()
|
||||
.map(Map.Entry::getValue)
|
||||
.collect(Collectors.groupingBy(Route::prefix))
|
||||
.collect(Collectors.groupingBy(RawRoute::prefix))
|
||||
.entrySet()
|
||||
.stream()
|
||||
.map(entry -> new RouteSet(id, entry.getKey(), ImmutableSet.copyOf(entry.getValue())))
|
||||
.map(entry -> new RouteSet(id,
|
||||
IpPrefix.valueOf(entry.getKey()),
|
||||
entry.getValue().stream().map(RawRoute::route).collect(Collectors.toSet())))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public RouteSet getRoutes(IpPrefix prefix) {
|
||||
Versioned<Collection<? extends Route>> routeSet = routes.get(prefix);
|
||||
|
||||
Versioned<Collection<? extends RawRoute>> routeSet = routes.get(prefix.toString());
|
||||
if (routeSet != null) {
|
||||
return new RouteSet(id, prefix, ImmutableSet.copyOf(routeSet.value()));
|
||||
return new RouteSet(id, prefix, routeSet.value().stream().map(RawRoute::route).collect(Collectors.toSet()));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -159,22 +166,25 @@ public class DefaultRouteTable implements RouteTable {
|
||||
public Collection<Route> getRoutesForNextHop(IpAddress nextHop) {
|
||||
return routes.stream()
|
||||
.map(Map.Entry::getValue)
|
||||
.filter(r -> r.nextHop().equals(nextHop))
|
||||
.filter(r -> IpAddress.valueOf(r.nextHop()).equals(nextHop))
|
||||
.map(RawRoute::route)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
private class RouteTableListener
|
||||
implements MultimapEventListener<IpPrefix, Route> {
|
||||
implements MultimapEventListener<String, RawRoute> {
|
||||
|
||||
private InternalRouteEvent createRouteEvent(
|
||||
InternalRouteEvent.Type type, MultimapEvent<IpPrefix, Route> event) {
|
||||
Collection<? extends Route> currentRoutes = Versioned.valueOrNull(routes.get(event.key()));
|
||||
InternalRouteEvent.Type type, MultimapEvent<String, RawRoute> event) {
|
||||
Collection<? extends RawRoute> currentRoutes = Versioned.valueOrNull(routes.get(event.key()));
|
||||
return new InternalRouteEvent(type, new RouteSet(
|
||||
id, event.key(), currentRoutes != null ? ImmutableSet.copyOf(currentRoutes) : Collections.emptySet()));
|
||||
id, IpPrefix.valueOf(event.key()), currentRoutes != null ?
|
||||
currentRoutes.stream().map(RawRoute::route).collect(Collectors.toSet())
|
||||
: Collections.emptySet()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void event(MultimapEvent<IpPrefix, Route> event) {
|
||||
public void event(MultimapEvent<String, RawRoute> event) {
|
||||
InternalRouteEvent ire = null;
|
||||
switch (event.type()) {
|
||||
case INSERT:
|
||||
@ -190,4 +200,32 @@ public class DefaultRouteTable implements RouteTable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a route object stored in the underlying ConsistentMultimap.
|
||||
*/
|
||||
private static class RawRoute {
|
||||
private Route.Source source;
|
||||
private String prefix;
|
||||
private String nextHop;
|
||||
private NodeId sourceNode;
|
||||
|
||||
RawRoute(Route route) {
|
||||
this.source = route.source();
|
||||
this.prefix = route.prefix().toString();
|
||||
this.nextHop = route.nextHop().toString();
|
||||
this.sourceNode = route.sourceNode();
|
||||
}
|
||||
|
||||
String prefix() {
|
||||
return prefix;
|
||||
}
|
||||
|
||||
String nextHop() {
|
||||
return nextHop;
|
||||
}
|
||||
|
||||
Route route() {
|
||||
return new Route(source, IpPrefix.valueOf(prefix), IpAddress.valueOf(nextHop), sourceNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -991,6 +991,11 @@ public class SegmentRoutingManager implements SegmentRoutingService {
|
||||
ImmutableMap.copyOf(defaultRoutingHandler.shouldProgramCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldProgram(DeviceId deviceId) {
|
||||
return defaultRoutingHandler.shouldProgram(deviceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicationId appId() {
|
||||
return appId;
|
||||
|
||||
@ -352,6 +352,14 @@ public interface SegmentRoutingService {
|
||||
*/
|
||||
Map<DeviceId, Boolean> getShouldProgramCache();
|
||||
|
||||
/**
|
||||
* Returns whether instance should program device or not.
|
||||
*
|
||||
* @param deviceId .
|
||||
* @return boolean status saying instance should program device or not.
|
||||
*/
|
||||
boolean shouldProgram(DeviceId deviceId);
|
||||
|
||||
/**
|
||||
* Gets application id.
|
||||
*
|
||||
|
||||
@ -67,8 +67,8 @@ public class DeviceConfiguration implements DeviceProperties {
|
||||
private SegmentRoutingManager srManager;
|
||||
|
||||
private class SegmentRouterInfo {
|
||||
int ipv4NodeSid;
|
||||
int ipv6NodeSid;
|
||||
int ipv4NodeSid = -1;
|
||||
int ipv6NodeSid = -1;
|
||||
DeviceId deviceId;
|
||||
Ip4Address ipv4Loopback;
|
||||
Ip6Address ipv6Loopback;
|
||||
@ -271,9 +271,15 @@ public class DeviceConfiguration implements DeviceProperties {
|
||||
* @return node segment id, or -1 if not found in config
|
||||
*/
|
||||
public int getIPv4SegmentId(Ip4Address routerAddress) {
|
||||
for (Map.Entry<DeviceId, SegmentRouterInfo> entry:
|
||||
deviceConfigMap.entrySet()) {
|
||||
for (Map.Entry<DeviceId, SegmentRouterInfo> entry: deviceConfigMap.entrySet()) {
|
||||
Ip4Address ipv4Loopback = entry.getValue().ipv4Loopback;
|
||||
if (ipv4Loopback == null) {
|
||||
continue;
|
||||
}
|
||||
if (entry.getValue().ipv4Loopback.equals(routerAddress)) {
|
||||
if (entry.getValue().ipv4NodeSid == -1) {
|
||||
continue;
|
||||
}
|
||||
return entry.getValue().ipv4NodeSid;
|
||||
}
|
||||
}
|
||||
@ -288,9 +294,15 @@ public class DeviceConfiguration implements DeviceProperties {
|
||||
* @return node segment id, or -1 if not found in config
|
||||
*/
|
||||
public int getIPv6SegmentId(Ip6Address routerAddress) {
|
||||
for (Map.Entry<DeviceId, SegmentRouterInfo> entry:
|
||||
deviceConfigMap.entrySet()) {
|
||||
for (Map.Entry<DeviceId, SegmentRouterInfo> entry: deviceConfigMap.entrySet()) {
|
||||
Ip6Address ipv6Loopback = entry.getValue().ipv6Loopback;
|
||||
if (ipv6Loopback == null) {
|
||||
continue;
|
||||
}
|
||||
if (entry.getValue().ipv6Loopback.equals(routerAddress)) {
|
||||
if (entry.getValue().ipv6NodeSid == -1) {
|
||||
continue;
|
||||
}
|
||||
return entry.getValue().ipv6NodeSid;
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,6 +21,7 @@ import org.onosproject.net.ConnectPoint;
|
||||
import org.onosproject.net.DeviceId;
|
||||
import org.onosproject.net.PortNumber;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@ -70,6 +71,24 @@ public interface XconnectService {
|
||||
*/
|
||||
boolean hasXconnect(ConnectPoint cp);
|
||||
|
||||
/**
|
||||
* Gives xconnect VLAN of given port of a device.
|
||||
*
|
||||
* @param deviceId Device ID
|
||||
* @param port Port number
|
||||
* @return true if given VLAN vlanId is XConnect VLAN on device deviceId.
|
||||
*/
|
||||
List<VlanId> getXconnectVlans(DeviceId deviceId, PortNumber port);
|
||||
|
||||
/**
|
||||
* Checks given VLAN is XConnect VLAN in given device.
|
||||
*
|
||||
* @param deviceId Device ID
|
||||
* @param vlanId VLAN ID
|
||||
* @return true if given VLAN vlanId is XConnect VLAN on device deviceId.
|
||||
*/
|
||||
boolean isXconnectVlan(DeviceId deviceId, VlanId vlanId);
|
||||
|
||||
/**
|
||||
* Returns the Xconnect next objective store.
|
||||
*
|
||||
@ -84,4 +103,12 @@ public interface XconnectService {
|
||||
*/
|
||||
void removeNextId(int nextId);
|
||||
|
||||
/**
|
||||
* Returns Xconnect next objective ID associated with group device + vlan.
|
||||
*
|
||||
* @param deviceId - Device ID
|
||||
* @param vlanId - VLAN ID
|
||||
* @return Current associated group ID
|
||||
*/
|
||||
int getNextId(DeviceId deviceId, VlanId vlanId);
|
||||
}
|
||||
|
||||
@ -18,6 +18,7 @@ package org.onosproject.segmentrouting.xconnect.impl;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.onlab.packet.Ethernet;
|
||||
import org.onlab.packet.MacAddress;
|
||||
import org.onlab.packet.VlanId;
|
||||
import org.onlab.util.KryoNamespace;
|
||||
@ -27,6 +28,8 @@ import org.onosproject.core.CoreService;
|
||||
import org.onosproject.mastership.MastershipService;
|
||||
import org.onosproject.net.ConnectPoint;
|
||||
import org.onosproject.net.DeviceId;
|
||||
import org.onosproject.net.Host;
|
||||
import org.onosproject.net.HostLocation;
|
||||
import org.onosproject.net.PortNumber;
|
||||
import org.onosproject.net.config.NetworkConfigService;
|
||||
import org.onosproject.net.device.DeviceEvent;
|
||||
@ -48,7 +51,12 @@ import org.onosproject.net.flowobjective.NextObjective;
|
||||
import org.onosproject.net.flowobjective.Objective;
|
||||
import org.onosproject.net.flowobjective.ObjectiveContext;
|
||||
import org.onosproject.net.flowobjective.ObjectiveError;
|
||||
import org.onosproject.net.host.HostEvent;
|
||||
import org.onosproject.net.host.HostListener;
|
||||
import org.onosproject.net.host.HostService;
|
||||
import org.onosproject.net.intf.InterfaceService;
|
||||
import org.onosproject.segmentrouting.SegmentRoutingService;
|
||||
import org.onosproject.segmentrouting.storekey.VlanNextObjectiveStoreKey;
|
||||
import org.onosproject.segmentrouting.xconnect.api.XconnectCodec;
|
||||
import org.onosproject.segmentrouting.xconnect.api.XconnectDesc;
|
||||
import org.onosproject.segmentrouting.xconnect.api.XconnectKey;
|
||||
@ -69,6 +77,11 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
@ -105,6 +118,12 @@ public class XconnectManager implements XconnectService {
|
||||
@Reference(cardinality = ReferenceCardinality.OPTIONAL)
|
||||
public SegmentRoutingService srService;
|
||||
|
||||
@Reference(cardinality = ReferenceCardinality.MANDATORY)
|
||||
public InterfaceService interfaceService;
|
||||
|
||||
@Reference(cardinality = ReferenceCardinality.MANDATORY)
|
||||
HostService hostService;
|
||||
|
||||
private static final String APP_NAME = "org.onosproject.xconnect";
|
||||
private static final String ERROR_NOT_MASTER = "Not master controller";
|
||||
|
||||
@ -114,11 +133,18 @@ public class XconnectManager implements XconnectService {
|
||||
private ConsistentMap<XconnectKey, Set<PortNumber>> xconnectStore;
|
||||
private ConsistentMap<XconnectKey, Integer> xconnectNextObjStore;
|
||||
|
||||
private ConsistentMap<VlanNextObjectiveStoreKey, Integer> xconnectMulticastNextStore;
|
||||
private ConsistentMap<VlanNextObjectiveStoreKey, List<PortNumber>> xconnectMulticastPortsStore;
|
||||
|
||||
private final MapEventListener<XconnectKey, Set<PortNumber>> xconnectListener = new XconnectMapListener();
|
||||
private final DeviceListener deviceListener = new InternalDeviceListener();
|
||||
|
||||
private ExecutorService deviceEventExecutor;
|
||||
|
||||
private final HostListener hostListener = new InternalHostListener();
|
||||
private ExecutorService hostEventExecutor;
|
||||
|
||||
|
||||
@Activate
|
||||
void activate() {
|
||||
appId = coreService.registerApplication(APP_NAME);
|
||||
@ -127,7 +153,8 @@ public class XconnectManager implements XconnectService {
|
||||
KryoNamespace.Builder serializer = KryoNamespace.newBuilder()
|
||||
.register(KryoNamespaces.API)
|
||||
.register(XconnectManager.class)
|
||||
.register(XconnectKey.class);
|
||||
.register(XconnectKey.class)
|
||||
.register(VlanNextObjectiveStoreKey.class);
|
||||
|
||||
xconnectStore = storageService.<XconnectKey, Set<PortNumber>>consistentMapBuilder()
|
||||
.withName("onos-sr-xconnect")
|
||||
@ -142,11 +169,25 @@ public class XconnectManager implements XconnectService {
|
||||
.withSerializer(Serializer.using(serializer.build()))
|
||||
.build();
|
||||
|
||||
xconnectMulticastNextStore = storageService.<VlanNextObjectiveStoreKey, Integer>consistentMapBuilder()
|
||||
.withName("onos-sr-xconnect-l2multicast-next")
|
||||
.withSerializer(Serializer.using(serializer.build()))
|
||||
.build();
|
||||
xconnectMulticastPortsStore = storageService.<VlanNextObjectiveStoreKey, List<PortNumber>>consistentMapBuilder()
|
||||
.withName("onos-sr-xconnect-l2multicast-ports")
|
||||
.withSerializer(Serializer.using(serializer.build()))
|
||||
.build();
|
||||
|
||||
deviceEventExecutor = Executors.newSingleThreadScheduledExecutor(
|
||||
groupedThreads("sr-xconnect-device-event", "%d", log));
|
||||
|
||||
deviceService.addListener(deviceListener);
|
||||
|
||||
hostEventExecutor = Executors.newSingleThreadExecutor(
|
||||
groupedThreads("sr-xconnect-host-event", "%d", log));
|
||||
|
||||
hostService.addListener(hostListener);
|
||||
|
||||
log.info("Started");
|
||||
}
|
||||
|
||||
@ -154,9 +195,11 @@ public class XconnectManager implements XconnectService {
|
||||
void deactivate() {
|
||||
xconnectStore.removeListener(xconnectListener);
|
||||
deviceService.removeListener(deviceListener);
|
||||
hostService.removeListener(hostListener);
|
||||
codecService.unregisterCodec(XconnectDesc.class);
|
||||
|
||||
deviceEventExecutor.shutdown();
|
||||
hostEventExecutor.shutdown();
|
||||
|
||||
log.info("Stopped");
|
||||
}
|
||||
@ -164,7 +207,7 @@ public class XconnectManager implements XconnectService {
|
||||
@Override
|
||||
public void addOrUpdateXconnect(DeviceId deviceId, VlanId vlanId, Set<PortNumber> ports) {
|
||||
log.info("Adding or updating xconnect. deviceId={}, vlanId={}, ports={}",
|
||||
deviceId, vlanId, ports);
|
||||
deviceId, vlanId, ports);
|
||||
final XconnectKey key = new XconnectKey(deviceId, vlanId);
|
||||
xconnectStore.put(key, ports);
|
||||
}
|
||||
@ -172,9 +215,15 @@ public class XconnectManager implements XconnectService {
|
||||
@Override
|
||||
public void removeXonnect(DeviceId deviceId, VlanId vlanId) {
|
||||
log.info("Removing xconnect. deviceId={}, vlanId={}",
|
||||
deviceId, vlanId);
|
||||
deviceId, vlanId);
|
||||
final XconnectKey key = new XconnectKey(deviceId, vlanId);
|
||||
xconnectStore.remove(key);
|
||||
|
||||
// Cleanup multicasting support, if any.
|
||||
srService.getPairDeviceId(deviceId).ifPresent(pairDeviceId -> {
|
||||
cleanupL2MulticastRule(pairDeviceId, srService.getPairLocalPort(pairDeviceId).get(), vlanId, true);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -187,10 +236,26 @@ public class XconnectManager implements XconnectService {
|
||||
@Override
|
||||
public boolean hasXconnect(ConnectPoint cp) {
|
||||
return getXconnects().stream().anyMatch(desc ->
|
||||
desc.key().deviceId().equals(cp.deviceId()) && desc.ports().contains(cp.port())
|
||||
desc.key().deviceId().equals(cp.deviceId())
|
||||
&& desc.ports().contains(cp.port())
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<VlanId> getXconnectVlans(DeviceId deviceId, PortNumber port) {
|
||||
return getXconnects().stream()
|
||||
.filter(desc -> desc.key().deviceId().equals(deviceId) && desc.ports().contains(port))
|
||||
.map(XconnectDesc::key)
|
||||
.map(XconnectKey::vlanId)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isXconnectVlan(DeviceId deviceId, VlanId vlanId) {
|
||||
return getXconnects().stream()
|
||||
.anyMatch(desc -> desc.key().deviceId().equals(deviceId) && desc.key().vlanId().equals(vlanId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableMap<XconnectKey, Integer> getNext() {
|
||||
if (xconnectNextObjStore != null) {
|
||||
@ -200,6 +265,15 @@ public class XconnectManager implements XconnectService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNextId(final DeviceId deviceId, final VlanId vlanId) {
|
||||
Optional<Integer> nextObjective = getNext().entrySet().stream()
|
||||
.filter(d -> d.getKey().deviceId().equals(deviceId) && d.getKey().vlanId().equals(vlanId))
|
||||
.findFirst()
|
||||
.map(Map.Entry::getValue);
|
||||
return nextObjective.isPresent() ? nextObjective.get() : -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeNextId(int nextId) {
|
||||
xconnectNextObjStore.entrySet().forEach(e -> {
|
||||
@ -258,6 +332,98 @@ public class XconnectManager implements XconnectService {
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalHostListener implements HostListener {
|
||||
@Override
|
||||
public void event(HostEvent event) {
|
||||
hostEventExecutor.execute(() -> {
|
||||
|
||||
switch (event.type()) {
|
||||
case HOST_MOVED:
|
||||
log.trace("Processing host event {}", event);
|
||||
|
||||
Host host = event.subject();
|
||||
Set<HostLocation> prevLocations = event.prevSubject().locations();
|
||||
Set<HostLocation> newLocations = host.locations();
|
||||
|
||||
// Dual-home host port failure
|
||||
// For each old location, in failed and paired devices update L2 vlan groups
|
||||
Sets.difference(prevLocations, newLocations).forEach(prevLocation -> {
|
||||
|
||||
Optional<DeviceId> pairDeviceId = srService.getPairDeviceId(prevLocation.deviceId());
|
||||
Optional<PortNumber> pairLocalPort = srService.getPairLocalPort(prevLocation.deviceId());
|
||||
|
||||
if (pairDeviceId.isPresent() && pairLocalPort.isPresent() && newLocations.stream()
|
||||
.anyMatch(location -> location.deviceId().equals(pairDeviceId.get())) &&
|
||||
hasXconnect(new ConnectPoint(prevLocation.deviceId(), prevLocation.port()))) {
|
||||
|
||||
List<VlanId> xconnectVlans = getXconnectVlans(prevLocation.deviceId(),
|
||||
prevLocation.port());
|
||||
xconnectVlans.forEach(xconnectVlan -> {
|
||||
// Add single-home host into L2 multicast group at paired device side.
|
||||
// Also append ACL rule to forward traffic from paired port to L2 multicast group.
|
||||
newLocations.stream()
|
||||
.filter(location -> location.deviceId().equals(pairDeviceId.get()))
|
||||
.forEach(location -> populateL2Multicast(location.deviceId(),
|
||||
srService.getPairLocalPort(
|
||||
location.deviceId()).get(),
|
||||
xconnectVlan,
|
||||
Collections.singletonList(
|
||||
location.port())));
|
||||
// Ensure pair-port attached to xconnect vlan flooding group at dual home failed device.
|
||||
updateL2Flooding(prevLocation.deviceId(), pairLocalPort.get(), xconnectVlan, true);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Dual-home host port restoration
|
||||
// For each new location, reverse xconnect loop prevention groups.
|
||||
Sets.difference(newLocations, prevLocations).forEach(newLocation -> {
|
||||
final Optional<DeviceId> pairDeviceId = srService.getPairDeviceId(newLocation.deviceId());
|
||||
Optional<PortNumber> pairLocalPort = srService.getPairLocalPort(newLocation.deviceId());
|
||||
if (pairDeviceId.isPresent() && pairLocalPort.isPresent() &&
|
||||
hasXconnect((new ConnectPoint(newLocation.deviceId(), newLocation.port())))) {
|
||||
|
||||
List<VlanId> xconnectVlans = getXconnectVlans(newLocation.deviceId(),
|
||||
newLocation.port());
|
||||
xconnectVlans.forEach(xconnectVlan -> {
|
||||
// Remove recovered dual homed port from vlan L2 multicast group
|
||||
prevLocations.stream()
|
||||
.filter(prevLocation -> prevLocation.deviceId().equals(pairDeviceId.get()))
|
||||
.forEach(prevLocation -> revokeL2Multicast(prevLocation.deviceId(),
|
||||
srService.getPairLocalPort(
|
||||
prevLocation.deviceId()).get(),
|
||||
xconnectVlan,
|
||||
Collections.singletonList(newLocation.port()))
|
||||
);
|
||||
|
||||
// Remove pair-port from vlan's flooding group at dual home restored device,if needed.
|
||||
if (!hasAccessPortInMulticastGroup(newLocation.deviceId(),
|
||||
xconnectVlan,
|
||||
pairLocalPort.get())) {
|
||||
updateL2Flooding(newLocation.deviceId(),
|
||||
pairLocalPort.get(),
|
||||
xconnectVlan,
|
||||
false);
|
||||
|
||||
// Clean L2 multicast group at pair-device; also update store.
|
||||
cleanupL2MulticastRule(pairDeviceId.get(),
|
||||
srService.getPairLocalPort(pairDeviceId.get()).get(),
|
||||
xconnectVlan,
|
||||
false);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
log.warn("Unsupported host event type: {} received. Ignoring.", event.type());
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void init(DeviceId deviceId) {
|
||||
getXconnects().stream()
|
||||
.filter(desc -> desc.key().deviceId().equals(deviceId))
|
||||
@ -274,7 +440,7 @@ public class XconnectManager implements XconnectService {
|
||||
/**
|
||||
* Populates XConnect groups and flows for given key.
|
||||
*
|
||||
* @param key XConnect key
|
||||
* @param key XConnect key
|
||||
* @param ports a set of ports to be cross-connected
|
||||
*/
|
||||
private void populateXConnect(XconnectKey key, Set<PortNumber> ports) {
|
||||
@ -283,7 +449,6 @@ public class XconnectManager implements XconnectService {
|
||||
return;
|
||||
}
|
||||
|
||||
ports = addPairPort(key.deviceId(), ports);
|
||||
populateFilter(key, ports);
|
||||
populateFwd(key, populateNext(key, ports));
|
||||
populateAcl(key);
|
||||
@ -292,7 +457,7 @@ public class XconnectManager implements XconnectService {
|
||||
/**
|
||||
* Populates filtering objectives for given XConnect.
|
||||
*
|
||||
* @param key XConnect store key
|
||||
* @param key XConnect store key
|
||||
* @param ports XConnect ports
|
||||
*/
|
||||
private void populateFilter(XconnectKey key, Set<PortNumber> ports) {
|
||||
@ -300,10 +465,10 @@ public class XconnectManager implements XconnectService {
|
||||
FilteringObjective.Builder filtObjBuilder = filterObjBuilder(key, port);
|
||||
ObjectiveContext context = new DefaultObjectiveContext(
|
||||
(objective) -> log.debug("XConnect FilterObj for {} on port {} populated",
|
||||
key, port),
|
||||
key, port),
|
||||
(objective, error) ->
|
||||
log.warn("Failed to populate XConnect FilterObj for {} on port {}: {}",
|
||||
key, port, error));
|
||||
key, port, error));
|
||||
flowObjectiveService.filter(key.deviceId(), filtObjBuilder.add(context));
|
||||
});
|
||||
}
|
||||
@ -311,7 +476,7 @@ public class XconnectManager implements XconnectService {
|
||||
/**
|
||||
* Populates next objectives for given XConnect.
|
||||
*
|
||||
* @param key XConnect store key
|
||||
* @param key XConnect store key
|
||||
* @param ports XConnect ports
|
||||
*/
|
||||
private int populateNext(XconnectKey key, Set<PortNumber> ports) {
|
||||
@ -340,7 +505,7 @@ public class XconnectManager implements XconnectService {
|
||||
/**
|
||||
* Populates bridging forwarding objectives for given XConnect.
|
||||
*
|
||||
* @param key XConnect store key
|
||||
* @param key XConnect store key
|
||||
* @param nextId next objective id
|
||||
*/
|
||||
private void populateFwd(XconnectKey key, int nextId) {
|
||||
@ -369,7 +534,7 @@ public class XconnectManager implements XconnectService {
|
||||
/**
|
||||
* Revokes XConnect groups and flows for given key.
|
||||
*
|
||||
* @param key XConnect key
|
||||
* @param key XConnect key
|
||||
* @param ports XConnect ports
|
||||
*/
|
||||
private void revokeXConnect(XconnectKey key, Set<PortNumber> ports) {
|
||||
@ -378,7 +543,6 @@ public class XconnectManager implements XconnectService {
|
||||
return;
|
||||
}
|
||||
|
||||
ports = addPairPort(key.deviceId(), ports);
|
||||
revokeFilter(key, ports);
|
||||
if (xconnectNextObjStore.containsKey(key)) {
|
||||
int nextId = xconnectNextObjStore.get(key).value();
|
||||
@ -393,7 +557,7 @@ public class XconnectManager implements XconnectService {
|
||||
/**
|
||||
* Revokes filtering objectives for given XConnect.
|
||||
*
|
||||
* @param key XConnect store key
|
||||
* @param key XConnect store key
|
||||
* @param ports XConnect ports
|
||||
*/
|
||||
private void revokeFilter(XconnectKey key, Set<PortNumber> ports) {
|
||||
@ -401,10 +565,10 @@ public class XconnectManager implements XconnectService {
|
||||
FilteringObjective.Builder filtObjBuilder = filterObjBuilder(key, port);
|
||||
ObjectiveContext context = new DefaultObjectiveContext(
|
||||
(objective) -> log.debug("XConnect FilterObj for {} on port {} revoked",
|
||||
key, port),
|
||||
key, port),
|
||||
(objective, error) ->
|
||||
log.warn("Failed to revoke XConnect FilterObj for {} on port {}: {}",
|
||||
key, port, error));
|
||||
key, port, error));
|
||||
flowObjectiveService.filter(key.deviceId(), filtObjBuilder.remove(context));
|
||||
});
|
||||
}
|
||||
@ -412,9 +576,9 @@ public class XconnectManager implements XconnectService {
|
||||
/**
|
||||
* Revokes next objectives for given XConnect.
|
||||
*
|
||||
* @param key XConnect store key
|
||||
* @param ports ports in the XConnect
|
||||
* @param nextId next objective id
|
||||
* @param key XConnect store key
|
||||
* @param ports ports in the XConnect
|
||||
* @param nextId next objective id
|
||||
* @param nextFuture completable future for this next objective operation
|
||||
*/
|
||||
private void revokeNext(XconnectKey key, Set<PortNumber> ports, int nextId,
|
||||
@ -444,8 +608,8 @@ public class XconnectManager implements XconnectService {
|
||||
/**
|
||||
* Revokes bridging forwarding objectives for given XConnect.
|
||||
*
|
||||
* @param key XConnect store key
|
||||
* @param nextId next objective id
|
||||
* @param key XConnect store key
|
||||
* @param nextId next objective id
|
||||
* @param fwdFuture completable future for this forwarding objective operation
|
||||
*/
|
||||
private void revokeFwd(XconnectKey key, int nextId, CompletableFuture<ObjectiveError> fwdFuture) {
|
||||
@ -487,9 +651,9 @@ public class XconnectManager implements XconnectService {
|
||||
/**
|
||||
* Updates XConnect groups and flows for given key.
|
||||
*
|
||||
* @param key XConnect key
|
||||
* @param key XConnect key
|
||||
* @param prevPorts previous XConnect ports
|
||||
* @param ports new XConnect ports
|
||||
* @param ports new XConnect ports
|
||||
*/
|
||||
private void updateXConnect(XconnectKey key, Set<PortNumber> prevPorts,
|
||||
Set<PortNumber> ports) {
|
||||
@ -498,10 +662,12 @@ public class XconnectManager implements XconnectService {
|
||||
|
||||
// remove old filter
|
||||
prevPorts.stream().filter(port -> !ports.contains(port)).forEach(port ->
|
||||
revokeFilter(key, ImmutableSet.of(port)));
|
||||
revokeFilter(key,
|
||||
ImmutableSet.of(port)));
|
||||
// install new filter
|
||||
ports.stream().filter(port -> !prevPorts.contains(port)).forEach(port ->
|
||||
populateFilter(key, ImmutableSet.of(port)));
|
||||
populateFilter(key,
|
||||
ImmutableSet.of(port)));
|
||||
|
||||
CompletableFuture<ObjectiveError> fwdFuture = new CompletableFuture<>();
|
||||
CompletableFuture<ObjectiveError> nextFuture = new CompletableFuture<>();
|
||||
@ -531,8 +697,8 @@ public class XconnectManager implements XconnectService {
|
||||
/**
|
||||
* Creates a next objective builder for XConnect with given nextId.
|
||||
*
|
||||
* @param key XConnect key
|
||||
* @param ports set of XConnect ports
|
||||
* @param key XConnect key
|
||||
* @param ports set of XConnect ports
|
||||
* @param nextId next objective id
|
||||
* @return next objective builder
|
||||
*/
|
||||
@ -554,7 +720,7 @@ public class XconnectManager implements XconnectService {
|
||||
/**
|
||||
* Creates a next objective builder for XConnect.
|
||||
*
|
||||
* @param key XConnect key
|
||||
* @param key XConnect key
|
||||
* @param ports set of XConnect ports
|
||||
* @return next objective builder
|
||||
*/
|
||||
@ -567,7 +733,7 @@ public class XconnectManager implements XconnectService {
|
||||
/**
|
||||
* Creates a bridging forwarding objective builder for XConnect.
|
||||
*
|
||||
* @param key XConnect key
|
||||
* @param key XConnect key
|
||||
* @param nextId next ID of the broadcast group for this XConnect key
|
||||
* @return forwarding objective builder
|
||||
*/
|
||||
@ -615,7 +781,7 @@ public class XconnectManager implements XconnectService {
|
||||
/**
|
||||
* Creates a filtering objective builder for XConnect.
|
||||
*
|
||||
* @param key XConnect key
|
||||
* @param key XConnect key
|
||||
* @param port XConnect ports
|
||||
* @return next objective builder
|
||||
*/
|
||||
@ -629,18 +795,320 @@ public class XconnectManager implements XconnectService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add pair port to the given set of port.
|
||||
* Updates L2 flooding groups; add pair link into L2 flooding group of given xconnect vlan.
|
||||
*
|
||||
* @param deviceId device Id
|
||||
* @param ports ports specified in the xconnect config
|
||||
* @return port specified in the xconnect config plus the pair port (if configured)
|
||||
* @param deviceId Device ID
|
||||
* @param port Port details
|
||||
* @param vlanId VLAN ID
|
||||
* @param install Whether to add or revoke pair link addition to flooding group
|
||||
*/
|
||||
private Set<PortNumber> addPairPort(DeviceId deviceId, Set<PortNumber> ports) {
|
||||
if (srService == null) {
|
||||
return ports;
|
||||
private void updateL2Flooding(DeviceId deviceId, final PortNumber port, VlanId vlanId, boolean install) {
|
||||
|
||||
// Ensure mastership on device
|
||||
if (!mastershipService.isLocalMaster(deviceId)) {
|
||||
return;
|
||||
}
|
||||
Set<PortNumber> newPorts = Sets.newHashSet(ports);
|
||||
srService.getPairLocalPort(deviceId).ifPresent(newPorts::add);
|
||||
return newPorts;
|
||||
|
||||
// Locate L2 flooding group details for given xconnect vlan
|
||||
int nextId = getNextId(deviceId, vlanId);
|
||||
if (nextId == -1) {
|
||||
log.debug("XConnect vlan {} broadcast group for device {} doesn't exists. " +
|
||||
"Aborting pair group linking.", vlanId, deviceId);
|
||||
return;
|
||||
}
|
||||
|
||||
// Add pairing-port group to flooding group
|
||||
TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
|
||||
// treatment.popVlan();
|
||||
treatment.setOutput(port);
|
||||
ObjectiveContext context = new DefaultObjectiveContext(
|
||||
(objective) ->
|
||||
log.debug("Pair port added/removed to vlan {} next objective {} on {}",
|
||||
vlanId, nextId, deviceId),
|
||||
(objective, error) ->
|
||||
log.warn("Failed adding/removing pair port to vlan {} next objective {} on {}." +
|
||||
"Error : {}", vlanId, nextId, deviceId, error)
|
||||
);
|
||||
NextObjective.Builder vlanNextObjectiveBuilder = DefaultNextObjective.builder()
|
||||
.withId(nextId)
|
||||
.withType(NextObjective.Type.BROADCAST)
|
||||
.fromApp(srService.appId())
|
||||
.withMeta(DefaultTrafficSelector.builder().matchVlanId(vlanId).build())
|
||||
.addTreatment(treatment.build());
|
||||
if (install) {
|
||||
flowObjectiveService.next(deviceId, vlanNextObjectiveBuilder.addToExisting(context));
|
||||
} else {
|
||||
flowObjectiveService.next(deviceId, vlanNextObjectiveBuilder.removeFromExisting(context));
|
||||
}
|
||||
log.debug("Submitted next objective {} for vlan: {} in device {}",
|
||||
nextId, vlanId, deviceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate L2 multicast rule on given deviceId that matches given mac, given vlan and
|
||||
* output to given port's L2 mulitcast group.
|
||||
*
|
||||
* @param deviceId Device ID
|
||||
* @param pairPort Pair port number
|
||||
* @param vlanId VLAN ID
|
||||
* @param accessPorts List of access ports to be added into L2 multicast group
|
||||
*/
|
||||
private void populateL2Multicast(DeviceId deviceId, final PortNumber pairPort,
|
||||
VlanId vlanId, List<PortNumber> accessPorts) {
|
||||
|
||||
boolean multicastGroupExists = true;
|
||||
int vlanMulticastNextId;
|
||||
|
||||
// Ensure enough rights to program pair device
|
||||
if (!srService.shouldProgram(deviceId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 1 : Populate single homed access ports into vlan's L2 multicast group
|
||||
NextObjective.Builder vlanMulticastNextObjBuilder = DefaultNextObjective
|
||||
.builder()
|
||||
.withType(NextObjective.Type.BROADCAST)
|
||||
.fromApp(srService.appId())
|
||||
.withMeta(DefaultTrafficSelector.builder().matchVlanId(vlanId)
|
||||
.matchEthDst(MacAddress.IPV4_MULTICAST).build());
|
||||
vlanMulticastNextId = getMulticastGroupNextObjectiveId(deviceId, vlanId);
|
||||
if (vlanMulticastNextId == -1) {
|
||||
// Vlan's L2 multicast group doesn't exist; create it, update store and add pair port as sub-group
|
||||
multicastGroupExists = false;
|
||||
vlanMulticastNextId = flowObjectiveService.allocateNextId();
|
||||
addMulticastGroupNextObjectiveId(deviceId, vlanId, vlanMulticastNextId);
|
||||
vlanMulticastNextObjBuilder.addTreatment(
|
||||
DefaultTrafficTreatment.builder().popVlan().setOutput(pairPort).build()
|
||||
);
|
||||
}
|
||||
vlanMulticastNextObjBuilder.withId(vlanMulticastNextId);
|
||||
final int nextId = vlanMulticastNextId;
|
||||
accessPorts.forEach(p -> {
|
||||
TrafficTreatment.Builder egressAction = DefaultTrafficTreatment.builder();
|
||||
// Do vlan popup action based on interface configuration
|
||||
if (interfaceService.getInterfacesByPort(new ConnectPoint(deviceId, p))
|
||||
.stream().noneMatch(i -> i.vlanTagged().contains(vlanId))) {
|
||||
egressAction.popVlan();
|
||||
}
|
||||
egressAction.setOutput(p);
|
||||
vlanMulticastNextObjBuilder.addTreatment(egressAction.build());
|
||||
addMulticastGroupPort(deviceId, vlanId, p);
|
||||
});
|
||||
ObjectiveContext context = new DefaultObjectiveContext(
|
||||
(objective) ->
|
||||
log.debug("L2 multicast group installed/updated. "
|
||||
+ "NextObject Id {} on {} for subnet {} ",
|
||||
nextId, deviceId, vlanId),
|
||||
(objective, error) ->
|
||||
log.warn("L2 multicast group failed to install/update. "
|
||||
+ " NextObject Id {} on {} for subnet {} : {}",
|
||||
nextId, deviceId, vlanId, error)
|
||||
);
|
||||
if (!multicastGroupExists) {
|
||||
flowObjectiveService.next(deviceId, vlanMulticastNextObjBuilder.add(context));
|
||||
|
||||
// Step 2 : Populate ACL rule; selector = vlan + pair-port, output = vlan L2 multicast group
|
||||
TrafficSelector.Builder multicastSelector = DefaultTrafficSelector.builder();
|
||||
multicastSelector.matchEthType(Ethernet.TYPE_VLAN);
|
||||
multicastSelector.matchInPort(pairPort);
|
||||
multicastSelector.matchVlanId(vlanId);
|
||||
ForwardingObjective.Builder vlanMulticastForwardingObj = DefaultForwardingObjective.builder()
|
||||
.withFlag(ForwardingObjective.Flag.VERSATILE)
|
||||
.nextStep(vlanMulticastNextId)
|
||||
.withSelector(multicastSelector.build())
|
||||
.withPriority(100)
|
||||
.fromApp(srService.appId())
|
||||
.makePermanent();
|
||||
context = new DefaultObjectiveContext(
|
||||
(objective) -> log.debug("L2 multicasting versatile rule for device {}, port/vlan {}/{} populated",
|
||||
deviceId,
|
||||
pairPort,
|
||||
vlanId),
|
||||
(objective, error) -> log.warn("Failed to populate L2 multicasting versatile rule for device {}, " +
|
||||
"ports/vlan {}/{}: {}", deviceId, pairPort, vlanId, error));
|
||||
flowObjectiveService.forward(deviceId, vlanMulticastForwardingObj.add(context));
|
||||
} else {
|
||||
// L2_MULTICAST & BROADCAST are similar structure in subgroups; so going with BROADCAST type.
|
||||
vlanMulticastNextObjBuilder.withType(NextObjective.Type.BROADCAST);
|
||||
flowObjectiveService.next(deviceId, vlanMulticastNextObjBuilder.addToExisting(context));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes access ports from VLAN L2 multicast group on given deviceId.
|
||||
*
|
||||
* @param deviceId Device ID
|
||||
* @param pairPort Pair port number
|
||||
* @param vlanId VLAN ID
|
||||
* @param accessPorts List of access ports to be added into L2 multicast group
|
||||
*/
|
||||
private void revokeL2Multicast(DeviceId deviceId, final PortNumber pairPort,
|
||||
VlanId vlanId, List<PortNumber> accessPorts) {
|
||||
|
||||
// Ensure enough rights to program pair device
|
||||
if (!srService.shouldProgram(deviceId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
int vlanMulticastNextId = getMulticastGroupNextObjectiveId(deviceId, vlanId);
|
||||
if (vlanMulticastNextId == -1) {
|
||||
return;
|
||||
}
|
||||
NextObjective.Builder vlanMulticastNextObjBuilder = DefaultNextObjective
|
||||
.builder()
|
||||
.withType(NextObjective.Type.BROADCAST)
|
||||
.fromApp(srService.appId())
|
||||
.withMeta(DefaultTrafficSelector.builder().matchVlanId(vlanId).build())
|
||||
.withId(vlanMulticastNextId);
|
||||
accessPorts.forEach(p -> {
|
||||
TrafficTreatment.Builder egressAction = DefaultTrafficTreatment.builder();
|
||||
// Do vlan popup action based on interface configuration
|
||||
if (interfaceService.getInterfacesByPort(new ConnectPoint(deviceId, p))
|
||||
.stream().noneMatch(i -> i.vlanTagged().contains(vlanId))) {
|
||||
egressAction.popVlan();
|
||||
}
|
||||
egressAction.setOutput(p);
|
||||
vlanMulticastNextObjBuilder.addTreatment(egressAction.build());
|
||||
removeMulticastGroupPort(deviceId, vlanId, p);
|
||||
});
|
||||
ObjectiveContext context = new DefaultObjectiveContext(
|
||||
(objective) ->
|
||||
log.debug("L2 multicast group installed/updated. "
|
||||
+ "NextObject Id {} on {} for subnet {} ",
|
||||
vlanMulticastNextId, deviceId, vlanId),
|
||||
(objective, error) ->
|
||||
log.warn("L2 multicast group failed to install/update. "
|
||||
+ " NextObject Id {} on {} for subnet {} : {}",
|
||||
vlanMulticastNextId, deviceId, vlanId, error)
|
||||
);
|
||||
flowObjectiveService.next(deviceId, vlanMulticastNextObjBuilder.removeFromExisting(context));
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up VLAN L2 multicast group on given deviceId. ACL rules for the group will also be deleted.
|
||||
* Normally multicast group is not removed if it contains access ports; which can be forced
|
||||
* by "force" flag
|
||||
*
|
||||
* @param deviceId Device ID
|
||||
* @param pairPort Pair port number
|
||||
* @param vlanId VLAN ID
|
||||
* @param force Forceful removal
|
||||
*/
|
||||
private void cleanupL2MulticastRule(DeviceId deviceId, PortNumber pairPort, VlanId vlanId, boolean force) {
|
||||
|
||||
// Ensure enough rights to program pair device
|
||||
if (!srService.shouldProgram(deviceId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure L2 multicast group doesn't contain access ports
|
||||
if (hasAccessPortInMulticastGroup(deviceId, vlanId, pairPort) && !force) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Load L2 multicast group details
|
||||
int vlanMulticastNextId = getMulticastGroupNextObjectiveId(deviceId, vlanId);
|
||||
if (vlanMulticastNextId == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 1 : Clear ACL rule; selector = vlan + pair-port, output = vlan L2 multicast group
|
||||
TrafficSelector.Builder l2MulticastSelector = DefaultTrafficSelector.builder();
|
||||
l2MulticastSelector.matchEthType(Ethernet.TYPE_VLAN);
|
||||
l2MulticastSelector.matchInPort(pairPort);
|
||||
l2MulticastSelector.matchVlanId(vlanId);
|
||||
ForwardingObjective.Builder vlanMulticastForwardingObj = DefaultForwardingObjective.builder()
|
||||
.withFlag(ForwardingObjective.Flag.VERSATILE)
|
||||
.nextStep(vlanMulticastNextId)
|
||||
.withSelector(l2MulticastSelector.build())
|
||||
.withPriority(100)
|
||||
.fromApp(srService.appId())
|
||||
.makePermanent();
|
||||
ObjectiveContext context = new DefaultObjectiveContext(
|
||||
(objective) -> log.debug("L2 multicasting rule for device {}, port/vlan {}/{} deleted", deviceId,
|
||||
pairPort, vlanId),
|
||||
(objective, error) -> log.warn("Failed to delete L2 multicasting rule for device {}, " +
|
||||
"ports/vlan {}/{}: {}", deviceId, pairPort, vlanId, error));
|
||||
flowObjectiveService.forward(deviceId, vlanMulticastForwardingObj.remove(context));
|
||||
|
||||
// Step 2 : Clear L2 multicast group associated with vlan
|
||||
NextObjective.Builder l2MulticastGroupBuilder = DefaultNextObjective
|
||||
.builder()
|
||||
.withId(vlanMulticastNextId)
|
||||
.withType(NextObjective.Type.BROADCAST)
|
||||
.fromApp(srService.appId())
|
||||
.withMeta(DefaultTrafficSelector.builder()
|
||||
.matchVlanId(vlanId)
|
||||
.matchEthDst(MacAddress.IPV4_MULTICAST).build())
|
||||
.addTreatment(DefaultTrafficTreatment.builder().popVlan().setOutput(pairPort).build());
|
||||
context = new DefaultObjectiveContext(
|
||||
(objective) ->
|
||||
log.debug("L2 multicast group with NextObject Id {} deleted on {} for subnet {} ",
|
||||
vlanMulticastNextId, deviceId, vlanId),
|
||||
(objective, error) ->
|
||||
log.warn("L2 multicast group with NextObject Id {} failed to delete on {} for subnet {} : {}",
|
||||
vlanMulticastNextId, deviceId, vlanId, error)
|
||||
);
|
||||
flowObjectiveService.next(deviceId, l2MulticastGroupBuilder.remove(context));
|
||||
|
||||
// Finally clear store.
|
||||
removeMulticastGroup(deviceId, vlanId);
|
||||
}
|
||||
|
||||
private boolean isMulticastGroupExists(DeviceId deviceId, VlanId vlanId) {
|
||||
return xconnectMulticastNextStore.asJavaMap().entrySet().stream()
|
||||
.anyMatch(e -> e.getKey().deviceId().equals(deviceId) &&
|
||||
e.getKey().vlanId().equals(vlanId));
|
||||
}
|
||||
|
||||
private int getMulticastGroupNextObjectiveId(DeviceId deviceId, VlanId vlanId) {
|
||||
Optional<Integer> nextId
|
||||
= xconnectMulticastNextStore.asJavaMap().entrySet().stream()
|
||||
.filter(e -> e.getKey().deviceId().equals(deviceId) &&
|
||||
e.getKey().vlanId().equals(vlanId))
|
||||
.findFirst()
|
||||
.map(Map.Entry::getValue);
|
||||
return nextId.orElse(-1);
|
||||
}
|
||||
|
||||
private void addMulticastGroupNextObjectiveId(DeviceId deviceId, VlanId vlanId, int nextId) {
|
||||
if (nextId == -1) {
|
||||
return;
|
||||
}
|
||||
VlanNextObjectiveStoreKey key = new VlanNextObjectiveStoreKey(deviceId, vlanId);
|
||||
xconnectMulticastNextStore.put(key, nextId);
|
||||
|
||||
// Update port store with empty entry.
|
||||
xconnectMulticastPortsStore.put(key, new ArrayList<PortNumber>());
|
||||
}
|
||||
|
||||
private void addMulticastGroupPort(DeviceId deviceId, VlanId vlanId, PortNumber port) {
|
||||
VlanNextObjectiveStoreKey key = new VlanNextObjectiveStoreKey(deviceId, vlanId);
|
||||
List<PortNumber> ports = xconnectMulticastPortsStore.get(key).value();
|
||||
ports.add(port);
|
||||
xconnectMulticastPortsStore.put(key, ports);
|
||||
}
|
||||
|
||||
private void removeMulticastGroupPort(DeviceId deviceId, VlanId vlanId, PortNumber port) {
|
||||
VlanNextObjectiveStoreKey key = new VlanNextObjectiveStoreKey(deviceId, vlanId);
|
||||
List<PortNumber> ports = xconnectMulticastPortsStore.get(key).value();
|
||||
ports.remove(port);
|
||||
xconnectMulticastPortsStore.put(key, ports);
|
||||
}
|
||||
|
||||
private void removeMulticastGroup(DeviceId deviceId, VlanId vlanId) {
|
||||
VlanNextObjectiveStoreKey key = new VlanNextObjectiveStoreKey(deviceId, vlanId);
|
||||
xconnectMulticastPortsStore.remove(key);
|
||||
xconnectMulticastNextStore.remove(key);
|
||||
}
|
||||
|
||||
private boolean hasAccessPortInMulticastGroup(DeviceId deviceId, VlanId vlanId, PortNumber pairPort) {
|
||||
VlanNextObjectiveStoreKey key = new VlanNextObjectiveStoreKey(deviceId, vlanId);
|
||||
if (!xconnectMulticastPortsStore.containsKey(key)) {
|
||||
return false;
|
||||
}
|
||||
List<PortNumber> ports = xconnectMulticastPortsStore.get(key).value();
|
||||
return ports.stream().anyMatch(p -> !p.equals(pairPort));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
12
apps/workflow/BUCK
Normal file
12
apps/workflow/BUCK
Normal file
@ -0,0 +1,12 @@
|
||||
BUNDLES = [
|
||||
'//apps/workflow/api:onos-apps-workflow-api',
|
||||
'//apps/workflow/app:onos-apps-workflow-app',
|
||||
]
|
||||
|
||||
onos_app (
|
||||
category = 'Utility',
|
||||
description = "Workflow application",
|
||||
included_bundles = BUNDLES,
|
||||
title = 'Workflow',
|
||||
url = 'http://onosproject.org',
|
||||
)
|
||||
12
apps/workflow/BUILD
Normal file
12
apps/workflow/BUILD
Normal file
@ -0,0 +1,12 @@
|
||||
BUNDLES = [
|
||||
"//apps/workflow/api:onos-apps-workflow-api",
|
||||
"//apps/workflow/app:onos-apps-workflow-app",
|
||||
]
|
||||
|
||||
onos_app(
|
||||
category = "Utility",
|
||||
description = "Workflow application",
|
||||
included_bundles = BUNDLES,
|
||||
title = "Workflow",
|
||||
url = "http://onosproject.org",
|
||||
)
|
||||
11
apps/workflow/api/BUCK
Normal file
11
apps/workflow/api/BUCK
Normal file
@ -0,0 +1,11 @@
|
||||
COMPILE_DEPS = [
|
||||
'//lib:CORE_DEPS',
|
||||
'//lib:jackson-core',
|
||||
'//lib:jackson-annotations',
|
||||
'//lib:jackson-databind',
|
||||
'//core/store/serializers:onos-core-serializers',
|
||||
]
|
||||
|
||||
osgi_jar_with_tests (
|
||||
deps = COMPILE_DEPS,
|
||||
)
|
||||
7
apps/workflow/api/BUILD
Normal file
7
apps/workflow/api/BUILD
Normal file
@ -0,0 +1,7 @@
|
||||
COMPILE_DEPS = CORE_DEPS + KRYO + JACKSON + [
|
||||
"//core/store/serializers:onos-core-serializers",
|
||||
]
|
||||
|
||||
osgi_jar_with_tests(
|
||||
deps = COMPILE_DEPS,
|
||||
)
|
||||
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.workflow.api;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
/**
|
||||
* Abstract class for workflow.
|
||||
*/
|
||||
public abstract class AbstractWorkflow implements Workflow {
|
||||
|
||||
/**
|
||||
* ID of workflow.
|
||||
*/
|
||||
private URI id;
|
||||
|
||||
/**
|
||||
* Constructor for AbstractWorkflow.
|
||||
* @param id ID of workflow
|
||||
*/
|
||||
protected AbstractWorkflow(URI id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI id() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorkflowContext buildContext(Workplace workplace, DataModelTree data) throws WorkflowException {
|
||||
return new DefaultWorkflowContext(id, workplace.name(), data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorkflowContext buildSystemContext(Workplace workplace, DataModelTree data) throws WorkflowException {
|
||||
return new SystemWorkflowContext(id, workplace.name(), data);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.workflow.api;
|
||||
|
||||
|
||||
import org.onosproject.event.Event;
|
||||
|
||||
/**
|
||||
* Abstract class for worklet.
|
||||
*/
|
||||
public abstract class AbstractWorklet implements Worklet {
|
||||
|
||||
@Override
|
||||
public String tag() {
|
||||
return this.getClass().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCompleted(WorkflowContext context, Event event)throws WorkflowException {
|
||||
throw new WorkflowException("(" + tag() + ").isCompleted should not be called");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNext(WorkflowContext context) throws WorkflowException {
|
||||
throw new WorkflowException("(" + tag() + ").isNext should not be called");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void timeout(WorkflowContext context) throws WorkflowException {
|
||||
throw new WorkflowException("Timeout happened");
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.workflow.api;
|
||||
|
||||
/**
|
||||
* An interface representing branch worklet. Branch worklet is used for branching workflow execution.
|
||||
*/
|
||||
public interface BranchWorklet extends Worklet {
|
||||
|
||||
@Override
|
||||
default void process(WorkflowContext context) throws WorkflowException {
|
||||
throw new WorkflowException("This workletType.process should not be called");
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean isNext(WorkflowContext context) throws WorkflowException {
|
||||
throw new WorkflowException("This workletType.isNext should not be called");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns next worklet class for branching.
|
||||
* @param context workflow context
|
||||
* @return next worklet class
|
||||
* @throws WorkflowException workflow exception
|
||||
*/
|
||||
Class<? extends Worklet> next(WorkflowContext context) throws WorkflowException;
|
||||
}
|
||||
|
||||
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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.workflow.api;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import org.onosproject.store.service.DocumentPath;
|
||||
import org.onosproject.store.service.Versioned;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* WorkflowContext Event Map Store.
|
||||
*/
|
||||
public interface ContextEventMapStore {
|
||||
|
||||
/**
|
||||
* Registers workflow context event mapping.
|
||||
* @param eventType the class name of event
|
||||
* @param eventHint event hint string value of the event
|
||||
* @param contextName workflow context name
|
||||
* @param workletType the class name of worklet
|
||||
* @throws WorkflowException workflow exception
|
||||
*/
|
||||
void registerEventMap(String eventType, String eventHint,
|
||||
String contextName, String workletType) throws WorkflowException;
|
||||
|
||||
/**
|
||||
* Unregisters workflow context event mapping.
|
||||
* @param eventType the class name of event
|
||||
* @param eventHint event hint string value of the event
|
||||
* @param contextName workflow context name
|
||||
* @throws WorkflowException workflow exception
|
||||
*/
|
||||
void unregisterEventMap(String eventType, String eventHint,
|
||||
String contextName) throws WorkflowException;
|
||||
|
||||
/**
|
||||
* Returns workflow context event mapping.
|
||||
* @param eventType the class name of event
|
||||
* @param eventHint event hint string value of the event
|
||||
* @return workflow context event mapping
|
||||
* @throws WorkflowException workflow exception
|
||||
*/
|
||||
Map<String, String> getEventMap(String eventType, String eventHint) throws WorkflowException;
|
||||
|
||||
/**
|
||||
* Returns child nodes on document tree path.
|
||||
* @param path document tree path
|
||||
* @return children under document tree path
|
||||
* @throws WorkflowException workflow exception
|
||||
*/
|
||||
Map<String, Versioned<String>> getChildren(String path) throws WorkflowException;
|
||||
|
||||
/**
|
||||
* Returns document path.
|
||||
* @param path document path string
|
||||
* @return document tree
|
||||
* @throws WorkflowException workflow exception
|
||||
*/
|
||||
DocumentPath getDocumentPath(String path) throws WorkflowException;
|
||||
|
||||
/**
|
||||
* Transforms document tree to json tree.
|
||||
* @return json tree
|
||||
* @throws WorkflowException workflow exception
|
||||
*/
|
||||
ObjectNode asJsonTree() throws WorkflowException;
|
||||
}
|
||||
|
||||
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.workflow.api;
|
||||
|
||||
/**
|
||||
* Common data model tree path.
|
||||
*/
|
||||
public interface DataModelPointer {
|
||||
|
||||
/**
|
||||
* Workplace array pointer.
|
||||
*/
|
||||
String WORKPLACES_PTR = "/workplaces";
|
||||
|
||||
/**
|
||||
* Workplace name pointer.
|
||||
*/
|
||||
String WORKPLACE_NAME_PTR = "/name";
|
||||
|
||||
/**
|
||||
* Workplace data pointer.
|
||||
*/
|
||||
String WORKPLACE_DATA_PTR = "/data";
|
||||
|
||||
/**
|
||||
* Workplace workflow pointer.
|
||||
*/
|
||||
String WORKPLACE_WORKFLOWS_PTR = "/workflows";
|
||||
|
||||
/**
|
||||
* Workflow op pointer.
|
||||
*/
|
||||
String WORKFLOW_OP_PTR = "/op";
|
||||
|
||||
/**
|
||||
* Workflow id pointer.
|
||||
*/
|
||||
String WORKFLOW_ID_PTR = "/id";
|
||||
|
||||
/**
|
||||
* Workflow data pointer.
|
||||
*/
|
||||
String WORKFLOW_DATA_PTR = "/data";
|
||||
|
||||
/**
|
||||
* Gets path string.
|
||||
* @return path string
|
||||
*/
|
||||
String getPath();
|
||||
}
|
||||
|
||||
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.workflow.api;
|
||||
|
||||
/**
|
||||
* Interface for data model tree.
|
||||
*/
|
||||
public interface DataModelTree {
|
||||
|
||||
/**
|
||||
* Data model tree node type (map or array).
|
||||
*/
|
||||
enum Nodetype {
|
||||
|
||||
/**
|
||||
* Map type data model tree node.
|
||||
*/
|
||||
MAP,
|
||||
|
||||
/**
|
||||
* Array type data model tree node.
|
||||
*/
|
||||
ARRAY
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns subtree on the path.
|
||||
* @param path data model tree path
|
||||
* @return subtree on the path
|
||||
*/
|
||||
DataModelTree subtree(String path);
|
||||
|
||||
/**
|
||||
* Attaches subtree on the path.
|
||||
* @param path data model tree path where subtree will be attached
|
||||
* @param tree subtree to be attached
|
||||
* @throws WorkflowException workflow exception
|
||||
*/
|
||||
void attach(String path, DataModelTree tree) throws WorkflowException;
|
||||
|
||||
/**
|
||||
* Allocates leaf node on the path.
|
||||
* @param path data model tree path where new leaf node will be allocated
|
||||
* @param leaftype leaf node type
|
||||
* @return data model tree
|
||||
* @throws WorkflowException workflow exception
|
||||
*/
|
||||
DataModelTree alloc(String path, Nodetype leaftype) throws WorkflowException;
|
||||
}
|
||||
|
||||
@ -0,0 +1,177 @@
|
||||
/*
|
||||
* 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.workflow.api;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.MissingNode;
|
||||
import com.fasterxml.jackson.databind.node.TextNode;
|
||||
import com.google.common.base.MoreObjects;
|
||||
|
||||
/**
|
||||
* Class of workflow RPC description.
|
||||
*/
|
||||
public final class DefaultRpcDescription implements RpcDescription {
|
||||
|
||||
/**
|
||||
* Workflow RPC operation.
|
||||
*/
|
||||
private final String op;
|
||||
|
||||
/**
|
||||
* Parameters.
|
||||
*/
|
||||
private final JsonNode params;
|
||||
|
||||
/**
|
||||
* Invocation ID.
|
||||
*/
|
||||
private final String id;
|
||||
|
||||
/**
|
||||
* Constructor of workplace description.
|
||||
* @param builder workplace builder
|
||||
*/
|
||||
private DefaultRpcDescription(Builder builder) {
|
||||
this.op = builder.op;
|
||||
this.params = builder.params;
|
||||
this.id = builder.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String op() {
|
||||
return this.op;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonNode params() {
|
||||
return this.params;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String id() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creating workflow RPC description from json tree.
|
||||
* @param root root node for workflow RPC description
|
||||
* @return workflow RPC description
|
||||
* @throws WorkflowException workflow exception
|
||||
*/
|
||||
public static DefaultRpcDescription valueOf(JsonNode root) throws WorkflowException {
|
||||
|
||||
JsonNode node = root.at(RPC_OP_PTR);
|
||||
if (!(node instanceof TextNode)) {
|
||||
throw new WorkflowException("invalid RPC operation for " + root);
|
||||
}
|
||||
String rpcOp = node.asText();
|
||||
|
||||
node = root.at(RPC_PARAMS_PTR);
|
||||
if (node instanceof MissingNode) {
|
||||
throw new WorkflowException("invalid RPC parameters for " + root);
|
||||
}
|
||||
JsonNode rpcParams = node;
|
||||
|
||||
node = root.at(RPC_ID_PTR);
|
||||
if (!(node instanceof TextNode)) {
|
||||
throw new WorkflowException("invalid RPC invocation ID for " + root);
|
||||
}
|
||||
String rpcId = node.asText();
|
||||
|
||||
|
||||
return builder()
|
||||
.setOp(rpcOp)
|
||||
.setParams(rpcParams)
|
||||
.setId(rpcId)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(getClass())
|
||||
.add("op", op())
|
||||
.add("params", params())
|
||||
.add("id", id())
|
||||
.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets builder instance.
|
||||
* @return builder instance
|
||||
*/
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for workplace RPC description.
|
||||
*/
|
||||
public static class Builder {
|
||||
|
||||
/**
|
||||
* Workflow RPC operation.
|
||||
*/
|
||||
private String op;
|
||||
|
||||
/**
|
||||
* Parameters.
|
||||
*/
|
||||
private JsonNode params;
|
||||
|
||||
/**
|
||||
* Invocation ID.
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* Sets workflow RPC operation.
|
||||
* @param op workflow RPC operation
|
||||
* @return builder
|
||||
*/
|
||||
public Builder setOp(String op) {
|
||||
this.op = op;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets workflow RPC parameters.
|
||||
* @param params workflow RPC parameters
|
||||
* @return builder
|
||||
*/
|
||||
public Builder setParams(JsonNode params) {
|
||||
this.params = params;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets workflow RPC invocation ID.
|
||||
* @param id workflow invocation ID
|
||||
* @return builder
|
||||
*/
|
||||
public Builder setId(String id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds workplace RPC description from builder.
|
||||
* @return instance of workflow RPC description
|
||||
*/
|
||||
public DefaultRpcDescription build() {
|
||||
return new DefaultRpcDescription(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,242 @@
|
||||
/*
|
||||
* 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.workflow.api;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import org.onosproject.event.Event;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
/**
|
||||
* Default implementation of WorkflowContext.
|
||||
*/
|
||||
public class DefaultWorkflowContext extends WorkflowContext {
|
||||
|
||||
/**
|
||||
* ID of workflow.
|
||||
*/
|
||||
private URI workflowId;
|
||||
|
||||
/**
|
||||
* Workplace name of the workflow.
|
||||
*/
|
||||
private String workplaceName;
|
||||
|
||||
/**
|
||||
* State of workflow.
|
||||
*/
|
||||
private WorkflowState state;
|
||||
|
||||
/**
|
||||
* Current worklet of the workflow.
|
||||
*/
|
||||
private String current;
|
||||
|
||||
/**
|
||||
* Cause of workflow exception.
|
||||
*/
|
||||
private String cause;
|
||||
|
||||
/**
|
||||
* Completion event type.
|
||||
*/
|
||||
private transient Class<? extends Event> completionEventType;
|
||||
|
||||
/**
|
||||
* Completion event hint.
|
||||
*/
|
||||
private transient String completionEventHint;
|
||||
|
||||
/**
|
||||
* Completion event generator method reference.
|
||||
*/
|
||||
private transient WorkExecutor completionEventGenerator;
|
||||
|
||||
/**
|
||||
* Completion event timeout milliseconds.
|
||||
*/
|
||||
private transient long completionEventTimeoutMs;
|
||||
|
||||
/**
|
||||
* Service reference for workflow service.
|
||||
*/
|
||||
private transient WorkflowExecutionService workflowExecutionService;
|
||||
|
||||
/**
|
||||
* Service reference for workflow store.
|
||||
*/
|
||||
private transient WorkflowStore workflowStore;
|
||||
|
||||
/**
|
||||
* Service reference for workplace store.
|
||||
*/
|
||||
private transient WorkplaceStore workplaceStore;
|
||||
|
||||
/**
|
||||
* Constructor of DefaultWorkflowContext.
|
||||
* @param workflowId ID of workflow
|
||||
* @param workplaceName name of workplace
|
||||
* @param data data model tree
|
||||
*/
|
||||
public DefaultWorkflowContext(URI workflowId, String workplaceName, DataModelTree data) {
|
||||
super(data);
|
||||
this.workflowId = workflowId;
|
||||
this.workplaceName = workplaceName;
|
||||
this.state = WorkflowState.IDLE;
|
||||
this.current = Worklet.Common.INIT.name();
|
||||
}
|
||||
|
||||
/**
|
||||
* DefaultWorkflowContext name builder.
|
||||
* @param workflowid workflow id
|
||||
* @param workplacename workplace name
|
||||
* @return DefaultWorkflowContext name
|
||||
*/
|
||||
public static String nameBuilder(URI workflowid, String workplacename) {
|
||||
return workflowid.toString() + "@" + workplacename;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return nameBuilder(workflowId, workplaceName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String distributor() {
|
||||
return workplaceName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI workflowId() {
|
||||
return this.workflowId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String workplaceName() {
|
||||
return workplaceName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorkflowState state() {
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setState(WorkflowState state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String current() {
|
||||
return this.current;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCurrent(Worklet worklet) {
|
||||
this.current = worklet.tag();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String cause() {
|
||||
return this.cause;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCause(String cause) {
|
||||
this.cause = cause;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void completed() {
|
||||
setTriggerNext(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void waitCompletion(Class<? extends Event> eventType, String eventHint,
|
||||
WorkExecutor eventGenerator, long timeoutMs) {
|
||||
this.completionEventType = eventType;
|
||||
this.completionEventHint = eventHint;
|
||||
this.completionEventGenerator = eventGenerator;
|
||||
this.completionEventTimeoutMs = timeoutMs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void waitFor(long timeoutMs) {
|
||||
this.completionEventTimeoutMs = timeoutMs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Event> completionEventType() {
|
||||
return completionEventType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String completionEventHint() {
|
||||
return completionEventHint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorkExecutor completionEventGenerator() {
|
||||
return completionEventGenerator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long completionEventTimeout() {
|
||||
return completionEventTimeoutMs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWorkflowExecutionService(WorkflowExecutionService workflowExecutionService) {
|
||||
this.workflowExecutionService = workflowExecutionService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorkflowExecutionService workflowService() {
|
||||
return workflowExecutionService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWorkflowStore(WorkflowStore workflowStore) {
|
||||
this.workflowStore = workflowStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorkflowStore workflowStore() {
|
||||
return workflowStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWorkplaceStore(WorkplaceStore workplaceStore) {
|
||||
this.workplaceStore = workplaceStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorkplaceStore workplaceStore() {
|
||||
return workplaceStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(getClass())
|
||||
.add("name", name())
|
||||
.add("triggernext", triggerNext())
|
||||
.add("data", data())
|
||||
.add("current", current)
|
||||
.add("state", state())
|
||||
.add("cause", cause())
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user