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