Lisp processing additional fixes
[groupbasedpolicy.git] / renderers / vpp / src / main / java / org / opendaylight / groupbasedpolicy / renderer / vpp / lisp / flat / overlay / FlatOverlayManager.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.flat.overlay;
10
11 import java.util.HashMap;
12 import java.util.List;
13 import java.util.Map;
14
15 import javax.annotation.Nonnull;
16
17 import org.apache.commons.net.util.SubnetUtils;
18 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
19 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
20 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
21 import org.opendaylight.groupbasedpolicy.renderer.vpp.commands.StaticArpCommand;
22 import org.opendaylight.groupbasedpolicy.renderer.vpp.iface.VppPathMapper;
23 import org.opendaylight.groupbasedpolicy.renderer.vpp.lisp.info.container.EndpointHost;
24 import org.opendaylight.groupbasedpolicy.renderer.vpp.lisp.info.container.HostRelatedInfoContainer;
25 import org.opendaylight.groupbasedpolicy.renderer.vpp.lisp.info.container.states.PhysicalInterfaces;
26 import org.opendaylight.groupbasedpolicy.renderer.vpp.lisp.info.container.states.PortInterfaces;
27 import org.opendaylight.groupbasedpolicy.renderer.vpp.lisp.mappers.NeutronTenantToVniMapper;
28 import org.opendaylight.groupbasedpolicy.renderer.vpp.lisp.util.ConfigManagerHelper;
29 import org.opendaylight.groupbasedpolicy.renderer.vpp.lisp.util.Constants;
30 import org.opendaylight.groupbasedpolicy.renderer.vpp.lisp.util.IpAddressUtil;
31 import org.opendaylight.groupbasedpolicy.renderer.vpp.listener.VppEndpointListener;
32 import org.opendaylight.groupbasedpolicy.renderer.vpp.routing.RoutingManager;
33 import org.opendaylight.groupbasedpolicy.renderer.vpp.util.GbpNetconfTransaction;
34 import org.opendaylight.groupbasedpolicy.renderer.vpp.util.General;
35 import org.opendaylight.groupbasedpolicy.renderer.vpp.util.LispUtil;
36 import org.opendaylight.groupbasedpolicy.renderer.vpp.util.MountedDataBrokerProvider;
37 import org.opendaylight.groupbasedpolicy.renderer.vpp.util.VppIidFactory;
38 import org.opendaylight.groupbasedpolicy.util.DataStoreHelper;
39 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
40 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4AddressNoZone;
41 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;
42 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
43 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.routing.rev140524.routing.routing.instance.routing.protocols.RoutingProtocol;
44 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress;
45 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.vpp.routing.rev170917.VniReference;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.absolute.location.absolute.location.location.type.ExternalLocationCase;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.child.endpoints.ChildEndpoint;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev170511.IpPrefixType;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.endpoints.AddressEndpointWithLocation;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.Config;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425._interface.attributes._interface.type.choice.TapCase;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpoint;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpointKey;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev170607.interfaces._interface.Routing;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev170607.interfaces._interface.RoutingBuilder;
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.network.topology.topology.Node;
58 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
59 import org.slf4j.Logger;
60 import org.slf4j.LoggerFactory;
61
62 import com.google.common.base.Optional;
63 import com.google.common.base.Preconditions;
64
65 public class FlatOverlayManager {
66     private static final Logger LOG = LoggerFactory.getLogger(FlatOverlayManager.class);
67
68     private ConfigManagerHelper overlayHelper;
69     private DataBroker dataBroker;
70
71     private NeutronTenantToVniMapper neutronTenantToVniMapper = NeutronTenantToVniMapper.getInstance();
72     private HostRelatedInfoContainer hostRelatedInfoContainer = HostRelatedInfoContainer.getInstance();
73     // Node ID, VRF ID, route count
74     private Map<String, Map<Long, Long>> vrfsByHostname = new HashMap<>();
75
76     private StaticRoutingHelper staticRoutingHelper;
77     private VppEndpointListener vppEndpointListener;
78
79     public FlatOverlayManager(@Nonnull DataBroker dataBroker,
80                               @Nonnull MountedDataBrokerProvider mountedDataBrokerProvider,
81                               @Nonnull VppEndpointListener vppEndpointListener) {
82         this.overlayHelper = new ConfigManagerHelper(mountedDataBrokerProvider);
83         staticRoutingHelper = new StaticRoutingHelper();
84         this.dataBroker = dataBroker;
85         this.vppEndpointListener = vppEndpointListener;
86     }
87
88     public void configureEndpointForFlatOverlay(AddressEndpointWithLocation addressEp) {
89         addStaticRoute(addressEp);
90         addStaticArpAndInfcsToVrf(addressEp);
91     }
92
93     private void addInterfaceInVrf(String hostName, String interfaceName, long vrf) {
94         if (hostRelatedInfoContainer.getPortInterfaceStateOfHost(hostName)
95                 .isVrfConfiguredForInterface(interfaceName)) {
96             return;
97         }
98
99         if (!putVrfInInterface(hostName, interfaceName, vrf)) {
100             LOG.warn("Failed to put interface {} to vrf {}", interfaceName, vrf);
101         } else {
102             hostRelatedInfoContainer
103                     .getPortInterfaceStateOfHost(hostName)
104                     .initializeRoutingContextForInterface(interfaceName, vrf);
105             LOG.debug("Added interface {} to vrf {}", interfaceName, vrf);
106         }
107     }
108
109     private boolean putVrfInInterface(String hostName, String interfaceName, Long vrf) {
110         InstanceIdentifier<Routing> iid = VppIidFactory.getRoutingIid(new InterfaceKey(interfaceName));
111         RoutingBuilder builder = new RoutingBuilder();
112         builder.setIpv4VrfId(vrf);
113         return GbpNetconfTransaction.netconfSyncedWrite(LispUtil.HOSTNAME_TO_IID.apply(hostName), iid,
114                 builder.build(), GbpNetconfTransaction.RETRY_COUNT);
115     }
116
117     private void addStaticArpAndInfcsToVrf(AddressEndpointWithLocation addressEp) {
118         if (!addressEp.getAddressType().equals(IpPrefixType.class)) {
119             return;
120         }
121         Map<String, String> intfcsByHostname = resolveIntfcsByHosts(addressEp);
122         ReadOnlyTransaction readTx = dataBroker.newReadOnlyTransaction();
123         Optional<Config> cfg = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION,
124                 VppIidFactory.getVppRendererConfig(), readTx);
125         readTx.close();
126         intfcsByHostname.entrySet().forEach(intfcByHost -> {
127             if (cfg.isPresent() && !cfg.get().getVppEndpoint().isEmpty()) {
128                 java.util.Optional<VppEndpoint> foundVpp = cfg.get()
129                     .getVppEndpoint()
130                     .stream()
131                     .filter(vpp -> vpp.getVppInterfaceName().equals(intfcByHost.getValue()))
132                     .findFirst();
133                 if (!foundVpp.isPresent()) {
134                     return;
135                 }
136                 Ipv4Address ipv4 = ConfigManagerHelper.getInterfaceIp(addressEp);
137                 putStaticArp(intfcByHost.getKey(), new InterfaceKey(intfcByHost.getValue()),
138                         new PhysAddress(foundVpp.get().getAddress()), new Ipv4AddressNoZone(ipv4));
139                 addInterfaceInVrf(intfcByHost.getKey(), intfcByHost.getValue(),
140                         getVni(addressEp.getTenant().getValue()));
141             }
142         });
143     }
144
145     private boolean putStaticArp(String hostName, InterfaceKey interfaceKey, PhysAddress physAddress,
146         Ipv4AddressNoZone ip) {
147         StaticArpCommand.StaticArpCommandBuilder staticArpCommandBuilder =
148             new StaticArpCommand.StaticArpCommandBuilder();
149
150         staticArpCommandBuilder.setOperation(General.Operations.PUT);
151         staticArpCommandBuilder.setInterfaceKey(interfaceKey);
152         staticArpCommandBuilder.setIp(ip);
153         staticArpCommandBuilder.setLinkLayerAddress(physAddress);
154
155         return GbpNetconfTransaction.netconfSyncedWrite(LispUtil.HOSTNAME_TO_IID.apply(hostName),
156                 staticArpCommandBuilder.build(), GbpNetconfTransaction.RETRY_COUNT);
157     }
158
159     private void addStaticRoute(AddressEndpointWithLocation addressEp) {
160         //TODO caller method
161         Optional<Long> routeId = routeId(addressEp);
162         if(!routeId.isPresent()) {
163             return;
164         }
165
166         //TODO workaround for interfaces that do not exist anymore
167         List<ChildEndpoint> childs = addressEp.getChildEndpoint();
168         if (childs != null) {
169             for (ChildEndpoint child : childs) {
170                 child.getAddress();
171                 child.getContextId();
172                 VppEndpointKey vppEndpointKey = new VppEndpointKey(child.getAddress(), child.getAddressType(),
173                         child.getContextId(), child.getContextType());
174                 ReadOnlyTransaction rTx = dataBroker.newReadOnlyTransaction();
175                 Optional<VppEndpoint> vppEp = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION,
176                         VppIidFactory.getVppRendererConfig().child(VppEndpoint.class, vppEndpointKey), rTx);
177                 rTx.close();
178                 if (!vppEp.isPresent()) {
179                     LOG.warn("Failed to add route for endpoint {}. Interface not created yet.", addressEp);
180                     return;
181                 }
182             }
183         }
184
185         Map<String, String> hostnamesAndIntfcs = resolveIntfcsByHosts(addressEp);
186         long vni = getVni(addressEp.getTenant().getValue());
187         long vrf = vni;
188
189         hostnamesAndIntfcs.forEach((hostname, outgoingInterfaceName) -> {
190
191             Ipv4Address ipWithoutPrefix = ConfigManagerHelper.getInterfaceIp(addressEp);
192
193             Ipv4Prefix ipv4Prefix = overlayHelper.getInterfaceIpAsPrefix(addressEp);
194             LOG.trace("Adding static route for addressEp: {}", addressEp);
195             addStaticRoute(routeId.get(), hostname, vrf, ipWithoutPrefix, ipv4Prefix, outgoingInterfaceName);
196         });
197     }
198
199     public static Map<String,String> resolveIntfcsByHosts(AddressEndpointWithLocation addressEp) {
200         Map<String, String> hostnamesAndIntfcs = new HashMap<>();
201         if (addressEp.getRelativeLocations() != null
202             && addressEp.getRelativeLocations().getExternalLocation() != null) {
203             LOG.trace("deleteStaticRoutingEntry -> addresEp locations: {}", addressEp.getRelativeLocations().getExternalLocation());
204             addressEp.getRelativeLocations().getExternalLocation().forEach(externalLocation -> {
205                 Optional<String> interfaceOptional =
206                     VppPathMapper.interfacePathToInterfaceName(externalLocation.getExternalNodeConnector());
207                 if (interfaceOptional.isPresent()) {
208                     hostnamesAndIntfcs.put(
209                         externalLocation.getExternalNodeMountPoint().firstKeyOf(Node.class).getNodeId().getValue(),
210                         interfaceOptional.get());
211                 } else {
212                     LOG.warn("Couldn't resolve interface name for addrEP: {}", addressEp);
213                 }
214             });
215         } else if (addressEp.getAbsoluteLocation() != null && addressEp.getAbsoluteLocation().getLocationType() != null
216             && addressEp.getAbsoluteLocation().getLocationType() instanceof ExternalLocationCase) {
217             ExternalLocationCase externalLocationCase =
218                 (ExternalLocationCase) addressEp.getAbsoluteLocation().getLocationType();
219             LOG.trace("deleteStaticRoutingEntry -> addresEp location: {}", externalLocationCase);
220             Optional<String> interfaceOptional =
221                 VppPathMapper.interfacePathToInterfaceName(externalLocationCase.getExternalNodeConnector());
222             if (interfaceOptional.isPresent()) {
223                 hostnamesAndIntfcs.put(
224                     externalLocationCase.getExternalNodeMountPoint().firstKeyOf(Node.class).getNodeId().getValue(),
225                     interfaceOptional.get());
226             } else {
227                 LOG.warn("Couldn't resolve interface name for addrEP: {}", addressEp);
228             }
229         }
230         return hostnamesAndIntfcs;
231     }
232
233     private boolean addStaticRoute(Long routeId, String hostName, long vrfId, Ipv4Address ipWithoutPrefix,
234             Ipv4Prefix ipv4Prefix, String outgoingInterfaceName) {
235         if (vrfsByHostname.get(hostName) == null || !vrfsByHostname.get(hostName).keySet().contains(vrfId)) {
236             if (staticRoutingHelper.addRoutingProtocolForVrf(LispUtil.HOSTNAME_TO_IID.apply(hostName), vrfId)) {
237                 addStaticRouteToPublicInterface(hostName, vrfId);
238                 countPlusPlus(hostName, vrfId);
239             }
240         }
241         if (staticRoutingHelper.addSingleStaticRouteInRoutingProtocol(routeId, hostName, vrfId, ipWithoutPrefix,
242                 ipv4Prefix, outgoingInterfaceName, null)) {
243             countPlusPlus(hostName, vrfId);
244             return true;
245         }
246         return false;
247     }
248
249     private void countPlusPlus(String hostName, Long vrfId) {
250         if (vrfsByHostname.get(hostName) == null || vrfsByHostname.get(hostName).get(vrfId) == null) {
251             HashMap<Long, Long> newEntry = new HashMap<>();
252             newEntry.put(vrfId, 1L);
253             vrfsByHostname.put(hostName, newEntry);
254         } else {
255             Long count = vrfsByHostname.get(hostName).get(vrfId);
256             HashMap<Long, Long> newEntry = new HashMap<>();
257             newEntry.put(vrfId, count + 1);
258             vrfsByHostname.put(hostName, newEntry);
259         }
260     }
261
262     private void countMinusMinus(String hostName, Long vrfId) {
263         if (vrfsByHostname.get(hostName) != null && vrfsByHostname.get(hostName).get(vrfId) != null) {
264             Long count = vrfsByHostname.get(hostName).get(vrfId);
265             HashMap<Long, Long> newEntry = new HashMap<>();
266             newEntry.put(vrfId, count - 1);
267             vrfsByHostname.put(hostName, newEntry);
268         }
269     }
270
271     private Long getRouteCount(String hostName, Long vrfId) {
272         if (vrfsByHostname.get(hostName) != null && vrfsByHostname.get(hostName).get(vrfId) != null) {
273             return vrfsByHostname.get(hostName).get(vrfId);
274         }
275         return 0L;
276     }
277
278     private void addStaticRouteToPublicInterface(String hostName, long vrfId) {
279         LOG.trace("addStaticRouteToPublicInterface, hostname: {}, vrfId: {}", hostName, vrfId);
280         Ipv4Address physicalInterfaceIp = hostRelatedInfoContainer.getPhysicalInterfaceState(hostName)
281             .getIp(PhysicalInterfaces.PhysicalInterfaceType.PUBLIC)
282             .getIpv4Address();
283         String interfaceName = hostRelatedInfoContainer.getPhysicalInterfaceState(hostName)
284             .getName(PhysicalInterfaces.PhysicalInterfaceType.PUBLIC);
285         if (interfaceName != null && !interfaceName.isEmpty()) {
286             LOG.trace("Adding Public interface route. hostname: {}, VrfId: {}, Ip: {}, Gw: {}, InterfaceName: {}.",
287                     hostName, vrfId, physicalInterfaceIp, null, interfaceName);
288             if (!staticRoutingHelper.addSingleStaticRouteInRoutingProtocol(0L, hostName, vrfId, physicalInterfaceIp,
289                     IpAddressUtil.toIpV4Prefix(physicalInterfaceIp), interfaceName,
290                     new VniReference(RoutingManager.DEFAULT_TABLE))) {
291                 LOG.warn("Failed to add route for physical interface in vrf {} compute host {}", vrfId, hostName);
292             } else {
293                 LOG.debug("addStaticRouteToPublicInterface -> Added route to public intf ({} via {}) in vrf {} in compute host {}",
294                         IpAddressUtil.toIpV4Prefix(physicalInterfaceIp), interfaceName, vrfId, hostName);
295             }
296         }
297     }
298
299     private Optional<Long> routeId(AddressEndpointWithLocation addressEp) {
300         if (!addressEp.getAddressType().equals(IpPrefixType.class) || !addressEp.getAddress().contains("/")) {
301             return Optional.absent();
302         }
303         String ipAddress = addressEp.getAddress().split("/")[0];
304         SubnetUtils subnet = new SubnetUtils(addressEp.getAddress());
305         Long routeId = Integer.toUnsignedLong(subnet.getInfo().asInteger(ipAddress));
306         return Optional.of(routeId);
307     }
308
309     public void deleteStaticRoutingEntry(AddressEndpointWithLocation addressEp) {
310         //TODO create
311         Optional<Long> routeId = routeId(addressEp);
312         if(!routeId.isPresent()) {
313             return;
314         }
315         long vni = getVni(addressEp.getTenant().getValue());
316         long vrfId = vni;
317         Map<String, String> hostnamesAndIntfcs = resolveIntfcsByHosts(addressEp);
318         LOG.trace("deleteStaticRoutingEntry -> addresEp locations: {}", addressEp);
319         hostnamesAndIntfcs.entrySet().forEach(intfcsByHost -> {
320             LOG.trace("deleteStaticRoutingEntry -> Deleting addresEp: {} for interface: {}, on node: {}", addressEp.getKey(), intfcsByHost.getValue(), intfcsByHost.getKey());
321             Ipv4Address ipWithoutPrefix = ConfigManagerHelper.getInterfaceIp(addressEp);
322             if (!staticRoutingHelper.deleteSingleStaticRouteFromRoutingProtocol(intfcsByHost.getKey(), vrfId, routeId.get())) {
323                 LOG.warn("Failed to delete route ({} via {}) from vrf {} from host{}", ipWithoutPrefix,
324                     intfcsByHost.getValue(), vrfId, intfcsByHost);
325             } else {
326                 LOG.trace("deletedStaticRoutingEntry -> Deleted addresEp: {} for interface: {}, on node: {}", addressEp.getKey(), intfcsByHost.getValue(), intfcsByHost.getKey());
327                 countMinusMinus(intfcsByHost.getKey(), vrfId);
328                 if (getRouteCount(intfcsByHost.getKey(), vrfId) <= 1) {
329                     LOG.info("deletedStaticRoutingEntry -> Removing route to public int from VRF {}", vrfId);
330                     staticRoutingHelper.deleteSingleStaticRouteFromRoutingProtocol(intfcsByHost.getKey(), vrfId, 0L);
331                     InstanceIdentifier<RoutingProtocol> protocol =
332                             VppIidFactory.getRoutingInstanceIid(StaticRoutingHelper.getRoutingProtocolName(vrfId));
333                     if(GbpNetconfTransaction.netconfSyncedDelete(
334                             VppIidFactory.getNetconfNodeIid(new NodeId(intfcsByHost.getKey())), protocol,
335                             GbpNetconfTransaction.RETRY_COUNT)) {
336                         vrfsByHostname.get(intfcsByHost.getKey()).remove(vrfId);
337                     }
338                 }
339                 LOG.trace("deleteStaticRoutingEntry -> flushPendingVppEndpoint for addresEp: {}", addressEp);
340                 hostRelatedInfoContainer.deleteRouteFromIntfc(intfcsByHost.getKey(), intfcsByHost.getValue(), routeId.get());
341                 vppEndpointListener.flushPendingVppEndpoint(intfcsByHost.getKey(), intfcsByHost.getValue());
342                 LOG.debug("Delete Static Route ({} via {}) from vrf {} from host {}", ipWithoutPrefix,
343                         intfcsByHost.getValue(), vrfId, intfcsByHost);
344             }
345         });
346
347     }
348
349     public long getVni(String tenantUuid) {
350         long vni = neutronTenantToVniMapper.getVni(tenantUuid);
351         LOG.debug("Debugging: getVni {} = {}", tenantUuid, vni);
352         return vni;
353     }
354 }