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 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");
85 * Get the collection of switches that are in the "ready" state. Note
86 * that the collection is immutable.
88 * @return A {@link Collection} containing the switches that are ready.
90 public synchronized Collection<NodeId> getReadySwitches() {
91 ImmutableList<NodeId> readySwitches = FluentIterable.from(switches.values())
92 .filter(new Predicate<SwitchState>() {
95 public boolean apply(SwitchState input) {
96 return input.status == SwitchStatus.READY;
99 .transform(new Function<SwitchState, NodeId>() {
102 public NodeId apply(SwitchState input) {
107 LOG.trace("Get ready switches: {}", readySwitches);
108 return readySwitches;
111 public synchronized Set<NodeConnectorId> getExternalPorts(NodeId nodeId) {
112 SwitchState state = switches.get(nodeId);
114 return Collections.emptySet();
115 return ImmutableSet.copyOf(state.externalPorts);
118 public synchronized NodeConnectorId getTunnelPort(NodeId nodeId, Class<? extends TunnelTypeBase> tunnelType) {
119 SwitchState state = switches.get(nodeId);
123 TunnelBuilder tunnel = state.tunnelBuilderByType.get(tunnelType);
124 if (tunnel == null) {
127 return tunnel.getNodeConnectorId();
130 public synchronized IpAddress getTunnelIP(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.getIp();
143 * Add a {@link SwitchListener} to get notifications of switch events
145 * @param listener the {@link SwitchListener} to add
147 public void registerListener(SwitchListener listener) {
148 listeners.add(listener);
152 * Set the encapsulation format the specified value
154 * @param format The new format
156 public void setEncapsulationFormat(EncapsulationFormat format) {
160 synchronized void updateSwitch(NodeId nodeId, @Nullable FlowCapableNode fcNode) {
161 SwitchState state = getSwitchState(checkNotNull(nodeId));
162 SwitchStatus oldStatus = state.status;
163 state.setFlowCapableNode(fcNode);
164 handleSwitchState(state, oldStatus);
167 synchronized void updateSwitchNodeConnectorConfig(InstanceIdentifier<NodeConnector> ncIid,
168 @Nullable FlowCapableNodeConnector fcnc) {
169 NodeId nodeId = ncIid.firstKeyOf(Node.class, NodeKey.class).getId();
170 SwitchState state = getSwitchState(nodeId);
171 SwitchStatus oldStatus = state.status;
172 state.setNodeConnectorConfig(ncIid, fcnc);
173 handleSwitchState(state, oldStatus);
176 synchronized void updateSwitchConfig(NodeId nodeId, @Nullable OfOverlayNodeConfig config) {
177 SwitchState state = getSwitchState(checkNotNull(nodeId));
178 SwitchStatus oldStatus = state.status;
179 state.setConfig(config);
180 handleSwitchState(state, oldStatus);
183 private SwitchState getSwitchState(NodeId id) {
184 SwitchState state = switches.get(id);
186 state = new SwitchState(id);
187 switches.put(id, state);
188 LOG.trace("Switch {} added to switches {}", state.nodeId.getValue(), switches.keySet());
193 private void handleSwitchState(SwitchState state, SwitchStatus oldStatus) {
194 if (oldStatus == SwitchStatus.READY && state.status != SwitchStatus.READY) {
195 LOG.info("Switch {} removed", state.nodeId.getValue());
196 notifySwitchRemoved(state.nodeId);
197 } else if (oldStatus != SwitchStatus.READY && state.status == SwitchStatus.READY) {
198 LOG.info("Switch {} ready", state.nodeId.getValue());
199 notifySwitchReady(state.nodeId);
200 } else if (oldStatus == SwitchStatus.READY && state.status == SwitchStatus.READY) {
201 // TODO Be msunal we could improve this by ignoring of updates where uninteresting fields are changed
202 LOG.debug("Switch {} updated", state.nodeId.getValue());
203 notifySwitchUpdated(state.nodeId);
205 if (state.status == SwitchStatus.DISCONNECTED && state.isConfigurationEmpty()) {
206 switches.remove(state.nodeId);
207 LOG.trace("Switch {} removed from switches {}", state.nodeId, switches.keySet());
211 private void notifySwitchRemoved(NodeId nodeId) {
212 for (SwitchListener listener : listeners) {
213 listener.switchRemoved(nodeId);
217 private void notifySwitchReady(NodeId nodeId) {
218 for (SwitchListener listener : listeners) {
219 listener.switchReady(nodeId);
223 private void notifySwitchUpdated(NodeId nodeId) {
224 for (SwitchListener listener : listeners) {
225 listener.switchUpdated(nodeId);
230 public void close() throws Exception {
231 nodeListener.close();
232 ofOverlayNodeListener.close();
233 nodeConnectorListener.close();
237 * Internal representation of the state of a connected switch
239 protected static final class SwitchState {
241 private NodeId nodeId;
242 private FlowCapableNode fcNode;
243 private OfOverlayNodeConfig nodeConfig;
244 private Map<InstanceIdentifier<NodeConnector>, FlowCapableNodeConnector> fcncByNcIid = Maps.newHashMap();
246 Map<Class<? extends TunnelTypeBase>, TunnelBuilder> tunnelBuilderByType = new HashMap<>();
247 Set<NodeConnectorId> externalPorts = new HashSet<>();
251 public SwitchState(NodeId switchNode) {
257 * Constructor used for tests
259 public SwitchState(NodeId node, NodeConnectorId tunnelPort, Set<NodeConnectorId> externalPorts,
260 OfOverlayNodeConfig nodeConfig) {
262 this.nodeConfig = nodeConfig;
264 this.externalPorts = externalPorts;
267 private void update() {
268 tunnelBuilderByType = new HashMap<>();
269 externalPorts = new HashSet<>();
270 for (Entry<InstanceIdentifier<NodeConnector>, FlowCapableNodeConnector> fcncByNcIidEntry : fcncByNcIid.entrySet()) {
271 FlowCapableNodeConnector fcnc = fcncByNcIidEntry.getValue();
272 if (fcnc.getName() == null) {
275 InstanceIdentifier<NodeConnector> ncIid = fcncByNcIidEntry.getKey();
276 NodeConnectorId ncId = ncIid.firstKeyOf(NodeConnector.class, NodeConnectorKey.class).getId();
277 if (fcnc.getName().matches(".*(vxlan).*")) {
278 TunnelBuilder tunnelBuilder = tunnelBuilderByType.get(TunnelTypeVxlan.class);
279 if (tunnelBuilder == null) {
280 tunnelBuilder = new TunnelBuilder().setTunnelType(TunnelTypeVxlan.class);
281 tunnelBuilderByType.put(TunnelTypeVxlan.class, tunnelBuilder);
283 tunnelBuilder.setNodeConnectorId(ncId);
285 if (nodeConfig != null && nodeConfig.getExternalInterfaces() != null) {
286 for (String pattern : nodeConfig.getExternalInterfaces()) {
287 if (fcnc.getName().matches(pattern)) {
288 externalPorts.add(ncId);
294 if (nodeConfig != null && nodeConfig.getTunnel() != null) {
295 for (Tunnel tunnel : nodeConfig.getTunnel()) {
296 TunnelBuilder tunnelBuilder = tunnelBuilderByType.get(tunnel.getTunnelType());
297 if (tunnelBuilder == null) {
298 tunnelBuilder = new TunnelBuilder();
299 tunnelBuilderByType.put(tunnel.getTunnelType(), tunnelBuilder);
301 if (tunnel.getIp() != null) {
302 tunnelBuilder.setIp(tunnel.getIp());
304 if (tunnel.getNodeConnectorId() != null) {
305 tunnelBuilder.setNodeConnectorId(tunnel.getNodeConnectorId());
307 if (tunnel.getPort() != null) {
308 tunnelBuilder.setPort(tunnel.getPort());
314 private void updateStatus() {
315 boolean tunnelWithIpAndNcExists = tunnelWithIpAndNcExists();
316 if (fcNode != null) {
317 if (tunnelWithIpAndNcExists) {
318 setStatus(SwitchStatus.READY);
320 setStatus(SwitchStatus.PREPARING);
323 setStatus(SwitchStatus.DISCONNECTED);
327 private void setStatus(SwitchStatus newStatus) {
328 if (Objects.equal(status, newStatus)) {
331 LOG.debug("Switch {} is changing status from {} to {}", nodeId.getValue(), this.status, newStatus);
332 this.status = newStatus;
335 private boolean tunnelWithIpAndNcExists() {
336 if (tunnelBuilderByType.isEmpty()) {
337 LOG.trace("No tunnel on switch {}", nodeId.getValue());
340 LOG.trace("Iterating over tunnel till tunnel with IP and node-connector is not found.");
341 for (TunnelBuilder tb : tunnelBuilderByType.values()) {
342 if (tb.getIp() != null && tb.getNodeConnectorId() != null) {
343 LOG.trace("Tunnel found. Type: {} IP: {} Port: {} Node-connector: {}", tb.getTunnelType()
344 .getSimpleName(), tb.getIp(), tb.getPort(), tb.getNodeConnectorId());
347 LOG.trace("Tunnel which is not completed: Type: {} IP: {} Port: {} Node-connector: {}",
348 tb.getTunnelType().getSimpleName(), tb.getIp(), tb.getPort(), tb.getNodeConnectorId());
354 public boolean isConfigurationEmpty() {
357 if (nodeConfig != null)
359 if (!fcncByNcIid.isEmpty())
364 public void setFlowCapableNode(FlowCapableNode fcNode) {
365 this.fcNode = fcNode;
366 LOG.trace("Switch {} set {}", nodeId.getValue(), fcNode);
370 public void setConfig(OfOverlayNodeConfig config) {
371 this.nodeConfig = config;
372 LOG.trace("Switch {} set {}", nodeId.getValue(), config);
377 public void setNodeConnectorConfig(InstanceIdentifier<NodeConnector> ncIid, FlowCapableNodeConnector fcnc) {
379 fcncByNcIid.remove(ncIid);
381 fcncByNcIid.put(ncIid, fcnc);
383 LOG.trace("Switch {} node connector {} set {}", nodeId.getValue(),
384 ncIid.firstKeyOf(NodeConnector.class, NodeConnectorKey.class).getId().getValue(), fcnc);
391 protected enum SwitchStatus {
393 * The switch is not currently connected
397 * The switch is connected but not yet configured
401 * The switch is ready to for policy rules to be installed