kmcpeake
Committed by Gerrit Code Review

[ONOS-3203] End-to-end demo of Fault Management via SNMP.

This adds SNMP device-discovery, and a Fault Management app which makes alarms available to users via REST/GUI/CLI interfaces.
There is still code cleanup that could be done, but aim of this commit is an end-to-end proof of concept.

To demonstrate :

1) /opt/onos/bin/onos-service
onos> app activate org.onosproject.snmp
onos> app activate org.onosproject.faultmanagement

2) SNMP devices are seeded via config file. The default seed file contains connection details for devices (SNMP agents) available via internet  e.g. demo.snmplabs.com
cp /opt/onos/apache-karaf-3.0.3/etc/samples/org.onosproject.provider.snmp.device.impl.SnmpDeviceProvider.cfg   /opt/onos/apache-karaf-3.0.3/etc/

3) ONOS will poll these SNMP devices and store their alarms.

4) You can now manipulate the alarms via REST  e.g. http://<onos>:8181/onos/v1/fm/alarms , via CLI  via various "alarm-*” commands or in UI with an Alarms Overlay.

More info at https://wiki.onosproject.org/display/ONOS/Fault+Management

15/Dec/15: Updated regarding review comments from Thomas Vachuska.
17/Dec/15: Updated coreService.registerApplication(name) as per https://gerrit.onosproject.org/#/c/6878/

