Fixed bug in update forwarding
[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         } else {
173             LOG.debug("Forwarding is not created - Location of renderer endpoint contains "
174                     + "external-node therefore VPP renderer assumes that interface for endpoint is "
175                     + "already assigned in bridge-domain representing external-node. {}", rEp);
176         }
177     }
178
179     public void removeForwardingForEndpoint(RendererEndpointKey rEpKey, PolicyContext policyCtx) {
180         AddressEndpointWithLocation rEp = policyCtx.getAddrEpByKey().get(KeyFactory.addressEndpointKey(rEpKey));
181         ExternalLocationCase rEpLoc = resolveAndValidateLocation(rEp);
182         if (Strings.isNullOrEmpty(rEpLoc.getExternalNodeConnector())) {
183             // nothing was created for endpoint therefore nothing is removed
184             return;
185         }
186
187         if (!Strings.isNullOrEmpty(rEpLoc.getExternalNode())) {
188             try {
189                 ifaceManager.deleteBridgeDomainFromInterface(rEp).get();
190                 LOG.debug("bridge-domain was deleted from interface for endpoint {}", rEp);
191             } catch (InterruptedException | ExecutionException e) {
192                 // TODO add it to the status for renderer manager
193                 LOG.warn("bridge-domain was not deleted from interface for endpoint {}", rEp, e);
194             }
195         } else {
196             LOG.debug("Forwarding is not removed - Location of renderer endpoint does not contain "
197                     + "external-node therefore VPP renderer assumes that interface for endpoint is not "
198                     + "assigned to bridge-domain representing external-node. {}", rEp);
199         }
200     }
201
202     public static ExternalLocationCase resolveAndValidateLocation(AddressEndpointWithLocation addrEpWithLoc) {
203         LocationType locationType = addrEpWithLoc.getAbsoluteLocation().getLocationType();
204         if (!(locationType instanceof ExternalLocationCase)) {
205             throw new IllegalStateException("Endpoint does not have external location " + addrEpWithLoc);
206         }
207         ExternalLocationCase result = (ExternalLocationCase) locationType;
208         if (result.getExternalNodeMountPoint() == null) {
209             throw new IllegalStateException("Endpoint does not have external-node-mount-point " + addrEpWithLoc);
210         }
211         return result;
212     }
213
214     public static Optional<String> resolveL2FloodDomain(@Nullable NetworkContainment netCont) {
215         if (netCont == null) {
216             return Optional.absent();
217         }
218         Containment containment = netCont.getContainment();
219         if (containment instanceof ForwardingContextContainment) {
220             ForwardingContextContainment fwCtxCont = (ForwardingContextContainment) containment;
221             if (fwCtxCont.getContextType().isAssignableFrom(L2FloodDomain.class)) {
222                 return fwCtxCont.getContextId() == null ? null : Optional.of(fwCtxCont.getContextId().getValue());
223             }
224         }
225         if (containment instanceof NetworkDomainContainment) {
226             // TODO address missing impl
227             LOG.info("Network domain containment in endpoint is not supported yet. {}", netCont);
228             return Optional.absent();
229         }
230         return Optional.absent();
231     }
232
233 }