317ea36df1e47012f3b51827f05e3dc71abb3887
[netvirt.git] / natservice / impl / src / main / java / org / opendaylight / netvirt / natservice / internal / NatTepChangeListener.java
1 /*
2  * Copyright (c) 2019 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.natservice.internal;
9
10 import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
11
12 import com.google.common.base.Optional;
13 import com.google.common.util.concurrent.FutureCallback;
14 import com.google.common.util.concurrent.Futures;
15 import com.google.common.util.concurrent.ListenableFuture;
16 import com.google.common.util.concurrent.MoreExecutors;
17
18 import java.util.ArrayList;
19 import java.util.List;
20 import java.util.concurrent.ExecutionException;
21 import javax.annotation.PostConstruct;
22 import javax.inject.Inject;
23 import javax.inject.Singleton;
24
25 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
26 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
27 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
28 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
29 import org.opendaylight.genius.infra.Datastore.Configuration;
30 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
31 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
32 import org.opendaylight.genius.infra.TypedReadWriteTransaction;
33 import org.opendaylight.genius.mdsalutil.MDSALUtil;
34 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
35 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
36 import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
37 import org.opendaylight.netvirt.fibmanager.api.IFibManager;
38 import org.opendaylight.netvirt.natservice.api.SnatServiceManager;
39 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.DpnEndpoints;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.dpn.endpoints.DPNTEPsInfo;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.dpn.endpoints.dpn.teps.info.TunnelEndPoints;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.FibRpcService;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryInput;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryInputBuilder;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryOutput;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.dpn.routers.DpnRoutersList;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.dpn.routers.dpn.routers.list.RoutersList;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.config.rev170206.NatserviceConfig;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.config.rev170206.NatserviceConfig.NatMode;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ProviderTypes;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.Routers;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.RouterPorts;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.Ports;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.ports.InternalToExternalPortMap;
58 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
59 import org.opendaylight.yangtools.yang.common.RpcResult;
60 import org.opendaylight.yangtools.yang.common.Uint32;
61 import org.opendaylight.yangtools.yang.common.Uint64;
62 import org.slf4j.Logger;
63 import org.slf4j.LoggerFactory;
64
65 @Singleton
66 public class NatTepChangeListener extends
67     AsyncDataTreeChangeListenerBase<TunnelEndPoints, NatTepChangeListener> {
68
69     private static final Logger LOG = LoggerFactory
70         .getLogger(NatTepChangeListener.class);
71     private final DataBroker dataBroker;
72     private SNATDefaultRouteProgrammer defaultRouteProgrammer;
73     private OdlInterfaceRpcService interfaceService;
74     private IdManagerService idManager;
75     private IFibManager fibManager;
76     private IBgpManager bgpManager;
77     private IMdsalApiManager mdsalManager;
78     private FloatingIPListener floatingIPListener;
79     private FibRpcService fibRpcService;
80     private NaptSwitchHA naptSwitchHA;
81     private final NatMode natMode;
82     private final SnatServiceManager natServiceManager;
83     private final JobCoordinator coordinator;
84     private final ManagedNewTransactionRunner txRunner;
85     private final NatOverVxlanUtil natOverVxlanUtil;
86
87     @Inject
88     public NatTepChangeListener(final DataBroker dataBroker,
89         final SNATDefaultRouteProgrammer defaultRouteProgrammer,
90         final OdlInterfaceRpcService interfaceService,
91         final IdManagerService idManager, final IFibManager fibManager,
92         final IBgpManager bgpManager,
93         final FloatingIPListener floatingIPListener,
94         final FibRpcService fibRpcService,
95         final IMdsalApiManager mdsalManager,
96         final NaptSwitchHA naptSwitchHA,
97         final NatserviceConfig config,
98         final SnatServiceManager natServiceManager,
99         final JobCoordinator coordinator,
100         final NatOverVxlanUtil natOverVxlanUtil) {
101         super(TunnelEndPoints.class, NatTepChangeListener.class);
102         this.dataBroker = dataBroker;
103         this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
104         this.defaultRouteProgrammer = defaultRouteProgrammer;
105         this.interfaceService = interfaceService;
106         this.idManager = idManager;
107         this.fibManager = fibManager;
108         this.bgpManager = bgpManager;
109         this.floatingIPListener = floatingIPListener;
110         this.fibRpcService = fibRpcService;
111         this.naptSwitchHA = naptSwitchHA;
112         this.mdsalManager = mdsalManager;
113         this.coordinator = coordinator;
114         this.natServiceManager = natServiceManager;
115         this.natOverVxlanUtil = natOverVxlanUtil;
116         if (config != null) {
117             this.natMode = config.getNatMode();
118         } else {
119             this.natMode = NatMode.Controller;
120         }
121     }
122
123     @Override
124     @PostConstruct
125     public void init() {
126         LOG.info("{} init", getClass().getSimpleName());
127         registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker);
128     }
129
130     @Override
131     protected InstanceIdentifier<TunnelEndPoints> getWildCardPath() {
132         return InstanceIdentifier.builder(DpnEndpoints.class)
133             .child(DPNTEPsInfo.class).child(TunnelEndPoints.class).build();
134     }
135
136     @Override
137     protected void remove(InstanceIdentifier<TunnelEndPoints> key,
138         TunnelEndPoints tep) {
139         /*
140          * Whenever the TEP on a given DPNID is removed, this API take care
141          * of withdrawing the FIB entries for those Floating-IP existing on this
142          * DPN and perform re-election of NAPT Switch for a VRF to which the current
143          * DPN is elected as NAPT Switch.
144          */
145         Uint64 srcDpnId = key.firstIdentifierOf(DPNTEPsInfo.class)
146             .firstKeyOf(DPNTEPsInfo.class).getDPNID();
147         final String srcTepIp = tep.getIpAddress().stringValue();
148         String tunnelType = tep.getTunnelType().getName();
149         LOG.debug(
150             "NAT Service : Remove Event triggered for Tep on DPN:{} having IP:{} and tunnelType:{}",
151             srcDpnId, srcTepIp, tunnelType);
152         handleTepDelForAllRtrs(srcDpnId, srcTepIp);
153     }
154
155     @Override
156     protected void update(InstanceIdentifier<TunnelEndPoints> key,
157         TunnelEndPoints origTep, TunnelEndPoints updatedTep) {
158         // Will be handled in NatTunnelInterfaceStateListener.add()
159         LOG.debug("NO ACTION duing update event : {}", updatedTep.key());
160     }
161
162     @Override
163     protected void add(InstanceIdentifier<TunnelEndPoints> key,
164         TunnelEndPoints tep) {
165         LOG.debug("NO ACTION duing add event : {}", tep.key());
166     }
167
168     @SuppressWarnings("checkstyle:IllegalCatch")
169     private void handleTepDelForAllRtrs(Uint64 srcDpnId, String srcTepIp) {
170         LOG.trace("handleTepDelForAllRtrs : TEP DEL ----- on DPN-ID {} having SRC IP : {}",
171             srcDpnId,
172             srcTepIp);
173
174         List<RoutersList> routersList = null;
175         InstanceIdentifier<DpnRoutersList> dpnRoutersListId = NatUtil.getDpnRoutersId(srcDpnId);
176         Optional<DpnRoutersList> optionalRouterDpnList =
177             SingleTransactionDataBroker
178                 .syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(dataBroker,
179                     LogicalDatastoreType.OPERATIONAL, dpnRoutersListId);
180         if (optionalRouterDpnList.isPresent()) {
181             routersList = optionalRouterDpnList.get().getRoutersList();
182         } else {
183             LOG.debug(
184                 "NAT Service : RouterDpnList is empty for DPN {}. Hence ignoring TEP DEL event",
185                 srcDpnId);
186             return;
187         }
188
189         if (routersList == null) {
190             LOG.error("handleTepDelForAllRtrs : DPN {} does not have the Routers presence",
191                 srcDpnId);
192             return;
193         }
194
195         for (RoutersList router : routersList) {
196             String routerName = router.getRouter();
197             LOG.debug(
198                 "handleTepDelForAllRtrs :  TEP DEL : DNAT -> Withdrawing routes for router {} ",
199                 routerName);
200             Uint32 routerId = NatUtil.getVpnId(dataBroker, routerName);
201             if (routerId == NatConstants.INVALID_ID) {
202                 LOG.error("handleTepDelForAllRtrs :Invalid ROUTER-ID {} returned for routerName {}",
203                     routerId, routerName);
204                 return;
205             }
206             Uuid externalNetworkId = NatUtil.getNetworkIdFromRouterName(dataBroker, routerName);
207             ProviderTypes extNwProvType = NatEvpnUtil.getExtNwProvTypeFromRouterName(dataBroker,
208                 routerName, externalNetworkId);
209             if (extNwProvType == null) {
210                 return;
211             }
212             boolean isFipExists = hndlTepDelForDnatInEachRtr(router, routerId, srcDpnId,
213                 extNwProvType);
214             LOG.debug(
215                 "handleTepDelForAllRtrs :  TEP DEL : SNAT -> Withdrawing and Advertising routes for router {} ",
216                 router.getRouter());
217             coordinator.enqueueJob((NatConstants.NAT_DJC_PREFIX + router.getRouter()), () -> {
218                 List<ListenableFuture<Void>> futures = new ArrayList<>();
219                 txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, configTx -> {
220                     hndlTepDelForSnatInEachRtr(router, routerId, srcDpnId, srcTepIp, isFipExists,
221                         extNwProvType, configTx);
222                 });
223
224                 return futures;
225             }, NatConstants.NAT_DJC_MAX_RETRIES);
226         }
227         return;
228     }
229
230     private boolean hndlTepDelForDnatInEachRtr(RoutersList router, Uint32 routerId,
231         Uint64 tepDeletedDpnId,
232         ProviderTypes extNwProvType) {
233         //DNAT : Withdraw the routes from the BGP
234         String routerName = router.getRouter();
235         Boolean isFipExists = Boolean.FALSE;
236
237         LOG.debug("hndlTepDelForDnatInEachRtr : DNAT -> Trying to clear routes to the Floating IP "
238             + "associated to the router {}", routerName);
239
240         InstanceIdentifier<RouterPorts> routerPortsId = NatUtil.getRouterPortsId(routerName);
241         Optional<RouterPorts> optRouterPorts = MDSALUtil.read(dataBroker, LogicalDatastoreType
242             .CONFIGURATION, routerPortsId);
243         if (!optRouterPorts.isPresent()) {
244             LOG.debug(
245                 "hndlTepDelForDnatInEachRtr : DNAT -> Could not read Router Ports data object with id: {} "
246                     + "from DNAT FloatingIpInfo", routerName);
247             return isFipExists;
248         }
249         RouterPorts routerPorts = optRouterPorts.get();
250         Uuid extNwId = routerPorts.getExternalNetworkId();
251         final String vpnName = NatUtil.getAssociatedVPN(dataBroker, extNwId);
252         if (vpnName == null) {
253             LOG.error(
254                 "hndlTepDelForDnatInEachRtr : DNAT -> No External VPN associated with Ext N/W {} for Router {}",
255                 extNwId, routerName);
256             return isFipExists;
257         }
258         String rd = NatUtil.getVpnRd(dataBroker, vpnName);
259         if (extNwProvType == null) {
260             return isFipExists;
261         }
262         Uint32 l3Vni = Uint32.ZERO;
263         if (extNwProvType == ProviderTypes.VXLAN) {
264             //get l3Vni value for external VPN
265             l3Vni = NatEvpnUtil.getL3Vni(dataBroker, rd);
266             if (l3Vni == NatConstants.DEFAULT_L3VNI_VALUE) {
267                 LOG.debug(
268                     "hndlTepDelForDnatInEachRtr : L3VNI value is not configured in Internet VPN {} and RD {} "
269                         + "Carve-out L3VNI value from OpenDaylight VXLAN VNI Pool and continue to installing "
270                         + "NAT flows", vpnName, rd);
271                 l3Vni = natOverVxlanUtil.getInternetVpnVni(vpnName, routerId);
272             }
273         }
274         List<Ports> interfaces = routerPorts.getPorts();
275         for (Ports port : interfaces) {
276             //Get the DPN on which this interface resides
277             String interfaceName = port.getPortName();
278             Uint64 fipCfgdDpnId = NatUtil.getDpnForInterface(interfaceService, interfaceName);
279             if (fipCfgdDpnId.equals(Uint64.ZERO)) {
280                 LOG.info(
281                     "hndlTepDelForDnatInEachRtr : DNAT -> Abort processing Floating ip configuration. "
282                         + "No DPN for port : {}", interfaceName);
283                 continue;
284             }
285             if (!fipCfgdDpnId.equals(tepDeletedDpnId)) {
286                 LOG.info(
287                     "hndlTepDelForDnatInEachRtr : DNAT -> TEP deleted DPN {} is not the DPN {} which has the "
288                         + "floating IP configured for the port: {}",
289                     tepDeletedDpnId, fipCfgdDpnId, interfaceName);
290                 continue;
291             }
292             isFipExists = Boolean.TRUE;
293             List<InternalToExternalPortMap> intExtPortMapList = port.getInternalToExternalPortMap();
294             for (InternalToExternalPortMap intExtPortMap : intExtPortMapList) {
295                 String internalIp = intExtPortMap.getInternalIp();
296                 String externalIp = intExtPortMap.getExternalIp();
297                 externalIp = NatUtil.validateAndAddNetworkMask(externalIp);
298                 LOG.debug(
299                     "hndlTepDelForDnatInEachRtr : DNAT -> Withdrawing the FIB route to the floating IP {} "
300                         + "configured for the port: {}",
301                     externalIp, interfaceName);
302                 NatUtil.removePrefixFromBGP(bgpManager, fibManager, rd, externalIp, vpnName);
303                 Uint32 serviceId = null;
304                 if (extNwProvType == ProviderTypes.VXLAN) {
305                     serviceId = l3Vni;
306                 } else {
307                     serviceId = floatingIPListener
308                         .getOperationalIpMapping(routerName, interfaceName, internalIp);
309                     if (serviceId == null || serviceId == NatConstants.INVALID_ID) {
310                         LOG.error(
311                             "hndlTepDelForDnatInEachRtr : DNAT -> Unable to remove the table 21 entry pushing the"
312                                 + " MPLS label to the tunnel since label is invalid");
313                         continue;
314                     }
315                 }
316
317                 RemoveFibEntryInput input = new RemoveFibEntryInputBuilder().setVpnName(vpnName)
318                     .setSourceDpid(fipCfgdDpnId).setIpAddress(externalIp).setServiceId(serviceId)
319                     .setIpAddressSource(RemoveFibEntryInput.IpAddressSource.FloatingIP).build();
320                 ListenableFuture<RpcResult<RemoveFibEntryOutput>> future = fibRpcService
321                     .removeFibEntry(input);
322
323                 Futures.addCallback(future, new FutureCallback<RpcResult<RemoveFibEntryOutput>>() {
324
325                     @Override
326                     public void onFailure(Throwable error) {
327                         LOG.error(
328                             "hndlTepDelForDnatInEachRtr : DNAT -> Error in removing the table 21 entry pushing "
329                                 + "the MPLS label to the tunnel since label is invalid ", error);
330                     }
331
332                     @Override
333                     public void onSuccess(RpcResult<RemoveFibEntryOutput> result) {
334                         if (result.isSuccessful()) {
335                             LOG.info(
336                                 "hndlTepDelForDnatInEachRtr : DNAT -> Successfully removed the entry pushing the "
337                                     + "MPLS label to the tunnel");
338                         } else {
339                             LOG.error(
340                                 "hndlTepDelForDnatInEachRtr : DNAT -> Error in fib rpc call to remove the table "
341                                     + "21 entry pushing the MPLS label to the tunnnel due to {}",
342                                 result.getErrors());
343                         }
344                     }
345                 }, MoreExecutors.directExecutor());
346             }
347         }
348         return isFipExists;
349     }
350
351     private void hndlTepDelForSnatInEachRtr(RoutersList router, Uint32 routerId, Uint64 dpnId,
352         String srcTepIp, Boolean isFipExists, ProviderTypes extNwProvType,
353         TypedReadWriteTransaction<Configuration> confTx)
354         throws ExecutionException, InterruptedException {
355
356         /*SNAT :
357         1) Elect a new switch as the primary NAPT
358         2) Advertise the new routes to BGP for the newly elected TEP IP as the DPN IP
359         3) This will make sure old routes are withdrawn and new routes are advertised.
360          */
361
362         String routerName = router.getRouter();
363         LOG.debug(
364             "hndlTepDelForSnatInEachRtr : SNAT -> Trying to clear routes to the External fixed IP associated "
365                 + "to the router {}", routerName);
366
367         // Check if this is externalRouter else ignore
368         InstanceIdentifier<Routers> extRoutersId = NatUtil.buildRouterIdentifier(routerName);
369         Optional<Routers> routerData = Optional.absent();
370         try {
371             routerData = confTx.read(extRoutersId).get();
372         } catch (InterruptedException | ExecutionException e) {
373             LOG.error("Error retrieving routers {}", extRoutersId, e);
374         }
375         if (!routerData.isPresent()) {
376             LOG.debug(
377                 "hndlTepDelForSnatInEachRtr : SNAT->Ignoring TEP del for router {} since its not External Router",
378                 routerName);
379             return;
380         }
381
382         //Check if the DPN having the router is the NAPT switch
383         Uint64 naptId = NatUtil.getPrimaryNaptfromRouterName(dataBroker, routerName);
384         if (naptId == null || naptId.equals(Uint64.ZERO) || !naptId.equals(dpnId)) {
385             LOG.error(
386                 "hndlTepDelForSnatInEachRtr : SNAT -> Ignoring TEP delete for the DPN {} since"
387                     + "srcTepIp : {} is NOT a NAPT switch", dpnId, srcTepIp);
388             return;
389         }
390         if (natMode == NatMode.Conntrack) {
391             Routers extRouter = routerData.get();
392             natServiceManager.notify(confTx, extRouter, null, naptId, dpnId,
393                 SnatServiceManager.Action.CNT_ROUTER_DISBL);
394             if (extRouter.isEnableSnat()) {
395                 natServiceManager.notify(confTx, extRouter, null, naptId, dpnId,
396                     SnatServiceManager.Action.SNAT_ROUTER_DISBL);
397             }
398         } else {
399
400             Uuid networkId = routerData.get().getNetworkId();
401             if (networkId == null) {
402                 LOG.error(
403                     "hndlTepDelForSnatInEachRtr : SNAT -> Ignoring TEP delete for the DPN {} for router {}"
404                         + "as external network configuraton is missing", dpnId, routerName);
405                 return;
406             }
407
408             LOG.debug("hndlTepDelForSnatInEachRtr : SNAT->Router {} is associated with ext nw {}",
409                 routerId, networkId);
410             Uuid bgpVpnUuid = NatUtil.getVpnForRouter(dataBroker, routerName);
411             Uint32 bgpVpnId;
412             if (bgpVpnUuid == null) {
413                 LOG.debug(
414                     "hndlTepDelForSnatInEachRtr : SNAT->Internal VPN-ID {} associated to router {}",
415                     routerId, routerName);
416                 bgpVpnId = routerId;
417             } else {
418                 bgpVpnId = NatUtil.getVpnId(dataBroker, bgpVpnUuid.getValue());
419                 if (bgpVpnId == NatConstants.INVALID_ID) {
420                     LOG.error(
421                         "hndlTepDelForSnatInEachRtr :SNAT->Invalid Private BGP VPN ID returned for routerName {}",
422                         routerName);
423                     return;
424                 }
425             }
426             if (!isFipExists) {
427                 // Remove default entry in FIB to SNAT table
428                 LOG.debug(
429                     "NAT Service : Installing default route in FIB on DPN {} for router {} with"
430                         + " vpn {}...",
431                     dpnId, routerName, bgpVpnId);
432                 defaultRouteProgrammer.installDefNATRouteInDPN(dpnId, bgpVpnId, routerId, confTx);
433             }
434
435             if (routerData.get().isEnableSnat()) {
436                 LOG.info("hndlTepDelForSnatInEachRtr : SNAT enabled for router {}", routerId);
437
438                 Uint32 routerVpnId = routerId;
439                 if (bgpVpnId != NatConstants.INVALID_ID) {
440                     LOG.debug(
441                         "hndlTepDelForSnatInEachRtr : SNAT -> Private BGP VPN ID (Internal BGP VPN ID) {} "
442                             + "associated to the router {}", bgpVpnId, routerName);
443                     routerVpnId = bgpVpnId;
444                 } else {
445                     LOG.debug(
446                         "hndlTepDelForSnatInEachRtr : SNAT -> Internal L3 VPN ID (Router ID) {} "
447                             + "associated to the router {}", routerVpnId, routerName);
448                 }
449                 //Re-elect the other available switch as the NAPT switch and program the NAT flows.
450                 String externalVpnName = NatUtil.getAssociatedVPN(dataBroker,
451                         routerData.get().getNetworkId());
452                 NatUtil.removeSNATFromDPN(dataBroker, mdsalManager, idManager, naptSwitchHA, dpnId,
453                         routerData.get(), routerId, routerVpnId, externalVpnName, extNwProvType, confTx);
454             } else {
455                 LOG.info(
456                     "hndlTepDelForSnatInEachRtr : SNAT is not enabled for router {} to handle addDPN event {}",
457                     routerId, dpnId);
458             }
459         }
460     }
461
462     @Override
463     protected NatTepChangeListener getDataTreeChangeListener() {
464         return this;
465     }
466 }