diff options
author | Ashlee Young <ashlee@onosfw.com> | 2015-09-09 22:15:21 -0700 |
---|---|---|
committer | Ashlee Young <ashlee@onosfw.com> | 2015-09-09 22:15:21 -0700 |
commit | 13d05bc8458758ee39cb829098241e89616717ee (patch) | |
tree | 22a4d1ce65f15952f07a3df5af4b462b4697cb3a /framework/src/onos/apps/metrics | |
parent | 6139282e1e93c2322076de4b91b1c85d0bc4a8b3 (diff) |
ONOS checkin based on commit tag e796610b1f721d02f9b0e213cf6f7790c10ecd60
Change-Id: Ife8810491034fe7becdba75dda20de4267bd15cd
Diffstat (limited to 'framework/src/onos/apps/metrics')
14 files changed, 1332 insertions, 0 deletions
diff --git a/framework/src/onos/apps/metrics/pom.xml b/framework/src/onos/apps/metrics/pom.xml new file mode 100644 index 00000000..89757c9a --- /dev/null +++ b/framework/src/onos/apps/metrics/pom.xml @@ -0,0 +1,69 @@ +<?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. + --> +<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-apps</artifactId> + <version>1.3.0-SNAPSHOT</version> + <relativePath>../pom.xml</relativePath> + </parent> + + <artifactId>onos-app-metrics</artifactId> + <packaging>bundle</packaging> + + <description>Performance metrics collection</description> + + <properties> + <onos.app.name>org.onosproject.metrics</onos.app.name> + </properties> + + <dependencies> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onlab-misc</artifactId> + </dependency> + + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-cli</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.core</artifactId> + </dependency> + + <dependency> + <groupId>org.apache.karaf.shell</groupId> + <artifactId>org.apache.karaf.shell.console</artifactId> + </dependency> + + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-databind</artifactId> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-annotations</artifactId> + </dependency> + </dependencies> + +</project> diff --git a/framework/src/onos/apps/metrics/src/main/java/org/onosproject/metrics/intent/IntentMetrics.java b/framework/src/onos/apps/metrics/src/main/java/org/onosproject/metrics/intent/IntentMetrics.java new file mode 100644 index 00000000..78a540c9 --- /dev/null +++ b/framework/src/onos/apps/metrics/src/main/java/org/onosproject/metrics/intent/IntentMetrics.java @@ -0,0 +1,234 @@ +/* + * 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.metrics.intent; + +import static org.slf4j.LoggerFactory.getLogger; + +import java.util.LinkedList; +import java.util.List; + +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.apache.felix.scr.annotations.Service; +import org.onlab.metrics.EventMetric; +import org.onlab.metrics.MetricsService; +import org.onosproject.core.ApplicationId; +import org.onosproject.core.CoreService; +import org.onosproject.net.intent.IntentEvent; +import org.onosproject.net.intent.IntentListener; +import org.onosproject.net.intent.IntentService; +import org.slf4j.Logger; + +/** + * ONOS Intent Metrics Application that collects intent-related metrics. + */ +@Component(immediate = true) +@Service +public class IntentMetrics implements IntentMetricsService, + IntentListener { + private static final Logger log = getLogger(IntentMetrics.class); + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected CoreService coreService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected IntentService intentService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected MetricsService metricsService; + + private ApplicationId appId; + + private LinkedList<IntentEvent> lastEvents = new LinkedList<>(); + private static final int LAST_EVENTS_MAX_N = 100; + + // + // Metrics + // + private static final String COMPONENT_NAME = "Intent"; + private static final String FEATURE_SUBMITTED_NAME = "Submitted"; + private static final String FEATURE_INSTALLED_NAME = "Installed"; + private static final String FEATURE_FAILED_NAME = "Failed"; + private static final String FEATURE_WITHDRAW_REQUESTED_NAME = + "WithdrawRequested"; + private static final String FEATURE_WITHDRAWN_NAME = "Withdrawn"; + private static final String FEATURE_PURGED_NAME = "Purged"; + // + // Event metrics: + // - Intent Submitted API operation + // - Intent Installed operation completion + // - Intent Failed compilation or installation + // - Intent Withdraw Requested API operation + // - Intent Withdrawn operation completion + // - Intent Purged operation completion + // + private EventMetric intentSubmittedEventMetric; + private EventMetric intentInstalledEventMetric; + private EventMetric intentFailedEventMetric; + private EventMetric intentWithdrawRequestedEventMetric; + private EventMetric intentWithdrawnEventMetric; + private EventMetric intentPurgedEventMetric; + + @Activate + protected void activate() { + appId = coreService.registerApplication("org.onosproject.metrics"); + + clear(); + registerMetrics(); + intentService.addListener(this); + log.info("Started with Application ID {}", appId.id()); + } + + @Deactivate + public void deactivate() { + intentService.removeListener(this); + removeMetrics(); + clear(); + log.info("Stopped"); + } + + @Override + public List<IntentEvent> getEvents() { + synchronized (lastEvents) { + return ImmutableList.<IntentEvent>copyOf(lastEvents); + } + } + + @Override + public EventMetric intentSubmittedEventMetric() { + return intentSubmittedEventMetric; + } + + @Override + public EventMetric intentInstalledEventMetric() { + return intentInstalledEventMetric; + } + + @Override + public EventMetric intentFailedEventMetric() { + return intentFailedEventMetric; + } + + @Override + public EventMetric intentWithdrawRequestedEventMetric() { + return intentWithdrawRequestedEventMetric; + } + + @Override + public EventMetric intentWithdrawnEventMetric() { + return intentWithdrawnEventMetric; + } + + @Override + public EventMetric intentPurgedEventMetric() { + return intentPurgedEventMetric; + } + + @Override + public void event(IntentEvent event) { + synchronized (lastEvents) { + switch (event.type()) { + case INSTALL_REQ: + intentSubmittedEventMetric.eventReceived(); + break; + case INSTALLED: + intentInstalledEventMetric.eventReceived(); + break; + case FAILED: + intentFailedEventMetric.eventReceived(); + break; + case WITHDRAW_REQ: + intentWithdrawRequestedEventMetric.eventReceived(); + break; + case WITHDRAWN: + intentWithdrawnEventMetric.eventReceived(); + break; + case PURGED: + intentPurgedEventMetric.eventReceived(); + break; + default: + break; + } + + // + // Keep only the last N events, where N = LAST_EVENTS_MAX_N + // + while (lastEvents.size() >= LAST_EVENTS_MAX_N) { + lastEvents.remove(); + } + lastEvents.add(event); + } + + log.debug("Intent Event: time = {} type = {} event = {}", + event.time(), event.type(), event); + } + + /** + * Clears the internal state. + */ + private void clear() { + synchronized (lastEvents) { + lastEvents.clear(); + } + } + + /** + * Registers the metrics. + */ + private void registerMetrics() { + intentSubmittedEventMetric = + new EventMetric(metricsService, COMPONENT_NAME, + FEATURE_SUBMITTED_NAME); + intentInstalledEventMetric = + new EventMetric(metricsService, COMPONENT_NAME, + FEATURE_INSTALLED_NAME); + intentFailedEventMetric = + new EventMetric(metricsService, COMPONENT_NAME, + FEATURE_FAILED_NAME); + intentWithdrawRequestedEventMetric = + new EventMetric(metricsService, COMPONENT_NAME, + FEATURE_WITHDRAW_REQUESTED_NAME); + intentWithdrawnEventMetric = + new EventMetric(metricsService, COMPONENT_NAME, + FEATURE_WITHDRAWN_NAME); + intentPurgedEventMetric = + new EventMetric(metricsService, COMPONENT_NAME, + FEATURE_PURGED_NAME); + + intentSubmittedEventMetric.registerMetrics(); + intentInstalledEventMetric.registerMetrics(); + intentFailedEventMetric.registerMetrics(); + intentWithdrawRequestedEventMetric.registerMetrics(); + intentWithdrawnEventMetric.registerMetrics(); + intentPurgedEventMetric.registerMetrics(); + } + + /** + * Removes the metrics. + */ + private void removeMetrics() { + intentSubmittedEventMetric.removeMetrics(); + intentInstalledEventMetric.removeMetrics(); + intentFailedEventMetric.removeMetrics(); + intentWithdrawRequestedEventMetric.removeMetrics(); + intentWithdrawnEventMetric.removeMetrics(); + intentPurgedEventMetric.removeMetrics(); + } +} diff --git a/framework/src/onos/apps/metrics/src/main/java/org/onosproject/metrics/intent/IntentMetricsService.java b/framework/src/onos/apps/metrics/src/main/java/org/onosproject/metrics/intent/IntentMetricsService.java new file mode 100644 index 00000000..aa03e390 --- /dev/null +++ b/framework/src/onos/apps/metrics/src/main/java/org/onosproject/metrics/intent/IntentMetricsService.java @@ -0,0 +1,74 @@ +/* + * 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.metrics.intent; + +import java.util.List; +import org.onlab.metrics.EventMetric; +import org.onosproject.net.intent.IntentEvent; + +/** + * Service interface exported by IntentMetrics. + */ +public interface IntentMetricsService { + /** + * Gets the last saved intent events. + * + * @return the last saved intent events + */ + List<IntentEvent> getEvents(); + + /** + * Gets the Event Metric for the intent INSTALL_REQ events. + * + * @return the Event Metric for the intent INSTALL_REQ events + */ + EventMetric intentSubmittedEventMetric(); + + /** + * Gets the Event Metric for the intent INSTALLED events. + * + * @return the Event Metric for the intent INSTALLED events + */ + EventMetric intentInstalledEventMetric(); + + /** + * Gets the Event Metric for the intent FAILED events. + * + * @return the Event Metric for the intent FAILED events + */ + EventMetric intentFailedEventMetric(); + + /** + * Gets the Event Metric for the intent WITHDRAW_REQ events. + * + * @return the Event Metric for the intent WITHDRAW_REQ events + */ + EventMetric intentWithdrawRequestedEventMetric(); + + /** + * Gets the Event Metric for the intent WITHDRAWN events. + * + * @return the Event Metric for the intent WITHDRAWN events + */ + EventMetric intentWithdrawnEventMetric(); + + /** + * Gets the Event Metric for the intent PURGED events. + * + * @return the Event Metric for the intent PURGED events + */ + EventMetric intentPurgedEventMetric(); +} diff --git a/framework/src/onos/apps/metrics/src/main/java/org/onosproject/metrics/intent/cli/IntentEventsListCommand.java b/framework/src/onos/apps/metrics/src/main/java/org/onosproject/metrics/intent/cli/IntentEventsListCommand.java new file mode 100644 index 00000000..3c8457ad --- /dev/null +++ b/framework/src/onos/apps/metrics/src/main/java/org/onosproject/metrics/intent/cli/IntentEventsListCommand.java @@ -0,0 +1,83 @@ +/* + * 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. + */ +package org.onosproject.metrics.intent.cli; + +import java.util.List; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.apache.karaf.shell.commands.Command; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.metrics.intent.IntentMetricsService; +import org.onosproject.net.intent.IntentEvent; + +/** + * Command to show the list of last intent events. + */ +@Command(scope = "onos", name = "intents-events", + description = "Lists the last intent events") +public class IntentEventsListCommand extends AbstractShellCommand { + + private static final String FORMAT_EVENT = "Event=%s"; + + @Override + protected void execute() { + IntentMetricsService service = get(IntentMetricsService.class); + + if (outputJson()) { + print("%s", json(service.getEvents())); + } else { + for (IntentEvent event : service.getEvents()) { + print(FORMAT_EVENT, event); + print(""); // Extra empty line for clarity + } + } + } + + /** + * Produces a JSON array of intent events. + * + * @param intentEvents the intent events with the data + * @return JSON array with the intent events + */ + private JsonNode json(List<IntentEvent> intentEvents) { + ObjectMapper mapper = new ObjectMapper(); + ArrayNode result = mapper.createArrayNode(); + + for (IntentEvent event : intentEvents) { + result.add(json(mapper, event)); + } + return result; + } + + /** + * Produces JSON object for a intent event. + * + * @param mapper the JSON object mapper to use + * @param intentEvent the intent event with the data + * @return JSON object for the intent event + */ + private ObjectNode json(ObjectMapper mapper, IntentEvent intentEvent) { + ObjectNode result = mapper.createObjectNode(); + + result.put("time", intentEvent.time()) + .put("type", intentEvent.type().toString()) + .put("event", intentEvent.toString()); + return result; + } +} diff --git a/framework/src/onos/apps/metrics/src/main/java/org/onosproject/metrics/intent/cli/IntentEventsMetricsCommand.java b/framework/src/onos/apps/metrics/src/main/java/org/onosproject/metrics/intent/cli/IntentEventsMetricsCommand.java new file mode 100644 index 00000000..356b9ada --- /dev/null +++ b/framework/src/onos/apps/metrics/src/main/java/org/onosproject/metrics/intent/cli/IntentEventsMetricsCommand.java @@ -0,0 +1,152 @@ +/* + * 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.metrics.intent.cli; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import com.codahale.metrics.Gauge; +import com.codahale.metrics.Meter; +import com.codahale.metrics.json.MetricsModule; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.apache.karaf.shell.commands.Command; +import org.onlab.metrics.EventMetric; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.metrics.intent.IntentMetricsService; + +/** + * Command to show the intent events metrics. + */ +@Command(scope = "onos", name = "intents-events-metrics", + description = "Lists intent events metrics") +public class IntentEventsMetricsCommand extends AbstractShellCommand { + + private static final String FORMAT_GAUGE = + "Intent %s Event Timestamp (ms from epoch)=%d"; + private static final String FORMAT_METER = + "Intent %s Events count=%d rate(events/sec) mean=%f m1=%f m5=%f m15=%f"; + + @Override + protected void execute() { + IntentMetricsService service = get(IntentMetricsService.class); + + if (outputJson()) { + ObjectMapper mapper = new ObjectMapper() + .registerModule(new MetricsModule(TimeUnit.SECONDS, + TimeUnit.MILLISECONDS, + false)); + ObjectNode result = mapper.createObjectNode(); + result = json(mapper, result, "intentSubmitted", + service.intentSubmittedEventMetric()); + result = json(mapper, result, "intentInstalled", + service.intentInstalledEventMetric()); + result = json(mapper, result, "intentFailed", + service.intentFailedEventMetric()); + result = json(mapper, result, "intentWithdrawRequested", + service.intentWithdrawRequestedEventMetric()); + result = json(mapper, result, "intentWithdrawn", + service.intentWithdrawnEventMetric()); + result = json(mapper, result, "intentPurged", + service.intentPurgedEventMetric()); + print("%s", result); + } else { + printEventMetric("Submitted", + service.intentSubmittedEventMetric()); + printEventMetric("Installed", + service.intentInstalledEventMetric()); + printEventMetric("Failed", + service.intentFailedEventMetric()); + printEventMetric("Withdraw Requested", + service.intentWithdrawRequestedEventMetric()); + printEventMetric("Withdrawn", + service.intentWithdrawnEventMetric()); + printEventMetric("Purged", + service.intentPurgedEventMetric()); + } + } + + /** + * Produces JSON node for an Event Metric. + * + * @param mapper the JSON object mapper to use + * @param objectNode the JSON object node to use + * @param propertyPrefix the property prefix to use + * @param eventMetric the Event Metric with the data + * @return JSON object node for the Event Metric + */ + private ObjectNode json(ObjectMapper mapper, ObjectNode objectNode, + String propertyPrefix, EventMetric eventMetric) { + String gaugeName = propertyPrefix + "Timestamp"; + String meterName = propertyPrefix + "Rate"; + Gauge<Long> gauge = eventMetric.lastEventTimestampGauge(); + Meter meter = eventMetric.eventRateMeter(); + + objectNode.set(gaugeName, json(mapper, gauge)); + objectNode.set(meterName, json(mapper, meter)); + return objectNode; + } + + /** + * Produces JSON node for an Object. + * + * @param mapper the JSON object mapper to use + * @param object the Object with the data + * @return JSON node for the Object + */ + private JsonNode json(ObjectMapper mapper, Object object) { + // + // NOTE: The API for custom serializers is incomplete, + // hence we have to parse the JSON string to create JsonNode. + // + try { + final String objectJson = mapper.writeValueAsString(object); + JsonNode jsonNode = mapper.readTree(objectJson); + return jsonNode; + } catch (JsonProcessingException e) { + log.error("Error writing value as JSON string", e); + } catch (IOException e) { + log.error("Error writing value as JSON string", e); + } + return null; + } + + /** + * Prints an Event Metric. + * + * @param operationStr the string with the intent operation to print + * @param eventMetric the Event Metric to print + */ + private void printEventMetric(String operationStr, + EventMetric eventMetric) { + Gauge<Long> gauge = eventMetric.lastEventTimestampGauge(); + Meter meter = eventMetric.eventRateMeter(); + TimeUnit rateUnit = TimeUnit.SECONDS; + double rateFactor = rateUnit.toSeconds(1); + + // Print the Gauge + print(FORMAT_GAUGE, operationStr, gauge.getValue()); + + // Print the Meter + print(FORMAT_METER, operationStr, meter.getCount(), + meter.getMeanRate() * rateFactor, + meter.getOneMinuteRate() * rateFactor, + meter.getFiveMinuteRate() * rateFactor, + meter.getFifteenMinuteRate() * rateFactor); + } +} diff --git a/framework/src/onos/apps/metrics/src/main/java/org/onosproject/metrics/intent/cli/package-info.java b/framework/src/onos/apps/metrics/src/main/java/org/onosproject/metrics/intent/cli/package-info.java new file mode 100644 index 00000000..ddc7877c --- /dev/null +++ b/framework/src/onos/apps/metrics/src/main/java/org/onosproject/metrics/intent/cli/package-info.java @@ -0,0 +1,20 @@ +/* + * 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. + */ + +/** + * Intent metrics command-line handlers. + */ +package org.onosproject.metrics.intent.cli; diff --git a/framework/src/onos/apps/metrics/src/main/java/org/onosproject/metrics/intent/package-info.java b/framework/src/onos/apps/metrics/src/main/java/org/onosproject/metrics/intent/package-info.java new file mode 100644 index 00000000..9a7ac66c --- /dev/null +++ b/framework/src/onos/apps/metrics/src/main/java/org/onosproject/metrics/intent/package-info.java @@ -0,0 +1,20 @@ +/* + * 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. + */ + +/** + * ONOS Intent Metrics Application that collects intent-related metrics. + */ +package org.onosproject.metrics.intent; diff --git a/framework/src/onos/apps/metrics/src/main/java/org/onosproject/metrics/topology/TopologyMetrics.java b/framework/src/onos/apps/metrics/src/main/java/org/onosproject/metrics/topology/TopologyMetrics.java new file mode 100644 index 00000000..5785f5f2 --- /dev/null +++ b/framework/src/onos/apps/metrics/src/main/java/org/onosproject/metrics/topology/TopologyMetrics.java @@ -0,0 +1,295 @@ +/* + * 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.metrics.topology; + +import static org.slf4j.LoggerFactory.getLogger; + +import java.util.LinkedList; +import java.util.List; + +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.apache.felix.scr.annotations.Service; +import org.onlab.metrics.EventMetric; +import org.onlab.metrics.MetricsService; +import org.onosproject.core.ApplicationId; +import org.onosproject.core.CoreService; +import org.onosproject.event.Event; +import org.onosproject.net.device.DeviceEvent; +import org.onosproject.net.device.DeviceListener; +import org.onosproject.net.device.DeviceService; +import org.onosproject.net.host.HostEvent; +import org.onosproject.net.host.HostListener; +import org.onosproject.net.host.HostService; +import org.onosproject.net.link.LinkEvent; +import org.onosproject.net.link.LinkListener; +import org.onosproject.net.link.LinkService; +import org.onosproject.net.topology.TopologyEvent; +import org.onosproject.net.topology.TopologyListener; +import org.onosproject.net.topology.TopologyService; +import org.slf4j.Logger; + +/** + * ONOS Topology Metrics Application that collects topology-related metrics. + */ +@Component(immediate = true) +@Service +public class TopologyMetrics implements TopologyMetricsService { + private static final Logger log = getLogger(TopologyMetrics.class); + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected CoreService coreService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected DeviceService deviceService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected HostService hostService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected LinkService linkService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected MetricsService metricsService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected TopologyService topologyService; + + private ApplicationId appId; + + private LinkedList<Event> lastEvents = new LinkedList<>(); + private static final int LAST_EVENTS_MAX_N = 100; + + private final DeviceListener deviceListener = new InnerDeviceListener(); + private final HostListener hostListener = new InnerHostListener(); + private final LinkListener linkListener = new InnerLinkListener(); + private final TopologyListener topologyListener = + new InnerTopologyListener(); + + // + // Metrics + // + private static final String COMPONENT_NAME = "Topology"; + private static final String FEATURE_DEVICE_NAME = "DeviceEvent"; + private static final String FEATURE_HOST_NAME = "HostEvent"; + private static final String FEATURE_LINK_NAME = "LinkEvent"; + private static final String FEATURE_GRAPH_NAME = "GraphEvent"; + private static final String FEATURE_GRAPH_REASONS_NAME = "GraphReasonsEvent"; + // + // Event metrics: + // - Device events + // - Host events + // - Link events + // - Topology Graph events + // - Topology Graph Reasons events + // + private EventMetric topologyDeviceEventMetric; + private EventMetric topologyHostEventMetric; + private EventMetric topologyLinkEventMetric; + private EventMetric topologyGraphEventMetric; + private EventMetric topologyGraphReasonsEventMetric; + + @Activate + protected void activate() { + appId = coreService.registerApplication("org.onosproject.metrics"); + + clear(); + registerMetrics(); + + // Register for all topology-related events + deviceService.addListener(deviceListener); + hostService.addListener(hostListener); + linkService.addListener(linkListener); + topologyService.addListener(topologyListener); + + log.info("Started with Application ID {}", appId.id()); + } + + @Deactivate + public void deactivate() { + // De-register from all topology-related events + deviceService.removeListener(deviceListener); + hostService.removeListener(hostListener); + linkService.removeListener(linkListener); + topologyService.removeListener(topologyListener); + + removeMetrics(); + clear(); + log.info("Stopped"); + } + + @Override + public List<Event> getEvents() { + synchronized (lastEvents) { + return ImmutableList.<Event>copyOf(lastEvents); + } + } + + @Override + public EventMetric topologyDeviceEventMetric() { + return topologyDeviceEventMetric; + } + + @Override + public EventMetric topologyHostEventMetric() { + return topologyHostEventMetric; + } + + @Override + public EventMetric topologyLinkEventMetric() { + return topologyLinkEventMetric; + } + + @Override + public EventMetric topologyGraphEventMetric() { + return topologyGraphEventMetric; + } + + @Override + public EventMetric topologyGraphReasonsEventMetric() { + return topologyGraphReasonsEventMetric; + } + + /** + * Records an event. + * + * @param event the event to record + * @param eventMetric the Event Metric to use + */ + private void recordEvent(Event event, EventMetric eventMetric) { + synchronized (lastEvents) { + eventMetric.eventReceived(); + + // + // Keep only the last N events, where N = LAST_EVENTS_MAX_N + // + while (lastEvents.size() >= LAST_EVENTS_MAX_N) { + lastEvents.remove(); + } + lastEvents.add(event); + } + } + + /** + * Inner Device Event Listener class. + */ + private class InnerDeviceListener implements DeviceListener { + @Override + public void event(DeviceEvent event) { + // Ignore PORT_STATS_UPDATED probe event from interfering with + // other device event timestamps + if (event.type() == DeviceEvent.Type.PORT_STATS_UPDATED) { + log.info("PORT_STATS_UPDATED event ignored from metrics"); + } else { + recordEvent(event, topologyDeviceEventMetric); + log.info("Device Event: time = {} type = {} event = {}", + event.time(), event.type(), event); + } + } + } + + /** + * Inner Host Event Listener class. + */ + private class InnerHostListener implements HostListener { + @Override + public void event(HostEvent event) { + recordEvent(event, topologyHostEventMetric); + log.debug("Host Event: time = {} type = {} event = {}", + event.time(), event.type(), event); + } + } + + /** + * Inner Link Event Listener class. + */ + private class InnerLinkListener implements LinkListener { + @Override + public void event(LinkEvent event) { + recordEvent(event, topologyLinkEventMetric); + log.debug("Link Event: time = {} type = {} event = {}", + event.time(), event.type(), event); + } + } + + /** + * Inner Topology Event Listener class. + */ + private class InnerTopologyListener implements TopologyListener { + @Override + public void event(TopologyEvent event) { + recordEvent(event, topologyGraphEventMetric); + log.debug("Topology Event: time = {} type = {} event = {}", + event.time(), event.type(), event); + for (Event reason : event.reasons()) { + recordEvent(event, topologyGraphReasonsEventMetric); + log.debug("Topology Event Reason: time = {} type = {} event = {}", + reason.time(), reason.type(), reason); + } + } + } + + /** + * Clears the internal state. + */ + private void clear() { + synchronized (lastEvents) { + lastEvents.clear(); + } + } + + /** + * Registers the metrics. + */ + private void registerMetrics() { + topologyDeviceEventMetric = + new EventMetric(metricsService, COMPONENT_NAME, + FEATURE_DEVICE_NAME); + topologyHostEventMetric = + new EventMetric(metricsService, COMPONENT_NAME, + FEATURE_HOST_NAME); + topologyLinkEventMetric = + new EventMetric(metricsService, COMPONENT_NAME, + FEATURE_LINK_NAME); + topologyGraphEventMetric = + new EventMetric(metricsService, COMPONENT_NAME, + FEATURE_GRAPH_NAME); + topologyGraphReasonsEventMetric = + new EventMetric(metricsService, COMPONENT_NAME, + FEATURE_GRAPH_REASONS_NAME); + + topologyDeviceEventMetric.registerMetrics(); + topologyHostEventMetric.registerMetrics(); + topologyLinkEventMetric.registerMetrics(); + topologyGraphEventMetric.registerMetrics(); + topologyGraphReasonsEventMetric.registerMetrics(); + } + + /** + * Removes the metrics. + */ + private void removeMetrics() { + topologyDeviceEventMetric.removeMetrics(); + topologyHostEventMetric.removeMetrics(); + topologyLinkEventMetric.removeMetrics(); + topologyGraphEventMetric.removeMetrics(); + topologyGraphReasonsEventMetric.removeMetrics(); + } +} diff --git a/framework/src/onos/apps/metrics/src/main/java/org/onosproject/metrics/topology/TopologyMetricsService.java b/framework/src/onos/apps/metrics/src/main/java/org/onosproject/metrics/topology/TopologyMetricsService.java new file mode 100644 index 00000000..47e46b28 --- /dev/null +++ b/framework/src/onos/apps/metrics/src/main/java/org/onosproject/metrics/topology/TopologyMetricsService.java @@ -0,0 +1,67 @@ +/* + * 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.metrics.topology; + +import java.util.List; +import org.onlab.metrics.EventMetric; +import org.onosproject.event.Event; + +/** + * Service interface exported by TopologyMetrics. + */ +public interface TopologyMetricsService { + /** + * Gets the last saved topology events. + * + * @return the last saved topology events + */ + List<Event> getEvents(); + + /** + * Gets the Event Metric for the Device Events. + * + * @return the Event Metric for the Device Events + */ + EventMetric topologyDeviceEventMetric(); + + /** + * Gets the Event Metric for the Host Events. + * + * @return the Event Metric for the Host Events + */ + EventMetric topologyHostEventMetric(); + + /** + * Gets the Event Metric for the Link Events. + * + * @return the Event Metric for the Link Events + */ + EventMetric topologyLinkEventMetric(); + + /** + * Gets the Event Metric for the Topology Graph Events. + * + * @return the Event Metric for the Topology Graph Events + */ + EventMetric topologyGraphEventMetric(); + + /** + * Gets the Event Metric for the Topology Graph Reasons Events. + * + * @return the Event Metric for the Topology Graph Reasons Events + */ + EventMetric topologyGraphReasonsEventMetric(); +} diff --git a/framework/src/onos/apps/metrics/src/main/java/org/onosproject/metrics/topology/cli/TopologyEventsListCommand.java b/framework/src/onos/apps/metrics/src/main/java/org/onosproject/metrics/topology/cli/TopologyEventsListCommand.java new file mode 100644 index 00000000..a070ed8d --- /dev/null +++ b/framework/src/onos/apps/metrics/src/main/java/org/onosproject/metrics/topology/cli/TopologyEventsListCommand.java @@ -0,0 +1,102 @@ +/* + * 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.metrics.topology.cli; + +import java.util.List; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.apache.karaf.shell.commands.Command; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.event.Event; +import org.onosproject.metrics.topology.TopologyMetricsService; +import org.onosproject.net.topology.TopologyEvent; + +/** + * Command to show the list of last topology events. + */ +@Command(scope = "onos", name = "topology-events", + description = "Lists the last topology events") +public class TopologyEventsListCommand extends AbstractShellCommand { + + private static final String FORMAT_EVENT = "Event=%s"; + private static final String FORMAT_REASON = " Reason=%s"; + + @Override + protected void execute() { + TopologyMetricsService service = get(TopologyMetricsService.class); + + if (outputJson()) { + print("%s", json(service.getEvents())); + } else { + for (Event event : service.getEvents()) { + print(FORMAT_EVENT, event); + if (event instanceof TopologyEvent) { + TopologyEvent topologyEvent = (TopologyEvent) event; + for (Event reason : topologyEvent.reasons()) { + print(FORMAT_REASON, reason); + } + } + print(""); // Extra empty line for clarity + } + } + } + + /** + * Produces a JSON array of topology events. + * + * @param events the topology events with the data + * @return JSON array with the topology events + */ + private JsonNode json(List<Event> events) { + ObjectMapper mapper = new ObjectMapper(); + ArrayNode result = mapper.createArrayNode(); + + for (Event event : events) { + result.add(json(mapper, event)); + } + return result; + } + + /** + * Produces JSON object for a topology event. + * + * @param mapper the JSON object mapper to use + * @param event the topology event with the data + * @return JSON object for the topology event + */ + private ObjectNode json(ObjectMapper mapper, Event event) { + ObjectNode result = mapper.createObjectNode(); + + result.put("time", event.time()) + .put("type", event.type().toString()) + .put("event", event.toString()); + + // Add the reasons if a TopologyEvent + if (event instanceof TopologyEvent) { + TopologyEvent topologyEvent = (TopologyEvent) event; + ArrayNode reasons = mapper.createArrayNode(); + for (Event reason : topologyEvent.reasons()) { + reasons.add(json(mapper, reason)); + } + result.set("reasons", reasons); + } + + return result; + } +} diff --git a/framework/src/onos/apps/metrics/src/main/java/org/onosproject/metrics/topology/cli/TopologyEventsMetricsCommand.java b/framework/src/onos/apps/metrics/src/main/java/org/onosproject/metrics/topology/cli/TopologyEventsMetricsCommand.java new file mode 100644 index 00000000..0fbbc053 --- /dev/null +++ b/framework/src/onos/apps/metrics/src/main/java/org/onosproject/metrics/topology/cli/TopologyEventsMetricsCommand.java @@ -0,0 +1,144 @@ +/* + * 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.metrics.topology.cli; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import com.codahale.metrics.Gauge; +import com.codahale.metrics.Meter; +import com.codahale.metrics.json.MetricsModule; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.apache.karaf.shell.commands.Command; +import org.onlab.metrics.EventMetric; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.metrics.topology.TopologyMetricsService; + +/** + * Command to show the topology events metrics. + */ +@Command(scope = "onos", name = "topology-events-metrics", + description = "Lists topology events metrics") +public class TopologyEventsMetricsCommand extends AbstractShellCommand { + + private static final String FORMAT_GAUGE = + "Topology %s Event Timestamp (ms from epoch)=%d"; + private static final String FORMAT_METER = + "Topology %s Events count=%d rate(events/sec) mean=%f m1=%f m5=%f m15=%f"; + + @Override + protected void execute() { + TopologyMetricsService service = get(TopologyMetricsService.class); + + if (outputJson()) { + ObjectMapper mapper = new ObjectMapper() + .registerModule(new MetricsModule(TimeUnit.SECONDS, + TimeUnit.MILLISECONDS, + false)); + ObjectNode result = mapper.createObjectNode(); + result = json(mapper, result, "topologyDeviceEvent", + service.topologyDeviceEventMetric()); + result = json(mapper, result, "topologyHostEvent", + service.topologyHostEventMetric()); + result = json(mapper, result, "topologyLinkEvent", + service.topologyLinkEventMetric()); + result = json(mapper, result, "topologyGraphEvent", + service.topologyGraphEventMetric()); + result = json(mapper, result, "topologyGraphReasonsEvent", + service.topologyGraphReasonsEventMetric()); + print("%s", result); + } else { + printEventMetric("Device", service.topologyDeviceEventMetric()); + printEventMetric("Host", service.topologyHostEventMetric()); + printEventMetric("Link", service.topologyLinkEventMetric()); + printEventMetric("Graph", service.topologyGraphEventMetric()); + printEventMetric("Graph Reasons", + service.topologyGraphReasonsEventMetric()); + } + } + + /** + * Produces JSON node for an Event Metric. + * + * @param mapper the JSON object mapper to use + * @param objectNode the JSON object node to use + * @param propertyPrefix the property prefix to use + * @param eventMetric the Event Metric with the data + * @return JSON object node for the Event Metric + */ + private ObjectNode json(ObjectMapper mapper, ObjectNode objectNode, + String propertyPrefix, EventMetric eventMetric) { + String gaugeName = propertyPrefix + "Timestamp"; + String meterName = propertyPrefix + "Rate"; + Gauge<Long> gauge = eventMetric.lastEventTimestampGauge(); + Meter meter = eventMetric.eventRateMeter(); + + objectNode.set(gaugeName, json(mapper, gauge)); + objectNode.set(meterName, json(mapper, meter)); + return objectNode; + } + + /** + * Produces JSON node for an Object. + * + * @param mapper the JSON object mapper to use + * @param object the Object with the data + * @return JSON node for the Object + */ + private JsonNode json(ObjectMapper mapper, Object object) { + // + // NOTE: The API for custom serializers is incomplete, + // hence we have to parse the JSON string to create JsonNode. + // + try { + final String objectJson = mapper.writeValueAsString(object); + JsonNode jsonNode = mapper.readTree(objectJson); + return jsonNode; + } catch (JsonProcessingException e) { + log.error("Error writing value as JSON string", e); + } catch (IOException e) { + log.error("Error writing value as JSON string", e); + } + return null; + } + + /** + * Prints an Event Metric. + * + * @param operationStr the string with the intent operation to print + * @param eventMetric the Event Metric to print + */ + private void printEventMetric(String operationStr, + EventMetric eventMetric) { + Gauge<Long> gauge = eventMetric.lastEventTimestampGauge(); + Meter meter = eventMetric.eventRateMeter(); + TimeUnit rateUnit = TimeUnit.SECONDS; + double rateFactor = rateUnit.toSeconds(1); + + // Print the Gauge + print(FORMAT_GAUGE, operationStr, gauge.getValue()); + + // Print the Meter + print(FORMAT_METER, operationStr, meter.getCount(), + meter.getMeanRate() * rateFactor, + meter.getOneMinuteRate() * rateFactor, + meter.getFiveMinuteRate() * rateFactor, + meter.getFifteenMinuteRate() * rateFactor); + } +} diff --git a/framework/src/onos/apps/metrics/src/main/java/org/onosproject/metrics/topology/cli/package-info.java b/framework/src/onos/apps/metrics/src/main/java/org/onosproject/metrics/topology/cli/package-info.java new file mode 100644 index 00000000..f8402bea --- /dev/null +++ b/framework/src/onos/apps/metrics/src/main/java/org/onosproject/metrics/topology/cli/package-info.java @@ -0,0 +1,20 @@ +/* + * 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. + */ + +/** + * Topology metrics command-line handlers. + */ +package org.onosproject.metrics.topology.cli; diff --git a/framework/src/onos/apps/metrics/src/main/java/org/onosproject/metrics/topology/package-info.java b/framework/src/onos/apps/metrics/src/main/java/org/onosproject/metrics/topology/package-info.java new file mode 100644 index 00000000..b35d85ce --- /dev/null +++ b/framework/src/onos/apps/metrics/src/main/java/org/onosproject/metrics/topology/package-info.java @@ -0,0 +1,20 @@ +/* + * 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. + */ + +/** + * ONOS Topology Metrics Application that collects topology-related metrics. + */ +package org.onosproject.metrics.topology; diff --git a/framework/src/onos/apps/metrics/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/framework/src/onos/apps/metrics/src/main/resources/OSGI-INF/blueprint/shell-config.xml new file mode 100644 index 00000000..c2d82418 --- /dev/null +++ b/framework/src/onos/apps/metrics/src/main/resources/OSGI-INF/blueprint/shell-config.xml @@ -0,0 +1,32 @@ +<!-- + ~ 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. + --> +<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.metrics.intent.cli.IntentEventsListCommand"/> + </command> + <command> + <action class="org.onosproject.metrics.intent.cli.IntentEventsMetricsCommand"/> + </command> + + <command> + <action class="org.onosproject.metrics.topology.cli.TopologyEventsListCommand"/> + </command> + <command> + <action class="org.onosproject.metrics.topology.cli.TopologyEventsMetricsCommand"/> + </command> + </command-bundle> +</blueprint> |