introducing loopback port 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.HashMap;
12 import java.util.Map;
13 import java.util.Set;
14 import java.util.concurrent.ExecutionException;
15 import java.util.concurrent.TimeUnit;
16 import java.util.concurrent.TimeoutException;
17
18 import javax.annotation.Nonnull;
19 import javax.annotation.Nullable;
20
21 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
22 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
23 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
24 import org.opendaylight.groupbasedpolicy.renderer.vpp.api.BridgeDomainManager;
25 import org.opendaylight.groupbasedpolicy.renderer.vpp.iface.InterfaceManager;
26 import org.opendaylight.groupbasedpolicy.renderer.vpp.util.KeyFactory;
27 import org.opendaylight.groupbasedpolicy.util.DataStoreHelper;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.common.endpoint.fields.NetworkContainment;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.common.endpoint.fields.network.containment.Containment;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.common.endpoint.fields.network.containment.containment.ForwardingContextContainment;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.common.endpoint.fields.network.containment.containment.NetworkDomainContainment;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.absolute.location.absolute.location.LocationType;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.absolute.location.absolute.location.location.type.ExternalLocationCase;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ContextId;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev160427.L2FloodDomain;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.rev160427.forwarding.fields.Parent;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.endpoints.AddressEndpointWithLocation;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.endpoints.RendererEndpointKey;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.forwarding.renderer.forwarding.by.tenant.RendererForwardingContext;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.forwarding.renderer.forwarding.by.tenant.RendererForwardingContextKey;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.forwarding.renderer.forwarding.by.tenant.RendererNetworkDomain;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.forwarding.renderer.forwarding.by.tenant.RendererNetworkDomainKey;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.Config;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.VlanNetwork;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.GbpBridgeDomain;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.GbpBridgeDomainKey;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpoint;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpointKey;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.vpp.endpoint.InterfaceTypeChoice;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.vpp.endpoint._interface.type.choice.LoopbackCase;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.VlanId;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VxlanVni;
54 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
55 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
56 import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
58
59 import com.google.common.annotations.VisibleForTesting;
60 import com.google.common.base.Optional;
61 import com.google.common.base.Preconditions;
62 import com.google.common.base.Strings;
63 import com.google.common.collect.SetMultimap;
64 import com.google.common.collect.Table;
65
66 public final class ForwardingManager {
67
68     private static final Logger LOG = LoggerFactory.getLogger(ForwardingManager.class);
69     @VisibleForTesting
70     private byte WAIT_FOR_BD_PROCESSING = 60; // seconds
71     private long lastVxlanVni = 1L;
72     private final Map<String, VxlanVni> vxlanVniByBridgeDomain = new HashMap<>();
73     private final InterfaceManager ifaceManager;
74     private final BridgeDomainManager bdManager;
75     private final DataBroker dataBroker;
76     public ForwardingManager(@Nonnull InterfaceManager ifaceManager, @Nonnull BridgeDomainManager bdManager, @Nonnull DataBroker dataBroker) {
77         this.ifaceManager = Preconditions.checkNotNull(ifaceManager);
78         this.bdManager = Preconditions.checkNotNull(bdManager);
79         this.dataBroker = Preconditions.checkNotNull(dataBroker);
80     }
81
82     public Optional<GbpBridgeDomain> readGbpBridgeDomainConfig(String name) {
83         InstanceIdentifier<GbpBridgeDomain> bdIid = InstanceIdentifier.builder(Config.class)
84             .child(GbpBridgeDomain.class, new GbpBridgeDomainKey(name))
85             .build();
86         ReadOnlyTransaction rTx = dataBroker.newReadOnlyTransaction();
87         return DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, bdIid, rTx);
88     }
89
90     public void createBridgeDomainOnNodes(SetMultimap<String, NodeId> vppNodesByBridgeDomain) {
91         for (String bd : vppNodesByBridgeDomain.keySet()) {
92             Optional<GbpBridgeDomain> bdConfig = readGbpBridgeDomainConfig(bd);
93             Set<NodeId> vppNodes = vppNodesByBridgeDomain.get(bd);
94             if (bdConfig.isPresent()) {
95                 if (bdConfig.get().getType().equals(VlanNetwork.class)) {
96                     createVlanBridgeDomains(bd, bdConfig.get().getVlan(), vppNodes);
97                 }
98             } else {
99                 VxlanVni vxlanVni = vxlanVniByBridgeDomain.get(bd);
100                 if (vxlanVni == null) {
101                     vxlanVni = new VxlanVni(lastVxlanVni++);
102                     vxlanVniByBridgeDomain.put(bd, vxlanVni);
103                 }
104                 createVxlanBridgeDomains(bd, vxlanVni, vppNodes);
105             }
106         }
107     }
108
109     private void createVxlanBridgeDomains(final String bd, final VxlanVni vni, final Set<NodeId> vppNodes) {
110         for (NodeId vppNode : vppNodes) {
111             try {
112                 LOG.debug("Creating VXLAN bridge-domain {} on node {} with VNI {}", bd, vppNode.getValue(),
113                         vni);
114                 bdManager.createVxlanBridgeDomainOnVppNode(bd, vni, vppNode).get(WAIT_FOR_BD_PROCESSING,
115                         TimeUnit.SECONDS);
116             } catch (InterruptedException | ExecutionException e) {
117                 LOG.warn("VXLAN Bridge domain {} was not created on node {}", bd, vppNode.getValue(), e);
118             } catch (TimeoutException e) {
119                 LOG.warn("Probably, VXLAN Bridge domain {} was not created on node {} because BridgeDomainManager "
120                         + "did not respond by {} seconds. Check VBD log for more details",
121                         bd, vppNode.getValue(), WAIT_FOR_BD_PROCESSING, e);
122             }
123         }
124     }
125
126     private void createVlanBridgeDomains(final String bd, final VlanId vlanId, final Set<NodeId> vppNodes) {
127         for (NodeId vppNode : vppNodes) {
128             try {
129                 LOG.debug("Creating VLAN bridge-domain {} on node {} with VLAN ID {}", bd, vppNode.getValue(),
130                         vlanId.getValue());
131                 bdManager.createVlanBridgeDomainOnVppNode(bd, vlanId, vppNode).get(WAIT_FOR_BD_PROCESSING,
132                         TimeUnit.SECONDS);
133             } catch (InterruptedException | ExecutionException e) {
134                 LOG.warn("VLAN Bridge domain {} was not created on node {}", bd, vppNode.getValue(), e);
135             } catch (TimeoutException e) {
136                 LOG.warn("Probably, VLAN Bridge domain {} was not created on node {} because BridgeDomainManager "
137                         + "did not respond by {} seconds. Check VBD log for more details",
138                         bd, vppNode.getValue(), WAIT_FOR_BD_PROCESSING, e);
139             }
140         }
141     }
142
143     public void removeBridgeDomainOnNodes(final SetMultimap<String, NodeId> vppNodesByBridgeDomain) {
144         for (String bd : vppNodesByBridgeDomain.keySet()) {
145             Set<NodeId> vppNodes = vppNodesByBridgeDomain.get(bd);
146             for (NodeId vppNode : vppNodes) {
147                 try {
148                     bdManager.removeBridgeDomainFromVppNode(bd, vppNode).get(WAIT_FOR_BD_PROCESSING,
149                             TimeUnit.SECONDS);
150                 } catch (InterruptedException | ExecutionException e) {
151                     LOG.warn("Bridge domain {} was not removed from node {}", bd, vppNode.getValue(), e);
152                 } catch (TimeoutException e) {
153                     LOG.warn("Probably, bridge domain {} was not removed from node {} because BridgeDomainManager "
154                             + "did not respond by {} seconds. Check VBD log for more details",
155                             bd, vppNode.getValue(), WAIT_FOR_BD_PROCESSING, e);
156                 }
157             }
158         }
159     }
160
161     public void createForwardingForEndpoint(RendererEndpointKey rEpKey, PolicyContext policyCtx) {
162         AddressEndpointWithLocation rEp = policyCtx.getAddrEpByKey().get(KeyFactory.addressEndpointKey(rEpKey));
163         ExternalLocationCase rEpLoc = resolveAndValidateLocation(rEp);
164         if (Strings.isNullOrEmpty(rEpLoc.getExternalNodeConnector())) {
165             // TODO add it to the status for renderer manager
166             LOG.info("Renderer endpoint does not have external-node-connector therefore it is ignored {}", rEp);
167             return;
168         }
169
170         if (Strings.isNullOrEmpty(rEpLoc.getExternalNode())) {
171             java.util.Optional<String> optL2FloodDomain = resolveL2FloodDomain(rEp, policyCtx);
172             if (!optL2FloodDomain.isPresent()) {
173                 // TODO add it to the status for renderer manager
174                 LOG.info("Renderer endpoint does not have l2FloodDomain as network containment {}", rEp);
175                 return;
176             }
177             String l2FloodDomain = optL2FloodDomain.get();
178             try {
179                 ifaceManager.addBridgeDomainToInterface(l2FloodDomain, rEp, isBviForEndpoint(rEp)).get();
180                 LOG.debug("Interface added to bridge-domain {} for endpoint {}", l2FloodDomain, rEp);
181             } catch (InterruptedException | ExecutionException e) {
182                 // TODO add it to the status for renderer manager
183                 LOG.warn("Interface was not added to bridge-domain {} for endpoint {}", l2FloodDomain, rEp, e);
184             }
185         } else {
186             LOG.warn("Forwarding is not created - Location of renderer endpoint contains "
187                     + "external-node therefore VPP renderer assumes that interface for endpoint is "
188                     + "already assigned in bridge-domain representing external-node. {}", rEp);
189         }
190     }
191
192     private boolean isBviForEndpoint(AddressEndpointWithLocation rEp) {
193         VppEndpointKey vppEndpointKey =
194             new VppEndpointKey(rEp.getAddress(), rEp.getAddressType(), rEp.getContextId(), rEp.getContextType());
195         ReadOnlyTransaction rTx = dataBroker.newReadOnlyTransaction();
196         Optional<VppEndpoint> vppEndpointOptional =
197             DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION,
198                 InstanceIdentifier.builder(Config.class).child(VppEndpoint.class, vppEndpointKey).build(), rTx);
199         if (vppEndpointOptional.isPresent()) {
200             InterfaceTypeChoice interfaceTypeChoice = vppEndpointOptional.get().getInterfaceTypeChoice();
201             if (interfaceTypeChoice instanceof LoopbackCase) {
202                 LOG.trace("Vpp renderer endpoint {} IS a BVI interface.", rEp.getKey());
203                 return ((LoopbackCase) interfaceTypeChoice).isBvi();
204             }
205         }
206         rTx.close();
207         LOG.trace("Vpp renderer endpoint {} IS NOT a BVI interface.", rEp.getKey());
208         return false;
209     }
210
211     public void removeForwardingForEndpoint(RendererEndpointKey rEpKey, PolicyContext policyCtx) {
212         AddressEndpointWithLocation rEp = policyCtx.getAddrEpByKey().get(KeyFactory.addressEndpointKey(rEpKey));
213         ExternalLocationCase rEpLoc = resolveAndValidateLocation(rEp);
214         if (Strings.isNullOrEmpty(rEpLoc.getExternalNodeConnector())) {
215             // nothing was created for endpoint therefore nothing is removed
216             return;
217         }
218         if (!Strings.isNullOrEmpty(rEpLoc.getExternalNode())) {
219             try {
220                 ifaceManager.deleteBridgeDomainFromInterface(rEp).get();
221                 LOG.debug("bridge-domain was deleted from interface for endpoint {}", rEp);
222             } catch (InterruptedException | ExecutionException e) {
223                 // TODO add it to the status for renderer manager
224                 LOG.warn("bridge-domain was not deleted from interface for endpoint {}", rEp, e);
225             }
226         } else {
227             LOG.warn("Forwarding is not removed - Location of renderer endpoint does not contain "
228                     + "external-node therefore VPP renderer assumes that interface for endpoint is not "
229                     + "assigned to bridge-domain representing external-node. {}", rEp);
230         }
231     }
232
233     public static ExternalLocationCase resolveAndValidateLocation(AddressEndpointWithLocation addrEpWithLoc) {
234         LocationType locationType = addrEpWithLoc.getAbsoluteLocation().getLocationType();
235         if (!(locationType instanceof ExternalLocationCase)) {
236             throw new IllegalStateException("Endpoint does not have external location " + addrEpWithLoc);
237         }
238         ExternalLocationCase result = (ExternalLocationCase) locationType;
239         if (result.getExternalNodeMountPoint() == null) {
240             throw new IllegalStateException("Endpoint does not have external-node-mount-point " + addrEpWithLoc);
241         }
242         return result;
243     }
244
245     public static java.util.Optional<String> resolveL2FloodDomain(@Nonnull AddressEndpointWithLocation ep,
246             @Nonnull PolicyContext policyCtx) {
247         NetworkContainment netCont = ep.getNetworkContainment();
248         if (netCont == null) {
249             return java.util.Optional.empty();
250         }
251         Containment containment = netCont.getContainment();
252         if (containment instanceof ForwardingContextContainment) {
253             ForwardingContextContainment fwCtxCont = (ForwardingContextContainment) containment;
254             if (L2FloodDomain.class.equals(fwCtxCont.getContextType())) {
255                 return fwCtxCont.getContextId() == null ? java.util.Optional.empty() : java.util.Optional
256                     .of(fwCtxCont.getContextId().getValue());
257             }
258         }
259         if (containment instanceof NetworkDomainContainment) {
260             NetworkDomainContainment netDomainCont = (NetworkDomainContainment) containment;
261             RendererNetworkDomain rendererNetworkDomain =
262                     policyCtx.getNetworkDomainTable().get(ep.getTenant(), new RendererNetworkDomainKey(
263                             netDomainCont.getNetworkDomainId(), netDomainCont.getNetworkDomainType()));
264             java.util.Optional<String> optL2Fd = getForwardingCtxForParent(ep.getTenant(),
265                     rendererNetworkDomain.getParent(), policyCtx.getForwardingCtxTable())
266                         .filter(fwdCtx -> L2FloodDomain.class.equals(fwdCtx.getContextType()))
267                         .map(RendererForwardingContext::getContextId)
268                         .map(ContextId::getValue);
269             if (!optL2Fd.isPresent()) {
270                 LOG.warn("network-domain-containment in endpoint does not have L2-flood-domain as parent. "
271                         + "This case is not supported in VPP renderer. {}", ep);
272             }
273             return optL2Fd;
274         }
275         return java.util.Optional.empty();
276     }
277
278     private static @Nonnull java.util.Optional<RendererForwardingContext> getForwardingCtxForParent(
279             @Nullable TenantId tenant, @Nullable Parent parent,
280             Table<TenantId, RendererForwardingContextKey, RendererForwardingContext> forwardingCtxTable) {
281         if (tenant == null || parent == null) {
282             return java.util.Optional.empty();
283         }
284         if (parent.getContextId() != null && parent.getContextType() != null) {
285             return java.util.Optional.ofNullable(forwardingCtxTable.get(tenant,
286                     new RendererForwardingContextKey(parent.getContextId(), parent.getContextType())));
287         }
288         return java.util.Optional.empty();
289     }
290
291     @VisibleForTesting
292     void setTimer(byte time) {
293         WAIT_FOR_BD_PROCESSING = time;
294     }
295 }