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.Tunnel;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.nodes.node.TunnelBuilder;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.overlay.rev150105.TunnelTypeBase;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.overlay.rev150105.TunnelTypeVxlan;
42 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
46 import com.google.common.base.Function;
47 import com.google.common.base.Objects;
48 import com.google.common.base.Predicate;
49 import com.google.common.collect.FluentIterable;
50 import com.google.common.collect.ImmutableList;
51 import com.google.common.collect.ImmutableSet;
52 import com.google.common.collect.Maps;
55 * Manage connected switches and ensure their configuration is set up
58 public class SwitchManager implements AutoCloseable {
60 private static final Logger LOG = LoggerFactory.getLogger(SwitchManager.class);
62 protected static Map<NodeId, SwitchState> switches = new HashMap<>();
63 protected List<SwitchListener> listeners = new CopyOnWriteArrayList<>();
65 private final FlowCapableNodeListener nodeListener;
66 private final OfOverlayNodeListener ofOverlayNodeListener;
67 private final FlowCapableNodeConnectorListener nodeConnectorListener;
69 public SwitchManager(DataBroker dataProvider) {
70 if (dataProvider == null) {
71 LOG.warn("No data provider for {}. Listeners {}, {}, {} are not registered.",
72 SwitchManager.class.getSimpleName(), FlowCapableNodeListener.class.getSimpleName(),
73 OfOverlayNodeListener.class.getSimpleName(), FlowCapableNodeConnectorListener.class.getSimpleName());
75 ofOverlayNodeListener = null;
76 nodeConnectorListener = null;
78 nodeListener = new FlowCapableNodeListener(dataProvider, this);
79 ofOverlayNodeListener = new OfOverlayNodeListener(dataProvider, this);
80 nodeConnectorListener = new FlowCapableNodeConnectorListener(dataProvider, this);
82 LOG.debug("Initialized OFOverlay switch manager");
85 // When first endpoint is attached to switch, it can be ready
86 public static void activatingSwitch(NodeId nodeId) {
87 switches.get(nodeId).setHasEndpoints(true);
88 switches.get(nodeId).updateStatus();
91 // When last endpoint is removed from switch, it is no longer ready
92 public static void deactivatingSwitch(NodeId nodeId) {
93 switches.get(nodeId).setHasEndpoints(false);;
94 switches.get(nodeId).updateStatus();
98 * Get the collection of switches that are in the "ready" state. Note
99 * that the collection is immutable.
101 * @return A {@link Collection} containing the switches that are ready.
103 public synchronized Collection<NodeId> getReadySwitches() {
104 ImmutableList<NodeId> readySwitches = FluentIterable.from(switches.values())
105 .filter(new Predicate<SwitchState>() {
108 public boolean apply(SwitchState input) {
109 return input.status == SwitchStatus.READY;
112 .transform(new Function<SwitchState, NodeId>() {
115 public NodeId apply(SwitchState input) {
120 LOG.trace("Get ready switches: {}", readySwitches);
121 return readySwitches;
124 public synchronized Set<NodeConnectorId> getExternalPorts(NodeId nodeId) {
125 SwitchState state = switches.get(nodeId);
127 return Collections.emptySet();
128 return ImmutableSet.copyOf(state.externalPorts);
131 public static synchronized NodeConnectorId getTunnelPort(NodeId nodeId, Class<? extends TunnelTypeBase> tunnelType) {
132 SwitchState state = switches.get(nodeId);
136 TunnelBuilder tunnel = state.tunnelBuilderByType.get(tunnelType);
137 if (tunnel == null) {
140 return tunnel.getNodeConnectorId();
143 public static synchronized IpAddress getTunnelIP(NodeId nodeId, Class<? extends TunnelTypeBase> tunnelType) {
144 SwitchState state = switches.get(nodeId);
148 TunnelBuilder tunnel = state.tunnelBuilderByType.get(tunnelType);
149 if (tunnel == null) {
152 return tunnel.getIp();
156 * Add a {@link SwitchListener} to get notifications of switch events
158 * @param listener the {@link SwitchListener} to add
160 public void registerListener(SwitchListener listener) {
161 listeners.add(listener);
165 * Set the encapsulation format the specified value
167 * @param format The new format
169 public void setEncapsulationFormat(EncapsulationFormat format) {
173 synchronized void updateSwitch(NodeId nodeId, @Nullable FlowCapableNode fcNode) {
174 SwitchState state = getSwitchState(checkNotNull(nodeId));
175 SwitchStatus oldStatus = state.status;
176 state.setFlowCapableNode(fcNode);
177 handleSwitchState(state, oldStatus);
180 synchronized void updateSwitchNodeConnectorConfig(InstanceIdentifier<NodeConnector> ncIid,
181 @Nullable FlowCapableNodeConnector fcnc) {
182 NodeId nodeId = ncIid.firstKeyOf(Node.class, NodeKey.class).getId();
183 SwitchState state = getSwitchState(nodeId);
184 SwitchStatus oldStatus = state.status;
185 state.setNodeConnectorConfig(ncIid, fcnc);
186 handleSwitchState(state, oldStatus);
189 synchronized void updateSwitchConfig(NodeId nodeId, @Nullable OfOverlayNodeConfig config) {
190 SwitchState state = getSwitchState(checkNotNull(nodeId));
191 SwitchStatus oldStatus = state.status;
192 state.setConfig(config);
193 handleSwitchState(state, oldStatus);
196 private SwitchState getSwitchState(NodeId id) {
197 SwitchState state = switches.get(id);
199 state = new SwitchState(id);
200 switches.put(id, state);
201 LOG.trace("Switch {} added to switches {}", state.nodeId.getValue(), switches.keySet());
206 private void handleSwitchState(SwitchState state, SwitchStatus oldStatus) {
207 if (oldStatus == SwitchStatus.READY && state.status != SwitchStatus.READY) {
208 LOG.info("Switch {} removed", state.nodeId.getValue());
209 notifySwitchRemoved(state.nodeId);
210 } else if (oldStatus != SwitchStatus.READY && state.status == SwitchStatus.READY) {
211 LOG.info("Switch {} ready", state.nodeId.getValue());
212 notifySwitchReady(state.nodeId);
213 } else if (oldStatus == SwitchStatus.READY && state.status == SwitchStatus.READY) {
214 // TODO Be msunal we could improve this by ignoring of updates where uninteresting fields are changed
215 LOG.debug("Switch {} updated", state.nodeId.getValue());
216 notifySwitchUpdated(state.nodeId);
218 if (state.status == SwitchStatus.DISCONNECTED && state.isConfigurationEmpty()) {
219 switches.remove(state.nodeId);
220 LOG.trace("Switch {} removed from switches {}", state.nodeId, switches.keySet());
224 private void notifySwitchRemoved(NodeId nodeId) {
225 for (SwitchListener listener : listeners) {
226 listener.switchRemoved(nodeId);
230 private void notifySwitchReady(NodeId nodeId) {
231 for (SwitchListener listener : listeners) {
232 listener.switchReady(nodeId);
236 private void notifySwitchUpdated(NodeId nodeId) {
237 for (SwitchListener listener : listeners) {
238 listener.switchUpdated(nodeId);
243 public void close() throws Exception {
244 nodeListener.close();
245 ofOverlayNodeListener.close();
246 nodeConnectorListener.close();
250 * Internal representation of the state of a connected switch
252 protected static final class SwitchState {
254 private NodeId nodeId;
255 private FlowCapableNode fcNode;
256 private OfOverlayNodeConfig nodeConfig;
257 private Map<InstanceIdentifier<NodeConnector>, FlowCapableNodeConnector> fcncByNcIid = Maps.newHashMap();
258 private boolean hasEndpoints=false;
261 public boolean isHasEndpoints() {
266 public void setHasEndpoints(boolean hasEndpoints) {
267 this.hasEndpoints = hasEndpoints;
270 Map<Class<? extends TunnelTypeBase>, TunnelBuilder> tunnelBuilderByType = new HashMap<>();
271 Set<NodeConnectorId> externalPorts = new HashSet<>();
275 public SwitchState(NodeId switchNode) {
281 * Constructor used for tests
283 public SwitchState(NodeId node, NodeConnectorId tunnelPort, Set<NodeConnectorId> externalPorts,
284 OfOverlayNodeConfig nodeConfig) {
286 this.nodeConfig = nodeConfig;
288 this.externalPorts = externalPorts;
291 private void update() {
292 tunnelBuilderByType = new HashMap<>();
293 externalPorts = new HashSet<>();
294 for (Entry<InstanceIdentifier<NodeConnector>, FlowCapableNodeConnector> fcncByNcIidEntry : fcncByNcIid.entrySet()) {
295 FlowCapableNodeConnector fcnc = fcncByNcIidEntry.getValue();
296 if (fcnc.getName() == null) {
299 InstanceIdentifier<NodeConnector> ncIid = fcncByNcIidEntry.getKey();
300 NodeConnectorId ncId = ncIid.firstKeyOf(NodeConnector.class, NodeConnectorKey.class).getId();
301 if (fcnc.getName().matches(".*(vxlan).*")) {
302 TunnelBuilder tunnelBuilder = tunnelBuilderByType.get(TunnelTypeVxlan.class);
303 if (tunnelBuilder == null) {
304 tunnelBuilder = new TunnelBuilder().setTunnelType(TunnelTypeVxlan.class);
305 tunnelBuilderByType.put(TunnelTypeVxlan.class, tunnelBuilder);
307 tunnelBuilder.setNodeConnectorId(ncId);
309 if (nodeConfig != null && nodeConfig.getExternalInterfaces() != null) {
310 for (String pattern : nodeConfig.getExternalInterfaces()) {
311 if (fcnc.getName().matches(pattern)) {
312 externalPorts.add(ncId);
318 if (nodeConfig != null && nodeConfig.getTunnel() != null) {
319 for (Tunnel tunnel : nodeConfig.getTunnel()) {
320 TunnelBuilder tunnelBuilder = tunnelBuilderByType.get(tunnel.getTunnelType());
321 if (tunnelBuilder == null) {
322 tunnelBuilder = new TunnelBuilder();
323 tunnelBuilderByType.put(tunnel.getTunnelType(), tunnelBuilder);
325 if (tunnel.getIp() != null) {
326 tunnelBuilder.setIp(tunnel.getIp());
328 if (tunnel.getNodeConnectorId() != null) {
329 tunnelBuilder.setNodeConnectorId(tunnel.getNodeConnectorId());
331 if (tunnel.getPort() != null) {
332 tunnelBuilder.setPort(tunnel.getPort());
338 private void updateStatus() {
339 boolean tunnelWithIpAndNcExists = tunnelWithIpAndNcExists();
340 if (fcNode != null) {
341 if (tunnelWithIpAndNcExists && isHasEndpoints()) {
342 setStatus(SwitchStatus.READY);
344 setStatus(SwitchStatus.PREPARING);
347 setStatus(SwitchStatus.DISCONNECTED);
351 private void setStatus(SwitchStatus newStatus) {
352 if (Objects.equal(status, newStatus)) {
355 LOG.debug("Switch {} is changing status from {} to {}", nodeId.getValue(), this.status, newStatus);
356 this.status = newStatus;
359 private boolean tunnelWithIpAndNcExists() {
360 if (tunnelBuilderByType.isEmpty()) {
361 LOG.trace("No tunnel on switch {}", nodeId.getValue());
364 LOG.trace("Iterating over tunnel till tunnel with IP and node-connector is not found.");
365 for (TunnelBuilder tb : tunnelBuilderByType.values()) {
366 if (tb.getIp() != null && tb.getNodeConnectorId() != null) {
367 // LOG.trace("Tunnel found. Type: {} IP: {} Port: {} Node-connector: {}", tb.getTunnelType()
368 // .getSimpleName(), tb.getIp(), tb.getPort(), tb.getNodeConnectorId());
369 LOG.trace("Tunnel found.");
372 // LOG.trace("Tunnel which is not completed: Type: {} IP: {} Port: {} Node-connector: {}",
373 // tb.getTunnelType().getSimpleName(), tb.getIp(), tb.getPort(), tb.getNodeConnectorId());
374 LOG.trace("Tunnel which is not completed");
380 public boolean isConfigurationEmpty() {
383 if (nodeConfig != null)
385 if (!fcncByNcIid.isEmpty())
390 public void setFlowCapableNode(FlowCapableNode fcNode) {
391 this.fcNode = fcNode;
392 LOG.trace("Switch {} set {}", nodeId.getValue(), fcNode);
396 public void setConfig(OfOverlayNodeConfig config) {
397 this.nodeConfig = config;
398 LOG.trace("Switch {} set {}", nodeId.getValue(), config);
403 public void setNodeConnectorConfig(InstanceIdentifier<NodeConnector> ncIid, FlowCapableNodeConnector fcnc) {
405 fcncByNcIid.remove(ncIid);
407 fcncByNcIid.put(ncIid, fcnc);
409 LOG.trace("Switch {} node connector {} set {}", nodeId.getValue(),
410 ncIid.firstKeyOf(NodeConnector.class, NodeConnectorKey.class).getId().getValue(), fcnc);
417 protected enum SwitchStatus {
419 * The switch is not currently connected
423 * The switch is connected but not yet configured
427 * The switch is ready to for policy rules to be installed