blob: 1dc1d8de066ffcadca0b21e12008ef8d837c48ca [file] [log] [blame]
jaegonkim185299e2018-04-29 20:15:25 +09001/*
2 * Copyright 2015-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.cli.net;
17
18import com.google.common.base.MoreObjects;
19import com.google.common.collect.ImmutableSet;
20import com.google.common.collect.SetMultimap;
21import com.google.common.collect.Streams;
22import org.apache.karaf.shell.commands.Argument;
23import org.apache.karaf.shell.commands.Command;
24import org.apache.karaf.shell.commands.Option;
25import org.onosproject.cli.AbstractShellCommand;
26import org.onosproject.net.AnnotationKeys;
27import org.onosproject.net.ConnectPoint;
28import org.onosproject.net.DefaultDevice;
29import org.onosproject.net.Device;
30import org.onosproject.net.DeviceId;
31import org.onosproject.net.Link;
32import org.onosproject.net.LinkKey;
33import org.onosproject.net.Port;
34import org.onosproject.net.device.DeviceService;
35import org.onosproject.net.device.PortStatistics;
36import org.onosproject.net.flow.FlowEntry;
37import org.onosproject.net.flow.FlowRule;
38import org.onosproject.net.flow.FlowRuleService;
39import org.onosproject.net.intent.FlowRuleIntent;
40import org.onosproject.net.intent.Intent;
41import org.onosproject.net.intent.IntentService;
42import org.onosproject.net.intent.Key;
43import org.onosproject.net.intent.ObjectiveTrackerService;
44import org.onosproject.net.intent.PointToPointIntent;
45import org.onosproject.net.intent.WorkPartitionService;
46import org.onosproject.net.statistic.FlowStatisticService;
47
48import java.lang.reflect.Field;
49import java.util.ArrayList;
50import java.util.Collection;
51import java.util.HashMap;
52import java.util.HashSet;
53import java.util.List;
54import java.util.Map;
55import java.util.Objects;
56import java.util.Set;
57import java.util.stream.Stream;
58
59@Command(scope = "onos", name = "intents-diagnosis",
60 description = "Diagnosis intents")
61public class IntentsDiagnosisCommand extends AbstractShellCommand {
62
63 @Argument(index = 0, name = "key",
64 description = "Intent key",
65 required = false, multiValued = false)
66 String key = null;
67
68 @Option(name = "-d", aliases = "--details", description = "printing intent details",
69 required = false, multiValued = false)
70 private boolean dump = false;
71
72 @Option(name = "-l", aliases = "--link", description = "printing local intentsByLink",
73 required = false, multiValued = false)
74 private boolean dumpIntentByLink = false;
75
76 private static final int MAX_INTENT_PATH = 100;
77 private static final String FIELD_INTENTS_BY_LINK = "intentsByLink";
78
79 @Override
80 protected void execute() {
81
82 print("intents-diagnosis");
83 ServiceRefs svcRefs = buildServiceRefs();
84 if (svcRefs == null) {
85 return;
86 }
87 try {
88 for (Intent intent : svcRefs.intentsService().getIntents()) {
89 if (key != null && !intent.key().toString().equals(key)) {
90 continue;
91 }
92 print("");
93 printIntentHdr(intent, svcRefs);
94 if (intent instanceof PointToPointIntent) {
95 diagnosisP2Pintent((PointToPointIntent) intent, svcRefs);
96 } else {
97 // TODO : it needs to implement other types of intent
98 print(" It doesn't support %s intent.", intent.getClass().getSimpleName());
99 }
100 }
101 if (dumpIntentByLink) {
102 dumpIntentsByLink(svcRefs);
103 }
104 } catch (Exception e) {
105 print("error: " + e);
106 }
107
108 }
109
110 private void printIntentHdr(Intent intent, ServiceRefs svcRefs) {
111 print("* intent key: %s", intent.key());
112 print(" - state: %s", svcRefs.intentsService().getIntentState(intent.key()));
113 dump(" - leader: %s %s", svcRefs.getWorkPartitionService().getLeader(intent.key(), Key::hash),
114 svcRefs.workPartitionService.isMine(intent.key(), Key::hash) ? "(Mine)" : "");
115 }
116
Ray Milkey30ab5152018-05-08 09:29:28 -0700117 private void dumpIntentsByLink(ServiceRefs svcRefs) {
jaegonkim185299e2018-04-29 20:15:25 +0900118 Set<Map.Entry<LinkKey, Key>> intentsByLink = getIntentsByLinkSet(svcRefs);
119
120 print("* intentsbylink:");
121 for (Map.Entry<LinkKey, Key> entry : intentsByLink) {
122 print(" - %s, Intents: %s ", entry.getKey(), entry.getValue());
123 }
124 }
125
Ray Milkey30ab5152018-05-08 09:29:28 -0700126 private Set<Map.Entry<LinkKey, Key>> getIntentsByLinkSet(ServiceRefs svcRefs) {
jaegonkim185299e2018-04-29 20:15:25 +0900127
Ray Milkey30ab5152018-05-08 09:29:28 -0700128 try {
jaegonkim185299e2018-04-29 20:15:25 +0900129
Ray Milkey30ab5152018-05-08 09:29:28 -0700130 ObjectiveTrackerService objTracker = svcRefs.getObjectiveTrackerService();
jaegonkim185299e2018-04-29 20:15:25 +0900131
Ray Milkey30ab5152018-05-08 09:29:28 -0700132 // Utilizing reflection instead of adding new interface for getting intentsByLink
133 Field f = objTracker.getClass().getDeclaredField(FIELD_INTENTS_BY_LINK);
134 f.setAccessible(true);
135 SetMultimap<LinkKey, Key> intentsByLink = (SetMultimap<LinkKey, Key>) f.get(objTracker);
136
137 return ImmutableSet.copyOf(intentsByLink.entries());
138 } catch (NoSuchFieldException | IllegalAccessException ex) {
139 error("error: " + ex);
140 return ImmutableSet.of();
141 }
jaegonkim185299e2018-04-29 20:15:25 +0900142 }
143
Ray Milkey30ab5152018-05-08 09:29:28 -0700144 private void diagnosisP2Pintent(PointToPointIntent intent, ServiceRefs svcRefs) {
jaegonkim185299e2018-04-29 20:15:25 +0900145
146 List<Intent> installableIntents = svcRefs.intentsService().getInstallableIntents(intent.key());
147
148 if (installableIntents.size() == 0) {
149 error("NO INSTALLABLE INTENTS");
150 return;
151 }
152
153 Set<String> notSupport = new HashSet<>();
154 for (Intent installable: installableIntents) {
155 if (installable instanceof FlowRuleIntent) {
156 checkP2PFlowRuleIntent(intent, (FlowRuleIntent) installable, svcRefs);
157 } else {
158 // TODO : it needs to implement other types of installables
159 notSupport.add(installable.getClass().getSimpleName());
160 }
161 }
162
163 if (notSupport.size() > 0) {
164 print(" It doesn't support %s.", notSupport);
165 }
166 }
167
Ray Milkey30ab5152018-05-08 09:29:28 -0700168 private void checkP2PFlowRuleIntent(PointToPointIntent intent, FlowRuleIntent installable, ServiceRefs svcRefs) {
jaegonkim185299e2018-04-29 20:15:25 +0900169
170 final Map<DeviceId, DeviceOnIntent> devs = createDevicesOnP2PIntent(intent, installable);
171
172 boolean errorOccurred = false;
173 // checking the number of links & CPs in P2P intent
174 for (DeviceOnIntent dev: devs.values()) {
175 if (dev.getIngressLinks().size() > 1) {
176 error("MULTIPLE NUMBER OF INGRESS LINKs on " + dev.deviceId()
177 + ": " + dev.getIngressLinks());
178 errorOccurred = true;
179 }
180 if (dev.getIngressCps().size() > 1) {
181 error("MULTIPLE NUMBER OF INGRESS CONNECT POINTs on " + dev.deviceId()
182 + ": " + dev.getIngressCps());
183 errorOccurred = true;
184 }
185 if (dev.getEgressLinks().size() > 1) {
186 error("MULTIPLE NUMBER OF EGRESS LINKs: on " + dev.deviceId()
187 + ": " + dev.getEgressLinks());
188 errorOccurred = true;
189 }
190 if (dev.getEgressCps().size() > 1) {
191 error("MULTIPLE NUMBER OF EGRESS CONNECT POINTs: on " + dev.deviceId()
192 + ": " + dev.getEgressCps());
193 errorOccurred = true;
194 }
195 }
196
197 ConnectPoint startCp = intent.filteredIngressPoint().connectPoint();
198 DeviceOnIntent startDev = devs.get(startCp.deviceId());
199 if (startDev == null) {
200 error("STARTING CONNECT POINT DEVICE: " + startCp.deviceId() + " is not on intent");
201 errorOccurred = true;
202 }
203
204 ConnectPoint endCp = intent.filteredEgressPoint().connectPoint();
205 DeviceOnIntent endDev = devs.get(endCp.deviceId());
206 if (endDev == null) {
207 error("END CONNECT POINT DEVICE: " + endCp.deviceId() + " is not on intent");
208 errorOccurred = true;
209 }
210
211 if (!errorOccurred) {
212 // Per device checking with path-order
213 DeviceOnIntent dev = startDev;
214 int i = 0;
215 for (; i < MAX_INTENT_PATH; i++) {
216 perDeviceChecking(dev, svcRefs);
217
218 // P2P intent has only 1 egress CP
219 ConnectPoint egressCp = dev.getEgressCps().stream().findFirst().orElse(null);
220 if (egressCp != null && Objects.equals(endCp, egressCp)) {
221 break;
222 }
223
224 // P2P intent has only 1 egress link
225 Link egressLink = dev.getEgressLinks().stream().findFirst().orElse(null);
226 if (egressLink == null) {
227 error("INVALID EGRESS LINK & CONNECT POINT for: " + dev);
228 errorOccurred = true;
229 break;
230 }
231 if (Objects.equals(egressLink.dst(), endCp)) {
232 break;
233 }
234
235 // P2P intent only 1 ingress link
236 dev = devs.values().stream()
237 .filter(nextDev -> Objects.equals(
238 egressLink, nextDev.getIngressLinks().stream().findFirst().orElse(null)))
239 .findAny().orElse(null);
240 if (dev == null) {
241 error("FAILED TO FIND NEXT DEV for: " + dev + ", LINK: " + egressLink);
242 errorOccurred = true;
243 break;
244 }
245 }
246 if (i == MAX_INTENT_PATH) {
247 error("MAX INTENT PATH WAS EXCEEDED");
248 errorOccurred = true;
249 }
250 }
251
252 if (errorOccurred) {
253 // Installable checking
254 dump("");
255 dump("ERROR OCCURRED. DO PER FLOW CHECKING");
256 perFlowRuleChecking(installable, svcRefs);
257 }
258
259 if (svcRefs.workPartitionService.isMine(intent.key(), Key::hash)) {
260 checkIntentsByLink(installable, svcRefs);
261 }
262 }
263
Ray Milkey30ab5152018-05-08 09:29:28 -0700264 private void checkIntentsByLink(FlowRuleIntent installable, ServiceRefs svcRefs) {
jaegonkim185299e2018-04-29 20:15:25 +0900265
266 Set<Map.Entry<LinkKey, Key>> intentsByLink = getIntentsByLinkSet(svcRefs);
267
268 installable.resources().forEach(
269 rsrc -> {
270 if (rsrc instanceof Link) {
271 Link link = (Link) rsrc;
272 LinkKey linkKey = LinkKey.linkKey(link);
273 intentsByLink.stream()
274 .filter(entry -> Objects.equals(entry.getKey(), linkKey)
275 && Objects.equals(entry.getValue(), installable.key()))
276 .findAny()
277 .orElseGet(() -> {
278 error("FAILED TO FIND LINK(" + link + ") for intents: " + installable.key());
279 return null;
280 });
281 }
282 }
283 );
284 }
285
286 // TODO: It needs to consider FLowObjectiveIntent case
287 private void perDeviceChecking(DeviceOnIntent devOnIntent, ServiceRefs svcRefs) {
288
289 Collection<PortStatistics> portStats =
290 svcRefs.deviceService().getPortStatistics(devOnIntent.deviceId());
291 Collection<PortStatistics> portDeltaStats =
292 svcRefs.deviceService().getPortDeltaStatistics(devOnIntent.deviceId());
293
294 dump("");
295 dump(" ------------------------------------------------------------------------------------------");
296
297 Device device = svcRefs.deviceService.getDevice(devOnIntent.deviceId());
298 if (device == null) {
299 error("INVALID DEVICE for " + devOnIntent.deviceId());
300 return;
301 }
302
303 dump(" %s", getDeviceString(device));
304 dump(" %s", device.annotations());
305
306 devOnIntent.getIngressCps().stream()
307 .forEach(cp -> dumpCpStatistics(cp, portStats, portDeltaStats, "INGRESS", svcRefs));
308
309 Stream<FlowEntry> flowEntries = Streams.stream(svcRefs.flowService.getFlowEntries(devOnIntent.deviceId()));
310
311 devOnIntent.getFlowRules().stream()
312 .forEach(
313 intentFlowRule -> {
314 FlowEntry matchedEntry = flowEntries
315 .filter(entry -> Objects.equals(intentFlowRule.id(), entry.id()))
316 .findFirst().orElse(null);
317
318 if (matchedEntry == null) {
319 error("FAILED TO FIND FLOW ENTRY: for " + intentFlowRule);
320 return;
321 }
322
323 if (Objects.equals(intentFlowRule.selector(), matchedEntry.selector()) &&
324 Objects.equals(intentFlowRule.treatment(), matchedEntry.treatment())) {
325 dumpFlowEntry(matchedEntry, "FLOW ENTRY");
326 return;
327 }
328
329 error("INSTALLABLE-FLOW ENTRY mismatch");
330 dumpFlowRule(intentFlowRule, "INSTALLABLE");
331 dumpFlowEntry(matchedEntry, "FLOW ENTRY");
332 }
333 );
334
335 devOnIntent.getEgressCps().stream()
336 .forEach(
337 cp -> dumpCpStatistics(cp, portStats, portDeltaStats, "EGRESS", svcRefs)
338 );
339 }
340
341 // TODO: It needs to consider FLowObjectiveIntent case
342 private void perFlowRuleChecking(FlowRuleIntent installable, ServiceRefs svcRefs) {
343
344 installable.flowRules().forEach(
345 flowrule -> {
346 DeviceId devId = flowrule.deviceId();
347 if (devId == null) {
348 error("INVALID DEVICE ID for " + flowrule);
349 return;
350 }
351
352 Device dev = svcRefs.deviceService.getDevice(devId);
353 if (dev == null) {
354 error("INVALID DEVICE for " + flowrule);
355 return;
356 }
357
358 dump("");
359 dump(
360 " ------------------------------------------------------------------------------------------");
361 dump(" %s", getDeviceString(dev));
362 dump(" %s", dev.annotations());
363
364 svcRefs.flowService().getFlowEntries(devId)
365 .forEach(
366 entry -> {
367 dumpFlowRule(flowrule, "INSTALLABLE");
368 dumpFlowEntry(entry, "FLOW ENTRY");
369
370 if (!flowrule.selector().equals(entry.selector())) {
371 return;
372 }
373 if (flowrule.id().equals(entry.id()) &&
374 flowrule.treatment().equals(entry.treatment())) {
375 dumpFlowEntry(entry, "FLOW ENTRY");
376 return;
377 }
378 error("INSTALLABLE-FLOW ENTRY mismatch");
379 }
380 );
381 }
382 );
383 }
384
385 private Map<DeviceId, DeviceOnIntent> createDevicesOnP2PIntent(
386 PointToPointIntent intent, FlowRuleIntent flowRuleIntent) {
387
388 final Map<DeviceId, DeviceOnIntent> devMap = new HashMap<>();
389
390 flowRuleIntent.resources().forEach(
391 rsrc -> {
392 if (rsrc instanceof Link) {
393 Link link = (Link) rsrc;
394 ConnectPoint srcCp = link.src();
395 ConnectPoint dstCp = link.dst();
396 try {
397 DeviceOnIntent dev = devMap.computeIfAbsent(srcCp.deviceId(), DeviceOnIntent::new);
Ray Milkey30ab5152018-05-08 09:29:28 -0700398 dev.addEgressLink(link);
399
jaegonkim185299e2018-04-29 20:15:25 +0900400 dev = devMap.computeIfAbsent(dstCp.deviceId(), DeviceOnIntent::new);
Ray Milkey30ab5152018-05-08 09:29:28 -0700401 dev.addIngressLink(link);
jaegonkim185299e2018-04-29 20:15:25 +0900402 } catch (IllegalStateException e) {
403 print("error: " + e);
404 }
405 }
406 }
407 );
408
409 ConnectPoint startCp = intent.filteredIngressPoint().connectPoint();
410 DeviceOnIntent startDev = devMap.computeIfAbsent(startCp.deviceId(), DeviceOnIntent::new);
411 if (!startDev.hasIngressCp(startCp)) {
412 startDev.addIngressCp(startCp);
413 }
414
415 ConnectPoint endCp = intent.filteredEgressPoint().connectPoint();
416 DeviceOnIntent endDev = devMap.computeIfAbsent(endCp.deviceId(), DeviceOnIntent::new);
417 if (!endDev.hasEgressCp(endCp)) {
418 endDev.addEgessCp(endCp);
419 }
420
421 flowRuleIntent.flowRules().forEach(
422 flowRule -> {
423 DeviceId devId = flowRule.deviceId();
424 if (devId == null) {
425 error("INVALID DEVICE ID for " + flowRule);
426 return;
427 }
428 DeviceOnIntent dev = devMap.get(devId);
429 if (dev == null) {
430 error("DEVICE(" + devId + ") IS NOT ON INTENTS LINKS");
431 return;
432 }
433
434 dev.addFlowRule(flowRule);
435 }
436 );
437
438 return devMap;
439 }
440
441 private String getDeviceString(Device dev) {
442
443 StringBuilder buf = new StringBuilder();
444 if (dev != null) {
445 buf.append(String.format("Device: %s, ", dev.id()));
446 buf.append(String.format("%s, ", dev.type()));
447 buf.append(String.format("%s, ", dev.manufacturer()));
448 buf.append(String.format("%s, ", dev.hwVersion()));
449 buf.append(String.format("%s, ", dev.swVersion()));
450 if (dev instanceof DefaultDevice) {
451 DefaultDevice dfltDev = (DefaultDevice) dev;
452 if (dfltDev.driver() != null) {
453 buf.append(String.format("%s, ", dfltDev.driver().name()));
454 }
455 String channelId = dfltDev.annotations().value("channelId");
456 if (channelId != null) {
457 buf.append(String.format("%s, ", channelId));
458 }
459 }
460 }
461
462 return buf.toString();
463 }
464
465 private void dumpFlowRule(FlowRule rule, String hdr) {
466 dump(" " + hdr + ":");
467 dump(" - id=%s, priority=%d", rule.id(), rule.priority());
468 dump(" - %s", rule.selector());
469 dump(" - %s", rule.treatment());
470 }
471
472 private void dumpFlowEntry(FlowEntry entry, String hdr) {
473 dumpFlowRule(entry, hdr);
474 dump(" - packets=%d", entry.packets());
475 }
476
477
478 private void dumpCpStatistics(ConnectPoint cp, Collection<PortStatistics> devPortStats,
479 Collection<PortStatistics> devPortDeltaStats, String direction, ServiceRefs svcs) {
480 if (cp == null) {
481 return;
482 }
483
484 dump(" %s:", direction);
485
486 if (cp.port().isLogical()) {
487 dump(" - logical: device: %s, port: %s", cp.deviceId(), cp.port());
488 return;
489 }
490
491 Port port = svcs.deviceService.getPort(cp.deviceId(), cp.port());
492 if (port == null) {
493 return;
494 }
495
496 try {
497 devPortStats.stream()
498 .filter(stat -> stat.portNumber().equals(cp.port()))
499 .forEach(stat -> dump(" - stat : %s:", getPortStatStr(stat, port)));
500 } catch (IllegalStateException e) {
501 error("error: " + e);
502 return;
503 }
504
505 try {
506 devPortDeltaStats.stream()
507 .filter(stat -> stat.portNumber().equals(cp.port()))
508 .forEach(stat -> dump(" - delta : %s:", getPortStatStr(stat, port)));
509 } catch (IllegalStateException e) {
510 error("error: " + e);
511 }
512 }
513
Yuta HIGUCHIe7e71a82018-05-18 16:36:43 -0700514 private void dump(String format, Object... args) {
jaegonkim185299e2018-04-29 20:15:25 +0900515 if (dump) {
516 print(format, args);
517 }
518 }
519
520 private String getPortStatStr(PortStatistics stat, Port port) {
521
522 final String portName = port.annotations().value(AnnotationKeys.PORT_NAME);
523
524 return String.format("port: %s(%s), ", stat.portNumber(), portName) +
525 String.format("enabled: %b, ", port.isEnabled()) +
526 String.format("pktRx: %d, ", stat.packetsReceived()) +
527 String.format("pktTx: %d, ", stat.packetsSent()) +
528 String.format("pktRxErr: %d, ", stat.packetsRxErrors()) +
529 String.format("pktTxErr: %d, ", stat.packetsTxErrors()) +
530 String.format("pktRxDrp: %d, ", stat.packetsRxDropped()) +
531 String.format("pktTxDrp: %d", stat.packetsTxDropped());
532 }
533
534 private static class DeviceOnIntent {
535
536 private final DeviceId devId;
537
538 private Collection<Link> ingressLinks = new ArrayList<>();
539
540 private Collection<Link> egressLinks = new ArrayList<>();
541
542 private Collection<ConnectPoint> ingressCps = new ArrayList<>();
543
544 private Collection<ConnectPoint> egressCps = new ArrayList<>();
545
546 private Collection<FlowRule> flowRules = new ArrayList<>();
547
548 public DeviceOnIntent(DeviceId devId) {
549 this.devId = devId;
550 }
551
552 public DeviceId deviceId() {
553 return devId;
554 }
555
556 public Collection<Link> getIngressLinks() {
557 return ingressLinks;
558 }
559
560 public Collection<Link> getEgressLinks() {
561 return egressLinks;
562 }
563
564 public void addIngressLink(Link link) {
565 ingressLinks.add(link);
566 addIngressCp(link.dst());
567 }
568
569 public void addEgressLink(Link link) {
570 egressLinks.add(link);
571 addEgessCp(link.src());
572 }
573
574 public void addIngressCp(ConnectPoint cp) {
575 ingressCps.add(cp);
576 }
577
578 public void addEgessCp(ConnectPoint cp) {
579 egressCps.add(cp);
580 }
581
582 public boolean hasIngressCp(final ConnectPoint cp) {
583 return ingressCps.stream().anyMatch(icp -> Objects.equals(icp, cp));
584 }
585
586 public boolean hasEgressCp(ConnectPoint cp) {
587 return egressCps.stream().anyMatch(ecp -> Objects.equals(ecp, cp));
588 }
589
590 public Collection<ConnectPoint> getIngressCps() {
591 return ingressCps;
592 }
593
594 public Collection<ConnectPoint> getEgressCps() {
595 return egressCps;
596 }
597
598 public Collection<FlowRule> getFlowRules() {
599 return flowRules;
600 }
601
602 public void addFlowRule(FlowRule flowRule) {
603 flowRules.add(flowRule);
604 }
605
Yuta HIGUCHIe7e71a82018-05-18 16:36:43 -0700606 @Override
jaegonkim185299e2018-04-29 20:15:25 +0900607 public String toString() {
608 return MoreObjects.toStringHelper(getClass())
609 .omitNullValues()
610 .add("devId", devId)
611 .add("ingressLinks", ingressLinks)
612 .add("egressLinks", egressLinks)
613 .add("flowRules", flowRules)
614 .toString();
615 }
616 }
617
618
619 private ServiceRefs buildServiceRefs() {
620 IntentService intentsService = get(IntentService.class);
621 if (intentsService == null) {
622 return null;
623 }
624 DeviceService deviceService = get(DeviceService.class);
625 if (deviceService == null) {
626 return null;
627 }
628 FlowStatisticService flowStatsService = get(FlowStatisticService.class);
629 if (flowStatsService == null) {
630 return null;
631 }
632 FlowRuleService flowService = get(FlowRuleService.class);
633 if (flowService == null) {
634 return null;
635 }
636 WorkPartitionService workPartitionService = get(WorkPartitionService.class);
637 if (workPartitionService == null) {
638 return null;
639 }
640 ObjectiveTrackerService objectiveTrackerService = get(ObjectiveTrackerService.class);
641 if (objectiveTrackerService == null) {
642 return null;
643 }
644
645 return new ServiceRefs(
646 intentsService,
647 deviceService,
648 flowService,
649 workPartitionService,
650 objectiveTrackerService
651 );
652 }
653
654 private static final class ServiceRefs {
655
656 private IntentService intentsService;
657 private DeviceService deviceService;
658 private FlowRuleService flowService;
659 private WorkPartitionService workPartitionService;
660 private ObjectiveTrackerService objectiveTrackerService;
661
662 private ServiceRefs(
663 IntentService intentsService,
664 DeviceService deviceService,
665 FlowRuleService flowService,
666 WorkPartitionService workPartitionService,
667 ObjectiveTrackerService objectiveTrackerService
668 ) {
669 this.intentsService = intentsService;
670 this.deviceService = deviceService;
671 this.flowService = flowService;
672 this.workPartitionService = workPartitionService;
673 this.objectiveTrackerService = objectiveTrackerService;
674 }
675
676 public IntentService intentsService() {
677 return intentsService;
678 }
679
680 public DeviceService deviceService() {
681 return deviceService;
682 }
683
684 public FlowRuleService flowService() {
685 return flowService;
686 }
687
688 public WorkPartitionService getWorkPartitionService() {
689 return workPartitionService;
690 }
691
692 public ObjectiveTrackerService getObjectiveTrackerService() {
693 return objectiveTrackerService;
694 }
695 }
696
697}