Implementing VNI generation for VBD
[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.forwarding.l2_l3.rev160427.L2FloodDomain;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.endpoints.AddressEndpointWithLocation;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.endpoints.RendererEndpointKey;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.Config;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.VlanNetwork;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.BridgeDomain;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.BridgeDomainKey;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.VlanId;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VxlanVni;
43 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
44 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47
48 import com.google.common.annotations.VisibleForTesting;
49 import com.google.common.base.Optional;
50 import com.google.common.base.Preconditions;
51 import com.google.common.base.Strings;
52 import com.google.common.collect.SetMultimap;
53
54 public final class ForwardingManager {
55
56     private static final Logger LOG = LoggerFactory.getLogger(ForwardingManager.class);
57     @VisibleForTesting
58     static long WAIT_FOR_BD_CREATION = 10; // seconds
59     private long lastVxlanVni = 1L;
60     private final Map<String, VxlanVni> vxlanVniByBridgeDomain = new HashMap<>();
61     private final InterfaceManager ifaceManager;
62     private final BridgeDomainManager bdManager;
63     private final DataBroker dataBroker;
64     public ForwardingManager(@Nonnull InterfaceManager ifaceManager, @Nonnull BridgeDomainManager bdManager, @Nonnull DataBroker dataBroker) {
65         this.ifaceManager = Preconditions.checkNotNull(ifaceManager);
66         this.bdManager = Preconditions.checkNotNull(bdManager);
67         this.dataBroker = Preconditions.checkNotNull(dataBroker);
68     }
69
70     public Optional<BridgeDomain> readBridgeDomainConfig(String name) {
71         InstanceIdentifier<BridgeDomain> bdIid = InstanceIdentifier.builder(Config.class)
72             .child(BridgeDomain.class, new BridgeDomainKey(name))
73             .build();
74         ReadOnlyTransaction rTx = dataBroker.newReadOnlyTransaction();
75         return DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, bdIid, rTx);
76     }
77
78     public void createBridgeDomainOnNodes(SetMultimap<String, NodeId> vppNodesByBridgeDomain) {
79         for (String bd : vppNodesByBridgeDomain.keySet()) {
80             Optional<BridgeDomain> bdConfig = readBridgeDomainConfig(bd);
81             Set<NodeId> vppNodes = vppNodesByBridgeDomain.get(bd);
82             if (bdConfig.isPresent()) {
83                 if (bdConfig.get().getType().equals(VlanNetwork.class)) {
84                     createVlanBridgeDomains(bd, bdConfig.get().getVlan(), vppNodes);
85                 }
86             } else {
87                 VxlanVni vxlanVni = vxlanVniByBridgeDomain.get(bd);
88                 if (vxlanVni == null) {
89                     vxlanVni = new VxlanVni(lastVxlanVni++);
90                     vxlanVniByBridgeDomain.put(bd, vxlanVni);
91                 }
92                 createVxlanBridgeDomains(bd, vxlanVni, vppNodes);
93             }
94         }
95     }
96
97     private void createVxlanBridgeDomains(String bd, VxlanVni vni, Set<NodeId> vppNodes) {
98         for (NodeId vppNode : vppNodes) {
99             try {
100                 LOG.trace("Creating VXLAN bridge-domain {} on node {} with VNI {}", bd, vppNode.getValue(),
101                         vni);
102                 // TODO think about propagating ListenableFuture - timeout set as workaround
103                 bdManager.createVxlanBridgeDomainOnVppNode(bd, vni, vppNode).get(WAIT_FOR_BD_CREATION,
104                         TimeUnit.SECONDS);
105             } catch (InterruptedException | ExecutionException e) {
106                 LOG.warn("VXLAN Bridge domain {} was not created on node {}", bd, vppNode.getValue(), e);
107             } catch (TimeoutException e) {
108                 LOG.warn("Probably, VXLAN Bridge domain {} was not created on node {} because BridgeDomainManager "
109                         + "did not respond by {} seconds.", bd, vppNode.getValue(), WAIT_FOR_BD_CREATION, e);
110             }
111         }
112     }
113
114     private void createVlanBridgeDomains(String bd, VlanId vlanId, Set<NodeId> vppNodes) {
115         for (NodeId vppNode : vppNodes) {
116             try {
117                 LOG.trace("Creating VLAN bridge-domain {} on node {} with VLAN ID {}", bd, vppNode.getValue(),
118                         vlanId.getValue());
119                 // TODO think about propagating ListenableFuture - timeout set as workaround
120                 bdManager.createVlanBridgeDomainOnVppNode(bd, vlanId, vppNode).get(WAIT_FOR_BD_CREATION,
121                         TimeUnit.SECONDS);
122             } catch (InterruptedException | ExecutionException e) {
123                 LOG.warn("VLAN Bridge domain {} was not created on node {}", bd, vppNode.getValue(), e);
124             } catch (TimeoutException e) {
125                 LOG.warn("Probably, VLAN Bridge domain {} was not created on node {} because BridgeDomainManager "
126                         + "did not respond by {} seconds.", bd, vppNode.getValue(), WAIT_FOR_BD_CREATION, e);
127             }
128         }
129     }
130
131     public void removeBridgeDomainOnNodes(SetMultimap<String, NodeId> vppNodesByBridgeDomain) {
132         for (String bd : vppNodesByBridgeDomain.keySet()) {
133             Set<NodeId> vppNodes = vppNodesByBridgeDomain.get(bd);
134             for (NodeId vppNode : vppNodes) {
135                 try {
136                     bdManager.removeBridgeDomainFromVppNode(bd, vppNode).get(WAIT_FOR_BD_CREATION,
137                             TimeUnit.SECONDS);
138                 } catch (InterruptedException | ExecutionException e) {
139                     LOG.warn("Bridge domain {} was not removed from node {}", bd, vppNode.getValue(), e);
140                 } catch (TimeoutException e) {
141                     LOG.warn("Probably, bridge domain {} was not removed from node {} because BridgeDomainManager "
142                             + "did not respond by {} seconds.", bd, vppNode.getValue(), WAIT_FOR_BD_CREATION, e);
143                 }
144             }
145         }
146     }
147
148     public void createForwardingForEndpoint(RendererEndpointKey rEpKey, PolicyContext policyCtx) {
149         AddressEndpointWithLocation rEp = policyCtx.getAddrEpByKey().get(KeyFactory.addressEndpointKey(rEpKey));
150         ExternalLocationCase rEpLoc = resolveAndValidateLocation(rEp);
151         if (Strings.isNullOrEmpty(rEpLoc.getExternalNodeConnector())) {
152             // TODO add it to the status for renderer manager
153             LOG.info("Rednerer endpoint does not have external-node-connector therefore it is ignored {}", rEp);
154             return;
155         }
156
157         if (Strings.isNullOrEmpty(rEpLoc.getExternalNode())) {
158             Optional<String> optL2FloodDomain = resolveL2FloodDomain(rEp.getNetworkContainment());
159             if (!optL2FloodDomain.isPresent()) {
160                 // TODO add it to the status for renderer manager
161                 LOG.info("Rednerer endpoint does not have l2FloodDomain as network containment {}", rEp);
162                 return;
163             }
164             String l2FloodDomain = optL2FloodDomain.get();
165             try {
166                 ifaceManager.addBridgeDomainToInterface(l2FloodDomain, rEp).get();
167                 LOG.debug("Interface added to bridge-domain {} for endpoint {}", l2FloodDomain, rEp);
168             } catch (InterruptedException | ExecutionException e) {
169                 // TODO add it to the status for renderer manager
170                 LOG.warn("Interface was not added to bridge-domain {} for endpoint {}", l2FloodDomain, rEp, e);
171             }
172         }
173     }
174
175     public void removeForwardingForEndpoint(RendererEndpointKey rEpKey, PolicyContext policyCtx) {
176         AddressEndpointWithLocation rEp = policyCtx.getAddrEpByKey().get(KeyFactory.addressEndpointKey(rEpKey));
177         ExternalLocationCase rEpLoc = resolveAndValidateLocation(rEp);
178         if (Strings.isNullOrEmpty(rEpLoc.getExternalNodeConnector())) {
179             // nothing was created for endpoint therefore nothing is removed
180             return;
181         }
182
183         if (!Strings.isNullOrEmpty(rEpLoc.getExternalNode())) {
184             try {
185                 ifaceManager.deleteBridgeDomainFromInterface(rEp).get();
186                 LOG.debug("bridge-domain was deleted from interface for endpoint {}", rEp);
187             } catch (InterruptedException | ExecutionException e) {
188                 // TODO add it to the status for renderer manager
189                 LOG.warn("bridge-domain was not deleted from interface for endpoint {}", rEp, e);
190             }
191         }
192     }
193
194     public static ExternalLocationCase resolveAndValidateLocation(AddressEndpointWithLocation addrEpWithLoc) {
195         LocationType locationType = addrEpWithLoc.getAbsoluteLocation().getLocationType();
196         if (!(locationType instanceof ExternalLocationCase)) {
197             throw new IllegalStateException("Endpoint does not have external location " + addrEpWithLoc);
198         }
199         ExternalLocationCase result = (ExternalLocationCase) locationType;
200         if (result.getExternalNodeMountPoint() == null) {
201             throw new IllegalStateException("Endpoint does not have external-node-mount-point " + addrEpWithLoc);
202         }
203         return result;
204     }
205
206     public static Optional<String> resolveL2FloodDomain(@Nullable NetworkContainment netCont) {
207         if (netCont == null) {
208             return Optional.absent();
209         }
210         Containment containment = netCont.getContainment();
211         if (containment instanceof ForwardingContextContainment) {
212             ForwardingContextContainment fwCtxCont = (ForwardingContextContainment) containment;
213             if (fwCtxCont.getContextType().isAssignableFrom(L2FloodDomain.class)) {
214                 return fwCtxCont.getContextId() == null ? null : Optional.of(fwCtxCont.getContextId().getValue());
215             }
216         }
217         if (containment instanceof NetworkDomainContainment) {
218             // TODO address missing impl
219             LOG.info("Network domain containment in endpoint is not supported yet. {}", netCont);
220             return Optional.absent();
221         }
222         return Optional.absent();
223     }
224
225 }