Add INFO.yaml for GBP
[groupbasedpolicy.git] / renderers / vpp / src / main / java / org / opendaylight / groupbasedpolicy / renderer / vpp / lisp / loopback / LoopbackManager.java
1 /*
2  * Copyright (c) 2017 Cisco Systems. 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 org.opendaylight.groupbasedpolicy.renderer.vpp.lisp.loopback;
10
11 import com.google.common.base.Optional;
12 import com.google.common.base.Preconditions;
13 import com.google.common.collect.HashBasedTable;
14 import com.google.common.collect.Lists;
15 import com.google.common.collect.Table;
16
17 import java.util.HashMap;
18 import java.util.List;
19 import java.util.Map;
20
21 import org.apache.commons.lang3.tuple.Pair;
22 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
23 import org.opendaylight.groupbasedpolicy.renderer.vpp.commands.LoopbackCommand;
24 import org.opendaylight.groupbasedpolicy.renderer.vpp.commands.LoopbackCommandWrapper;
25 import org.opendaylight.groupbasedpolicy.renderer.vpp.commands.ProxyRangeCommand;
26 import org.opendaylight.groupbasedpolicy.renderer.vpp.commands.UnnumberedInterfaceCommand;
27 import org.opendaylight.groupbasedpolicy.renderer.vpp.commands.lisp.AbstractLispCommand;
28 import org.opendaylight.groupbasedpolicy.renderer.vpp.commands.lisp.LispCommandWrapper;
29 import org.opendaylight.groupbasedpolicy.renderer.vpp.lisp.LispStateCommandExecutor;
30 import org.opendaylight.groupbasedpolicy.renderer.vpp.lisp.exception.LispConfigCommandFailedException;
31 import org.opendaylight.groupbasedpolicy.renderer.vpp.lisp.exception.LispArgumentException;
32 import org.opendaylight.groupbasedpolicy.renderer.vpp.lisp.flat.overlay.FlatOverlayManager;
33 import org.opendaylight.groupbasedpolicy.renderer.vpp.lisp.mappers.NeutronTenantToVniMapper;
34 import org.opendaylight.groupbasedpolicy.renderer.vpp.lisp.util.ConfigManagerHelper;
35 import org.opendaylight.groupbasedpolicy.renderer.vpp.lisp.util.Constants;
36 import org.opendaylight.groupbasedpolicy.renderer.vpp.lisp.util.IpAddressUtil;
37 import org.opendaylight.groupbasedpolicy.renderer.vpp.util.GbpNetconfTransaction;
38 import org.opendaylight.groupbasedpolicy.renderer.vpp.util.General;
39 import org.opendaylight.groupbasedpolicy.renderer.vpp.util.LispUtil;
40 import org.opendaylight.groupbasedpolicy.renderer.vpp.util.VppIidFactory;
41 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
42 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;
43 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
44 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
45 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.lisp.address.types.rev151105.Ipv4PrefixAfi;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev170511.IpPrefixType;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.endpoints.AddressEndpointWithLocation;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.GbpSubnet;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.gpe.rev170801.gpe.entry.table.grouping.gpe.entry.table.GpeEntry;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.gpe.rev170801.gpe.entry.table.grouping.gpe.entry.table.gpe.entry.RemoteEid;
51 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
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.slf4j.Logger;
56 import org.slf4j.LoggerFactory;
57
58 /**
59  * Created by Shakib Ahmed on 4/26/17.
60  */
61 public class LoopbackManager {
62     private static final Logger LOG = LoggerFactory.getLogger(LoopbackManager.class);
63
64     private ConfigManagerHelper configManagerHelper;
65     private Table<NodeKey, String, List<String>> unnumberedCache = HashBasedTable.create();
66
67     private NeutronTenantToVniMapper neutronTenantToVniMapper = NeutronTenantToVniMapper.getInstance();
68
69
70     private Map<String, GbpSubnet> GbpSubnetCache = new HashMap<>();
71     private Map<String, List<LoopBackDetails>> loopBackHostnames = new HashMap<>();
72
73     private class LoopBackDetails {
74         private LoopbackCommand loopbackCommand;
75         private String hostName;
76
77         LoopbackCommand getLoopbackCommand() {
78             return loopbackCommand;
79         }
80
81         void setLoopbackCommand(LoopbackCommand loopbackCommand) {
82             this.loopbackCommand = loopbackCommand;
83         }
84
85         String getHostName() {
86             return hostName;
87         }
88
89         void setHostName(String hostName) {
90             this.hostName = hostName;
91         }
92     }
93
94
95     public LoopbackManager() {
96         this.configManagerHelper = new ConfigManagerHelper();
97     }
98
99     public void createSimpleLoopbackIfNeeded(AddressEndpointWithLocation addressEp) {
100         if (!addressEp.getAddressType().equals(IpPrefixType.class)) {
101             return;
102         }
103         Map<String, String> intfcsByHostname = FlatOverlayManager.resolveIntfcsByHosts(addressEp);
104
105         intfcsByHostname.forEach((hostname, interfaceName) -> {
106             try {
107                 long vni = getVni(addressEp.getTenant().getValue());
108                 long vrfId = vni;
109                 String subnetUuid = configManagerHelper.getSubnet(addressEp);
110                 GbpSubnet gbpSubnetInfo = GbpSubnetCache.get(subnetUuid);
111                 String loopIntfcName = Constants.GW_NAME_PREFIX + subnetUuid;
112
113                 if (gbpSubnetInfo != null) {
114                     Optional<Interface> optionalLoopback =
115                         GbpNetconfTransaction.read(VppIidFactory.getNetconfNodeIid(new NodeId(hostname)),
116                             LogicalDatastoreType.CONFIGURATION,
117                             VppIidFactory.getInterfaceIID(new InterfaceKey(loopIntfcName)),
118                             GbpNetconfTransaction.RETRY_COUNT);
119                     if (!optionalLoopback.isPresent()) {
120                         LoopbackCommand simpleLoopbackCommand =
121                             LoopbackCommandWrapper.simpleLoopbackPutCommand(loopIntfcName, vrfId,
122                                 gbpSubnetInfo.getGatewayIp(), gbpSubnetInfo.getCidr());
123                         if (createLoopbackInterface(hostname, simpleLoopbackCommand)) {
124                             addGpeEntry(VppIidFactory.getNetconfNodeIid(new NodeId(hostname)), gbpSubnetInfo, vni);
125                             addProxyArpRange(hostname, vrfId, gbpSubnetInfo);
126                             if(loopBackHostnames.get(loopIntfcName) == null) {
127                                 LoopBackDetails loopBackDetails = new LoopBackDetails();
128                                 loopBackDetails.setHostName(hostname);
129                                 loopBackDetails.setLoopbackCommand(simpleLoopbackCommand);
130                                 loopBackHostnames.put(loopIntfcName, Lists.newArrayList(loopBackDetails));
131                             } else {
132                                 LoopBackDetails loopBackDetails = new LoopBackDetails();
133                                 loopBackDetails.setHostName(hostname);
134                                 loopBackDetails.setLoopbackCommand(simpleLoopbackCommand);
135                                 loopBackHostnames.get(loopIntfcName).add(loopBackDetails);
136                             }
137                         }
138                     } else {
139                         LOG.trace("Loopback already present on host: {} skip update for: {} - {} in vrf: {}", hostname,
140                             loopIntfcName, gbpSubnetInfo.getGatewayIp(), vrfId);
141                     }
142                 }
143
144
145                 if (!addUnnumberedInterface(hostname, interfaceName, loopIntfcName)){
146                     LOG.warn("Failed to add unnumbered for addressEp : {}", addressEp);
147                 }
148             } catch (LispConfigCommandFailedException e) {
149                 LOG.warn("LISP couldn't be configured: {}", e.getMessage());
150             }
151         });
152
153
154
155     }
156
157     private boolean createLoopbackInterface(String hostName, LoopbackCommand loopbackCommand){
158
159         return GbpNetconfTransaction.netconfSyncedWrite(VppIidFactory.getNetconfNodeIid(new NodeId(hostName)),
160                 loopbackCommand, GbpNetconfTransaction.RETRY_COUNT);
161     }
162
163     private boolean deleteSpecificLoopback(InstanceIdentifier<Node> nodeIid, String loopbackName) {
164         LOG.trace("deleteSpecificLoopback -> nodeiid: {}, loopbackInterface: {}", nodeIid, loopbackName);
165         if (unnumberedCache.get(new NodeKey(nodeIid.firstKeyOf(Node.class)), loopbackName) != null) {
166             unnumberedCache.get(new NodeKey(nodeIid.firstKeyOf(Node.class)), loopbackName).forEach(intfc -> {
167                 if (GbpNetconfTransaction.netconfSyncedDelete(nodeIid,
168                     VppIidFactory.getUnnumberedIid(new InterfaceKey(intfc)), GbpNetconfTransaction.RETRY_COUNT)) {
169                     unnumberedCache.remove(new NodeKey(nodeIid.firstKeyOf(Node.class)), loopbackName);
170                 }
171             });
172         }
173         return GbpNetconfTransaction.netconfSyncedDelete(nodeIid,
174                 VppIidFactory.getInterfaceIID(new InterfaceKey(loopbackName)), GbpNetconfTransaction.RETRY_COUNT);
175     }
176
177     private void addProxyArpRange(String hostName,
178                                   long vrf,
179                                   GbpSubnet gbpSubnetInfo) throws LispConfigCommandFailedException {
180         Ipv4Prefix subnetPrefix = gbpSubnetInfo.getCidr().getIpv4Prefix();
181
182         Preconditions.checkNotNull(subnetPrefix, "Subnet CIDR found to be null for "
183                 + "subnet uuid =" +  gbpSubnetInfo.getId() + "!");
184
185         Pair<Ipv4Address, Ipv4Address> startAndEndAddress = IpAddressUtil.getStartAndEndIp(subnetPrefix);
186
187         if (!putArpRangesCommand(VppIidFactory.getNetconfNodeIid(new NodeId(hostName)), vrf,
188             startAndEndAddress.getLeft(), startAndEndAddress.getRight())) {
189             throw new LispConfigCommandFailedException("Proxy arp configuration failed for subnet uuid: " +
190                     gbpSubnetInfo.getId() + "!");
191         } else {
192             LOG.debug("Configured proxy arp for range {} to {} on node : {}!", startAndEndAddress.getLeft(),
193                     startAndEndAddress.getRight(), hostName);
194         }
195     }
196
197     private boolean deleteProxyArpRange(String hostName, long vrf, GbpSubnet gbpSubnetInfo) {
198         Ipv4Prefix subnetPrefix = gbpSubnetInfo.getCidr().getIpv4Prefix();
199
200         Preconditions.checkNotNull(subnetPrefix, "Subnet CIDR found to be null for "
201                 + "subnet uuid =" +  gbpSubnetInfo.getId() + "!");
202
203         Pair<Ipv4Address, Ipv4Address> startAndEndAddress = IpAddressUtil.getStartAndEndIp(subnetPrefix);
204
205         return (deleteArpRangesCommand(VppIidFactory.getNetconfNodeIid(new NodeId(hostName)), vrf,
206             startAndEndAddress.getLeft(), startAndEndAddress.getRight()));
207     }
208
209     private boolean putArpRangesCommand(InstanceIdentifier<Node> iid, long vrf, Ipv4Address start, Ipv4Address end) {
210         ProxyRangeCommand.ProxyRangeCommandBuilder builder = new ProxyRangeCommand.ProxyRangeCommandBuilder();
211         builder.setOperation(General.Operations.PUT);
212         builder.setVrf(vrf);
213         builder.setStartAddress(start);
214         builder.setEndAddress(end);
215         return GbpNetconfTransaction.netconfSyncedWrite(iid, builder.build(), GbpNetconfTransaction.RETRY_COUNT);
216     }
217
218     private boolean deleteArpRangesCommand(InstanceIdentifier<Node> iid,
219                                            long vrf,
220                                            Ipv4Address start,
221                                            Ipv4Address end) {
222         ProxyRangeCommand.ProxyRangeCommandBuilder builder = new ProxyRangeCommand.ProxyRangeCommandBuilder();
223         builder.setOperation(General.Operations.DELETE);
224         builder.setVrf(vrf);
225         builder.setStartAddress(start);
226         builder.setEndAddress(end);
227         return GbpNetconfTransaction.netconfSyncedDelete(iid, builder.build(), GbpNetconfTransaction.RETRY_COUNT);
228     }
229
230     private boolean addUnnumberedInterface(String hostname, String neutronInterfaceName, String loopbackName) {
231         InstanceIdentifier<Node> nodeIid = VppIidFactory.getNetconfNodeIid(new NodeId(hostname));
232         if (neutronInterfaceName.equalsIgnoreCase(loopbackName)) {
233             LOG.trace("No need to configure unnumbered for loopback: {} on host: {}. skip processing.", loopbackName,
234                 neutronInterfaceName);
235             return true;
236         }
237         LOG.trace("Adding unnumbered configuration hostname: {}, interface: {} use : {}", hostname,
238             neutronInterfaceName, loopbackName);
239         boolean unnumberWritten = putUnnumberedInterface(nodeIid, neutronInterfaceName, loopbackName);
240         if (unnumberWritten) {
241             if (unnumberedCache.get(nodeIid.firstKeyOf(Node.class), loopbackName) != null) {
242                 unnumberedCache.get(nodeIid.firstKeyOf(Node.class), loopbackName).add(neutronInterfaceName);
243             } else {
244                 unnumberedCache.put(nodeIid.firstKeyOf(Node.class), loopbackName,
245                     Lists.newArrayList(neutronInterfaceName));
246             }
247             LOG.debug("Added Interface {} as unnumbered for {}", loopbackName, neutronInterfaceName);
248         }
249         return unnumberWritten;
250     }
251
252     private boolean putUnnumberedInterface(InstanceIdentifier<Node> iid, String interfaceName, String useInterface) {
253         UnnumberedInterfaceCommand unnumberedCommand =
254             UnnumberedInterfaceCommand.builder()
255                 .setOperation(General.Operations.MERGE)
256                 .setUseInterface(useInterface)
257                 .setInterfaceName(interfaceName)
258                 .build();
259         return GbpNetconfTransaction.netconfSyncedWrite(iid, unnumberedCommand, GbpNetconfTransaction.RETRY_COUNT);
260     }
261
262     private void addGpeEntry(InstanceIdentifier<Node> iid, GbpSubnet gbpSubnetInfo, long vni) {
263         LOG.trace("addGpeEntry called. iid: {}, GbpSubnet: {}, vni: {}", iid, gbpSubnetInfo, vni);
264         try {
265             Pair<Ipv4Prefix, Ipv4Prefix> delegatingSubnets =
266                 IpAddressUtil.getSmallerSubnet(gbpSubnetInfo.getCidr().getIpv4Prefix());
267
268             RemoteEid firstREid =
269                 LispUtil.toRemoteEid(LispUtil.toLispIpv4Prefix(delegatingSubnets.getLeft()), vni, Ipv4PrefixAfi.class);
270             if (!putGpeEntry(iid, Constants.GPE_ENTRY_PREFIX + gbpSubnetInfo.getId() + "_1", firstREid, vni, vni)) {
271                 LOG.warn("Failed to write GPE entry: {}", Constants.GPE_ENTRY_PREFIX + gbpSubnetInfo.getId() + "_1");
272             }
273
274             if (delegatingSubnets.getLeft().equals(delegatingSubnets.getRight())) {
275                 return;
276             }
277
278             RemoteEid secondREid =
279                 LispUtil.toRemoteEid(LispUtil.toLispIpv4Prefix(delegatingSubnets.getRight()), vni, Ipv4PrefixAfi.class);
280
281             if (!putGpeEntry(iid, Constants.GPE_ENTRY_PREFIX + gbpSubnetInfo.getId() + "_2", secondREid, vni, vni)) {
282                 LOG.warn("Failed to write GPE entry: {}", Constants.GPE_ENTRY_PREFIX + gbpSubnetInfo.getId() + "_2");
283             }
284         } catch (LispArgumentException e) {
285             e.printStackTrace();
286         }
287     }
288
289     private boolean putGpeEntry(InstanceIdentifier<Node> iid, String id, RemoteEid rEid, long vni, long vrf) {
290         AbstractLispCommand<GpeEntry> gpeEntryCommand = LispCommandWrapper
291                 .addGpeSendMapregisterAction(id, rEid, vni, vrf);
292         return LispStateCommandExecutor.executePutCommand(iid, gpeEntryCommand);
293     }
294
295     private boolean deleteGpeEntry(InstanceIdentifier<Node> iid, String id) {
296         AbstractLispCommand<GpeEntry> gpeEntryDeletionCommand = LispCommandWrapper
297                 .deleteGpeEntry(id);
298         return LispStateCommandExecutor.executeDeleteCommand(iid, gpeEntryDeletionCommand);
299     }
300
301     private long getVni(String tenantUuid) {
302         return neutronTenantToVniMapper.getVni(tenantUuid);
303     }
304
305
306     public void gbpSubnetCreated(String subnetUuid, GbpSubnet subnetInfo) {
307         GbpSubnetCache.put(subnetUuid, subnetInfo);
308     }
309
310     public void gbpSubnetdeleted(String subnetUuid) {
311         LOG.trace("gbpSubnetdeleted -> subnetUuid:{}", subnetUuid);
312         GbpSubnet gbpSubnet = GbpSubnetCache.get(subnetUuid);
313         String loopIntfcName = Constants.GW_NAME_PREFIX + subnetUuid;
314         List<LoopBackDetails> loopBackDetails = loopBackHostnames.get(loopIntfcName);
315         if (loopBackDetails != null) {
316             loopBackDetails.forEach(loopbackDetail -> {
317
318                 InstanceIdentifier<Node> iid =
319                     VppIidFactory.getNetconfNodeIid(new NodeId(loopbackDetail.getHostName()));
320
321                 if (deleteSpecificLoopback(iid, loopIntfcName)) {
322                     if (!deleteProxyArpRange(loopbackDetail.getHostName(),
323                         loopbackDetail.getLoopbackCommand().getVrfId(), gbpSubnet)) {
324                         LOG.warn("Failed to delete ProxyArpRange: {} on host: {}", gbpSubnet.getAllocationPools(),
325                             loopbackDetail.getHostName());
326                     }
327
328                     if (!deleteGpeEntry(iid, Constants.GPE_ENTRY_PREFIX + gbpSubnet.getId() + "_1")) {
329                         LOG.warn("Failed to delete gpeEntry: {} on host: {}",
330                             Constants.GPE_ENTRY_PREFIX + gbpSubnet.getId() + "_1", loopbackDetail.getHostName());
331                     }
332                     if (!deleteGpeEntry(iid, Constants.GPE_ENTRY_PREFIX + gbpSubnet.getId() + "_2")) {
333                         LOG.warn("Failed to delete gpeEntry: {} on host: {}",
334                             Constants.GPE_ENTRY_PREFIX + gbpSubnet.getId() + "_2", loopbackDetail.getHostName());
335                     }
336                     if (!deleteGpeFeatureData(loopbackDetail.getHostName())) {
337                         LOG.warn("Failed to delete gpe configuration: {} on host: {}", loopbackDetail.getHostName());
338                     }
339
340                 } else {
341                     LOG.warn("Failed to delete loopback: {} on host: {}", loopIntfcName, loopbackDetail.getHostName());
342                 }
343             });
344         }
345         GbpSubnetCache.remove(subnetUuid);
346     }
347
348     private boolean deleteGpeFeatureData(String hostname) {
349         return GbpNetconfTransaction.netconfSyncedDelete(VppIidFactory.getNetconfNodeIid(new NodeId(hostname)),
350             VppIidFactory.getGpeFeatureDataIid(), GbpNetconfTransaction.RETRY_COUNT);
351     }
352 }