0c9635da7a54fa03881d66710c8aa5ea88da982f
[netvirt.git] / vpnmanager / impl / src / main / java / org / opendaylight / netvirt / vpnmanager / intervpnlink / InterVpnLinkUtil.java
1 /*
2  * Copyright (c) 2016, 2017 Ericsson India Global Services Pvt Ltd. 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 package org.opendaylight.netvirt.vpnmanager.intervpnlink;
9
10 import com.google.common.base.Optional;
11 import com.google.common.util.concurrent.ListenableFuture;
12 import java.math.BigInteger;
13 import java.util.ArrayList;
14 import java.util.Collections;
15 import java.util.List;
16 import java.util.stream.Collectors;
17 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
18 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
19 import org.opendaylight.genius.mdsalutil.MDSALUtil;
20 import org.opendaylight.genius.mdsalutil.MatchInfo;
21 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
22 import org.opendaylight.genius.mdsalutil.NwConstants;
23 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
24 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
25 import org.opendaylight.genius.utils.ServiceIndex;
26 import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
27 import org.opendaylight.netvirt.fibmanager.api.IFibManager;
28 import org.opendaylight.netvirt.fibmanager.api.RouteOrigin;
29 import org.opendaylight.netvirt.vpnmanager.VpnConstants;
30 import org.opendaylight.netvirt.vpnmanager.VpnFootprintService;
31 import org.opendaylight.netvirt.vpnmanager.VpnUtil;
32 import org.opendaylight.netvirt.vpnmanager.api.InterfaceUtils;
33 import org.opendaylight.netvirt.vpnmanager.api.intervpnlink.InterVpnLinkCache;
34 import org.opendaylight.netvirt.vpnmanager.api.intervpnlink.InterVpnLinkDataComposite;
35 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.InterVpnLinkStates;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.InterVpnLinks;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.inter.vpn.link.states.InterVpnLinkState;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.inter.vpn.link.states.InterVpnLinkStateBuilder;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.inter.vpn.link.states.InterVpnLinkStateKey;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.inter.vpn.link.states.inter.vpn.link.state.FirstEndpointState;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.inter.vpn.link.states.inter.vpn.link.state.FirstEndpointStateBuilder;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.inter.vpn.link.states.inter.vpn.link.state.SecondEndpointState;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.inter.vpn.link.states.inter.vpn.link.state.SecondEndpointStateBuilder;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.inter.vpn.links.InterVpnLink;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.inter.vpn.links.InterVpnLinkKey;
50 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
53
54 /**
55  * This class contains methods to be used as utilities related with inter-vpn-link.
56  */
57 public final class InterVpnLinkUtil {
58
59     private static final Logger LOG = LoggerFactory.getLogger(InterVpnLinkUtil.class);
60
61     private InterVpnLinkUtil() { }
62
63     /**
64      * Retrieves the Instance Identifier that points to an InterVpnLink object
65      * in MD-SAL.
66      *
67      * @param interVpnLinkName The name of the InterVpnLink
68      * @return The requested InstanceIdentifier
69      */
70     public static InstanceIdentifier<InterVpnLink> getInterVpnLinkPath(String interVpnLinkName) {
71         return InstanceIdentifier.builder(InterVpnLinks.class)
72             .child(InterVpnLink.class, new InterVpnLinkKey(interVpnLinkName))
73             .build();
74     }
75
76     /**
77      * Retrieves the Instance Identifier that points to an InterVpnLinkState object
78      * in MD-SAL.
79      *
80      * @param vpnLinkName The name of the InterVpnLink
81      * @return The requested InstanceIdentifier
82      */
83     public static InstanceIdentifier<InterVpnLinkState> getInterVpnLinkStateIid(String vpnLinkName) {
84         return InstanceIdentifier.builder(InterVpnLinkStates.class)
85             .child(InterVpnLinkState.class, new InterVpnLinkStateKey(vpnLinkName))
86             .build();
87     }
88
89     public static String buildInterVpnLinkIfaceName(String vpnName, BigInteger dpnId) {
90         return String.format("InterVpnLink.%s.%s", vpnName, dpnId.toString());
91     }
92
93     /**
94      * Updates VpnToDpn map by adding a fake VpnInterface related to an
95      * InterVpnLink in the corresponding DPNs. If the fake iface is the
96      * first one on the any of the specified DPNs, the installation of
97      * Fib flows on that DPN will be triggered.
98      *
99      * @param vpnFootprintService VpnFootprintService service reference
100      * @param vpnName Name of the VPN to which the fake interfaces belong
101      * @param dpnList List of DPNs where the fake InterVpnLink interface must be added
102      */
103     public static void updateVpnFootprint(VpnFootprintService vpnFootprintService, String vpnName,
104         String primaryRd, List<BigInteger> dpnList) {
105         LOG.debug("updateVpnFootprint (add):  vpn={}  dpnList={}", vpnName, dpnList);
106         // Note: when a set of DPNs is calculated for Vpn1, these DPNs are added to the VpnToDpn map of Vpn2. Why?
107         // because we do the handover from Vpn1 to Vpn2 in those DPNs, so in those DPNs we must know how to reach
108         // to Vpn2 targets. If new Vpn2 targets are added later, the Fib will be maintained in these DPNs even if
109         // Vpn2 is not physically present there.
110         for (BigInteger dpnId : dpnList) {
111             String ifaceName = buildInterVpnLinkIfaceName(vpnName, dpnId);
112             vpnFootprintService.updateVpnToDpnMapping(dpnId, vpnName, primaryRd, ifaceName,
113                     null/*ipAddressSourceValuePair*/, true /* addition */);
114         }
115     }
116
117     /**
118      * Updates VpnToDpn map by removing the fake VpnInterface related to an
119      * InterVpnLink in the corresponding DPNs.
120      *
121      * @param vpnFootprintService VpnFootprintService service reference
122      * @param vpnName Name of the VPN to which the fake interfaces belong
123      * @param dpnId DPN where the fake InterVpnLink interface must be removed from
124      */
125     public static void removeIVpnLinkIfaceFromVpnFootprint(VpnFootprintService vpnFootprintService,
126         String vpnName, String rd, BigInteger dpnId) {
127         String ifaceName = buildInterVpnLinkIfaceName(vpnName, dpnId);
128         LOG.debug("updateVpnFootprint (remove):  vpn={}  dpn={}  ifaceName={}", vpnName, dpnId, ifaceName);
129         vpnFootprintService.updateVpnToDpnMapping(dpnId, vpnName, rd, ifaceName,
130                 null/*ipAddressSourceValuePair*/, false /* removal */);
131     }
132
133
134     public static FirstEndpointState buildFirstEndpointState(FirstEndpointState original,
135                                                              Optional<List<BigInteger>> new1stEndpointDpns,
136                                                              Optional<Long> new1stEndpointLportTag) {
137         FirstEndpointStateBuilder builder = new FirstEndpointStateBuilder(original);
138         if (new1stEndpointDpns.isPresent()) {
139             builder.setDpId(new1stEndpointDpns.get());
140         }
141         if (new1stEndpointLportTag.isPresent()) {
142             builder.setLportTag(new1stEndpointLportTag.get());
143         }
144         return builder.build();
145     }
146
147     public static FirstEndpointState buildFirstEndpointState(String vpnName, List<BigInteger> dpnList, long lportTag) {
148         return new FirstEndpointStateBuilder().setVpnUuid(new Uuid(vpnName)).setDpId(dpnList).setLportTag(lportTag)
149                                               .build();
150     }
151
152     public static SecondEndpointState buildSecondEndpointState(SecondEndpointState original,
153                                                                Optional<List<BigInteger>> new2ndEndpointDpns,
154                                                                Optional<Long> new2ndEndpointLportTag) {
155         SecondEndpointStateBuilder builder = new SecondEndpointStateBuilder(original);
156         if (new2ndEndpointDpns.isPresent()) {
157             builder.setDpId(new2ndEndpointDpns.get());
158         }
159         if (new2ndEndpointLportTag.isPresent()) {
160             builder.setLportTag(new2ndEndpointLportTag.get());
161         }
162         return builder.build();
163     }
164
165     public static SecondEndpointState buildSecondEndpointState(String vpnName, List<BigInteger> dpnList,
166                                                                long lportTag) {
167         return new SecondEndpointStateBuilder().setVpnUuid(new Uuid(vpnName)).setDpId(dpnList).setLportTag(lportTag)
168                                               .build();
169     }
170
171     /**
172      * Creates an InterVpnLinkState out of an existing one and modifying only the desired attributes.
173      *
174      * @param original InterVpnLinkState to start from.
175      * @param new1stEndpointState Sets this FirstEndpointState if present
176      * @param new2ndEndpointState  Sets this SecondEndpointState if present
177      * @param errDescription  Sets this ErrorDescription if present
178      * @return the newly build InterVpnLinkState
179      */
180     public static InterVpnLinkState buildIvlStateFromOriginal(InterVpnLinkState original,
181                                                              Optional<FirstEndpointState> new1stEndpointState,
182                                                              Optional<SecondEndpointState> new2ndEndpointState,
183                                                              Optional<String> errDescription) {
184         InterVpnLinkStateBuilder ivlStateBuilder = new InterVpnLinkStateBuilder(original);
185         if (new1stEndpointState.isPresent()) {
186             ivlStateBuilder.setFirstEndpointState(new1stEndpointState.get());
187         }
188         if (new2ndEndpointState.isPresent()) {
189             ivlStateBuilder.setSecondEndpointState(new2ndEndpointState.get());
190         }
191         if (errDescription.isPresent()) {
192             ivlStateBuilder.setErrorDescription(errDescription.get());
193         }
194         return ivlStateBuilder.build();
195     }
196
197     /**
198      * Updates inter-VPN link state.
199      *
200      * @param broker dataBroker service reference
201      * @param vpnLinkName The name of the InterVpnLink
202      * @param state Sets the state of the InterVpnLink to Active or Error
203      * @param newFirstEndpointState Updates the lportTag and/or DPNs of the 1st endpoint of the InterVpnLink
204      * @param newSecondEndpointState Updates the lportTag and/or DPNs of the 2nd endpoint of the InterVpnLink
205      * @param interVpnLinkCache the InterVpnLinkCache
206      */
207     public static void updateInterVpnLinkState(DataBroker broker, String vpnLinkName, InterVpnLinkState.State state,
208             FirstEndpointState newFirstEndpointState, SecondEndpointState newSecondEndpointState,
209             InterVpnLinkCache interVpnLinkCache) {
210         Optional<InterVpnLinkState> optOldVpnLinkState = getInterVpnLinkState(broker, vpnLinkName);
211         if (optOldVpnLinkState.isPresent()) {
212             InterVpnLinkState newVpnLinkState =
213                 new InterVpnLinkStateBuilder(optOldVpnLinkState.get()).setState(state)
214                             .setFirstEndpointState(newFirstEndpointState)
215                             .setSecondEndpointState(newSecondEndpointState)
216                             .build();
217             VpnUtil.syncUpdate(broker, LogicalDatastoreType.CONFIGURATION,
218                 InterVpnLinkUtil.getInterVpnLinkStateIid(vpnLinkName), newVpnLinkState);
219             interVpnLinkCache.addInterVpnLinkStateToCaches(newVpnLinkState);
220         } else {
221             InterVpnLinkState newIVpnLinkState =
222                 new InterVpnLinkStateBuilder().setKey(new InterVpnLinkStateKey(vpnLinkName))
223                     .setInterVpnLinkName(vpnLinkName)
224                     .setFirstEndpointState(newFirstEndpointState)
225                     .setSecondEndpointState(newSecondEndpointState)
226                     .setState(InterVpnLinkState.State.Active)
227                     .build();
228             VpnUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION,
229                 InterVpnLinkUtil.getInterVpnLinkStateIid(vpnLinkName), newIVpnLinkState);
230             interVpnLinkCache.addInterVpnLinkStateToCaches(newIVpnLinkState);
231         }
232     }
233
234     /**
235      * Installs a Flow in LPortDispatcher table that matches on SI=2 and
236      * the lportTag of one InterVpnLink's endpoint and sets the vrfTag of the
237      * other endpoint and sends to FIB table.
238      *
239      * @param broker dataBroker service reference
240      * @param mdsalManager MDSAL API accessor
241      * @param interVpnLinkName Name of the InterVpnLink.
242      * @param dpnList The list of DPNs where this flow must be installed
243      * @param vpnUuidOtherEndpoint UUID of the other endpoint of the InterVpnLink
244      * @param lportTagOfOtherEndpoint Dataplane identifier of the other endpoint of the InterVpnLink
245      * @return the list of Futures for each and every flow that has been installed
246      */
247     public static List<ListenableFuture<Void>> installLPortDispatcherTableFlow(DataBroker broker,
248                                                                                IMdsalApiManager mdsalManager,
249                                                                                String interVpnLinkName,
250                                                                                List<BigInteger> dpnList,
251                                                                                String vpnUuidOtherEndpoint,
252                                                                                Long lportTagOfOtherEndpoint) {
253         List<ListenableFuture<Void>> result = new ArrayList<>();
254         long vpnId = VpnUtil.getVpnId(broker, vpnUuidOtherEndpoint);
255         for (BigInteger dpnId : dpnList) {
256             // insert into LPortDispatcher table
257             Flow lportDispatcherFlow = buildLPortDispatcherFlow(interVpnLinkName, vpnId,
258                                                                 lportTagOfOtherEndpoint.intValue());
259             result.add(mdsalManager.installFlow(dpnId, lportDispatcherFlow));
260         }
261
262         return result;
263     }
264
265     /**
266      * Builds a Flow to be installed into LPortDispatcher table, that matches on
267      * SI=2 + vpnLinkEndpointPseudoPortTag and sends to FIB.
268      *
269      * @param interVpnLinkName The name of the InterVpnLink
270      * @param vpnId Dataplane identifier of the VPN, the Vrf Tag.
271      * @param lportTag DataPlane identifier of the LogicalPort.
272      * @return the Flow ready to be installed
273      */
274     public static Flow buildLPortDispatcherFlow(String interVpnLinkName, long vpnId, int lportTag) {
275         LOG.info("Inter-vpn-link : buildLPortDispatcherFlow. vpnId {}   lportTag {} ", vpnId, lportTag);
276         List<MatchInfo> matches = Collections.singletonList(new MatchMetadata(
277                         MetaDataUtil.getMetaDataForLPortDispatcher(lportTag,
278                                 ServiceIndex.getIndex(NwConstants.L3VPN_SERVICE_NAME, NwConstants.L3VPN_SERVICE_INDEX)),
279                         MetaDataUtil.getMetaDataMaskForLPortDispatcher()));
280         String flowRef = getLportDispatcherFlowRef(interVpnLinkName, lportTag);
281
282         return MDSALUtil.buildFlowNew(NwConstants.LPORT_DISPATCHER_TABLE, flowRef,
283                                       VpnConstants.DEFAULT_LPORT_DISPATCHER_FLOW_PRIORITY, flowRef,
284                                       0, 0, VpnUtil.getCookieL3((int) vpnId), matches,
285                                       buildLportDispatcherTableInstructions(vpnId));
286     }
287
288     /**
289      * Builds a flowRef to be assigned to the flow to be installed into
290      * LPortDispatcher table.
291      *
292      * @param interVpnLinkName The name of the InterVpnLink
293      * @param lportTag Dataplane identifier of the LogicalPort
294      * @return the flow reference string
295      */
296     public static String getLportDispatcherFlowRef(String interVpnLinkName, Integer lportTag) {
297         return VpnConstants.FLOWID_PREFIX + "INTERVPNLINK" + NwConstants.FLOWID_SEPARATOR + interVpnLinkName
298              + NwConstants.FLOWID_SEPARATOR + lportTag
299              + NwConstants.FLOWID_SEPARATOR + ServiceIndex.getIndex(NwConstants.L3VPN_SERVICE_NAME,
300                                                                     NwConstants.L3VPN_SERVICE_INDEX)
301              + NwConstants.FLOWID_SEPARATOR + VpnConstants.DEFAULT_LPORT_DISPATCHER_FLOW_PRIORITY;
302     }
303
304
305     public static List<Instruction> buildLportDispatcherTableInstructions(long vpnId) {
306         int instructionKey = 0;
307         List<Instruction> instructions = new ArrayList<>();
308         instructions.add(MDSALUtil.buildAndGetWriteMetadaInstruction(MetaDataUtil.getVpnIdMetadata(vpnId),
309             MetaDataUtil.METADATA_MASK_VRFID,
310             ++instructionKey));
311         instructions.add(MDSALUtil.buildAndGetGotoTableInstruction(NwConstants.L3_FIB_TABLE, ++instructionKey));
312
313         return instructions;
314     }
315
316     /**
317      * Retrieves the States of all InterVpnLinks.
318      *
319      * @param broker dataBroker service reference
320      * @return the list of objects that holds the InterVpnLink state information
321      */
322     public static List<InterVpnLinkState> getAllInterVpnLinkState(DataBroker broker) {
323         InstanceIdentifier<InterVpnLinkStates> interVpnLinkStateIid =
324             InstanceIdentifier.builder(InterVpnLinkStates.class).build();
325
326         Optional<InterVpnLinkStates> interVpnLinkStateOpData =
327             MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, interVpnLinkStateIid);
328
329         return interVpnLinkStateOpData.isPresent() ? interVpnLinkStateOpData.get().getInterVpnLinkState()
330             : new ArrayList<>();
331     }
332
333     /**
334      * Retrieves the State of an InterVpnLink.
335      *
336      * @param broker dataBroker service reference
337      * @param interVpnLinkName The name of the InterVpnLink
338      * @return the object that contains the State of the specified InterVpnLink or Optional.absent() if it doesnt exist
339      */
340     public static Optional<InterVpnLinkState> getInterVpnLinkState(DataBroker broker, String interVpnLinkName) {
341         return MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, getInterVpnLinkStateIid(interVpnLinkName));
342     }
343
344     /**
345      * Retrieves all configured InterVpnLinks.
346      *
347      * @param broker dataBroker service reference
348      * @return the list of InterVpnLinks
349      */
350     public static List<InterVpnLink> getAllInterVpnLinks(DataBroker broker) {
351         InstanceIdentifier<InterVpnLinks> interVpnLinksIid = InstanceIdentifier.builder(InterVpnLinks.class).build();
352
353         Optional<InterVpnLinks> interVpnLinksOpData =
354             MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, interVpnLinksIid);
355
356         return interVpnLinksOpData.isPresent() ? interVpnLinksOpData.get().getInterVpnLink()
357             : new ArrayList<>();
358     }
359
360     public static void handleStaticRoute(InterVpnLinkDataComposite interVpnLink, String vpnName,
361         String destination, String nexthop, int label,
362         DataBroker dataBroker, IFibManager fibManager, IBgpManager bgpManager) throws Exception {
363
364         LOG.debug("handleStaticRoute [vpnLink={} srcVpn={} destination={} nextHop={} label={}]",
365             interVpnLink.getInterVpnLinkName(), vpnName, destination, nexthop, label);
366
367         String vpnRd = VpnUtil.getVpnRd(dataBroker, vpnName);
368         if (vpnRd == null) {
369             LOG.warn("Could not find Route-Distinguisher for VpnName {}", vpnName);
370             return;
371         }
372         LOG.debug("Writing FibEntry to DS:  vpnRd={}, prefix={}, label={}, nexthop={} (interVpnLink)",
373             vpnRd, destination, label, nexthop);
374         fibManager.addOrUpdateFibEntry(vpnRd, null /*macAddress*/, destination,
375                 Collections.singletonList(nexthop), VrfEntry.EncapType.Mplsgre, label,
376                 0 /*l3vni*/, null /*gatewayMacAddress*/, null /*parentVpnRd*/, RouteOrigin.STATIC, null /*writeTxn*/);
377
378         // Now advertise to BGP. The nexthop that must be advertised to BGP are the IPs of the DPN where the
379         // VPN's endpoint have been instantiated
380         // List<String> nexthopList = new ArrayList<>(); // The nexthops to be advertised to BGP
381         List<BigInteger> endpointDpns = interVpnLink.getEndpointDpnsByVpnName(vpnName);
382         List<String> nexthopList =
383             endpointDpns.stream().map(dpnId -> InterfaceUtils.getEndpointIpAddressForDPN(dataBroker, dpnId))
384                         .collect(Collectors.toList());
385         LOG.debug("advertising IVpnLink route to BGP:  vpnRd={}, prefix={}, label={}, nexthops={}",
386             vpnRd, destination, label, nexthopList);
387         bgpManager.advertisePrefix(vpnRd, null /*macAddress*/, destination, nexthopList,
388                 VrfEntry.EncapType.Mplsgre, label, 0 /*l3vni*/, 0 /*l2vni*/,
389                 null /*gatewayMacAddress*/);
390     }
391 }