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.inventory.rev130819.NodeConnectorId;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
37 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
41 import com.google.common.base.Function;
42 import com.google.common.base.Objects;
43 import com.google.common.base.Predicate;
44 import com.google.common.collect.FluentIterable;
45 import com.google.common.collect.ImmutableList;
46 import com.google.common.collect.Maps;
49 * Manage connected switches and ensure their configuration is set up
52 public class SwitchManager implements AutoCloseable {
54 private static final Logger LOG = LoggerFactory.getLogger(SwitchManager.class);
56 protected Map<NodeId, SwitchState> switches = new HashMap<>();
57 protected List<SwitchListener> listeners = new CopyOnWriteArrayList<>();
59 private final FlowCapableNodeListener nodeListener;
60 private final OfOverlayNodeListener ofOverlayNodeListener;
61 private final FlowCapableNodeConnectorListener nodeConnectorListener;
63 public SwitchManager(DataBroker dataProvider) {
64 if (dataProvider == null) {
65 LOG.warn("No data provider for {}. Listeners {}, {}, {} are not registered.",
66 SwitchManager.class.getSimpleName(), FlowCapableNodeListener.class.getSimpleName(),
67 OfOverlayNodeListener.class.getSimpleName(), FlowCapableNodeConnectorListener.class.getSimpleName());
69 ofOverlayNodeListener = null;
70 nodeConnectorListener = null;
72 nodeListener = new FlowCapableNodeListener(dataProvider, this);
73 ofOverlayNodeListener = new OfOverlayNodeListener(dataProvider, this);
74 nodeConnectorListener = new FlowCapableNodeConnectorListener(dataProvider, this);
76 LOG.debug("Initialized OFOverlay switch manager");
80 * Get the collection of switches that are in the "ready" state. Note
81 * that the collection is immutable.
83 * @return A {@link Collection} containing the switches that are ready.
85 public synchronized Collection<NodeId> getReadySwitches() {
86 ImmutableList<NodeId> readySwitches = FluentIterable.from(switches.values())
87 .filter(new Predicate<SwitchState>() {
90 public boolean apply(SwitchState input) {
91 return input.status == SwitchStatus.READY;
94 .transform(new Function<SwitchState, NodeId>() {
97 public NodeId apply(SwitchState input) {
102 LOG.trace("Get ready switches: {}", readySwitches);
103 return readySwitches;
106 public synchronized Set<NodeConnectorId> getExternalPorts(NodeId nodeId) {
107 SwitchState state = switches.get(nodeId);
109 return Collections.emptySet();
110 return state.externalPorts;
113 public synchronized NodeConnectorId getTunnelPort(NodeId nodeId) {
114 SwitchState state = switches.get(nodeId);
117 return state.tunnelPort;
120 public synchronized IpAddress getTunnelIP(NodeId nodeId) {
121 SwitchState state = switches.get(nodeId);
122 if (state == null || state.nodeConfig == null)
124 return state.nodeConfig.getTunnelIp();
128 * Add a {@link SwitchListener} to get notifications of switch events
130 * @param listener the {@link SwitchListener} to add
132 public void registerListener(SwitchListener listener) {
133 listeners.add(listener);
137 * Set the encapsulation format the specified value
139 * @param format The new format
141 public void setEncapsulationFormat(EncapsulationFormat format) {
145 synchronized void updateSwitch(NodeId nodeId, @Nullable FlowCapableNode fcNode) {
146 SwitchState state = getSwitchState(checkNotNull(nodeId));
147 SwitchStatus oldStatus = state.status;
148 state.setFlowCapableNode(fcNode);
149 handleSwitchState(state, oldStatus);
152 synchronized void updateSwitchNodeConnectorConfig(InstanceIdentifier<NodeConnector> ncIid,
153 @Nullable FlowCapableNodeConnector fcnc) {
154 NodeId nodeId = ncIid.firstKeyOf(Node.class, NodeKey.class).getId();
155 SwitchState state = getSwitchState(nodeId);
156 SwitchStatus oldStatus = state.status;
157 state.setNodeConnectorConfig(ncIid, fcnc);
158 handleSwitchState(state, oldStatus);
161 synchronized void updateSwitchConfig(NodeId nodeId, @Nullable OfOverlayNodeConfig config) {
162 SwitchState state = getSwitchState(checkNotNull(nodeId));
163 SwitchStatus oldStatus = state.status;
164 state.setConfig(config);
165 handleSwitchState(state, oldStatus);
168 private SwitchState getSwitchState(NodeId id) {
169 SwitchState state = switches.get(id);
171 state = new SwitchState(id);
172 switches.put(id, state);
173 LOG.trace("Switch {} added to switches {}", state.nodeId.getValue(), switches.keySet());
178 private void handleSwitchState(SwitchState state, SwitchStatus oldStatus) {
179 if (oldStatus == SwitchStatus.READY && state.status != SwitchStatus.READY) {
180 LOG.info("Switch {} removed", state.nodeId.getValue());
181 notifySwitchRemoved(state.nodeId);
182 } else if (oldStatus != SwitchStatus.READY && state.status == SwitchStatus.READY) {
183 LOG.info("Switch {} ready", state.nodeId.getValue());
184 notifySwitchReady(state.nodeId);
185 } else if (oldStatus == SwitchStatus.READY && state.status == SwitchStatus.READY) {
186 // TODO Be msunal we could improve this by ignoring of updates where uninteresting fields are changed
187 LOG.debug("Switch {} updated", state.nodeId.getValue());
188 notifySwitchUpdated(state.nodeId);
190 if (state.status == SwitchStatus.DISCONNECTED && state.isConfigurationEmpty()) {
191 switches.remove(state.nodeId);
192 LOG.trace("Switch {} removed from switches {}", state.nodeId, switches.keySet());
196 private void notifySwitchRemoved(NodeId nodeId) {
197 for (SwitchListener listener : listeners) {
198 listener.switchRemoved(nodeId);
202 private void notifySwitchReady(NodeId nodeId) {
203 for (SwitchListener listener : listeners) {
204 listener.switchReady(nodeId);
208 private void notifySwitchUpdated(NodeId nodeId) {
209 for (SwitchListener listener : listeners) {
210 listener.switchUpdated(nodeId);
215 public void close() throws Exception {
216 nodeListener.close();
217 ofOverlayNodeListener.close();
218 nodeConnectorListener.close();
222 * Internal representation of the state of a connected switch
224 protected static final class SwitchState {
226 private NodeId nodeId;
227 private FlowCapableNode fcNode;
228 private OfOverlayNodeConfig nodeConfig;
229 private Map<InstanceIdentifier<NodeConnector>, FlowCapableNodeConnector> fcncByNcIid = Maps.newHashMap();
231 NodeConnectorId tunnelPort;
232 Set<NodeConnectorId> externalPorts = new HashSet<>();
236 public SwitchState(NodeId switchNode) {
242 * Constructor used for tests
244 public SwitchState(NodeId node, NodeConnectorId tunnelPort, Set<NodeConnectorId> externalPorts,
245 OfOverlayNodeConfig nodeConfig) {
247 this.tunnelPort = tunnelPort;
248 this.externalPorts = externalPorts;
249 this.nodeConfig = nodeConfig;
252 private void update() {
253 HashSet<NodeConnectorId> externalPorts = new HashSet<>();
254 NodeConnectorId tunnelPort = null;
255 for (Entry<InstanceIdentifier<NodeConnector>, FlowCapableNodeConnector> fcncByNcIidEntry : fcncByNcIid.entrySet()) {
256 FlowCapableNodeConnector fcnc = fcncByNcIidEntry.getValue();
257 if (fcnc.getName() == null) {
260 InstanceIdentifier<NodeConnector> ncIid = fcncByNcIidEntry.getKey();
261 NodeConnectorId ncId = ncIid.firstKeyOf(NodeConnector.class, NodeConnectorKey.class).getId();
262 if (fcnc.getName().matches(".*(vxlan|tun).*")) {
265 if (nodeConfig != null && nodeConfig.getExternalInterfaces() != null) {
266 for (String pattern : nodeConfig.getExternalInterfaces()) {
267 if (fcnc.getName().matches(pattern)) {
268 externalPorts.add(ncId);
274 this.tunnelPort = tunnelPort;
275 this.externalPorts = Collections.unmodifiableSet(externalPorts);
278 private void updateStatus() {
279 boolean tunnelPortWithIpExists = tunnelPortWithIpExists();
280 if (fcNode != null) {
281 if (tunnelPortWithIpExists) {
282 setStatus(SwitchStatus.READY);
284 setStatus(SwitchStatus.PREPARING);
287 setStatus(SwitchStatus.DISCONNECTED);
291 private void setStatus(SwitchStatus newStatus) {
292 if (Objects.equal(status, newStatus)) {
295 LOG.debug("Switch {} is changing status from {} to {}", nodeId.getValue(), this.status, newStatus);
296 this.status = newStatus;
299 private boolean tunnelPortWithIpExists() {
300 boolean tunnelPortWithIpExists = false;
301 if (tunnelPort != null && nodeConfig != null && nodeConfig.getTunnelIp() != null) {
302 tunnelPortWithIpExists = true;
304 LOG.trace("Status of tunnel on switch {} - tunnelPort: {} tunnelIp: {}", nodeId.getValue(),
305 tunnelPort == null ? null : tunnelPort.getValue(),
306 nodeConfig == null ? null : nodeConfig.getTunnelIp());
307 return tunnelPortWithIpExists;
310 public boolean isConfigurationEmpty() {
313 if (nodeConfig != null)
315 if (!fcncByNcIid.isEmpty())
320 public void setFlowCapableNode(FlowCapableNode fcNode) {
321 this.fcNode = fcNode;
322 LOG.trace("Switch {} set {}", nodeId.getValue(), fcNode);
326 public void setConfig(OfOverlayNodeConfig config) {
327 this.nodeConfig = config;
328 LOG.trace("Switch {} set {}", nodeId.getValue(), config);
333 public void setNodeConnectorConfig(InstanceIdentifier<NodeConnector> ncIid, FlowCapableNodeConnector fcnc) {
335 fcncByNcIid.remove(ncIid);
337 fcncByNcIid.put(ncIid, fcnc);
339 LOG.trace("Switch {} node connector {} set {}", nodeId.getValue(),
340 ncIid.firstKeyOf(NodeConnector.class, NodeConnectorKey.class).getId().getValue(), fcnc);
347 protected enum SwitchStatus {
349 * The switch is not currently connected
353 * The switch is connected but not yet configured
357 * The switch is ready to for policy rules to be installed