Bug 4098 - ofoverlay-renderer failure in jdk8
[groupbasedpolicy.git] / renderers / ofoverlay / src / main / java / org / opendaylight / groupbasedpolicy / renderer / ofoverlay / node / SwitchManager.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.groupbasedpolicy.renderer.ofoverlay.node;
10
11 import static com.google.common.base.Preconditions.checkNotNull;
12
13 import java.util.Collection;
14 import java.util.Collections;
15 import java.util.HashMap;
16 import java.util.HashSet;
17 import java.util.List;
18 import java.util.Map;
19 import java.util.Map.Entry;
20 import java.util.Set;
21 import java.util.concurrent.CopyOnWriteArrayList;
22
23 import javax.annotation.Nullable;
24
25 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.node.SwitchListener;
26 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
27 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayConfig.EncapsulationFormat;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayNodeConfig;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.nodes.node.ExternalInterfaces;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.nodes.node.Tunnel;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.nodes.node.TunnelBuilder;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.overlay.rev150105.TunnelTypeBase;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.overlay.rev150105.TunnelTypeVxlan;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.overlay.rev150105.TunnelTypeVxlanGpe;
44 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47
48 import com.google.common.base.Function;
49 import com.google.common.base.Objects;
50 import com.google.common.base.Predicate;
51 import com.google.common.collect.Collections2;
52 import com.google.common.collect.FluentIterable;
53 import com.google.common.collect.ImmutableList;
54 import com.google.common.collect.ImmutableSet;
55 import com.google.common.collect.Maps;
56
57 /**
58  * Manage connected switches and ensure their configuration is set up
59  * correctly
60  */
61 public class SwitchManager implements AutoCloseable {
62
63     private static final Logger LOG = LoggerFactory.getLogger(SwitchManager.class);
64
65     protected static Map<NodeId, SwitchState> switches = new HashMap<>();
66     protected List<SwitchListener> listeners = new CopyOnWriteArrayList<>();
67
68     private final FlowCapableNodeListener nodeListener;
69     private final OfOverlayNodeListener ofOverlayNodeListener;
70     private final FlowCapableNodeConnectorListener nodeConnectorListener;
71
72     public SwitchManager(DataBroker dataProvider) {
73         if (dataProvider == null) {
74             LOG.warn("No data provider for {}. Listeners {}, {}, {} are not registered.",
75                     SwitchManager.class.getSimpleName(), FlowCapableNodeListener.class.getSimpleName(),
76                     OfOverlayNodeListener.class.getSimpleName(), FlowCapableNodeConnectorListener.class.getSimpleName());
77             nodeListener = null;
78             ofOverlayNodeListener = null;
79             nodeConnectorListener = null;
80         } else {
81             nodeListener = new FlowCapableNodeListener(dataProvider, this);
82             ofOverlayNodeListener = new OfOverlayNodeListener(dataProvider, this);
83             nodeConnectorListener = new FlowCapableNodeConnectorListener(dataProvider, this);
84         }
85         LOG.debug("Initialized OFOverlay switch manager");
86     }
87
88     // When first endpoint is attached to switch, it can be ready
89     public static void activatingSwitch(NodeId nodeId) {
90         SwitchState state = switches.get(nodeId);
91         if (state == null) {
92             state = new SwitchState(nodeId);
93             switches.put(nodeId, state);
94         }
95         state.setHasEndpoints(true);
96         state.updateStatus();
97     }
98
99     // When last endpoint is removed from switch, it is no longer ready
100     public static void deactivatingSwitch(NodeId nodeId) {
101         SwitchState state = switches.get(nodeId);
102         if (state == null) {
103             LOG.error("No SwitchState for {} in deactivatingSwitch. This should not happen.",nodeId);
104             return;
105         }
106         state.setHasEndpoints(false);
107         state.updateStatus();
108     }
109
110     /**
111      * Get the collection of switches that are in the "ready" state. Note
112      * that the collection is immutable.
113      *
114      * @return A {@link Collection} containing the switches that are ready.
115      */
116     public synchronized Collection<NodeId> getReadySwitches() {
117         ImmutableList<NodeId> readySwitches = FluentIterable.from(switches.values())
118             .filter(new Predicate<SwitchState>() {
119
120                 @Override
121                 public boolean apply(SwitchState input) {
122                     return input.status == SwitchStatus.READY;
123                 }
124             })
125             .transform(new Function<SwitchState, NodeId>() {
126
127                 @Override
128                 public NodeId apply(SwitchState input) {
129                     return input.nodeId;
130                 }
131             })
132             .toList();
133         LOG.trace("Get ready switches: {}", readySwitches);
134         return readySwitches;
135     }
136
137     public synchronized Set<NodeConnectorId> getExternalPorts(NodeId nodeId) {
138         SwitchState state = switches.get(nodeId);
139         if (state == null) {
140             return Collections.emptySet();
141         }
142         return ImmutableSet.copyOf(state.externalPorts);
143     }
144
145     public synchronized Collection<NodeConnectorId> getTunnelPorts(NodeId nodeId) {
146         Collection<NodeConnectorId> ncIds = new HashSet<>();
147         SwitchState state = switches.get(nodeId);
148         if (state == null ) {
149             return Collections.emptySet();
150         }
151         ncIds = Collections2.transform(state.tunnelBuilderByType.values(),new Function<TunnelBuilder, NodeConnectorId>() {
152
153             @Override
154             public NodeConnectorId apply(TunnelBuilder input) {
155                 return input.getNodeConnectorId();
156             }
157         });
158
159         return ncIds;
160     }
161     public synchronized NodeConnectorId getTunnelPort(NodeId nodeId, Class<? extends TunnelTypeBase> tunnelType) {
162         SwitchState state = switches.get(nodeId);
163         if (state == null) {
164             return null;
165         }
166         TunnelBuilder tunnel = state.tunnelBuilderByType.get(tunnelType);
167         if (tunnel == null) {
168             return null;
169         }
170         return tunnel.getNodeConnectorId();
171     }
172
173     public synchronized IpAddress getTunnelIP(NodeId nodeId, Class<? extends TunnelTypeBase> tunnelType) {
174         SwitchState state = switches.get(nodeId);
175         if (state == null) {
176             return null;
177         }
178         TunnelBuilder tunnel = state.tunnelBuilderByType.get(tunnelType);
179         if (tunnel == null) {
180             return null;
181         }
182         return tunnel.getIp();
183     }
184
185     /**
186      * Add a {@link SwitchListener} to get notifications of switch events
187      *
188      * @param listener the {@link SwitchListener} to add
189      */
190     public void registerListener(SwitchListener listener) {
191         listeners.add(listener);
192     }
193
194     /**
195      * Set the encapsulation format the specified value
196      *
197      * @param format The new format
198      */
199     public void setEncapsulationFormat(EncapsulationFormat format) {
200         // No-op for now
201     }
202
203     synchronized void updateSwitch(NodeId nodeId, @Nullable FlowCapableNode fcNode) {
204         SwitchState state = getSwitchState(checkNotNull(nodeId));
205         SwitchStatus oldStatus = state.status;
206         state.setFlowCapableNode(fcNode);
207         handleSwitchState(state, oldStatus);
208     }
209
210     synchronized void updateSwitchNodeConnectorConfig(InstanceIdentifier<NodeConnector> ncIid,
211             @Nullable FlowCapableNodeConnector fcnc) {
212         NodeId nodeId = ncIid.firstKeyOf(Node.class, NodeKey.class).getId();
213         SwitchState state = getSwitchState(nodeId);
214         SwitchStatus oldStatus = state.status;
215         state.setNodeConnectorConfig(ncIid, fcnc);
216         handleSwitchState(state, oldStatus);
217     }
218
219     synchronized void updateSwitchConfig(NodeId nodeId, @Nullable OfOverlayNodeConfig config) {
220         SwitchState state = getSwitchState(checkNotNull(nodeId));
221         SwitchStatus oldStatus = state.status;
222         state.setConfig(config);
223         handleSwitchState(state, oldStatus);
224     }
225
226     private SwitchState getSwitchState(NodeId id) {
227         SwitchState state = switches.get(id);
228         if (state == null) {
229             state = new SwitchState(id);
230             switches.put(id, state);
231             LOG.trace("Switch {} added to switches {}", state.nodeId.getValue(), switches.keySet());
232         }
233         return state;
234     }
235
236     private void handleSwitchState(SwitchState state, SwitchStatus oldStatus) {
237         if (oldStatus == SwitchStatus.READY && state.status != SwitchStatus.READY) {
238             LOG.info("Switch {} removed", state.nodeId.getValue());
239             notifySwitchRemoved(state.nodeId);
240         } else if (oldStatus != SwitchStatus.READY && state.status == SwitchStatus.READY) {
241             LOG.info("Switch {} ready", state.nodeId.getValue());
242             notifySwitchReady(state.nodeId);
243         } else if (oldStatus == SwitchStatus.READY && state.status == SwitchStatus.READY) {
244             // TODO Be msunal we could improve this by ignoring of updates where uninteresting fields are changed
245             LOG.debug("Switch {} updated", state.nodeId.getValue());
246             notifySwitchUpdated(state.nodeId);
247         }
248         if (state.status == SwitchStatus.DISCONNECTED && state.isConfigurationEmpty()) {
249             switches.remove(state.nodeId);
250             LOG.trace("Switch {} removed from switches {}", state.nodeId, switches.keySet());
251         }
252     }
253
254     private void notifySwitchRemoved(NodeId nodeId) {
255         for (SwitchListener listener : listeners) {
256             listener.switchRemoved(nodeId);
257         }
258     }
259
260     private void notifySwitchReady(NodeId nodeId) {
261         for (SwitchListener listener : listeners) {
262             listener.switchReady(nodeId);
263         }
264     }
265
266     private void notifySwitchUpdated(NodeId nodeId) {
267         for (SwitchListener listener : listeners) {
268             listener.switchUpdated(nodeId);
269         }
270     }
271
272     @Override
273     public void close() throws Exception {
274         nodeListener.close();
275         ofOverlayNodeListener.close();
276         nodeConnectorListener.close();
277     }
278
279     /**
280      * Internal representation of the state of a connected switch
281      */
282     protected static final class SwitchState {
283
284         private NodeId nodeId;
285         private FlowCapableNode fcNode;
286         private OfOverlayNodeConfig nodeConfig;
287         private Map<InstanceIdentifier<NodeConnector>, FlowCapableNodeConnector> fcncByNcIid = Maps.newHashMap();
288         private boolean hasEndpoints=false;
289
290
291         public boolean isHasEndpoints() {
292             return hasEndpoints;
293         }
294
295
296         public void setHasEndpoints(boolean hasEndpoints) {
297             this.hasEndpoints = hasEndpoints;
298         }
299
300         Map<Class<? extends TunnelTypeBase>, TunnelBuilder> tunnelBuilderByType = new HashMap<>();
301         Set<NodeConnectorId> externalPorts = new HashSet<>();
302
303         SwitchStatus status;
304
305         public SwitchState(NodeId switchNode) {
306             super();
307             nodeId = switchNode;
308         }
309
310         /**
311          * Constructor used for tests
312          *
313          * @param node the node id
314          * @param tunnelPort the tunnel port
315          * @param externalPorts the set of expternal ports
316          * @param nodeConfig the ofoverlay node config
317          */
318         public SwitchState(NodeId node, NodeConnectorId tunnelPort, Set<NodeConnectorId> externalPorts,
319                 OfOverlayNodeConfig nodeConfig) {
320             this.nodeId = node;
321             this.nodeConfig = nodeConfig;
322             update();
323             this.externalPorts = externalPorts;
324         }
325
326         private void update() {
327             tunnelBuilderByType = new HashMap<>();
328             externalPorts = new HashSet<>();
329             if (nodeConfig != null && nodeConfig.getExternalInterfaces() != null) {
330                 for (ExternalInterfaces nc : nodeConfig.getExternalInterfaces()) {
331                     externalPorts.add(nc.getNodeConnectorId());
332                 }
333             }
334             if (nodeConfig != null && nodeConfig.getTunnel() != null) {
335                 for (Tunnel tunnel : nodeConfig.getTunnel()) {
336                     TunnelBuilder tunnelBuilder = tunnelBuilderByType.get(tunnel.getTunnelType());
337                     if (tunnelBuilder == null) {
338                         tunnelBuilder = new TunnelBuilder();
339                         tunnelBuilderByType.put(tunnel.getTunnelType(), tunnelBuilder);
340                     }
341                     if (tunnel.getIp() != null) {
342                         tunnelBuilder.setIp(tunnel.getIp());
343                     }
344                     if (tunnel.getNodeConnectorId() != null) {
345                         tunnelBuilder.setNodeConnectorId(tunnel.getNodeConnectorId());
346                     }
347                     if (tunnel.getPort() != null) {
348                         tunnelBuilder.setPort(tunnel.getPort());
349                     }
350                 }
351             }
352             for (Entry<InstanceIdentifier<NodeConnector>, FlowCapableNodeConnector> fcncByNcIidEntry : fcncByNcIid.entrySet()) {
353                 FlowCapableNodeConnector fcnc = fcncByNcIidEntry.getValue();
354                 if (fcnc.getName() == null) {
355                     continue;
356                 }
357                 InstanceIdentifier<NodeConnector> ncIid = fcncByNcIidEntry.getKey();
358                 NodeConnectorId ncId = ncIid.firstKeyOf(NodeConnector.class, NodeConnectorKey.class).getId();
359                 if (fcnc.getName().matches(".*(vxlan-).*")) {
360                     TunnelBuilder tunnelBuilder = tunnelBuilderByType.get(TunnelTypeVxlan.class);
361                     if (tunnelBuilder == null) {
362                         tunnelBuilder = new TunnelBuilder().setTunnelType(TunnelTypeVxlan.class);
363                         tunnelBuilderByType.put(TunnelTypeVxlan.class, tunnelBuilder);
364                     }
365                     tunnelBuilder.setNodeConnectorId(ncId);
366                 } else if (fcnc.getName().matches(".*(vxlangpe-).*")) {
367                     TunnelBuilder tunnelBuilder = tunnelBuilderByType.get(TunnelTypeVxlanGpe.class);
368                     if (tunnelBuilder == null) {
369                         tunnelBuilder = new TunnelBuilder().setTunnelType(TunnelTypeVxlanGpe.class);
370                         tunnelBuilderByType.put(TunnelTypeVxlanGpe.class, tunnelBuilder);
371                     }
372                     tunnelBuilder.setNodeConnectorId(ncId);
373                 }
374             }
375         }
376
377         private void updateStatus() {
378             boolean tunnelWithIpAndNcExists = tunnelWithIpAndNcExists();
379             if (fcNode != null) {
380                 if (tunnelWithIpAndNcExists && isHasEndpoints()) {
381                     setStatus(SwitchStatus.READY);
382                 } else {
383                     setStatus(SwitchStatus.PREPARING);
384                 }
385             } else {
386                 setStatus(SwitchStatus.DISCONNECTED);
387             }
388         }
389
390         private void setStatus(SwitchStatus newStatus) {
391             if (Objects.equal(status, newStatus)) {
392                 return;
393             }
394             LOG.debug("Switch {} is changing status from {} to {}", nodeId.getValue(), this.status, newStatus);
395             this.status = newStatus;
396         }
397
398         private boolean tunnelWithIpAndNcExists() {
399             if (tunnelBuilderByType.isEmpty()) {
400                 LOG.trace("No tunnel on switch {}", nodeId.getValue());
401                 return false;
402             }
403             LOG.trace("Iterating over tunnel till tunnel with IP and node-connector is not found.");
404             for (TunnelBuilder tb : tunnelBuilderByType.values()) {
405                 if (tb.getIp() != null && tb.getNodeConnectorId() != null) {
406                     LOG.trace("Tunnel {} found.",tb.toString());
407                     return true;
408                 } else {
409                     LOG.trace("Tunnel is not complete for node: {}", nodeId.getValue());
410                 }
411             }
412             return false;
413         }
414
415         public boolean isConfigurationEmpty() {
416             if (fcNode != null) {
417                 return false;
418             }
419             if (nodeConfig != null) {
420                 return false;
421             }
422             if (!fcncByNcIid.isEmpty()) {
423                 return false;
424             }
425             return true;
426         }
427
428         public void setFlowCapableNode(FlowCapableNode fcNode) {
429             this.fcNode = fcNode;
430             LOG.trace("Switch {} set {}", nodeId.getValue(), fcNode);
431             updateStatus();
432         }
433
434         public void setConfig(OfOverlayNodeConfig config) {
435             this.nodeConfig = config;
436             LOG.trace("Switch {} set {}", nodeId.getValue(), config);
437             update();
438             updateStatus();
439         }
440
441         public void setNodeConnectorConfig(InstanceIdentifier<NodeConnector> ncIid, FlowCapableNodeConnector fcnc) {
442             if (fcnc == null) {
443                 fcncByNcIid.remove(ncIid);
444             } else {
445                 fcncByNcIid.put(ncIid, fcnc);
446             }
447             LOG.trace("Switch {} node connector {} set {}", nodeId.getValue(),
448                     ncIid.firstKeyOf(NodeConnector.class, NodeConnectorKey.class).getId().getValue(), fcnc);
449             update();
450             updateStatus();
451         }
452
453     }
454
455     protected enum SwitchStatus {
456         /**
457          * The switch is not currently connected
458          */
459         DISCONNECTED,
460         /**
461          * The switch is connected but not yet configured
462          */
463         PREPARING,
464         /**
465          * The switch is ready to for policy rules to be installed
466          */
467         READY
468     }
469
470
471
472 }