2 * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
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
9 package org.opendaylight.groupbasedpolicy.renderer.vpp.policy;
11 import java.util.HashMap;
14 import java.util.concurrent.ExecutionException;
15 import java.util.concurrent.TimeUnit;
16 import java.util.concurrent.TimeoutException;
18 import javax.annotation.Nonnull;
19 import javax.annotation.Nullable;
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._interface.attributes.InterfaceTypeChoice;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425._interface.attributes._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;
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;
66 public final class ForwardingManager {
68 private static final Logger LOG = LoggerFactory.getLogger(ForwardingManager.class);
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);
82 public Optional<GbpBridgeDomain> readGbpBridgeDomainConfig(String name) {
83 InstanceIdentifier<GbpBridgeDomain> bdIid = InstanceIdentifier.builder(Config.class)
84 .child(GbpBridgeDomain.class, new GbpBridgeDomainKey(name))
86 ReadOnlyTransaction rTx = dataBroker.newReadOnlyTransaction();
87 return DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, bdIid, rTx);
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);
99 VxlanVni vxlanVni = vxlanVniByBridgeDomain.get(bd);
100 if (vxlanVni == null) {
101 vxlanVni = new VxlanVni(lastVxlanVni++);
102 vxlanVniByBridgeDomain.put(bd, vxlanVni);
104 createVxlanBridgeDomains(bd, vxlanVni, vppNodes);
109 private void createVxlanBridgeDomains(final String bd, final VxlanVni vni, final Set<NodeId> vppNodes) {
110 for (NodeId vppNode : vppNodes) {
112 LOG.debug("Creating VXLAN bridge-domain {} on node {} with VNI {}", bd, vppNode.getValue(),
114 bdManager.createVxlanBridgeDomainOnVppNode(bd, vni, vppNode).get(WAIT_FOR_BD_PROCESSING,
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);
126 private void createVlanBridgeDomains(final String bd, final VlanId vlanId, final Set<NodeId> vppNodes) {
127 for (NodeId vppNode : vppNodes) {
129 LOG.debug("Creating VLAN bridge-domain {} on node {} with VLAN ID {}", bd, vppNode.getValue(),
131 bdManager.createVlanBridgeDomainOnVppNode(bd, vlanId, vppNode).get(WAIT_FOR_BD_PROCESSING,
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);
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) {
148 bdManager.removeBridgeDomainFromVppNode(bd, vppNode).get(WAIT_FOR_BD_PROCESSING,
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);
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);
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);
177 String l2FloodDomain = optL2FloodDomain.get();
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);
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);
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();
207 LOG.trace("Vpp renderer endpoint {} IS NOT a BVI interface.", rEp.getKey());
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
218 if (!Strings.isNullOrEmpty(rEpLoc.getExternalNode())) {
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);
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);
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);
238 ExternalLocationCase result = (ExternalLocationCase) locationType;
239 if (result.getExternalNodeMountPoint() == null) {
240 throw new IllegalStateException("Endpoint does not have external-node-mount-point " + addrEpWithLoc);
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();
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());
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);
275 return java.util.Optional.empty();
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();
284 if (parent.getContextId() != null && parent.getContextType() != null) {
285 return java.util.Optional.ofNullable(forwardingCtxTable.get(tenant,
286 new RendererForwardingContextKey(parent.getContextId(), parent.getContextType())));
288 return java.util.Optional.empty();
292 void setTimer(byte time) {
293 WAIT_FOR_BD_PROCESSING = time;