blob: 5df25b4ed531c430d0fe385fef86db6451f87696 [file] [log] [blame]
Madan Jampani4209def2014-09-29 13:54:57 -07001package org.onlab.onos.store.link.impl;
2
3import static org.onlab.onos.net.Link.Type.DIRECT;
4import static org.onlab.onos.net.Link.Type.INDIRECT;
5import static org.onlab.onos.net.link.LinkEvent.Type.LINK_ADDED;
6import static org.onlab.onos.net.link.LinkEvent.Type.LINK_REMOVED;
7import static org.onlab.onos.net.link.LinkEvent.Type.LINK_UPDATED;
8import static org.slf4j.LoggerFactory.getLogger;
9
10import java.util.HashSet;
11import java.util.Set;
12import java.util.concurrent.ConcurrentHashMap;
13import java.util.concurrent.ConcurrentMap;
14
15import org.apache.felix.scr.annotations.Activate;
16import org.apache.felix.scr.annotations.Component;
17import org.apache.felix.scr.annotations.Deactivate;
18import org.apache.felix.scr.annotations.Reference;
19import org.apache.felix.scr.annotations.ReferenceCardinality;
20import org.apache.felix.scr.annotations.Service;
21import org.onlab.onos.net.ConnectPoint;
22import org.onlab.onos.net.DefaultLink;
23import org.onlab.onos.net.DeviceId;
24import org.onlab.onos.net.Link;
25import org.onlab.onos.net.LinkKey;
26import org.onlab.onos.net.link.LinkDescription;
27import org.onlab.onos.net.link.LinkEvent;
28import org.onlab.onos.net.link.LinkStore;
29import org.onlab.onos.net.link.LinkStoreDelegate;
30import org.onlab.onos.net.provider.ProviderId;
31import org.onlab.onos.store.AbstractStore;
32import org.onlab.onos.store.ClockService;
33import org.onlab.onos.store.Timestamp;
34import org.onlab.onos.store.device.impl.VersionedValue;
35import org.slf4j.Logger;
36
37import com.google.common.collect.HashMultimap;
38import com.google.common.collect.ImmutableSet;
39import com.google.common.collect.Multimap;
40import com.google.common.collect.ImmutableSet.Builder;
41
42import static com.google.common.base.Preconditions.checkArgument;
43import static com.google.common.base.Preconditions.checkState;
44
45/**
46 * Manages inventory of infrastructure links using a protocol that takes into consideration
47 * the order in which events occur.
48 */
49// FIXME: This does not yet implement the full protocol.
50// The full protocol requires the sender of LLDP message to include the
51// version information of src device/port and the receiver to
52// take that into account when figuring out if a more recent src
53// device/port down event renders the link discovery obsolete.
54@Component(immediate = true)
55@Service
56public class OnosDistributedLinkStore
57 extends AbstractStore<LinkEvent, LinkStoreDelegate>
58 implements LinkStore {
59
60 private final Logger log = getLogger(getClass());
61
62 // Link inventory
63 private ConcurrentMap<LinkKey, VersionedValue<Link>> links;
64
65 public static final String LINK_NOT_FOUND = "Link between %s and %s not found";
66
67 // TODO synchronize?
68 // Egress and ingress link sets
69 private final Multimap<DeviceId, VersionedValue<Link>> srcLinks = HashMultimap.create();
70 private final Multimap<DeviceId, VersionedValue<Link>> dstLinks = HashMultimap.create();
71
72 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
73 protected ClockService clockService;
74
75 @Activate
76 public void activate() {
77
78 links = new ConcurrentHashMap<>();
79
80 log.info("Started");
81 }
82
83 @Deactivate
84 public void deactivate() {
85 log.info("Stopped");
86 }
87
88 @Override
89 public int getLinkCount() {
90 return links.size();
91 }
92
93 @Override
94 public Iterable<Link> getLinks() {
95 Builder<Link> builder = ImmutableSet.builder();
96 synchronized (this) {
97 for (VersionedValue<Link> link : links.values()) {
98 builder.add(link.entity());
99 }
100 return builder.build();
101 }
102 }
103
104 @Override
105 public Set<Link> getDeviceEgressLinks(DeviceId deviceId) {
106 Set<VersionedValue<Link>> egressLinks = ImmutableSet.copyOf(srcLinks.get(deviceId));
107 Set<Link> rawEgressLinks = new HashSet<>();
108 for (VersionedValue<Link> link : egressLinks) {
109 rawEgressLinks.add(link.entity());
110 }
111 return rawEgressLinks;
112 }
113
114 @Override
115 public Set<Link> getDeviceIngressLinks(DeviceId deviceId) {
116 Set<VersionedValue<Link>> ingressLinks = ImmutableSet.copyOf(dstLinks.get(deviceId));
117 Set<Link> rawIngressLinks = new HashSet<>();
118 for (VersionedValue<Link> link : ingressLinks) {
119 rawIngressLinks.add(link.entity());
120 }
121 return rawIngressLinks;
122 }
123
124 @Override
125 public Link getLink(ConnectPoint src, ConnectPoint dst) {
126 VersionedValue<Link> link = links.get(new LinkKey(src, dst));
127 checkArgument(link != null, "LINK_NOT_FOUND", src, dst);
128 return link.entity();
129 }
130
131 @Override
132 public Set<Link> getEgressLinks(ConnectPoint src) {
133 Set<Link> egressLinks = new HashSet<>();
134 for (VersionedValue<Link> link : srcLinks.get(src.deviceId())) {
135 if (link.entity().src().equals(src)) {
136 egressLinks.add(link.entity());
137 }
138 }
139 return egressLinks;
140 }
141
142 @Override
143 public Set<Link> getIngressLinks(ConnectPoint dst) {
144 Set<Link> ingressLinks = new HashSet<>();
145 for (VersionedValue<Link> link : dstLinks.get(dst.deviceId())) {
146 if (link.entity().dst().equals(dst)) {
147 ingressLinks.add(link.entity());
148 }
149 }
150 return ingressLinks;
151 }
152
153 @Override
154 public LinkEvent createOrUpdateLink(ProviderId providerId,
155 LinkDescription linkDescription) {
156
157 final DeviceId destinationDeviceId = linkDescription.dst().deviceId();
158 final Timestamp newTimestamp = clockService.getTimestamp(destinationDeviceId);
159
160 LinkKey key = new LinkKey(linkDescription.src(), linkDescription.dst());
161 VersionedValue<Link> link = links.get(key);
162 if (link == null) {
163 return createLink(providerId, key, linkDescription, newTimestamp);
164 }
165
166 checkState(newTimestamp.compareTo(link.timestamp()) > 0,
167 "Existing Link has a timestamp in the future!");
168
169 return updateLink(providerId, link, key, linkDescription, newTimestamp);
170 }
171
172 // Creates and stores the link and returns the appropriate event.
173 private LinkEvent createLink(ProviderId providerId, LinkKey key,
174 LinkDescription linkDescription, Timestamp timestamp) {
175 VersionedValue<Link> link = new VersionedValue<Link>(new DefaultLink(providerId, key.src(), key.dst(),
176 linkDescription.type()), true, timestamp);
177 synchronized (this) {
178 links.put(key, link);
179 addNewLink(link, timestamp);
180 }
181 // FIXME: notify peers.
182 return new LinkEvent(LINK_ADDED, link.entity());
183 }
184
185 // update Egress and ingress link sets
186 private void addNewLink(VersionedValue<Link> link, Timestamp timestamp) {
187 Link rawLink = link.entity();
188 synchronized (this) {
189 srcLinks.put(rawLink.src().deviceId(), link);
190 dstLinks.put(rawLink.dst().deviceId(), link);
191 }
192 }
193
194 // Updates, if necessary the specified link and returns the appropriate event.
195 private LinkEvent updateLink(ProviderId providerId, VersionedValue<Link> existingLink,
196 LinkKey key, LinkDescription linkDescription, Timestamp timestamp) {
197 // FIXME confirm Link update condition is OK
198 if (existingLink.entity().type() == INDIRECT && linkDescription.type() == DIRECT) {
199 synchronized (this) {
200
201 VersionedValue<Link> updatedLink = new VersionedValue<Link>(
202 new DefaultLink(providerId, existingLink.entity().src(), existingLink.entity().dst(),
203 linkDescription.type()), true, timestamp);
204 links.replace(key, existingLink, updatedLink);
205
206 replaceLink(existingLink, updatedLink);
207 // FIXME: notify peers.
208 return new LinkEvent(LINK_UPDATED, updatedLink.entity());
209 }
210 }
211 return null;
212 }
213
214 // update Egress and ingress link sets
215 private void replaceLink(VersionedValue<Link> current, VersionedValue<Link> updated) {
216 synchronized (this) {
217 srcLinks.remove(current.entity().src().deviceId(), current);
218 dstLinks.remove(current.entity().dst().deviceId(), current);
219
220 srcLinks.put(current.entity().src().deviceId(), updated);
221 dstLinks.put(current.entity().dst().deviceId(), updated);
222 }
223 }
224
225 @Override
226 public LinkEvent removeLink(ConnectPoint src, ConnectPoint dst) {
227 synchronized (this) {
228 LinkKey key = new LinkKey(src, dst);
229 VersionedValue<Link> link = links.remove(key);
230 if (link != null) {
231 removeLink(link);
232 // notify peers
233 return new LinkEvent(LINK_REMOVED, link.entity());
234 }
235 return null;
236 }
237 }
238
239 // update Egress and ingress link sets
240 private void removeLink(VersionedValue<Link> link) {
241 synchronized (this) {
242 srcLinks.remove(link.entity().src().deviceId(), link);
243 dstLinks.remove(link.entity().dst().deviceId(), link);
244 }
245 }
246}