2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
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
9 package org.opendaylight.groupbasedpolicy.renderer.ofoverlay.node;
11 import static com.google.common.base.Preconditions.checkNotNull;
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;
19 import java.util.Map.Entry;
21 import java.util.concurrent.CopyOnWriteArrayList;
23 import javax.annotation.Nullable;
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;
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;
58 * Manage connected switches and ensure their configuration is set up
61 public class SwitchManager implements AutoCloseable {
63 private static final Logger LOG = LoggerFactory.getLogger(SwitchManager.class);
65 protected static Map<NodeId, SwitchState> switches = new HashMap<>();
66 protected List<SwitchListener> listeners = new CopyOnWriteArrayList<>();
68 private final FlowCapableNodeListener nodeListener;
69 private final OfOverlayNodeListener ofOverlayNodeListener;
70 private final FlowCapableNodeConnectorListener nodeConnectorListener;
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());
78 ofOverlayNodeListener = null;
79 nodeConnectorListener = null;
81 nodeListener = new FlowCapableNodeListener(dataProvider, this);
82 ofOverlayNodeListener = new OfOverlayNodeListener(dataProvider, this);
83 nodeConnectorListener = new FlowCapableNodeConnectorListener(dataProvider, this);
85 LOG.debug("Initialized OFOverlay switch manager");
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);
92 state = new SwitchState(nodeId);
93 switches.put(nodeId, state);
95 state.setHasEndpoints(true);
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);
103 LOG.error("No SwitchState for {} in deactivatingSwitch. This should not happen.",nodeId);
106 state.setHasEndpoints(false);
107 state.updateStatus();
111 * Get the collection of switches that are in the "ready" state. Note
112 * that the collection is immutable.
114 * @return A {@link Collection} containing the switches that are ready.
116 public synchronized Collection<NodeId> getReadySwitches() {
117 ImmutableList<NodeId> readySwitches = FluentIterable.from(switches.values())
118 .filter(new Predicate<SwitchState>() {
121 public boolean apply(SwitchState input) {
122 return input.status == SwitchStatus.READY;
125 .transform(new Function<SwitchState, NodeId>() {
128 public NodeId apply(SwitchState input) {
133 LOG.trace("Get ready switches: {}", readySwitches);
134 return readySwitches;
137 public synchronized Set<NodeConnectorId> getExternalPorts(NodeId nodeId) {
138 SwitchState state = switches.get(nodeId);
140 return Collections.emptySet();
142 return ImmutableSet.copyOf(state.externalPorts);
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();
151 ncIds = Collections2.transform(state.tunnelBuilderByType.values(),new Function<TunnelBuilder, NodeConnectorId>() {
154 public NodeConnectorId apply(TunnelBuilder input) {
155 return input.getNodeConnectorId();
161 public synchronized NodeConnectorId getTunnelPort(NodeId nodeId, Class<? extends TunnelTypeBase> tunnelType) {
162 SwitchState state = switches.get(nodeId);
166 TunnelBuilder tunnel = state.tunnelBuilderByType.get(tunnelType);
167 if (tunnel == null) {
170 return tunnel.getNodeConnectorId();
173 public synchronized IpAddress getTunnelIP(NodeId nodeId, Class<? extends TunnelTypeBase> tunnelType) {
174 SwitchState state = switches.get(nodeId);
178 TunnelBuilder tunnel = state.tunnelBuilderByType.get(tunnelType);
179 if (tunnel == null) {
182 return tunnel.getIp();
186 * Add a {@link SwitchListener} to get notifications of switch events
188 * @param listener the {@link SwitchListener} to add
190 public void registerListener(SwitchListener listener) {
191 listeners.add(listener);
195 * Set the encapsulation format the specified value
197 * @param format The new format
199 public void setEncapsulationFormat(EncapsulationFormat format) {
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);
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);
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);
226 private SwitchState getSwitchState(NodeId id) {
227 SwitchState state = switches.get(id);
229 state = new SwitchState(id);
230 switches.put(id, state);
231 LOG.trace("Switch {} added to switches {}", state.nodeId.getValue(), switches.keySet());
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);
248 if (state.status == SwitchStatus.DISCONNECTED && state.isConfigurationEmpty()) {
249 switches.remove(state.nodeId);
250 LOG.trace("Switch {} removed from switches {}", state.nodeId, switches.keySet());
254 private void notifySwitchRemoved(NodeId nodeId) {
255 for (SwitchListener listener : listeners) {
256 listener.switchRemoved(nodeId);
260 private void notifySwitchReady(NodeId nodeId) {
261 for (SwitchListener listener : listeners) {
262 listener.switchReady(nodeId);
266 private void notifySwitchUpdated(NodeId nodeId) {
267 for (SwitchListener listener : listeners) {
268 listener.switchUpdated(nodeId);
273 public void close() throws Exception {
274 nodeListener.close();
275 ofOverlayNodeListener.close();
276 nodeConnectorListener.close();
280 * Internal representation of the state of a connected switch
282 protected static final class SwitchState {
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;
291 public boolean isHasEndpoints() {
296 public void setHasEndpoints(boolean hasEndpoints) {
297 this.hasEndpoints = hasEndpoints;
300 Map<Class<? extends TunnelTypeBase>, TunnelBuilder> tunnelBuilderByType = new HashMap<>();
301 Set<NodeConnectorId> externalPorts = new HashSet<>();
305 public SwitchState(NodeId switchNode) {
311 * Constructor used for tests
313 public SwitchState(NodeId node, NodeConnectorId tunnelPort, Set<NodeConnectorId> externalPorts,
314 OfOverlayNodeConfig nodeConfig) {
316 this.nodeConfig = nodeConfig;
318 this.externalPorts = externalPorts;
321 private void update() {
322 tunnelBuilderByType = new HashMap<>();
323 externalPorts = new HashSet<>();
324 if (nodeConfig != null && nodeConfig.getExternalInterfaces() != null) {
325 for (ExternalInterfaces nc : nodeConfig.getExternalInterfaces()) {
326 externalPorts.add(nc.getNodeConnectorId());
329 if (nodeConfig != null && nodeConfig.getTunnel() != null) {
330 for (Tunnel tunnel : nodeConfig.getTunnel()) {
331 TunnelBuilder tunnelBuilder = tunnelBuilderByType.get(tunnel.getTunnelType());
332 if (tunnelBuilder == null) {
333 tunnelBuilder = new TunnelBuilder();
334 tunnelBuilderByType.put(tunnel.getTunnelType(), tunnelBuilder);
336 if (tunnel.getIp() != null) {
337 tunnelBuilder.setIp(tunnel.getIp());
339 if (tunnel.getNodeConnectorId() != null) {
340 tunnelBuilder.setNodeConnectorId(tunnel.getNodeConnectorId());
342 if (tunnel.getPort() != null) {
343 tunnelBuilder.setPort(tunnel.getPort());
347 for (Entry<InstanceIdentifier<NodeConnector>, FlowCapableNodeConnector> fcncByNcIidEntry : fcncByNcIid.entrySet()) {
348 FlowCapableNodeConnector fcnc = fcncByNcIidEntry.getValue();
349 if (fcnc.getName() == null) {
352 InstanceIdentifier<NodeConnector> ncIid = fcncByNcIidEntry.getKey();
353 NodeConnectorId ncId = ncIid.firstKeyOf(NodeConnector.class, NodeConnectorKey.class).getId();
354 if (fcnc.getName().matches(".*(vxlan-).*")) {
355 TunnelBuilder tunnelBuilder = tunnelBuilderByType.get(TunnelTypeVxlan.class);
356 if (tunnelBuilder == null) {
357 tunnelBuilder = new TunnelBuilder().setTunnelType(TunnelTypeVxlan.class);
358 tunnelBuilderByType.put(TunnelTypeVxlan.class, tunnelBuilder);
360 tunnelBuilder.setNodeConnectorId(ncId);
361 } else if (fcnc.getName().matches(".*(vxlangpe-).*")) {
362 TunnelBuilder tunnelBuilder = tunnelBuilderByType.get(TunnelTypeVxlanGpe.class);
363 if (tunnelBuilder == null) {
364 tunnelBuilder = new TunnelBuilder().setTunnelType(TunnelTypeVxlanGpe.class);
365 tunnelBuilderByType.put(TunnelTypeVxlanGpe.class, tunnelBuilder);
367 tunnelBuilder.setNodeConnectorId(ncId);
372 private void updateStatus() {
373 boolean tunnelWithIpAndNcExists = tunnelWithIpAndNcExists();
374 if (fcNode != null) {
375 if (tunnelWithIpAndNcExists && isHasEndpoints()) {
376 setStatus(SwitchStatus.READY);
378 setStatus(SwitchStatus.PREPARING);
381 setStatus(SwitchStatus.DISCONNECTED);
385 private void setStatus(SwitchStatus newStatus) {
386 if (Objects.equal(status, newStatus)) {
389 LOG.debug("Switch {} is changing status from {} to {}", nodeId.getValue(), this.status, newStatus);
390 this.status = newStatus;
393 private boolean tunnelWithIpAndNcExists() {
394 if (tunnelBuilderByType.isEmpty()) {
395 LOG.trace("No tunnel on switch {}", nodeId.getValue());
398 LOG.trace("Iterating over tunnel till tunnel with IP and node-connector is not found.");
399 for (TunnelBuilder tb : tunnelBuilderByType.values()) {
400 if (tb.getIp() != null && tb.getNodeConnectorId() != null) {
401 LOG.trace("Tunnel {} found.",tb.toString());
404 LOG.trace("Tunnel is not complete for node: {}", nodeId.getValue());
410 public boolean isConfigurationEmpty() {
411 if (fcNode != null) {
414 if (nodeConfig != null) {
417 if (!fcncByNcIid.isEmpty()) {
423 public void setFlowCapableNode(FlowCapableNode fcNode) {
424 this.fcNode = fcNode;
425 LOG.trace("Switch {} set {}", nodeId.getValue(), fcNode);
429 public void setConfig(OfOverlayNodeConfig config) {
430 this.nodeConfig = config;
431 LOG.trace("Switch {} set {}", nodeId.getValue(), config);
436 public void setNodeConnectorConfig(InstanceIdentifier<NodeConnector> ncIid, FlowCapableNodeConnector fcnc) {
438 fcncByNcIid.remove(ncIid);
440 fcncByNcIid.put(ncIid, fcnc);
442 LOG.trace("Switch {} node connector {} set {}", nodeId.getValue(),
443 ncIid.firstKeyOf(NodeConnector.class, NodeConnectorKey.class).getId().getValue(), fcnc);
450 protected enum SwitchStatus {
452 * The switch is not currently connected
456 * The switch is connected but not yet configured
460 * The switch is ready to for policy rules to be installed