blob: 6fd8d033108c3586ccf837c7643c35fb5756ddee [file] [log] [blame]
Yuta HIGUCHIeb3733a2014-08-26 21:38:16 -07001package net.onrc.onos.core.topology;
2
3import static com.google.common.base.Preconditions.checkNotNull;
4
5import java.util.Collection;
6import java.util.Collections;
7import java.util.HashMap;
8import java.util.LinkedList;
9import java.util.List;
10import java.util.Map;
11import java.util.Map.Entry;
12import java.util.SortedSet;
13import java.util.TreeSet;
14
15import javax.annotation.concurrent.Immutable;
16import javax.annotation.concurrent.NotThreadSafe;
17
18import net.floodlightcontroller.core.IFloodlightProviderService.Role;
19import net.floodlightcontroller.util.MACAddress;
20import net.onrc.onos.core.util.Dpid;
21import net.onrc.onos.core.util.LinkTuple;
22import net.onrc.onos.core.util.OnosInstanceId;
23import net.onrc.onos.core.util.PortNumber;
24import net.onrc.onos.core.util.SwitchPort;
25
26import org.slf4j.Logger;
27import org.slf4j.LoggerFactory;
28
29import com.google.common.collect.HashMultimap;
30import com.google.common.collect.ImmutableMultimap;
31import com.google.common.collect.Multimap;
32
33/**
34 * Immutable Topology snapshot.
35 */
36@Immutable
37public final class ImmutableTopologySnapshot
38 implements ImmutableTopology, ImmutableInternalTopology {
39
40 private static final Logger log = LoggerFactory
41 .getLogger(ImmutableTopologySnapshot.class);
42
43 /**
44 * Empty Topology.
45 */
46 public static final ImmutableTopologySnapshot EMPTY = new ImmutableTopologySnapshot();
47
48 // interface adaptor
49 private final BaseTopologyAdaptor adaptor;
50
51
52 // Mastership info
53 // Dpid -> [ (InstanceID, Role) ]
54 private final Map<Dpid, SortedSet<MastershipEvent>> mastership;
55
56 // DPID -> Switch
57 private final Map<Dpid, SwitchEvent> switches;
58 private final Map<Dpid, Map<PortNumber, PortEvent>> ports;
59
60 // Index from Port to Host
61 private final Multimap<SwitchPort, HostEvent> hosts;
62 private final Map<MACAddress, HostEvent> mac2Host;
63
64 // SwitchPort -> (type -> Link)
65 private final Map<SwitchPort, Map<String, LinkEvent>> outgoingLinks;
66 private final Map<SwitchPort, Map<String, LinkEvent>> incomingLinks;
67
68
69 // TODO Slice out Topology Builder interface.
70 // May need to change put*, remove* return values.
71 /**
72 * Immutable Topology Builder.
73 */
74 @NotThreadSafe
75 public static final class Builder {
76
77 private final ImmutableTopologySnapshot current;
78
79 /**
80 * Builder to start from empty topology.
81 */
82 private Builder() {
83 this.current = new ImmutableTopologySnapshot(EMPTY);
84 }
85
86 /**
87 * Builder to start building from existing topology.
88 *
89 * @param original topology to start building from
90 */
91 private Builder(final ImmutableTopologySnapshot original) {
92 // original must be internally mutable instance
93 this.current = original;
94 }
95
96
97 /**
98 * Gets the current InternalTopology being built.
99 *
100 * @return InternalTopology
101 */
102 BaseInternalTopology getCurrentInternal() {
103 return this.current;
104 }
105
106 /**
107 * Gets the current Topology being built.
108 *
109 * @return Topology
110 */
111 BaseTopology getCurrent() {
112 return this.current;
113 }
114
115 /**
116 * Builds the {@link ImmutableTopologySnapshot}.
117 *
118 * @return ImmutableTopologySnapshot
119 */
120 public ImmutableTopologySnapshot build() {
121 return new ImmutableTopologySnapshot(this);
122 }
123
124 // TODO Define error conditions for topology mutation
125 // - Element was already gone:
126 // Treat as error or silently ignore?
127 // - Removing element, where child element still exist:
128 // Treat as error or silently remove?
129
130 /**
131 * Puts a SwitchEvent.
132 *
133 * @param sw Switch to add. (Will be frozen if not already)
134 * @return Builder
135 */
136 public Builder putSwitch(SwitchEvent sw) {
137 checkNotNull(sw);
138
139 current.switches.put(sw.getDpid(), sw.freeze());
140 if (current.ports.get(sw.getDpid()) == null) {
141 current.ports.put(sw.getDpid(), new HashMap<PortNumber, PortEvent>());
142 }
143 return this;
144 }
145
146 /**
147 * Removes a SwitchEvent from this snapshot.
148 * <p>
149 * Will also remove ports, if it has not been removed already.
150 *
151 * @param dpid Switch DPID
152 * @return Builder
153 */
154 public Builder removeSwitch(Dpid dpid) {
155 checkNotNull(dpid);
156
157 current.switches.remove(dpid);
158 Map<PortNumber, PortEvent> removedPorts = current.ports.remove(dpid);
159 if (removedPorts != null && !removedPorts.isEmpty()) {
160 log.warn("Some ports were removed as side-effect of #removeSwitch({})", dpid);
161 }
162 return this;
163 }
164
165 /**
166 * Puts a PortEvent.
167 *
168 * @param port Port to add. (Will be frozen if not already)
169 * @return Builder
170 */
171 public Builder putPort(PortEvent port) {
172 checkNotNull(port);
173
174 // TODO check parent port and throw TopologyMutationFailed
175
176 Map<PortNumber, PortEvent> portMap = current.ports.get(port.getDpid());
177 if (portMap == null) {
178 // shouldn't happen but just to be sure
179 portMap = new HashMap<>();
180 current.ports.put(port.getDpid(), portMap);
181 }
182 portMap.put(port.getPortNumber(), port.freeze());
183 return this;
184 }
185
186 /**
187 * Removes a PortEvent from this snapshot.
188 *
189 * @param port SwitchPort to remove
190 * @return Builder
191 */
192 public Builder removePort(SwitchPort port) {
193 checkNotNull(port);
194
195 removePort(port.getDpid(), port.getPortNumber());
196 return this;
197 }
198
199 /**
200 * Removes a PortEvent from this snapshot.
201 * <p>
202 * Will also remove ports, if it has not been removed already.
203 *
204 * @param dpid Switch DPID
205 * @param number PortNumber
206 * @return Builder
207 */
208 public Builder removePort(Dpid dpid, PortNumber number) {
209 checkNotNull(dpid);
210 checkNotNull(number);
211
212 // TODO sanity check:
213 // - Links should be removed
214 // - Host attachment point should be updated.
215 Map<PortNumber, PortEvent> portMap = current.ports.get(dpid);
216 if (portMap != null) {
217 portMap.remove(number);
218 }
219 return this;
220 }
221
222 /**
223 * Puts a LinkEvent.
224 *
225 * @param link LinkEvent
226 * @return Builder
227 */
228 public Builder putLink(LinkEvent link) {
229 checkNotNull(link);
230
231 // TODO check ports and throw TopologyMutationFailed
232
233 // TODO remove host or ignore?
234
235 // TODO Add sanity check?
236 // - There cannot be 2 links in same direction between a port pair.
237 putLinkMap(current.outgoingLinks, link.getSrc(), link);
238 putLinkMap(current.incomingLinks, link.getDst(), link);
239 return this;
240 }
241
242 /**
243 * Helper method to update outgoingLinks, incomingLinks.
244 *
245 * @param linkMap outgoingLinks or incomingLinks to update
246 * @param port {@code linkMap} key to update
247 * @param link Link to add
248 */
249 private void putLinkMap(Map<SwitchPort, Map<String, LinkEvent>> linkMap,
250 SwitchPort port, LinkEvent link) {
251
252 Map<String, LinkEvent> linksOnPort = linkMap.get(port);
253 if (linksOnPort == null) {
254 linksOnPort = new HashMap<String, LinkEvent>();
255 }
256 linksOnPort.put(link.getType(), link);
257 }
258
259 /**
260 * Removes a LinkEvent from this snapshot.
261 *
262 * @param link Link to remove
263 * @param type type of link to remove
264 * @return Builder
265 */
266 public Builder removeLink(LinkTuple link, String type) {
267 checkNotNull(link);
268
269 Map<String, LinkEvent> portLinks
270 = current.outgoingLinks.get(link.getSrc());
271 if (portLinks != null) {
272 // no conditional update here
273 portLinks.remove(type);
274 }
275 portLinks
276 = current.incomingLinks.get(link.getDst());
277 if (portLinks != null) {
278 // no conditional update here
279 portLinks.remove(type);
280 }
281 return this;
282 }
283
284 /**
285 * Removes a LinkEvent from this snapshot.
286 *
287 * @param link Link to remove
288 * @return Builder
289 */
290 public Builder removeLink(LinkTuple link) {
291 checkNotNull(link);
292
293 Map<String, LinkEvent> links = current.outgoingLinks.get(link.getSrc());
294 if (links == null) {
295 // nothing to do
296 return this;
297 }
298
299 for (LinkEvent linkEvt : links.values()) {
300 removeLink(linkEvt.getLinkTuple(), linkEvt.getType());
301 }
302 return this;
303 }
304
305 /**
306 * Puts a HostEvent.
307 * <p>
308 * Removes attachment points for previous HostEvent and update
309 * them with new HostEvent
310 *
311 * @param host HostEvent
312 * @return Builder
313 */
314 public Builder putHost(HostEvent host) {
315 checkNotNull(host);
316
317 // TODO check Link does not exist on port and throw TopologyMutationFailed
318
319 // Host cannot be simply put() to replace instance
320 // since we need to track attachment point update.
321 // remove -> put to replace all attachment points, etc. for now.
322
323 // remove old attachment points
324 removeHost(host.getMac());
325
326 // add new attachment points
327 for (SwitchPort port : host.getAttachmentPoints()) {
328 current.hosts.put(port, host);
329 }
330 current.mac2Host.put(host.getMac(), host);
331 return this;
332 }
333
334 /**
335 * Removes a HostEvent from this snapshot.
336 *
337 * @param mac MACAddress of the Host to remove
338 * @return Builder
339 */
340 public Builder removeHost(MACAddress mac) {
341 checkNotNull(mac);
342
343 HostEvent host = current.mac2Host.remove(mac);
344 if (host != null) {
345 for (SwitchPort port : host.getAttachmentPoints()) {
346 current.hosts.remove(port, host);
347 }
348 }
349 return this;
350 }
351
352 /**
353 * Puts a mastership change event.
354 *
355 * @param master MastershipEvent
356 * @return Builder
357 */
358 public Builder putSwitchMastershipEvent(MastershipEvent master) {
359 checkNotNull(master);
360
361 SortedSet<MastershipEvent> candidates
362 = current.mastership.get(master.getDpid());
363 if (candidates == null) {
364 // SortedSet, customized so that MASTER MastershipEvent appear
365 // earlier during iteration.
366 candidates = new TreeSet<>(new MastershipEvent.MasterFirstComparator());
367 }
368
369 // always replace
370 candidates.remove(master);
371 candidates.add(master);
372 return this;
373 }
374
375 /**
376 * Removes a mastership change event.
377 * <p>
378 * Note: Only Dpid and OnosInstanceId will be used to identify the
379 * {@link MastershipEvent} to remove.
380 *
381 * @param master {@link MastershipEvent} to remove. (Role is ignored)
382 * @return Builder
383 */
384 public Builder removeSwitchMastershipEvent(MastershipEvent master) {
385 checkNotNull(master);
386
387 SortedSet<MastershipEvent> candidates
388 = current.mastership.get(master.getDpid());
389 if (candidates == null) {
390 // nothing to do
391 return this;
392 }
393 candidates.remove(master);
394
395 return this;
396 }
397 }
398
399 /**
400 * Create an empty Topology.
401 */
402 private ImmutableTopologySnapshot() {
403 mastership = Collections.emptyMap();
404 switches = Collections.emptyMap();
405 ports = Collections.emptyMap();
406 hosts = ImmutableMultimap.of();
407 mac2Host = Collections.emptyMap();
408 outgoingLinks = Collections.emptyMap();
409 incomingLinks = Collections.emptyMap();
410 this.adaptor = new BaseTopologyAdaptor(this);
411 }
412
413 /**
414 * Constructor to create instance from Builder.
415 *
416 * @param builder Builder
417 */
418 private ImmutableTopologySnapshot(final Builder builder) {
419
420 // TODO Change to move semantics to avoid shallow copying or
421 // Shallow copies should be created using
422 // Immutable variant or wrapped by Unmodifiable.
423 //
424 // If we switched to Immutable* Collections,
425 // wrapping by Collections.unmodifiableCollection() can be removed.
426
427 // shallow copy Set in Map
428 this.mastership = new HashMap<>(builder.current.mastership.size());
429 for (Entry<Dpid, SortedSet<MastershipEvent>> e
430 : builder.current.mastership.entrySet()) {
431 this.mastership.put(e.getKey(), new TreeSet<>(e.getValue()));
432 }
433
434 this.switches = new HashMap<>(builder.current.switches);
435
436 // shallow copy Map in Map
437 this.ports = new HashMap<>(builder.current.ports.size());
438 for (Entry<Dpid, Map<PortNumber, PortEvent>> entry
439 : builder.current.ports.entrySet()) {
440 this.ports.put(entry.getKey(), new HashMap<>(entry.getValue()));
441 }
442
443 this.hosts =
444 HashMultimap.<SwitchPort, HostEvent>create(builder.current.hosts);
445 this.mac2Host = new HashMap<>(builder.current.mac2Host);
446
447 // shallow copy Map in Map
448 this.outgoingLinks = new HashMap<>(builder.current.outgoingLinks.size());
449 for (Entry<SwitchPort, Map<String, LinkEvent>> entry
450 : builder.current.outgoingLinks.entrySet()) {
451 this.outgoingLinks.put(entry.getKey(), new HashMap<>(entry.getValue()));
452 }
453
454 // shallow copy Map in Map
455 this.incomingLinks = new HashMap<>(builder.current.incomingLinks.size());
456 for (Entry<SwitchPort, Map<String, LinkEvent>> entry
457 : builder.current.incomingLinks.entrySet()) {
458 this.incomingLinks.put(entry.getKey(), new HashMap<>(entry.getValue()));
459 }
460
461 this.adaptor = new BaseTopologyAdaptor(this);
462 }
463
464 /**
465 * Create internally mutable shallow copy of given instance.
466 * <p>
467 * Note: only expected to be used by Builder.
468 *
469 * @param original instance to copy from
470 */
471 private ImmutableTopologySnapshot(ImmutableTopologySnapshot original) {
472
473 // shallow copy Set in Map
474 this.mastership = new HashMap<>(original.mastership.size());
475 for (Entry<Dpid, SortedSet<MastershipEvent>> e
476 : original.mastership.entrySet()) {
477 this.mastership.put(e.getKey(), new TreeSet<>(e.getValue()));
478 }
479
480 this.switches = new HashMap<>(original.switches);
481
482 // shallow copy Map in Map
483 this.ports = new HashMap<>(original.ports.size());
484 for (Entry<Dpid, Map<PortNumber, PortEvent>> entry
485 : original.ports.entrySet()) {
486 this.ports.put(entry.getKey(), new HashMap<>(entry.getValue()));
487 }
488
489 this.hosts =
490 HashMultimap.<SwitchPort, HostEvent>create(original.hosts);
491 this.mac2Host = new HashMap<>(original.mac2Host);
492
493 // shallow copy Map in Map
494 this.outgoingLinks = new HashMap<>(original.outgoingLinks.size());
495 for (Entry<SwitchPort, Map<String, LinkEvent>> entry
496 : original.outgoingLinks.entrySet()) {
497 this.outgoingLinks.put(entry.getKey(), new HashMap<>(entry.getValue()));
498 }
499
500 // shallow copy Map in Map
501 this.incomingLinks = new HashMap<>(original.incomingLinks.size());
502 for (Entry<SwitchPort, Map<String, LinkEvent>> entry
503 : original.incomingLinks.entrySet()) {
504 this.incomingLinks.put(entry.getKey(), new HashMap<>(entry.getValue()));
505 }
506
507 this.adaptor = new BaseTopologyAdaptor(this);
508 }
509
510
511 /**
512 * Gets the builder starting from empty topology.
513 *
514 * @return Builder
515 */
516 public static Builder initialBuilder() {
517 return new Builder();
518 }
519
520 /**
521 * Gets the builder starting from this topology.
522 *
523 * @return Builder
524 */
525 public Builder builder() {
526 return new Builder(new ImmutableTopologySnapshot(this));
527 }
528
529 @Override
530 public SwitchEvent getSwitchEvent(final Dpid dpid) {
531 return this.switches.get(dpid);
532 }
533
534 @Override
535 public Collection<SwitchEvent> getAllSwitchEvents() {
536 return Collections.unmodifiableCollection(switches.values());
537 }
538
539 @Override
540 public PortEvent getPortEvent(final SwitchPort port) {
541 return getPortEvent(port.getDpid(), port.getPortNumber());
542 }
543
544 @Override
545 public PortEvent getPortEvent(final Dpid dpid, PortNumber portNumber) {
546 Map<PortNumber, PortEvent> portMap = this.ports.get(dpid);
547 if (portMap != null) {
548 return portMap.get(portNumber);
549 }
550 return null;
551 }
552
553 @Override
554 public Collection<PortEvent> getPortEvents(final Dpid dpid) {
555 Map<PortNumber, PortEvent> portList = ports.get(dpid);
556 if (portList == null) {
557 return Collections.emptyList();
558 }
559 return Collections.unmodifiableCollection(portList.values());
560 }
561
562 @Override
563 public Collection<PortEvent> getAllPortEvents() {
564 List<PortEvent> events = new LinkedList<>();
565 for (Map<PortNumber, PortEvent> cm : ports.values()) {
566 events.addAll(cm.values());
567 }
568 return Collections.unmodifiableCollection(events);
569 }
570
571 @Override
572 public LinkEvent getLinkEvent(final LinkTuple linkId) {
573 Map<String, LinkEvent> links = this.outgoingLinks.get(linkId.getSrc());
574 if (links == null) {
575 return null;
576 }
577
578 // TODO Should we look for Packet link first?
579 // => Not needed unless invariant is broken.
580
581 for (LinkEvent link : links.values()) {
582 if (link.getDst().equals(linkId.getDst())) {
583 return link;
584 }
585 }
586 return null;
587 }
588
589 @Override
590 public LinkEvent getLinkEvent(final LinkTuple linkId, final String type) {
591 Map<String, LinkEvent> links = this.outgoingLinks.get(linkId.getSrc());
592 if (links == null) {
593 return null;
594 }
595 return links.get(type);
596 }
597
598 @Override
599 public Collection<LinkEvent> getLinkEventsFrom(SwitchPort srcPort) {
600 Map<String, LinkEvent> links = this.outgoingLinks.get(srcPort);
601 if (links == null) {
602 return Collections.emptyList();
603 }
604
605 return Collections.unmodifiableCollection(links.values());
606 }
607
608 @Override
609 public Collection<LinkEvent> getLinkEventsTo(SwitchPort dstPort) {
610 Map<String, LinkEvent> links = this.incomingLinks.get(dstPort);
611 if (links == null) {
612 return Collections.emptyList();
613 }
614
615 return Collections.unmodifiableCollection(links.values());
616 }
617
618 @Override
619 public Collection<LinkEvent> getLinkEvents(final LinkTuple linkId) {
620 Map<String, LinkEvent> links = this.outgoingLinks.get(linkId.getSrc());
621 if (links == null) {
622 return Collections.emptyList();
623 }
624
625 // unless invariant is broken, this should contain at most 1 element.
626 return Collections.unmodifiableCollection(links.values());
627 }
628
629 @Override
630 public Collection<LinkEvent> getAllLinkEvents() {
631 List<LinkEvent> events = new LinkedList<>();
632 for (Map<String, LinkEvent> cm : outgoingLinks.values()) {
633 events.addAll(cm.values());
634 }
635 return Collections.unmodifiableCollection(events);
636 }
637
638 @Override
639 public HostEvent getHostEvent(final MACAddress mac) {
640 return this.mac2Host.get(mac);
641 }
642
643 @Override
644 public Collection<HostEvent> getHostEvents(SwitchPort port) {
645 return Collections.unmodifiableCollection(this.hosts.get(port));
646 }
647
648 @Override
649 public Collection<HostEvent> getAllHostEvents() {
650 return Collections.unmodifiableCollection(mac2Host.values());
651 }
652
653 /**
654 * Gets the master instance ID for a switch.
655 *
656 * @param dpid switch dpid
657 * @return master instance ID or null if there is no master
658 */
659 public OnosInstanceId getSwitchMaster(Dpid dpid) {
660 final SortedSet<MastershipEvent> candidates = mastership.get(dpid);
661 if (candidates == null) {
662 return null;
663 }
664 for (MastershipEvent candidate : candidates) {
665 if (candidate.getRole() == Role.MASTER) {
666 return candidate.getOnosInstanceId();
667 }
668 }
669 return null;
670 }
671
672
673 // TODO find better way to delegate following to interface adaptor
674
675 @Override
676 public Switch getSwitch(Dpid dpid) {
677 return adaptor.getSwitch(dpid);
678 }
679
680 @Override
681 public Iterable<Switch> getSwitches() {
682 return adaptor.getSwitches();
683 }
684
685 @Override
686 public Port getPort(Dpid dpid, PortNumber number) {
687 return adaptor.getPort(dpid, number);
688 }
689
690 @Override
691 public Port getPort(SwitchPort port) {
692 return adaptor.getPort(port);
693 }
694
695 @Override
696 public Collection<Port> getPorts(Dpid dpid) {
697 return adaptor.getPorts(dpid);
698 }
699
700 @Override
701 public Link getOutgoingLink(Dpid dpid, PortNumber number) {
702 return adaptor.getOutgoingLink(dpid, number);
703 }
704
705 @Override
706 public Link getOutgoingLink(SwitchPort port) {
707 return adaptor.getOutgoingLink(port);
708 }
709
710 @Override
711 public Link getOutgoingLink(Dpid dpid, PortNumber number, String type) {
712 return adaptor.getOutgoingLink(dpid, number, type);
713 }
714
715 @Override
716 public Link getOutgoingLink(SwitchPort port, String type) {
717 return adaptor.getOutgoingLink(port, type);
718 }
719
720 @Override
721 public Collection<Link> getOutgoingLinks(SwitchPort port) {
722 return adaptor.getOutgoingLinks(port);
723 }
724
725 @Override
726 public Link getIncomingLink(Dpid dpid, PortNumber number) {
727 return adaptor.getIncomingLink(dpid, number);
728 }
729
730 @Override
731 public Link getIncomingLink(SwitchPort port) {
732 return adaptor.getIncomingLink(port);
733 }
734
735 @Override
736 public Link getIncomingLink(Dpid dpid, PortNumber number, String type) {
737 return adaptor.getIncomingLink(dpid, number, type);
738 }
739
740 @Override
741 public Link getIncomingLink(SwitchPort port, String type) {
742 return adaptor.getIncomingLink(port, type);
743 }
744
745 @Override
746 public Collection<Link> getIncomingLinks(SwitchPort port) {
747 return adaptor.getIncomingLinks(port);
748 }
749
750 @Override
751 public Link getLink(Dpid srcDpid, PortNumber srcNumber,
752 Dpid dstDpid, PortNumber dstNumber) {
753
754 return adaptor.getLink(srcDpid, srcNumber, dstDpid, dstNumber);
755 }
756
757 @Override
758 public Link getLink(Dpid srcDpid, PortNumber srcNumber,
759 Dpid dstDpid, PortNumber dstNumber,
760 String type) {
761
762 return adaptor.getLink(srcDpid, srcNumber, dstDpid, dstNumber, type);
763 }
764
765 @Override
766 public Iterable<Link> getLinks() {
767 return adaptor.getLinks();
768 }
769
770 @Override
771 public Host getHostByMac(MACAddress address) {
772 return adaptor.getHostByMac(address);
773 }
774
775 @Override
776 public Iterable<Host> getHosts() {
777 return adaptor.getHosts();
778 }
779
780 @Override
781 public Collection<Host> getHosts(SwitchPort port) {
782 return adaptor.getHosts(port);
783 }
784}