Event history service and CLI
- Tool created while debugging ONOS-3509
Usage Example: (See recent Mastership and Device events)
onos> events -m -d
Change-Id: I87aceaf8fe61732a61c2d1e39399d0f10a729b54
diff --git a/apps/events/src/main/java/org/onosproject/events/EventsCommand.java b/apps/events/src/main/java/org/onosproject/events/EventsCommand.java
new file mode 100644
index 0000000..c6f15de
--- /dev/null
+++ b/apps/events/src/main/java/org/onosproject/events/EventsCommand.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright 2016 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.events;
+
+import static java.util.stream.Collectors.toList;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.List;
+import java.util.function.Predicate;
+import java.util.stream.Collector;
+import java.util.stream.Stream;
+
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.joda.time.LocalDateTime;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.cluster.ClusterEvent;
+import org.onosproject.event.Event;
+import org.onosproject.mastership.MastershipEvent;
+import org.onosproject.net.Link;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.host.HostEvent;
+import org.onosproject.net.link.LinkEvent;
+import org.onosproject.net.topology.Topology;
+import org.onosproject.net.topology.TopologyEvent;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Command to print history of instance local ONOS Events.
+ */
+@Command(scope = "onos", name = "events",
+ description = "Command to print history of instance local ONOS Events")
+public class EventsCommand
+ extends AbstractShellCommand {
+
+ @Option(name = "--all", aliases = "-a",
+ description = "Include all Events (default behavior)",
+ required = false)
+ private boolean all = false;
+
+ @Option(name = "--mastership", aliases = "-m",
+ description = "Include MastershipEvent",
+ required = false)
+ private boolean mastership = false;
+
+ @Option(name = "--device", aliases = "-d",
+ description = "Include DeviceEvent",
+ required = false)
+ private boolean device = false;
+
+ @Option(name = "--link", aliases = "-l",
+ description = "Include LinkEvent",
+ required = false)
+ private boolean link = false;
+
+ @Option(name = "--topology", aliases = "-t",
+ description = "Include TopologyEvent",
+ required = false)
+ private boolean topology = false;
+
+ @Option(name = "--host", aliases = "-t",
+ description = "Include HostEvent",
+ required = false)
+ private boolean host = false;
+
+ @Option(name = "--cluster", aliases = "-c",
+ description = "Include ClusterEvent",
+ required = false)
+ private boolean cluster = false;
+
+ @Option(name = "--max-events", aliases = "-n",
+ description = "Maximum number of events to print",
+ required = false,
+ valueToShowInHelp = "-1 [no limit]")
+ private long maxSize = -1;
+
+ @Override
+ protected void execute() {
+ EventHistoryService eventHistoryService = get(EventHistoryService.class);
+
+ Stream<Event<?, ?>> events = eventHistoryService.history().stream();
+
+ boolean dumpAll = all || !(mastership || device || link || topology || host);
+
+ if (!dumpAll) {
+ Predicate<Event<?, ?>> filter = (defaultIs) -> false;
+
+ if (mastership) {
+ filter = filter.or(evt -> evt instanceof MastershipEvent);
+ }
+ if (device) {
+ filter = filter.or(evt -> evt instanceof DeviceEvent);
+ }
+ if (link) {
+ filter = filter.or(evt -> evt instanceof LinkEvent);
+ }
+ if (topology) {
+ filter = filter.or(evt -> evt instanceof TopologyEvent);
+ }
+ if (host) {
+ filter = filter.or(evt -> evt instanceof HostEvent);
+ }
+ if (cluster) {
+ filter = filter.or(evt -> evt instanceof ClusterEvent);
+ }
+
+ events = events.filter(filter);
+ }
+
+ if (maxSize > 0) {
+ events = events.limit(maxSize);
+ }
+
+ if (outputJson()) {
+ ArrayNode jsonEvents = events.map(this::json).collect(toArrayNode());
+ printJson(jsonEvents);
+ } else {
+ events.forEach(this::printEvent);
+ }
+
+ }
+
+ private Collector<JsonNode, ArrayNode, ArrayNode> toArrayNode() {
+ return Collector.of(() -> mapper().createArrayNode(),
+ ArrayNode::add,
+ ArrayNode::addAll);
+ }
+
+ private ObjectNode json(Event<?, ?> event) {
+ ObjectNode result = mapper().createObjectNode();
+
+ result.put("time", event.time())
+ .put("type", event.type().toString())
+ .put("event", event.toString());
+
+ return result;
+ }
+
+ /**
+ * Print JsonNode using default pretty printer.
+ *
+ * @param json JSON node to print
+ */
+ private void printJson(JsonNode json) {
+ try {
+ print("%s", mapper().writerWithDefaultPrettyPrinter().writeValueAsString(json));
+ } catch (JsonProcessingException e) {
+ StringWriter sw = new StringWriter();
+ e.printStackTrace(new PrintWriter(sw));
+ print("[ERROR] %s\n%s", e.getMessage(), sw.toString());
+ }
+ }
+
+ private void printEvent(Event<?, ?> event) {
+ if (event instanceof DeviceEvent) {
+ DeviceEvent deviceEvent = (DeviceEvent) event;
+ if (event.type().toString().startsWith("PORT")) {
+ // Port event
+ print("%s %s\t%s/%s [%s]",
+ new LocalDateTime(event.time()),
+ event.type(),
+ deviceEvent.subject().id(), deviceEvent.port().number(),
+ deviceEvent.port()
+ );
+ } else {
+ // Device event
+ print("%s %s\t%s [%s]",
+ new LocalDateTime(event.time()),
+ event.type(),
+ deviceEvent.subject().id(),
+ deviceEvent.subject()
+ );
+ }
+
+ } else if (event instanceof MastershipEvent) {
+ print("%s %s\t%s [%s]",
+ new LocalDateTime(event.time()),
+ event.type(),
+ event.subject(),
+ ((MastershipEvent) event).roleInfo());
+
+ } else if (event instanceof LinkEvent) {
+ LinkEvent linkEvent = (LinkEvent) event;
+ Link link = linkEvent.subject();
+ print("%s %s\t%s/%s-%s/%s [%s]",
+ new LocalDateTime(event.time()),
+ event.type(),
+ link.src().deviceId(), link.src().port(), link.dst().deviceId(), link.dst().port(),
+ link);
+
+ } else if (event instanceof HostEvent) {
+ HostEvent hostEvent = (HostEvent) event;
+ print("%s %s\t%s [%s->%s]",
+ new LocalDateTime(event.time()),
+ event.type(),
+ hostEvent.subject().id(),
+ hostEvent.prevSubject(), hostEvent.subject());
+
+ } else if (event instanceof TopologyEvent) {
+ TopologyEvent topoEvent = (TopologyEvent) event;
+ List<Event> reasons = MoreObjects.firstNonNull(topoEvent.reasons(), ImmutableList.of());
+ Topology topo = topoEvent.subject();
+ String summary = String.format("(d=%d,l=%d,c=%d)",
+ topo.deviceCount(),
+ topo.linkCount(),
+ topo.clusterCount());
+ print("%s %s%s [%s]",
+ new LocalDateTime(event.time()),
+ event.type(),
+ summary,
+ reasons.stream().map(e -> e.type()).collect(toList()));
+
+ } else if (event instanceof ClusterEvent) {
+ print("%s %s\t%s [%s]",
+ new LocalDateTime(event.time()),
+ event.type(),
+ ((ClusterEvent) event).subject().id(),
+ event.subject());
+
+ } else {
+ // Unknown Event?
+ print("%s %s\t%s [%s]",
+ new LocalDateTime(event.time()),
+ event.type(),
+ event.subject(),
+ event);
+ }
+ }
+
+}