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