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