352310c1f073a28f55b71f134ae5060aa01dde9c
[sfc.git] /
1 /*
2  * Copyright (c) 2014, 2015 Ericsson 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.sfc.ofrenderer.processors;
10
11 import java.util.HashMap;
12 import java.util.Iterator;
13 import java.util.Map;
14 import java.util.Optional;
15 import java.util.Set;
16
17 import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
18 import org.opendaylight.sfc.genius.util.SfcGeniusDataUtils;
19 import org.opendaylight.sfc.genius.util.SfcGeniusRpcClient;
20 import org.opendaylight.sfc.ofrenderer.openflow.SfcOfFlowProgrammerInterface;
21 import org.opendaylight.sfc.ofrenderer.utils.SfcOfBaseProviderUtils;
22 import org.opendaylight.sfc.ofrenderer.utils.SfcSynchronizer;
23 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SfName;
24 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SffName;
25 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.RenderedServicePath;
26 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.rendered.service.path.RenderedServicePathHop;
27 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.service.function.base.SfDataPlaneLocator;
28 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.service.functions.ServiceFunction;
29 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarder.base.SffDataPlaneLocator;
30 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarders.ServiceFunctionForwarder;
31 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarders.service.function.forwarder.service.function.dictionary.SffSfDataPlaneLocator;
32 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sl.rev140701.DataPlaneLocator;
33 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sl.rev140701.Mac;
34 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sl.rev140701.Mpls;
35 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sl.rev140701.Nsh;
36 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sl.rev140701.Transport;
37 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sl.rev140701.VxlanGpe;
38 import org.opendaylight.yang.gen.v1.urn.ericsson.params.xml.ns.yang.sfc.sff.logical.rev160620.DpnIdType;
39 import org.opendaylight.yang.gen.v1.urn.ericsson.params.xml.ns.yang.sfc.sff.logical.rev160620.service.functions.service.function.sf.data.plane.locator.locator.type.LogicalInterface;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44
45 public class SfcOfRspProcessor {
46
47     public static final long SFC_FLOWS = 0xdeadbeef;
48     private static final Logger LOG = LoggerFactory.getLogger(SfcOfRspProcessor.class);
49     private SfcOfFlowProgrammerInterface sfcOfFlowProgrammer;
50     private SfcOfBaseProviderUtils sfcOfProviderUtils;
51     private SfcSynchronizer sfcSynchronizer;
52     private Map<NodeId, Boolean> sffInitialized;
53     private Map<String, Class<? extends SfcRspTransportProcessorBase>> rspTransportProcessors;
54     private static final String TRANSPORT_ENCAP_SEPARATOR_STRING = "//";
55
56     /* Logical SFF always assumes vxlan-gpe tunnels for inter-sff transport, and eth-encapsulated
57     * NSH for sff-sf transport. The initial "LogicalInterface" prefix is used as an
58     * identifier only (that is, it is not a true transport)
59     */
60     private static final String LOGICAL_SFF_TRANSPORT_PROCESSOR_KEY =
61             LogicalInterface.class.getName() + TRANSPORT_ENCAP_SEPARATOR_STRING + Nsh.class.getName();
62
63     public SfcOfRspProcessor(
64             SfcOfFlowProgrammerInterface sfcOfFlowProgrammer,
65             SfcOfBaseProviderUtils sfcOfProviderUtils,
66             SfcSynchronizer sfcSynchronizer,
67             RpcProviderRegistry rpcProviderRegistry) {
68         this.sfcOfFlowProgrammer = sfcOfFlowProgrammer;
69         this.sfcOfProviderUtils = sfcOfProviderUtils;
70         this.sfcSynchronizer = sfcSynchronizer;
71         this.sffInitialized = new HashMap<>();
72
73         //FIXME this is temporary. SfcGeniusRpcClient will self-initialize via blueprint injection when the module is finished
74         SfcGeniusRpcClient.getInstance().initialize(rpcProviderRegistry);
75
76         this.rspTransportProcessors = new HashMap<>();
77         this.rspTransportProcessors.put(
78                 getTransportEncapName(VxlanGpe.class.getName(), Nsh.class.getName()),
79                 SfcRspProcessorNshVxgpe.class);
80         this.rspTransportProcessors.put(
81                 getTransportEncapName(Mpls.class.getName(), Transport.class.getName()),
82                 SfcRspProcessorMpls.class);
83         this.rspTransportProcessors.put(
84                 getTransportEncapName(Mac.class.getName(), Transport.class.getName()),
85                 SfcRspProcessorVlan.class);
86         this.rspTransportProcessors.put(
87                 LOGICAL_SFF_TRANSPORT_PROCESSOR_KEY,
88                 SfcRspProcessorLogicalSff.class);
89     }
90
91     /**
92      * Main entry point for processing an RSP. Orchestrates logic to call
93      * different FlowProgrammer flow creation methods.
94      *
95      * @param rsp - a newly created/updated Rendered Service Path
96      */
97     public void processRenderedServicePath(RenderedServicePath rsp) {
98         // if this method takes too long, consider launching it in a thread
99         try {
100             // This call blocks until the lock is obtained
101             sfcSynchronizer.lock();
102
103             sfcOfProviderUtils.addRsp(rsp.getPathId());
104
105             //
106             // Populate the SFF Connection Graph
107             //
108             SffGraph sffGraph = populateSffGraph(rsp);
109             SfcRspTransportProcessorBase transportProcessor = getTransportProcessor(sffGraph, rsp);
110
111             //
112             // Populate the SFF ingress and egress DPLs from the sffGraph
113             //
114             transportProcessor.processSffDpls();
115
116             //
117             // Internally calculate and set the RSP transport values
118             //
119             transportProcessor.setRspTransports();
120
121             //
122             // Now process the entries in the SFF Graph and populate the flow tables
123             //
124             SffGraph.SffGraphEntry entry = null;
125             Iterator<SffGraph.SffGraphEntry> sffGraphIter = sffGraph.getGraphEntryIterator();
126             sfcOfFlowProgrammer.setTableIndexMapper(
127                     transportProcessor.getTableIndexMapper().isPresent()
128                             ? transportProcessor.getTableIndexMapper().get()
129                             : null);
130             while (sffGraphIter.hasNext()) {
131                 entry = sffGraphIter.next();
132                 LOG.debug("build flows of entry: {}", entry);
133                 // The flows created by initializeSff dont belong to any particular RSP
134                 sfcOfFlowProgrammer.setFlowRspId(SFC_FLOWS);
135                 initializeSff(entry);
136                 sfcOfFlowProgrammer.setFlowRspId(rsp.getPathId());
137                 configureTransportIngressFlows(entry, sffGraph, transportProcessor);
138                 configurePathMapperFlows(entry, sffGraph, transportProcessor);
139                 configureNextHopFlows(entry, sffGraph, transportProcessor);
140                 configureTransportEgressFlows(entry, sffGraph, transportProcessor);
141             }
142
143             // Flush the flows to the data store
144             this.sfcOfFlowProgrammer.flushFlows();
145
146             LOG.info("Processing complete for RSP: name [{}] Id [{}]", rsp.getName(), rsp.getPathId());
147
148         } catch (RuntimeException e) {
149             LOG.error("RuntimeException in processRenderedServicePath: ", e.getMessage(), e);
150         } finally {
151             // If there were any errors, purge any remaining flows so they're not written
152             this.sfcOfFlowProgrammer.purgeFlows();
153             sfcSynchronizer.unlock();
154             sfcOfProviderUtils.removeRsp(rsp.getPathId());
155         }
156     }
157
158     /**
159      * Deletes the OpenFlow flows associated with this Rendered Service Path.
160      *
161      * @param rsp - the Rendered Service Path to delete
162      */
163     public void deleteRenderedServicePath(RenderedServicePath rsp) {
164         Set<NodeId> clearedSffNodeIDs = sfcOfFlowProgrammer.deleteRspFlows(rsp.getPathId());
165         for(NodeId sffNodeId : clearedSffNodeIDs){
166             setSffInitialized(sffNodeId, false);
167         }
168     }
169
170     /**
171      * Given the RSP transport type, return an Rsp Transport Processor that
172      * will call the appropriate FlowProgrammer methods.
173      *
174      * @param sffGraph - used to inject dependencies into the newly created object.
175      * @param rsp - contains the RSP transport type
176      *
177      * @return an RSP Transport Processor for the RSP.
178      */
179     public SfcRspTransportProcessorBase getTransportProcessor(SffGraph sffGraph, RenderedServicePath rsp) {
180         try {
181             String transportProcessorKey = sffGraph.isUsingLogicalSFF() ?
182                     LOGICAL_SFF_TRANSPORT_PROCESSOR_KEY :
183                     getTransportEncapName(
184                         rsp.getTransportType().getName(),
185                         rsp.getSfcEncapsulation().getName());
186
187             Class<? extends SfcRspTransportProcessorBase> transportClass =
188                     rspTransportProcessors.get(transportProcessorKey);
189             LOG.debug("getTransportProcessor :: transport [{}] encap [{} selected transport processor [{}]]",
190                     rsp.getTransportType().getName(), rsp.getSfcEncapsulation(), transportClass);
191             SfcRspTransportProcessorBase transportProcessor = transportClass.newInstance();
192             transportProcessor.setFlowProgrammer(sfcOfFlowProgrammer);
193             transportProcessor.setRsp(rsp);
194             transportProcessor.setSffGraph(sffGraph);
195             transportProcessor.setSfcProviderUtils(sfcOfProviderUtils);
196
197             return transportProcessor;
198         } catch(Exception e) {
199             throw new RuntimeException(
200                     "getTransportProcessor no processor for transport [" +
201                     rsp.getTransportType().getName() +
202                     "] encap [" + rsp.getSfcEncapsulation() + "] " + e);
203         }
204     }
205
206     /**
207      * Given an RSP, create and populate an SffGraph.
208      *
209      * @param rsp - input to create the graph
210      * @return a newly populates SffGraph
211      */
212     private SffGraph populateSffGraph(RenderedServicePath rsp) {
213         SffGraph sffGraph = new SffGraph();
214
215         // Setting to INGRESS for the first graph entry, which is the RSP Ingress
216         SffName prevSffName = new SffName(SffGraph.INGRESS);
217         // Set to null in the first graph entry
218         DpnIdType srcDpnId = null;
219
220         Iterator<RenderedServicePathHop> servicePathHopIter = rsp.getRenderedServicePathHop().iterator();
221         SfName sfName = null;
222         SfName prevSfName = null;
223         String sfgName = null;
224         SffGraph.SffGraphEntry entry = null;
225         short lastServiceIndex = rsp.getStartingIndex();
226
227         while (servicePathHopIter.hasNext()) {
228             RenderedServicePathHop rspHop = servicePathHopIter.next();
229             SffName curSffName = rspHop.getServiceFunctionForwarder();
230             sfName = rspHop.getServiceFunctionName();
231             sfgName = rspHop.getServiceFunctionGroupName();
232             ServiceFunction sf = sfcOfProviderUtils.getServiceFunction(sfName, rsp.getPathId());
233             entry = sffGraph.addGraphEntry(prevSffName, curSffName, sfName, sfgName, rsp.getPathId(),
234                     rspHop.getServiceIndex());
235             entry.setPrevSf(prevSfName);
236             lastServiceIndex = rspHop.getServiceIndex();
237             prevSfName = sfName;
238             prevSffName = curSffName;
239
240
241             if (SfcGeniusDataUtils.isSfUsingALogicalInterface(sf)) {
242                 String logicalInterfaceName = sfcOfProviderUtils.getSfLogicalInterfaceName(sf);
243                 LOG.debug("populateSffGraph: SF uses a logical interface -> storing id for the dataplane node (interface:{})", logicalInterfaceName);
244                 Optional<DpnIdType> dpnid = SfcGeniusRpcClient.getInstance().getDpnIdFromInterfaceNameFromGeniusRPC(logicalInterfaceName);
245                 if (!dpnid.isPresent()) {
246                     throw new RuntimeException("populateSffGraph:failed.dpnid for interface ["
247                             + logicalInterfaceName + "] was not returned by genius. "
248                             + "Rendered service path cannot be generated at this time");
249                 }
250                 LOG.debug("populateSffGraph: retrieved dpn id for SF {} :[{}] ", sf.getName(), dpnid.get());
251                 entry.setDstDpnId(dpnid.get());
252             }
253             entry.setSrcDpnId(srcDpnId);
254             LOG.debug("populateSffGraph:added graph entry: [{}]", entry);
255             srcDpnId = entry.getDstDpnId();
256
257         }
258         // Add the final connection, which will be the RSP Egress
259         // Using the previous sfName as the SrcSf
260         entry = sffGraph.addGraphEntry(prevSffName, SffGraph.EGRESS, sfName, sfgName, rsp.getPathId(),
261                 (short) (lastServiceIndex - 1));
262         entry.setPrevSf(prevSfName);
263         entry.setSrcDpnId(srcDpnId);
264
265         LOG.debug("populateSffGraph: added final graph entry: [{}]", entry);
266         return sffGraph;
267     }
268
269     /**
270      * Call the appropriate flow creation methods on the TransportProcessor for
271      * the TransportIngress table.
272      *
273      * @param entry - data for the current flows to be created
274      * @param sffGraph - contains data for the RSP
275      * @param transportProcessor - specific TransportProcessor to call into
276      */
277     private void configureTransportIngressFlows(SffGraph.SffGraphEntry entry,
278             SffGraph sffGraph, SfcRspTransportProcessorBase transportProcessor) {
279
280         if (entry.getDstSff().equals(SffGraph.EGRESS)) {
281             return;
282         }
283
284         ServiceFunctionForwarder sffDst =
285                 sfcOfProviderUtils.getServiceFunctionForwarder(entry.getDstSff(), entry.getPathId());
286         SffDataPlaneLocator sffDstIngressDpl = sfcOfProviderUtils.getSffDataPlaneLocator(sffDst,
287                 sffGraph.getSffIngressDpl(entry.getDstSff(), entry.getPathId()));
288
289         transportProcessor.configureSffTransportIngressFlow(entry, sffDstIngressDpl);
290
291         // Configure the SF related flows
292         if (entry.getSf() != null) {
293             ServiceFunction sf = sfcOfProviderUtils.getServiceFunction(entry.getSf(), entry.getPathId());
294             SfDataPlaneLocator sfDpl = sfcOfProviderUtils.getSfDataPlaneLocator(sf, entry.getDstSff());
295             transportProcessor.configureSfTransportIngressFlow(entry, sfDpl);
296         }
297     }
298
299     /**
300      * Call the appropriate flow creation methods on the TransportProcessor for
301      * the PathMapper table.
302      *
303      * @param entry - data for the current flows to be created
304      * @param sffGraph - contains data for the RSP
305      * @param transportProcessor - specific TransportProcessor to call into
306      */
307     private void configurePathMapperFlows(SffGraph.SffGraphEntry entry,
308             SffGraph sffGraph, SfcRspTransportProcessorBase transportProcessor) {
309
310         if(entry.getDstSff().equals(SffGraph.EGRESS)) {
311             return;
312         }
313
314         DataPlaneLocator dstHopIngressDpl = sffGraph.getHopIngressDpl(entry.getDstSff(), entry.getPathId());
315
316         // configure SFF-SFF-SF ingress -OR- Ingress-SFF-SF ingress flow using dstHopIngressDpl
317         transportProcessor.configureSffPathMapperFlow(entry, dstHopIngressDpl);
318
319         // configure the SF Ingress Flow
320         if (entry.getSf() != null) {
321             ServiceFunction sf = sfcOfProviderUtils.getServiceFunction(entry.getSf(), entry.getPathId());
322             SfDataPlaneLocator sfDpl = sfcOfProviderUtils.getSfDataPlaneLocator(sf, entry.getDstSff());
323             transportProcessor.configureSfPathMapperFlow(entry, sfDpl);
324         }
325     }
326
327     /**
328      * Call the appropriate flow creation methods on the TransportProcessor for
329      * the NextHop table.
330      *
331      * @param entry - data for the current flows to be created
332      * @param sffGraph - contains data for the RSP
333      * @param transportProcessor - specific TransportProcessor to call into
334      */
335     private void configureNextHopFlows(SffGraph.SffGraphEntry entry,
336             SffGraph sffGraph, SfcRspTransportProcessorBase transportProcessor) {
337
338         ServiceFunction sfDst = sfcOfProviderUtils.getServiceFunction(entry.getSf(), entry.getPathId());
339         SfDataPlaneLocator sfDstDpl = (sfDst == null) ? null : sfcOfProviderUtils.getSfDataPlaneLocator(sfDst, entry.getDstSff());
340         if (sfDstDpl != null) {
341             if (entry.getSrcSff().equals(SffGraph.INGRESS)) {
342                 // Configure the GW-SFF-SF NextHop using sfDpl
343                 transportProcessor.configureNextHopFlow(entry, (SffDataPlaneLocator) null, sfDstDpl);
344
345                 // If its Ingress, nothing else to be done
346                 return;
347             } else {
348                 ServiceFunctionForwarder sffSrc =
349                         sfcOfProviderUtils.getServiceFunctionForwarder(entry.getSrcSff(), entry.getPathId());
350                 SffDataPlaneLocator sffSrcEgressDpl = sfcOfProviderUtils.getSffDataPlaneLocator(sffSrc,
351                         sffGraph.getSffEgressDpl(entry.getSrcSff(), entry.getPathId()));
352                 // Configure the SFF-SFF-SF NextHop using sfDpl
353                 transportProcessor.configureNextHopFlow(entry, sffSrcEgressDpl, sfDstDpl);
354             }
355         }
356
357         if (entry.getDstSff().equals(SffGraph.EGRESS)) {
358             // If dstSff is EGRESS, the SF is actually on the srcSff
359             SfDataPlaneLocator sfSrcDpl = sfcOfProviderUtils.getSfDataPlaneLocator(sfDst, entry.getSrcSff());
360
361             // Configure the SF-SFF-GW NextHop, we dont have the DstDpl, leaving it blank
362             transportProcessor.configureNextHopFlow(entry, sfSrcDpl, (SffDataPlaneLocator) null);
363         }
364
365
366         SfDataPlaneLocator sfSrcDpl = null;
367         if (entry.getPrevSf() != null) {
368             sfSrcDpl = sfcOfProviderUtils.getSfDataPlaneLocator(
369                     sfcOfProviderUtils.getServiceFunction(entry.getPrevSf(), entry.getPathId()), entry.getSrcSff());
370         }
371
372         // Configure the SFF-SFF NextHop using the sfDpl and sffDstIngressDpl
373         if (sfSrcDpl != null) {
374             if ( (entry.getSrcSff().getValue().equals(entry.getDstSff().getValue()))
375                     && !((entry.isIntraLogicalSFFEntry() && (!entry.getSrcDpnId().equals(entry.getDstDpnId()))))) {
376                 // If the next hop is on this SFF then go straight to the next SF
377                 // Also used in logical SFF, but only when both dpnids are the same (two SFs in the same compute node)
378                 // Configure SF-SFF-SF NextHop on the same SFF
379                 transportProcessor.configureNextHopFlow(entry, sfSrcDpl, sfDstDpl);
380             } else {
381                 // Configure the SFF-SFF NextHop using the sfDpl and sffDstIngressDpl
382                 ServiceFunctionForwarder sffDst =
383                         sfcOfProviderUtils.getServiceFunctionForwarder(entry.getDstSff(), entry.getPathId());
384                 SffDataPlaneLocator sffDstIngressDpl = sfcOfProviderUtils.getSffDataPlaneLocator(sffDst,
385                         sffGraph.getSffIngressDpl(entry.getDstSff(), entry.getPathId()));
386                 transportProcessor.configureNextHopFlow(entry, sfSrcDpl, sffDstIngressDpl);
387             }
388         }
389     }
390
391     /**
392      * Call the appropriate flow creation methods on the TransportProcessor for
393      * the TransportEgress table.
394      *
395      * @param entry - data for the current flows to be created
396      * @param sffGraph - contains data for the RSP
397      * @param transportProcessor - specific TransportProcessor to call into
398      */
399     private void configureTransportEgressFlows(SffGraph.SffGraphEntry entry,
400             SffGraph sffGraph, SfcRspTransportProcessorBase transportProcessor) {
401
402         // Configure the SFF-Egress Transport Egress
403         ServiceFunctionForwarder sffSrc =
404                 sfcOfProviderUtils.getServiceFunctionForwarder(entry.getSrcSff(), entry.getPathId());
405         if (entry.getDstSff().equals(SffGraph.EGRESS)) {
406             SffDataPlaneLocator sffSrcEgressDpl = sfcOfProviderUtils.getSffDataPlaneLocator(sffSrc,
407                     sffGraph.getSffEgressDpl(entry.getSrcSff(), entry.getPathId()));
408
409             transportProcessor.configureSffTransportEgressFlow(
410                     entry, sffSrcEgressDpl, null,
411                     sffGraph.getPathEgressDpl(entry.getPathId()));
412
413             return;
414         }
415
416         // Configure the SFF-SF Transport Egress using sfDpl
417         ServiceFunction sfDst = sfcOfProviderUtils.getServiceFunction(entry.getSf(), entry.getPathId());
418         SfDataPlaneLocator sfDstDpl = sfcOfProviderUtils.getSfDataPlaneLocator(sfDst, entry.getDstSff());
419         ServiceFunctionForwarder sffDst =
420                 sfcOfProviderUtils.getServiceFunctionForwarder(entry.getDstSff(), entry.getPathId());
421
422         if (sfDstDpl != null) {
423             SffDataPlaneLocator sffDstDpl = null;
424             SffSfDataPlaneLocator sffSfDpl = sfcOfProviderUtils
425                     .getSffSfDataPlaneLocator(sffDst, entry.getSf());
426             if (sffSfDpl != null) {
427                 sffDstDpl = sfcOfProviderUtils
428                         .getSffDataPlaneLocator(sffDst, sffSfDpl.getSffDplName());
429             }
430             transportProcessor.configureSfTransportEgressFlow(entry, sffDstDpl,
431                     sfDstDpl, sfDstDpl);
432         }
433
434         // Nothing else to be done for Ingress
435         if (entry.getSrcSff().equals(SffGraph.INGRESS)) {
436             return;
437         }
438
439         // Configure the SFF-SFF Transport Egress using the sffDstIngressDpl
440         if ((! entry.getSrcSff().getValue().equals(entry.getDstSff().getValue()))
441                 || ((entry.isIntraLogicalSFFEntry()) && (entry.getSrcDpnId() != entry.getDstDpnId()))) {
442             SffDataPlaneLocator sffDstIngressDpl = sfcOfProviderUtils.getSffDataPlaneLocator(sffDst,
443                     sffGraph.getSffIngressDpl(entry.getDstSff(), entry.getPathId()));
444             SffDataPlaneLocator sffSrcEgressDpl = sfcOfProviderUtils.getSffDataPlaneLocator(sffSrc,
445                     sffGraph.getSffEgressDpl(entry.getSrcSff(), entry.getPathId()));
446             // This is the HOP DPL details between srcSFF and dstSFF, for example: VLAN ID 100
447             DataPlaneLocator dstHopIngressDpl = sffGraph.getHopIngressDpl(entry.getDstSff(), entry.getPathId());
448
449             transportProcessor.configureSffTransportEgressFlow(
450                     entry, sffSrcEgressDpl, sffDstIngressDpl, dstHopIngressDpl);
451         }
452     }
453
454     /* TODO what about SFG??
455      * Currently there is nobody available to maintain the Service
456      * Function Groups (SFGs) code, nor are there any tests available
457
458     private void configureSffEgressForGroup(SffGraph.SffGraphEntry entry, SffGraph sffGraph) {
459     }
460
461     private void configureGroupNextHopFlow(final SffName sffName, SffDataPlaneLocator srcSffDpl, long groupId,
462             String groupName, final long pathId, final short serviceIndex) {
463     }
464
465     */
466
467
468     /**
469      * Initialize the SFF by creating the match any flows, if not already created.
470      *
471      * @param entry - contains the SFF and RSP id
472      */
473     private void initializeSff(SffGraph.SffGraphEntry entry) {
474         if (entry.getDstSff().equals(SffGraph.EGRESS)) {
475             return;
476         }
477
478         String sffNodeName = sfcOfProviderUtils.getSffOpenFlowNodeName(entry.getDstSff(), entry.getPathId(), entry.getDstDpnId());
479         if (sffNodeName == null) {
480             throw new RuntimeException("initializeSff SFF [" + entry.getDstSff().getValue() + "] does not exist");
481         }
482
483         NodeId sffNodeId = new NodeId(sffNodeName);
484         if (!getSffInitialized(sffNodeId)) {
485             LOG.debug("Initializing SFF [{}] node [{}]", entry.getDstSff().getValue(), sffNodeName);
486             this.sfcOfFlowProgrammer.configureClassifierTableMatchAny(sffNodeName);
487             this.sfcOfFlowProgrammer.configureTransportIngressTableMatchAny(sffNodeName);
488             this.sfcOfFlowProgrammer.configurePathMapperTableMatchAny(sffNodeName);
489             this.sfcOfFlowProgrammer.configurePathMapperAclTableMatchAny(sffNodeName);
490             this.sfcOfFlowProgrammer.configureNextHopTableMatchAny(sffNodeName);
491             this.sfcOfFlowProgrammer.configureTransportEgressTableMatchAny(sffNodeName);
492
493             setSffInitialized(sffNodeId, true);
494         }
495     }
496
497
498     //
499     // Internal util methods
500     //
501
502
503     /**
504      * Given an SFF name, determine if its been initialized yet or not.
505      * Called by initializeSff()
506      *
507      * @param   sffNodeId   The SFF node ID to check
508      * @return  true if its been initialized, false otherwise
509      */
510     private boolean getSffInitialized(final NodeId sffNodeId) {
511         Boolean isInitialized = sffInitialized.get(sffNodeId);
512
513         if (isInitialized == null) {
514             return false;
515         }
516
517         return isInitialized;
518     }
519
520     /**
521      * Set a given SFF as initialized.
522      * Called by initializeSff()
523      *
524      * @param sffNodeId - the SFF to set
525      * @param initialized - boolean value to set the SFF as
526      */
527     private void setSffInitialized(final NodeId sffNodeId, boolean initialized) {
528         // If the value is already in the map, its value will be replaced
529         sffInitialized.put(new NodeId(sffNodeId), initialized);
530     }
531
532     private String getTransportEncapName(final String transportName, final String encapName) {
533         StringBuffer sb =
534                 new StringBuffer(transportName).
535                 append(TRANSPORT_ENCAP_SEPARATOR_STRING).
536                 append(encapName);
537         LOG.info("getTransportEncapName :: transport [{}] encap [{}] result [{}]", transportName, encapName, sb.toString());
538         return sb.toString();
539     }
540 }