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.controller.md.sal.binding.api.DataBroker;
26 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayConfig.EncapsulationFormat;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayNodeConfig;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.nodes.node.Tunnel;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.nodes.node.TunnelBuilder;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.overlay.rev150105.TunnelTypeBase;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.overlay.rev150105.TunnelTypeVxlan;
41 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
45 import com.google.common.base.Function;
46 import com.google.common.base.Objects;
47 import com.google.common.base.Predicate;
48 import com.google.common.collect.FluentIterable;
49 import com.google.common.collect.ImmutableList;
50 import com.google.common.collect.ImmutableSet;
51 import com.google.common.collect.Maps;
54 * Manage connected switches and ensure their configuration is set up
57 public class SwitchManager implements AutoCloseable {
59 private static final Logger LOG = LoggerFactory.getLogger(SwitchManager.class);
61 protected static Map<NodeId, SwitchState> switches = new HashMap<>();
62 protected List<SwitchListener> listeners = new CopyOnWriteArrayList<>();
64 private final FlowCapableNodeListener nodeListener;
65 private final OfOverlayNodeListener ofOverlayNodeListener;
66 private final FlowCapableNodeConnectorListener nodeConnectorListener;
68 public SwitchManager(DataBroker dataProvider) {
69 if (dataProvider == null) {
70 LOG.warn("No data provider for {}. Listeners {}, {}, {} are not registered.",
71 SwitchManager.class.getSimpleName(), FlowCapableNodeListener.class.getSimpleName(),
72 OfOverlayNodeListener.class.getSimpleName(), FlowCapableNodeConnectorListener.class.getSimpleName());
74 ofOverlayNodeListener = null;
75 nodeConnectorListener = null;
77 nodeListener = new FlowCapableNodeListener(dataProvider, this);
78 ofOverlayNodeListener = new OfOverlayNodeListener(dataProvider, this);
79 nodeConnectorListener = new FlowCapableNodeConnectorListener(dataProvider, this);
81 LOG.debug("Initialized OFOverlay switch manager");
84 // When first endpoint is attached to switch, it can be ready
85 public static void activateEndpoint(NodeId nodeId) {
86 switches.get(nodeId).hasEndpoints=true;
87 switches.get(nodeId).updateStatus();
90 // When last endpoint is removed from switch, it is no longer ready
91 public static void deactivateEndpoint(NodeId nodeId) {
92 switches.get(nodeId).hasEndpoints=false;
93 switches.get(nodeId).updateStatus();
97 * Get the collection of switches that are in the "ready" state. Note
98 * that the collection is immutable.
100 * @return A {@link Collection} containing the switches that are ready.
102 public synchronized Collection<NodeId> getReadySwitches() {
103 ImmutableList<NodeId> readySwitches = FluentIterable.from(switches.values())
104 .filter(new Predicate<SwitchState>() {
107 public boolean apply(SwitchState input) {
108 return input.status == SwitchStatus.READY;
111 .transform(new Function<SwitchState, NodeId>() {
114 public NodeId apply(SwitchState input) {
119 LOG.trace("Get ready switches: {}", readySwitches);
120 return readySwitches;
123 public synchronized Set<NodeConnectorId> getExternalPorts(NodeId nodeId) {
124 SwitchState state = switches.get(nodeId);
126 return Collections.emptySet();
127 return ImmutableSet.copyOf(state.externalPorts);
130 public static synchronized NodeConnectorId getTunnelPort(NodeId nodeId, Class<? extends TunnelTypeBase> tunnelType) {
131 SwitchState state = switches.get(nodeId);
135 TunnelBuilder tunnel = state.tunnelBuilderByType.get(tunnelType);
136 if (tunnel == null) {
139 return tunnel.getNodeConnectorId();
142 public static synchronized IpAddress getTunnelIP(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.getIp();
155 * Add a {@link SwitchListener} to get notifications of switch events
157 * @param listener the {@link SwitchListener} to add
159 public void registerListener(SwitchListener listener) {
160 listeners.add(listener);
164 * Set the encapsulation format the specified value
166 * @param format The new format
168 public void setEncapsulationFormat(EncapsulationFormat format) {
172 synchronized void updateSwitch(NodeId nodeId, @Nullable FlowCapableNode fcNode) {
173 SwitchState state = getSwitchState(checkNotNull(nodeId));
174 SwitchStatus oldStatus = state.status;
175 state.setFlowCapableNode(fcNode);
176 handleSwitchState(state, oldStatus);
179 synchronized void updateSwitchNodeConnectorConfig(InstanceIdentifier<NodeConnector> ncIid,
180 @Nullable FlowCapableNodeConnector fcnc) {
181 NodeId nodeId = ncIid.firstKeyOf(Node.class, NodeKey.class).getId();
182 SwitchState state = getSwitchState(nodeId);
183 SwitchStatus oldStatus = state.status;
184 state.setNodeConnectorConfig(ncIid, fcnc);
185 handleSwitchState(state, oldStatus);
188 synchronized void updateSwitchConfig(NodeId nodeId, @Nullable OfOverlayNodeConfig config) {
189 SwitchState state = getSwitchState(checkNotNull(nodeId));
190 SwitchStatus oldStatus = state.status;
191 state.setConfig(config);
192 handleSwitchState(state, oldStatus);
195 private SwitchState getSwitchState(NodeId id) {
196 SwitchState state = switches.get(id);
198 state = new SwitchState(id);
199 switches.put(id, state);
200 LOG.trace("Switch {} added to switches {}", state.nodeId.getValue(), switches.keySet());
205 private void handleSwitchState(SwitchState state, SwitchStatus oldStatus) {
206 if (oldStatus == SwitchStatus.READY && state.status != SwitchStatus.READY) {
207 LOG.info("Switch {} removed", state.nodeId.getValue());
208 notifySwitchRemoved(state.nodeId);
209 } else if (oldStatus != SwitchStatus.READY && state.status == SwitchStatus.READY) {
210 LOG.info("Switch {} ready", state.nodeId.getValue());
211 notifySwitchReady(state.nodeId);
212 } else if (oldStatus == SwitchStatus.READY && state.status == SwitchStatus.READY) {
213 // TODO Be msunal we could improve this by ignoring of updates where uninteresting fields are changed
214 LOG.debug("Switch {} updated", state.nodeId.getValue());
215 notifySwitchUpdated(state.nodeId);
217 if (state.status == SwitchStatus.DISCONNECTED && state.isConfigurationEmpty()) {
218 switches.remove(state.nodeId);
219 LOG.trace("Switch {} removed from switches {}", state.nodeId, switches.keySet());
223 private void notifySwitchRemoved(NodeId nodeId) {
224 for (SwitchListener listener : listeners) {
225 listener.switchRemoved(nodeId);
229 private void notifySwitchReady(NodeId nodeId) {
230 for (SwitchListener listener : listeners) {
231 listener.switchReady(nodeId);
235 private void notifySwitchUpdated(NodeId nodeId) {
236 for (SwitchListener listener : listeners) {
237 listener.switchUpdated(nodeId);
242 public void close() throws Exception {
243 nodeListener.close();
244 ofOverlayNodeListener.close();
245 nodeConnectorListener.close();
249 * Internal representation of the state of a connected switch
251 protected static final class SwitchState {
253 private NodeId nodeId;
254 private FlowCapableNode fcNode;
255 private OfOverlayNodeConfig nodeConfig;
256 private Map<InstanceIdentifier<NodeConnector>, FlowCapableNodeConnector> fcncByNcIid = Maps.newHashMap();
257 private boolean hasEndpoints=false;
259 Map<Class<? extends TunnelTypeBase>, TunnelBuilder> tunnelBuilderByType = new HashMap<>();
260 Set<NodeConnectorId> externalPorts = new HashSet<>();
264 public SwitchState(NodeId switchNode) {
270 * Constructor used for tests
272 public SwitchState(NodeId node, NodeConnectorId tunnelPort, Set<NodeConnectorId> externalPorts,
273 OfOverlayNodeConfig nodeConfig) {
275 this.nodeConfig = nodeConfig;
277 this.externalPorts = externalPorts;
280 private void update() {
281 tunnelBuilderByType = new HashMap<>();
282 externalPorts = new HashSet<>();
283 for (Entry<InstanceIdentifier<NodeConnector>, FlowCapableNodeConnector> fcncByNcIidEntry : fcncByNcIid.entrySet()) {
284 FlowCapableNodeConnector fcnc = fcncByNcIidEntry.getValue();
285 if (fcnc.getName() == null) {
288 InstanceIdentifier<NodeConnector> ncIid = fcncByNcIidEntry.getKey();
289 NodeConnectorId ncId = ncIid.firstKeyOf(NodeConnector.class, NodeConnectorKey.class).getId();
290 if (fcnc.getName().matches(".*(vxlan).*")) {
291 TunnelBuilder tunnelBuilder = tunnelBuilderByType.get(TunnelTypeVxlan.class);
292 if (tunnelBuilder == null) {
293 tunnelBuilder = new TunnelBuilder().setTunnelType(TunnelTypeVxlan.class);
294 tunnelBuilderByType.put(TunnelTypeVxlan.class, tunnelBuilder);
296 tunnelBuilder.setNodeConnectorId(ncId);
298 if (nodeConfig != null && nodeConfig.getExternalInterfaces() != null) {
299 for (String pattern : nodeConfig.getExternalInterfaces()) {
300 if (fcnc.getName().matches(pattern)) {
301 externalPorts.add(ncId);
307 if (nodeConfig != null && nodeConfig.getTunnel() != null) {
308 for (Tunnel tunnel : nodeConfig.getTunnel()) {
309 TunnelBuilder tunnelBuilder = tunnelBuilderByType.get(tunnel.getTunnelType());
310 if (tunnelBuilder == null) {
311 tunnelBuilder = new TunnelBuilder();
312 tunnelBuilderByType.put(tunnel.getTunnelType(), tunnelBuilder);
314 if (tunnel.getIp() != null) {
315 tunnelBuilder.setIp(tunnel.getIp());
317 if (tunnel.getNodeConnectorId() != null) {
318 tunnelBuilder.setNodeConnectorId(tunnel.getNodeConnectorId());
320 if (tunnel.getPort() != null) {
321 tunnelBuilder.setPort(tunnel.getPort());
327 private void updateStatus() {
328 boolean tunnelWithIpAndNcExists = tunnelWithIpAndNcExists();
329 if (fcNode != null) {
330 if (tunnelWithIpAndNcExists && hasEndpoints) {
331 setStatus(SwitchStatus.READY);
333 setStatus(SwitchStatus.PREPARING);
336 setStatus(SwitchStatus.DISCONNECTED);
340 private void setStatus(SwitchStatus newStatus) {
341 if (Objects.equal(status, newStatus)) {
344 LOG.debug("Switch {} is changing status from {} to {}", nodeId.getValue(), this.status, newStatus);
345 this.status = newStatus;
348 private boolean tunnelWithIpAndNcExists() {
349 if (tunnelBuilderByType.isEmpty()) {
350 LOG.trace("No tunnel on switch {}", nodeId.getValue());
353 LOG.trace("Iterating over tunnel till tunnel with IP and node-connector is not found.");
354 for (TunnelBuilder tb : tunnelBuilderByType.values()) {
355 if (tb.getIp() != null && tb.getNodeConnectorId() != null) {
356 // LOG.trace("Tunnel found. Type: {} IP: {} Port: {} Node-connector: {}", tb.getTunnelType()
357 // .getSimpleName(), tb.getIp(), tb.getPort(), tb.getNodeConnectorId());
358 LOG.trace("Tunnel found.");
361 // LOG.trace("Tunnel which is not completed: Type: {} IP: {} Port: {} Node-connector: {}",
362 // tb.getTunnelType().getSimpleName(), tb.getIp(), tb.getPort(), tb.getNodeConnectorId());
363 LOG.trace("Tunnel which is not completed");
369 public boolean isConfigurationEmpty() {
372 if (nodeConfig != null)
374 if (!fcncByNcIid.isEmpty())
379 public void setFlowCapableNode(FlowCapableNode fcNode) {
380 this.fcNode = fcNode;
381 LOG.trace("Switch {} set {}", nodeId.getValue(), fcNode);
385 public void setConfig(OfOverlayNodeConfig config) {
386 this.nodeConfig = config;
387 LOG.trace("Switch {} set {}", nodeId.getValue(), config);
392 public void setNodeConnectorConfig(InstanceIdentifier<NodeConnector> ncIid, FlowCapableNodeConnector fcnc) {
394 fcncByNcIid.remove(ncIid);
396 fcncByNcIid.put(ncIid, fcnc);
398 LOG.trace("Switch {} node connector {} set {}", nodeId.getValue(),
399 ncIid.firstKeyOf(NodeConnector.class, NodeConnectorKey.class).getId().getValue(), fcnc);
406 protected enum SwitchStatus {
408 * The switch is not currently connected
412 * The switch is connected but not yet configured
416 * The switch is ready to for policy rules to be installed