[ONOS-7639] intents-diagnosis CLI (initial implementation for P2P-flow rule intent)
Change-Id: Iba668809c6d2ad20fd86fb703f4d8e211acf2f14
diff --git a/cli/src/main/java/org/onosproject/cli/net/IntentsDiagnosisCommand.java b/cli/src/main/java/org/onosproject/cli/net/IntentsDiagnosisCommand.java
new file mode 100644
index 0000000..1518137
--- /dev/null
+++ b/cli/src/main/java/org/onosproject/cli/net/IntentsDiagnosisCommand.java
@@ -0,0 +1,694 @@
+/*
+ * Copyright 2015-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.cli.net;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.SetMultimap;
+import com.google.common.collect.Streams;
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.AnnotationKeys;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultDevice;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.LinkKey;
+import org.onosproject.net.Port;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.device.PortStatistics;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.intent.FlowRuleIntent;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.intent.Key;
+import org.onosproject.net.intent.ObjectiveTrackerService;
+import org.onosproject.net.intent.PointToPointIntent;
+import org.onosproject.net.intent.WorkPartitionService;
+import org.onosproject.net.statistic.FlowStatisticService;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Stream;
+
+@Command(scope = "onos", name = "intents-diagnosis",
+ description = "Diagnosis intents")
+public class IntentsDiagnosisCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "key",
+ description = "Intent key",
+ required = false, multiValued = false)
+ String key = null;
+
+ @Option(name = "-d", aliases = "--details", description = "printing intent details",
+ required = false, multiValued = false)
+ private boolean dump = false;
+
+ @Option(name = "-l", aliases = "--link", description = "printing local intentsByLink",
+ required = false, multiValued = false)
+ private boolean dumpIntentByLink = false;
+
+ private static final int MAX_INTENT_PATH = 100;
+ private static final String FIELD_INTENTS_BY_LINK = "intentsByLink";
+
+ @Override
+ protected void execute() {
+
+ print("intents-diagnosis");
+ ServiceRefs svcRefs = buildServiceRefs();
+ if (svcRefs == null) {
+ return;
+ }
+ try {
+ for (Intent intent : svcRefs.intentsService().getIntents()) {
+ if (key != null && !intent.key().toString().equals(key)) {
+ continue;
+ }
+ print("");
+ printIntentHdr(intent, svcRefs);
+ if (intent instanceof PointToPointIntent) {
+ diagnosisP2Pintent((PointToPointIntent) intent, svcRefs);
+ } else {
+ // TODO : it needs to implement other types of intent
+ print(" It doesn't support %s intent.", intent.getClass().getSimpleName());
+ }
+ }
+ if (dumpIntentByLink) {
+ dumpIntentsByLink(svcRefs);
+ }
+ } catch (Exception e) {
+ print("error: " + e);
+ }
+
+ }
+
+ private void printIntentHdr(Intent intent, ServiceRefs svcRefs) {
+ print("* intent key: %s", intent.key());
+ print(" - state: %s", svcRefs.intentsService().getIntentState(intent.key()));
+ dump(" - leader: %s %s", svcRefs.getWorkPartitionService().getLeader(intent.key(), Key::hash),
+ svcRefs.workPartitionService.isMine(intent.key(), Key::hash) ? "(Mine)" : "");
+ }
+
+ private void dumpIntentsByLink(ServiceRefs svcRefs) throws Exception {
+ Set<Map.Entry<LinkKey, Key>> intentsByLink = getIntentsByLinkSet(svcRefs);
+
+ print("* intentsbylink:");
+ for (Map.Entry<LinkKey, Key> entry : intentsByLink) {
+ print(" - %s, Intents: %s ", entry.getKey(), entry.getValue());
+ }
+ }
+
+ private Set<Map.Entry<LinkKey, Key>> getIntentsByLinkSet(ServiceRefs svcRefs) throws Exception {
+
+ ObjectiveTrackerService objTracker = svcRefs.getObjectiveTrackerService();
+
+ // Utilizing reflection instead of adding new interface for getting intentsByLink
+ Field f = objTracker.getClass().getDeclaredField(FIELD_INTENTS_BY_LINK);
+ f.setAccessible(true);
+ SetMultimap<LinkKey, Key> intentsByLink = (SetMultimap<LinkKey, Key>) f.get(objTracker);
+
+ return ImmutableSet.copyOf(intentsByLink.entries());
+ }
+
+ private void diagnosisP2Pintent(PointToPointIntent intent, ServiceRefs svcRefs) throws Exception {
+
+ List<Intent> installableIntents = svcRefs.intentsService().getInstallableIntents(intent.key());
+
+ if (installableIntents.size() == 0) {
+ error("NO INSTALLABLE INTENTS");
+ return;
+ }
+
+ Set<String> notSupport = new HashSet<>();
+ for (Intent installable: installableIntents) {
+ if (installable instanceof FlowRuleIntent) {
+ checkP2PFlowRuleIntent(intent, (FlowRuleIntent) installable, svcRefs);
+ } else {
+ // TODO : it needs to implement other types of installables
+ notSupport.add(installable.getClass().getSimpleName());
+ }
+ }
+
+ if (notSupport.size() > 0) {
+ print(" It doesn't support %s.", notSupport);
+ }
+ }
+
+ private void checkP2PFlowRuleIntent(PointToPointIntent intent, FlowRuleIntent installable, ServiceRefs svcRefs)
+ throws Exception {
+
+ final Map<DeviceId, DeviceOnIntent> devs = createDevicesOnP2PIntent(intent, installable);
+
+ boolean errorOccurred = false;
+ // checking the number of links & CPs in P2P intent
+ for (DeviceOnIntent dev: devs.values()) {
+ if (dev.getIngressLinks().size() > 1) {
+ error("MULTIPLE NUMBER OF INGRESS LINKs on " + dev.deviceId()
+ + ": " + dev.getIngressLinks());
+ errorOccurred = true;
+ }
+ if (dev.getIngressCps().size() > 1) {
+ error("MULTIPLE NUMBER OF INGRESS CONNECT POINTs on " + dev.deviceId()
+ + ": " + dev.getIngressCps());
+ errorOccurred = true;
+ }
+ if (dev.getEgressLinks().size() > 1) {
+ error("MULTIPLE NUMBER OF EGRESS LINKs: on " + dev.deviceId()
+ + ": " + dev.getEgressLinks());
+ errorOccurred = true;
+ }
+ if (dev.getEgressCps().size() > 1) {
+ error("MULTIPLE NUMBER OF EGRESS CONNECT POINTs: on " + dev.deviceId()
+ + ": " + dev.getEgressCps());
+ errorOccurred = true;
+ }
+ }
+
+ ConnectPoint startCp = intent.filteredIngressPoint().connectPoint();
+ DeviceOnIntent startDev = devs.get(startCp.deviceId());
+ if (startDev == null) {
+ error("STARTING CONNECT POINT DEVICE: " + startCp.deviceId() + " is not on intent");
+ errorOccurred = true;
+ }
+
+ ConnectPoint endCp = intent.filteredEgressPoint().connectPoint();
+ DeviceOnIntent endDev = devs.get(endCp.deviceId());
+ if (endDev == null) {
+ error("END CONNECT POINT DEVICE: " + endCp.deviceId() + " is not on intent");
+ errorOccurred = true;
+ }
+
+ if (!errorOccurred) {
+ // Per device checking with path-order
+ DeviceOnIntent dev = startDev;
+ int i = 0;
+ for (; i < MAX_INTENT_PATH; i++) {
+ perDeviceChecking(dev, svcRefs);
+
+ // P2P intent has only 1 egress CP
+ ConnectPoint egressCp = dev.getEgressCps().stream().findFirst().orElse(null);
+ if (egressCp != null && Objects.equals(endCp, egressCp)) {
+ break;
+ }
+
+ // P2P intent has only 1 egress link
+ Link egressLink = dev.getEgressLinks().stream().findFirst().orElse(null);
+ if (egressLink == null) {
+ error("INVALID EGRESS LINK & CONNECT POINT for: " + dev);
+ errorOccurred = true;
+ break;
+ }
+ if (Objects.equals(egressLink.dst(), endCp)) {
+ break;
+ }
+
+ // P2P intent only 1 ingress link
+ dev = devs.values().stream()
+ .filter(nextDev -> Objects.equals(
+ egressLink, nextDev.getIngressLinks().stream().findFirst().orElse(null)))
+ .findAny().orElse(null);
+ if (dev == null) {
+ error("FAILED TO FIND NEXT DEV for: " + dev + ", LINK: " + egressLink);
+ errorOccurred = true;
+ break;
+ }
+ }
+ if (i == MAX_INTENT_PATH) {
+ error("MAX INTENT PATH WAS EXCEEDED");
+ errorOccurred = true;
+ }
+ }
+
+ if (errorOccurred) {
+ // Installable checking
+ dump("");
+ dump("ERROR OCCURRED. DO PER FLOW CHECKING");
+ perFlowRuleChecking(installable, svcRefs);
+ }
+
+ if (svcRefs.workPartitionService.isMine(intent.key(), Key::hash)) {
+ checkIntentsByLink(installable, svcRefs);
+ }
+ }
+
+ private void checkIntentsByLink(FlowRuleIntent installable, ServiceRefs svcRefs) throws Exception {
+
+ Set<Map.Entry<LinkKey, Key>> intentsByLink = getIntentsByLinkSet(svcRefs);
+
+ installable.resources().forEach(
+ rsrc -> {
+ if (rsrc instanceof Link) {
+ Link link = (Link) rsrc;
+ LinkKey linkKey = LinkKey.linkKey(link);
+ intentsByLink.stream()
+ .filter(entry -> Objects.equals(entry.getKey(), linkKey)
+ && Objects.equals(entry.getValue(), installable.key()))
+ .findAny()
+ .orElseGet(() -> {
+ error("FAILED TO FIND LINK(" + link + ") for intents: " + installable.key());
+ return null;
+ });
+ }
+ }
+ );
+ }
+
+ // TODO: It needs to consider FLowObjectiveIntent case
+ private void perDeviceChecking(DeviceOnIntent devOnIntent, ServiceRefs svcRefs) {
+
+ Collection<PortStatistics> portStats =
+ svcRefs.deviceService().getPortStatistics(devOnIntent.deviceId());
+ Collection<PortStatistics> portDeltaStats =
+ svcRefs.deviceService().getPortDeltaStatistics(devOnIntent.deviceId());
+
+ dump("");
+ dump(" ------------------------------------------------------------------------------------------");
+
+ Device device = svcRefs.deviceService.getDevice(devOnIntent.deviceId());
+ if (device == null) {
+ error("INVALID DEVICE for " + devOnIntent.deviceId());
+ return;
+ }
+
+ dump(" %s", getDeviceString(device));
+ dump(" %s", device.annotations());
+
+ devOnIntent.getIngressCps().stream()
+ .forEach(cp -> dumpCpStatistics(cp, portStats, portDeltaStats, "INGRESS", svcRefs));
+
+ Stream<FlowEntry> flowEntries = Streams.stream(svcRefs.flowService.getFlowEntries(devOnIntent.deviceId()));
+
+ devOnIntent.getFlowRules().stream()
+ .forEach(
+ intentFlowRule -> {
+ FlowEntry matchedEntry = flowEntries
+ .filter(entry -> Objects.equals(intentFlowRule.id(), entry.id()))
+ .findFirst().orElse(null);
+
+ if (matchedEntry == null) {
+ error("FAILED TO FIND FLOW ENTRY: for " + intentFlowRule);
+ return;
+ }
+
+ if (Objects.equals(intentFlowRule.selector(), matchedEntry.selector()) &&
+ Objects.equals(intentFlowRule.treatment(), matchedEntry.treatment())) {
+ dumpFlowEntry(matchedEntry, "FLOW ENTRY");
+ return;
+ }
+
+ error("INSTALLABLE-FLOW ENTRY mismatch");
+ dumpFlowRule(intentFlowRule, "INSTALLABLE");
+ dumpFlowEntry(matchedEntry, "FLOW ENTRY");
+ }
+ );
+
+ devOnIntent.getEgressCps().stream()
+ .forEach(
+ cp -> dumpCpStatistics(cp, portStats, portDeltaStats, "EGRESS", svcRefs)
+ );
+ }
+
+ // TODO: It needs to consider FLowObjectiveIntent case
+ private void perFlowRuleChecking(FlowRuleIntent installable, ServiceRefs svcRefs) {
+
+ installable.flowRules().forEach(
+ flowrule -> {
+ DeviceId devId = flowrule.deviceId();
+ if (devId == null) {
+ error("INVALID DEVICE ID for " + flowrule);
+ return;
+ }
+
+ Device dev = svcRefs.deviceService.getDevice(devId);
+ if (dev == null) {
+ error("INVALID DEVICE for " + flowrule);
+ return;
+ }
+
+ dump("");
+ dump(
+ " ------------------------------------------------------------------------------------------");
+ dump(" %s", getDeviceString(dev));
+ dump(" %s", dev.annotations());
+
+ svcRefs.flowService().getFlowEntries(devId)
+ .forEach(
+ entry -> {
+ dumpFlowRule(flowrule, "INSTALLABLE");
+ dumpFlowEntry(entry, "FLOW ENTRY");
+
+ if (!flowrule.selector().equals(entry.selector())) {
+ return;
+ }
+ if (flowrule.id().equals(entry.id()) &&
+ flowrule.treatment().equals(entry.treatment())) {
+ dumpFlowEntry(entry, "FLOW ENTRY");
+ return;
+ }
+ error("INSTALLABLE-FLOW ENTRY mismatch");
+ }
+ );
+ }
+ );
+ }
+
+ private Map<DeviceId, DeviceOnIntent> createDevicesOnP2PIntent(
+ PointToPointIntent intent, FlowRuleIntent flowRuleIntent) {
+
+ final Map<DeviceId, DeviceOnIntent> devMap = new HashMap<>();
+
+ flowRuleIntent.resources().forEach(
+ rsrc -> {
+ if (rsrc instanceof Link) {
+ Link link = (Link) rsrc;
+ ConnectPoint srcCp = link.src();
+ ConnectPoint dstCp = link.dst();
+ try {
+ DeviceOnIntent dev = devMap.computeIfAbsent(srcCp.deviceId(), DeviceOnIntent::new);
+ if (dev != null) {
+ dev.addEgressLink(link);
+ }
+ dev = devMap.computeIfAbsent(dstCp.deviceId(), DeviceOnIntent::new);
+ if (dev != null) {
+ dev.addIngressLink(link);
+ }
+ } catch (IllegalStateException e) {
+ print("error: " + e);
+ }
+ }
+ }
+ );
+
+ ConnectPoint startCp = intent.filteredIngressPoint().connectPoint();
+ DeviceOnIntent startDev = devMap.computeIfAbsent(startCp.deviceId(), DeviceOnIntent::new);
+ if (!startDev.hasIngressCp(startCp)) {
+ startDev.addIngressCp(startCp);
+ }
+
+ ConnectPoint endCp = intent.filteredEgressPoint().connectPoint();
+ DeviceOnIntent endDev = devMap.computeIfAbsent(endCp.deviceId(), DeviceOnIntent::new);
+ if (!endDev.hasEgressCp(endCp)) {
+ endDev.addEgessCp(endCp);
+ }
+
+ flowRuleIntent.flowRules().forEach(
+ flowRule -> {
+ DeviceId devId = flowRule.deviceId();
+ if (devId == null) {
+ error("INVALID DEVICE ID for " + flowRule);
+ return;
+ }
+ DeviceOnIntent dev = devMap.get(devId);
+ if (dev == null) {
+ error("DEVICE(" + devId + ") IS NOT ON INTENTS LINKS");
+ return;
+ }
+
+ dev.addFlowRule(flowRule);
+ }
+ );
+
+ return devMap;
+ }
+
+ private String getDeviceString(Device dev) {
+
+ StringBuilder buf = new StringBuilder();
+ if (dev != null) {
+ buf.append(String.format("Device: %s, ", dev.id()));
+ buf.append(String.format("%s, ", dev.type()));
+ buf.append(String.format("%s, ", dev.manufacturer()));
+ buf.append(String.format("%s, ", dev.hwVersion()));
+ buf.append(String.format("%s, ", dev.swVersion()));
+ if (dev instanceof DefaultDevice) {
+ DefaultDevice dfltDev = (DefaultDevice) dev;
+ if (dfltDev.driver() != null) {
+ buf.append(String.format("%s, ", dfltDev.driver().name()));
+ }
+ String channelId = dfltDev.annotations().value("channelId");
+ if (channelId != null) {
+ buf.append(String.format("%s, ", channelId));
+ }
+ }
+ }
+
+ return buf.toString();
+ }
+
+ private void dumpFlowRule(FlowRule rule, String hdr) {
+ dump(" " + hdr + ":");
+ dump(" - id=%s, priority=%d", rule.id(), rule.priority());
+ dump(" - %s", rule.selector());
+ dump(" - %s", rule.treatment());
+ }
+
+ private void dumpFlowEntry(FlowEntry entry, String hdr) {
+ dumpFlowRule(entry, hdr);
+ dump(" - packets=%d", entry.packets());
+ }
+
+
+ private void dumpCpStatistics(ConnectPoint cp, Collection<PortStatistics> devPortStats,
+ Collection<PortStatistics> devPortDeltaStats, String direction, ServiceRefs svcs) {
+ if (cp == null) {
+ return;
+ }
+
+ dump(" %s:", direction);
+
+ if (cp.port().isLogical()) {
+ dump(" - logical: device: %s, port: %s", cp.deviceId(), cp.port());
+ return;
+ }
+
+ Port port = svcs.deviceService.getPort(cp.deviceId(), cp.port());
+ if (port == null) {
+ return;
+ }
+
+ try {
+ devPortStats.stream()
+ .filter(stat -> stat.portNumber().equals(cp.port()))
+ .forEach(stat -> dump(" - stat : %s:", getPortStatStr(stat, port)));
+ } catch (IllegalStateException e) {
+ error("error: " + e);
+ return;
+ }
+
+ try {
+ devPortDeltaStats.stream()
+ .filter(stat -> stat.portNumber().equals(cp.port()))
+ .forEach(stat -> dump(" - delta : %s:", getPortStatStr(stat, port)));
+ } catch (IllegalStateException e) {
+ error("error: " + e);
+ }
+ }
+
+ private void dump(String format, Object ... args) {
+ if (dump) {
+ print(format, args);
+ }
+ }
+
+ private String getPortStatStr(PortStatistics stat, Port port) {
+
+ final String portName = port.annotations().value(AnnotationKeys.PORT_NAME);
+
+ return String.format("port: %s(%s), ", stat.portNumber(), portName) +
+ String.format("enabled: %b, ", port.isEnabled()) +
+ String.format("pktRx: %d, ", stat.packetsReceived()) +
+ String.format("pktTx: %d, ", stat.packetsSent()) +
+ String.format("pktRxErr: %d, ", stat.packetsRxErrors()) +
+ String.format("pktTxErr: %d, ", stat.packetsTxErrors()) +
+ String.format("pktRxDrp: %d, ", stat.packetsRxDropped()) +
+ String.format("pktTxDrp: %d", stat.packetsTxDropped());
+ }
+
+ private static class DeviceOnIntent {
+
+ private final DeviceId devId;
+
+ private Collection<Link> ingressLinks = new ArrayList<>();
+
+ private Collection<Link> egressLinks = new ArrayList<>();
+
+ private Collection<ConnectPoint> ingressCps = new ArrayList<>();
+
+ private Collection<ConnectPoint> egressCps = new ArrayList<>();
+
+ private Collection<FlowRule> flowRules = new ArrayList<>();
+
+ public DeviceOnIntent(DeviceId devId) {
+ this.devId = devId;
+ }
+
+ public DeviceId deviceId() {
+ return devId;
+ }
+
+ public Collection<Link> getIngressLinks() {
+ return ingressLinks;
+ }
+
+ public Collection<Link> getEgressLinks() {
+ return egressLinks;
+ }
+
+ public void addIngressLink(Link link) {
+ ingressLinks.add(link);
+ addIngressCp(link.dst());
+ }
+
+ public void addEgressLink(Link link) {
+ egressLinks.add(link);
+ addEgessCp(link.src());
+ }
+
+ public void addIngressCp(ConnectPoint cp) {
+ ingressCps.add(cp);
+ }
+
+ public void addEgessCp(ConnectPoint cp) {
+ egressCps.add(cp);
+ }
+
+ public boolean hasIngressCp(final ConnectPoint cp) {
+ return ingressCps.stream().anyMatch(icp -> Objects.equals(icp, cp));
+ }
+
+ public boolean hasEgressCp(ConnectPoint cp) {
+ return egressCps.stream().anyMatch(ecp -> Objects.equals(ecp, cp));
+ }
+
+ public Collection<ConnectPoint> getIngressCps() {
+ return ingressCps;
+ }
+
+ public Collection<ConnectPoint> getEgressCps() {
+ return egressCps;
+ }
+
+ public Collection<FlowRule> getFlowRules() {
+ return flowRules;
+ }
+
+ public void addFlowRule(FlowRule flowRule) {
+ flowRules.add(flowRule);
+ }
+
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .omitNullValues()
+ .add("devId", devId)
+ .add("ingressLinks", ingressLinks)
+ .add("egressLinks", egressLinks)
+ .add("flowRules", flowRules)
+ .toString();
+ }
+ }
+
+
+ private ServiceRefs buildServiceRefs() {
+ IntentService intentsService = get(IntentService.class);
+ if (intentsService == null) {
+ return null;
+ }
+ DeviceService deviceService = get(DeviceService.class);
+ if (deviceService == null) {
+ return null;
+ }
+ FlowStatisticService flowStatsService = get(FlowStatisticService.class);
+ if (flowStatsService == null) {
+ return null;
+ }
+ FlowRuleService flowService = get(FlowRuleService.class);
+ if (flowService == null) {
+ return null;
+ }
+ WorkPartitionService workPartitionService = get(WorkPartitionService.class);
+ if (workPartitionService == null) {
+ return null;
+ }
+ ObjectiveTrackerService objectiveTrackerService = get(ObjectiveTrackerService.class);
+ if (objectiveTrackerService == null) {
+ return null;
+ }
+
+ return new ServiceRefs(
+ intentsService,
+ deviceService,
+ flowService,
+ workPartitionService,
+ objectiveTrackerService
+ );
+ }
+
+ private static final class ServiceRefs {
+
+ private IntentService intentsService;
+ private DeviceService deviceService;
+ private FlowRuleService flowService;
+ private WorkPartitionService workPartitionService;
+ private ObjectiveTrackerService objectiveTrackerService;
+
+ private ServiceRefs(
+ IntentService intentsService,
+ DeviceService deviceService,
+ FlowRuleService flowService,
+ WorkPartitionService workPartitionService,
+ ObjectiveTrackerService objectiveTrackerService
+ ) {
+ this.intentsService = intentsService;
+ this.deviceService = deviceService;
+ this.flowService = flowService;
+ this.workPartitionService = workPartitionService;
+ this.objectiveTrackerService = objectiveTrackerService;
+ }
+
+ public IntentService intentsService() {
+ return intentsService;
+ }
+
+ public DeviceService deviceService() {
+ return deviceService;
+ }
+
+ public FlowRuleService flowService() {
+ return flowService;
+ }
+
+ public WorkPartitionService getWorkPartitionService() {
+ return workPartitionService;
+ }
+
+ public ObjectiveTrackerService getObjectiveTrackerService() {
+ return objectiveTrackerService;
+ }
+ }
+
+}
diff --git a/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index 6ca9c5a..0633c44 100644
--- a/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -303,6 +303,12 @@
<action class="org.onosproject.cli.net.IntentsListCommand"/>
</command>
<command>
+ <action class="org.onosproject.cli.net.IntentsDiagnosisCommand"/>
+ <completers>
+ <ref component-id="intentKeyCompleter"/>
+ </completers>
+ </command>
+ <command>
<action class="org.onosproject.cli.net.IntentListCompilers"/>
</command>
<command>