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();
141 return ImmutableSet.copyOf(state.externalPorts);
144 public synchronized Collection<NodeConnectorId> getTunnelPorts(NodeId nodeId) {
145 Collection<NodeConnectorId> ncIds = new HashSet<>();
146 SwitchState state = switches.get(nodeId);
147 if (state == null ) {
148 return Collections.emptySet();
150 ncIds = Collections2.transform(state.tunnelBuilderByType.values(),new Function<TunnelBuilder, NodeConnectorId>() {
153 public NodeConnectorId apply(TunnelBuilder input) {
154 return input.getNodeConnectorId();
160 public synchronized NodeConnectorId getTunnelPort(NodeId nodeId, Class<? extends TunnelTypeBase> tunnelType) {
161 SwitchState state = switches.get(nodeId);
165 TunnelBuilder tunnel = state.tunnelBuilderByType.get(tunnelType);
166 if (tunnel == null) {
169 return tunnel.getNodeConnectorId();
172 public synchronized IpAddress getTunnelIP(NodeId nodeId, Class<? extends TunnelTypeBase> tunnelType) {
173 SwitchState state = switches.get(nodeId);
177 TunnelBuilder tunnel = state.tunnelBuilderByType.get(tunnelType);
178 if (tunnel == null) {
181 return tunnel.getIp();
185 * Add a {@link SwitchListener} to get notifications of switch events
187 * @param listener the {@link SwitchListener} to add
189 public void registerListener(SwitchListener listener) {
190 listeners.add(listener);
194 * Set the encapsulation format the specified value
196 * @param format The new format
198 public void setEncapsulationFormat(EncapsulationFormat format) {
202 synchronized void updateSwitch(NodeId nodeId, @Nullable FlowCapableNode fcNode) {
203 SwitchState state = getSwitchState(checkNotNull(nodeId));
204 SwitchStatus oldStatus = state.status;
205 state.setFlowCapableNode(fcNode);
206 handleSwitchState(state, oldStatus);
209 synchronized void updateSwitchNodeConnectorConfig(InstanceIdentifier<NodeConnector> ncIid,
210 @Nullable FlowCapableNodeConnector fcnc) {
211 NodeId nodeId = ncIid.firstKeyOf(Node.class, NodeKey.class).getId();
212 SwitchState state = getSwitchState(nodeId);
213 SwitchStatus oldStatus = state.status;
214 state.setNodeConnectorConfig(ncIid, fcnc);
215 handleSwitchState(state, oldStatus);
218 synchronized void updateSwitchConfig(NodeId nodeId, @Nullable OfOverlayNodeConfig config) {
219 SwitchState state = getSwitchState(checkNotNull(nodeId));
220 SwitchStatus oldStatus = state.status;
221 state.setConfig(config);
222 handleSwitchState(state, oldStatus);
225 private SwitchState getSwitchState(NodeId id) {
226 SwitchState state = switches.get(id);
228 state = new SwitchState(id);
229 switches.put(id, state);
230 LOG.trace("Switch {} added to switches {}", state.nodeId.getValue(), switches.keySet());
235 private void handleSwitchState(SwitchState state, SwitchStatus oldStatus) {
236 if (oldStatus == SwitchStatus.READY && state.status != SwitchStatus.READY) {
237 LOG.info("Switch {} removed", state.nodeId.getValue());
238 notifySwitchRemoved(state.nodeId);
239 } else if (oldStatus != SwitchStatus.READY && state.status == SwitchStatus.READY) {
240 LOG.info("Switch {} ready", state.nodeId.getValue());
241 notifySwitchReady(state.nodeId);
242 } else if (oldStatus == SwitchStatus.READY && state.status == SwitchStatus.READY) {
243 // TODO Be msunal we could improve this by ignoring of updates where uninteresting fields are changed
244 LOG.debug("Switch {} updated", state.nodeId.getValue());
245 notifySwitchUpdated(state.nodeId);
247 if (state.status == SwitchStatus.DISCONNECTED && state.isConfigurationEmpty()) {
248 switches.remove(state.nodeId);
249 LOG.trace("Switch {} removed from switches {}", state.nodeId, switches.keySet());
253 private void notifySwitchRemoved(NodeId nodeId) {
254 for (SwitchListener listener : listeners) {
255 listener.switchRemoved(nodeId);
259 private void notifySwitchReady(NodeId nodeId) {
260 for (SwitchListener listener : listeners) {
261 listener.switchReady(nodeId);
265 private void notifySwitchUpdated(NodeId nodeId) {
266 for (SwitchListener listener : listeners) {
267 listener.switchUpdated(nodeId);
272 public void close() throws Exception {
273 nodeListener.close();
274 ofOverlayNodeListener.close();
275 nodeConnectorListener.close();
279 * Internal representation of the state of a connected switch
281 protected static final class SwitchState {
283 private NodeId nodeId;
284 private FlowCapableNode fcNode;
285 private OfOverlayNodeConfig nodeConfig;
286 private Map<InstanceIdentifier<NodeConnector>, FlowCapableNodeConnector> fcncByNcIid = Maps.newHashMap();
287 private boolean hasEndpoints=false;
290 public boolean isHasEndpoints() {
295 public void setHasEndpoints(boolean hasEndpoints) {
296 this.hasEndpoints = hasEndpoints;
299 Map<Class<? extends TunnelTypeBase>, TunnelBuilder> tunnelBuilderByType = new HashMap<>();
300 Set<NodeConnectorId> externalPorts = new HashSet<>();
304 public SwitchState(NodeId switchNode) {
310 * Constructor used for tests
312 public SwitchState(NodeId node, NodeConnectorId tunnelPort, Set<NodeConnectorId> externalPorts,
313 OfOverlayNodeConfig nodeConfig) {
315 this.nodeConfig = nodeConfig;
317 this.externalPorts = externalPorts;
320 private void update() {
321 tunnelBuilderByType = new HashMap<>();
322 externalPorts = new HashSet<>();
323 if (nodeConfig != null && nodeConfig.getExternalInterfaces() != null) {
324 for (ExternalInterfaces nc : nodeConfig.getExternalInterfaces()) {
325 externalPorts.add(nc.getNodeConnectorId());
328 if (nodeConfig != null && nodeConfig.getTunnel() != null) {
329 for (Tunnel tunnel : nodeConfig.getTunnel()) {
330 TunnelBuilder tunnelBuilder = tunnelBuilderByType.get(tunnel.getTunnelType());
331 if (tunnelBuilder == null) {
332 tunnelBuilder = new TunnelBuilder();
333 tunnelBuilderByType.put(tunnel.getTunnelType(), tunnelBuilder);
335 if (tunnel.getIp() != null) {
336 tunnelBuilder.setIp(tunnel.getIp());
338 if (tunnel.getNodeConnectorId() != null) {
339 tunnelBuilder.setNodeConnectorId(tunnel.getNodeConnectorId());
341 if (tunnel.getPort() != null) {
342 tunnelBuilder.setPort(tunnel.getPort());
346 for (Entry<InstanceIdentifier<NodeConnector>, FlowCapableNodeConnector> fcncByNcIidEntry : fcncByNcIid.entrySet()) {
347 FlowCapableNodeConnector fcnc = fcncByNcIidEntry.getValue();
348 if (fcnc.getName() == null) {
351 InstanceIdentifier<NodeConnector> ncIid = fcncByNcIidEntry.getKey();
352 NodeConnectorId ncId = ncIid.firstKeyOf(NodeConnector.class, NodeConnectorKey.class).getId();
353 if (fcnc.getName().matches(".*(vxlan-).*")) {
354 TunnelBuilder tunnelBuilder = tunnelBuilderByType.get(TunnelTypeVxlan.class);
355 if (tunnelBuilder == null) {
356 tunnelBuilder = new TunnelBuilder().setTunnelType(TunnelTypeVxlan.class);
357 tunnelBuilderByType.put(TunnelTypeVxlan.class, tunnelBuilder);
359 tunnelBuilder.setNodeConnectorId(ncId);
360 } else if (fcnc.getName().matches(".*(vxlangpe-).*")) {
361 TunnelBuilder tunnelBuilder = tunnelBuilderByType.get(TunnelTypeVxlanGpe.class);
362 if (tunnelBuilder == null) {
363 tunnelBuilder = new TunnelBuilder().setTunnelType(TunnelTypeVxlanGpe.class);
364 tunnelBuilderByType.put(TunnelTypeVxlanGpe.class, tunnelBuilder);
366 tunnelBuilder.setNodeConnectorId(ncId);
371 private void updateStatus() {
372 boolean tunnelWithIpAndNcExists = tunnelWithIpAndNcExists();
373 if (fcNode != null) {
374 if (tunnelWithIpAndNcExists && isHasEndpoints()) {
375 setStatus(SwitchStatus.READY);
377 setStatus(SwitchStatus.PREPARING);
380 setStatus(SwitchStatus.DISCONNECTED);
384 private void setStatus(SwitchStatus newStatus) {
385 if (Objects.equal(status, newStatus)) {
388 LOG.debug("Switch {} is changing status from {} to {}", nodeId.getValue(), this.status, newStatus);
389 this.status = newStatus;
392 private boolean tunnelWithIpAndNcExists() {
393 if (tunnelBuilderByType.isEmpty()) {
394 LOG.trace("No tunnel on switch {}", nodeId.getValue());
397 LOG.trace("Iterating over tunnel till tunnel with IP and node-connector is not found.");
398 for (TunnelBuilder tb : tunnelBuilderByType.values()) {
399 if (tb.getIp() != null && tb.getNodeConnectorId() != null) {
400 LOG.trace("Tunnel {} found.",tb.toString());
403 LOG.trace("Tunnel is not complete for node: {}", nodeId.getValue());
409 public boolean isConfigurationEmpty() {
412 if (nodeConfig != null)
414 if (!fcncByNcIid.isEmpty())
419 public void setFlowCapableNode(FlowCapableNode fcNode) {
420 this.fcNode = fcNode;
421 LOG.trace("Switch {} set {}", nodeId.getValue(), fcNode);
425 public void setConfig(OfOverlayNodeConfig config) {
426 this.nodeConfig = config;
427 LOG.trace("Switch {} set {}", nodeId.getValue(), config);
432 public void setNodeConnectorConfig(InstanceIdentifier<NodeConnector> ncIid, FlowCapableNodeConnector fcnc) {
434 fcncByNcIid.remove(ncIid);
436 fcncByNcIid.put(ncIid, fcnc);
438 LOG.trace("Switch {} node connector {} set {}", nodeId.getValue(),
439 ncIid.firstKeyOf(NodeConnector.class, NodeConnectorKey.class).getId().getValue(), fcnc);
446 protected enum SwitchStatus {
448 * The switch is not currently connected
452 * The switch is connected but not yet configured
456 * The switch is ready to for policy rules to be installed