Optimize DHCP relay processing for VPP
[groupbasedpolicy.git] / renderers / vpp / src / main / java / org / opendaylight / groupbasedpolicy / renderer / vpp / policy / ForwardingManager.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 org.opendaylight.groupbasedpolicy.renderer.vpp.policy;
10
11 import java.util.ArrayList;
12 import java.util.HashMap;
13 import java.util.List;
14 import java.util.Map;
15 import java.util.Set;
16 import java.util.concurrent.ExecutionException;
17 import java.util.concurrent.TimeUnit;
18 import java.util.concurrent.TimeoutException;
19
20 import javax.annotation.Nonnull;
21 import javax.annotation.Nullable;
22
23 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
24 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
25 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
26 import org.opendaylight.groupbasedpolicy.renderer.vpp.api.BridgeDomainManager;
27 import org.opendaylight.groupbasedpolicy.renderer.vpp.commands.DhcpRelayCommand;
28 import org.opendaylight.groupbasedpolicy.renderer.vpp.commands.RoutingCommand;
29 import org.opendaylight.groupbasedpolicy.renderer.vpp.config.ConfigUtil;
30 import org.opendaylight.groupbasedpolicy.renderer.vpp.dhcp.DhcpRelayHandler;
31 import org.opendaylight.groupbasedpolicy.renderer.vpp.iface.InterfaceManager;
32 import org.opendaylight.groupbasedpolicy.renderer.vpp.lisp.LispStateManager;
33 import org.opendaylight.groupbasedpolicy.renderer.vpp.lisp.loopback.LoopbackManager;
34 import org.opendaylight.groupbasedpolicy.renderer.vpp.lisp.flat.overlay.FlatOverlayManager;
35 import org.opendaylight.groupbasedpolicy.renderer.vpp.lisp.mappers.NeutronTenantToVniMapper;
36 import org.opendaylight.groupbasedpolicy.renderer.vpp.nat.NatManager;
37 import org.opendaylight.groupbasedpolicy.renderer.vpp.nat.NatUtil;
38 import org.opendaylight.groupbasedpolicy.renderer.vpp.policy.acl.AclManager;
39 import org.opendaylight.groupbasedpolicy.renderer.vpp.routing.RoutingManager;
40 import org.opendaylight.groupbasedpolicy.renderer.vpp.util.General;
41 import org.opendaylight.groupbasedpolicy.renderer.vpp.util.KeyFactory;
42 import org.opendaylight.groupbasedpolicy.util.DataStoreHelper;
43 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
44 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.nat.instances.nat.instance.mapping.table.MappingEntryBuilder;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.common.endpoint.fields.NetworkContainment;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.common.endpoint.fields.network.containment.Containment;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.common.endpoint.fields.network.containment.containment.ForwardingContextContainment;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.common.endpoint.fields.network.containment.containment.NetworkDomainContainment;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.absolute.location.absolute.location.LocationType;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.absolute.location.absolute.location.location.type.ExternalLocationCase;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.child.endpoints.ChildEndpoint;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.parent.child.endpoints.ParentEndpointChoice;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.parent.child.endpoints.parent.endpoint.choice.ParentEndpointCase;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.parent.child.endpoints.parent.endpoint.choice.parent.endpoint._case.ParentEndpoint;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ContextId;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.NetworkDomainId;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev170511.IpPrefixType;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev170511.L2FloodDomain;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev170511.MacAddressType;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev170511.SubnetAugmentRenderer;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.rev160427.NetworkDomain;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.rev160427.forwarding.fields.Parent;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.NatAddressRenderer;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.Configuration;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.Endpoints;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.RendererForwarding;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.endpoints.AddressEndpointWithLocation;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.endpoints.RendererEndpointKey;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.forwarding.RendererForwardingByTenant;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.forwarding.renderer.forwarding.by.tenant.RendererForwardingContext;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.forwarding.renderer.forwarding.by.tenant.RendererForwardingContextKey;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.forwarding.renderer.forwarding.by.tenant.RendererNetworkDomain;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.forwarding.renderer.forwarding.by.tenant.RendererNetworkDomainKey;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.Config;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.VlanNetwork;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425._interface.attributes.InterfaceTypeChoice;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425._interface.attributes._interface.type.choice.LoopbackCase;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.GbpBridgeDomain;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.GbpBridgeDomainKey;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpoint;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpointKey;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.renderers.renderer.renderer.nodes.renderer.node.PhysicalInterface;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.VlanId;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev170607.VxlanVni;
86 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
87 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
88 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
89 import org.slf4j.Logger;
90 import org.slf4j.LoggerFactory;
91
92 import com.google.common.annotations.VisibleForTesting;
93 import com.google.common.base.Optional;
94 import com.google.common.base.Preconditions;
95 import com.google.common.base.Strings;
96 import com.google.common.collect.SetMultimap;
97 import com.google.common.collect.Table;
98
99 public final class ForwardingManager {
100
101     private static final Logger LOG = LoggerFactory.getLogger(ForwardingManager.class);
102     @VisibleForTesting
103     private byte WAIT_FOR_BD_PROCESSING = 60; // seconds
104     private long lastVxlanVni = 1L;
105     private final Map<String, VxlanVni> vxlanVniByBridgeDomain = new HashMap<>();
106     private final InterfaceManager ifaceManager;
107     private final AclManager aclManager;
108     private final BridgeDomainManager bdManager;
109     private final NatManager natManager;
110     private final RoutingManager routingManager;
111     private final LispStateManager lispStateManager;
112     private final LoopbackManager loopbackManager;
113     private final FlatOverlayManager flatOverlayManager;
114     private final DhcpRelayHandler dhcpRelayHandler;
115     private final DataBroker dataBroker;
116
117     public ForwardingManager(@Nonnull InterfaceManager ifaceManager, @Nonnull AclManager aclManager,
118                              @Nonnull NatManager natManager, @Nonnull RoutingManager routingManager,
119                              @Nonnull BridgeDomainManager bdManager, @Nonnull LispStateManager lispStateManager,
120                              @Nonnull LoopbackManager loopbackManager, @Nonnull FlatOverlayManager flatOverlayManager,
121                              @Nonnull DhcpRelayHandler dhcpRelayHandler, @Nonnull DataBroker dataBroker) {
122         this.ifaceManager = Preconditions.checkNotNull(ifaceManager);
123         this.bdManager = Preconditions.checkNotNull(bdManager);
124         this.natManager = Preconditions.checkNotNull(natManager);
125         this.routingManager = Preconditions.checkNotNull(routingManager);
126         this.lispStateManager = Preconditions.checkNotNull(lispStateManager);
127         this.loopbackManager = Preconditions.checkNotNull(loopbackManager);
128         this.flatOverlayManager = Preconditions.checkNotNull(flatOverlayManager);
129         this.dataBroker = Preconditions.checkNotNull(dataBroker);
130         this.aclManager = Preconditions.checkNotNull(aclManager);
131         this.dhcpRelayHandler = Preconditions.checkNotNull(dhcpRelayHandler);
132     }
133
134     Optional<GbpBridgeDomain> readGbpBridgeDomainConfig(String name) {
135         InstanceIdentifier<GbpBridgeDomain> bdIid = InstanceIdentifier.builder(Config.class)
136             .child(GbpBridgeDomain.class, new GbpBridgeDomainKey(name))
137             .build();
138         ReadOnlyTransaction rTx = dataBroker.newReadOnlyTransaction();
139         Optional<GbpBridgeDomain> optBd = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, bdIid, rTx);
140         rTx.close();
141         return optBd;
142     }
143
144     void createBridgeDomainOnNodes(SetMultimap<String, NodeId> vppNodesByBridgeDomain) {
145         for (String bd : vppNodesByBridgeDomain.keySet()) {
146             Optional<GbpBridgeDomain> bdConfig = readGbpBridgeDomainConfig(bd);
147             Set<NodeId> vppNodes = vppNodesByBridgeDomain.get(bd);
148             if (bdConfig.isPresent()) {
149                 if (bdConfig.get().getType().equals(VlanNetwork.class)) {
150                     createVlanBridgeDomains(bd, bdConfig.get().getVlan(), vppNodes);
151                 }
152             } else {
153                 VxlanVni vxlanVni = vxlanVniByBridgeDomain.get(bd);
154                 if (vxlanVni == null) {
155                     vxlanVni = new VxlanVni(lastVxlanVni++);
156                     vxlanVniByBridgeDomain.put(bd, vxlanVni);
157                 }
158                 createVxlanBridgeDomains(bd, vxlanVni, vppNodes);
159             }
160         }
161     }
162
163     private void createVxlanBridgeDomains(final String bd, final VxlanVni vni, final Set<NodeId> vppNodes) {
164         for (NodeId vppNode : vppNodes) {
165             try {
166                 LOG.debug("Creating VXLAN bridge-domain {} on node {} with VNI {}", bd, vppNode.getValue(),
167                         vni);
168                 bdManager.createVxlanBridgeDomainOnVppNode(bd, vni, vppNode).get(WAIT_FOR_BD_PROCESSING,
169                         TimeUnit.SECONDS);
170             } catch (InterruptedException | ExecutionException e) {
171                 LOG.warn("VXLAN Bridge domain {} was not created on node {}", bd, vppNode.getValue(), e);
172             } catch (TimeoutException e) {
173                 LOG.warn("Probably, VXLAN Bridge domain {} was not created on node {} because BridgeDomainManager "
174                         + "did not respond by {} seconds. Check VBD log for more details",
175                         bd, vppNode.getValue(), WAIT_FOR_BD_PROCESSING, e);
176             }
177         }
178     }
179
180     private void createVlanBridgeDomains(final String bd, final VlanId vlanId, final Set<NodeId> vppNodes) {
181         for (NodeId vppNode : vppNodes) {
182             try {
183                 LOG.debug("Creating VLAN bridge-domain {} on node {} with VLAN ID {}", bd, vppNode.getValue(),
184                         vlanId.getValue());
185                 bdManager.createVlanBridgeDomainOnVppNode(bd, vlanId, vppNode).get(WAIT_FOR_BD_PROCESSING,
186                         TimeUnit.SECONDS);
187             } catch (InterruptedException | ExecutionException e) {
188                 LOG.warn("VLAN Bridge domain {} was not created on node {}", bd, vppNode.getValue(), e);
189             } catch (TimeoutException e) {
190                 LOG.warn("Probably, VLAN Bridge domain {} was not created on node {} because BridgeDomainManager "
191                         + "did not respond by {} seconds. Check VBD log for more details",
192                         bd, vppNode.getValue(), WAIT_FOR_BD_PROCESSING, e);
193             }
194         }
195     }
196
197     void removeBridgeDomainOnNodes(final SetMultimap<String, NodeId> vppNodesByBridgeDomain) {
198         for (String bd : vppNodesByBridgeDomain.keySet()) {
199             Set<NodeId> vppNodes = vppNodesByBridgeDomain.get(bd);
200             for (NodeId vppNode : vppNodes) {
201                 try {
202                     bdManager.removeBridgeDomainFromVppNode(bd, vppNode).get(WAIT_FOR_BD_PROCESSING,
203                             TimeUnit.SECONDS);
204                 } catch (InterruptedException | ExecutionException e) {
205                     LOG.warn("Bridge domain {} was not removed from node {}", bd, vppNode.getValue(), e);
206                 } catch (TimeoutException e) {
207                     LOG.warn("Probably, bridge domain {} was not removed from node {} because BridgeDomainManager "
208                             + "did not respond by {} seconds. Check VBD log for more details",
209                             bd, vppNode.getValue(), WAIT_FOR_BD_PROCESSING, e);
210                 }
211             }
212         }
213     }
214
215     void createForwardingForEndpoint(RendererEndpointKey rEpKey, PolicyContext policyCtx) {
216         AddressEndpointWithLocation rEp = policyCtx.getAddrEpByKey().get(KeyFactory.addressEndpointKey(rEpKey));
217
218         if (ConfigUtil.getInstance().isLispOverlayEnabled()) {
219             lispStateManager.processCreateEndPoint(rEp);
220             if (ConfigUtil.getInstance().isL3FlatEnabled()) {
221                 flatOverlayManager.configureEndpointForFlatOverlay(rEp);
222                 loopbackManager.createSimpleLoopbackIfNeeded(rEp);
223             }
224         }
225
226         ExternalLocationCase rEpLoc = resolveAndValidateLocation(rEp);
227         if (rEpLoc == null || Strings.isNullOrEmpty(rEpLoc.getExternalNodeConnector())) {
228             // TODO add it to the status for renderer manager
229             LOG.info("Renderer endpoint does not have external-node-connector therefore it is ignored {}", rEp);
230             return;
231         }
232
233         if (Strings.isNullOrEmpty(rEpLoc.getExternalNode())) {
234             java.util.Optional<String> optL2FloodDomain = resolveL2FloodDomain(rEp, policyCtx);
235             if (!optL2FloodDomain.isPresent()) {
236                 // TODO add it to the status for renderer manager
237                 LOG.info("Renderer endpoint does not have l2FloodDomain as network containment {}", rEp);
238                 return;
239             }
240             if (!ConfigUtil.getInstance().isL3FlatEnabled()) {
241                 String l2FloodDomain = optL2FloodDomain.get();
242                 try {
243                     ifaceManager.addBridgeDomainToInterface(l2FloodDomain, rEp, aclManager.resolveAclsOnInterface(
244                             rEpKey, policyCtx).get(), isBviForEndpoint(rEp)).get();
245                     LOG.debug("Interface added to bridge-domain {} for endpoint {}", l2FloodDomain, rEp);
246
247                     if (ConfigUtil.getInstance().isLispOverlayEnabled()) {
248                         loopbackManager.createBviLoopbackIfNeeded(rEp, l2FloodDomain);
249                     }
250
251                 } catch (InterruptedException | ExecutionException e) {
252                     // TODO add it to the status for renderer manager
253                     LOG.warn("Interface was not added to bridge-domain {} for endpoint {}", l2FloodDomain, rEp, e);
254                 }
255             }
256             aclManager.updateAclsForPeers(policyCtx, rEpKey);
257         } else {
258             LOG.debug("Forwarding is not created - Location of renderer endpoint contains "
259                     + "external-node therefore VPP renderer assumes that interface for endpoint is "
260                     + "already assigned in bridge-domain representing external-node. {}", rEp);
261         }
262     }
263
264     private boolean isBviForEndpoint(AddressEndpointWithLocation rEp) {
265         List<ChildEndpoint> childEndpointList = rEp.getChildEndpoint();
266         VppEndpointKey vppEndpointKey;
267         if(childEndpointList == null || !childEndpointList.stream().findFirst().isPresent()) {
268             vppEndpointKey =
269                 new VppEndpointKey(rEp.getAddress(), rEp.getAddressType(), rEp.getContextId(), rEp.getContextType());
270         } else {
271             java.util.Optional<ChildEndpoint> childEndpointOpt = childEndpointList.stream().findFirst();
272             vppEndpointKey =
273                 new VppEndpointKey(childEndpointOpt.get().getAddress(), childEndpointOpt.get().getAddressType(),
274                     childEndpointOpt.get().getContextId(), childEndpointOpt.get().getContextType());
275         }
276         ReadOnlyTransaction rTx = dataBroker.newReadOnlyTransaction();
277         Optional<VppEndpoint> vppEndpointOptional =
278             DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION,
279                 InstanceIdentifier.builder(Config.class).child(VppEndpoint.class, vppEndpointKey).build(), rTx);
280         rTx.close();
281         if (vppEndpointOptional.isPresent()) {
282             InterfaceTypeChoice interfaceTypeChoice = vppEndpointOptional.get().getInterfaceTypeChoice();
283             if (interfaceTypeChoice instanceof LoopbackCase) {
284                 LOG.trace("Found new BVI interface: {}", rEp.getKey());
285                 return ((LoopbackCase) interfaceTypeChoice).isBvi();
286             }
287         }
288
289         return false;
290     }
291
292     void removeForwardingForEndpoint(RendererEndpointKey rEpKey, PolicyContext policyCtx) {
293         AddressEndpointWithLocation rEp = policyCtx.getAddrEpByKey().get(KeyFactory.addressEndpointKey(rEpKey));
294         ExternalLocationCase rEpLoc = resolveAndValidateLocation(rEp);
295
296         if (ConfigUtil.getInstance().isLispOverlayEnabled()) {
297             lispStateManager.processDeleteEndpoint(rEp);
298             if (ConfigUtil.getInstance().isL3FlatEnabled()) {
299                 flatOverlayManager.handleEndpointDeleteForFlatOverlay(rEp);
300             }
301             loopbackManager.handleEndpointDelete(rEp);
302         }
303
304         if (rEpLoc == null || Strings.isNullOrEmpty(rEpLoc.getExternalNodeConnector())) {
305             // nothing was created for endpoint therefore nothing is removed
306             return;
307         }
308         if (!Strings.isNullOrEmpty(rEpLoc.getExternalNode())) {
309             try {
310                 ifaceManager.deleteBridgeDomainFromInterface(rEp).get();
311                 LOG.debug("bridge-domain was deleted from interface for endpoint {}", rEp);
312             } catch (InterruptedException | ExecutionException e) {
313                 // TODO add it to the status for renderer manager
314                 LOG.warn("bridge-domain was not deleted from interface for endpoint {}", rEp, e);
315             }
316         } else {
317             LOG.debug("Forwarding is not removed - Location of renderer endpoint does not contain "
318                     + "external-node therefore VPP renderer assumes that interface for endpoint is not "
319                     + "assigned to bridge-domain representing external-node. {}", rEp);
320         }
321     }
322
323     static ExternalLocationCase resolveAndValidateLocation(AddressEndpointWithLocation addrEpWithLoc) {
324         if (addrEpWithLoc.getAbsoluteLocation() != null
325                 && addrEpWithLoc.getAbsoluteLocation().getLocationType() != null) {
326             LocationType locationType = addrEpWithLoc.getAbsoluteLocation().getLocationType();
327             if (!(locationType instanceof ExternalLocationCase)) {
328                 throw new IllegalStateException("Endpoint does not have external location " + addrEpWithLoc);
329             }
330             ExternalLocationCase result = (ExternalLocationCase) locationType;
331             if (result.getExternalNodeMountPoint() == null) {
332                 throw new IllegalStateException("Endpoint does not have external-node-mount-point " + addrEpWithLoc);
333             }
334             return result;
335         }
336         return null;
337     }
338
339     static java.util.Optional<String> resolveL2FloodDomain(@Nonnull AddressEndpointWithLocation ep,
340             @Nonnull PolicyContext policyCtx) {
341         NetworkContainment netCont = ep.getNetworkContainment();
342         if (netCont == null) {
343             return java.util.Optional.empty();
344         }
345         Containment containment = netCont.getContainment();
346         if (containment instanceof ForwardingContextContainment) {
347             ForwardingContextContainment fwCtxCont = (ForwardingContextContainment) containment;
348             if (L2FloodDomain.class.equals(fwCtxCont.getContextType())) {
349                 return fwCtxCont.getContextId() == null ? java.util.Optional.empty() : java.util.Optional
350                     .of(fwCtxCont.getContextId().getValue());
351             }
352         }
353         if (containment instanceof NetworkDomainContainment) {
354             final NetworkDomainContainment netDomainCont = (NetworkDomainContainment) containment;
355             final TenantId tenantId = ep.getTenant();
356             final NetworkDomainId domainId = netDomainCont.getNetworkDomainId();
357             final Class<? extends NetworkDomain> domainKey = netDomainCont.getNetworkDomainType();
358             final RendererNetworkDomainKey rendererNetworkDomainKey = new RendererNetworkDomainKey(domainId, domainKey);
359             final RendererNetworkDomain rendererNetworkDomain =
360                     policyCtx.getNetworkDomainTable().get(tenantId, rendererNetworkDomainKey);
361             if (rendererNetworkDomain == null) {
362                 LOG.debug("Network domain not found. Containment: {}", containment);
363                 return java.util.Optional.empty();
364             }
365             java.util.Optional<String> optL2Fd = getForwardingCtxForParent(ep.getTenant(),
366                     rendererNetworkDomain.getParent(), policyCtx.getForwardingCtxTable())
367                         .filter(fwdCtx -> L2FloodDomain.class.equals(fwdCtx.getContextType()))
368                         .map(RendererForwardingContext::getContextId)
369                         .map(ContextId::getValue);
370             if (!optL2Fd.isPresent()) {
371                 LOG.debug("network-domain-containment in endpoint does not have L2-flood-domain as parent. "
372                         + "This case is not supported in VPP renderer. {}", ep);
373             }
374             return optL2Fd;
375         }
376         return java.util.Optional.empty();
377     }
378
379     private static @Nonnull java.util.Optional<RendererForwardingContext> getForwardingCtxForParent(
380             @Nullable TenantId tenant, @Nullable Parent parent,
381             Table<TenantId, RendererForwardingContextKey, RendererForwardingContext> forwardingCtxTable) {
382         if (tenant == null || parent == null) {
383             return java.util.Optional.empty();
384         }
385         if (parent.getContextId() != null && parent.getContextType() != null) {
386             return java.util.Optional.ofNullable(forwardingCtxTable.get(tenant,
387                     new RendererForwardingContextKey(parent.getContextId(), parent.getContextType())));
388         }
389         return java.util.Optional.empty();
390     }
391
392     void syncNatEntries(PolicyContext policyCtx) {
393         Configuration cfg = policyCtx.getPolicy().getConfiguration();
394         if(cfg != null) {
395             final List<MappingEntryBuilder> sNatEntries = resolveStaticNatTableEntries(cfg.getEndpoints());
396             LOG.trace("Syncing static NAT entries {}", sNatEntries);
397             if (cfg.getRendererForwarding() != null) {
398                 for (RendererForwardingByTenant fwd : cfg.getRendererForwarding().getRendererForwardingByTenant()) {
399                     List<InstanceIdentifier<PhysicalInterface>> physIfacesIid =
400                         resolvePhysicalInterfacesForNat(fwd.getRendererNetworkDomain());
401                     natManager.submitNatChanges(physIfacesIid, sNatEntries, policyCtx, true);
402                 }
403             }
404         }
405     }
406
407     void deleteNatEntries(PolicyContext policyCtx) {
408         Configuration cfg = policyCtx.getPolicy().getConfiguration();
409         if(cfg != null) {
410             List<MappingEntryBuilder> natEntries = resolveStaticNatTableEntries(cfg.getEndpoints());
411             if (natEntries.isEmpty()) {
412                 LOG.trace("NAT entries are empty,nothing to delete, skipping processing.");
413                 return;
414             }
415             LOG.trace("Deleting NAT entries {}", natEntries);
416             if (cfg.getRendererForwarding() != null) {
417                 for (RendererForwardingByTenant fwd : cfg.getRendererForwarding().getRendererForwardingByTenant()) {
418                     List<InstanceIdentifier<PhysicalInterface>> physIfacesIid =
419                         resolvePhysicalInterfacesForNat(fwd.getRendererNetworkDomain());
420                     natManager.submitNatChanges(physIfacesIid, natEntries, policyCtx, false);
421                 }
422             }
423         }
424     }
425
426     private List<InstanceIdentifier<PhysicalInterface>> resolvePhysicalInterfacesForNat(
427         List<RendererNetworkDomain> rendNetDomains) {
428         List<InstanceIdentifier<PhysicalInterface>> physIfaces = new ArrayList<>();
429         for (RendererNetworkDomain rendDomain : rendNetDomains) {
430             Optional<IpPrefix> resolvedIpPrefix = resolveIpPrefix(rendDomain);
431             if (resolvedIpPrefix.isPresent()) {
432                 Optional<InstanceIdentifier<PhysicalInterface>> resPhIface =
433                     NatUtil.resolvePhysicalInterface(resolvedIpPrefix.get(), dataBroker.newReadOnlyTransaction());
434                 if (resPhIface.isPresent()) {
435                     physIfaces.add(resPhIface.get());
436                 }
437             }
438         }
439         return physIfaces;
440     }
441
442     private Optional<IpPrefix> resolveIpPrefix(RendererNetworkDomain rendDomain) {
443         SubnetAugmentRenderer subnetAug = rendDomain.getAugmentation(SubnetAugmentRenderer.class);
444         if (subnetAug.getSubnet() != null) {
445             return Optional.of(subnetAug.getSubnet().getIpPrefix());
446         }
447         return Optional.absent();
448     }
449
450     private List<MappingEntryBuilder> resolveStaticNatTableEntries(Endpoints endpoints) {
451         List<MappingEntryBuilder> sNatEntries = new ArrayList<>();
452         for (AddressEndpointWithLocation addrEp : endpoints.getAddressEndpointWithLocation()) {
453             if (addrEp.getAugmentation(NatAddressRenderer.class) == null) {
454                 continue;
455             }
456             String endpointIP = resolveEpIpAddressForSnat(addrEp);
457
458             if (endpointIP == null) {
459                 LOG.warn("Endpoints {} IP cannot be null, skipping processing of SNAT", addrEp);
460                 continue;
461             }
462
463             NatAddressRenderer natAddr = addrEp.getAugmentation(NatAddressRenderer.class);
464             if (natAddr.getNatAddress() == null && natAddr.getNatAddress().getIpv4Address() == null) {
465                 LOG.warn("Only Ipv4 SNAT is currently supported. Cannot apply SNAT for [{},{}]", endpointIP,
466                         natAddr.getNatAddress());
467                 continue;
468             }
469             Optional<MappingEntryBuilder> entry = natManager.resolveSnatEntry(endpointIP, natAddr.getNatAddress()
470                 .getIpv4Address());
471             if (entry.isPresent()) {
472                 sNatEntries.add(entry.get());
473             }
474         }
475         return sNatEntries;
476     }
477
478     private String resolveEpIpAddressForSnat(AddressEndpointWithLocation addrEp) {
479         LOG.trace("Resolving Ep IP address for SNAT from Address endpoint: {}", addrEp);
480         if (addrEp.getAddressType().equals(MacAddressType.class)) {
481             ParentEndpointChoice parentEndpointChoice = addrEp.getParentEndpointChoice();
482             if (parentEndpointChoice instanceof ParentEndpointCase
483                 && !((ParentEndpointCase) parentEndpointChoice).getParentEndpoint().isEmpty()) {
484                 ParentEndpoint parentEndpoint = ((ParentEndpointCase) parentEndpointChoice).getParentEndpoint().get(0);
485                 if (parentEndpoint.getAddressType().equals(IpPrefixType.class)) {
486                     String[] ipWithPrefix = parentEndpoint.getAddress().split("/");
487                     LOG.trace("Resolved Ep IP address for SNAT. Resolved  IP Address: {}", ipWithPrefix[0]);
488                     return ipWithPrefix[0];
489                 } else {
490                     LOG.warn("Endpoint {} Does not have a Parent Ep with IP for SNAT. skipping processing of SNAT",
491                         addrEp);
492                     return null;
493                 }
494
495             } else {
496                 LOG.warn("Endpoint {} Does not contain IP address for SNAT. skipping processing of SNAT", addrEp);
497                 return null;
498             }
499         } else if (addrEp.getAddressType().equals(IpPrefixType.class)) {
500             String ipAddress = addrEp.getAddress().split("/")[0];
501             LOG.trace("Resolved Ep IP address for SNAT. Resolved  IP Address: {}", ipAddress);
502             return ipAddress;
503         }
504         return null;
505     }
506
507     @VisibleForTesting
508     void setTimer(byte time) {
509         WAIT_FOR_BD_PROCESSING = time;
510     }
511
512     void syncRouting(PolicyContext policyCtx) {
513         Configuration cfg = policyCtx.getPolicy().getConfiguration();
514         if (cfg != null && cfg.getRendererForwarding() != null) {
515             for (RendererForwardingByTenant fwd : cfg.getRendererForwarding().getRendererForwardingByTenant()) {
516                 if (fwd == null) {
517                     continue;
518                 }
519
520                 List<InstanceIdentifier<PhysicalInterface>>
521                     physIfacesIid = resolvePhysicalInterfacesForNat(fwd.getRendererNetworkDomain());
522                 Map<InstanceIdentifier<Node>, RoutingCommand> routingCommandMap =
523                     routingManager.createRouting(fwd, physIfacesIid, General.Operations.PUT);
524
525                 routingCommandMap.forEach((node, command) -> {
526                     if (command != null && routingManager.submitRouting(command, node)) {
527                         LOG.debug("Routing was successfully applied: {}.", command);
528                     }
529                 });
530             }
531         }
532     }
533
534     void deleteRouting(PolicyContext policyCtx) {
535         Configuration cfg = policyCtx.getPolicy().getConfiguration();
536         if (cfg != null && cfg.getRendererForwarding() != null) {
537             for (RendererForwardingByTenant fwd : cfg.getRendererForwarding().getRendererForwardingByTenant()) {
538                 if (fwd == null) {
539                     continue;
540                 }
541
542                 List<InstanceIdentifier<PhysicalInterface>>
543                     physIfacesIid = resolvePhysicalInterfacesForNat(fwd.getRendererNetworkDomain());
544                 Map<InstanceIdentifier<Node>, RoutingCommand> routingCommandMap =
545                     routingManager.createRouting(fwd, physIfacesIid, General.Operations.DELETE);
546                 routingCommandMap.forEach((node, command) -> {
547                     if (command != null && routingManager.submitRouting(command, node)) {
548                         LOG.debug("Routing was successfully removed: {}.", command);
549                     }
550                 });
551             }
552         }
553     }
554
555     List<DhcpRelayCommand> createDhcpRelay(RendererForwarding rendererForwarding,
556         SetMultimap<String, NodeId> vppNodesByL2Fd) {
557         for (RendererForwardingByTenant forwardingByTenant : rendererForwarding.getRendererForwardingByTenant()) {
558             long vni_vrfid = NeutronTenantToVniMapper.getInstance().getVni(forwardingByTenant.getTenantId().getValue());
559             for (RendererNetworkDomain networkDomain : forwardingByTenant.getRendererNetworkDomain()) {
560                 org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev170511.has.subnet.Subnet
561                     subnet = networkDomain.getAugmentation((SubnetAugmentRenderer.class)).getSubnet();
562                 if (subnet.isIsTenant()) {
563                     LOG.trace("Creating Dhcp Relay from subnet: {}, vrfid: {}, vppNodesByL2Fd: {}", subnet, vni_vrfid,
564                         vppNodesByL2Fd);
565                     return dhcpRelayHandler.getCreatedIpv4DhcpRelays(vni_vrfid, subnet, vppNodesByL2Fd);
566                 }
567             }
568         }
569         return new ArrayList<>();
570     }
571
572     List<DhcpRelayCommand> deleteDhcpRelay(RendererForwarding rendererForwarding, SetMultimap<String,
573         NodeId> vppNodesByL2Fd) {
574         for (RendererForwardingByTenant forwardingByTenant : rendererForwarding.getRendererForwardingByTenant()) {
575             long vni_vrfid = NeutronTenantToVniMapper.getInstance().getVni(forwardingByTenant.getTenantId().getValue());
576             for (RendererNetworkDomain networkDomain : forwardingByTenant.getRendererNetworkDomain()) {
577                 org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev170511.has.subnet.Subnet
578                     subnet = networkDomain.getAugmentation((SubnetAugmentRenderer.class)).getSubnet();
579                 if (subnet.isIsTenant()) {
580                     return dhcpRelayHandler.getDeletedIpv4DhcpRelays(vni_vrfid, subnet, vppNodesByL2Fd);
581                 }
582             }
583         }
584         return new ArrayList<>();
585     }
586
587     void syncDhcpRelay(List<DhcpRelayCommand> createdDhcpRelays, List<DhcpRelayCommand> deletedDhcpRelays) {
588         deletedDhcpRelays.stream().filter(deleteCommand -> !createdDhcpRelays.contains(deleteCommand))
589             .forEach(dhcpRelayHandler::submitDhcpRelay);
590
591         createdDhcpRelays.stream().filter(createCommand -> !deletedDhcpRelays.contains(createCommand))
592             .forEach(dhcpRelayHandler::submitDhcpRelay);
593     }
594 }