2 * Copyright (c) 2016 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 io.fd.honeycomb.vbd.impl;
11 import com.google.common.base.Optional;
12 import com.google.common.base.Preconditions;
13 import com.google.common.collect.ArrayListMultimap;
14 import com.google.common.collect.Multimap;
15 import com.google.common.util.concurrent.CheckedFuture;
16 import com.google.common.util.concurrent.FutureCallback;
17 import com.google.common.util.concurrent.Futures;
18 import java.util.Collection;
20 import javax.annotation.concurrent.GuardedBy;
21 import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
22 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
23 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
24 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification.ModificationType;
25 import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
26 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
27 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
28 import org.opendaylight.controller.md.sal.binding.api.MountPoint;
29 import org.opendaylight.controller.md.sal.binding.api.MountPointService;
30 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
31 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
32 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
33 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
34 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
35 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
36 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesState;
37 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
38 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.external.reference.rev160129.ExternalReference;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.Vpp;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppInterfaceAugmentation;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.L2;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.L2Builder;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.l2.interconnection.BridgeBasedBuilder;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.BridgeDomains;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.bridge.domains.BridgeDomainBuilder;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.bridge.domains.BridgeDomainKey;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.topology.rev160129.NodeVbridgeAugment;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.topology.rev160129.TerminationPointVbridgeAugment;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.topology.rev160129.TopologyVbridgeAugment;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.topology.rev160129.network.topology.topology.node.BridgeMember;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.topology.rev160129.network.topology.topology.node.BridgeMemberBuilder;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.topology.rev160129.network.topology.topology.node.termination.point.InterfaceType;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.topology.rev160129.network.topology.topology.node.termination.point._interface.type.UserInterface;
55 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
56 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
57 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
58 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
59 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
60 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
61 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
62 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
63 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.node.attributes.SupportingNode;
64 import org.opendaylight.yangtools.concepts.ListenerRegistration;
65 import org.opendaylight.yangtools.yang.binding.DataObject;
66 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
67 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
68 import org.slf4j.Logger;
69 import org.slf4j.LoggerFactory;
72 * Implementation of a single Virtual Bridge Domain. It is bound to a particular network topology instance, manages
73 * bridge members and projects state into the operational data store.
75 final class BridgeDomain implements DataTreeChangeListener<Topology> {
76 private static final Logger LOG = LoggerFactory.getLogger(BridgeDomain.class);
77 private final KeyedInstanceIdentifier<Topology, TopologyKey> topology;
80 private final BindingTransactionChain chain;
81 private final ListenerRegistration<?> reg;
82 private final MountPointService mountService;
83 private TopologyVbridgeAugment config;
84 private final String bridgeDomainName;
85 private final InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.bridge.domains.BridgeDomain> iiBridgeDomainOnVPP;
86 private final String iiBridgeDomainOnVPPRest;
87 private final DataBroker dataBroker;
88 private Multimap<NodeId, KeyedInstanceIdentifier<Node, NodeKey>> nodesToVpps = ArrayListMultimap.create();
90 private BridgeDomain(final DataBroker dataBroker, final MountPointService mountService, final KeyedInstanceIdentifier<Topology, TopologyKey> topology,
91 final BindingTransactionChain chain) {
92 this.topology = Preconditions.checkNotNull(topology);
93 this.chain = Preconditions.checkNotNull(chain);
94 this.mountService = mountService;
96 this.bridgeDomainName = topology.getKey().getTopologyId().getValue();
97 this.iiBridgeDomainOnVPPRest = provideIIBrdigeDomainOnVPPRest();
98 this.iiBridgeDomainOnVPP = InstanceIdentifier.create(Vpp.class)
99 .child(BridgeDomains.class)
100 .child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.bridge.domains.BridgeDomain.class, new BridgeDomainKey(bridgeDomainName));
102 this.dataBroker = dataBroker;
103 reg = dataBroker.registerDataTreeChangeListener(
104 new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, topology), this);
107 private String provideIIBrdigeDomainOnVPPRest() {
108 final StringBuilder strBuilder = new StringBuilder();
109 strBuilder.append("v3po:vpp/bridge-domains/bridge-domain/");
110 strBuilder.append(bridgeDomainName);
111 return strBuilder.toString();
114 static BridgeDomain create(final DataBroker dataBroker,
115 MountPointService mountService, final KeyedInstanceIdentifier<Topology, TopologyKey> topology, final BindingTransactionChain chain) {
117 LOG.debug("Wiping operational state of {}", topology);
119 final WriteTransaction tx = chain.newWriteOnlyTransaction();
120 tx.delete(LogicalDatastoreType.OPERATIONAL, topology);
123 return new BridgeDomain(dataBroker, mountService, topology, chain);
126 synchronized void forceStop() {
127 LOG.info("Bridge domain {} for {} going down", this, topology);
130 LOG.info("Bridge domain {} for {} is down", this, topology);
133 synchronized void stop() {
134 LOG.debug("Bridge domain {} for {} shutting down", this, topology);
136 final WriteTransaction tx = chain.newWriteOnlyTransaction();
137 tx.delete(LogicalDatastoreType.OPERATIONAL, topology);
143 public synchronized void onDataTreeChanged(final Collection<DataTreeModification<Topology>> changes) {
144 for (DataTreeModification<Topology> c : changes) {
145 LOG.debug("Domain {} for {} processing change {}", this, topology, c);
147 final DataObjectModification<Topology> mod = c.getRootNode();
148 switch (mod.getModificationType()) {
150 LOG.debug("Topology {} deleted, expecting shutdown", topology);
152 case SUBTREE_MODIFIED:
153 // First check if the configuration has changed
154 final DataObjectModification<TopologyVbridgeAugment> newConfig = mod.getModifiedAugmentation(TopologyVbridgeAugment.class);
155 if (newConfig != null) {
156 if (newConfig.getModificationType() != ModificationType.DELETE) {
157 LOG.debug("Topology {} modified configuration {}", topology, newConfig);
158 updateConfiguration(newConfig);
160 // FIXME: okay, what can we do about this one?
161 LOG.error("Topology {} configuration deleted, good luck!", topology);
165 for (DataObjectModification<? extends DataObject> child : mod.getModifiedChildren()) {
166 LOG.debug("Topology {} modified child {}", topology, child);
168 if (Node.class.isAssignableFrom(child.getDataType())) {
169 modifyNode((DataObjectModification<Node>) child);
175 final Topology data = mod.getDataAfter();
177 // Read configuration
178 final TopologyVbridgeAugment config = data.getAugmentation(TopologyVbridgeAugment.class);
179 if (config != null) {
180 setConfiguration(config);
182 LOG.error("Topology {} has no configuration, good luck!", topology);
185 // FIXME: deal with nodes
189 LOG.warn("Unhandled topology modification {}", mod);
195 private void modifyNode(final DataObjectModification<Node> nodeMod) {
196 switch (nodeMod.getModificationType()) {
198 LOG.debug("Topology {} node {} deleted", topology, nodeMod.getIdentifier());
199 // FIXME: do something
201 case SUBTREE_MODIFIED:
202 LOG.debug("Topology {} node {} modified", topology, nodeMod.getIdentifier());
203 for (DataObjectModification<? extends DataObject> nodeChild : nodeMod.getModifiedChildren()) {
204 if (TerminationPoint.class.isAssignableFrom(nodeChild.getDataType())) {
205 modifyTerminationPoint((DataObjectModification<TerminationPoint>) nodeChild,nodeMod.getDataAfter().getNodeId());
210 LOG.debug("Topology {} node {} created", topology, nodeMod.getIdentifier());
211 final int numberVppsBeforeAddition = nodesToVpps.keySet().size();
212 final Node newNode = nodeMod.getDataAfter();
214 final int numberVppsAfterAddition = nodesToVpps.keySet().size();
215 if ((numberVppsBeforeAddition < numberVppsAfterAddition) && (numberVppsBeforeAddition >= 1)) {
216 addTunnel(newNode.getNodeId());
220 LOG.warn("Unhandled node modification {} in topology {}", nodeMod, topology);
225 private void addTunnel(final NodeId newNode) {
226 for (Map.Entry<NodeId, KeyedInstanceIdentifier<Node, NodeKey>> entryToVpp : nodesToVpps.entries()) {
227 if (!entryToVpp.getKey().equals(newNode)) {
228 createTunnelEndPoint(entryToVpp.getValue());
229 createTunnelEndPoint(nodesToVpps.get(newNode));
236 private void createTunnelEndPoint(Collection<KeyedInstanceIdentifier<Node, NodeKey>> keyedInstanceIdentifiers) {
237 for (KeyedInstanceIdentifier<Node, NodeKey> iiToVpp : keyedInstanceIdentifiers) {
238 createTunnelEndPoint(iiToVpp);
242 private void createTunnelEndPoint(final KeyedInstanceIdentifier<Node, NodeKey> iiToVpp) {
243 final DataBroker vppDataBroker = resolveDataBrokerForMountPoint(iiToVpp);
244 final ReadOnlyTransaction rTx = vppDataBroker.newReadOnlyTransaction();
246 final CheckedFuture<Optional<InterfacesState>, ReadFailedException> interfaceStateFuture
247 = rTx.read(LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.create(InterfacesState.class));
248 Futures.addCallback(interfaceStateFuture, new FutureCallback<Optional<InterfacesState>>() {
250 public void onSuccess(Optional<InterfacesState> optInterfacesState) {
251 if (optInterfacesState.isPresent()) {
252 for (org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface intf : optInterfacesState.get().getInterface()) {
253 //TODO find interface with IP address set
260 public void onFailure(Throwable t) {
269 private void modifyTerminationPoint(final DataObjectModification<TerminationPoint> nodeChild, final NodeId nodeId) {
270 final TerminationPoint terminationPoint = nodeChild.getDataAfter();
271 final TerminationPointVbridgeAugment termPointVbridgeAug = terminationPoint.getAugmentation(TerminationPointVbridgeAugment.class);
272 if (termPointVbridgeAug != null) {
273 final Collection<KeyedInstanceIdentifier<Node, NodeKey>> instanceIdentifiersVPP = nodesToVpps.get(nodeId);
274 //TODO: probably iterate via all instance identifiers.
275 if (!instanceIdentifiersVPP.isEmpty()) {
276 final DataBroker dataBroker = resolveDataBrokerForMountPoint(instanceIdentifiersVPP.iterator().next());
277 addInterfaceToBridgeDomainOnVpp(dataBroker, termPointVbridgeAug);
282 private void addInterfaceToBridgeDomainOnVpp(final DataBroker vppDataBroker, final TerminationPointVbridgeAugment termPointVbridgeAug) {
283 final InterfaceType interfaceType = termPointVbridgeAug.getInterfaceType();
284 if (interfaceType instanceof UserInterface) {
285 //REMARK: according contract in YANG model this should be URI to data on mount point (accroding to RESTCONF)
286 //It was much more easier to just await concrete interface name, thus isn't necessary parse it (splitting on '/')
287 final ExternalReference userInterface = ((UserInterface) interfaceType).getUserInterface();
288 final KeyedInstanceIdentifier<Interface, InterfaceKey> iiToVpp =
289 InstanceIdentifier.create(Interfaces.class)
290 .child(Interface.class, new InterfaceKey(userInterface.getValue()));
291 InstanceIdentifier<L2> iiToV3poL2 = iiToVpp.augmentation(VppInterfaceAugmentation.class).child(L2.class);
292 LOG.debug("Writing L2 data to configuration DS to concrete interface.");
293 final WriteTransaction wTx = vppDataBroker.newWriteOnlyTransaction();
294 wTx.put(LogicalDatastoreType.CONFIGURATION, iiToV3poL2, prepareL2Data());
299 private L2 prepareL2Data() {
300 final L2Builder l2Builder = new L2Builder();
301 final BridgeBasedBuilder bridgeBasedBuilder = new BridgeBasedBuilder();
302 bridgeBasedBuilder.setSplitHorizonGroup((short) 0);
303 bridgeBasedBuilder.setBridgedVirtualInterface(false);
304 bridgeBasedBuilder.setBridgeDomain(bridgeDomainName);
305 l2Builder.setInterconnection(bridgeBasedBuilder.build());
306 return l2Builder.build();
310 private DataBroker resolveDataBrokerForMountPoint(final InstanceIdentifier<?> iiToMountPoint) {
311 final Optional<MountPoint> vppMountPointOpt = mountService.getMountPoint(iiToMountPoint);
312 if (vppMountPointOpt.isPresent()) {
313 final MountPoint vppMountPoint = vppMountPointOpt.get();
314 final Optional<DataBroker> dataBrokerOpt = vppMountPoint.getService(DataBroker.class);
315 if (dataBrokerOpt.isPresent()) {
316 return dataBrokerOpt.get();
322 private void createNode(final Node node) {
323 for (SupportingNode supportingNode : node.getSupportingNode()) {
324 final NodeId nodeMount = supportingNode.getNodeRef();
325 final TopologyId topologyMount = supportingNode.getTopologyRef();
327 final KeyedInstanceIdentifier<Node, NodeKey> iiToMount = InstanceIdentifier
328 .create(NetworkTopology.class)
329 .child(Topology.class, new TopologyKey(topologyMount))
330 .child(Node.class, new NodeKey(nodeMount));
331 nodesToVpps.put(node.getNodeId(), iiToMount);
332 final DataBroker dataBrokerOfMount = resolveDataBrokerForMountPoint(iiToMount);
333 addVppToBridgeDomain(dataBrokerOfMount, node);
337 private void addVppToBridgeDomain(final DataBroker vppDataBroker, final Node node) {
338 if (vppDataBroker != null) {
339 final WriteTransaction wTx = vppDataBroker.newWriteOnlyTransaction();
340 wTx.put(LogicalDatastoreType.CONFIGURATION, iiBridgeDomainOnVPP, prepareNewBridgeDomainData());
341 final CheckedFuture<Void, TransactionCommitFailedException> addVppToBridgeDomainFuture = wTx.submit();
342 addSupportingBridgeDomain(addVppToBridgeDomainFuture, node);
346 private void addSupportingBridgeDomain(final CheckedFuture<Void, TransactionCommitFailedException> addVppToBridgeDomainFuture, final Node node) {
347 Futures.addCallback(addVppToBridgeDomainFuture, new FutureCallback() {
349 public void onSuccess(Object result) {
350 LOG.debug("Storing bridge member to operational DS....");
351 final BridgeMemberBuilder bridgeMemberBuilder = new BridgeMemberBuilder();
352 bridgeMemberBuilder.setSupportingBridgeDomain(new ExternalReference(iiBridgeDomainOnVPPRest));
353 final InstanceIdentifier<BridgeMember> iiToBridgeMember = topology.child(Node.class, node.getKey()).augmentation(NodeVbridgeAugment.class).child(BridgeMember.class);
354 final WriteTransaction wTx = chain.newWriteOnlyTransaction();
355 wTx.put(LogicalDatastoreType.OPERATIONAL, iiToBridgeMember, bridgeMemberBuilder.build(), true);
360 public void onFailure(Throwable t) {
361 //TODO handle this state
367 private org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.bridge.domains.BridgeDomain
368 prepareNewBridgeDomainData() {
369 final BridgeDomainBuilder bridgeDomainBuilder = new BridgeDomainBuilder(config);
370 bridgeDomainBuilder.setName(topology.getKey().getTopologyId().getValue());
371 return bridgeDomainBuilder.build();
374 private void setConfiguration(final TopologyVbridgeAugment config) {
375 LOG.debug("Topology {} configuration set to {}", topology, config);
377 this.config = config;
381 private void updateConfiguration(final DataObjectModification<TopologyVbridgeAugment> mod) {
382 LOG.debug("Topology {} configuration changed", topology);
384 // FIXME: do something smarter
385 setConfiguration(mod.getDataAfter());