Adding automatic creation of tunnel.
[honeycomb.git] / vbd / impl / src / main / java / io / fd / honeycomb / vbd / impl / BridgeDomain.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8
9 package io.fd.honeycomb.vbd.impl;
10
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;
19 import java.util.Map;
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;
70
71 /**
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.
74  */
75 final class BridgeDomain implements DataTreeChangeListener<Topology> {
76     private static final Logger LOG = LoggerFactory.getLogger(BridgeDomain.class);
77     private final KeyedInstanceIdentifier<Topology, TopologyKey> topology;
78     @GuardedBy("this")
79
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();
89
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;
95
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));
101
102         this.dataBroker = dataBroker;
103         reg = dataBroker.registerDataTreeChangeListener(
104                                      new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, topology), this);
105     }
106
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();
112     }
113
114     static BridgeDomain create(final DataBroker dataBroker,
115                                MountPointService mountService, final KeyedInstanceIdentifier<Topology, TopologyKey> topology, final BindingTransactionChain chain) {
116
117         LOG.debug("Wiping operational state of {}", topology);
118
119         final WriteTransaction tx = chain.newWriteOnlyTransaction();
120         tx.delete(LogicalDatastoreType.OPERATIONAL, topology);
121         tx.submit();
122
123         return new BridgeDomain(dataBroker, mountService, topology, chain);
124     }
125
126     synchronized void forceStop() {
127         LOG.info("Bridge domain {} for {} going down", this, topology);
128         reg.close();
129         chain.close();
130         LOG.info("Bridge domain {} for {} is down", this, topology);
131     }
132
133     synchronized void stop() {
134         LOG.debug("Bridge domain {} for {} shutting down", this, topology);
135
136         final WriteTransaction tx = chain.newWriteOnlyTransaction();
137         tx.delete(LogicalDatastoreType.OPERATIONAL, topology);
138         tx.submit();
139         chain.close();
140     }
141
142     @Override
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);
146
147             final DataObjectModification<Topology> mod = c.getRootNode();
148             switch (mod.getModificationType()) {
149                 case DELETE:
150                     LOG.debug("Topology {} deleted, expecting shutdown", topology);
151                     break;
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);
159                         } else {
160                             // FIXME: okay, what can we do about this one?
161                             LOG.error("Topology {} configuration deleted, good luck!", topology);
162                         }
163                     }
164
165                     for (DataObjectModification<? extends DataObject> child : mod.getModifiedChildren()) {
166                         LOG.debug("Topology {} modified child {}", topology, child);
167
168                         if (Node.class.isAssignableFrom(child.getDataType())) {
169                             modifyNode((DataObjectModification<Node>) child);
170                         }
171                     }
172
173                     break;
174                 case WRITE:
175                     final Topology data = mod.getDataAfter();
176
177                     // Read configuration
178                     final TopologyVbridgeAugment config = data.getAugmentation(TopologyVbridgeAugment.class);
179                     if (config != null) {
180                         setConfiguration(config);
181                     } else {
182                         LOG.error("Topology {} has no configuration, good luck!", topology);
183                     }
184
185                     // FIXME: deal with nodes
186
187                     break;
188                 default:
189                     LOG.warn("Unhandled topology modification {}", mod);
190                     break;
191             }
192         }
193     }
194
195     private void modifyNode(final DataObjectModification<Node> nodeMod) {
196         switch (nodeMod.getModificationType()) {
197             case DELETE:
198                 LOG.debug("Topology {} node {} deleted", topology, nodeMod.getIdentifier());
199                 // FIXME: do something
200                 break;
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());
206                     }
207                 }
208                 break;
209             case WRITE:
210                 LOG.debug("Topology {} node {} created", topology, nodeMod.getIdentifier());
211                 final int numberVppsBeforeAddition = nodesToVpps.keySet().size();
212                 final Node newNode = nodeMod.getDataAfter();
213                 createNode(newNode);
214                 final int numberVppsAfterAddition = nodesToVpps.keySet().size();
215                 if ((numberVppsBeforeAddition < numberVppsAfterAddition) && (numberVppsBeforeAddition >= 1)) {
216                     addTunnel(newNode.getNodeId());
217                 }
218                 break;
219             default:
220                 LOG.warn("Unhandled node modification {} in topology {}", nodeMod, topology);
221                 break;
222         }
223     }
224
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));
230             }
231
232
233         }
234     }
235
236     private void createTunnelEndPoint(Collection<KeyedInstanceIdentifier<Node, NodeKey>> keyedInstanceIdentifiers) {
237         for (KeyedInstanceIdentifier<Node, NodeKey> iiToVpp : keyedInstanceIdentifiers) {
238             createTunnelEndPoint(iiToVpp);
239         }
240     }
241
242     private void createTunnelEndPoint(final KeyedInstanceIdentifier<Node, NodeKey> iiToVpp) {
243         final DataBroker vppDataBroker = resolveDataBrokerForMountPoint(iiToVpp);
244         final ReadOnlyTransaction rTx = vppDataBroker.newReadOnlyTransaction();
245
246         final CheckedFuture<Optional<InterfacesState>, ReadFailedException> interfaceStateFuture
247                 = rTx.read(LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.create(InterfacesState.class));
248         Futures.addCallback(interfaceStateFuture, new FutureCallback<Optional<InterfacesState>>() {
249             @Override
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
254                     }
255                 }
256
257             }
258
259             @Override
260             public void onFailure(Throwable t) {
261
262             }
263         });
264
265
266     }
267
268
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);
278             }
279         }
280     }
281
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());
295             wTx.submit();
296         }
297     }
298
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();
307     }
308
309
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();
317             }
318         }
319         return null;
320     }
321
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();
326
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);
334         }
335     }
336
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);
343         }
344     }
345
346     private void addSupportingBridgeDomain(final CheckedFuture<Void, TransactionCommitFailedException> addVppToBridgeDomainFuture, final Node node) {
347         Futures.addCallback(addVppToBridgeDomainFuture, new FutureCallback() {
348             @Override
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);
356                 wTx.submit();
357             }
358
359             @Override
360             public void onFailure(Throwable t) {
361                 //TODO handle this state
362             }
363         });
364
365     }
366
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();
372     }
373
374     private void setConfiguration(final TopologyVbridgeAugment config) {
375         LOG.debug("Topology {} configuration set to {}", topology, config);
376
377         this.config = config;
378     }
379
380     @GuardedBy("this")
381     private void updateConfiguration(final DataObjectModification<TopologyVbridgeAugment> mod) {
382         LOG.debug("Topology {} configuration changed", topology);
383
384         // FIXME: do something smarter
385         setConfiguration(mod.getDataAfter());
386     }
387 }