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