Checking in GossipHostStore. The implementation is only partly done and not fully distributed, yet.
Showing
1 changed file
with
343 additions
and
0 deletions
| 1 | +package org.onlab.onos.store.host.impl; | ||
| 2 | + | ||
| 3 | +import com.google.common.collect.HashMultimap; | ||
| 4 | +import com.google.common.collect.ImmutableSet; | ||
| 5 | +import com.google.common.collect.Multimap; | ||
| 6 | +import com.google.common.collect.Sets; | ||
| 7 | + | ||
| 8 | +import org.apache.felix.scr.annotations.Activate; | ||
| 9 | +import org.apache.felix.scr.annotations.Component; | ||
| 10 | +import org.apache.felix.scr.annotations.Deactivate; | ||
| 11 | +import org.apache.felix.scr.annotations.Reference; | ||
| 12 | +import org.apache.felix.scr.annotations.ReferenceCardinality; | ||
| 13 | +import org.apache.felix.scr.annotations.Service; | ||
| 14 | +import org.onlab.onos.cluster.ClusterService; | ||
| 15 | +import org.onlab.onos.net.Annotations; | ||
| 16 | +import org.onlab.onos.net.ConnectPoint; | ||
| 17 | +import org.onlab.onos.net.DefaultHost; | ||
| 18 | +import org.onlab.onos.net.DeviceId; | ||
| 19 | +import org.onlab.onos.net.Host; | ||
| 20 | +import org.onlab.onos.net.HostId; | ||
| 21 | +import org.onlab.onos.net.HostLocation; | ||
| 22 | +import org.onlab.onos.net.host.HostClockService; | ||
| 23 | +import org.onlab.onos.net.host.HostDescription; | ||
| 24 | +import org.onlab.onos.net.host.HostEvent; | ||
| 25 | +import org.onlab.onos.net.host.HostStore; | ||
| 26 | +import org.onlab.onos.net.host.HostStoreDelegate; | ||
| 27 | +import org.onlab.onos.net.host.PortAddresses; | ||
| 28 | +import org.onlab.onos.net.provider.ProviderId; | ||
| 29 | +import org.onlab.onos.store.AbstractStore; | ||
| 30 | +import org.onlab.onos.store.Timestamp; | ||
| 31 | +import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService; | ||
| 32 | +import org.onlab.onos.store.common.impl.Timestamped; | ||
| 33 | +import org.onlab.packet.IpPrefix; | ||
| 34 | +import org.onlab.packet.MacAddress; | ||
| 35 | +import org.onlab.packet.VlanId; | ||
| 36 | +import org.slf4j.Logger; | ||
| 37 | + | ||
| 38 | +import java.util.HashSet; | ||
| 39 | +import java.util.Map; | ||
| 40 | +import java.util.Set; | ||
| 41 | +import java.util.concurrent.ConcurrentHashMap; | ||
| 42 | + | ||
| 43 | +import static org.onlab.onos.net.host.HostEvent.Type.*; | ||
| 44 | +import static org.slf4j.LoggerFactory.getLogger; | ||
| 45 | + | ||
| 46 | +/** | ||
| 47 | + * Manages inventory of end-station hosts in distributed data store | ||
| 48 | + * that uses optimistic replication and gossip based techniques. | ||
| 49 | + */ | ||
| 50 | +@Component(immediate = true) | ||
| 51 | +@Service | ||
| 52 | +public class GossipHostStore | ||
| 53 | + extends AbstractStore<HostEvent, HostStoreDelegate> | ||
| 54 | + implements HostStore { | ||
| 55 | + | ||
| 56 | + private final Logger log = getLogger(getClass()); | ||
| 57 | + | ||
| 58 | + // Host inventory | ||
| 59 | + private final Map<HostId, StoredHost> hosts = new ConcurrentHashMap<>(2000000, 0.75f, 16); | ||
| 60 | + | ||
| 61 | + private final Map<HostId, Timestamped<Host>> removedHosts = new ConcurrentHashMap<>(2000000, 0.75f, 16); | ||
| 62 | + | ||
| 63 | + // Hosts tracked by their location | ||
| 64 | + private final Multimap<ConnectPoint, Host> locations = HashMultimap.create(); | ||
| 65 | + | ||
| 66 | + private final Map<ConnectPoint, PortAddresses> portAddresses = | ||
| 67 | + new ConcurrentHashMap<>(); | ||
| 68 | + | ||
| 69 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
| 70 | + protected HostClockService hostClockService; | ||
| 71 | + | ||
| 72 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
| 73 | + protected ClusterCommunicationService clusterCommunicator; | ||
| 74 | + | ||
| 75 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
| 76 | + protected ClusterService clusterService; | ||
| 77 | + | ||
| 78 | + @Activate | ||
| 79 | + public void activate() { | ||
| 80 | + log.info("Started"); | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | + @Deactivate | ||
| 84 | + public void deactivate() { | ||
| 85 | + log.info("Stopped"); | ||
| 86 | + } | ||
| 87 | + | ||
| 88 | + @Override | ||
| 89 | + public HostEvent createOrUpdateHost(ProviderId providerId, HostId hostId, | ||
| 90 | + HostDescription hostDescription) { | ||
| 91 | + Timestamp timestamp = hostClockService.getTimestamp(hostId); | ||
| 92 | + return createOrUpdateHostInternal(providerId, hostId, hostDescription, timestamp); | ||
| 93 | + // TODO: tell peers. | ||
| 94 | + } | ||
| 95 | + | ||
| 96 | + private HostEvent createOrUpdateHostInternal(ProviderId providerId, HostId hostId, | ||
| 97 | + HostDescription hostDescription, Timestamp timestamp) { | ||
| 98 | + StoredHost host = hosts.get(hostId); | ||
| 99 | + if (host == null) { | ||
| 100 | + return createHost(providerId, hostId, hostDescription, timestamp); | ||
| 101 | + } | ||
| 102 | + return updateHost(providerId, host, hostDescription, timestamp); | ||
| 103 | + } | ||
| 104 | + | ||
| 105 | + // creates a new host and sends HOST_ADDED | ||
| 106 | + private HostEvent createHost(ProviderId providerId, HostId hostId, | ||
| 107 | + HostDescription descr, Timestamp timestamp) { | ||
| 108 | + synchronized (this) { | ||
| 109 | + // If this host was previously removed, first ensure | ||
| 110 | + // this new request is "newer" | ||
| 111 | + if (removedHosts.containsKey(hostId)) { | ||
| 112 | + if (removedHosts.get(hostId).isNewer(timestamp)) { | ||
| 113 | + return null; | ||
| 114 | + } else { | ||
| 115 | + removedHosts.remove(hostId); | ||
| 116 | + } | ||
| 117 | + } | ||
| 118 | + StoredHost newhost = new StoredHost(providerId, hostId, | ||
| 119 | + descr.hwAddress(), | ||
| 120 | + descr.vlan(), | ||
| 121 | + new Timestamped<>(descr.location(), timestamp), | ||
| 122 | + ImmutableSet.of(descr.ipAddress())); | ||
| 123 | + hosts.put(hostId, newhost); | ||
| 124 | + locations.put(descr.location(), newhost); | ||
| 125 | + return new HostEvent(HOST_ADDED, newhost); | ||
| 126 | + } | ||
| 127 | + } | ||
| 128 | + | ||
| 129 | + // checks for type of update to host, sends appropriate event | ||
| 130 | + private HostEvent updateHost(ProviderId providerId, StoredHost host, | ||
| 131 | + HostDescription descr, Timestamp timestamp) { | ||
| 132 | + HostEvent event; | ||
| 133 | + if (!host.location.isNewer(timestamp) && !host.location().equals(descr.location())) { | ||
| 134 | + host.setLocation(new Timestamped<>(descr.location(), timestamp)); | ||
| 135 | + return new HostEvent(HOST_MOVED, host); | ||
| 136 | + } | ||
| 137 | + | ||
| 138 | + if (host.ipAddresses().contains(descr.ipAddress())) { | ||
| 139 | + return null; | ||
| 140 | + } | ||
| 141 | + | ||
| 142 | + Set<IpPrefix> addresses = new HashSet<>(host.ipAddresses()); | ||
| 143 | + addresses.add(descr.ipAddress()); | ||
| 144 | + StoredHost updated = new StoredHost(providerId, host.id(), | ||
| 145 | + host.mac(), host.vlan(), | ||
| 146 | + host.location, addresses); | ||
| 147 | + event = new HostEvent(HOST_UPDATED, updated); | ||
| 148 | + synchronized (this) { | ||
| 149 | + hosts.put(host.id(), updated); | ||
| 150 | + locations.remove(host.location(), host); | ||
| 151 | + locations.put(updated.location(), updated); | ||
| 152 | + } | ||
| 153 | + return event; | ||
| 154 | + } | ||
| 155 | + | ||
| 156 | + @Override | ||
| 157 | + public HostEvent removeHost(HostId hostId) { | ||
| 158 | + Timestamp timestamp = hostClockService.getTimestamp(hostId); | ||
| 159 | + return removeHostInternal(hostId, timestamp); | ||
| 160 | + // TODO: tell peers | ||
| 161 | + } | ||
| 162 | + | ||
| 163 | + private HostEvent removeHostInternal(HostId hostId, Timestamp timestamp) { | ||
| 164 | + synchronized (this) { | ||
| 165 | + Host host = hosts.remove(hostId); | ||
| 166 | + if (host != null) { | ||
| 167 | + locations.remove((host.location()), host); | ||
| 168 | + removedHosts.put(hostId, new Timestamped<>(host, timestamp)); | ||
| 169 | + return new HostEvent(HOST_REMOVED, host); | ||
| 170 | + } | ||
| 171 | + return null; | ||
| 172 | + } | ||
| 173 | + } | ||
| 174 | + | ||
| 175 | + @Override | ||
| 176 | + public int getHostCount() { | ||
| 177 | + return hosts.size(); | ||
| 178 | + } | ||
| 179 | + | ||
| 180 | + @Override | ||
| 181 | + public Iterable<Host> getHosts() { | ||
| 182 | + return ImmutableSet.<Host>copyOf(hosts.values()); | ||
| 183 | + } | ||
| 184 | + | ||
| 185 | + @Override | ||
| 186 | + public Host getHost(HostId hostId) { | ||
| 187 | + return hosts.get(hostId); | ||
| 188 | + } | ||
| 189 | + | ||
| 190 | + @Override | ||
| 191 | + public Set<Host> getHosts(VlanId vlanId) { | ||
| 192 | + Set<Host> vlanset = new HashSet<>(); | ||
| 193 | + for (Host h : hosts.values()) { | ||
| 194 | + if (h.vlan().equals(vlanId)) { | ||
| 195 | + vlanset.add(h); | ||
| 196 | + } | ||
| 197 | + } | ||
| 198 | + return vlanset; | ||
| 199 | + } | ||
| 200 | + | ||
| 201 | + @Override | ||
| 202 | + public Set<Host> getHosts(MacAddress mac) { | ||
| 203 | + Set<Host> macset = new HashSet<>(); | ||
| 204 | + for (Host h : hosts.values()) { | ||
| 205 | + if (h.mac().equals(mac)) { | ||
| 206 | + macset.add(h); | ||
| 207 | + } | ||
| 208 | + } | ||
| 209 | + return macset; | ||
| 210 | + } | ||
| 211 | + | ||
| 212 | + @Override | ||
| 213 | + public Set<Host> getHosts(IpPrefix ip) { | ||
| 214 | + Set<Host> ipset = new HashSet<>(); | ||
| 215 | + for (Host h : hosts.values()) { | ||
| 216 | + if (h.ipAddresses().contains(ip)) { | ||
| 217 | + ipset.add(h); | ||
| 218 | + } | ||
| 219 | + } | ||
| 220 | + return ipset; | ||
| 221 | + } | ||
| 222 | + | ||
| 223 | + @Override | ||
| 224 | + public Set<Host> getConnectedHosts(ConnectPoint connectPoint) { | ||
| 225 | + return ImmutableSet.copyOf(locations.get(connectPoint)); | ||
| 226 | + } | ||
| 227 | + | ||
| 228 | + @Override | ||
| 229 | + public Set<Host> getConnectedHosts(DeviceId deviceId) { | ||
| 230 | + Set<Host> hostset = new HashSet<>(); | ||
| 231 | + for (ConnectPoint p : locations.keySet()) { | ||
| 232 | + if (p.deviceId().equals(deviceId)) { | ||
| 233 | + hostset.addAll(locations.get(p)); | ||
| 234 | + } | ||
| 235 | + } | ||
| 236 | + return hostset; | ||
| 237 | + } | ||
| 238 | + | ||
| 239 | + @Override | ||
| 240 | + public void updateAddressBindings(PortAddresses addresses) { | ||
| 241 | + synchronized (portAddresses) { | ||
| 242 | + PortAddresses existing = portAddresses.get(addresses.connectPoint()); | ||
| 243 | + if (existing == null) { | ||
| 244 | + portAddresses.put(addresses.connectPoint(), addresses); | ||
| 245 | + } else { | ||
| 246 | + Set<IpPrefix> union = Sets.union(existing.ips(), addresses.ips()) | ||
| 247 | + .immutableCopy(); | ||
| 248 | + | ||
| 249 | + MacAddress newMac = (addresses.mac() == null) ? existing.mac() | ||
| 250 | + : addresses.mac(); | ||
| 251 | + | ||
| 252 | + PortAddresses newAddresses = | ||
| 253 | + new PortAddresses(addresses.connectPoint(), union, newMac); | ||
| 254 | + | ||
| 255 | + portAddresses.put(newAddresses.connectPoint(), newAddresses); | ||
| 256 | + } | ||
| 257 | + } | ||
| 258 | + } | ||
| 259 | + | ||
| 260 | + @Override | ||
| 261 | + public void removeAddressBindings(PortAddresses addresses) { | ||
| 262 | + synchronized (portAddresses) { | ||
| 263 | + PortAddresses existing = portAddresses.get(addresses.connectPoint()); | ||
| 264 | + if (existing != null) { | ||
| 265 | + Set<IpPrefix> difference = | ||
| 266 | + Sets.difference(existing.ips(), addresses.ips()).immutableCopy(); | ||
| 267 | + | ||
| 268 | + // If they removed the existing mac, set the new mac to null. | ||
| 269 | + // Otherwise, keep the existing mac. | ||
| 270 | + MacAddress newMac = existing.mac(); | ||
| 271 | + if (addresses.mac() != null && addresses.mac().equals(existing.mac())) { | ||
| 272 | + newMac = null; | ||
| 273 | + } | ||
| 274 | + | ||
| 275 | + PortAddresses newAddresses = | ||
| 276 | + new PortAddresses(addresses.connectPoint(), difference, newMac); | ||
| 277 | + | ||
| 278 | + portAddresses.put(newAddresses.connectPoint(), newAddresses); | ||
| 279 | + } | ||
| 280 | + } | ||
| 281 | + } | ||
| 282 | + | ||
| 283 | + @Override | ||
| 284 | + public void clearAddressBindings(ConnectPoint connectPoint) { | ||
| 285 | + synchronized (portAddresses) { | ||
| 286 | + portAddresses.remove(connectPoint); | ||
| 287 | + } | ||
| 288 | + } | ||
| 289 | + | ||
| 290 | + @Override | ||
| 291 | + public Set<PortAddresses> getAddressBindings() { | ||
| 292 | + synchronized (portAddresses) { | ||
| 293 | + return new HashSet<>(portAddresses.values()); | ||
| 294 | + } | ||
| 295 | + } | ||
| 296 | + | ||
| 297 | + @Override | ||
| 298 | + public PortAddresses getAddressBindingsForPort(ConnectPoint connectPoint) { | ||
| 299 | + PortAddresses addresses; | ||
| 300 | + | ||
| 301 | + synchronized (portAddresses) { | ||
| 302 | + addresses = portAddresses.get(connectPoint); | ||
| 303 | + } | ||
| 304 | + | ||
| 305 | + if (addresses == null) { | ||
| 306 | + addresses = new PortAddresses(connectPoint, null, null); | ||
| 307 | + } | ||
| 308 | + | ||
| 309 | + return addresses; | ||
| 310 | + } | ||
| 311 | + | ||
| 312 | + // Auxiliary extension to allow location to mutate. | ||
| 313 | + private class StoredHost extends DefaultHost { | ||
| 314 | + private Timestamped<HostLocation> location; | ||
| 315 | + | ||
| 316 | + /** | ||
| 317 | + * Creates an end-station host using the supplied information. | ||
| 318 | + * | ||
| 319 | + * @param providerId provider identity | ||
| 320 | + * @param id host identifier | ||
| 321 | + * @param mac host MAC address | ||
| 322 | + * @param vlan host VLAN identifier | ||
| 323 | + * @param location host location | ||
| 324 | + * @param ips host IP addresses | ||
| 325 | + * @param annotations optional key/value annotations | ||
| 326 | + */ | ||
| 327 | + public StoredHost(ProviderId providerId, HostId id, | ||
| 328 | + MacAddress mac, VlanId vlan, Timestamped<HostLocation> location, | ||
| 329 | + Set<IpPrefix> ips, Annotations... annotations) { | ||
| 330 | + super(providerId, id, mac, vlan, location.value(), ips, annotations); | ||
| 331 | + this.location = location; | ||
| 332 | + } | ||
| 333 | + | ||
| 334 | + void setLocation(Timestamped<HostLocation> location) { | ||
| 335 | + this.location = location; | ||
| 336 | + } | ||
| 337 | + | ||
| 338 | + @Override | ||
| 339 | + public HostLocation location() { | ||
| 340 | + return location.value(); | ||
| 341 | + } | ||
| 342 | + } | ||
| 343 | +} |
-
Please register or login to post a comment