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.yangtools.yang.binding.InstanceIdentifier;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
47 import com.google.common.base.Function;
48 import com.google.common.base.Objects;
49 import com.google.common.base.Predicate;
50 import com.google.common.collect.FluentIterable;
51 import com.google.common.collect.ImmutableList;
52 import com.google.common.collect.ImmutableSet;
53 import com.google.common.collect.Maps;
56 * Manage connected switches and ensure their configuration is set up
59 public class SwitchManager implements AutoCloseable {
61 private static final Logger LOG = LoggerFactory.getLogger(SwitchManager.class);
63 protected static Map<NodeId, SwitchState> switches = new HashMap<>();
64 protected List<SwitchListener> listeners = new CopyOnWriteArrayList<>();
66 private final FlowCapableNodeListener nodeListener;
67 private final OfOverlayNodeListener ofOverlayNodeListener;
68 private final FlowCapableNodeConnectorListener nodeConnectorListener;
70 public SwitchManager(DataBroker dataProvider) {
71 if (dataProvider == null) {
72 LOG.warn("No data provider for {}. Listeners {}, {}, {} are not registered.",
73 SwitchManager.class.getSimpleName(), FlowCapableNodeListener.class.getSimpleName(),
74 OfOverlayNodeListener.class.getSimpleName(), FlowCapableNodeConnectorListener.class.getSimpleName());
76 ofOverlayNodeListener = null;
77 nodeConnectorListener = null;
79 nodeListener = new FlowCapableNodeListener(dataProvider, this);
80 ofOverlayNodeListener = new OfOverlayNodeListener(dataProvider, this);
81 nodeConnectorListener = new FlowCapableNodeConnectorListener(dataProvider, this);
83 LOG.debug("Initialized OFOverlay switch manager");
86 // When first endpoint is attached to switch, it can be ready
87 public static void activatingSwitch(NodeId nodeId) {
88 SwitchState state = switches.get(nodeId);
90 state = new SwitchState(nodeId);
91 switches.put(nodeId, state);
93 state.setHasEndpoints(true);
97 // When last endpoint is removed from switch, it is no longer ready
98 public static void deactivatingSwitch(NodeId nodeId) {
99 SwitchState state = switches.get(nodeId);
101 LOG.error("No SwitchState for {} in deactivatingSwitch. This should not happen.",nodeId);
104 state.setHasEndpoints(false);;
105 state.updateStatus();
109 * Get the collection of switches that are in the "ready" state. Note
110 * that the collection is immutable.
112 * @return A {@link Collection} containing the switches that are ready.
114 public synchronized Collection<NodeId> getReadySwitches() {
115 ImmutableList<NodeId> readySwitches = FluentIterable.from(switches.values())
116 .filter(new Predicate<SwitchState>() {
119 public boolean apply(SwitchState input) {
120 return input.status == SwitchStatus.READY;
123 .transform(new Function<SwitchState, NodeId>() {
126 public NodeId apply(SwitchState input) {
131 LOG.trace("Get ready switches: {}", readySwitches);
132 return readySwitches;
135 public synchronized Set<NodeConnectorId> getExternalPorts(NodeId nodeId) {
136 SwitchState state = switches.get(nodeId);
138 return Collections.emptySet();
139 return ImmutableSet.copyOf(state.externalPorts);
142 public static synchronized NodeConnectorId getTunnelPort(NodeId nodeId, Class<? extends TunnelTypeBase> tunnelType) {
143 SwitchState state = switches.get(nodeId);
147 TunnelBuilder tunnel = state.tunnelBuilderByType.get(tunnelType);
148 if (tunnel == null) {
151 return tunnel.getNodeConnectorId();
154 public static synchronized IpAddress getTunnelIP(NodeId nodeId, Class<? extends TunnelTypeBase> tunnelType) {
155 SwitchState state = switches.get(nodeId);
159 TunnelBuilder tunnel = state.tunnelBuilderByType.get(tunnelType);
160 if (tunnel == null) {
163 return tunnel.getIp();
167 * Add a {@link SwitchListener} to get notifications of switch events
169 * @param listener the {@link SwitchListener} to add
171 public void registerListener(SwitchListener listener) {
172 listeners.add(listener);
176 * Set the encapsulation format the specified value
178 * @param format The new format
180 public void setEncapsulationFormat(EncapsulationFormat format) {
184 synchronized void updateSwitch(NodeId nodeId, @Nullable FlowCapableNode fcNode) {
185 SwitchState state = getSwitchState(checkNotNull(nodeId));
186 SwitchStatus oldStatus = state.status;
187 state.setFlowCapableNode(fcNode);
188 handleSwitchState(state, oldStatus);
191 synchronized void updateSwitchNodeConnectorConfig(InstanceIdentifier<NodeConnector> ncIid,
192 @Nullable FlowCapableNodeConnector fcnc) {
193 NodeId nodeId = ncIid.firstKeyOf(Node.class, NodeKey.class).getId();
194 SwitchState state = getSwitchState(nodeId);
195 SwitchStatus oldStatus = state.status;
196 state.setNodeConnectorConfig(ncIid, fcnc);
197 handleSwitchState(state, oldStatus);
200 synchronized void updateSwitchConfig(NodeId nodeId, @Nullable OfOverlayNodeConfig config) {
201 SwitchState state = getSwitchState(checkNotNull(nodeId));
202 SwitchStatus oldStatus = state.status;
203 state.setConfig(config);
204 handleSwitchState(state, oldStatus);
207 private SwitchState getSwitchState(NodeId id) {
208 SwitchState state = switches.get(id);
210 state = new SwitchState(id);
211 switches.put(id, state);
212 LOG.trace("Switch {} added to switches {}", state.nodeId.getValue(), switches.keySet());
217 private void handleSwitchState(SwitchState state, SwitchStatus oldStatus) {
218 if (oldStatus == SwitchStatus.READY && state.status != SwitchStatus.READY) {
219 LOG.info("Switch {} removed", state.nodeId.getValue());
220 notifySwitchRemoved(state.nodeId);
221 } else if (oldStatus != SwitchStatus.READY && state.status == SwitchStatus.READY) {
222 LOG.info("Switch {} ready", state.nodeId.getValue());
223 notifySwitchReady(state.nodeId);
224 } else if (oldStatus == SwitchStatus.READY && state.status == SwitchStatus.READY) {
225 // TODO Be msunal we could improve this by ignoring of updates where uninteresting fields are changed
226 LOG.debug("Switch {} updated", state.nodeId.getValue());
227 notifySwitchUpdated(state.nodeId);
229 if (state.status == SwitchStatus.DISCONNECTED && state.isConfigurationEmpty()) {
230 switches.remove(state.nodeId);
231 LOG.trace("Switch {} removed from switches {}", state.nodeId, switches.keySet());
235 private void notifySwitchRemoved(NodeId nodeId) {
236 for (SwitchListener listener : listeners) {
237 listener.switchRemoved(nodeId);
241 private void notifySwitchReady(NodeId nodeId) {
242 for (SwitchListener listener : listeners) {
243 listener.switchReady(nodeId);
247 private void notifySwitchUpdated(NodeId nodeId) {
248 for (SwitchListener listener : listeners) {
249 listener.switchUpdated(nodeId);
254 public void close() throws Exception {
255 nodeListener.close();
256 ofOverlayNodeListener.close();
257 nodeConnectorListener.close();
261 * Internal representation of the state of a connected switch
263 protected static final class SwitchState {
265 private NodeId nodeId;
266 private FlowCapableNode fcNode;
267 private OfOverlayNodeConfig nodeConfig;
268 private Map<InstanceIdentifier<NodeConnector>, FlowCapableNodeConnector> fcncByNcIid = Maps.newHashMap();
269 private boolean hasEndpoints=false;
272 public boolean isHasEndpoints() {
277 public void setHasEndpoints(boolean hasEndpoints) {
278 this.hasEndpoints = hasEndpoints;
281 Map<Class<? extends TunnelTypeBase>, TunnelBuilder> tunnelBuilderByType = new HashMap<>();
282 Set<NodeConnectorId> externalPorts = new HashSet<>();
286 public SwitchState(NodeId switchNode) {
292 * Constructor used for tests
294 public SwitchState(NodeId node, NodeConnectorId tunnelPort, Set<NodeConnectorId> externalPorts,
295 OfOverlayNodeConfig nodeConfig) {
297 this.nodeConfig = nodeConfig;
299 this.externalPorts = externalPorts;
302 private void update() {
303 tunnelBuilderByType = new HashMap<>();
304 externalPorts = new HashSet<>();
305 for (Entry<InstanceIdentifier<NodeConnector>, FlowCapableNodeConnector> fcncByNcIidEntry : fcncByNcIid.entrySet()) {
306 FlowCapableNodeConnector fcnc = fcncByNcIidEntry.getValue();
307 if (fcnc.getName() == null) {
310 InstanceIdentifier<NodeConnector> ncIid = fcncByNcIidEntry.getKey();
311 NodeConnectorId ncId = ncIid.firstKeyOf(NodeConnector.class, NodeConnectorKey.class).getId();
312 if (fcnc.getName().matches(".*(vxlan).*")) {
313 TunnelBuilder tunnelBuilder = tunnelBuilderByType.get(TunnelTypeVxlan.class);
314 if (tunnelBuilder == null) {
315 tunnelBuilder = new TunnelBuilder().setTunnelType(TunnelTypeVxlan.class);
316 tunnelBuilderByType.put(TunnelTypeVxlan.class, tunnelBuilder);
318 tunnelBuilder.setNodeConnectorId(ncId);
322 if (nodeConfig != null && nodeConfig.getExternalInterfaces() != null) {
323 for (ExternalInterfaces nc : nodeConfig.getExternalInterfaces()) {
324 externalPorts.add(nc.getNodeConnectorId());
327 if (nodeConfig != null && nodeConfig.getTunnel() != null) {
328 for (Tunnel tunnel : nodeConfig.getTunnel()) {
329 TunnelBuilder tunnelBuilder = tunnelBuilderByType.get(tunnel.getTunnelType());
330 if (tunnelBuilder == null) {
331 tunnelBuilder = new TunnelBuilder();
332 tunnelBuilderByType.put(tunnel.getTunnelType(), tunnelBuilder);
334 if (tunnel.getIp() != null) {
335 tunnelBuilder.setIp(tunnel.getIp());
337 if (tunnel.getNodeConnectorId() != null) {
338 tunnelBuilder.setNodeConnectorId(tunnel.getNodeConnectorId());
340 if (tunnel.getPort() != null) {
341 tunnelBuilder.setPort(tunnel.getPort());
347 private void updateStatus() {
348 boolean tunnelWithIpAndNcExists = tunnelWithIpAndNcExists();
349 if (fcNode != null) {
350 if (tunnelWithIpAndNcExists && isHasEndpoints()) {
351 setStatus(SwitchStatus.READY);
353 setStatus(SwitchStatus.PREPARING);
356 setStatus(SwitchStatus.DISCONNECTED);
360 private void setStatus(SwitchStatus newStatus) {
361 if (Objects.equal(status, newStatus)) {
364 LOG.debug("Switch {} is changing status from {} to {}", nodeId.getValue(), this.status, newStatus);
365 this.status = newStatus;
368 private boolean tunnelWithIpAndNcExists() {
369 if (tunnelBuilderByType.isEmpty()) {
370 LOG.trace("No tunnel on switch {}", nodeId.getValue());
373 LOG.trace("Iterating over tunnel till tunnel with IP and node-connector is not found.");
374 for (TunnelBuilder tb : tunnelBuilderByType.values()) {
375 if (tb.getIp() != null && tb.getNodeConnectorId() != null) {
376 // LOG.trace("Tunnel found. Type: {} IP: {} Port: {} Node-connector: {}", tb.getTunnelType()
377 // .getSimpleName(), tb.getIp(), tb.getPort(), tb.getNodeConnectorId());
378 LOG.trace("Tunnel found.");
381 // LOG.trace("Tunnel which is not completed: Type: {} IP: {} Port: {} Node-connector: {}",
382 // tb.getTunnelType().getSimpleName(), tb.getIp(), tb.getPort(), tb.getNodeConnectorId());
383 LOG.trace("Tunnel which is not completed");
389 public boolean isConfigurationEmpty() {
392 if (nodeConfig != null)
394 if (!fcncByNcIid.isEmpty())
399 public void setFlowCapableNode(FlowCapableNode fcNode) {
400 this.fcNode = fcNode;
401 LOG.trace("Switch {} set {}", nodeId.getValue(), fcNode);
405 public void setConfig(OfOverlayNodeConfig config) {
406 this.nodeConfig = config;
407 LOG.trace("Switch {} set {}", nodeId.getValue(), config);
412 public void setNodeConnectorConfig(InstanceIdentifier<NodeConnector> ncIid, FlowCapableNodeConnector fcnc) {
414 fcncByNcIid.remove(ncIid);
416 fcncByNcIid.put(ncIid, fcnc);
418 LOG.trace("Switch {} node connector {} set {}", nodeId.getValue(),
419 ncIid.firstKeyOf(NodeConnector.class, NodeConnectorKey.class).getId().getValue(), fcnc);
426 protected enum SwitchStatus {
428 * The switch is not currently connected
432 * The switch is connected but not yet configured
436 * The switch is ready to for policy rules to be installed