Fix - creating parent node at VPP
[honeycomb.git] / vbd / impl / src / main / java / io / fd / honeycomb / vbd / impl / VppModifier.java
1 /**
2  * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
3  * <p>
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 package io.fd.honeycomb.vbd.impl;
9
10 import com.google.common.base.Optional;
11 import com.google.common.util.concurrent.CheckedFuture;
12 import com.google.common.util.concurrent.FutureCallback;
13 import com.google.common.util.concurrent.Futures;
14 import com.google.common.util.concurrent.ListenableFuture;
15 import com.google.common.util.concurrent.SettableFuture;
16 import java.util.ArrayList;
17 import java.util.List;
18 import javax.annotation.Nullable;
19 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
20 import org.opendaylight.controller.md.sal.binding.api.MountPointService;
21 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
22 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
23 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
24 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
25 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
26 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4AddressNoZone;
27 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceBuilder;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.Interface1;
32 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.Ipv4;
33 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.Address;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.external.reference.rev160129.ExternalReference;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.Vpp;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppInterfaceAugmentation;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppInterfaceAugmentationBuilder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VxlanTunnel;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.L2;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.L2Builder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.Vxlan;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.VxlanBuilder;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.l2.interconnection.BridgeBasedBuilder;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.BridgeDomains;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.bridge.domains.BridgeDomainBuilder;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.bridge.domains.BridgeDomainKey;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.topology.rev160129.TerminationPointVbridgeAugment;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.topology.rev160129.TopologyVbridgeAugment;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.topology.rev160129.network.topology.topology.TunnelParameters;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.topology.rev160129.network.topology.topology.node.termination.point.InterfaceType;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.topology.rev160129.network.topology.topology.node.termination.point._interface.type.UserInterface;
52 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
53 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
54 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
55 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
56 import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
58
59 /**
60  *  Class which is used for manipulation with VPP
61  */
62 public class VppModifier {
63     private static final Long DEFAULT_ENCAP_VRF_ID = 0L;
64
65     private static final Logger LOG = LoggerFactory.getLogger(BridgeDomain.class);
66     private final MountPointService mountService;
67     private final String bridgeDomainName;
68     private TopologyVbridgeAugment config;
69     private final InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.bridge.domains.BridgeDomain> iiBridgeDomainOnVPP;
70
71
72     public VppModifier(final MountPointService mountService, final String bridgeDomainName) {
73         this.mountService = mountService;
74         this.bridgeDomainName = bridgeDomainName;
75         this.iiBridgeDomainOnVPP = InstanceIdentifier.create(Vpp.class)
76                 .child(BridgeDomains.class)
77                 .child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.bridge.domains.BridgeDomain.class, new BridgeDomainKey(bridgeDomainName));
78     }
79     /**
80      * Tryies to read ipv4 addresses from all specified {@code iiToVpps } vpps.
81      *
82      * @param iiToVpps collection of instance identifiers which points to concrete mount points.
83      * @return future which contains list of ip addreases in the same order as was specified in {@code iiToVpps}
84      */
85     ListenableFuture<List<Optional<Ipv4AddressNoZone>>> readIpAddressesFromVpps(final KeyedInstanceIdentifier<Node, NodeKey>... iiToVpps) {
86         final List<ListenableFuture<Optional<Ipv4AddressNoZone>>> ipv4Futures = new ArrayList<>();
87         for (final KeyedInstanceIdentifier<Node, NodeKey> iiToVpp : iiToVpps) {
88             ipv4Futures.add(readIpAddressFromVpp(iiToVpp));
89         }
90         return Futures.successfulAsList(ipv4Futures);
91     }
92
93     /**
94      * Passes through interfaces at mount point specified via {@code iiToVpp}.
95      *
96      * When first ipv4 address is found then it is returned.
97      *
98      * @param iiToVpp instance idenfifier which point to mounted vpp
99      * @return if set ipv4 address is found at mounted vpp then it is returned as future. Otherwise absent value is returned
100      * in future or exception which has been thrown
101      */
102     private ListenableFuture<Optional<Ipv4AddressNoZone>> readIpAddressFromVpp(final KeyedInstanceIdentifier<Node, NodeKey> iiToVpp) {
103         final SettableFuture<Optional<Ipv4AddressNoZone>> resultFuture = SettableFuture.create();
104
105         final DataBroker vppDataBroker = VbdUtil.resolveDataBrokerForMountPoint(iiToVpp, mountService);
106         if (vppDataBroker != null) {
107             final ReadOnlyTransaction rTx = vppDataBroker.newReadOnlyTransaction();
108             final CheckedFuture<Optional<Interfaces>, ReadFailedException> interfaceStateFuture
109                     = rTx.read(LogicalDatastoreType.CONFIGURATION, InstanceIdentifier.create(Interfaces.class));
110
111             Futures.addCallback(interfaceStateFuture, new FutureCallback<Optional<Interfaces>>() {
112                 @Override
113                 public void onSuccess(final Optional<Interfaces> optInterfaces) {
114                     if (optInterfaces.isPresent()) {
115                         for (org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface intf : optInterfaces.get().getInterface()) {
116                             final Interface1 interface1 = intf.getAugmentation(Interface1.class);
117                             if (interface1 != null) {
118                                 final Ipv4 ipv4 = interface1.getIpv4();
119                                 if (ipv4 != null) {
120                                     final List<Address> addresses = ipv4.getAddress();
121                                     if (!addresses.isEmpty()) {
122                                         final Ipv4AddressNoZone ip = addresses.iterator().next().getIp();
123                                         if (ip != null) {
124                                             resultFuture.set(Optional.of(ip));
125                                             break;
126                                         }
127                                     }
128                                 }
129                             }
130                         }
131                     } else {
132                         LOG.debug("There is no inferface with ipv4 address set at VPP {}.", iiToVpp);
133                         resultFuture.set(Optional.<Ipv4AddressNoZone>absent());
134                     }
135                 }
136
137                 @Override
138                 public void onFailure(Throwable t) {
139                     resultFuture.setException(t);
140                 }
141             });
142         } else {
143             LOG.debug("Data broker for vpp {} is missing.", iiToVpp);
144             resultFuture.set(Optional.<Ipv4AddressNoZone>absent());
145         }
146         return resultFuture;
147     }
148
149     void createVirtualInterfaceOnVpp(final Ipv4AddressNoZone ipSrc, final Ipv4AddressNoZone ipDst, final KeyedInstanceIdentifier<Node, NodeKey> iiToVpp,
150                                      final Integer vxlanTunnelId) {
151         final Vxlan vxlanData = prepareVxlan(ipSrc, ipDst);
152         final Interface intfData = prepareVirtualInterfaceData(vxlanData, vxlanTunnelId);
153
154         final DataBroker vppDataBroker = VbdUtil.resolveDataBrokerForMountPoint(iiToVpp, mountService);
155         if (vppDataBroker != null) {
156             final WriteTransaction wTx = vppDataBroker.newWriteOnlyTransaction();
157             final KeyedInstanceIdentifier<Interface, InterfaceKey> iiToInterface
158                     = InstanceIdentifier.create(Interfaces.class).child(Interface.class, new InterfaceKey(VbdUtil.provideVxlanId(vxlanTunnelId)));
159             wTx.put(LogicalDatastoreType.CONFIGURATION, iiToInterface, intfData);
160             final CheckedFuture<Void, TransactionCommitFailedException> submitFuture = wTx.submit();
161             Futures.addCallback(submitFuture, new FutureCallback<Void>() {
162                 @Override
163                 public void onSuccess(@Nullable Void result) {
164                     LOG.debug("Writing super virtual interface to {} finished successfully.",iiToVpp.getKey().getNodeId());
165                 }
166
167                 @Override
168                 public void onFailure(Throwable t) {
169                     LOG.debug("Writing super virtual interface to {} failed.", iiToVpp.getKey().getNodeId());
170                 }
171             });
172         } else {
173             LOG.debug("Writing virtual interface {} to VPP {} wasn't successfull because missing data broker.", VbdUtil.provideVxlanId(vxlanTunnelId), iiToVpp);
174         }
175     }
176
177     private Interface prepareVirtualInterfaceData(final Vxlan vxlan, Integer vxlanTunnelId) {
178         final InterfaceBuilder interfaceBuilder = new InterfaceBuilder();
179         //TODO implement tunnel counter
180         interfaceBuilder.setName(VbdUtil.provideVxlanId(vxlanTunnelId));
181         interfaceBuilder.setType(VxlanTunnel.class);
182         VppInterfaceAugmentationBuilder vppInterfaceAugmentationBuilder = new VppInterfaceAugmentationBuilder();
183         vppInterfaceAugmentationBuilder.setVxlan(vxlan);
184         interfaceBuilder.addAugmentation(VppInterfaceAugmentation.class, vppInterfaceAugmentationBuilder.build());
185         return interfaceBuilder.build();
186     }
187
188     private Vxlan prepareVxlan(final Ipv4AddressNoZone ipSrc, final Ipv4AddressNoZone ipDst) {
189         final VxlanBuilder vxlanBuilder = new VxlanBuilder();
190         vxlanBuilder.setSrc(ipSrc);
191         vxlanBuilder.setDst(ipDst);
192         final TunnelParameters tunnelParameters = config.getTunnelParameters();
193         if (tunnelParameters instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.topology.rev160129.network.topology.topology.tunnel.parameters.Vxlan) {
194             org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.topology.rev160129.network.topology.topology.tunnel.parameters.Vxlan vxlan =
195                     (org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.topology.rev160129.network.topology.topology.tunnel.parameters.Vxlan) tunnelParameters;
196             //TODO: handle NPE
197             vxlanBuilder.setVni(vxlan.getVxlan().getVni());
198         }
199         vxlanBuilder.setEncapVrfId(DEFAULT_ENCAP_VRF_ID);
200         return vxlanBuilder.build();
201     }
202
203     void addInterfaceToBridgeDomainOnVpp(final DataBroker vppDataBroker, final TerminationPointVbridgeAugment termPointVbridgeAug) {
204         final InterfaceType interfaceType = termPointVbridgeAug.getInterfaceType();
205         if (interfaceType instanceof UserInterface) {
206             //REMARK: according contract in YANG model this should be URI to data on mount point (accroding to RESTCONF)
207             //It was much more easier to just await concrete interface name, thus isn't necessary parse it (splitting on '/')
208             final ExternalReference userInterface = ((UserInterface) interfaceType).getUserInterface();
209             final KeyedInstanceIdentifier<Interface, InterfaceKey> iiToVpp =
210                     InstanceIdentifier.create(Interfaces.class)
211                             .child(Interface.class, new InterfaceKey(userInterface.getValue()));
212             InstanceIdentifier<L2> iiToV3poL2 = iiToVpp.augmentation(VppInterfaceAugmentation.class).child(L2.class);
213             LOG.debug("Writing L2 data to configuration DS to concrete interface.");
214             final WriteTransaction wTx = vppDataBroker.newWriteOnlyTransaction();
215             wTx.put(LogicalDatastoreType.CONFIGURATION, iiToV3poL2, prepareL2Data(), true);
216             wTx.submit();
217         }
218     }
219
220     ListenableFuture<Void> addVppToBridgeDomain(final KeyedInstanceIdentifier<Node, NodeKey> iiToVpp, final Node node) {
221         final DataBroker vppDataBroker = VbdUtil.resolveDataBrokerForMountPoint(iiToVpp, mountService);
222         if (vppDataBroker != null) {
223             final WriteTransaction wTx = vppDataBroker.newWriteOnlyTransaction();
224             wTx.put(LogicalDatastoreType.CONFIGURATION, iiBridgeDomainOnVPP, prepareNewBridgeDomainData(), true);
225             return wTx.submit();
226         }
227         return Futures.immediateFailedFuture(new IllegalStateException("Data broker for vpp is missing"));
228     }
229
230     private org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.bridge.domains.BridgeDomain
231     prepareNewBridgeDomainData() {
232         final BridgeDomainBuilder bridgeDomainBuilder = new BridgeDomainBuilder(config);
233         bridgeDomainBuilder.setName(bridgeDomainName);
234         return bridgeDomainBuilder.build();
235     }
236
237
238
239     private L2 prepareL2Data() {
240         final L2Builder l2Builder = new L2Builder();
241         final BridgeBasedBuilder bridgeBasedBuilder = new BridgeBasedBuilder();
242         bridgeBasedBuilder.setSplitHorizonGroup((short) 0);
243         bridgeBasedBuilder.setBridgedVirtualInterface(false);
244         bridgeBasedBuilder.setBridgeDomain(bridgeDomainName);
245         l2Builder.setInterconnection(bridgeBasedBuilder.build());
246         return l2Builder.build();
247     }
248
249     public void setConfig(final TopologyVbridgeAugment config) {
250         this.config = config;
251     }
252 }