Change-Id: I886f8511f178dc4600ab96e5ff10cc90329cabec
Showing 76 changed files with 4206 additions and 381 deletions
......@@ -21,4 +21,6 @@
<artifact>mvn:${project.groupId}/onos-app-fm-mgr/${project.version}</artifact>
<artifact>mvn:${project.groupId}/onos-app-fm-web/${project.version}</artifact>
<artifact>mvn:${project.groupId}/onos-app-fm-gui/${project.version}</artifact>
<artifact>mvn:${project.groupId}/onos-app-fm-cli/${project.version}</artifact>
</app>
......
......@@ -21,5 +21,7 @@
<feature>onos-drivers</feature>
<bundle>mvn:${project.groupId}/onos-app-fm-mgr/${project.version}</bundle>
<bundle>mvn:${project.groupId}/onos-app-fm-web/${project.version}</bundle>
<bundle>mvn:${project.groupId}/onos-app-fm-gui/${project.version}</bundle>
<bundle>mvn:${project.groupId}/onos-app-fm-cli/${project.version}</bundle>
</feature>
</features>
......
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2015 Open Networking Laboratory
~
~ 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.
-->
~ Copyright 2015 Open Networking Laboratory
~
~ 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
......@@ -28,7 +28,7 @@
<artifactId>onos-app-fm-onosfw</artifactId>
<packaging>pom</packaging>
<description>ONOS framework applications</description>
<description>ONOS fault management application</description>
<dependencies>
<dependency>
......@@ -41,6 +41,15 @@
<artifactId>onos-app-fm-mgr</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-app-fm-gui</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-app-fm-cli</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>
......
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2015 Open Networking Laboratory
~
~ 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.
-->
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.onosproject</groupId>
<artifactId>onos-app-fm</artifactId>
<version>1.4.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>onos-app-fm-cli</artifactId>
<packaging>bundle</packaging>
<dependencies>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onlab-osgi</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onlab-junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.scr.annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-cli</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.karaf.shell</groupId>
<artifactId>org.apache.karaf.shell.console</artifactId>
</dependency>
</dependencies>
</project>
/*
* Copyright 2015 Open Networking Laboratory
*
* 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 java.util.Set;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.apache.karaf.shell.commands.Command;
import org.onosproject.cli.AbstractShellCommand;
import org.onosproject.incubator.net.faultmanagement.alarm.Alarm;
import org.onosproject.incubator.net.faultmanagement.alarm.AlarmService;
/**
* Lists active alarms across all devices.
*/
@Command(scope = "onos", name = "alarm-list-active",
description = "Lists all the ACTIVE alarms across all devices.")
public class GetAllActiveAlarms extends AbstractShellCommand {
@Override
protected void execute() {
printAlarms(AbstractShellCommand.get(AlarmService.class).getActiveAlarms());
}
void printAlarms(Set<Alarm> alarms) {
alarms.stream().forEach((alarm) -> {
print(ToStringBuilder.reflectionToString(alarm, ToStringStyle.SHORT_PREFIX_STYLE));
});
}
}
/*
* Copyright 2015 Open Networking Laboratory
*
* 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 java.util.Set;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.apache.karaf.shell.commands.Command;
import org.onosproject.cli.AbstractShellCommand;
import org.onosproject.incubator.net.faultmanagement.alarm.Alarm;
import org.onosproject.incubator.net.faultmanagement.alarm.AlarmService;
/**
* Lists alarms across all devices.
*/
@Command(scope = "onos", name = "alarm-list",
description = "Lists active alarms across all devices.")
public class GetAllAlarms extends AbstractShellCommand {
@Override
protected void execute() {
printAlarms(AbstractShellCommand.get(AlarmService.class).getAlarms());
}
void printAlarms(Set<Alarm> alarms) {
alarms.stream().forEach((alarm) -> {
print(ToStringBuilder.reflectionToString(alarm, ToStringStyle.SHORT_PREFIX_STYLE));
});
}
}
/*
* Copyright 2015 Open Networking Laboratory
*
* 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 java.util.Map;
import org.apache.karaf.shell.commands.Command;
import org.onosproject.cli.AbstractShellCommand;
import org.onosproject.incubator.net.faultmanagement.alarm.Alarm;
import org.onosproject.incubator.net.faultmanagement.alarm.AlarmService;
/**
* Lists alarm counts across all devices.
*/
@Command(scope = "onos", name = "alarm-counts",
description = "Lists alarm counts across all devices.")
public class GetAllAlarmsCounts extends AbstractShellCommand {
@Override
protected void execute() {
Map<Alarm.SeverityLevel, Long> alarmCounts
= AbstractShellCommand.get(AlarmService.class).getAlarmCounts();
printCounts(alarmCounts);
}
static void printCounts(Map<Alarm.SeverityLevel, Long> alarmCounts) {
alarmCounts.entrySet().stream().forEach((countEntry) -> {
System.out.println(String.format("%s, %d",
countEntry.getKey(), countEntry.getValue()));
});
}
}
/*
* Copyright 2015 Open Networking Laboratory
*
* 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 java.util.Map;
import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
import org.onosproject.cli.AbstractShellCommand;
import org.onosproject.incubator.net.faultmanagement.alarm.Alarm;
import org.onosproject.incubator.net.faultmanagement.alarm.AlarmService;
import org.onosproject.net.DeviceId;
/**
* Lists alarm counts across specified device.
*/
@Command(scope = "onos", name = "alarm-counts-device",
description = "Lists alarm counts across specified device.")
public class GetDeviceAlarmsCounts extends AbstractShellCommand {
@Argument(index = 0, name = "deviceId", description = "Device identity", required = true, multiValued = false)
String deviceId = null;
@Override
protected void execute() {
Map<Alarm.SeverityLevel, Long> alarmCounts = AbstractShellCommand.get(AlarmService.class).
getAlarmCounts(DeviceId.deviceId(deviceId));
// Deliberately using same formatting for both ...
GetAllAlarmsCounts.printCounts(alarmCounts);
}
}
/*
* Copyright 2015 Open Networking Laboratory
*
* 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 implementation for alarms.
*/
package org.onosproject.faultmanagement.alarms.cli;
<!--
~ Copyright 2015 Open Networking Laboratory
~
~ 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.
-->
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
<command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
<command>
<action class="org.onosproject.faultmanagement.alarms.cli.GetAllActiveAlarms"/>
</command>
<command>
<action class="org.onosproject.faultmanagement.alarms.cli.GetAllAlarms"/>
</command>
<command>
<action class="org.onosproject.faultmanagement.alarms.cli.GetAllAlarmsCounts"/>
</command>
<command>
<action class="org.onosproject.faultmanagement.alarms.cli.GetDeviceAlarmsCounts"/>
<completers>
<ref component-id="deviceIdCompleter"/>
</completers>
</command>
</command-bundle>
<bean id="deviceIdCompleter" class="org.onosproject.cli.net.DeviceIdCompleter"/>
</blueprint>
# Need to write unit tests but do so after there are other CLI tests to use as a template.
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2015 Open Networking Laboratory
~
~ 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.onosproject</groupId>
<artifactId>onos-app-fm</artifactId>
<version>1.4.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>onos-app-fm-gui</artifactId>
<packaging>bundle</packaging>
<dependencies>
<dependency>
<groupId>org.apache.karaf.shell</groupId>
<artifactId>org.apache.karaf.shell.console</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onlab-osgi</artifactId>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onlab-junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.scr.annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-cli</artifactId>
<version>${project.version}</version>
<type>jar</type>
</dependency>
</dependencies>
</project>
/*
* Copyright 2015 Open Networking Laboratory
*
* 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.gui;
import java.util.Map;
import java.util.Set;
import org.onosproject.cli.AbstractShellCommand;
import org.onosproject.incubator.net.faultmanagement.alarm.Alarm;
import org.onosproject.incubator.net.faultmanagement.alarm.AlarmId;
import org.onosproject.incubator.net.faultmanagement.alarm.AlarmService;
import org.onosproject.net.DeviceId;
/**
*
* Utility for invoking on alarm service.
*/
public final class AlarmServiceUtil {
static Alarm lookupAlarm(AlarmId alarmId) {
return alarmService().getAlarm(alarmId);
}
static Set<Alarm> lookUpAlarms() {
return alarmService().getAlarms();
}
static Set<Alarm> lookUpAlarms(DeviceId deviceId) {
return alarmService().getAlarms(deviceId);
}
static Map<Alarm.SeverityLevel, Long> lookUpAlarmCounts(DeviceId deviceId) {
return alarmService().getAlarmCounts(deviceId);
}
static Map<Alarm.SeverityLevel, Long> lookUpAlarmCounts() {
return alarmService().getAlarmCounts();
}
private static AlarmService alarmService() {
return AbstractShellCommand.get(AlarmService.class);
}
private AlarmServiceUtil() {
}
}
/*
* Copyright 2015 Open Networking Laboratory
*
* 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.gui;
import com.google.common.collect.ImmutableList;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onosproject.ui.UiExtension;
import org.onosproject.ui.UiExtensionService;
import org.onosproject.ui.UiMessageHandlerFactory;
import org.onosproject.ui.UiView;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
/**
* Skeletal ONOS UI Table-View application component.
*/
@Component(immediate = true)
public class AlarmTableComponent {
private static final String VIEW_ID = "alarmTable";
private static final String VIEW_TEXT = "Alarms";
private final Logger log = LoggerFactory.getLogger(getClass());
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected UiExtensionService uiExtensionService;
// List of application views
private final List<UiView> uiViews = ImmutableList.of(
new UiView(UiView.Category.OTHER, VIEW_ID, VIEW_TEXT)
);
// Factory for UI message handlers
private final UiMessageHandlerFactory messageHandlerFactory =
() -> ImmutableList.of(
new AlarmTableMessageHandler()
);
// Application UI extension
protected UiExtension extension =
new UiExtension.Builder(getClass().getClassLoader(), uiViews)
.resourcePath(VIEW_ID)
.messageHandlerFactory(messageHandlerFactory)
.build();
@Activate
protected void activate() {
uiExtensionService.register(extension);
log.info("Started");
}
@Deactivate
protected void deactivate() {
uiExtensionService.unregister(extension);
log.info("Stopped");
}
}
/*
* Copyright 2015 Open Networking Laboratory
*
* 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.gui;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet;
import org.onosproject.ui.RequestHandler;
import org.onosproject.ui.UiMessageHandler;
import org.onosproject.ui.table.TableModel;
import org.onosproject.ui.table.TableRequestHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collection;
import java.util.Set;
import org.joda.time.DateTime;
import org.onosproject.incubator.net.faultmanagement.alarm.Alarm;
import org.onosproject.incubator.net.faultmanagement.alarm.AlarmId;
import org.onosproject.net.DeviceId;
import org.onosproject.ui.table.cell.TimeFormatter;
/**
* Skeletal ONOS UI Table-View message handler.
*/
public class AlarmTableMessageHandler extends UiMessageHandler {
private static final String ALARM_TABLE_DATA_REQ = "alarmTableDataRequest";
private static final String ALARM_TABLE_DATA_RESP = "alarmTableDataResponse";
private static final String ALARM_TABLES = "alarmTables";
private static final String ALARM_TABLE_DETAIL_REQ = "alarmTableDetailsRequest";
private static final String ALARM_TABLE_DETAIL_RESP = "alarmTableDetailsResponse";
private static final String DETAILS = "details";
private static final String ID = "id";
private static final String DEVICE_ID_STR = "alarmDeviceId";
private static final String DESCRIPTION = "alarmDesc";
private static final String SOURCE = "alarmSource";
private static final String TIME_RAISED = "alarmTimeRaised";
private static final String TIME_UPDATED = "alarmTimeUpdated";
private static final String TIME_CLEARED = "alarmTimeCleared";
private static final String SEVERITY = "alarmSeverity";
private static final String RESULT = "result";
// TODO No need to show id column in ONOS-GUI
// TODO Replace SEVERITY column by color-coding of row depending on severity ie. red=critical, green=cleared etc
private static final String[] COLUMN_IDS = {ID, DEVICE_ID_STR, DESCRIPTION, SOURCE, TIME_RAISED, SEVERITY};
private final Logger log = LoggerFactory.getLogger(getClass());
@Override
protected Collection<RequestHandler> createRequestHandlers() {
return ImmutableSet.of(
new AlarmTableDataRequestHandler(),
new AlarmTableDetailRequestHandler()
);
}
// handler for alarm table requests
private final class AlarmTableDataRequestHandler extends TableRequestHandler {
private AlarmTableDataRequestHandler() {
super(ALARM_TABLE_DATA_REQ, ALARM_TABLE_DATA_RESP, ALARM_TABLES);
}
@Override
protected String defaultColumnId() {
// if necessary, override defaultColumnId() -- if it isn't "id"
return ID;
}
@Override
protected String[] getColumnIds() {
return COLUMN_IDS;
}
@Override
protected TableModel createTableModel() {
// if required, override createTableModel() to set column formatters / comparators
TableModel tm = super.createTableModel();
tm.setFormatter(TIME_RAISED, new TimeFormatter());
return tm;
}
@Override
protected void populateTable(TableModel tm, ObjectNode payload) {
log.debug(" populateTable tm={} payload ={}", tm, payload);
String devId = string(payload, "devId");
Set<Alarm> alarms = Strings.isNullOrEmpty(devId) ?
AlarmServiceUtil.lookUpAlarms() :
AlarmServiceUtil.lookUpAlarms(DeviceId.deviceId(devId));
alarms.stream().forEach((alarm) -> {
populateRow(tm.addRow(), alarm);
});
}
private void populateRow(TableModel.Row row, Alarm alarm) {
log.debug("populate table Row row={} item ={}", row, alarm);
row.cell(ID, alarm.id().fingerprint())
.cell(DEVICE_ID_STR, alarm.deviceId())
.cell(DESCRIPTION, alarm.description())
.cell(SOURCE, alarm.source())
.cell(TIME_RAISED, new DateTime(alarm.timeRaised()))
.cell(SEVERITY, alarm.severity());
}
}
// handler for alarm details requests
private final class AlarmTableDetailRequestHandler extends RequestHandler {
private AlarmTableDetailRequestHandler() {
super(ALARM_TABLE_DETAIL_REQ);
}
@Override
public void process(long sid, ObjectNode payload) {
log.debug("sid={}, payload ={}", sid, payload);
String id = string(payload, ID, "(none)");
Alarm alarm = AlarmServiceUtil.lookupAlarm(AlarmId.alarmId(Long.parseLong(id)));
ObjectNode rootNode = objectNode();
ObjectNode data = objectNode();
rootNode.set(DETAILS, data);
if (alarm == null) {
rootNode.put(RESULT, "Item with id '" + id + "' not found");
log.warn("attempted to get item detail for id '{}'", id);
} else {
rootNode.put(RESULT, "Found item with id '" + id + "'");
data.put(ID, alarm.id().fingerprint());
data.put(DESCRIPTION, alarm.description());
data.put(DEVICE_ID_STR, alarm.deviceId().toString());
data.put(SOURCE, alarm.source().toString());
long timeRaised = alarm.timeRaised();
data.put(TIME_RAISED,
formatTime(timeRaised)
);
data.put(TIME_UPDATED, formatTime(alarm.timeUpdated()));
data.put(TIME_CLEARED, formatTime(alarm.timeCleared()));
data.put(SEVERITY, alarm.severity().toString());
}
log.debug("send ={}", rootNode);
sendMessage(ALARM_TABLE_DETAIL_RESP, 0, rootNode);
}
}
private static String formatTime(Long msSinceStartOfEpoch) {
if (msSinceStartOfEpoch == null) {
return "-";
}
return new TimeFormatter().format(new DateTime(msSinceStartOfEpoch));
}
}
/*
* Copyright 2015 Open Networking Laboratory
*
* 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.gui;
import com.google.common.collect.ImmutableList;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onosproject.ui.UiExtension;
import org.onosproject.ui.UiExtensionService;
import org.onosproject.ui.UiMessageHandlerFactory;
import org.onosproject.ui.UiTopoOverlayFactory;
import org.onosproject.ui.UiView;
import org.onosproject.ui.UiViewHidden;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
/**
* Skeletal ONOS UI Topology-Overlay application component.
*/
@Component(immediate = true)
public class AlarmTopovComponent {
private static final ClassLoader CL = AlarmTopovComponent.class.getClassLoader();
private static final String VIEW_ID = "alarmTopov";
private final Logger log = LoggerFactory.getLogger(getClass());
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected UiExtensionService uiExtensionService;
// List of application views
private final List<UiView> uiViews = ImmutableList.of(
new UiViewHidden(VIEW_ID)
);
// Factory for UI message handlers
private final UiMessageHandlerFactory messageHandlerFactory =
() -> ImmutableList.of(
new AlarmTopovMessageHandler()
);
// Factory for UI topology overlays
private final UiTopoOverlayFactory topoOverlayFactory =
() -> ImmutableList.of(
new AlarmTopovOverlay()
);
// Application UI extension
protected UiExtension extension =
new UiExtension.Builder(CL, uiViews)
.resourcePath(VIEW_ID)
.messageHandlerFactory(messageHandlerFactory)
.topoOverlayFactory(topoOverlayFactory)
.build();
@Activate
protected void activate() {
uiExtensionService.register(extension);
log.info("Started");
}
@Deactivate
protected void deactivate() {
uiExtensionService.unregister(extension);
log.info("Stopped");
}
}
/*
* Copyright 2014,2015 Open Networking Laboratory
*
* 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.gui;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet;
import org.onlab.osgi.ServiceDirectory;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Element;
import org.onosproject.net.HostId;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.host.HostService;
import org.onosproject.ui.RequestHandler;
import org.onosproject.ui.UiConnection;
import org.onosproject.ui.UiMessageHandler;
import org.onosproject.ui.topo.DeviceHighlight;
import org.onosproject.ui.topo.Highlights;
import org.onosproject.ui.topo.NodeBadge;
import org.onosproject.ui.topo.NodeBadge.Status;
import org.onosproject.ui.topo.TopoJson;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collection;
import java.util.Set;
import org.onosproject.incubator.net.faultmanagement.alarm.Alarm;
import org.onosproject.incubator.net.faultmanagement.alarm.AlarmService;
/**
* Skeletal ONOS UI Topology-Overlay message handler.
*/
public class AlarmTopovMessageHandler extends UiMessageHandler {
private static final String ALARM_TOPOV_DISPLAY_START = "alarmTopovDisplayStart";
private static final String ALARM_TOPOV_DISPLAY_UPDATE = "alarmTopovDisplayUpdate";
private static final String ALARM_TOPOV_DISPLAY_STOP = "alarmTopovDisplayStop";
private static final String ID = "id";
private static final String MODE = "mode";
private enum Mode {
IDLE, MOUSE
}
private final Logger log = LoggerFactory.getLogger(getClass());
private DeviceService deviceService;
private HostService hostService;
private AlarmService alarmService;
private Mode currentMode = Mode.IDLE;
private Element elementOfNote;
// ===============-=-=-=-=-=-======================-=-=-=-=-=-=-================================
@Override
public void init(UiConnection connection, ServiceDirectory directory) {
super.init(connection, directory);
deviceService = directory.get(DeviceService.class);
hostService = directory.get(HostService.class);
alarmService = directory.get(AlarmService.class);
}
@Override
protected Collection<RequestHandler> createRequestHandlers() {
return ImmutableSet.of(
new DisplayStartHandler(),
new DisplayUpdateHandler(),
new DisplayStopHandler()
);
}
// === -------------------------
// === Handler classes
private final class DisplayStartHandler extends RequestHandler {
public DisplayStartHandler() {
super(ALARM_TOPOV_DISPLAY_START);
}
@Override
public void process(long sid, ObjectNode payload) {
String mode = string(payload, MODE);
log.debug("Start Display: mode [{}]", mode);
clearState();
clearForMode();
switch (mode) {
case "mouse":
currentMode = Mode.MOUSE;
sendMouseData();
break;
default:
currentMode = Mode.IDLE;
break;
}
}
}
private final class DisplayUpdateHandler extends RequestHandler {
public DisplayUpdateHandler() {
super(ALARM_TOPOV_DISPLAY_UPDATE);
}
@Override
public void process(long sid, ObjectNode payload) {
String id = string(payload, ID);
log.debug("Update Display: id [{}]", id);
if (!Strings.isNullOrEmpty(id)) {
updateForMode(id);
} else {
clearForMode();
}
}
}
private final class DisplayStopHandler extends RequestHandler {
public DisplayStopHandler() {
super(ALARM_TOPOV_DISPLAY_STOP);
}
@Override
public void process(long sid, ObjectNode payload) {
log.debug("Stop Display");
clearState();
clearForMode();
}
}
// === ------------
private void clearState() {
currentMode = Mode.IDLE;
elementOfNote = null;
}
private void updateForMode(String id) {
log.debug("host service: {}", hostService);
log.debug("device service: {}", deviceService);
try {
HostId hid = HostId.hostId(id);
log.debug("host id {}", hid);
elementOfNote = hostService.getHost(hid);
log.debug("host element {}", elementOfNote);
} catch (RuntimeException e) {
try {
DeviceId did = DeviceId.deviceId(id);
log.debug("device id {}", did);
elementOfNote = deviceService.getDevice(did);
log.debug("device element {}", elementOfNote);
} catch (RuntimeException e2) {
log.debug("Unable to process ID [{}]", id);
elementOfNote = null;
}
}
switch (currentMode) {
case MOUSE:
sendMouseData();
break;
default:
break;
}
}
private void clearForMode() {
sendHighlights(new Highlights());
}
private void sendHighlights(Highlights highlights) {
sendMessage(TopoJson.highlightsMessage(highlights));
}
private void sendMouseData() {
if (elementOfNote != null && elementOfNote instanceof Device) {
DeviceId devId = (DeviceId) elementOfNote.id();
Set<Alarm> alarmsOnDevice = alarmService.getAlarms(devId);
Highlights highlights = new Highlights();
addDeviceBadge(highlights, devId, alarmsOnDevice.size());
sendHighlights(highlights);
}
// Note: could also process Host, if available
}
private void addDeviceBadge(Highlights h, DeviceId devId, int n) {
DeviceHighlight dh = new DeviceHighlight(devId.toString());
dh.setBadge(createBadge(n));
h.add(dh);
}
private NodeBadge createBadge(int n) {
Status status = n > 0 ? Status.ERROR : Status.INFO;
String noun = n > 0 ? "(Alarmed)" : "(Normal)";
String msg = "Alarms: " + n + " " + noun;
return NodeBadge.number(status, n, msg);
}
}
/*
* Copyright 2015 Open Networking Laboratory
*
* 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.gui;
import java.util.Map;
import org.onosproject.incubator.net.faultmanagement.alarm.Alarm;
import org.onosproject.net.DeviceId;
import org.onosproject.ui.UiTopoOverlay;
import org.onosproject.ui.topo.ButtonId;
import org.onosproject.ui.topo.PropertyPanel;
import org.onosproject.ui.topo.TopoConstants.CoreButtons;
import static org.onosproject.ui.topo.TopoConstants.Properties.*;
/**
* Our topology overlay.
*/
public class AlarmTopovOverlay extends UiTopoOverlay {
// NOTE: this must match the ID defined in alarmTopov.js
private static final String OVERLAY_ID = "alarmsTopo-overlay";
private static final ButtonId ALARM1_BUTTON = new ButtonId("alarm1button");
private static final ButtonId ALARM2_BUTTON = new ButtonId("alarm2button");
public AlarmTopovOverlay() {
super(OVERLAY_ID);
}
@Override
public void modifySummary(PropertyPanel pp) {
pp.title("Alarms Overview");
// We could just remove some properties here but lets keep it uncluttered, unless
// there is feedback other properties are essential.
pp.removeAllProps();
Map<Alarm.SeverityLevel, Long> countsForAll = AlarmServiceUtil.lookUpAlarmCounts();
addAlarmCountsProperties(pp, countsForAll);
}
@Override
public void modifyDeviceDetails(PropertyPanel pp, DeviceId deviceId) {
pp.title("Alarm Details");
pp.removeProps(LATITUDE, LONGITUDE, PORTS, FLOWS, TUNNELS, SERIAL_NUMBER, PROTOCOL);
Map<Alarm.SeverityLevel, Long> countsForDevice = AlarmServiceUtil.lookUpAlarmCounts(deviceId);
addAlarmCountsProperties(pp, countsForDevice);
pp.addButton(ALARM1_BUTTON)
.addButton(ALARM2_BUTTON);
pp.removeButtons(CoreButtons.SHOW_PORT_VIEW)
.removeButtons(CoreButtons.SHOW_GROUP_VIEW);
}
private void addAlarmCountsProperties(PropertyPanel pp, Map<Alarm.SeverityLevel, Long> countsForDevice) {
// TODO we could show these as color-coded squares with a count inside, to save space on the screen.
long cr = countsForDevice.getOrDefault(Alarm.SeverityLevel.CRITICAL, 0L);
long ma = countsForDevice.getOrDefault(Alarm.SeverityLevel.MAJOR, 0L);
long mi = countsForDevice.getOrDefault(Alarm.SeverityLevel.MINOR, 0L);
long wa = countsForDevice.getOrDefault(Alarm.SeverityLevel.WARNING, 0L);
long in = countsForDevice.getOrDefault(Alarm.SeverityLevel.INDETERMINATE, 0L);
long cl = countsForDevice.getOrDefault(Alarm.SeverityLevel.CLEARED, 0L);
// Unfortunately the PropertyPanel does not righ justify numbers even when using longs,
// but that not in scope of fault management work
pp.addProp("Critical", cr);
pp.addProp("Major", ma);
pp.addProp("Minor", mi);
pp.addProp("Warning", wa);
pp.addProp("Indeter.", in);
pp.addProp("Cleared", cl);
pp.addSeparator();
pp.addProp("Total", cr + ma + mi + wa + in + cl);
}
}
/*
* Copyright 2015 Open Networking Laboratory
*
* 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.
*/
/**
* Fault Management GUI implementation.
*/
package org.onosproject.faultmanagement.alarms.gui;
<link rel="stylesheet" href="app/view/alarmTable/alarmTable.css">
\ No newline at end of file
<script src="app/view/alarmTable/alarmTable.js"></script>
\ No newline at end of file
<link rel="stylesheet" href="app/view/alarmTopov/alarmTopov.css">
<script src="app/view/alarmTopov/alarmTopovDemo.js"></script>
<script src="app/view/alarmTopov/alarmTopovOverlay.js"></script>
/* css for alarm table view */
#ov-alarm-table h2 {
display: inline-block;
}
/* Panel Styling */
#ov-alarm-table-item-details-panel.floatpanel {
position: absolute;
top: 115px;
}
.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 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-style: italic;
opacity: 0.8;
}
<!-- partial HTML -->
<div id="ov-alarm-table">
<div class="tabular-header">
<h2>Alarms for {{devId || "all devices."}} ({{tableData.length}} total)</h2>
<div class="ctrl-btns">
<div class="refresh" ng-class="{active: autoRefresh}"
icon icon-id="refresh" icon-size="36"
tooltip tt-msg="autoRefreshTip"
ng-click="toggleRefresh()"></div>
</div>
</div>
<div class="summary-list" onos-table-resize>
<div class="table-header" onos-sortable-header>
<table>
<tr>
<td colId="id" sortable>Id </td>
<td colId="alarmDeviceId" sortable>Device </td>
<td colId="alarmDesc" sortable>Description </td>
<td colId="alarmSource" sortable>Source </td>
<td colId="alarmTimeRaised" sortable>Time Raised </td>
<td colId="alarmSeverity" sortable>Severity </td>
</tr>
</table>
</div>
<div class="table-body">
<table>
<tr ng-if="!tableData.length" class="no-data">
<td colspan="3">
No Alarms found
</td>
</tr>
<tr ng-repeat="item in tableData track by $index"
ng-click="selectCallback($event, item)"
ng-class="{selected: item.id === selId}">
<td>{{item.id}}</td>
<td>{{item.alarmDeviceId}}</td>
<td>{{item.alarmDesc}}</td>
<td>{{item.alarmSource}}</td>
<td>{{item.alarmTimeRaised}}</td>
<td>{{item.alarmSeverity}}</td>
</tr>
</table>
</div>
</div>
<ov-alarm-table-item-details-panel></ov-alarm-table-item-details-panel>
</div>
// js for alarm app table view
(function () {
'use strict';
// injected refs
var $log, $scope, $loc, devId, fs, wss;
// constants
var detailsReq = 'alarmTableDetailsRequest',
detailsResp = 'alarmTableDetailsResponse',
pName = 'ov-alarm-table-item-details-panel',
propOrder = ['id', 'alarmDeviceId', 'alarmDesc', 'alarmSource', 'alarmTimeRaised', 'alarmTimeUpdated', 'alarmTimeCleared', 'alarmSeverity'],
friendlyProps = ['Alarm Id', 'Device Id', 'Description', 'Source', 'Time Raised', 'Time Updated', 'Time Cleared', 'Severity'];
function addProp(tbody, index, value) {
var tr = tbody.append('tr');
function addCell(cls, txt) {
tr.append('td').attr('class', cls).html(txt);
}
addCell('label', friendlyProps[index] + ' :');
addCell('value', value);
}
function populatePanel(panel) {
var title = panel.append('h3'),
tbody = panel.append('table').append('tbody');
title.text('Alarm Details');
propOrder.forEach(function (prop, i) {
addProp(tbody, i, $scope.panelDetails[prop]);
});
panel.append('hr');
panel.append('h4').text('Comments');
panel.append('p').text($scope.panelDetails.comment);
}
function respDetailsCb(data) {
$scope.panelDetails = data.details;
$scope.$apply();
}
angular.module('ovAlarmTable', [])
.controller('OvAlarmTableCtrl',
['$log', '$scope', '$location', 'TableBuilderService',
'FnService', 'WebSocketService',
function (_$log_, _$scope_, _$location_, tbs, _fs_, _wss_) {
var params;
$log = _$log_;
$scope = _$scope_;
$loc = _$location_;
fs = _fs_;
wss = _wss_;
params = $loc.search();
if (params.hasOwnProperty('devId')) {
$scope.devId = params['devId'];
}
var handlers = {};
$scope.panelDetails = {};
// details response handler
handlers[detailsResp] = respDetailsCb;
wss.bindHandlers(handlers);
// custom selection callback
function selCb($event, row) {
$log.debug("selCb row=" + JSON.stringify(row, null, 4) +
", $event=" + JSON.stringify($event, null, 4));
$log.debug('$scope.selId=', $scope.selId);
if ($scope.selId) {
$log.debug('send');
wss.sendEvent(detailsReq, {id: row.id});
} else {
$log.debug('hidePanel');
$scope.hidePanel();
}
$log.debug('Got a click on:', row);
}
// TableBuilderService creating a table for us
tbs.buildTable({
scope: $scope,
tag: 'alarmTable',
selCb: selCb,
query: params
});
// cleanup
$scope.$on('$destroy', function () {
wss.unbindHandlers(handlers);
$log.log('OvAlarmTableCtrl has been destroyed');
});
$log.log('OvAlarmTableCtrl has been created');
}])
.directive('ovAlarmTableItemDetailsPanel', ['PanelService', 'KeyService',
function (ps, ks) {
return {
restrict: 'E',
link: function (scope, element, attrs) {
// insert details panel with PanelService
// create the panel
var panel = ps.createPanel(pName, {
width: 400,
margin: 20,
hideMargin: 0
});
panel.hide();
scope.hidePanel = function () {
panel.hide();
};
function closePanel() {
if (panel.isVisible()) {
$scope.selId = null;
panel.hide();
return true;
}
return false;
}
// create key bindings to handle panel
ks.keyBindings({
esc: [closePanel, 'Close the details panel'],
_helpFormat: ['esc']
});
ks.gestureNotes([
['click', 'Select a row to show item details']
]);
// update the panel's contents when the data is changed
scope.$watch('panelDetails', function () {
if (!fs.isEmptyObject(scope.panelDetails)) {
panel.empty();
populatePanel(panel);
panel.show();
}
});
// cleanup on destroyed scope
scope.$on('$destroy', function () {
ks.unbindKeys();
ps.destroyPanel(pName);
});
}
};
}]);
}());
/*
* Copyright 2015 Open Networking Laboratory
*
* 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.
*/
/*
Alarm Demo module. This contains the "business logic" for the topology
overlay that we are implementing.
*/
(function () {
'use strict';
// injected refs
var $log, fs, flash, wss;
// constants
var displayStart = 'alarmTopovDisplayStart',
displayUpdate = 'alarmTopovDisplayUpdate',
displayStop = 'alarmTopovDisplayStop';
// internal state
var currentMode = null;
// === ---------------------------
// === Helper functions
function sendDisplayStart(mode) {
wss.sendEvent(displayStart, {
mode: mode
});
}
function sendDisplayUpdate(what) {
wss.sendEvent(displayUpdate, {
id: what ? what.id : ''
});
}
function sendDisplayStop() {
wss.sendEvent(displayStop);
}
// === ---------------------------
// === Main API functions
function startDisplay(mode) {
if (currentMode === mode) {
$log.debug('(in mode', mode, 'already)');
} else {
currentMode = mode;
sendDisplayStart(mode);
flash.flash('Starting display mode: ' + mode);
}
}
function updateDisplay(m) {
if (currentMode) {
sendDisplayUpdate(m);
}
}
function stopDisplay() {
if (currentMode) {
currentMode = null;
sendDisplayStop();
flash.flash('Canceling display mode');
return true;
}
return false;
}
// === ---------------------------
// === Module Factory Definition
angular.module('ovAlarmTopov', [])
.factory('AlarmTopovDemoService',
['$log', 'FnService', 'FlashService', 'WebSocketService',
function (_$log_, _fs_, _flash_, _wss_) {
$log = _$log_;
fs = _fs_;
flash = _flash_;
wss = _wss_;
return {
startDisplay: startDisplay,
updateDisplay: updateDisplay,
stopDisplay: stopDisplay
};
}]);
}());
// alarm topology overlay - client side
//
// This is the glue that binds our business logic (in alarmTopovDemo.js)
// to the overlay framework.
(function () {
'use strict';
// injected refs
var $log, tov, stds, ns;
// internal state should be kept in the service module (not here)
// our overlay definition
var overlay = {
// NOTE: this must match the ID defined in AppUiTopovOverlay
overlayId: 'alarmsTopo-overlay',
glyphId: '*star4',
tooltip: 'Alarms Overlay',
// These glyphs get installed using the overlayId as a prefix.
// e.g. 'star4' is installed as 'alarmsTopo-overlay-star4'
// They can be referenced (from this overlay) as '*star4'
// That is, the '*' prefix stands in for 'alarmsTopo-overlay-'
glyphs: {
star4: {
vb: '0 0 8 8',
// TODO new icon needed
d: 'M1,4l2,-1l1,-2l1,2l2,1l-2,1l-1,2l-1,-2z'
},
banner: {
vb: '0 0 6 6',
// TODO new icon needed
d: 'M1,1v4l2,-2l2,2v-4z'
}
},
activate: function () {
$log.debug("Alarm topology overlay ACTIVATED");
},
deactivate: function () {
stds.stopDisplay();
$log.debug("Alarm topology overlay DEACTIVATED");
},
// detail panel button definitions
buttons: {
alarm1button: {
gid: 'chain',
tt: 'Show alarms for this device',
cb: function (data) {
$log.debug('Show alarms for selected device. data:', data);
ns.navTo("alarmTable", {devId: data.id});
}
},
alarm2button: {
gid: '*banner',
tt: 'Show alarms for all devices',
cb: function (data) {
$log.debug('Show alarms for all devices. data:', data);
ns.navTo("alarmTable");
}
}
},
// Key bindings for traffic overlay buttons
// NOTE: fully qual. button ID is derived from overlay-id and key-name
keyBindings: {
0: {
cb: function () {
stds.stopDisplay();
},
tt: 'Cancel Alarm Count on Device',
gid: 'xMark'
},
V: {
cb: function () {
stds.startDisplay('mouse');
},
tt: 'Start Alarm Count on Device',
gid: '*banner'
},
_keyOrder: [
'0', 'V'
]
},
hooks: {
// hook for handling escape key
// Must return true to consume ESC, false otherwise.
escape: function () {
// Must return true to consume ESC, false otherwise.
return stds.stopDisplay();
},
// hooks for when the selection changes...
empty: function () {
selectionCallback('empty');
},
single: function (data) {
selectionCallback('single', data);
},
multi: function (selectOrder) {
selectionCallback('multi', selectOrder);
tov.addDetailButton('alarm1button');
tov.addDetailButton('alarm2button');
},
mouseover: function (m) {
// m has id, class, and type properties
$log.debug('mouseover:', m);
stds.updateDisplay(m);
},
mouseout: function () {
$log.debug('mouseout');
stds.updateDisplay();
}
}
};
function buttonCallback(x) {
$log.debug('Toolbar-button callback', x);
}
function selectionCallback(x, d) {
$log.debug('Selection callback', x, d);
}
// invoke code to register with the overlay service
angular.module('ovAlarmTopov')
.run(['$log', 'TopoOverlayService', 'AlarmTopovDemoService', 'NavService',
function (_$log_, _tov_, _stds_, _ns_) {
$log = _$log_;
tov = _tov_;
stds = _stds_;
ns = _ns_;
tov.register(overlay);
}]);
}());
<!-- partial HTML -->
<div id="ov-alarm-topov">
<p>This is a hidden view .. just a placeholder to house the javascript</p>
</div>
<link rel="stylesheet" href="app/view/alarmCustom/alarmCustom.css">
\ No newline at end of file
<script src="app/view/alarmCustom/alarmCustom.js"></script>
\ No newline at end of file
# Need to write unit tests for the GUI server-side code ; write them after there are examples at web/gui/src/test/java/org/onosproject/ui/impl
\ No newline at end of file
......@@ -45,6 +45,11 @@
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-snmp-provider-alarm</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.compendium</artifactId>
<version>5.0.0</version>
......
......@@ -15,13 +15,16 @@
*/
package org.onosproject.faultmanagement.impl;
import static com.google.common.base.Strings.isNullOrEmpty;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Dictionary;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.stream.Collectors;
import org.apache.commons.collections.CollectionUtils;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
......@@ -47,8 +50,15 @@ import org.onosproject.core.CoreService;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onlab.util.ItemNotFoundException;
import org.onosproject.incubator.net.faultmanagement.alarm.AlarmProvider;
import org.onosproject.incubator.net.faultmanagement.alarm.DefaultAlarm;
import org.onosproject.net.device.DeviceService;
import org.osgi.service.component.ComponentContext;
import static java.util.concurrent.TimeUnit.SECONDS;
import java.util.concurrent.atomic.AtomicLong;
import static org.onlab.util.Tools.groupedThreads;
import org.onosproject.net.Device;
/**
* Implementation of the Alarm service.
......@@ -57,89 +67,133 @@ import org.osgi.service.component.ComponentContext;
@Service
public class AlarmsManager implements AlarmService {
// For subscribing to device-related events
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CoreService coreService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DeviceService deviceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected AlarmProvider alarmProvider;
private final Logger log = getLogger(getClass());
private ApplicationId appId;
private IdGenerator idGenerator;
private ScheduledExecutorService alarmPollExecutor;
@Property(name = "fmDevices", value = "127.0.0.1", label = "Instance-specific configurations")
private String devConfigs;
// dummy data
private final AtomicLong alarmIdGenerator = new AtomicLong(0);
private final Map<AlarmId, Alarm> alarms = new ConcurrentHashMap<>();
private AlarmId generateAlarmId() {
return AlarmId.alarmId(alarmIdGenerator.incrementAndGet());
}
private static final int DEFAULT_POLL_FREQUENCY_SECONDS = 120;
@Property(name = "alarmPollFrequencySeconds", intValue = DEFAULT_POLL_FREQUENCY_SECONDS,
label = "Frequency (in seconds) for polling alarm from devices")
private int alarmPollFrequencySeconds = DEFAULT_POLL_FREQUENCY_SECONDS;
private final AtomicLong alarmIdGenerator = new AtomicLong(0);
// TODO implement purging of old alarms.
private static final int DEFAULT_CLEAR_FREQUENCY_SECONDS = 500;
@Property(name = "clearedAlarmPurgeSeconds", intValue = DEFAULT_CLEAR_FREQUENCY_SECONDS,
label = "Frequency (in seconds) for deleting cleared alarms")
private int clearedAlarmPurgeFrequencySeconds = DEFAULT_CLEAR_FREQUENCY_SECONDS;
// TODO Later should must be persisted to disk or database
private final Map<AlarmId, Alarm> alarms = new ConcurrentHashMap<>();
@Override
public Alarm update(Alarm replacement) {
public Alarm updateBookkeepingFields(AlarmId id, boolean isAcknowledged, String assignedUser) {
Alarm found = alarms.get(id);
if (found == null) {
throw new ItemNotFoundException("Alarm with id " + id + " found");
}
final Alarm found = alarms.get(replacement.id());
Alarm updated = new DefaultAlarm.Builder(found).
withAcknowledged(isAcknowledged).
withAssignedUser(assignedUser).build();
alarms.put(id, updated);
return updated;
}
public Alarm clear(AlarmId id) {
Alarm found = alarms.get(id);
if (found == null) {
throw new ItemNotFoundException("Alarm with id " + replacement.id() + " found");
log.warn("id {} cant be cleared as it is already gone.", id);
return null;
}
final Alarm updated = new DefaultAlarm.Builder(found).
withAcknowledged(replacement.acknowledged()).
withAssignedUser(replacement.assignedUser()).build();
alarms.put(replacement.id(), updated);
Alarm updated = new DefaultAlarm.Builder(found).clear().build();
alarms.put(id, updated);
return updated;
}
@Override
public int getActiveAlarmCount(DeviceId deviceId) {
//TODO
throw new UnsupportedOperationException(NOT_SUPPORTED_YET);
public Map<Alarm.SeverityLevel, Long> getAlarmCounts(DeviceId deviceId) {
return getAlarms(deviceId).stream().collect(
Collectors.groupingBy(Alarm::severity, Collectors.counting()));
}
@Override
public Map<Alarm.SeverityLevel, Long> getAlarmCounts() {
return getAlarms().stream().collect(
Collectors.groupingBy(Alarm::severity, Collectors.counting()));
}
private static final String NOT_SUPPORTED_YET = "Not supported yet.";
@Override
public Alarm getAlarm(AlarmId alarmId) {
return nullIsNotFound(
alarms.get(alarmId),
alarms.get(
checkNotNull(alarmId, "Alarm Id cannot be null")),
"Alarm is not found");
}
@Override
public Set<Alarm> getAlarms() {
//TODO
throw new UnsupportedOperationException(NOT_SUPPORTED_YET);
return new HashSet<>(alarms.values());
}
@Override
public Set<Alarm> getActiveAlarms() {
// Enpty set if no values
return alarms.isEmpty() ? new HashSet<>() : new HashSet<>(alarms.values());
}
private static DefaultAlarm generateFake(DeviceId deviceId, AlarmId alarmId) {
return new DefaultAlarm.Builder(
alarmId, deviceId, "NE is not reachable", Alarm.SeverityLevel.MAJOR, System.currentTimeMillis()).
withTimeUpdated(System.currentTimeMillis()).
withServiceAffecting(true)
.withAcknowledged(true).
withManuallyClearable(true)
.withAssignedUser("user1").build();
return alarms.values().stream().filter(
a -> !a.severity().equals(Alarm.SeverityLevel.CLEARED)).
collect(Collectors.toSet());
}
@Override
public Set<Alarm> getAlarms(Alarm.SeverityLevel severity) {
//TODO
throw new UnsupportedOperationException(NOT_SUPPORTED_YET);
return alarms.values().stream().filter(
a -> a.severity().equals(severity)).
collect(Collectors.toSet());
}
@Override
public Set<Alarm> getAlarms(DeviceId deviceId) {
//TODO
throw new UnsupportedOperationException(NOT_SUPPORTED_YET);
return alarms.values().stream().filter(
a -> deviceId.equals(a.deviceId())).
collect(Collectors.toSet());
}
private Set<Alarm> getActiveAlarms(DeviceId deviceId) {
return getActiveAlarms().stream().filter(
a -> deviceId.equals(a.deviceId())).
collect(Collectors.toSet());
}
@Override
public Set<Alarm> getAlarms(DeviceId deviceId, AlarmEntityId source) {
//TODO
throw new UnsupportedOperationException(NOT_SUPPORTED_YET);
return getAlarms(deviceId).stream().filter(
a -> source.equals(a.source())
).collect(Collectors.toSet());
}
@Override
......@@ -154,41 +208,92 @@ public class AlarmsManager implements AlarmService {
throw new UnsupportedOperationException(NOT_SUPPORTED_YET);
}
private void discoverAlarmsForDevice(DeviceId deviceId) {
final AlarmId alarmId = new AlarmId(alarmIdGenerator.incrementAndGet());
// TODO In a new thread invoke SNMP Provider with DeviceId and device type and when done update our of alarms
//
alarms.put(alarmId, generateFake(deviceId, alarmId));
}
private final AlarmListener alarmListener = new InternalAlarmListener();
private class InternalAlarmListener implements AlarmListener {
@Override
public void event(AlarmEvent event) {
// TODO
throw new UnsupportedOperationException(NOT_SUPPORTED_YET);
log.debug("AlarmsManager. InternalAlarmListener received {}", event);
try {
switch (event.type()) {
case DEVICE_DISCOVERY:
DeviceId deviceId = checkNotNull(event.getDeviceRefreshed(), "Listener cannot be null");
log.info("New alarm set for {} received!", deviceId);
updateAlarms(event.subject(), deviceId);
break;
case NOTIFICATION:
throw new IllegalArgumentException(
"Alarm Notifications (Traps) not expected or implemented yet. Received =" + event);
default:
break;
}
} catch (Exception e) {
log.warn("Failed to process {}", event, e);
}
}
}
@Activate
public void activate(ComponentContext context) {
log.info("Activate ...");
appId = coreService.registerApplication("org.onos.faultmanagement.alarms");
appId = coreService.registerApplication("org.onosproject.faultmanagement.alarms");
idGenerator = coreService.getIdGenerator("alarm-ids");
log.info("Started with appId={} idGenerator={}", appId, idGenerator);
log.info("Started with appId={}", appId);
alarmProvider.addAlarmListener(alarmListener);
probeActiveDevices();
final boolean result = modified(context);
boolean result = modified(context);
log.info("modified result = {}", result);
alarmPollExecutor = newSingleThreadScheduledExecutor(groupedThreads("onos/fm", "alarms-poll-%d"));
alarmPollExecutor.scheduleAtFixedRate(new PollAlarmsTask(),
alarmPollFrequencySeconds, alarmPollFrequencySeconds, SECONDS);
}
/**
* Auxiliary task to keep alarms up to date. IN future release alarm-notifications will be used as an optimization
* so we dont have to wait until polling to detect changes. Furthermore with simple polling flapping alarms may be
* missed.
*/
private final class PollAlarmsTask implements Runnable {
@Override
public void run() {
if (Thread.currentThread().isInterrupted()) {
log.info("Interrupted, quitting");
return;
}
try {
probeActiveDevices();
} catch (RuntimeException e) {
log.error("Exception thrown during alarm synchronization process", e);
}
}
}
private void probeActiveDevices() {
Iterable<Device> devices = deviceService.getAvailableDevices();
log.info("Refresh alarms for all available devices={} ...", devices);
for (Device d : devices) {
log.info("Lets tell alarm provider to refresh alarms for {} ...", d.id());
alarmProvider.triggerProbe(d.id());
}
}
@Deactivate
public void deactivate(ComponentContext context) {
log.info("Deactivate ...");
// cfgService.unregisterProperties(getClass(), false);
alarmProvider.removeAlarmListener(alarmListener);
if (alarmPollExecutor != null) {
alarmPollExecutor.shutdownNow();
}
alarms.clear();
log.info("Stopped");
}
......@@ -199,24 +304,36 @@ public class AlarmsManager implements AlarmService {
log.info("No configuration file");
return false;
}
final Dictionary<?, ?> properties = context.getProperties();
final String ipaddresses = get(properties, "fmDevices");
log.info("Settings: devConfigs={}", ipaddresses);
if (!isNullOrEmpty(ipaddresses)) {
discover(ipaddresses);
Dictionary<?, ?> properties = context.getProperties();
String clearedAlarmPurgeSeconds = get(properties, "clearedAlarmPurgeSeconds");
log.info("Settings: clearedAlarmPurgeSeconds={}", clearedAlarmPurgeSeconds);
}
return true;
}
private void discover(String ipaddresses) {
for (String deviceEntry : ipaddresses.split(",")) {
final DeviceId deviceId = DeviceId.deviceId(deviceEntry);
if (deviceId != null) {
log.info("Device {} needs to have its alarms refreshed!", deviceId);
discoverAlarmsForDevice(deviceId);
}
// Synchronised to prevent duplicate NE alarms being raised
synchronized void updateAlarms(Set<Alarm> discoveredSet, DeviceId deviceId) {
Set<Alarm> storedSet = getActiveAlarms(deviceId);
log.trace("currentNeAlarms={}. discoveredAlarms={}", storedSet, discoveredSet);
if (CollectionUtils.isEqualCollection(storedSet, discoveredSet)) {
log.debug("Alarm lists are equivalent so no update for {}.", deviceId);
return;
}
storedSet.stream().filter(
(stored) -> (!discoveredSet.contains(stored))).forEach((stored) -> {
log.info("Alarm will be cleared as it is not on the element. Cleared alarm: {}.", stored);
clear(stored.id());
});
discoveredSet.stream().filter(
(discovered) -> (!storedSet.contains(discovered))).forEach((discovered) -> {
log.info("Alarm will be raised as it is missing. New alarm: {}.", discovered);
AlarmId id = generateAlarmId();
alarms.put(id, new DefaultAlarm.Builder(discovered).withId(id).build());
});
}
}
......
/*
* Copyright 2015 Open Networking Laboratory
*
* 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.impl;
import com.google.common.collect.Sets;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import org.junit.Test;
import static org.junit.Assert.*;
import org.onlab.util.ItemNotFoundException;
import org.onosproject.net.DeviceId;
import static org.hamcrest.Matchers.containsString;
import org.onosproject.incubator.net.faultmanagement.alarm.Alarm;
import static org.onosproject.incubator.net.faultmanagement.alarm.Alarm.SeverityLevel.*;
import org.onosproject.incubator.net.faultmanagement.alarm.AlarmEntityId;
import org.onosproject.incubator.net.faultmanagement.alarm.AlarmId;
import org.onosproject.incubator.net.faultmanagement.alarm.DefaultAlarm;
public class AlarmsManagerTest {
@Test
public void testGettersWhenNoAlarms() {
AlarmsManager am = new AlarmsManager();
assertTrue("No alarms", am.getAlarms().isEmpty());
assertTrue("No active alarms", am.getActiveAlarms().isEmpty());
assertTrue("No alarms gives empty map per unknown device", am.getAlarmCounts(DeviceId.NONE).keySet().isEmpty());
assertTrue("No alarms gives empty map", am.getAlarmCounts().keySet().isEmpty());
assertEquals("Zero alarms for that device", 0, am.getAlarms(DeviceId.NONE).size());
assertEquals("Zero major alarms", 0, am.getAlarms(Alarm.SeverityLevel.MAJOR).size());
try {
assertEquals("no alarms", 0, am.getAlarm(null));
} catch (NullPointerException ex) {
assertThat(ex.getMessage(),
containsString("cannot be null"));
}
try {
assertEquals("no alarms", 0, am.getAlarm(AlarmId.alarmId(1)));
} catch (ItemNotFoundException ex) {
assertThat(ex.getMessage(),
containsString("not found"));
}
}
@Test
public void testAlarmUpdates() {
AlarmsManager am = new AlarmsManager();
assertTrue("no alarms", am.getAlarms().isEmpty());
am.updateAlarms(new HashSet<>(), DEVICE_ID);
assertTrue("still no alarms", am.getAlarms().isEmpty());
Map<Alarm.SeverityLevel, Long> zeroAlarms = new CountsMapBuilder().create();
assertEquals(zeroAlarms, am.getAlarmCounts());
assertEquals(zeroAlarms, am.getAlarmCounts(DEVICE_ID));
am.updateAlarms(Sets.newHashSet(ALARM_B, ALARM_A), DEVICE_ID);
verifyGettingSetsOfAlarms(am, 2, 2);
Map<Alarm.SeverityLevel, Long> critical2 = new CountsMapBuilder().with(CRITICAL, 2L).create();
assertEquals(critical2, am.getAlarmCounts());
assertEquals(critical2, am.getAlarmCounts(DEVICE_ID));
am.updateAlarms(Sets.newHashSet(ALARM_A), DEVICE_ID);
verifyGettingSetsOfAlarms(am, 2, 1);
Map<Alarm.SeverityLevel, Long> critical1cleared1 =
new CountsMapBuilder().with(CRITICAL, 1L).with(CLEARED, 1L).create();
assertEquals(critical1cleared1, am.getAlarmCounts());
assertEquals(critical1cleared1, am.getAlarmCounts(DEVICE_ID));
// No change map when same alarms sent
am.updateAlarms(Sets.newHashSet(ALARM_A), DEVICE_ID);
verifyGettingSetsOfAlarms(am, 2, 1);
assertEquals(critical1cleared1, am.getAlarmCounts());
assertEquals(critical1cleared1, am.getAlarmCounts(DEVICE_ID));
am.updateAlarms(Sets.newHashSet(ALARM_A, ALARM_A_WITHSRC), DEVICE_ID);
verifyGettingSetsOfAlarms(am, 3, 2);
Map<Alarm.SeverityLevel, Long> critical2cleared1 =
new CountsMapBuilder().with(CRITICAL, 2L).with(CLEARED, 1L).create();
assertEquals(critical2cleared1, am.getAlarmCounts());
assertEquals(critical2cleared1, am.getAlarmCounts(DEVICE_ID));
am.updateAlarms(Sets.newHashSet(), DEVICE_ID);
verifyGettingSetsOfAlarms(am, 3, 0);
assertEquals(new CountsMapBuilder().with(CLEARED, 3L).create(), am.getAlarmCounts(DEVICE_ID));
assertEquals("No alarms for unknown devices", zeroAlarms, am.getAlarmCounts(DeviceId.NONE));
assertEquals("No alarms for unknown devices", zeroAlarms, am.getAlarmCounts(DeviceId.deviceId("junk:junk")));
}
private void verifyGettingSetsOfAlarms(AlarmsManager am, int expectedTotal, int expectedActive) {
assertEquals("Wrong total", expectedTotal, am.getAlarms().size());
assertEquals("Wrong active count", expectedActive, am.getActiveAlarms().size());
}
private static final DeviceId DEVICE_ID = DeviceId.deviceId("foo:bar");
private static final DefaultAlarm ALARM_A = new DefaultAlarm.Builder(
DEVICE_ID, "aaa", Alarm.SeverityLevel.CRITICAL, 0).build();
private static final DefaultAlarm ALARM_A_WITHSRC = new DefaultAlarm.Builder(
ALARM_A).forSource(AlarmEntityId.alarmEntityId("port:foo")).build();
private static final DefaultAlarm ALARM_B = new DefaultAlarm.Builder(
DEVICE_ID, "bbb", Alarm.SeverityLevel.CRITICAL, 0).build();
private static class CountsMapBuilder {
private final Map<Alarm.SeverityLevel, Long> map = new HashMap<>();
public CountsMapBuilder with(Alarm.SeverityLevel sev, Long count) {
map.put(sev, count);
return this;
}
public Map<Alarm.SeverityLevel, Long> create() {
return Collections.unmodifiableMap(map);
}
}
}
......@@ -124,6 +124,7 @@
com.fasterxml.jackson.core,
org.apache.karaf.shell.commands,
org.apache.commons.lang.math.*,
org.apache.commons.lang.*,
com.google.common.*,
org.onlab.packet.*,
org.onlab.rest.*,
......
......@@ -66,28 +66,29 @@ public final class AlarmCodec extends JsonCodec<Alarm> {
}
log.debug("id={}, full json={} ", json.get("id"), json);
final Long id = json.get("id").asLong();
Long id = json.get("id").asLong();
final DeviceId deviceId = DeviceId.deviceId(json.get("deviceId").asText());
final String description = json.get("description").asText();
final Long timeRaised = json.get("timeRaised").asLong();
final Long timeUpdated = json.get("timeUpdated").asLong();
DeviceId deviceId = DeviceId.deviceId(json.get("deviceId").asText());
String description = json.get("description").asText();
Long timeRaised = json.get("timeRaised").asLong();
Long timeUpdated = json.get("timeUpdated").asLong();
final JsonNode jsonTimeCleared = json.get("timeCleared");
final Long timeCleared = jsonTimeCleared == null || jsonTimeCleared.isNull() ? null : jsonTimeCleared.asLong();
JsonNode jsonTimeCleared = json.get("timeCleared");
Long timeCleared = jsonTimeCleared == null || jsonTimeCleared.isNull() ? null : jsonTimeCleared.asLong();
final Alarm.SeverityLevel severity = Alarm.SeverityLevel.valueOf(json.get("severity").asText().toUpperCase());
Alarm.SeverityLevel severity = Alarm.SeverityLevel.valueOf(json.get("severity").asText().toUpperCase());
final Boolean serviceAffecting = json.get("serviceAffecting").asBoolean();
final Boolean acknowledged = json.get("acknowledged").asBoolean();
final Boolean manuallyClearable = json.get("manuallyClearable").asBoolean();
Boolean serviceAffecting = json.get("serviceAffecting").asBoolean();
Boolean acknowledged = json.get("acknowledged").asBoolean();
Boolean manuallyClearable = json.get("manuallyClearable").asBoolean();
final JsonNode jsonAssignedUser = json.get("assignedUser");
final String assignedUser
JsonNode jsonAssignedUser = json.get("assignedUser");
String assignedUser
= jsonAssignedUser == null || jsonAssignedUser.isNull() ? null : jsonAssignedUser.asText();
return new DefaultAlarm.Builder(
AlarmId.valueOf(id), deviceId, description, severity, timeRaised).forSource(AlarmEntityId.NONE).
deviceId, description, severity, timeRaised).forSource(AlarmEntityId.NONE).
withId(AlarmId.alarmId(id)).
withTimeUpdated(timeUpdated).
withTimeCleared(timeCleared).
withServiceAffecting(serviceAffecting).
......
/*
* Copyright 2014-2015 Open Networking Laboratory
* Copyright 2015 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -34,7 +34,9 @@ import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import org.apache.commons.lang.StringUtils;
import org.onosproject.incubator.net.faultmanagement.alarm.AlarmService;
import org.onosproject.net.DeviceId;
import org.slf4j.Logger;
import static org.slf4j.LoggerFactory.getLogger;
......@@ -48,31 +50,32 @@ public class AlarmsWebResource extends AbstractWebResource {
private final Logger log = getLogger(getClass());
public AlarmsWebResource() {
}
/**
* Get all alarms. Returns a list of all alarms across all devices.
* Get alarms. Returns a list of alarms
*
* @param includeCleared include recently cleared alarms in response
* @param includeCleared (optional) include recently cleared alarms in response
* @param devId (optional) include only for specified device
* @return JSON encoded set of alarms
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getAlarms(@DefaultValue("false") @QueryParam("includeCleared") boolean includeCleared
public Response getAlarms(@DefaultValue("false") @QueryParam("includeCleared") boolean includeCleared,
@DefaultValue("") @QueryParam("devId") String devId
) {
log.info("Requesting all alarms, includeCleared={}", includeCleared);
final AlarmService service = get(AlarmService.class);
final Iterable<Alarm> alarms = includeCleared
? service.getAlarms()
: service.getActiveAlarms();
final ObjectNode result = new ObjectMapper().createObjectNode();
result.set("alarms",
codec(Alarm.class).
encode(alarms, this));
AlarmService service = get(AlarmService.class);
Iterable<Alarm> alarms;
if (StringUtils.isBlank(devId)) {
alarms = includeCleared
? service.getAlarms()
: service.getActiveAlarms();
} else {
alarms = service.getAlarms(DeviceId.deviceId(devId));
}
ObjectNode result = new ObjectMapper().createObjectNode();
result.set("alarms", new AlarmCodec().encode(alarms, this));
return ok(result.toString()).build();
}
......@@ -89,11 +92,11 @@ public class AlarmsWebResource extends AbstractWebResource {
public Response getAlarm(@PathParam("id") String id) {
log.info("HTTP GET alarm for id={}", id);
final AlarmId alarmId = toAlarmId(id);
final Alarm alarm = get(AlarmService.class).getAlarm(alarmId);
AlarmId alarmId = toAlarmId(id);
Alarm alarm = get(AlarmService.class).getAlarm(alarmId);
final ObjectNode result = mapper().createObjectNode();
result.set("alarm", codec(Alarm.class).encode(alarm, this));
ObjectNode result = new ObjectMapper().createObjectNode();
result.set("alarm", new AlarmCodec().encode(alarm, this));
return ok(result.toString()).build();
}
......@@ -113,20 +116,22 @@ public class AlarmsWebResource extends AbstractWebResource {
log.info("PUT NEW ALARM at /{}", alarmIdPath);
try {
final ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
log.info("jsonTree={}", jsonTree);
final Alarm alarm = codec(Alarm.class).decode(jsonTree, this);
Alarm alarm = new AlarmCodec().decode(jsonTree, this);
final AlarmService service = get(AlarmService.class);
AlarmService service = get(AlarmService.class);
if (Long.parseLong(alarmIdPath) != alarm.id().fingerprint()) {
throw new IllegalArgumentException("id in path is " + Long.parseLong(alarmIdPath)
+ " but payload uses id=" + alarm.id().fingerprint());
}
final Alarm updated = service.update(alarm);
final ObjectNode encoded = new AlarmCodec().encode(updated, this);
Alarm updated = service.updateBookkeepingFields(
alarm.id(), alarm.acknowledged(), alarm.assignedUser()
);
ObjectNode encoded = new AlarmCodec().encode(updated, this);
return ok(encoded.toString()).build();
} catch (IOException ioe) {
......@@ -136,7 +141,7 @@ public class AlarmsWebResource extends AbstractWebResource {
private static AlarmId toAlarmId(String id) {
try {
return AlarmId.valueOf(Long.parseLong(id));
return AlarmId.alarmId(Long.parseLong(id));
} catch (NumberFormatException ex) {
throw new IllegalArgumentException("Alarm id should be numeric", ex);
}
......
......@@ -25,7 +25,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
/**
* Mock codec context for use in codec unit tests.
*/
public class AlarmCodecContext implements CodecContext {
public final class AlarmCodecContext implements CodecContext {
private final ObjectMapper mapper = new ObjectMapper();
private final CodecManager codecManager = new CodecManager();
......
......@@ -36,37 +36,26 @@ import org.onosproject.incubator.net.faultmanagement.alarm.DefaultAlarm;
public class AlarmCodecTest {
private final AlarmCodecContext context = new AlarmCodecContext();
private static final AlarmId ALARM_ID = AlarmId.alarmId(44);
// Use this to check handling for miminal Alarm
private final Alarm alarmMinimumFields = new DefaultAlarm.Builder(
new AlarmId(44),
DeviceId.deviceId("of:2222000000000000"),
"NE unreachable",
Alarm.SeverityLevel.CLEARED,
1).
build();
DeviceId.deviceId("of:2222000000000000"), "NE unreachable", Alarm.SeverityLevel.CLEARED, 1
).withId(ALARM_ID).build();
// Use this to check handling for fully populated Alarm
private final Alarm alarmWithSource = new DefaultAlarm.Builder(
new AlarmId(44),
DeviceId.deviceId("of:2222000000000000"),
"NE unreachable",
Alarm.SeverityLevel.CLEARED, 1).
forSource(AlarmEntityId.alarmEntityId("port:1/2/3/4")).
withTimeUpdated(2).
withTimeCleared(3L).
withServiceAffecting(true).
withAcknowledged(true).
withManuallyClearable(true).
DeviceId.deviceId("of:2222000000000000"), "NE unreachable", Alarm.SeverityLevel.CLEARED, 1
).withId(ALARM_ID).forSource(AlarmEntityId.alarmEntityId("port:1/2/3/4")).withTimeUpdated(2).withTimeCleared(3L).
withServiceAffecting(true).withAcknowledged(true).withManuallyClearable(true).
withAssignedUser("the assigned user").build();
@Test
public void alarmCodecTestWithOptionalFieldMissing() {
//context.registerService(AlarmService.class, new AlarmServiceAdapter());
final JsonCodec<Alarm> codec = context.codec(Alarm.class);
JsonCodec<Alarm> codec = context.codec(Alarm.class);
assertThat(codec, is(notNullValue()));
final ObjectNode alarmJson = codec.encode(alarmMinimumFields, context);
ObjectNode alarmJson = codec.encode(alarmMinimumFields, context);
assertThat(alarmJson, notNullValue());
assertThat(alarmJson, matchesAlarm(alarmMinimumFields));
......@@ -74,10 +63,10 @@ public class AlarmCodecTest {
@Test
public void alarmCodecTestWithOptionalField() {
final JsonCodec<Alarm> codec = context.codec(Alarm.class);
JsonCodec<Alarm> codec = context.codec(Alarm.class);
assertThat(codec, is(notNullValue()));
final ObjectNode alarmJson = codec.encode(alarmWithSource, context);
ObjectNode alarmJson = codec.encode(alarmWithSource, context);
assertThat(alarmJson, notNullValue());
assertThat(alarmJson, matchesAlarm(alarmWithSource));
......@@ -85,9 +74,9 @@ public class AlarmCodecTest {
@Test
public void verifyMinimalAlarmIsEncoded() throws Exception {
final JsonCodec<Alarm> alarmCodec = context.codec(Alarm.class);
JsonCodec<Alarm> alarmCodec = context.codec(Alarm.class);
final Alarm alarm = getDecodedAlarm(alarmCodec, "alarm-minimal.json");
Alarm alarm = getDecodedAlarm(alarmCodec, "alarm-minimal.json");
assertCommon(alarm);
assertThat(alarm.timeCleared(), nullValue());
......@@ -97,9 +86,9 @@ public class AlarmCodecTest {
@Test
public void verifyFullyLoadedAlarmIsEncoded() throws Exception {
final JsonCodec<Alarm> alarmCodec = context.codec(Alarm.class);
JsonCodec<Alarm> alarmCodec = context.codec(Alarm.class);
final Alarm alarm = getDecodedAlarm(alarmCodec, "alarm-full.json");
Alarm alarm = getDecodedAlarm(alarmCodec, "alarm-full.json");
assertCommon(alarm);
assertThat(alarm.timeCleared(), is(2222L));
......@@ -108,7 +97,7 @@ public class AlarmCodecTest {
}
private void assertCommon(Alarm alarm) {
assertThat(alarm.id(), is(new AlarmId(10L)));
assertThat(alarm.id(), is(AlarmId.alarmId(10L)));
assertThat(alarm.description(), is("NE is not reachable"));
assertThat(alarm.source(), is(AlarmEntityId.NONE));
assertThat(alarm.timeRaised(), is(999L));
......@@ -127,14 +116,14 @@ public class AlarmCodecTest {
* @throws IOException if processing the resource fails to decode
*/
private Alarm getDecodedAlarm(JsonCodec<Alarm> codec, String resourceName) throws IOException {
final InputStream jsonStream = AlarmCodecTest.class
.getResourceAsStream(resourceName);
final JsonNode json = context.mapper().readTree(jsonStream);
assertThat(json, notNullValue());
final Alarm result = codec.decode((ObjectNode) json, context);
assertThat(result, notNullValue());
return result;
try (InputStream jsonStream = AlarmCodecTest.class
.getResourceAsStream(resourceName)) {
JsonNode json = context.mapper().readTree(jsonStream);
assertThat(json, notNullValue());
Alarm result = codec.decode((ObjectNode) json, context);
assertThat(result, notNullValue());
return result;
}
}
}
......
......@@ -34,48 +34,48 @@ public final class AlarmJsonMatcher extends TypeSafeDiagnosingMatcher<JsonNode>
@Override
public boolean matchesSafely(JsonNode jsonAlarm, Description description) {
final String jsonAlarmId = jsonAlarm.get("id").asText();
final String alarmId = Long.toString(alarm.id().fingerprint());
String jsonAlarmId = jsonAlarm.get("id").asText();
String alarmId = Long.toString(alarm.id().fingerprint());
if (!jsonAlarmId.equals(alarmId)) {
description.appendText("alarm id was " + jsonAlarmId);
return false;
}
final String jsonDeviceId = jsonAlarm.get("deviceId").asText();
final String alarmDeviceId = alarm.deviceId().toString();
String jsonDeviceId = jsonAlarm.get("deviceId").asText();
String alarmDeviceId = alarm.deviceId().toString();
if (!jsonDeviceId.equals(alarmDeviceId)) {
description.appendText("DeviceId was " + jsonDeviceId);
return false;
}
final String jsonDescription = jsonAlarm.get("description").asText();
final String alarmDesc = alarm.description();
String jsonDescription = jsonAlarm.get("description").asText();
String alarmDesc = alarm.description();
if (!jsonDescription.equals(alarmDesc)) {
description.appendText("description was " + jsonDescription);
return false;
}
final long jsonTimeRaised = jsonAlarm.get("timeRaised").asLong();
final long timeRaised = alarm.timeRaised();
long jsonTimeRaised = jsonAlarm.get("timeRaised").asLong();
long timeRaised = alarm.timeRaised();
if (timeRaised != jsonTimeRaised) {
description.appendText("timeRaised was " + jsonTimeRaised);
return false;
}
final long jsonTimeUpdated = jsonAlarm.get("timeUpdated").asLong();
final long timeUpdated = alarm.timeUpdated();
long jsonTimeUpdated = jsonAlarm.get("timeUpdated").asLong();
long timeUpdated = alarm.timeUpdated();
if (timeUpdated != jsonTimeUpdated) {
description.appendText("timeUpdated was " + jsonTimeUpdated);
return false;
}
final JsonNode jsonTimeClearedNode = jsonAlarm.get("timeCleared");
JsonNode jsonTimeClearedNode = jsonAlarm.get("timeCleared");
if (alarm.timeCleared() != null) {
final Long jsonTimeCleared = jsonTimeClearedNode.longValue();
final Long timeCleared = alarm.timeCleared();
Long jsonTimeCleared = jsonTimeClearedNode.longValue();
Long timeCleared = alarm.timeCleared();
if (!timeCleared.equals(jsonTimeCleared)) {
description.appendText("Time Cleared was " + jsonTimeCleared);
......@@ -89,18 +89,18 @@ public final class AlarmJsonMatcher extends TypeSafeDiagnosingMatcher<JsonNode>
}
}
final String jsonSeverity = jsonAlarm.get("severity").asText();
final String severity = alarm.severity().toString();
String jsonSeverity = jsonAlarm.get("severity").asText();
String severity = alarm.severity().toString();
if (!severity.equals(jsonSeverity)) {
description.appendText("severity was " + jsonSeverity);
return false;
}
final JsonNode jsonAlarmNode = jsonAlarm.get("source");
JsonNode jsonAlarmNode = jsonAlarm.get("source");
if (alarm.source() != null) {
final String jsonSource = jsonAlarmNode.textValue();
final String source = alarm.source().toString();
String jsonSource = jsonAlarmNode.textValue();
String source = alarm.source().toString();
if (!source.equals(jsonSource)) {
description.appendText("source was " + jsonSource);
......
......@@ -38,10 +38,10 @@ public class AlarmsWebResourceTest extends ResourceTest {
@Before
public void setUp() {
final CodecManager codecService = new CodecManager();
CodecManager codecService = new CodecManager();
codecService.activate();
final ServiceDirectory testDirectory = new TestServiceDirectory()
ServiceDirectory testDirectory = new TestServiceDirectory()
// Currently no alarms-service implemented
// .add(AlarmsService.class, alarmsService)
.add(CodecService.class, codecService);
......@@ -51,8 +51,8 @@ public class AlarmsWebResourceTest extends ResourceTest {
@Test
@Ignore
public void getAllAlarms() {
final WebResource rs = resource();
final String response = rs.path("/alarms").get(String.class);
WebResource rs = resource();
String response = rs.path("/alarms").get(String.class);
// Ensure hard-coded alarms returned okay
assertThat(response, containsString("\"NE is not reachable\","));
assertThat(response, containsString("\"Equipment Missing\","));
......@@ -61,8 +61,8 @@ public class AlarmsWebResourceTest extends ResourceTest {
@Test
@Ignore
public void getAlarm() {
final WebResource rs = resource();
final String response = rs.path("/alarms/1").get(String.class);
WebResource rs = resource();
String response = rs.path("/alarms/1").get(String.class);
// Ensure hard-coded alarms returned okay
assertThat(response, containsString("\"NE is not reachable\","));
assertThat(response, not(containsString("\"Equipment Missing\",")));
......
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2014 Open Networking Laboratory
~
~ 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.
-->
~ Copyright 2015 Open Networking Laboratory
~
~ 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
......@@ -34,10 +34,12 @@
<modules>
<module>fmmgr</module>
<module>fmweb</module>
<module>fmgui</module>
<module>fmcli</module>
<module>app</module>
</modules>
<dependencies>
<dependency>
</modules>
<dependencies>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onlab-junit</artifactId>
<scope>test</scope>
......
......@@ -15,51 +15,64 @@
*/
package org.onosproject.incubator.net.faultmanagement.alarm;
import java.util.Set;
import org.onosproject.event.AbstractEvent;
import org.onosproject.net.DeviceId;
/**
* Entity that represents Alarm events.
* Entity that represents Alarm events. Note: although the event will itself have a time, consumers may be more
* interested in the times embedded in the alarms themselves.
*
*/
public class AlarmEvent extends AbstractEvent<AlarmEvent.Type, Alarm> {
public class AlarmEvent extends AbstractEvent<AlarmEvent.Type, Set<Alarm>> {
private final DeviceId deviceRefreshed;
/**
* Creates an event due to one or more notification.
*
* @param alarms the set one or more of alarms.
*/
public AlarmEvent(Set<Alarm> alarms) {
super(Type.NOTIFICATION, alarms);
deviceRefreshed = null;
}
/**
* Creates an event of a given type and for the specified alarm and the
* current time.
* Creates an event due to alarm discovery for a device.
*
* @param type topology event type
* @param alarm the alarm
* @param alarms the set of alarms.
* @param deviceRefreshed if of refreshed device, populated after a de-discovery
*/
public AlarmEvent(Type type, Alarm alarm) {
super(type, alarm);
public AlarmEvent(Set<Alarm> alarms,
DeviceId deviceRefreshed) {
super(Type.DEVICE_DISCOVERY, alarms);
this.deviceRefreshed = deviceRefreshed;
}
/**
* Creates an event of a given type and for the specified alarm and time.
* Gets which device was refreshed.
*
* @param type link event type
* @param alarm the alarm
* @param time occurrence time
* @return the refreshed device, or null if event related to a asynchronous notification(s)
*/
public AlarmEvent(Type type, Alarm alarm,
long time) {
super(type, alarm, time);
public DeviceId getDeviceRefreshed() {
return deviceRefreshed;
}
/**
* Type of alarm events.
* Type of alarm event.
*/
public enum Type {
/**
* A Raised Alarm.
* Individual alarm(s) updated.
*/
RAISE,
NOTIFICATION,
/**
* A Cleared Alarm.
* Alarm set updated for a given device.
*/
CLEAR
DEVICE_DISCOVERY,
}
}
......
......@@ -16,11 +16,9 @@
package org.onosproject.incubator.net.faultmanagement.alarm;
import com.google.common.annotations.Beta;
import java.util.Objects;
import static com.google.common.base.MoreObjects.toStringHelper;
import static com.google.common.base.Preconditions.checkArgument;
/**
* Alarm identifier suitable as an external key.
* <p>
......@@ -30,23 +28,29 @@ import static com.google.common.base.MoreObjects.toStringHelper;
public final class AlarmId {
private final long id;
public static final AlarmId NONE = new AlarmId();
/**
* Instantiates a new Alarm id.
*
* @param id the id
*/
public AlarmId(final long id) {
private AlarmId(long id) {
checkArgument(id != 0L, "id must be non-zero");
this.id = id;
}
private AlarmId() {
this.id = 0L;
}
/**
* Creates an alarm identifier from the specified long representation.
*
* @param value long value
* @return intent identifier
*/
public static AlarmId valueOf(final long value) {
public static AlarmId alarmId(long value) {
return new AlarmId(value);
}
......@@ -65,12 +69,12 @@ public final class AlarmId {
}
@Override
public boolean equals(final Object obj) {
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof AlarmId) {
final AlarmId other = (AlarmId) obj;
AlarmId other = (AlarmId) obj;
return Objects.equals(this.id, other.id);
}
return false;
......
......@@ -24,14 +24,25 @@ import org.onosproject.net.provider.Provider;
public interface AlarmProvider extends Provider {
/**
* Triggers an asynchronous discovery of the alarms on the specified device,
* intended to refresh internal alarm model for the device. An indirect
* result of this should be invocation of
* {@link org.onosproject.incubator.net.faultmanagement.alarm.AlarmProviderService#updateAlarmList} )}
* at some later point in time.
* Triggers an asynchronous discovery of the alarms on the specified device, intended to refresh internal alarm
* model for the device. An indirect result of this should be a event sent later with discovery result ie a set of
* alarms.
*
* @param deviceId ID of device to be probed
*/
void triggerProbe(DeviceId deviceId);
/**
* Register a listener for alarms.
*
* @param listener the listener to notify
*/
void addAlarmListener(AlarmListener listener);
/**
* Unregister a listener.
*
* @param listener the listener to unregister
*/
void removeAlarmListener(AlarmListener listener);
}
......
......@@ -16,6 +16,7 @@
package org.onosproject.incubator.net.faultmanagement.alarm;
import com.google.common.annotations.Beta;
import org.onosproject.net.DeviceId;
import org.onosproject.net.provider.ProviderService;
......@@ -24,6 +25,7 @@ import java.util.Collection;
/**
* The interface Alarm provider service.
*/
@Beta
public interface AlarmProviderService extends ProviderService<AlarmProvider> {
/**
......
......@@ -16,60 +16,65 @@
package org.onosproject.incubator.net.faultmanagement.alarm;
import com.google.common.annotations.Beta;
//import org.onosproject.event.ListenerService;
import java.util.Map;
import java.util.Set;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DeviceId;
/**
* Service for interacting with the alarm handling of devices. Unless stated
* otherwise method return active AND recently-cleared alarms.
* Service for interacting with the alarm handling of devices. Unless stated otherwise, getter methods
* return active AND recently-cleared alarms.
*/
@Beta
public interface AlarmService {
// extends ListenerService<AlarmEvent, AlarmListener> {
/**
* Alarm should be updated in ONOS's internal representation; only
* administration/book-keeping fields may be updated. Attempting to update
* fields which are mapped directly from device is prohibited.
* Update book-keeping (ie administrative) fields for the alarm matching the specified identifier.
*
* @param id alarm identifier
* @param isAcknowledged new acknowledged state
* @param assignedUser new assigned user, null clear
* @return updated alarm (including any recent device-derived changes)
*
* @param replacement alarm with updated book-keeping fields
* @return updated alarm (including any recent device derived changes)
* @throws java.lang.IllegalStateException if attempt to update not allowed
* fields.
*/
Alarm update(Alarm replacement);
Alarm updateBookkeepingFields(AlarmId id, boolean isAcknowledged, String assignedUser);
/**
* Returns the number of ACTIVE alarms on a device.
* Returns summary of alarms on a given device.
*
* @param deviceId the device
* @return number of alarms
* @return map of severity (if applicable) vs alarm counts; empty map if either the device has no alarms or
* identified device is not managed.
*/
int getActiveAlarmCount(DeviceId deviceId);
Map<Alarm.SeverityLevel, Long> getAlarmCounts(DeviceId deviceId);
/**
* Returns summary of alarms on all devices.
*
* @return map of severity (if applicable) vs alarm counts; empty map if no alarms.
*/
Map<Alarm.SeverityLevel, Long> getAlarmCounts();
/**
* Returns the alarm with the specified identifier.
*
* @param alarmId alarm identifier
* @return alarm or null if one with the given identifier is not known
* @return alarm matching id; null if no alarm matches the identifier.
*/
Alarm getAlarm(AlarmId alarmId);
/**
* Returns all of the alarms.
*
* @return the alarms
* @return set of alarms; empty set if no alarms
*/
Set<Alarm> getAlarms();
/**
* Returns all of the ACTIVE alarms. Recently cleared alarms excluded.
*
* @return the alarms
* @return set of alarms; empty set if no alarms
*/
Set<Alarm> getActiveAlarms();
......@@ -77,16 +82,15 @@ public interface AlarmService {
* Returns the alarms with the specified severity.
*
* @param severity the alarm severity
* @return the active alarms with a particular severity
* @return set of alarms with a particular severity; empty set if no alarms
*/
Set<Alarm> getAlarms(Alarm.SeverityLevel severity);
/**
* Returns the alarm for a given device, regardless of source within that
* device.
* Returns the alarm matching a given device, regardless of source within that device.
*
* @param deviceId the device
* @return the alarms
* @param deviceId the device to use when searching alarms.
* @return set of alarms; empty set if no alarms
*/
Set<Alarm> getAlarms(DeviceId deviceId);
......@@ -95,7 +99,7 @@ public interface AlarmService {
*
* @param deviceId the device
* @param source the source within the device
* @return the alarms
* @return set of alarms; empty set if no alarms
*/
Set<Alarm> getAlarms(DeviceId deviceId, AlarmEntityId source);
......@@ -104,7 +108,7 @@ public interface AlarmService {
*
* @param src one end of the link
* @param dst one end of the link
* @return the alarms
* @return set of alarms; empty set if no alarms
*/
Set<Alarm> getAlarmsForLink(ConnectPoint src, ConnectPoint dst);
......@@ -113,9 +117,9 @@ public interface AlarmService {
*
* @param deviceId the device
* @param flowId the flow
* @return the alarms
* @return set of alarms; empty set if no alarms
*/
Set<Alarm> getAlarmsForFlow(DeviceId deviceId, long flowId);
// Support retrieving alarms affecting other ONOS entity types may be added in future release
// TODO Support retrieving alarms affecting other entity types may be added in future release
}
......
......@@ -145,14 +145,16 @@ public final class DefaultAlarm implements Alarm {
@Override
public int hashCode() {
return Objects.hash(id, deviceId, description,
source, timeRaised, timeUpdated, timeCleared, severity,
// id or timeRaised or timeUpdated may differ
return Objects.hash(deviceId, description,
source, timeCleared, severity,
isServiceAffecting, isAcknowledged,
isManuallyClearable, assignedUser);
}
@Override
public boolean equals(final Object obj) {
// Make sure equals() is tune with hashCode() so works ok in a hashSet !
if (obj == null) {
return false;
}
......@@ -160,9 +162,8 @@ public final class DefaultAlarm implements Alarm {
return false;
}
final DefaultAlarm other = (DefaultAlarm) obj;
if (!Objects.equals(this.id, other.id)) {
return false;
}
// id or timeRaised or timeUpdated may differ
if (!Objects.equals(this.deviceId, other.deviceId)) {
return false;
}
......@@ -172,12 +173,7 @@ public final class DefaultAlarm implements Alarm {
if (!Objects.equals(this.source, other.source)) {
return false;
}
if (this.timeRaised != other.timeRaised) {
return false;
}
if (this.timeUpdated != other.timeUpdated) {
return false;
}
if (!Objects.equals(this.timeCleared, other.timeCleared)) {
return false;
}
......@@ -219,11 +215,11 @@ public final class DefaultAlarm implements Alarm {
public static class Builder {
// Manadatory fields ..
private final AlarmId id;
// Manadatory fields when constructing alarm ...
private AlarmId id;
private final DeviceId deviceId;
private final String description;
private final SeverityLevel severity;
private SeverityLevel severity;
private final long timeRaised;
// Optional fields ..
......@@ -236,8 +232,8 @@ public final class DefaultAlarm implements Alarm {
private String assignedUser = null;
public Builder(final Alarm alarm) {
this(alarm.id(), alarm.deviceId(), alarm.description(), alarm.severity(), alarm.timeRaised());
this.source = AlarmEntityId.NONE;
this(alarm.deviceId(), alarm.description(), alarm.severity(), alarm.timeRaised());
this.source = alarm.source();
this.timeUpdated = alarm.timeUpdated();
this.timeCleared = alarm.timeCleared();
this.isServiceAffecting = alarm.serviceAffecting();
......@@ -247,10 +243,10 @@ public final class DefaultAlarm implements Alarm {
}
public Builder(final AlarmId id, final DeviceId deviceId,
public Builder(final DeviceId deviceId,
final String description, final SeverityLevel severity, final long timeRaised) {
super();
this.id = id;
this.id = AlarmId.NONE;
this.deviceId = deviceId;
this.description = description;
this.severity = severity;
......@@ -274,6 +270,17 @@ public final class DefaultAlarm implements Alarm {
return this;
}
public Builder withId(final AlarmId id) {
this.id = id;
return this;
}
public Builder clear() {
this.severity = SeverityLevel.CLEARED;
final long now = System.currentTimeMillis();
return withTimeCleared(now).withTimeUpdated(now);
}
public Builder withServiceAffecting(final boolean isServiceAffecting) {
this.isServiceAffecting = isServiceAffecting;
return this;
......
......@@ -16,19 +16,21 @@
package org.onosproject.incubator.net.faultmanagement.alarm;
import com.google.common.testing.EqualsTester;
import static org.hamcrest.Matchers.containsString;
import org.junit.Test;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
/**
* This class tests the immutability, equality, and non-equality of
* {@link AlarmId}.
* This class tests the immutability, equality, and non-equality of {@link AlarmId}.
*/
public class AlarmIdTest {
private static final long ID_A = 1L;
private static final long ID_B = 2L;
private static final long ID_Z = 987654321L;
......@@ -46,28 +48,27 @@ public class AlarmIdTest {
*/
@Test
public void testEquality() {
final AlarmId id1 = new AlarmId(ID_A);
final AlarmId id2 = new AlarmId(ID_A);
final AlarmId id1 = AlarmId.alarmId(ID_A);
final AlarmId id2 = AlarmId.alarmId(ID_A);
assertThat(id1, is(id2));
}
/**
* Tests non-equality of {@link AlarmId}.
*/
@Test
public void testNonEquality() {
final AlarmId id1 = new AlarmId(ID_A);
final AlarmId id2 = new AlarmId(ID_B);
final AlarmId id1 = AlarmId.alarmId(ID_A);
final AlarmId id2 = AlarmId.alarmId(ID_B);
assertThat(id1, is(not(id2)));
}
@Test
public void valueOf() {
final AlarmId id = new AlarmId(0xdeadbeefL);
assertEquals("incorrect valueOf", id, AlarmId.valueOf(0xdeadbeefL));
final AlarmId id = AlarmId.alarmId(0xdeadbeefL);
assertEquals("incorrect valueOf", id, AlarmId.alarmId(0xdeadbeefL));
}
/**
......@@ -75,9 +76,9 @@ public class AlarmIdTest {
*/
@Test
public void testEquals() {
final AlarmId id1 = new AlarmId(11111L);
final AlarmId sameAsId1 = new AlarmId(11111L);
final AlarmId id2 = new AlarmId(22222L);
final AlarmId id1 = AlarmId.alarmId(11111L);
final AlarmId sameAsId1 = AlarmId.alarmId(11111L);
final AlarmId id2 = AlarmId.alarmId(22222L);
new EqualsTester()
.addEqualityGroup(id1, sameAsId1)
......@@ -90,9 +91,18 @@ public class AlarmIdTest {
*/
@Test
public void testConstruction() {
final AlarmId id1 = new AlarmId(ID_Z);
final AlarmId id1 = AlarmId.alarmId(ID_Z);
assertEquals(id1.fingerprint(), ID_Z);
// No default constructor so no need to test it !
assertEquals(0L, AlarmId.NONE.fingerprint());
try {
final AlarmId bad = AlarmId.alarmId(0L);
fail("0 is a Reserved value but we created " + bad);
} catch (IllegalArgumentException ex) {
assertThat(ex.getMessage(),
containsString("id must be non-zero"));
}
}
}
......
......@@ -18,6 +18,7 @@ package org.onosproject.incubator.net.faultmanagement.alarm;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.greaterThan;
import org.junit.Test;
import static org.junit.Assert.*;
import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
......@@ -35,13 +36,57 @@ public class DefaultAlarmTest {
*/
@Test
public void testConstruction() {
final String nameValue = "name3";
final DefaultAlarm a = new DefaultAlarm.Builder(AlarmId.valueOf(4),
DeviceId.NONE, nameValue, Alarm.SeverityLevel.CLEARED, 3).build();
final DefaultAlarm a = generate();
assertThat(a, is(notNullValue()));
final DefaultAlarm b = new DefaultAlarm.Builder(a).build();
assertEquals(a, b);
}
@Test
public void testEquals() {
final DefaultAlarm a = new DefaultAlarm.Builder(
DeviceId.NONE, "desc", Alarm.SeverityLevel.MINOR, 3).build();
final DefaultAlarm b = new DefaultAlarm.Builder(
DeviceId.NONE, "desc", Alarm.SeverityLevel.MINOR, a.timeRaised() + 1).
withId(ALARM_ID).withTimeUpdated(a.timeUpdated() + 1).build();
assertEquals("id or timeRaised or timeUpdated may differ", a, b);
assertNotEquals(a, new DefaultAlarm.Builder(a).withAcknowledged(!a.acknowledged()).build());
assertNotEquals(a, new DefaultAlarm.Builder(a).withManuallyClearable(!a.manuallyClearable()).build());
assertNotEquals(a, new DefaultAlarm.Builder(a).withServiceAffecting(!a.serviceAffecting()).build());
assertNotEquals(a, new DefaultAlarm.Builder(a).withAssignedUser("Changed" + a.assignedUser()).build());
}
@Test
public void testClear() {
final DefaultAlarm active = generate();
final DefaultAlarm cleared = new DefaultAlarm.Builder(active).clear().build();
assertNotEquals(active, cleared);
assertThat(cleared.timeRaised(), is(active.timeRaised()));
assertThat(cleared.severity(), is(Alarm.SeverityLevel.CLEARED));
assertThat(cleared.timeUpdated(), greaterThan(active.timeUpdated()));
assertNotNull(cleared.timeCleared());
}
@Test
public void testId() {
final DefaultAlarm a = generate();
assertThat(a.id(), is(AlarmId.NONE));
final DefaultAlarm b = new DefaultAlarm.Builder(a).withId(ALARM_ID).build();
assertEquals("id ignored in equals", a, b);
assertNotEquals(ALARM_ID, a.id());
assertEquals(ALARM_ID, b.id());
assertEquals(ALARM_ENTITY_ID, b.source());
}
private static final AlarmEntityId ALARM_ENTITY_ID = AlarmEntityId.alarmEntityId("port:bar");
private static final AlarmId ALARM_ID = AlarmId.alarmId(888L);
private static DefaultAlarm generate() {
return new DefaultAlarm.Builder(
DeviceId.NONE, "desc", Alarm.SeverityLevel.MINOR, 3).forSource(ALARM_ENTITY_ID).build();
}
}
......
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2014 Open Networking Laboratory
~
~ 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.
-->
~ Copyright 2015 Open Networking Laboratory
~
~ 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
......@@ -31,4 +31,136 @@
<description>ONOS SNMP protocol alarm provider</description>
</project>
\ No newline at end of file
<dependencies>
<dependency>
<groupId>com.btisystems</groupId>
<artifactId>snmp-core</artifactId>
<version>1.3-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.btisystems.mibbler.mibs</groupId>
<artifactId>bti7000</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.btisystems.mibbler.mibs</groupId>
<artifactId>net-snmp</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.compendium</artifactId>
<version>5.0.0</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onlab-junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onlab-osgi</artifactId>
<version>${project.version}</version>
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-api</artifactId>
<version>${project.version}</version>
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<configuration>
<filters>
<filter>
<artifact>com.btisystems:snmp-core</artifact>
<excludes>
<exclude>**</exclude>
</excludes>
</filter>
<filter>
<artifact>com.btisystems.mibbler.mibs:bti7000</artifact>
<excludes>
<exclude>**</exclude>
</excludes>
</filter>
<filter>
<artifact>com.btisystems.mibbler.mibs:net-snmp</artifact>
<excludes>
<exclude>**</exclude>
</excludes>
</filter>
</filters>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-scr-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.onosproject</groupId>
<artifactId>onos-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
......
/*
*
* 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.provider.snmp.alarm.impl;
import com.btisystems.mibbler.mibs.bti7000.bti7000_13_2_0.I_Device;
import com.btisystems.mibbler.mibs.bti7000.bti7000_13_2_0._OidRegistry;
import com.btisystems.mibbler.mibs.bti7000.bti7000_13_2_0.btisystems.btiproducts.bti7000.objects.conditions.ActAlarmTable;
import com.btisystems.mibbler.mibs.bti7000.interfaces.btisystems.btiproducts.bti7000.objects.conditions.IActAlarmTable;
import com.btisystems.pronx.ems.core.model.ClassRegistry;
import com.btisystems.pronx.ems.core.model.IClassRegistry;
import com.btisystems.pronx.ems.core.model.NetworkDevice;
import com.btisystems.pronx.ems.core.snmp.ISnmpSession;
import java.io.IOException;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashSet;
import java.util.Set;
import java.util.TimeZone;
import org.apache.commons.lang.StringUtils;
import org.onosproject.incubator.net.faultmanagement.alarm.Alarm;
import org.onosproject.incubator.net.faultmanagement.alarm.AlarmEntityId;
import org.onosproject.incubator.net.faultmanagement.alarm.DefaultAlarm;
import org.onosproject.net.DeviceId;
import org.slf4j.Logger;
import static org.slf4j.LoggerFactory.getLogger;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
/**
* BTI 7000 specific implementation to provide a list of current alarms.
*/
public class Bti7000SnmpAlarmProvider implements SnmpDeviceAlarmProvider {
private final Logger log = getLogger(getClass());
protected static final IClassRegistry CLASS_REGISTRY = new ClassRegistry(_OidRegistry.oidRegistry, I_Device.class);
static final int ALARM_SEVERITY_MINOR = 2;
static final int ALARM_SEVERITY_MAJOR = 3;
static final int ALARM_SEVERITY_CRITICAL = 4;
@Override
public Collection<Alarm> getAlarms(ISnmpSession session, DeviceId deviceID) {
log.info("Getting alarms for BTI 7000 device at {}", deviceID);
Set<Alarm> alarms = new HashSet<>();
NetworkDevice networkDevice = new NetworkDevice(CLASS_REGISTRY,
session.getAddress().getHostAddress());
try {
session.walkDevice(networkDevice, Arrays.asList(
new OID[]{CLASS_REGISTRY.getClassToOidMap().get(ActAlarmTable.class)}));
IActAlarmTable deviceAlarms = (IActAlarmTable) networkDevice.getRootObject()
.getEntity(CLASS_REGISTRY.getClassToOidMap().get(ActAlarmTable.class));
if ((deviceAlarms != null) && (deviceAlarms.getActAlarmEntry() != null)
&& (!deviceAlarms.getActAlarmEntry().isEmpty())) {
deviceAlarms.getActAlarmEntry().values().stream().forEach((alarm) -> {
DefaultAlarm.Builder alarmBuilder = new DefaultAlarm.Builder(
deviceID, alarm.getActAlarmDescription(),
mapAlarmSeverity(alarm.getActAlarmSeverity()),
getLocalDateAndTime(alarm.getActAlarmDateAndTime(), null, null).getTime())
.forSource(AlarmEntityId.alarmEntityId("other:" + alarm.getActAlarmInstanceIdx()));
alarms.add(alarmBuilder.build());
});
}
log.info("Conditions retrieved: {}", deviceAlarms);
} catch (IOException ex) {
log.error("Error reading alarms for device {}.", deviceID, ex);
}
return alarms;
}
private Alarm.SeverityLevel mapAlarmSeverity(int intAlarmSeverity) {
Alarm.SeverityLevel mappedSeverity;
switch (intAlarmSeverity) {
case ALARM_SEVERITY_MINOR:
mappedSeverity = Alarm.SeverityLevel.MINOR;
break;
case ALARM_SEVERITY_MAJOR:
mappedSeverity = Alarm.SeverityLevel.MAJOR;
break;
case ALARM_SEVERITY_CRITICAL:
mappedSeverity = Alarm.SeverityLevel.CRITICAL;
break;
default:
mappedSeverity = Alarm.SeverityLevel.MINOR;
log.warn("Unexpected alarm severity: {}", intAlarmSeverity);
}
return mappedSeverity;
}
/**
* Converts an SNMP string representation into a {@link Date} object,
* and applies time zone conversion to provide the time on the local machine, ie PSM server.
*
* @param actAlarmDateAndTime MIB-II DateAndTime formatted. May optionally contain
* a timezone offset in 3 extra bytes
* @param sysInfoTimeZone Must be supplied if actAlarmDateAndTime is just local time (with no timezone)
* @param swVersion Must be supplied if actAlarmDateAndTime is just local time (with no timezone)
* @return adjusted {@link Date} or a simple conversion if other fields are null.
*/
public static Date getLocalDateAndTime(String actAlarmDateAndTime, String sysInfoTimeZone,
String swVersion) {
if (StringUtils.isBlank(actAlarmDateAndTime)) {
return null;
}
GregorianCalendar decodedDateAndTimeCal = btiMakeCalendar(OctetString.fromHexString(actAlarmDateAndTime));
if ((sysInfoTimeZone == null) || (swVersion == null)) {
return decodedDateAndTimeCal.getTime();
}
TimeZone javaTimeZone = getTimeZone();
decodedDateAndTimeCal.setTimeZone(javaTimeZone);
GregorianCalendar localTime = new GregorianCalendar();
localTime.setTimeInMillis(decodedDateAndTimeCal.getTimeInMillis());
return localTime.getTime();
}
/**
* This method is similar to SNMP4J approach with some fixes for the 11-bytes version (ie the one with timezone
* offset).
*
* For original makeCalendar refer @see http://www.snmp4j.org/agent/doc/org/snmp4j/agent/mo/snmp/DateAndTime.html
*
* Creates a <code>GregorianCalendar</code> from a properly formatted SNMP4J DateAndTime <code>OctetString</code>.
*
* @param dateAndTimeValue an OctetString conforming to the DateAndTime TC.
* @return the corresponding <code>GregorianCalendar</code> instance.
*
*/
public static GregorianCalendar btiMakeCalendar(OctetString dateAndTimeValue) {
int year = (dateAndTimeValue.get(0) & 0xFF) * 256
+ (dateAndTimeValue.get(1) & 0xFF);
int month = (dateAndTimeValue.get(2) & 0xFF);
int date = (dateAndTimeValue.get(3) & 0xFF);
int hour = (dateAndTimeValue.get(4) & 0xFF);
int minute = (dateAndTimeValue.get(5) & 0xFF);
int second = (dateAndTimeValue.get(6) & 0xFF);
int deci = (dateAndTimeValue.get(7) & 0xFF);
GregorianCalendar gc =
new GregorianCalendar(year, month - 1, date, hour, minute, second);
gc.set(Calendar.MILLISECOND, deci * 100);
if (dateAndTimeValue.length() == 11) {
char directionOfOffset = (char) dateAndTimeValue.get(8);
int hoursOffset = directionOfOffset == '+'
? dateAndTimeValue.get(9) : -dateAndTimeValue.get(9);
org.joda.time.DateTimeZone offset =
org.joda.time.DateTimeZone.forOffsetHoursMinutes(hoursOffset, dateAndTimeValue.get(10));
org.joda.time.DateTime dt =
new org.joda.time.DateTime(year, month, date, hour, minute, second, offset);
return dt.toGregorianCalendar();
}
return gc;
}
private static TimeZone getTimeZone() {
return Calendar.getInstance().getTimeZone();
}
}
/*
*
* 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.provider.snmp.alarm.impl;
import com.btisystems.mibbler.mibs.netsnmp.netsnmp.I_Device;
import com.btisystems.mibbler.mibs.netsnmp.netsnmp._OidRegistry;
import com.btisystems.mibbler.mibs.netsnmp.netsnmp.mib_2.interfaces.IfTable;
import com.btisystems.pronx.ems.core.model.ClassRegistry;
import com.btisystems.pronx.ems.core.model.IClassRegistry;
import com.btisystems.pronx.ems.core.model.NetworkDevice;
import com.btisystems.pronx.ems.core.snmp.ISnmpSession;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.onosproject.incubator.net.faultmanagement.alarm.Alarm;
import org.onosproject.incubator.net.faultmanagement.alarm.AlarmEntityId;
import org.onosproject.incubator.net.faultmanagement.alarm.DefaultAlarm;
import org.onosproject.net.DeviceId;
import org.slf4j.Logger;
import static org.slf4j.LoggerFactory.getLogger;
import org.snmp4j.smi.OID;
/**
* Net SNMP specific implementation to provide a list of current alarms.
*/
public class NetSnmpAlarmProvider implements SnmpDeviceAlarmProvider {
private final Logger log = getLogger(getClass());
protected static final IClassRegistry CLASS_REGISTRY =
new ClassRegistry(_OidRegistry.oidRegistry, I_Device.class);
@Override
public Collection<Alarm> getAlarms(ISnmpSession session, DeviceId deviceId) {
Set<Alarm> alarms = new HashSet<>();
NetworkDevice networkDevice = new NetworkDevice(CLASS_REGISTRY,
session.getAddress().getHostAddress());
try {
session.walkDevice(networkDevice, Arrays.asList(new OID[]{
CLASS_REGISTRY.getClassToOidMap().get(IfTable.class)}));
IfTable interfaceTable = (IfTable) networkDevice.getRootObject()
.getEntity(CLASS_REGISTRY.getClassToOidMap().get(IfTable.class));
if (interfaceTable != null) {
interfaceTable.getEntries().values().stream().forEach((ifEntry) -> {
//TODO will raise alarm for each interface as a demo.
// if (ifEntry.getIfAdminStatus() == 1 && ifEntry.getIfOperStatus() == 2){
alarms.add(new DefaultAlarm.Builder(deviceId, "Link Down.",
Alarm.SeverityLevel.CRITICAL, System.currentTimeMillis())
.forSource(AlarmEntityId.alarmEntityId("port:" + ifEntry.getIfDescr())).build());
// }
log.info("Interface: " + ifEntry);
});
}
} catch (IOException ex) {
log.error("Error reading alarms for device {}.", deviceId, ex);
}
return alarms;
}
}
/*
* Copyright 2014-2015 Open Networking Laboratory
*
* 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.provider.snmp.alarm.impl;
import org.apache.felix.scr.annotations.Component;
import org.onosproject.net.DeviceId;
import org.onosproject.incubator.net.faultmanagement.alarm.AlarmProvider;
import org.onosproject.net.provider.AbstractProvider;
import org.onosproject.net.provider.ProviderId;
import org.slf4j.Logger;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Provider which uses an SNMP controller to detect network device alarms. The class leverages functionality from
*
* @see <a href="https://github.com/btisystems/snmp-core">https://github.com/btisystems/snmp-core</a>
* @see <a href="https://github.com/btisystems/mibbler">https://github.com/btisystems/mibbler</a>
*/
@Component(immediate = true)
public class SNMPAlarmProvider extends AbstractProvider implements AlarmProvider {
private static final Logger LOG = getLogger(SNMPAlarmProvider.class);
/**
* Creates a SNMP alarm provider, dummy class provided as template, tbd later.
*/
public SNMPAlarmProvider() {
super(new ProviderId("snmp", "org.onosproject.provider.alarm"));
}
@Override
public void triggerProbe(DeviceId deviceId) {
// TODO in shout term should this just be synchronous and return result?
LOG.info("Run a SNMP discovery for device at {} when done invoke on AlarmProviderService", deviceId);
// TODO Look up AlarmProviderService
// TODO Decide threading
// TODO Decide shouldn't it be generic not alarm-specific ? Its user responsible for passing in OID list ?
// Same for its callack AlarmProviderService ?
}
}
/*
* Copyright 2015 Open Networking Laboratory
*
* 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.provider.snmp.alarm.impl;
import com.btisystems.pronx.ems.core.snmp.DefaultSnmpConfigurationFactory;
import com.btisystems.pronx.ems.core.snmp.ISnmpConfiguration;
import com.btisystems.pronx.ems.core.snmp.ISnmpSession;
import com.btisystems.pronx.ems.core.snmp.ISnmpSessionFactory;
import com.btisystems.pronx.ems.core.snmp.SnmpSessionFactory;
import com.btisystems.pronx.ems.core.snmp.V2cSnmpConfiguration;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.collect.Sets;
import java.io.IOException;
import static org.slf4j.LoggerFactory.getLogger;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Modified;
import org.onosproject.incubator.net.faultmanagement.alarm.Alarm;
import org.onosproject.incubator.net.faultmanagement.alarm.AlarmEvent;
import org.onosproject.incubator.net.faultmanagement.alarm.AlarmListener;
import org.onosproject.incubator.net.faultmanagement.alarm.AlarmProvider;
import org.onosproject.net.DeviceId;
import org.onosproject.net.provider.AbstractProvider;
import org.onosproject.net.provider.ProviderId;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import static org.onlab.util.Tools.groupedThreads;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.incubator.net.faultmanagement.alarm.DefaultAlarm;
import org.onosproject.net.device.DeviceEvent;
import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_ADDED;
import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED;
import org.onosproject.net.device.DeviceListener;
import org.onosproject.net.device.DeviceService;
/**
* SNMP alarms provider.
*/
@Component(immediate = true)
public class SnmpAlarmProviderService extends AbstractProvider implements AlarmProvider {
private final Logger log = getLogger(getClass());
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CoreService coreService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DeviceService deviceService;
private ApplicationId appId;
private final ISnmpSessionFactory sessionFactory;
// TODO convert to standard ONOS listener service approach ?
protected Set<AlarmListener> alarmEventListener = Sets.newHashSet();
private ExecutorService eventHandlingExecutor;
// TODO Could be replaced with a service lookup, and bundles per device variant.
Map<String, SnmpDeviceAlarmProvider> providers = new HashMap<>();
public SnmpAlarmProviderService() {
super(new ProviderId("snmp", "org.onosproject.provider.alarm"));
sessionFactory = new SnmpSessionFactory(
new DefaultSnmpConfigurationFactory(new V2cSnmpConfiguration()));
providers.put("1.3.6.1.4.1.18070.2.2", new Bti7000SnmpAlarmProvider());
providers.put("1.3.6.1.4.1.20408", new NetSnmpAlarmProvider());
}
@Activate
public void activate(ComponentContext context) {
appId = coreService.registerApplication("org.onosproject.snmp");
eventHandlingExecutor = Executors.newSingleThreadExecutor(
groupedThreads("onos/alarms", "event-handler"));
deviceService.addListener(new InternalDeviceListener());
log.info("activated SNMP provider with appId = {} and context props {}", appId, context.getProperties());
modified(context);
log.info("Started");
}
@Deactivate
public void deactivate() {
log.info("deactivate SNMP provider {}", appId);
}
@Modified
public void modified(ComponentContext context) {
log.info("modified {}", context);
if (context == null) {
log.info("No configuration file");
}
}
@Override
public void triggerProbe(DeviceId deviceId) {
log.info("SNMP walk request for alarms at deviceId={}", deviceId);
if (!isSnmpDevice(deviceId)) {
log.info("Ignore non-snmp device!");
return;
}
String[] deviceComponents = deviceId.toString().split(":");
Set<Alarm> alarms = new HashSet<>(Sets.newHashSet());
if (deviceComponents.length > 1) {
String ipAddress = deviceComponents[1];
String port = deviceComponents[2];
ISnmpConfiguration config = new V2cSnmpConfiguration();
config.setPort(Integer.parseInt(port));
try (ISnmpSession session = getSessionFactory().createSession(config, ipAddress)) {
// Each session will be auto-closed.
String deviceOID = session.identifyDevice();
alarms.addAll(getAlarmsForDevice(deviceOID, session, deviceId));
log.info("SNMP walk completed ok for deviceId={}", deviceId);
} catch (IOException | RuntimeException ex) {
log.error("Failed to walk device.", ex.getMessage());
log.debug("Detailed problem was ", ex);
alarms.add(
buildWalkFailedAlarm(deviceId)
);
}
}
AlarmEvent alarmEvent = new AlarmEvent(alarms, deviceId);
alarmEventListener.stream().forEach((listener) -> {
listener.event(alarmEvent);
log.info("Successfully event with discovered alarms for deviceId={} to {}", deviceId, listener);
});
}
private static DefaultAlarm buildWalkFailedAlarm(DeviceId deviceId) {
return new DefaultAlarm.Builder(
deviceId, "SNMP alarm retrieval failed",
Alarm.SeverityLevel.CRITICAL,
System.currentTimeMillis()).build();
}
protected ISnmpSessionFactory getSessionFactory() {
return sessionFactory;
}
private Collection<Alarm> getAlarmsForDevice(String deviceOID, ISnmpSession session,
DeviceId deviceID) throws IOException {
Collection<Alarm> alarms = new HashSet<>();
if (providers.containsKey(deviceOID)) {
alarms.addAll(providers.get(deviceOID).getAlarms(session, deviceID));
}
return alarms;
}
@Override
public void addAlarmListener(AlarmListener listener) {
alarmEventListener.add(checkNotNull(listener, "Listener cannot be null"));
}
@Override
public void removeAlarmListener(AlarmListener listener) {
alarmEventListener.remove(checkNotNull(listener, "Listener cannot be null"));
}
/**
* Internal listener for device service events.
*/
private class InternalDeviceListener implements DeviceListener {
@Override
public void event(DeviceEvent event) {
log.info("InternalDeviceListener has got event from device-service{} with ", event);
eventHandlingExecutor.execute(() -> {
try {
DeviceId deviceId = event.subject().id();
log.info("From device {}", deviceId);
if (!isSnmpDevice(deviceId)) {
log.info("Ignore non-snmp device event for {}", deviceId);
return;
}
switch (event.type()) {
case DEVICE_ADDED:
case DEVICE_UPDATED:
case DEVICE_AVAILABILITY_CHANGED:
if (deviceService.isAvailable(event.subject().id())) {
triggerProbe(deviceId);
}
break;
case DEVICE_REMOVED:
case DEVICE_SUSPENDED:
default:
// Could potentially remove all alarms when eg DEVICE_REMOVED or DEVICE_SUSPENDED
// however for now ignore and fall through
break;
}
} catch (Exception e) {
log.warn("Failed to process {}", event, e);
}
});
}
}
private static boolean isSnmpDevice(DeviceId deviceId) {
return deviceId.uri().getScheme().equalsIgnoreCase("snmp");
}
}
/*
* 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.provider.snmp.alarm.impl;
import com.btisystems.pronx.ems.core.snmp.ISnmpSession;
import java.util.Collection;
import org.onosproject.incubator.net.faultmanagement.alarm.Alarm;
import org.onosproject.net.DeviceId;
public interface SnmpDeviceAlarmProvider {
/**
* Implemented by device specific implementations which query the current
* alarms from a device.
* @param snmpSession SNMP Session
* @param deviceId device identifier
* @return device alarms
*/
Collection<Alarm> getAlarms(ISnmpSession snmpSession, DeviceId deviceId);
}
/*
<<<<<<< HEAD
* Copyright 2015 Open Networking Laboratory
=======
* Copyright 2014 Open Networking Laboratory
>>>>>>> master
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -15,6 +19,6 @@
*/
/**
* Provider that uses SNMP as a means of discovering alarms on devices.
* Provider that will support SNMP alarm discoveries.
*/
package org.onosproject.provider.snmp.alarm.impl;
......
/*
* 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.provider.snmp.alarm.impl;
import com.btisystems.mibbler.mibs.bti7000.bti7000_13_2_0.btisystems.btiproducts.bti7000.objects.conditions.ActAlarmTable;
import com.btisystems.mibbler.mibs.bti7000.interfaces.btisystems.btiproducts.bti7000.objects.conditions.actalarmtable.IActAlarmEntry;
import com.btisystems.pronx.ems.core.model.NetworkDevice;
import com.btisystems.pronx.ems.core.snmp.ISnmpSession;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Collection;
import org.easymock.Capture;
import static org.easymock.EasyMock.capture;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.eq;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.isA;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;
import org.onosproject.incubator.net.faultmanagement.alarm.Alarm;
import org.onosproject.net.DeviceId;
import org.snmp4j.smi.OID;
public class Bti7000SnmpAlarmProviderTest {
private Bti7000SnmpAlarmProvider alarmProvider;
private ISnmpSession mockSession;
private ActAlarmTable alarmsTable;
public Bti7000SnmpAlarmProviderTest() {
}
@Before
public void setUp() {
mockSession = createMock(ISnmpSession.class);
alarmProvider = new Bti7000SnmpAlarmProvider();
}
@Test
public void shouldWalkDevice() throws UnknownHostException, IOException {
expect(mockSession.getAddress()).andReturn(InetAddress.getLoopbackAddress());
expect(mockSession.walkDevice(isA(NetworkDevice.class),
eq(Arrays.asList(new OID[]{
Bti7000SnmpAlarmProvider.CLASS_REGISTRY.getClassToOidMap().get(ActAlarmTable.class)}))))
.andReturn(null);
replay(mockSession);
assertNotNull(alarmProvider.getAlarms(mockSession, DeviceId.deviceId("snmp:1.1.1.1")));
verify(mockSession);
}
@Test
public void shouldFindAlarms() throws UnknownHostException, IOException {
alarmsTable = new ActAlarmTable();
alarmsTable.createEntry("14.1.3.6.1.4.1.18070.2.2.2.2.20.0.1.13.1.3.6.1.4.1."
+ "18070.2.2.1.4.14.1.7.49.46.55.46.50.46.53");
IActAlarmEntry entry = alarmsTable.getEntries().values().iterator().next();
entry.setActAlarmDescription("XFP Missing.");
entry.setActAlarmDateAndTime("07:df:0c:01:03:0d:30:00");
entry.setActAlarmSeverity(1);
Capture<NetworkDevice> networkDeviceCapture = new Capture<>();
expect(mockSession.getAddress()).andReturn(InetAddress.getLoopbackAddress());
expect(mockSession.walkDevice(capture(networkDeviceCapture),
eq(Arrays.asList(new OID[]{
Bti7000SnmpAlarmProvider.CLASS_REGISTRY.getClassToOidMap().get(ActAlarmTable.class)}))))
.andAnswer(() -> {
networkDeviceCapture.getValue().getRootObject().setObject(alarmsTable);
return null;
});
replay(mockSession);
Collection<Alarm> alarms = alarmProvider.getAlarms(mockSession, DeviceId.deviceId("snmp:1.1.1.1"));
assertEquals(1, alarms.size());
assertEquals("XFP Missing.", alarms.iterator().next().description());
verify(mockSession);
}
@Test
public void shouldHandleException() throws UnknownHostException, IOException {
expect(mockSession.getAddress()).andReturn(InetAddress.getLoopbackAddress());
expect(mockSession.walkDevice(isA(NetworkDevice.class),
eq(Arrays.asList(new OID[]{
Bti7000SnmpAlarmProvider.CLASS_REGISTRY.getClassToOidMap().get(ActAlarmTable.class)}))))
.andThrow(new IOException());
replay(mockSession);
assertNotNull(alarmProvider.getAlarms(mockSession, DeviceId.deviceId("snmp:1.1.1.1")));
verify(mockSession);
}
}
/*
* 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.provider.snmp.alarm.impl;
import com.btisystems.mibbler.mibs.netsnmp.interfaces.mib_2.interfaces.iftable.IIfEntry;
import com.btisystems.mibbler.mibs.netsnmp.netsnmp.mib_2.interfaces.IfTable;
import com.btisystems.pronx.ems.core.model.NetworkDevice;
import com.btisystems.pronx.ems.core.snmp.ISnmpSession;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Collection;
import org.easymock.Capture;
import static org.easymock.EasyMock.capture;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.eq;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.isA;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;
import org.onosproject.incubator.net.faultmanagement.alarm.Alarm;
import org.onosproject.net.DeviceId;
import org.snmp4j.smi.OID;
public class NetSnmpSnmpAlarmProviderTest {
private NetSnmpAlarmProvider alarmProvider;
private ISnmpSession mockSession;
private IfTable interfaceTable;
public NetSnmpSnmpAlarmProviderTest() {
}
@Before
public void setUp() {
mockSession = createMock(ISnmpSession.class);
alarmProvider = new NetSnmpAlarmProvider();
}
@Test
public void shouldWalkDevice() throws UnknownHostException, IOException {
expect(mockSession.getAddress()).andReturn(InetAddress.getLoopbackAddress());
expect(mockSession.walkDevice(isA(NetworkDevice.class),
eq(Arrays.asList(new OID[]{
NetSnmpAlarmProvider.CLASS_REGISTRY.getClassToOidMap().get(IfTable.class)}))))
.andReturn(null);
replay(mockSession);
assertNotNull(alarmProvider.getAlarms(mockSession, DeviceId.deviceId("snmp:1.1.1.1")));
verify(mockSession);
}
@Test
public void shouldFindAlarms() throws UnknownHostException, IOException {
interfaceTable = new IfTable();
interfaceTable.createEntry("1");
IIfEntry entry = interfaceTable.getEntry("1");
entry.setIfDescr("eth1");
entry.setIfAdminStatus(1);
entry.setIfOperStatus(2);
Capture<NetworkDevice> networkDeviceCapture = new Capture<>();
expect(mockSession.getAddress()).andReturn(InetAddress.getLoopbackAddress());
expect(mockSession.walkDevice(capture(networkDeviceCapture),
eq(Arrays.asList(new OID[]{
NetSnmpAlarmProvider.CLASS_REGISTRY.getClassToOidMap().get(IfTable.class)}))))
.andAnswer(() -> {
networkDeviceCapture.getValue().getRootObject().setObject(interfaceTable);
return null;
});
replay(mockSession);
Collection<Alarm> alarms = alarmProvider.getAlarms(mockSession, DeviceId.deviceId("snmp:1.1.1.1"));
assertEquals(1, alarms.size());
assertEquals("Link Down.", alarms.iterator().next().description());
verify(mockSession);
}
@Test
public void shouldHandleException() throws UnknownHostException, IOException {
expect(mockSession.getAddress()).andReturn(InetAddress.getLoopbackAddress());
expect(mockSession.walkDevice(isA(NetworkDevice.class),
eq(Arrays.asList(new OID[]{
NetSnmpAlarmProvider.CLASS_REGISTRY.getClassToOidMap().get(IfTable.class)}))))
.andThrow(new IOException());
replay(mockSession);
assertNotNull(alarmProvider.getAlarms(mockSession, DeviceId.deviceId("snmp:1.1.1.1")));
verify(mockSession);
}
}
/*
* 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.provider.snmp.alarm.impl;
import com.btisystems.pronx.ems.core.snmp.ISnmpConfiguration;
import com.btisystems.pronx.ems.core.snmp.ISnmpSession;
import com.btisystems.pronx.ems.core.snmp.ISnmpSessionFactory;
import java.io.IOException;
import java.util.HashSet;
import org.easymock.EasyMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.onosproject.incubator.net.faultmanagement.alarm.AlarmEvent;
import org.onosproject.net.DeviceId;
public class SnmpDeviceAlarmProviderTest {
private SnmpAlarmProviderService alarmProvider;
private ISnmpSessionFactory mockSessionFactory;
private ISnmpSession mockSession;
private SnmpDeviceAlarmProvider mockProvider;
private AlarmEvent alarmEvent;
public SnmpDeviceAlarmProviderTest() {}
@Before
public void setUp() {
mockSessionFactory = EasyMock.createMock(ISnmpSessionFactory.class);
mockSession = EasyMock.createMock(ISnmpSession.class);
mockProvider = EasyMock.createMock(SnmpDeviceAlarmProvider.class);
alarmProvider = new SnmpAlarmProviderService() {
@Override
protected ISnmpSessionFactory getSessionFactory() {
return mockSessionFactory;
}
};
alarmProvider.addAlarmListener((AlarmEvent event) -> {
alarmEvent = event;
});
}
@Test
public void shouldPopulateAlarmsForNetSnmp() throws IOException {
alarmProvider.providers.put("1.2.3.4", mockProvider);
expect(mockSessionFactory.createSession(EasyMock.isA(ISnmpConfiguration.class),
EasyMock.eq("1.1.1.1"))).andReturn(mockSession);
expect(mockSession.identifyDevice()).andReturn("1.2.3.4");
expect(mockProvider.getAlarms(mockSession, DeviceId.deviceId("snmp:1.1.1.1:161")))
.andReturn(new HashSet<>());
mockSession.close();
EasyMock.expectLastCall().once();
replay(mockSessionFactory, mockSession, mockProvider);
alarmProvider.triggerProbe(DeviceId.deviceId("snmp:1.1.1.1:161"));
verify(mockSessionFactory, mockSession, mockProvider);
Assert.assertNotNull(alarmEvent);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2015 Open Networking Laboratory
~
~ 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.
-->
<app name="org.onosproject.snmp" origin="BTI Systems" version="${project.version}"
featuresRepo="mvn:${project.groupId}/${project.artifactId}/${project.version}/xml/features"
features="${project.artifactId}">
<description>${project.description}</description>
<artifact>mvn:${project.groupId}/onos-snmp-provider-device/${project.version}</artifact>
<artifact>mvn:${project.groupId}/onos-snmp-provider-alarm/${project.version}</artifact>
</app>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!--
~ Copyright 2015 Open Networking Laboratory
~
~ 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.
-->
<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0" name="${project.artifactId}-${project.version}">
<feature name="${project.artifactId}" version="${project.version}"
description="${project.description}">
<feature>onos-api</feature>
<bundle>mvn:io.netty/netty/3.9.2.Final</bundle>
<bundle>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.snmp4j/2.3.4_1</bundle>
<bundle>mvn:${project.groupId}/onos-snmp-provider-device/${project.version}</bundle>
<bundle>mvn:com.btisystems/snmp-core/1.3-SNAPSHOT</bundle>
<bundle>mvn:com.btisystems.mibbler.mibs/bti7000/1.0-SNAPSHOT</bundle>
<bundle>mvn:com.btisystems.mibbler.mibs/net-snmp/1.0-SNAPSHOT</bundle>
</feature>
</features>
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2015 Open Networking Laboratory
~
~ 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.onosproject</groupId>
<artifactId>onos-snmp-providers</artifactId>
<version>1.4.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>onos-snmp-app</artifactId>
<packaging>pom</packaging>
<description>SNMP protocol southbound providers</description>
<dependencies>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-snmp-provider-device</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-snmp-provider-alarm</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2015 Open Networking Laboratory
~
~ 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.onosproject</groupId>
<artifactId>onos-snmp-providers</artifactId>
<version>1.4.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>onos-snmp-provider-device</artifactId>
<packaging>bundle</packaging>
<description>ONOS SNMP protocol device provider</description>
<dependencies>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.compendium</artifactId>
</dependency>
<!-- <dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>jsr311-api</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-incubator-api</artifactId>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-core-serializers</artifactId>
<version>${project.version}</version>
</dependency>-->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-scr-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
\ No newline at end of file
/*
* 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.provider.snmp.device.impl;
import com.btisystems.mibbler.mibs.bti7000.bti7000_13_2_0.I_Device;
import com.btisystems.mibbler.mibs.bti7000.bti7000_13_2_0._OidRegistry;
import com.btisystems.pronx.ems.core.model.ClassRegistry;
import com.btisystems.pronx.ems.core.model.IClassRegistry;
import com.btisystems.pronx.ems.core.model.NetworkDevice;
import com.btisystems.pronx.ems.core.snmp.ISnmpSession;
import java.io.IOException;
import java.util.Arrays;
import org.onosproject.net.device.DefaultDeviceDescription;
import org.onosproject.net.device.DeviceDescription;
import org.slf4j.Logger;
import static org.slf4j.LoggerFactory.getLogger;
import org.snmp4j.smi.OID;
/**
* A vendor-specific implementation supporting BTI Systems BTI-7000 equipment.
*/
public class Bti7000DeviceDescriptionProvider implements SnmpDeviceDescriptionProvider {
private final Logger log = getLogger(getClass());
protected static final IClassRegistry CLASS_REGISTRY =
new ClassRegistry(_OidRegistry.oidRegistry, I_Device.class);
private static final String UNKNOWN = "unknown";
@Override
public DeviceDescription populateDescription(ISnmpSession session, DeviceDescription description) {
NetworkDevice networkDevice = new NetworkDevice(CLASS_REGISTRY,
session.getAddress().getHostAddress());
try {
session.walkDevice(networkDevice, Arrays.asList(new OID[]{
CLASS_REGISTRY.getClassToOidMap().get(
com.btisystems.mibbler.mibs.bti7000.bti7000_13_2_0.mib_2.System.class)}));
com.btisystems.mibbler.mibs.bti7000.bti7000_13_2_0.mib_2.System systemTree =
(com.btisystems.mibbler.mibs.bti7000.bti7000_13_2_0.mib_2.System)
networkDevice.getRootObject().getEntity(CLASS_REGISTRY.getClassToOidMap().get(
com.btisystems.mibbler.mibs.bti7000.bti7000_13_2_0.mib_2.System.class));
if (systemTree != null) {
String[] systemComponents = systemTree.getSysDescr().split(";");
return new DefaultDeviceDescription(description.deviceUri(), description.type(),
systemComponents[0], systemComponents[2], systemComponents[3],
UNKNOWN, description.chassisId());
}
} catch (IOException ex) {
log.error("Error reading details for device {}.", session.getAddress(), ex);
}
return description;
}
}
/*
* Copyright 2015 Open Networking Laboratory
*
* 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.provider.snmp.device.impl;
/**
* The Device State is used to determine whether the device is active or inactive. This state information will help
* Device Creator to add or delete the device from the core.
*/
public enum DeviceState {
/* Used to specify Active state of the device */
ACTIVE,
/* Used to specify inactive state of the device */
INACTIVE,
/* Used to specify invalid state of the device */
INVALID
}
/*
* 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.provider.snmp.device.impl;
import com.btisystems.mibbler.mibs.netsnmp.netsnmp.I_Device;
import com.btisystems.mibbler.mibs.netsnmp.netsnmp._OidRegistry;
import com.btisystems.pronx.ems.core.model.ClassRegistry;
import com.btisystems.pronx.ems.core.model.IClassRegistry;
import com.btisystems.pronx.ems.core.model.NetworkDevice;
import com.btisystems.pronx.ems.core.snmp.ISnmpSession;
import java.io.IOException;
import java.util.Arrays;
import org.apache.commons.lang.StringUtils;
import org.onosproject.net.device.DefaultDeviceDescription;
import org.onosproject.net.device.DeviceDescription;
import org.slf4j.Logger;
import static org.slf4j.LoggerFactory.getLogger;
import org.snmp4j.smi.OID;
/**
* A agent-specific implementation supporting NET-SNMP agents.
*/
public class NetSnmpDeviceDescriptionProvider implements SnmpDeviceDescriptionProvider {
private final Logger log = getLogger(getClass());
protected static final IClassRegistry CLASS_REGISTRY =
new ClassRegistry(_OidRegistry.oidRegistry, I_Device.class);
private static final String UNKNOWN = "unknown";
@Override
public DeviceDescription populateDescription(ISnmpSession session, DeviceDescription description) {
NetworkDevice networkDevice = new NetworkDevice(CLASS_REGISTRY,
session.getAddress().getHostAddress());
try {
session.walkDevice(networkDevice, Arrays.asList(new OID[]{
CLASS_REGISTRY.getClassToOidMap().get(
com.btisystems.mibbler.mibs.netsnmp.netsnmp.mib_2.System.class)}));
com.btisystems.mibbler.mibs.netsnmp.netsnmp.mib_2.System systemTree =
(com.btisystems.mibbler.mibs.netsnmp.netsnmp.mib_2.System)
networkDevice.getRootObject().getEntity(CLASS_REGISTRY.getClassToOidMap().get(
com.btisystems.mibbler.mibs.netsnmp.netsnmp.mib_2.System.class));
if (systemTree != null) {
// TODO SNMP sys-contacts may be verbose; ONOS-GUI doesn't abbreviate fields neatly;
// so cut it here until supported in prop displayer
String manufacturer = StringUtils.abbreviate(systemTree.getSysContact(), 20);
return new DefaultDeviceDescription(description.deviceUri(), description.type(), manufacturer,
UNKNOWN, UNKNOWN, UNKNOWN, description.chassisId());
}
} catch (IOException ex) {
log.error("Error reading details for device {}.", session.getAddress(), ex);
}
return description;
}
}
/*
* Copyright 2015 Open Networking Laboratory
*
* 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.provider.snmp.device.impl;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.slf4j.LoggerFactory.getLogger;
import org.slf4j.Logger;
/**
* This is a logical representation of actual SNMP device, carrying all the necessary information to connect and execute
* SNMP operations.
*/
public class SnmpDevice {
private final Logger log = getLogger(SnmpDevice.class);
private static final int DEFAULT_SNMP_PORT = 161;
private final String snmpHost;
private int snmpPort = DEFAULT_SNMP_PORT;
private final String community;
private boolean reachable = false;
private DeviceState deviceState = DeviceState.INVALID;
protected SnmpDevice(String snmpHost, int snmpPort, String community) {
this.snmpHost = checkNotNull(snmpHost, "SNMP Device IP cannot be null");
this.snmpPort = checkNotNull(snmpPort, "SNMP Device snmp port cannot be null");
this.community = community;
}
/**
* This will try to connect to SNMP device.
*
*/
public void init() {
reachable = true;
}
/**
* This would return host IP and host Port, used by this particular SNMP Device.
*
* @return Device Information.
*/
public String deviceInfo() {
return new StringBuilder("host: ").append(snmpHost).append(". port: ")
.append(snmpPort).toString();
}
/**
* This will terminate the device connection.
*/
public void disconnect() {
log.info("disconnect");
reachable = false;
}
/**
* This api is intended to know whether the device is connected or not.
*
* @return true if connected
*/
public boolean isReachable() {
return reachable;
}
/**
* This will return the IP used connect ssh on the device.
*
* @return SNMP Device IP
*/
public String getSnmpHost() {
return snmpHost;
}
/**
* This will return the SSH Port used connect the device.
*
* @return SSH Port number
*/
public int getSnmpPort() {
return snmpPort;
}
public String getCommunity() {
return community;
}
/**
* Retrieve current state of the device.
*
* @return Current Device State
*/
public DeviceState getDeviceState() {
return deviceState;
}
/**
* This is set the state information for the device.
*
* @param deviceState Next Device State
*/
public void setDeviceState(DeviceState deviceState) {
this.deviceState = deviceState;
}
/**
* Check whether the device is in Active state.
*
* @return true if the device is Active
*/
public boolean isActive() {
return deviceState == DeviceState.ACTIVE;
}
}
/*
* 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.provider.snmp.device.impl;
import com.btisystems.pronx.ems.core.snmp.ISnmpSession;
import org.onosproject.net.device.DeviceDescription;
/**
* Abstraction of an entity which updates a device description with information retrieved via SNMP.
*/
public interface SnmpDeviceDescriptionProvider {
/**
* Generated an updated device description.
*
* @param session SNMP session
* @param description old device description
* @return new updated description
*/
DeviceDescription populateDescription(ISnmpSession session, DeviceDescription description);
}
/*
* Copyright 2015 Open Networking Laboratory
*
* 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.provider.snmp.device.impl;
import com.btisystems.pronx.ems.core.snmp.DefaultSnmpConfigurationFactory;
import com.btisystems.pronx.ems.core.snmp.ISnmpConfiguration;
import com.btisystems.pronx.ems.core.snmp.ISnmpConfigurationFactory;
import com.btisystems.pronx.ems.core.snmp.ISnmpSession;
import com.btisystems.pronx.ems.core.snmp.ISnmpSessionFactory;
import com.btisystems.pronx.ems.core.snmp.SnmpSessionFactory;
import com.btisystems.pronx.ems.core.snmp.V2cSnmpConfiguration;
import static com.google.common.base.Strings.isNullOrEmpty;
import java.io.IOException;
import static org.onlab.util.Tools.delay;
import static org.onlab.util.Tools.get;
import static org.onlab.util.Tools.groupedThreads;
import static org.slf4j.LoggerFactory.getLogger;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Modified;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onlab.packet.ChassisId;
import org.onosproject.cfg.ComponentConfigService;
import org.onosproject.cluster.ClusterService;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.MastershipRole;
import org.onosproject.net.device.DefaultDeviceDescription;
import org.onosproject.net.device.DeviceDescription;
import org.onosproject.net.device.DeviceProvider;
import org.onosproject.net.device.DeviceProviderRegistry;
import org.onosproject.net.device.DeviceProviderService;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.provider.AbstractProvider;
import org.onosproject.net.provider.ProviderId;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
/**
* Provider which will try to fetch the details of SNMP devices from the core and run a capability discovery on each of
* the device.
*/
@Component(immediate = true)
public class SnmpDeviceProvider extends AbstractProvider
implements DeviceProvider {
private final Logger log = getLogger(SnmpDeviceProvider.class);
private static final String UNKNOWN = "unknown";
protected Map<DeviceId, SnmpDevice> snmpDeviceMap = new ConcurrentHashMap<>();
private DeviceProviderService providerService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DeviceProviderRegistry providerRegistry;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DeviceService deviceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected ClusterService clusterService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected ComponentConfigService cfgService;
private final ExecutorService deviceBuilder = Executors
.newFixedThreadPool(1, groupedThreads("onos/snmp", "device-creator"));
// Delay between events in ms.
private static final int EVENTINTERVAL = 5;
private static final String SCHEME = "snmp";
@Property(name = "devConfigs", value = "", label = "Instance-specific configurations")
private String devConfigs = null;
@Property(name = "devPasswords", value = "", label = "Instance-specific password")
private String devPasswords = null;
//TODO Could be replaced with a service lookup, and bundles per device variant.
Map<String, SnmpDeviceDescriptionProvider> providers = new HashMap<>();
private final ISnmpSessionFactory sessionFactory;
/**
* Creates a provider with the supplier identifier.
*/
public SnmpDeviceProvider() {
super(new ProviderId("snmp", "org.onosproject.provider.device"));
sessionFactory = new SnmpSessionFactory(
new DefaultSnmpConfigurationFactory(new V2cSnmpConfiguration()));
providers.put("1.3.6.1.4.1.18070.2.2", new Bti7000DeviceDescriptionProvider());
providers.put("1.3.6.1.4.1.20408", new NetSnmpDeviceDescriptionProvider());
}
@Activate
public void activate(ComponentContext context) {
log.info("activating for snmp devices ...");
cfgService.registerProperties(getClass());
providerService = providerRegistry.register(this);
modified(context);
log.info("activated ok");
}
@Deactivate
public void deactivate(ComponentContext context) {
log.info("deactivating for snmp devices ...");
cfgService.unregisterProperties(getClass(), false);
try {
snmpDeviceMap
.entrySet().stream().forEach((deviceEntry) -> {
deviceBuilder.submit(new DeviceCreator(deviceEntry.getValue(), false));
});
deviceBuilder.awaitTermination(1000, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
log.error("Device builder did not terminate");
}
deviceBuilder.shutdownNow();
snmpDeviceMap.clear();
providerRegistry.unregister(this);
providerService = null;
log.info("Stopped");
}
@Modified
public void modified(ComponentContext context) {
log.info("modified ...");
if (context == null) {
log.info("No configuration file");
return;
}
Dictionary<?, ?> properties = context.getProperties();
log.info("properties={}", context.getProperties());
String deviceCfgValue = get(properties, "devConfigs");
log.info("Settings: devConfigs={}", deviceCfgValue);
if (!isNullOrEmpty(deviceCfgValue)) {
addOrRemoveDevicesConfig(deviceCfgValue);
}
log.info("... modified");
}
private void addOrRemoveDevicesConfig(String deviceConfig) {
for (String deviceEntry : deviceConfig.split(",")) {
SnmpDevice device = processDeviceEntry(deviceEntry);
if (device != null) {
log.info("Device Detail:host={}, port={}, state={}",
new Object[]{device.getSnmpHost(),
device.getSnmpPort(),
device.getDeviceState().name()}
);
if (device.isActive()) {
deviceBuilder.submit(new DeviceCreator(device, true));
} else {
deviceBuilder.submit(new DeviceCreator(device, false));
}
}
}
}
private SnmpDevice processDeviceEntry(String deviceEntry) {
if (deviceEntry == null) {
log.info("No content for Device Entry, so cannot proceed further.");
return null;
}
log.info("Trying to convert {} to a SNMP Device Object", deviceEntry);
SnmpDevice device = null;
try {
String userInfo = deviceEntry.substring(0, deviceEntry
.lastIndexOf('@'));
String hostInfo = deviceEntry.substring(deviceEntry
.lastIndexOf('@') + 1);
String[] infoSplit = userInfo.split(":");
String username = infoSplit[0];
String password = infoSplit[1];
infoSplit = hostInfo.split(":");
String hostIp = infoSplit[0];
Integer hostPort;
try {
hostPort = Integer.parseInt(infoSplit[1]);
} catch (NumberFormatException nfe) {
log.error("Bad Configuration Data: Failed to parse host port number string: "
+ infoSplit[1]);
throw nfe;
}
String deviceState = infoSplit[2];
if (isNullOrEmpty(username) || isNullOrEmpty(password)
|| isNullOrEmpty(hostIp) || hostPort == 0) {
log.warn("Bad Configuration Data: both user and device information parts of Configuration "
+ deviceEntry + " should be non-nullable");
} else {
device = new SnmpDevice(hostIp, hostPort, password);
if (!isNullOrEmpty(deviceState)) {
if (deviceState.toUpperCase().equals(DeviceState.ACTIVE.name())) {
device.setDeviceState(DeviceState.ACTIVE);
} else if (deviceState.toUpperCase()
.equals(DeviceState.INACTIVE.name())) {
device.setDeviceState(DeviceState.INACTIVE);
} else {
log.warn("Device State Information can not be empty, so marking the state as INVALID");
device.setDeviceState(DeviceState.INVALID);
}
} else {
log.warn("The device entry do not specify state information, so marking the state as INVALID");
device.setDeviceState(DeviceState.INVALID);
}
}
} catch (ArrayIndexOutOfBoundsException aie) {
log.error("Error while reading config infromation from the config file: "
+ "The user, host and device state infomation should be "
+ "in the order 'userInfo@hostInfo:deviceState'"
+ deviceEntry, aie);
} catch (Exception e) {
log.error("Error while parsing config information for the device entry: "
+ deviceEntry, e);
}
return device;
}
@Override
public void triggerProbe(DeviceId deviceId) {
// TODO SNMP devices should be polled at scheduled intervals to retrieve their
// reachability status and other details e.g.swVersion, serialNumber,chassis,
}
@Override
public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
}
@Override
public boolean isReachable(DeviceId deviceId) {
SnmpDevice snmpDevice = snmpDeviceMap.get(deviceId);
if (snmpDevice == null) {
log.warn("BAD REQUEST: the requested device id: "
+ deviceId.toString()
+ " is not associated to any SNMP Device");
return false;
}
return snmpDevice.isReachable();
}
/**
* This class is intended to add or remove Configured SNMP Devices. Functionality relies on 'createFlag' and
* 'SnmpDevice' content. The functionality runs as a thread and depending on the 'createFlag' value it will create
* or remove Device entry from the core.
*/
private class DeviceCreator implements Runnable {
private SnmpDevice device;
private boolean createFlag;
public DeviceCreator(SnmpDevice device, boolean createFlag) {
this.device = device;
this.createFlag = createFlag;
}
@Override
public void run() {
if (createFlag) {
log.info("Trying to create Device Info on ONOS core");
advertiseDevices();
} else {
log.info("Trying to remove Device Info on ONOS core");
removeDevices();
}
}
/**
* For each SNMP Device, remove the entry from the device store.
*/
private void removeDevices() {
if (device == null) {
log.warn("The Request SNMP Device is null, cannot proceed further");
return;
}
try {
DeviceId did = getDeviceId();
if (!snmpDeviceMap.containsKey(did)) {
log.error("BAD Request: 'Currently device is not discovered, "
+ "so cannot remove/disconnect the device: "
+ device.deviceInfo() + "'");
return;
}
providerService.deviceDisconnected(did);
device.disconnect();
snmpDeviceMap.remove(did);
delay(EVENTINTERVAL);
} catch (URISyntaxException uriSyntaxExcpetion) {
log.error("Syntax Error while creating URI for the device: "
+ device.deviceInfo()
+ " couldn't remove the device from the store",
uriSyntaxExcpetion);
}
}
/**
* Initialize SNMP Device object, and notify core saying device connected.
*/
private void advertiseDevices() {
try {
if (device == null) {
log.warn("The Request SNMP Device is null, cannot proceed further");
return;
}
device.init();
DeviceId did = getDeviceId();
ChassisId cid = new ChassisId();
DeviceDescription desc = new DefaultDeviceDescription(
did.uri(), Device.Type.OTHER, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, cid);
desc = populateDescriptionFromDevice(did, desc);
log.info("Persisting Device " + did.uri().toString());
snmpDeviceMap.put(did, device);
providerService.deviceConnected(did, desc);
log.info("Done with Device Info Creation on ONOS core. Device Info: "
+ device.deviceInfo() + " " + did.uri().toString());
delay(EVENTINTERVAL);
} catch (URISyntaxException e) {
log.error("Syntax Error while creating URI for the device: "
+ device.deviceInfo()
+ " couldn't persist the device onto the store", e);
} catch (Exception e) {
log.error("Error while initializing session for the device: "
+ (device != null ? device.deviceInfo() : null), e);
}
}
private DeviceDescription populateDescriptionFromDevice(DeviceId did, DeviceDescription desc) {
String[] deviceComponents = did.toString().split(":");
if (deviceComponents.length > 1) {
String ipAddress = deviceComponents[1];
String port = deviceComponents[2];
ISnmpConfiguration config = new V2cSnmpConfiguration();
config.setPort(Integer.parseInt(port));
try (ISnmpSession session = sessionFactory.createSession(config, ipAddress)) {
// Each session will be auto-closed.
String deviceOID = session.identifyDevice();
if (providers.containsKey(deviceOID)) {
desc = providers.get(deviceOID).populateDescription(session, desc);
}
} catch (IOException | RuntimeException ex) {
log.error("Failed to walk device.", ex.getMessage());
log.debug("Detailed problem was ", ex);
}
}
return desc;
}
/**
* This will build a device id for the device.
*/
private DeviceId getDeviceId() throws URISyntaxException {
String additionalSSP = new StringBuilder(
device.getSnmpHost()).append(":")
.append(device.getSnmpPort()).toString();
return DeviceId.deviceId(new URI(SCHEME, additionalSSP,
null));
}
}
protected ISnmpSessionFactory getSessionFactory(ISnmpConfigurationFactory configurationFactory) {
return new SnmpSessionFactory(configurationFactory);
}
}
/*
* Copyright 2015 Open Networking Laboratory
*
* 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.
*/
/**
* Provider that uses SNMP capability request as a means of infrastructure device discovery.
*/
package org.onosproject.provider.snmp.device.impl;
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2014 Open Networking Laboratory
~ Copyright 2015 Open Networking Laboratory
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
......@@ -29,10 +29,55 @@
<artifactId>onos-snmp-providers</artifactId>
<packaging>pom</packaging>
<description>ONOS SNMP Protocol Adapters</description>
<description>ONOS SNMP protocol adapters</description>
<modules>
<module>device</module>
<module>app</module>
<module>alarm</module>
</modules>
<dependencies>
<dependency>
<groupId>org.apache.servicemix.bundles</groupId>
<artifactId>org.apache.servicemix.bundles.snmp4j</artifactId>
<version>2.3.4_1</version>
<exclusions>
<exclusion>
<artifactId>log4j</artifactId>
<groupId>log4j</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.btisystems</groupId>
<artifactId>snmp-core</artifactId>
<version>1.3-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.btisystems.mibbler.mibs</groupId>
<artifactId>bti7000</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.btisystems.mibbler.mibs</groupId>
<artifactId>net-snmp</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<repositories>
<repository>
<!-- TODO move over to release snmp-core when it becomes available.-->
<id>oss.sonatype.org-snapshot</id>
<url>http://oss.sonatype.org/content/repositories/snapshots</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
</project>
\ No newline at end of file
</project>
......
#
# devices which may have faults.
# TODO change to NetworkConfig subsystem.
#
fmDevices = 172.27.7.110,172.27.7.110,3.3.3.3
#
# devices which support SNMP, these may support SNMP fault-management.
# demo.snmplabs.com is a publically available SNMP agent-simulator accessible via the internet, see http://snmpsim.sourceforge.net/public-snmp-simulator.html
#
devConfigs = bti7000:public@172.27.7.109:161:active,net-snmp:public@demo.snmplabs.com:161:active,net-snmp:public@demo.snmplabs.com:1161:active,net-snmp:public@demo.snmplabs.com:2161:active,net-snmp:public@demo.snmplabs.com:3161:active