5903bc7b57c0a9378b679bf9036d5bfcaeab8160
[netvirt.git] / openstack / net-virt-sfc / impl / src / main / java / org / opendaylight / netvirt / openstack / netvirt / sfc / standalone / openflow13 / NetvirtSfcStandaloneOF13Provider.java
1 /*
2  * Copyright (c) 2015 Dell, 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.netvirt.openstack.netvirt.sfc.standalone.openflow13;
10
11 import com.google.common.base.Preconditions;
12
13 import com.google.common.collect.Iterables;
14 import java.util.List;
15 import java.util.StringTokenizer;
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.netvirt.openstack.netvirt.api.NodeCacheManager;
20 import org.opendaylight.netvirt.openstack.netvirt.sfc.NshUtils;
21 import org.opendaylight.netvirt.openstack.netvirt.api.Southbound;
22 import org.opendaylight.netvirt.openstack.netvirt.sfc.INetvirtSfcOF13Provider;
23 import org.opendaylight.netvirt.openstack.netvirt.sfc.ISfcClassifierService;
24 import org.opendaylight.netvirt.utils.mdsal.utils.MdsalUtils;
25 import org.opendaylight.netvirt.utils.servicehelper.ServiceHelper;
26 import org.opendaylight.sfc.provider.api.SfcProviderRenderedPathAPI;
27 import org.opendaylight.sfc.provider.api.SfcProviderServicePathAPI;
28 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.RspName;
29 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.CreateRenderedPathInput;
30 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.CreateRenderedPathInputBuilder;
31 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.RenderedServicePaths;
32 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.path.first.hop.info.RenderedServicePathFirstHop;
33 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.RenderedServicePath;
34 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.RenderedServicePathKey;
35 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.rendered.service.path.RenderedServicePathHop;
36 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.ServiceFunctionPaths;
37 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.service.function.paths.ServiceFunctionPath;
38 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.Acl;
39 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.Ace;
40 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.Matches;
41 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIp;
42 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.ace.ip.version.AceIpv4;
43 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.acl.rev150105.RedirectToSfc;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.Classifiers;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.classifiers.Classifier;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.classifiers.classifier.Bridges;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.classifiers.classifier.bridges.Bridge;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.classifiers.classifier.sffs.Sff;
50 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
51 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
52 import org.osgi.framework.ServiceReference;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
55
56 /**
57  * Open vSwitch OpenFlow 1.3 Networking Provider for Netvirt SFC
58  * @author Arun Yerra
59  */
60 public class NetvirtSfcStandaloneOF13Provider implements INetvirtSfcOF13Provider {
61     private static final Logger LOG = LoggerFactory.getLogger(NetvirtSfcStandaloneOF13Provider.class);
62     private static final short TABLE_0_CLASSIFIER = 0;
63     private static final short TABLE_3_INGR_ACL = 50;
64
65     private volatile NodeCacheManager nodeCacheManager;
66     private volatile Southbound southbound;
67     private MdsalUtils mdsalUtils;
68     private SfcClassifier sfcClassifier;
69
70     // TBD:: Remove these constants after integrating with openstack.
71     private static final String TUNNEL_DST = "192.168.50.75";
72     private static final String TUNNEL_VNID = "10";
73     private static final String CLIENT_PORT_NAME = "vethl-h35_2";
74     private static final String SERVER_PORT_NAME = "vethl-h35_4";
75     private static final String CLIENT_GPE_PORT_NAME = "sw1-vxlangpe-0";
76     private static final String SERVER_GPE_PORT_NAME = "sw6-vxlangpe-0";
77     private static final String INTERFACE_TYPE_VXLAN_GPE = "vxlangpe";
78
79     /**
80      * {@link NetvirtSfcStandaloneOF13Provider} constructor.
81      * @param dataBroker MdSal {@link DataBroker}
82      */
83     public NetvirtSfcStandaloneOF13Provider(final DataBroker dataBroker) {
84         Preconditions.checkNotNull(dataBroker, "Input dataBroker cannot be NULL!");
85         mdsalUtils = new MdsalUtils(dataBroker);
86         //this.setDependencies(null);
87         sfcClassifier = new SfcClassifier(dataBroker, southbound, mdsalUtils);
88     }
89
90     public void removeClassifierRules(Sff sff, Acl acl) {
91         // TODO Auto-generated method stub
92     }
93
94     @Override
95     public void addClassifierRules(Acl acl) {
96         String aclName = acl.getAclName();
97         Classifiers classifiers = mdsalUtils.read(LogicalDatastoreType.CONFIGURATION, getClassifierIid());
98         if (classifiers == null) {
99             LOG.debug("add: No Classifiers found");
100             return;
101         }
102
103         LOG.debug("add: Classifiers: {}", classifiers);
104         for (Classifier classifier : classifiers.getClassifier()) {
105             if (classifier.getAcl().equals(aclName) && classifier.getBridges() != null) {
106                 addClassifierRules(classifier.getBridges(), acl);
107             }
108         }
109     }
110
111     @Override
112     public void removeClassifierRules(Acl acl) {
113         String aclName = acl.getAclName();
114         Classifiers classifiers = mdsalUtils.read(LogicalDatastoreType.CONFIGURATION, getClassifierIid());
115         if (classifiers != null) {
116             for (Classifier classifier : classifiers.getClassifier()) {
117                 if (classifier.getAcl().equalsIgnoreCase(aclName) && classifier.getSffs() != null) {
118                     for (Sff sff : classifier.getSffs().getSff()) {
119                         removeClassifierRules(sff, acl);
120                     }
121                 }
122             }
123         }
124     }
125
126     @Override
127     public void setSfcClassifierService(ISfcClassifierService sfcClassifierService) {
128
129     }
130
131     @Override
132     public void addClassifierRules(Bridge bridge, Acl acl) {
133
134     }
135
136     @Override
137     public void addClassifierRules(Bridges bridges, Acl acl) {
138         Preconditions.checkNotNull(bridges, "Input bridges cannot be NULL!");
139         Preconditions.checkNotNull(acl, "Input accesslist cannot be NULL!");
140
141         for (Ace ace : acl.getAccessListEntries().getAce()) {
142             processAclEntry(ace, bridges, true);
143         }
144     }
145
146     private void processAclEntry(Ace entry, Bridges bridges, boolean write) {
147         Matches matches = entry.getMatches();
148         if (matches == null) {
149             LOG.warn("processAclEntry: matches not found");
150             return;
151         }
152
153         RenderedServicePath rsp = getRenderedServicePath(entry);
154         if (rsp == null) {
155             LOG.warn("Failed to get renderedServicePatch for entry: {}", entry);
156             return;
157         }
158
159         LOG.info("processAclEntry: RSP: {}", rsp);
160         List<RenderedServicePathHop> pathHopList = rsp.getRenderedServicePathHop();
161         if (pathHopList.isEmpty()) {
162             LOG.warn("Service Path = {} has empty hops!!", rsp.getName());
163             return;
164         }
165
166         for (Bridge bridge : bridges.getBridge()) {
167             if (bridge.getDirection().getIntValue() == 0) {
168                 Node bridgeNode = getBridgeNode(bridge.getName());
169                 if (bridgeNode == null) {
170                     LOG.debug("processAclEntry: bridge {} not yet configured. Skip processing !!", bridge.getName());
171                     continue;
172                 }
173
174                 long tunnelOfPort = southbound.getOFPort(bridgeNode, CLIENT_GPE_PORT_NAME);
175                 if (tunnelOfPort == 0L) {
176                     LOG.error("programAclEntry: Could not identify tunnel port {} -> OF ({}) on {}",
177                             CLIENT_GPE_PORT_NAME, tunnelOfPort, bridgeNode);
178                     return;
179                 }
180
181                 long localOfPort = southbound.getOFPort(bridgeNode, CLIENT_PORT_NAME);
182                 if (localOfPort == 0L) {
183                     LOG.error("programAclEntry: Could not identify local port {} -> OF ({}) on {}",
184                         CLIENT_PORT_NAME, localOfPort, bridgeNode);
185                     return;
186                 }
187
188                 // Find the first Hop within an RSP.
189                 // The classifier flow needs to send all matched traffic to this first hop SFF.
190                 RenderedServicePathFirstHop firstRspHop = SfcProviderRenderedPathAPI
191                         .readRenderedServicePathFirstHop(new RspName(rsp.getName()));
192
193                 LOG.debug("First Hop IPAddress = {}, Port = {}", firstRspHop.getIp().getIpv4Address().getValue(),
194                         firstRspHop.getPort().getValue());
195
196                 NshUtils nshHeader = new NshUtils();
197                 // C1 is the normal overlay dest ip and c2 is the vnid
198                 // Hardcoded for now, netvirt integration will have those values
199                 nshHeader.setNshMetaC1(NshUtils.convertIpAddressToLong(new Ipv4Address(TUNNEL_DST)));
200                 nshHeader.setNshMetaC2(Long.parseLong(TUNNEL_VNID));
201                 nshHeader.setNshNsp(rsp.getPathId());
202
203                 RenderedServicePathHop firstHop = pathHopList.get(0);
204                 nshHeader.setNshNsi(firstHop.getServiceIndex());
205                 nshHeader.setNshTunIpDst(firstRspHop.getIp().getIpv4Address());
206                 nshHeader.setNshTunUdpPort(firstRspHop.getPort());
207                 LOG.debug("The Nsh Header = {}", nshHeader);
208
209                 handleLocalInPort(southbound.getDataPathId(bridgeNode), rsp.getPathId().toString(), localOfPort,
210                         TABLE_0_CLASSIFIER, TABLE_3_INGR_ACL, matches, true);
211
212                 handleSfcClassiferFlows(southbound.getDataPathId(bridgeNode), TABLE_3_INGR_ACL, entry.getRuleName(),
213                         matches, nshHeader, tunnelOfPort, true);
214             } else {
215                 Node bridgeNode = getBridgeNode(bridge.getName());
216                 if (bridgeNode == null) {
217                     LOG.debug("processAclEntry: bridge {} not yet configured. Skip processing !!", bridge.getName());
218                     continue;
219                 }
220
221                 long tunnelOfPort = southbound.getOFPort(bridgeNode, SERVER_GPE_PORT_NAME);
222                 if (tunnelOfPort == 0L) {
223                     LOG.error("programAclEntry: Could not identify tunnel port {} -> OF ({}) on {}",
224                         SERVER_GPE_PORT_NAME, tunnelOfPort, bridgeNode);
225                     return;
226                 }
227
228                 long localOfPort = southbound.getOFPort(bridgeNode, SERVER_PORT_NAME);
229                 if (localOfPort == 0L) {
230                     LOG.error("programAclEntry: Could not identify local port {} -> OF ({}) on {}",
231                         SERVER_PORT_NAME, localOfPort, bridgeNode);
232                     return;
233                 }
234
235                 RenderedServicePathHop lastRspHop = Iterables.getLast(rsp.getRenderedServicePathHop());
236
237                 LOG.debug("programAclEntry: Last Hop #: {}, nsi: {}", lastRspHop.getHopNumber().intValue(),
238                         lastRspHop.getServiceIndex().intValue() - 1);
239
240                 NshUtils nshHeader = new NshUtils();
241                 nshHeader.setNshNsp(rsp.getPathId());
242                 nshHeader.setNshNsi((short)(lastRspHop.getServiceIndex().intValue() - 1));
243                 nshHeader.setNshMetaC2(Long.parseLong(TUNNEL_VNID));
244                 LOG.debug("programAclEntry: The Nsh Header = {}", nshHeader);
245
246                 //handleLocalEgressPort(southbound.getDataPathId(bridgeNode), rsp.getPathId().toString(), localOfPort,
247                 //        TABLE_0_CLASSIFIER, TABLE_3_INGR_ACL, true);
248
249                 handleEgressSfcClassiferFlows(southbound.getDataPathId(bridgeNode),
250                         TABLE_0_CLASSIFIER, entry.getRuleName(), matches, nshHeader, tunnelOfPort, localOfPort, true);
251             }
252         }
253     }
254
255     private RenderedServicePath getRenderedServicePath (Ace entry) {
256         RenderedServicePath rsp = null;
257         RedirectToSfc sfcRedirect = entry.getActions().getAugmentation(RedirectToSfc.class);
258         LOG.debug("Processing ACL entry = {} sfcRedirect = {}", entry.getRuleName(), sfcRedirect);
259         if (sfcRedirect == null) {
260             LOG.warn("processAClEntry: sfcRedirect is null");
261             return null;
262         }
263
264         if (sfcRedirect.getRspName() != null) {
265             rsp = getRenderedServicePathFromRsp(sfcRedirect.getRspName());
266         } else if (sfcRedirect.getSfpName() != null) {
267             LOG.warn("getRenderedServicePath: sfp not handled yet");
268         } else {
269             rsp = getRenderedServicePathFromSfc(entry);
270         }
271         LOG.info("getRenderedServicePath: rsp: {}", rsp);
272         return rsp;
273     }
274
275     private RenderedServicePath getRenderedServicePathFromRsp(String rspName) {
276         return null;//getRsp(rspName);
277     }
278
279     private RenderedServicePath getRenderedServicePathFromSfc (Ace entry) {
280         RedirectToSfc sfcRedirect = entry.getActions().getAugmentation(RedirectToSfc.class);
281         LOG.debug("Processing ACL entry = {} sfcRedirect = {}", entry.getRuleName(), sfcRedirect);
282         if (sfcRedirect == null) {
283             LOG.warn("processAClEntry: sfcRedirect is null");
284             return null;
285         }
286
287         String sfcName = sfcRedirect.getSfcName();
288         ServiceFunctionPath sfp = getSfp(sfcName);
289         if (sfp == null || sfp.getName() == null) {
290             LOG.warn("There is no configured SFP with sfcName = {}; so skip installing the ACL entry!!", sfcName);
291             return null;
292         }
293
294         LOG.debug("Processing Redirect to SFC = {}, SFP = {}", sfcName, sfp);
295         // If RSP doesn't exist, create an RSP.
296         String sfpName = sfp.getName().getValue();
297         RenderedServicePath rsp = getRspforSfp(sfpName);
298         String rspName = sfp.getName().getValue() + "_rsp";
299         if (rsp == null) {
300             LOG.info("No configured RSP corresponding to SFP = {}, Creating new RSP = {}", sfpName, rspName);
301             CreateRenderedPathInput rspInput = new CreateRenderedPathInputBuilder()
302                     .setParentServiceFunctionPath(sfpName)
303                     .setName(rspName)
304                     .setSymmetric(sfp.isSymmetric())
305                     .build();
306             rsp = SfcProviderRenderedPathAPI.createRenderedServicePathAndState(sfp, rspInput);
307             if (rsp == null) {
308                 LOG.warn("failed to add RSP");
309                 return null;
310             }
311
312             // If SFP is symmetric, create RSP in the reverse direction.
313             if (sfp.isSymmetric()) {
314                 LOG.info("SFP = {} is symmetric, installing RSP in the reverse direction!!", sfpName);
315                 String rspNameRev = rspName + "-Reverse";
316                 RenderedServicePath rspReverse = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL,
317                         getRspId(rspNameRev));
318                 if (rspReverse == null) {
319                     rspReverse = SfcProviderRenderedPathAPI.createSymmetricRenderedServicePathAndState(rsp);
320                     if (rspReverse == null) {
321                         LOG.warn("failed to add reverse RSP");
322                         return null;
323                     }
324                 }
325             }
326         }
327         return rsp;
328     }
329
330     private void handleLocalEgressPort(long dataPathId, String s, long localOfPort, short writeTable,
331                                        short gotoTable, boolean write) {
332
333     }
334
335     private void handleEgressSfcClassiferFlows(long dataPathId, short writeTable, String ruleName,
336                                                Matches matches, NshUtils nshHeader, long tunnelOfPort,
337                                                long outOfPort, boolean write) {
338         sfcClassifier.programEgressSfcClassiferFlows(dataPathId, writeTable, ruleName, matches, nshHeader,
339                 tunnelOfPort, outOfPort, write);
340     }
341
342     private void handleSfcClassiferFlows(long dataPathId, short writeTable, String ruleName,
343                                          Matches matches, NshUtils nshHeader, long tunnelOfPort,
344                                          boolean write) {
345         sfcClassifier.programSfcClassiferFlows(dataPathId, writeTable, ruleName, matches, nshHeader,
346                 tunnelOfPort, write);
347     }
348
349     private InstanceIdentifier<RenderedServicePaths> getRspsId() {
350         return InstanceIdentifier.builder(RenderedServicePaths.class).build();
351     }
352
353     private InstanceIdentifier<RenderedServicePath> getRspId(String rspName) {
354         return InstanceIdentifier.builder(RenderedServicePaths.class)
355                 .child(RenderedServicePath.class, new RenderedServicePathKey(new RspName(rspName))).build();
356     }
357
358     public Node getBridgeNode(String bridgeName) {
359         Node nodeFound = null;
360         final List<Node> nodes = nodeCacheManager.getBridgeNodes();
361         if (nodes != null && !nodes.isEmpty()) {
362             for (Node node : nodes) {
363                 if (southbound.getBridge(node, bridgeName) != null) {
364                     nodeFound = node;
365                     break;
366                 }
367             }
368         }
369         return nodeFound;
370     }
371
372     public RenderedServicePath getRspforSfp(String sfpName) {
373         RenderedServicePath rspFound = null;
374         RenderedServicePaths rsps = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, this.getRspsId());
375         if (rsps != null) {
376             for (RenderedServicePath rsp : rsps.getRenderedServicePath()) {
377                 if (rsp.getParentServiceFunctionPath() != null) {
378                     if (rsp.getParentServiceFunctionPath().getValue().equals(sfpName)) {
379                         rspFound = rsp;
380                     }
381                 }
382             }
383         }
384         return rspFound;
385     }
386
387     public ServiceFunctionPath getSfp(String redirectSfc) {
388         ServiceFunctionPath sfpFound = null;
389         ServiceFunctionPaths sfps = SfcProviderServicePathAPI.readAllServiceFunctionPaths();
390         if (sfps != null) {
391             for (ServiceFunctionPath sfp: sfps.getServiceFunctionPath()) {
392                 if (sfp.getServiceChainName().getValue().equalsIgnoreCase(redirectSfc)) {
393                     sfpFound = sfp;
394                 }
395             }
396         }
397         return sfpFound;
398     }
399
400 /*
401  * (TABLE:0) EGRESS VM TRAFFIC TOWARDS TEP
402  * MATCH: DESTINATION ETHERNET ADDR AND OPENFLOW INPORT
403  * INSTRUCTION: SET TUNNELID AND GOTO TABLE TUNNEL TABLE (N)
404  * TABLE=0,IN_PORT=2,DL_SRC=00:00:00:00:00:01 \
405  * ACTIONS=SET_FIELD:5->TUN_ID,GOTO_TABLE=1"
406  */
407     public String getDestIp(Matches match) {
408         if (match.getAceType() instanceof AceIp) {
409             AceIp aceIp = (AceIp)match.getAceType();
410             if (aceIp.getAceIpVersion() instanceof AceIpv4) {
411                 AceIpv4 aceIpv4 = (AceIpv4) aceIp.getAceIpVersion();
412                 if (aceIpv4.getDestinationIpv4Network() != null) {
413                     String ipAddrPrefix = aceIpv4.getDestinationIpv4Network().getValue();
414                     return new StringTokenizer(ipAddrPrefix, "/").nextToken();
415                 }
416             }
417         }
418         return null;
419     }
420
421     public String getSourceIp(Matches match) {
422         if (match.getAceType() instanceof AceIp) {
423             AceIp aceIp = (AceIp)match.getAceType();
424             if (aceIp.getAceIpVersion() instanceof AceIpv4) {
425                 AceIpv4 aceIpv4 = (AceIpv4) aceIp.getAceIpVersion();
426                 if (aceIpv4.getSourceIpv4Network() != null) {
427                     //String ipAddr = new StringTokenizer(ipAddrPrefix, "/").nextToken();
428                     return aceIpv4.getSourceIpv4Network().getValue();
429                 }
430             }
431         }
432         return null;
433     }
434
435     private InstanceIdentifier<Classifiers> getClassifierIid() {
436         return InstanceIdentifier.create(Classifiers.class);
437     }
438
439     public void handleLocalInPort(long dpidLong, String segmentationId, Long inPort,
440                                   short writeTable, short goToTableId, Matches matches, boolean write) {
441         sfcClassifier.programLocalInPort(dpidLong, segmentationId, inPort, writeTable, goToTableId, matches, write);
442     }
443
444     @Override
445     public void setDependencies(ServiceReference serviceReference) {
446         nodeCacheManager = (NodeCacheManager) ServiceHelper.getGlobalInstance(NodeCacheManager.class, this);
447         southbound = (Southbound) ServiceHelper.getGlobalInstance(Southbound.class, this);
448     }
449
450     @Override
451     public void removeRsp(RenderedServicePath change) {
452         LOG.warn("removeRsp is not implemented yet");
453     }
454
455     @Override
456     public void addRsp(RenderedServicePath change) {
457         LOG.warn("addRsp is not implemented yet");
458     }
459
460     @Override
461     public void updateRsp(RenderedServicePath change) {
462         LOG.warn("updateRsp is not implemented yet");
463     }
464 }