7c3270686968bfac2acefe6b25c5d5a3c1347226
[netvirt.git] /
1 /*
2  * Copyright (c) 2015 - 2016 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
9 package org.opendaylight.netvirt.cloudservicechain;
10
11 import com.google.common.base.Optional;
12 import java.math.BigInteger;
13 import java.util.ArrayList;
14 import java.util.Collection;
15 import java.util.List;
16
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.datastoreutils.DataStoreJobCoordinator;
20 import org.opendaylight.genius.mdsalutil.MDSALUtil;
21 import org.opendaylight.genius.mdsalutil.NWUtil;
22 import org.opendaylight.genius.mdsalutil.NwConstants;
23 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
24 import org.opendaylight.netvirt.cloudservicechain.jobs.AddVpnPseudoPortDataJob;
25 import org.opendaylight.netvirt.cloudservicechain.jobs.RemoveVpnPseudoPortDataJob;
26 import org.opendaylight.netvirt.cloudservicechain.utils.VpnPseudoPortCache;
27 import org.opendaylight.netvirt.cloudservicechain.utils.VpnServiceChainUtils;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.FibRpcService;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.VpnInstanceOpData;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntryKey;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList;
37 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41
42 public class VPNServiceChainHandler implements AutoCloseable {
43
44     private static final Logger LOG = LoggerFactory.getLogger(VPNServiceChainHandler.class);
45
46     private final IMdsalApiManager mdsalManager;
47     private final DataBroker broker;
48     private final FibRpcService fibRpcService;
49
50     public VPNServiceChainHandler(final DataBroker db, IMdsalApiManager mdsalManager, FibRpcService fibRpcSrv) {
51         this.broker = db;
52         this.mdsalManager = mdsalManager;
53         this.fibRpcService = fibRpcSrv;
54     }
55
56     public void init() {
57         VpnPseudoPortCache.createVpnPseudoPortCache(broker);
58     }
59
60     @Override
61     public void close() throws Exception {
62         VpnPseudoPortCache.destroyVpnPseudoPortCache();
63     }
64
65     /**
66      * Getting the VpnInstance from RouteDistinguisher.
67      *
68      * @param rd the Route-Distinguisher
69      * @return an Object that holds the Operational info of the VPN
70      */
71     protected VpnInstanceOpDataEntry getVpnInstance(String rd) {
72         InstanceIdentifier<VpnInstanceOpDataEntry> id = InstanceIdentifier.create(VpnInstanceOpData.class)
73                 .child(VpnInstanceOpDataEntry.class, new VpnInstanceOpDataEntryKey(rd));
74         Optional<VpnInstanceOpDataEntry> vpnInstanceOpData = MDSALUtil.read(broker, LogicalDatastoreType.OPERATIONAL,
75                                                                             id);
76         if (vpnInstanceOpData.isPresent()) {
77             return vpnInstanceOpData.get();
78         }
79         return null;
80     }
81
82     /**
83      * Programs the necessary flows in LFIB and LPortDispatcher table so that
84      * the packets coming from a given VPN are delivered to a given
85      * ServiceChain Pipeline.
86      *
87      * @param vpnName Name of the VPN. Typically the UUID
88      * @param tableId Table to which the LPortDispatcher table sends the packet
89      *                 to (Uplink or Downlink Subsc table)
90      * @param scfTag Scf tag to the SCF to which the Vpn is linked to.
91      * @param lportTag VpnPseudo Port lportTag
92      * @param addOrRemove States if the VPN2SCF Pipeline must be installed or
93      *        removed
94      */
95     public void programVpnToScfPipeline(String vpnName, short tableId, long scfTag, int lportTag, int addOrRemove) {
96         // This entries must be created in the DPN where the CGNAT is installed. Since it is not possible
97         // to know where CGNAT is located, this entries are installed in all the VPN footprint.
98
99         //   LFIB:
100         //     - Match: cgnatLabel   Instr: lportTag=vpnPseudoPortTag + SI=SCF  +  GOTO 17
101         //   LportDisp:
102         //     - Match: vpnPseudoPortTag + SI==SCF   Instr:  scfTag  +  GOTO 70
103         LOG.info("programVpnToScfPipeline ({}) : Parameters VpnName:{} tableId:{} scftag:{}  lportTag:{}",
104                  addOrRemove == NwConstants.ADD_FLOW ? "Creation" : "Removal", vpnName, tableId, scfTag, lportTag);
105         String rd = VpnServiceChainUtils.getVpnRd(broker, vpnName);
106         LOG.debug("Router distinguisher (rd):{}", rd);
107         if (rd == null || rd.isEmpty()) {
108             LOG.warn("programVpnToScfPipeline: Could not find Router-distinguisher for VPN {}. No further actions",
109                      vpnName);
110             return;
111         }
112         VpnInstanceOpDataEntry vpnInstance = getVpnInstance(rd);
113         if ( vpnInstance == null ) {
114             LOG.warn("Could not find a suitable VpnInstance for Route-Distinguisher={}", rd);
115             return;
116         }
117
118         // Find out the set of DPNs for the given VPN ID
119         Collection<VpnToDpnList> vpnToDpnList = vpnInstance.getVpnToDpnList();
120         List<VrfEntry> vrfEntries = VpnServiceChainUtils.getAllVrfEntries(broker, rd);
121         if (vrfEntries != null) {
122             AddVpnPseudoPortDataJob updateVpnToPseudoPortTask =
123                 new AddVpnPseudoPortDataJob(broker, rd, lportTag, tableId, (int) scfTag);
124             DataStoreJobCoordinator.getInstance().enqueueJob(updateVpnToPseudoPortTask.getDsJobCoordinatorKey(),
125                                                              updateVpnToPseudoPortTask);
126
127             for (VpnToDpnList dpnInVpn : vpnToDpnList) {
128                 BigInteger dpnId = dpnInVpn.getDpnId();
129                 programVpnToScfPipelineOnDpn(dpnId, vrfEntries, tableId, (int) scfTag, lportTag, addOrRemove);
130             }
131         }
132     }
133
134     public void programVpnToScfPipelineOnDpn(BigInteger dpnId, List<VrfEntry> vpnVrfEntries, short tableIdToGoTo,
135                                              int scfTag, int lportTag, int addOrRemove) {
136         VpnServiceChainUtils.programLFibEntriesForSCF(mdsalManager, dpnId, vpnVrfEntries, lportTag,
137                                                       addOrRemove);
138
139         VpnServiceChainUtils.programLPortDispatcherFlowForVpnToScf(mdsalManager, dpnId, lportTag, scfTag,
140                                                                    tableIdToGoTo, addOrRemove);
141     }
142
143     /**
144      * Get fake VPNPseudoPort interface name.
145      *
146      * @param dpId Dpn Id
147      * @param scfTag Service Function tag
148      * @param scsTag Service Chain tag
149      * @param lportTag Lport tag
150      * @return the fake VpnPseudoPort interface name
151      */
152     private String buildVpnPseudoPortIfName(Long dpId, long scfTag, int scsTag, int lportTag) {
153         return new StringBuilder("VpnPseudo.").append(dpId).append(NwConstants.FLOWID_SEPARATOR)
154                                               .append(lportTag).append(NwConstants.FLOWID_SEPARATOR)
155                                               .append(scfTag).append(NwConstants.FLOWID_SEPARATOR)
156                                               .append(scsTag).toString();
157     }
158
159     /**
160      * L3VPN Service chaining: It moves traffic from a ServiceChain to a L3VPN.
161      *
162      * @param vpnName Vpn Instance Name. Typicall the UUID
163      * @param scfTag ServiceChainForwarding Tag
164      * @param servChainTag ServiceChain Tag
165      * @param dpnId DpnId in which the egress pseudo logical port belongs
166      * @param vpnPseudoLportTag VpnPseudo Logical port tag
167      * @param isLastServiceChain Flag stating if there is no other ServiceChain
168      *        using this VpnPseudoPort
169      * @param addOrRemove States if pipeline must be installed or removed
170      */
171     public void programScfToVpnPipeline(String vpnName, long scfTag, int servChainTag, long dpnId,
172                                         int vpnPseudoLportTag, boolean isLastServiceChain, int addOrRemove) {
173         // These Flows must be installed in the DPN where the last SF in the ServiceChain is located
174         //   + ScForwardingTable (75):  (This one is created and maintained by ScHopManager)
175         //       - Match:  scfTag + servChainId + lportTagOfvVSF    Instr: VpnPseudoPortTag + SI=L3VPN + GOTO LPortDisp
176         // And these 2 flows must be installed in all Dpns where the Vpn is present:
177         //   + LPortDisp (17):
178         //       - Match:  VpnPseudoPortTag + SI==L3VPN    Instr: setVpnTag + GOTO FIB
179         //   + FIB (21): (one entry per VrfEntry, and it is maintained by FibManager)
180         //       - Match:  vrfTag==vpnTag + eth_type=IPv4  + dst_ip   Instr:  Output DC-GW
181         //
182         LOG.info("L3VPN: Service Chaining programScfToVpnPipeline [Started]: Parameters Vpn Name: {} ", vpnName);
183         String rd = VpnServiceChainUtils.getVpnRd(broker, vpnName);
184
185         if (rd == null || rd.isEmpty()) {
186             LOG.warn("programScfToVpnPipeline: Could not find Router-distinguisher for VPN {}. No further actions",
187                      vpnName);
188             return;
189         }
190
191         VpnInstanceOpDataEntry vpnInstance = getVpnInstance(rd);
192         LOG.debug("programScfToVpnPipeline: rd={}, lportTag={} ", rd, vpnPseudoLportTag);
193         // Find out the set of DPNs for the given VPN ID
194         if (vpnInstance != null) {
195
196             if ( addOrRemove == NwConstants.ADD_FLOW
197                    || (addOrRemove == NwConstants.DEL_FLOW && isLastServiceChain) ) {
198
199                 Long vpnId = vpnInstance.getVpnId();
200                 List<VpnToDpnList> vpnToDpnList = vpnInstance.getVpnToDpnList();
201                 if ( vpnToDpnList != null ) {
202                     List<BigInteger> dpns = new ArrayList<>();
203                     for (VpnToDpnList dpnInVpn : vpnToDpnList ) {
204                         dpns.add(dpnInVpn.getDpnId());
205                     }
206                     if ( !dpns.contains(dpnId) ) {
207                         LOG.debug("Dpn {} is not included in the current VPN Footprint", dpnId);
208                         dpns.add(BigInteger.valueOf(dpnId));
209                     }
210                     for ( BigInteger dpn : dpns ) {
211                         VpnServiceChainUtils.programLPortDispatcherFlowForScfToVpn(mdsalManager, vpnId, dpn,
212                                 vpnPseudoLportTag, addOrRemove);
213                     }
214                 } else {
215                     LOG.debug("Could not find VpnToDpn list for VPN {} with rd {}", vpnName, rd);
216                 }
217             }
218
219             // We need to keep a fake VpnInterface in the DPN where the last vSF (before the VpnPseudoPort) is
220             // located, because in case the last real VpnInterface is removed from that DPN, we still need
221             // the Fib table programmed there
222             String intfName = buildVpnPseudoPortIfName(dpnId, scfTag, servChainTag, vpnPseudoLportTag);
223             if (addOrRemove == NwConstants.ADD_FLOW ) {
224                 // Including this DPN in the VPN footprint even if there is no VpnInterface here.
225                 // This is needed so that FibManager maintains the FIB table in this DPN
226                 VpnServiceChainUtils.updateMappingDbs(broker, fibRpcService, vpnInstance.getVpnId(),
227                                                       BigInteger.valueOf(dpnId), intfName, vpnName);
228             } else {
229                 VpnServiceChainUtils.removeFromMappingDbs(broker, fibRpcService, vpnInstance.getVpnId(),
230                                                           BigInteger.valueOf(dpnId), intfName, vpnName);
231             }
232         }
233         LOG.info("L3VPN: Service Chaining programScfToVpnPipeline [End]");
234     }
235
236     /**
237      * Removes all Flows in LFIB and LPortDispatcher that are related to this VpnPseudoLPort.
238      *
239      * @param vpnInstanceName vpn Instance name
240      * @param vpnPseudoLportTag vpnPseudoLPort tag
241      */
242     public void removeVpnPseudoPortFlows(String vpnInstanceName, int vpnPseudoLportTag) {
243         // At VpnPseudoPort removal time the current Vpn footprint could not be enough, so let's try to
244         // remove all possible entries in all DPNs.
245         // TODO: Study how this could be enhanced. It could be done at ServiceChain removal, but that
246         // could imply check all ServiceChains ending in all DPNs in Vpn footprint to decide that if the entries
247         // can be removed, and that sounds even costlier than this.
248
249         String rd = VpnServiceChainUtils.getVpnRd(broker, vpnInstanceName);
250         List<VrfEntry> vrfEntries = null;
251         if ( rd != null ) {
252             vrfEntries = VpnServiceChainUtils.getAllVrfEntries(broker, rd);
253         }
254         boolean cleanLFib = vrfEntries != null && !vrfEntries.isEmpty();
255
256         List<BigInteger> operativeDPNs = NWUtil.getOperativeDPNs(broker);
257         for (BigInteger dpnId : operativeDPNs) {
258             if ( cleanLFib ) {
259                 VpnServiceChainUtils.programLFibEntriesForSCF(mdsalManager, dpnId, vrfEntries, vpnPseudoLportTag,
260                                                               NwConstants.DEL_FLOW);
261             }
262
263             String vpnToScfflowRef = VpnServiceChainUtils.getL3VpnToScfLportDispatcherFlowRef(vpnPseudoLportTag);
264             Flow vpnToScfFlow = new FlowBuilder().setTableId(NwConstants.LPORT_DISPATCHER_TABLE)
265                                                  .setId(new FlowId(vpnToScfflowRef)).build();
266             mdsalManager.removeFlow(dpnId, vpnToScfFlow);
267             String scfToVpnFlowRef = VpnServiceChainUtils.getScfToL3VpnLportDispatcherFlowRef(vpnPseudoLportTag);
268             Flow scfToVpnFlow = new FlowBuilder().setTableId(NwConstants.LPORT_DISPATCHER_TABLE)
269                                                  .setId(new FlowId(scfToVpnFlowRef)).build();
270             mdsalManager.removeFlow(dpnId, scfToVpnFlow);
271         }
272
273         if ( rd != null ) {
274             RemoveVpnPseudoPortDataJob removeVpnPseudoPortDataTask = new RemoveVpnPseudoPortDataJob(broker, rd);
275             DataStoreJobCoordinator.getInstance().enqueueJob(removeVpnPseudoPortDataTask.getDsJobCoordinatorKey(),
276                                                              removeVpnPseudoPortDataTask);
277         }
278     }
279
280 }