65713f693d850a86a1870f67d263d1d890365fa8
[netvirt.git] / neutronvpn / impl / src / main / java / org / opendaylight / netvirt / neutronvpn / NeutronvpnManager.java
1 /*
2  * Copyright © 2015, 2018 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.neutronvpn;
9
10 import static java.util.Collections.singletonList;
11 import static org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker.syncReadOptional;
12 import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
13 import static org.opendaylight.netvirt.neutronvpn.api.utils.NeutronUtils.requireNonNullElse;
14
15 import com.google.common.base.Optional;
16 import com.google.common.base.Preconditions;
17 import com.google.common.util.concurrent.FutureCallback;
18 import com.google.common.util.concurrent.Futures;
19 import com.google.common.util.concurrent.JdkFutureAdapters;
20 import com.google.common.util.concurrent.ListenableFuture;
21 import com.google.common.util.concurrent.MoreExecutors;
22 import com.google.common.util.concurrent.SettableFuture;
23 import java.util.ArrayList;
24 import java.util.Collection;
25 import java.util.Collections;
26 import java.util.EventListener;
27 import java.util.HashMap;
28 import java.util.HashSet;
29 import java.util.Iterator;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Map.Entry;
33 import java.util.Objects;
34 import java.util.Set;
35 import java.util.concurrent.ConcurrentHashMap;
36 import java.util.concurrent.ExecutionException;
37 import java.util.concurrent.Future;
38 import java.util.concurrent.TimeUnit;
39 import java.util.concurrent.locks.ReentrantLock;
40 import java.util.function.Consumer;
41 import javax.annotation.Nonnull;
42 import javax.annotation.Nullable;
43 import javax.annotation.PreDestroy;
44 import javax.inject.Inject;
45 import javax.inject.Singleton;
46 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
47 import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService;
48 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
49 import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException;
50 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
51 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
52 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
53 import org.opendaylight.genius.infra.Datastore.Configuration;
54 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
55 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
56 import org.opendaylight.genius.infra.TypedWriteTransaction;
57 import org.opendaylight.genius.mdsalutil.NwConstants;
58 import org.opendaylight.genius.utils.JvmGlobalLocks;
59 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
60 import org.opendaylight.infrautils.utils.concurrent.KeyedLocks;
61 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
62 import org.opendaylight.netvirt.alarm.NeutronvpnAlarms;
63 import org.opendaylight.netvirt.elanmanager.api.IElanService;
64 import org.opendaylight.netvirt.fibmanager.api.FibHelper;
65 import org.opendaylight.netvirt.neutronvpn.api.enums.IpVersionChoice;
66 import org.opendaylight.netvirt.neutronvpn.api.utils.NeutronConstants;
67 import org.opendaylight.netvirt.neutronvpn.evpn.manager.NeutronEvpnManager;
68 import org.opendaylight.netvirt.neutronvpn.evpn.utils.NeutronEvpnUtils;
69 import org.opendaylight.netvirt.vpnmanager.api.IVpnManager;
70 import org.opendaylight.netvirt.vpnmanager.api.VpnHelper;
71 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInstances;
72 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInterfaces;
73 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.af.config.VpnTargets;
74 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.af.config.VpnTargetsBuilder;
75 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.af.config.vpntargets.VpnTarget;
76 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.af.config.vpntargets.VpnTargetBuilder;
77 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.af.config.vpntargets.VpnTargetKey;
78 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstance;
79 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstanceBuilder;
80 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstanceKey;
81 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.vpn.instance.Ipv4FamilyBuilder;
82 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.vpn.instance.Ipv6FamilyBuilder;
83 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
84 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceBuilder;
85 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceKey;
86 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.vpn._interface.VpnInstanceNames;
87 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.vpn._interface.VpnInstanceNames.AssociatedSubnetType;
88 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
89 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
90 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.Adjacencies;
91 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.AdjacenciesBuilder;
92 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.Adjacency;
93 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.Adjacency.AdjacencyType;
94 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.AdjacencyBuilder;
95 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.AdjacencyKey;
96 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.learnt.vpn.vip.to.port.data.LearntVpnVipToPort;
97 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
98 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry.BgpvpnType;
99 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ExternalNetworks;
100 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ProviderTypes;
101 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.networks.Networks;
102 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.networks.NetworksBuilder;
103 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.networks.NetworksKey;
104 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.config.rev160806.NeutronvpnConfig;
105 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.AssociateNetworksInput;
106 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.AssociateNetworksOutput;
107 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.AssociateNetworksOutputBuilder;
108 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.AssociateRouterInput;
109 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.AssociateRouterOutput;
110 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.AssociateRouterOutputBuilder;
111 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.CreateEVPNInput;
112 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.CreateEVPNOutput;
113 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.CreateL3VPNInput;
114 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.CreateL3VPNOutput;
115 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.CreateL3VPNOutputBuilder;
116 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.DeleteEVPNInput;
117 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.DeleteEVPNOutput;
118 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.DeleteL3VPNInput;
119 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.DeleteL3VPNOutput;
120 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.DeleteL3VPNOutputBuilder;
121 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.DissociateNetworksInput;
122 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.DissociateNetworksOutput;
123 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.DissociateNetworksOutputBuilder;
124 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.DissociateRouterInput;
125 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.DissociateRouterOutput;
126 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.DissociateRouterOutputBuilder;
127 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.GetEVPNInput;
128 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.GetEVPNOutput;
129 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.GetFixedIPsForNeutronPortInput;
130 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.GetFixedIPsForNeutronPortOutput;
131 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.GetFixedIPsForNeutronPortOutputBuilder;
132 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.GetL3VPNInput;
133 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.GetL3VPNInputBuilder;
134 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.GetL3VPNOutput;
135 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.GetL3VPNOutputBuilder;
136 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.NetworkAttributes;
137 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.NeutronvpnService;
138 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.RouterInterfacesMap;
139 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.Subnetmaps;
140 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.VpnMaps;
141 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.createl3vpn.input.L3vpn;
142 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.getl3vpn.output.L3vpnInstances;
143 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.getl3vpn.output.L3vpnInstancesBuilder;
144 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.router.interfaces.map.RouterInterfaces;
145 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.router.interfaces.map.RouterInterfacesKey;
146 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.router.interfaces.map.router.interfaces.Interfaces;
147 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.router.interfaces.map.router.interfaces.InterfacesBuilder;
148 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.router.interfaces.map.router.interfaces.InterfacesKey;
149 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.Subnetmap;
150 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.SubnetmapBuilder;
151 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.SubnetmapKey;
152 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.vpnmaps.VpnMap;
153 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.vpnmaps.VpnMapBuilder;
154 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.vpnmaps.VpnMapKey;
155 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.vpnmaps.vpnmap.RouterIds;
156 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.vpnmaps.vpnmap.RouterIdsBuilder;
157 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.AddStaticRouteInput;
158 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.AddStaticRouteInputBuilder;
159 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.AddStaticRouteOutput;
160 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.RemoveStaticRouteInput;
161 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.RemoveStaticRouteInputBuilder;
162 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.VpnRpcService;
163 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.extensions.rev160617.BgpvpnVni;
164 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.extensions.rev160617.OperationalPortStatus;
165 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.extensions.rev160617.service.provider.features.attributes.Features;
166 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.extensions.rev160617.service.provider.features.attributes.features.Feature;
167 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.extensions.rev160617.service.provider.features.attributes.features.FeatureBuilder;
168 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.extensions.rev160617.service.provider.features.attributes.features.FeatureKey;
169 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.l3.attributes.Routes;
170 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.Router;
171 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.networks.attributes.networks.Network;
172 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes.FixedIps;
173 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.Ports;
174 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
175 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.provider.ext.rev150712.NetworkProviderExtension;
176 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
177 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.subnets.Subnet;
178 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.inter.vpn.links.InterVpnLink;
179 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
180 import org.opendaylight.yangtools.yang.common.RpcError;
181 import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
182 import org.opendaylight.yangtools.yang.common.RpcResult;
183 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
184 import org.slf4j.Logger;
185 import org.slf4j.LoggerFactory;
186 import org.slf4j.helpers.FormattingTuple;
187 import org.slf4j.helpers.MessageFormatter;
188
189 @Singleton
190 public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, EventListener {
191
192     private static final Logger LOG = LoggerFactory.getLogger(NeutronvpnManager.class);
193     private static long LOCK_WAIT_TIME = 10L;
194
195     private final DataBroker dataBroker;
196     private final ManagedNewTransactionRunner txRunner;
197     private final NotificationPublishService notificationPublishService;
198     private final VpnRpcService vpnRpcService;
199     private final NeutronFloatingToFixedIpMappingChangeListener floatingIpMapListener;
200     private final IElanService elanService;
201     private final NeutronvpnConfig neutronvpnConfig;
202     private final NeutronEvpnManager neutronEvpnManager;
203     private final NeutronEvpnUtils neutronEvpnUtils;
204     private final JobCoordinator jobCoordinator;
205     private final NeutronvpnUtils neutronvpnUtils;
206     private final IVpnManager vpnManager;
207     private final ConcurrentHashMap<Uuid, Uuid> unprocessedPortsMap = new ConcurrentHashMap<>();
208     private final NeutronvpnAlarms neutronvpnAlarm = new NeutronvpnAlarms();
209     private final KeyedLocks<Uuid> vpnLock = new KeyedLocks<>();
210     private final KeyedLocks<String> interfaceLock = new KeyedLocks<>();
211
212     @Inject
213     public NeutronvpnManager(
214             final DataBroker dataBroker, final NotificationPublishService notiPublishService,
215             final VpnRpcService vpnRpcSrv, final IElanService elanService,
216             final NeutronFloatingToFixedIpMappingChangeListener neutronFloatingToFixedIpMappingChangeListener,
217             final NeutronvpnConfig neutronvpnConfig, final IVpnManager vpnManager,
218             final JobCoordinator jobCoordinator,
219             final NeutronvpnUtils neutronvpnUtils) throws TransactionCommitFailedException {
220         this.dataBroker = dataBroker;
221         this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
222         notificationPublishService = notiPublishService;
223         vpnRpcService = vpnRpcSrv;
224         this.elanService = elanService;
225         floatingIpMapListener = neutronFloatingToFixedIpMappingChangeListener;
226         this.neutronvpnConfig = neutronvpnConfig;
227         neutronEvpnManager = new NeutronEvpnManager(dataBroker, this, neutronvpnUtils);
228         neutronEvpnUtils = new NeutronEvpnUtils(dataBroker, vpnManager, jobCoordinator);
229         this.jobCoordinator = jobCoordinator;
230         this.neutronvpnUtils = neutronvpnUtils;
231         this.vpnManager = vpnManager;
232
233         configureFeatures();
234     }
235
236     @Override
237     @PreDestroy
238     public void close() {
239         LOG.info("{} close", getClass().getSimpleName());
240     }
241
242     private void configureFeatures() throws TransactionCommitFailedException {
243         InstanceIdentifier<Feature> iid = InstanceIdentifier.builder(
244                         Neutron.class).child(Features.class).child(
245                         Feature.class, new FeatureKey(OperationalPortStatus.class)).build();
246         Feature feature = new FeatureBuilder().withKey(new FeatureKey(OperationalPortStatus.class)).build();
247         try {
248             SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, iid, feature);
249         } catch (OptimisticLockFailedException e) {
250             LOG.debug("Optimistic lock failed exception while configuring feature {}", feature, e);
251         }
252         InstanceIdentifier<Feature> bgpvpnVniIid = InstanceIdentifier.builder(
253                 Neutron.class).child(Features.class).child(
254                 Feature.class, new FeatureKey(BgpvpnVni.class)).build();
255         Feature bgpvpnVniFeature = new FeatureBuilder().withKey(new FeatureKey(BgpvpnVni.class)).build();
256         try {
257             SingleTransactionDataBroker.syncWrite(
258                     dataBroker, LogicalDatastoreType.OPERATIONAL, bgpvpnVniIid, bgpvpnVniFeature);
259         } catch (OptimisticLockFailedException e) {
260             LOG.debug("Optimistic lock failed exception while configuring feature {}", bgpvpnVniFeature, e);
261         }
262     }
263
264     public String getOpenDaylightVniRangesConfig() {
265         return neutronvpnConfig.getOpendaylightVniRanges();
266     }
267
268     // TODO Clean up the exception handling
269     @SuppressWarnings("checkstyle:IllegalCatch")
270     protected void createSubnetmapNode(Uuid subnetId, String subnetIp, Uuid tenantId, Uuid networkId,
271                                        @Nullable NetworkAttributes.NetworkType networkType, long segmentationId) {
272         try {
273             InstanceIdentifier<Subnetmap> subnetMapIdentifier = NeutronvpnUtils.buildSubnetMapIdentifier(subnetId);
274             final ReentrantLock lock = lockForUuid(subnetId);
275             lock.lock();
276             try {
277                 LOG.info("createSubnetmapNode: subnet ID {}", subnetId.toString());
278                 Optional<Subnetmap> sn = SingleTransactionDataBroker.syncReadOptional(dataBroker,
279                         LogicalDatastoreType.CONFIGURATION, subnetMapIdentifier);
280                 if (sn.isPresent()) {
281                     LOG.error("createSubnetmapNode: Subnetmap node for subnet ID {} already exists, returning",
282                         subnetId.getValue());
283                     return;
284                 }
285                 SubnetmapBuilder subnetmapBuilder = new SubnetmapBuilder().withKey(new SubnetmapKey(subnetId))
286                         .setId(subnetId).setSubnetIp(subnetIp).setTenantId(tenantId).setNetworkId(networkId)
287                         .setNetworkType(networkType).setSegmentationId(segmentationId);
288                 LOG.debug("createSubnetmapNode: Adding a new subnet node in Subnetmaps DS for subnet {}",
289                     subnetId.getValue());
290                 SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION,
291                         subnetMapIdentifier, subnetmapBuilder.build());
292             } finally {
293                 lock.unlock();
294             }
295         } catch (TransactionCommitFailedException | ReadFailedException e) {
296             LOG.error("createSubnetmapNode: Creating subnetmap node failed for subnet {}", subnetId.getValue());
297         }
298         // check if there are ports to update for already created Subnetmap node
299         LOG.debug("createSubnetmapNode: Update created Subnetmap for subnet {} with ports", subnetId.getValue());
300         for (Map.Entry<Uuid, Uuid> entry : unprocessedPortsMap.entrySet()) {
301             if (entry.getValue().getValue().equals(subnetId.getValue())) {
302                 updateSubnetmapNodeWithPorts(subnetId, entry.getKey(), null);
303                 unprocessedPortsMap.remove(entry.getKey());
304             }
305         }
306     }
307
308     @Nullable
309     protected Subnetmap updateSubnetNode(Uuid subnetId, @Nullable Uuid routerId, Uuid vpnId,
310             @Nullable Uuid internetvpnId) {
311         InstanceIdentifier<Subnetmap> id = InstanceIdentifier.builder(Subnetmaps.class)
312                 .child(Subnetmap.class, new SubnetmapKey(subnetId))
313                 .build();
314         final ReentrantLock lock = lockForUuid(subnetId);
315         lock.lock();
316         try {
317             Optional<Subnetmap> sn =
318                     SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
319             if (!sn.isPresent()) {
320                 LOG.error("subnetmap node for subnet {} does not exist, returning", subnetId.getValue());
321                 return null;
322             }
323             LOG.debug("updating existing subnetmap node for subnet ID {}", subnetId.getValue());
324             SubnetmapBuilder builder = new SubnetmapBuilder(sn.get());
325             if (routerId != null) {
326                 builder.setRouterId(routerId);
327             }
328             if (vpnId != null) {
329                 builder.setVpnId(vpnId);
330             }
331             if (NeutronvpnUtils.getIpVersionFromString(sn.get().getSubnetIp()) == IpVersionChoice.IPV6) {
332                 builder.setInternetVpnId(internetvpnId);
333             }
334             Subnetmap subnetmap = builder.build();
335             LOG.debug("Creating/Updating subnetMap node: {} ", subnetId.getValue());
336             SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, id, subnetmap);
337             return subnetmap;
338         } catch (ReadFailedException | TransactionCommitFailedException e) {
339             LOG.error("Subnet map update failed for node {}", subnetId.getValue(), e);
340             return null;
341         } finally {
342             lock.unlock();
343         }
344     }
345
346     protected void updateSubnetNodeWithFixedIp(Uuid subnetId, @Nullable Uuid routerId,
347                                                @Nullable Uuid routerInterfacePortId, @Nullable String fixedIp,
348                                                @Nullable String routerIntfMacAddress, @Nullable Uuid vpnId) {
349         InstanceIdentifier<Subnetmap> id =
350                 InstanceIdentifier.builder(Subnetmaps.class).child(Subnetmap.class, new SubnetmapKey(subnetId)).build();
351         final ReentrantLock lock = lockForUuid(subnetId);
352         lock.lock();
353         try {
354             Optional<Subnetmap> sn =
355                     SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
356             if (!sn.isPresent()) {
357                 LOG.error("WithRouterFixedIP: subnetmap node for subnet {} does not exist, returning ",
358                     subnetId.getValue());
359                 return;
360             }
361             LOG.debug("WithRouterFixedIP: Updating existing subnetmap node for subnet ID {}",
362                 subnetId.getValue());
363             SubnetmapBuilder builder = new SubnetmapBuilder(sn.get());
364             builder.setRouterId(routerId);
365             builder.setRouterInterfacePortId(routerInterfacePortId);
366             builder.setRouterIntfMacAddress(routerIntfMacAddress);
367             builder.setRouterInterfaceFixedIp(fixedIp);
368             if (vpnId != null) {
369                 builder.setVpnId(vpnId);
370             }
371             Subnetmap subnetmap = builder.build();
372             LOG.debug("WithRouterFixedIP Creating/Updating subnetMap node for Router FixedIp: {} ",
373                 subnetId.getValue());
374             SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, id, subnetmap);
375         } catch (ReadFailedException | TransactionCommitFailedException e) {
376             LOG.error("WithRouterFixedIP: subnet map for Router FixedIp failed for node {}",
377                 subnetId.getValue(), e);
378         } finally {
379             lock.unlock();
380         }
381     }
382
383     protected Subnetmap updateSubnetmapNodeWithPorts(Uuid subnetId, @Nullable Uuid portId,
384             @Nullable Uuid directPortId) {
385         Subnetmap subnetmap = null;
386         InstanceIdentifier<Subnetmap> id = InstanceIdentifier.builder(Subnetmaps.class).child(Subnetmap.class,
387                 new SubnetmapKey(subnetId)).build();
388         LOG.info("updateSubnetmapNodeWithPorts : subnetId {}, subnetMapId {}", subnetId.toString(), id.toString());
389         final ReentrantLock lock = lockForUuid(subnetId);
390         lock.lock();
391         try {
392             Optional<Subnetmap> sn =
393                     SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
394                         id);
395             if (sn.isPresent()) {
396                 SubnetmapBuilder builder = new SubnetmapBuilder(sn.get());
397                 if (null != portId) {
398                     List<Uuid> portList = builder.getPortList();
399                     if (null == portList) {
400                         portList = new ArrayList<>();
401                     }
402                     portList.add(portId);
403                     builder.setPortList(portList);
404                     LOG.debug("updateSubnetmapNodeWithPorts: Updating existing subnetmap node {} with port {}",
405                         subnetId.getValue(), portId.getValue());
406                 }
407                 if (null != directPortId) {
408                     List<Uuid> directPortList = builder.getDirectPortList();
409                     if (null == directPortList) {
410                         directPortList = new ArrayList<>();
411                     }
412                     directPortList.add(directPortId);
413                     builder.setDirectPortList(directPortList);
414                     LOG.debug("Updating existing subnetmap node {} with port {}", subnetId.getValue(),
415                         directPortId.getValue());
416                 }
417                 subnetmap = builder.build();
418                 SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, id,
419                     subnetmap);
420             } else {
421                 LOG.info("updateSubnetmapNodeWithPorts: Subnetmap node is not ready {}, put port {} in unprocessed "
422                         + "cache ", subnetId.getValue(), portId.getValue());
423                 unprocessedPortsMap.put(portId, subnetId);
424             }
425         } catch (ReadFailedException | TransactionCommitFailedException e) {
426             LOG.error("Updating port list of a given subnetMap failed for node: {}", subnetId.getValue(), e);
427         } finally {
428             lock.unlock();
429         }
430         return subnetmap;
431     }
432
433     protected Subnetmap removeFromSubnetNode(Uuid subnetId, @Nullable Uuid networkId, @Nullable Uuid routerId,
434                                             Uuid vpnId, @Nullable Uuid portId) {
435         Subnetmap subnetmap = null;
436         InstanceIdentifier<Subnetmap> id = InstanceIdentifier.builder(Subnetmaps.class)
437                 .child(Subnetmap.class, new SubnetmapKey(subnetId))
438                 .build();
439         final ReentrantLock lock = lockForUuid(subnetId);
440         lock.lock();
441         try {
442             Optional<Subnetmap> sn =
443                     SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
444                         id);
445             if (sn.isPresent()) {
446                 SubnetmapBuilder builder = new SubnetmapBuilder(sn.get());
447                 if (routerId != null) {
448                     builder.setRouterId(null);
449                 }
450                 if (networkId != null) {
451                     builder.setNetworkId(null);
452                 }
453                 if (vpnId != null) {
454                     builder.setVpnId(null);
455                 }
456                 builder.setInternetVpnId(null);
457                 if (portId != null && builder.getPortList() != null) {
458                     List<Uuid> portList = builder.getPortList();
459                     portList.remove(portId);
460                     builder.setPortList(portList);
461                 }
462
463                 subnetmap = builder.build();
464                 LOG.debug("Removing from existing subnetmap node: {} ", subnetId.getValue());
465                 SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, id,
466                     subnetmap);
467             } else {
468                 LOG.warn("removing from non-existing subnetmap node: {} ", subnetId.getValue());
469             }
470         } catch (ReadFailedException | TransactionCommitFailedException e) {
471             LOG.error("Removal from subnetmap failed for node: {}", subnetId.getValue());
472         } finally {
473             lock.unlock();
474         }
475         return subnetmap;
476     }
477
478     @Nullable
479     protected Subnetmap removePortsFromSubnetmapNode(Uuid subnetId, @Nullable Uuid portId,
480             @Nullable Uuid directPortId) {
481         Subnetmap subnetmap = null;
482         InstanceIdentifier<Subnetmap> id = InstanceIdentifier.builder(Subnetmaps.class).child(Subnetmap.class,
483                 new SubnetmapKey(subnetId)).build();
484         final ReentrantLock lock = lockForUuid(subnetId);
485         lock.lock();
486         try {
487             Optional<Subnetmap> sn =
488                     SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
489                         id);
490             if (sn.isPresent()) {
491                 SubnetmapBuilder builder = new SubnetmapBuilder(sn.get());
492                 if (null != portId && null != builder.getPortList()) {
493                     List<Uuid> portList = builder.getPortList();
494                     portList.remove(portId);
495                     builder.setPortList(portList);
496                     LOG.debug("Removing port {} from existing subnetmap node: {} ", portId.getValue(),
497                         subnetId.getValue());
498                 }
499                 if (null != directPortId && null != builder.getDirectPortList()) {
500                     List<Uuid> directPortList = builder.getDirectPortList();
501                     directPortList.remove(directPortId);
502                     builder.setDirectPortList(directPortList);
503                     LOG.debug("Removing direct port {} from existing subnetmap node: {} ", directPortId
504                         .getValue(), subnetId.getValue());
505                 }
506                 subnetmap = builder.build();
507                 SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, id,
508                     subnetmap);
509             } else {
510                 LOG.info("Trying to remove port from non-existing subnetmap node {}", subnetId.getValue());
511             }
512         } catch (ReadFailedException | TransactionCommitFailedException e) {
513             LOG.error("Removing a port from port list of a subnetmap failed for node: {}",
514                     subnetId.getValue(), e);
515         } finally {
516             lock.unlock();
517         }
518         return subnetmap;
519     }
520
521     // TODO Clean up the exception handling
522     @SuppressWarnings("checkstyle:IllegalCatch")
523     protected void deleteSubnetMapNode(Uuid subnetId) {
524         InstanceIdentifier<Subnetmap> subnetMapIdentifier =
525                 InstanceIdentifier.builder(Subnetmaps.class).child(Subnetmap.class, new SubnetmapKey(subnetId)).build();
526         LOG.debug("removing subnetMap node: {} ", subnetId.getValue());
527         final ReentrantLock lock = lockForUuid(subnetId);
528         lock.lock();
529         try {
530             SingleTransactionDataBroker.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, subnetMapIdentifier);
531         } catch (TransactionCommitFailedException e) {
532             LOG.error("Delete subnetMap node failed for subnet : {} ", subnetId.getValue());
533         } finally {
534             lock.unlock();
535         }
536     }
537
538     public void updateVpnInstanceWithRDs(String vpnInstanceId, final List<String> rds) {
539         InstanceIdentifier<VpnInstance> vpnIdentifier = InstanceIdentifier.builder(VpnInstances.class)
540             .child(VpnInstance.class, new VpnInstanceKey(vpnInstanceId)).build();
541         try {
542             Optional<VpnInstance> vpnInstanceConfig =
543                     SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
544                             vpnIdentifier);
545             if (!vpnInstanceConfig.isPresent()) {
546                 LOG.debug("No VpnInstance present under config vpnInstance:{}", vpnInstanceId);
547                 return;
548             }
549             VpnInstance vpnInstance = vpnInstanceConfig.get();
550             VpnInstanceBuilder updateVpnInstanceBuilder = new VpnInstanceBuilder(vpnInstance);
551             if (vpnInstance.getIpv4Family() != null) {
552                 Ipv4FamilyBuilder ipv4FamilyBuilder = new Ipv4FamilyBuilder(vpnInstance.getIpv4Family());
553                 updateVpnInstanceBuilder.setIpv4Family(ipv4FamilyBuilder.setRouteDistinguisher(rds).build());
554             }
555             if (vpnInstance.getIpv6Family() != null) {
556                 Ipv6FamilyBuilder ipv6FamilyBuilder = new Ipv6FamilyBuilder(vpnInstance.getIpv6Family());
557                 updateVpnInstanceBuilder.setIpv6Family(ipv6FamilyBuilder.setRouteDistinguisher(rds).build());
558             }
559             LOG.debug("Updating Config vpn-instance: {} with the list of RDs: {}", vpnInstanceId, rds);
560             SingleTransactionDataBroker.syncUpdate(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnIdentifier,
561                     updateVpnInstanceBuilder.build());
562         } catch (ReadFailedException | TransactionCommitFailedException ex) {
563             LOG.warn("Error configuring feature ", ex);
564         }
565     }
566
567     private void updateVpnInstanceNode(Uuid vpnId, List<String> rd, List<String> irt, List<String> ert,
568                                        VpnInstance.Type type, long l3vni, IpVersionChoice ipVersion) {
569         String vpnName = vpnId.getValue();
570         VpnInstanceBuilder builder = null;
571         List<VpnTarget> vpnTargetList = new ArrayList<>();
572         boolean isLockAcquired = false;
573         InstanceIdentifier<VpnInstance> vpnIdentifier = InstanceIdentifier.builder(VpnInstances.class)
574             .child(VpnInstance.class, new VpnInstanceKey(vpnName)).build();
575         try {
576             Optional<VpnInstance> optionalVpn =
577                     SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
578                             vpnIdentifier);
579             LOG.debug("Creating/Updating a new vpn-instance node: {} ", vpnName);
580             if (optionalVpn.isPresent()) {
581                 builder = new VpnInstanceBuilder(optionalVpn.get());
582                 LOG.debug("updating existing vpninstance node");
583             } else {
584                 builder = new VpnInstanceBuilder().withKey(new VpnInstanceKey(vpnName)).setVpnInstanceName(vpnName)
585                         .setType(type).setL3vni(l3vni);
586             }
587             if (irt != null && !irt.isEmpty()) {
588                 if (ert != null && !ert.isEmpty()) {
589                     List<String> commonRT = new ArrayList<>(irt);
590                     commonRT.retainAll(ert);
591
592                     for (String common : commonRT) {
593                         irt.remove(common);
594                         ert.remove(common);
595                         VpnTarget vpnTarget =
596                                 new VpnTargetBuilder().withKey(new VpnTargetKey(common)).setVrfRTValue(common)
597                                         .setVrfRTType(VpnTarget.VrfRTType.Both).build();
598                         vpnTargetList.add(vpnTarget);
599                     }
600                 }
601                 for (String importRT : irt) {
602                     VpnTarget vpnTarget =
603                             new VpnTargetBuilder().withKey(new VpnTargetKey(importRT)).setVrfRTValue(importRT)
604                                     .setVrfRTType(VpnTarget.VrfRTType.ImportExtcommunity).build();
605                     vpnTargetList.add(vpnTarget);
606                 }
607             }
608
609             if (ert != null && !ert.isEmpty()) {
610                 for (String exportRT : ert) {
611                     VpnTarget vpnTarget =
612                             new VpnTargetBuilder().withKey(new VpnTargetKey(exportRT)).setVrfRTValue(exportRT)
613                                     .setVrfRTType(VpnTarget.VrfRTType.ExportExtcommunity).build();
614                     vpnTargetList.add(vpnTarget);
615                 }
616             }
617
618             VpnTargets vpnTargets = new VpnTargetsBuilder().setVpnTarget(vpnTargetList).build();
619
620             Ipv4FamilyBuilder ipv4vpnBuilder = new Ipv4FamilyBuilder().setVpnTargets(vpnTargets);
621             Ipv6FamilyBuilder ipv6vpnBuilder = new Ipv6FamilyBuilder().setVpnTargets(vpnTargets);
622
623             if (rd != null && !rd.isEmpty()) {
624                 ipv4vpnBuilder.setRouteDistinguisher(rd);
625                 ipv6vpnBuilder.setRouteDistinguisher(rd);
626             }
627
628             if (ipVersion != null && ipVersion.isIpVersionChosen(IpVersionChoice.IPV4)) {
629                 builder.setIpv4Family(ipv4vpnBuilder.build());
630             }
631             if (ipVersion != null && ipVersion.isIpVersionChosen(IpVersionChoice.IPV6)) {
632                 builder.setIpv6Family(ipv6vpnBuilder.build());
633             }
634             if (ipVersion != null && ipVersion.isIpVersionChosen(IpVersionChoice.UNDEFINED)) {
635                 builder.setIpv4Family(ipv4vpnBuilder.build());
636             }
637             VpnInstance newVpn = builder.build();
638             isLockAcquired = vpnLock.tryLock(vpnId, LOCK_WAIT_TIME, TimeUnit.SECONDS);
639             LOG.debug("Creating/Updating vpn-instance for {} ", vpnName);
640             SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnIdentifier,
641                     newVpn);
642         } catch (ReadFailedException | TransactionCommitFailedException e) {
643             LOG.error("Update VPN Instance node failed for node: {} {} {} {}", vpnName, rd, irt, ert);
644         } finally {
645             if (isLockAcquired) {
646                 vpnLock.unlock(vpnId);
647             }
648         }
649     }
650
651     private void deleteVpnMapsNode(Uuid vpnId) {
652         boolean isLockAcquired = false;
653         InstanceIdentifier<VpnMap> vpnMapIdentifier = InstanceIdentifier.builder(VpnMaps.class)
654                 .child(VpnMap.class, new VpnMapKey(vpnId))
655                 .build();
656         LOG.debug("removing vpnMaps node: {} ", vpnId.getValue());
657         try {
658             isLockAcquired = vpnLock.tryLock(vpnId, LOCK_WAIT_TIME, TimeUnit.SECONDS);
659             SingleTransactionDataBroker.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnMapIdentifier);
660         } catch (TransactionCommitFailedException e) {
661             LOG.error("Delete vpnMaps node failed for vpn : {} ", vpnId.getValue());
662         } finally {
663             if (isLockAcquired) {
664                 vpnLock.unlock(vpnId);
665             }
666         }
667     }
668
669     protected void updateVpnMaps(Uuid vpnId, @Nullable String name, @Nullable Uuid router, @Nullable Uuid tenantId,
670             @Nullable List<Uuid> networks) {
671         VpnMapBuilder builder;
672         boolean isLockAcquired = false;
673         InstanceIdentifier<VpnMap> vpnMapIdentifier = InstanceIdentifier.builder(VpnMaps.class)
674                 .child(VpnMap.class, new VpnMapKey(vpnId))
675                 .build();
676         try {
677             Optional<VpnMap> optionalVpnMap =
678                     SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
679                             vpnMapIdentifier);
680             if (optionalVpnMap.isPresent()) {
681                 builder = new VpnMapBuilder(optionalVpnMap.get());
682             } else {
683                 builder = new VpnMapBuilder().withKey(new VpnMapKey(vpnId)).setVpnId(vpnId);
684             }
685
686             if (name != null) {
687                 builder.setName(name);
688             }
689             if (tenantId != null) {
690                 builder.setTenantId(tenantId);
691             }
692             if (router != null) {
693                 RouterIds vpnRouterId = new RouterIdsBuilder().setRouterId(router).build();
694                 List<RouterIds> rtrIds = builder.getRouterIds();
695                 if (rtrIds == null) {
696                     rtrIds = Collections.singletonList(vpnRouterId);
697                 } else {
698                     rtrIds.add(vpnRouterId);
699                 }
700                 builder.setRouterIds(rtrIds);
701             }
702             if (networks != null) {
703                 List<Uuid> nwList = builder.getNetworkIds();
704                 if (nwList == null) {
705                     nwList = new ArrayList<>();
706                 }
707                 nwList.addAll(networks);
708                 builder.setNetworkIds(nwList);
709             }
710
711             isLockAcquired = vpnLock.tryLock(vpnId, LOCK_WAIT_TIME, TimeUnit.SECONDS);
712             LOG.debug("Creating/Updating vpnMaps node: {} ", vpnId.getValue());
713             SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnMapIdentifier,
714                     builder.build());
715             LOG.debug("VPNMaps DS updated for VPN {} ", vpnId.getValue());
716         } catch (ReadFailedException | TransactionCommitFailedException e) {
717             LOG.error("UpdateVpnMaps failed for node: {} ", vpnId.getValue());
718         } finally {
719             if (isLockAcquired) {
720                 vpnLock.unlock(vpnId);
721             }
722         }
723     }
724
725     private void clearFromVpnMaps(Uuid vpnId, @Nullable Uuid routerId, @Nullable List<Uuid> networkIds) {
726         boolean isLockAcquired = false;
727         InstanceIdentifier<VpnMap> vpnMapIdentifier = InstanceIdentifier.builder(VpnMaps.class)
728                 .child(VpnMap.class, new VpnMapKey(vpnId))
729                 .build();
730         Optional<VpnMap> optionalVpnMap;
731         try {
732             optionalVpnMap =
733                     SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
734                             vpnMapIdentifier);
735         } catch (ReadFailedException e) {
736             LOG.error("Error reading the VPN map for {}", vpnMapIdentifier, e);
737             return;
738         }
739         if (optionalVpnMap.isPresent()) {
740             VpnMap vpnMap = optionalVpnMap.get();
741             VpnMapBuilder vpnMapBuilder = new VpnMapBuilder(vpnMap);
742             List<RouterIds> rtrIds = vpnMap.getRouterIds();
743             if (rtrIds == null) {
744                 rtrIds = new ArrayList<>();
745             }
746             if (routerId != null) {
747                 if (vpnMap.getNetworkIds() == null && routerId.equals(vpnMap.getVpnId())) {
748                     rtrIds.add(new RouterIdsBuilder().setRouterId(routerId).build());
749                     vpnMapBuilder.setRouterIds(rtrIds);
750                     try {
751                         // remove entire node in case of internal VPN
752                         isLockAcquired = vpnLock.tryLock(vpnId, LOCK_WAIT_TIME, TimeUnit.SECONDS);
753                         LOG.debug("removing vpnMaps node: {} ", vpnId);
754                         SingleTransactionDataBroker.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION,
755                                 vpnMapIdentifier);
756                     } catch (TransactionCommitFailedException e) {
757                         LOG.error("Deletion of vpnMaps node failed for vpn {}", vpnId.getValue());
758                     } finally {
759                         if (isLockAcquired) {
760                             vpnLock.unlock(vpnId);
761                         }
762                     }
763                     return;
764                 } else if (vpnMap.getNetworkIds() == null && !routerId.equals(vpnMap.getVpnId())) {
765                     rtrIds.remove(new RouterIdsBuilder().setRouterId(routerId).build());
766                     vpnMapBuilder.setRouterIds(rtrIds);
767                     LOG.debug("Removing routerId {} in vpnMaps for the vpn {}", routerId, vpnId.getValue());
768                 }
769             }
770             if (networkIds != null) {
771                 List<Uuid> vpnNw = vpnMap.getNetworkIds();
772                 vpnNw.removeAll(networkIds);
773                 if (vpnNw.isEmpty()) {
774                     LOG.debug("setting networks null in vpnMaps node: {} ", vpnId.getValue());
775                     vpnMapBuilder.setNetworkIds(null);
776                 } else {
777                     vpnMapBuilder.setNetworkIds(vpnNw);
778                 }
779             }
780
781             try {
782                 isLockAcquired = vpnLock.tryLock(vpnId, LOCK_WAIT_TIME, TimeUnit.SECONDS);
783                 LOG.debug("clearing from vpnMaps node: {} ", vpnId.getValue());
784                 SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnMapIdentifier,
785                         vpnMapBuilder.build());
786             } catch (TransactionCommitFailedException e) {
787                 LOG.error("Clearing from vpnMaps node failed for vpn {}", vpnId.getValue());
788             } finally {
789                 if (isLockAcquired) {
790                     vpnLock.unlock(vpnId);
791                 }
792             }
793         } else {
794             LOG.error("VPN : {} not found", vpnId.getValue());
795         }
796         LOG.debug("Clear from VPNMaps DS successful for VPN {} ", vpnId.getValue());
797     }
798
799     private void deleteVpnInstance(Uuid vpnId) {
800         boolean isLockAcquired = false;
801         InstanceIdentifier<VpnInstance> vpnIdentifier = InstanceIdentifier.builder(VpnInstances.class)
802                 .child(VpnInstance.class,
803                         new VpnInstanceKey(vpnId.getValue()))
804                 .build();
805         try {
806             isLockAcquired = vpnLock.tryLock(vpnId, LOCK_WAIT_TIME, TimeUnit.SECONDS);
807             LOG.debug("Deleting vpnInstance {}", vpnId.getValue());
808             SingleTransactionDataBroker.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnIdentifier);
809         } catch (TransactionCommitFailedException e) {
810             LOG.error("Deletion of VPNInstance node failed for VPN {}", vpnId.getValue());
811         } finally {
812             if (isLockAcquired) {
813                 vpnLock.unlock(vpnId);
814             }
815         }
816     }
817
818     protected Adjacencies createPortIpAdjacencies(Port port, Boolean isRouterInterface,
819                                                   TypedWriteTransaction<Configuration> wrtConfigTxn,
820                                                   @Nullable Subnetmap sn, @Nullable VpnInterface vpnIface) {
821         List<Adjacency> adjList = new ArrayList<>();
822         if (vpnIface != null) {
823             adjList = vpnIface.augmentation(Adjacencies.class).getAdjacency();
824         }
825         String infName = port.getUuid().getValue();
826         LOG.trace("neutronVpnManager: create config adjacencies for Port: {}", infName);
827         for (FixedIps ip : requireNonNullElse(port.getFixedIps(), Collections.<FixedIps>emptyList())) {
828             String ipValue = ip.getIpAddress().stringValue();
829             String ipPrefix = ip.getIpAddress().getIpv4Address() != null ? ipValue + "/32" : ipValue + "/128";
830             Subnetmap snTemp = neutronvpnUtils.getSubnetmap(ip.getSubnetId());
831             if (snTemp != null && !FibHelper.doesPrefixBelongToSubnet(ipPrefix,
832                     snTemp.getSubnetIp(), false)) {
833                 continue;
834             }
835             Uuid vpnId = snTemp != null ? snTemp.getVpnId() : null;
836             if (vpnId != null) {
837                 neutronvpnUtils.createVpnPortFixedIpToPort(vpnId.getValue(), ipValue,
838                         infName, port.getMacAddress().getValue(), isRouterInterface, wrtConfigTxn);
839                 //Create Neutron port adjacency if VPN presence is existing for subnet
840                 Adjacency vmAdj = new AdjacencyBuilder().withKey(new AdjacencyKey(ipPrefix)).setIpAddress(ipPrefix)
841                         .setMacAddress(port.getMacAddress().getValue()).setAdjacencyType(AdjacencyType.PrimaryAdjacency)
842                         .setSubnetId(ip.getSubnetId()).build();
843                 if (!adjList.contains(vmAdj)) {
844                     adjList.add(vmAdj);
845                 }
846             }
847             Uuid routerId = snTemp != null ? snTemp.getRouterId() : null;
848             if (snTemp != null && snTemp.getInternetVpnId() != null) {
849                 neutronvpnUtils.createVpnPortFixedIpToPort(snTemp.getInternetVpnId().getValue(),
850                     ipValue, infName, port.getMacAddress().getValue(), isRouterInterface, wrtConfigTxn);
851             }
852             if (routerId != null) {
853                 Router rtr = neutronvpnUtils.getNeutronRouter(routerId);
854                 if (rtr != null && rtr.getRoutes() != null) {
855                     List<Routes> routeList = rtr.getRoutes();
856                     // create extraroute Adjacence for each ipValue,
857                     // because router can have IPv4 and IPv6 subnet ports, or can have
858                     // more that one IPv4 subnet port or more than one IPv6 subnet port
859                     List<Adjacency> erAdjList = getAdjacencyforExtraRoute(routeList, ipValue);
860                     if (!erAdjList.isEmpty()) {
861                         adjList.addAll(erAdjList);
862                     }
863                 }
864             }
865         }
866         return new AdjacenciesBuilder().setAdjacency(adjList).build();
867     }
868
869     protected void createVpnInterface(Collection<Uuid> vpnIds, Port port,
870                                       @Nullable TypedWriteTransaction<Configuration> wrtConfigTxn) {
871         boolean isRouterInterface = false;
872         if (port.getDeviceOwner() != null) {
873             isRouterInterface = NeutronConstants.DEVICE_OWNER_ROUTER_INF.equals(port.getDeviceOwner());
874         }
875         String infName = port.getUuid().getValue();
876         // Handling cluster reboot scenario where VpnInterface already exists in datastore.
877         VpnInterface vpnIface = VpnHelper.getVpnInterface(dataBroker, infName);
878         Adjacencies adjs = createPortIpAdjacencies(port, isRouterInterface, wrtConfigTxn, null, vpnIface);
879         LOG.trace("createVpnInterface for Port: {}, isRouterInterface: {}", infName, isRouterInterface);
880         writeVpnInterfaceToDs(vpnIds, infName, adjs, port.getNetworkId(), isRouterInterface, wrtConfigTxn);
881     }
882
883     protected void withdrawPortIpFromVpnIface(Uuid vpnId, Uuid internetVpnId,
884                        Port port, Subnetmap sn, TypedWriteTransaction<Configuration> wrtConfigTxn) {
885         String infName = port.getUuid().getValue();
886         InstanceIdentifier<VpnInterface> vpnIfIdentifier = NeutronvpnUtils.buildVpnInterfaceIdentifier(infName);
887         Optional<VpnInterface> optionalVpnInterface = null;
888         LOG.debug("withdrawPortIpFromVpnIface vpn {} internetVpn {} Port {}",
889                   vpnId, internetVpnId, infName);
890         try {
891             optionalVpnInterface = SingleTransactionDataBroker
892                     .syncReadOptional(dataBroker, LogicalDatastoreType
893                     .CONFIGURATION, vpnIfIdentifier);
894         } catch (ReadFailedException e) {
895             LOG.error("withdrawPortIpFromVpnIface: Error reading the VPN interface for {}", vpnIfIdentifier, e);
896             return;
897         }
898         if (!optionalVpnInterface.isPresent()) {
899             return;
900         }
901         LOG.trace("withdraw adjacencies for Port: {} subnet {}", port.getUuid().getValue(),
902                 sn != null ? sn.getSubnetIp() : "null");
903         List<Adjacency> vpnAdjsList =
904             requireNonNullElse(optionalVpnInterface.get().augmentation(Adjacencies.class).getAdjacency(),
905                 Collections.emptyList());
906         List<Adjacency> updatedAdjsList = new ArrayList<>();
907         boolean isIpFromAnotherSubnet = false;
908         for (Adjacency adj : vpnAdjsList) {
909             String adjString = FibHelper.getIpFromPrefix(adj.getIpAddress());
910             if (sn == null || !Objects.equals(adj.getSubnetId(), sn.getId())) {
911                 if (adj.getAdjacencyType() == AdjacencyType.PrimaryAdjacency) {
912                     isIpFromAnotherSubnet = true;
913                 }
914                 updatedAdjsList.add(adj);
915                 continue;
916             }
917             if (adj.getAdjacencyType() == AdjacencyType.PrimaryAdjacency) {
918                 LOG.error("withdrawPortIpFromVpnIface: suppressing primaryAdjacency {} FixedIp for vpnId {}",
919                       adjString, vpnId);
920                 if (vpnId != null) {
921                     neutronvpnUtils.removeVpnPortFixedIpToPort(vpnId.getValue(),
922                             String.valueOf(adjString), wrtConfigTxn);
923                 }
924                 if (internetVpnId != null) {
925                     neutronvpnUtils.removeVpnPortFixedIpToPort(internetVpnId.getValue(),
926                           String.valueOf(adjString), wrtConfigTxn);
927                 }
928             } else {
929                 if (NeutronConstants.DEVICE_OWNER_ROUTER_INF.equals(port.getDeviceOwner())
930                         && sn.getRouterId() != null) {
931                     Router rtr = neutronvpnUtils.getNeutronRouter(sn.getRouterId());
932                     if (rtr != null && rtr.getRoutes() != null) {
933                         List<Routes> extraRoutesToRemove = new ArrayList<>();
934                         for (Routes rt: rtr.getRoutes()) {
935                             if (rt.getNexthop().toString().equals(adjString)) {
936                                 extraRoutesToRemove.add(rt);
937                             }
938                         }
939                         if (vpnId != null) {
940                             LOG.error("withdrawPortIpFromVpnIface: suppressing extraRoute {} for vpnId {}",
941                                   extraRoutesToRemove, vpnId);
942                             removeAdjacencyforExtraRoute(vpnId, extraRoutesToRemove);
943                         }
944                         /* removeAdjacencyforExtraRoute done also for internet-vpn-id, in previous call */
945                     }
946                 }
947             }
948         }
949         Adjacencies adjacencies = new AdjacenciesBuilder().setAdjacency(updatedAdjsList).build();
950         if (vpnId != null) {
951             updateVpnInterfaceWithAdjacencies(vpnId, infName, adjacencies, wrtConfigTxn);
952         }
953         if (!isIpFromAnotherSubnet) {
954             // no more subnetworks for neutron port
955             if (sn != null && sn.getRouterId() != null) {
956                 removeFromNeutronRouterInterfacesMap(sn.getRouterId(), port.getUuid().getValue());
957             }
958             deleteVpnInterface(infName, null /* vpn-id */, wrtConfigTxn);
959             return;
960         }
961         return;
962     }
963
964     // TODO Clean up the exception handling
965     @SuppressWarnings("checkstyle:IllegalCatch")
966     protected void deleteVpnInterface(String infName, @Nullable String vpnId,
967                                       @Nullable TypedWriteTransaction<Configuration> wrtConfigTxn) {
968         if (wrtConfigTxn == null) {
969             ListenableFutures.addErrorLogging(
970                     txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
971                         tx -> deleteVpnInterface(infName, vpnId, tx)),
972                     LOG, "Error deleting VPN interface {} {}", infName, vpnId);
973             return;
974         }
975
976         InstanceIdentifier<VpnInterface> vpnIfIdentifier =
977             NeutronvpnUtils.buildVpnInterfaceIdentifier(infName);
978         Optional<VpnInterface> optionalVpnInterface;
979         try {
980             optionalVpnInterface =
981                 SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
982                             vpnIfIdentifier);
983         } catch (ReadFailedException ex) {
984             LOG.error("Error during deletion of vpninterface {}", infName, ex);
985             return;
986         }
987         if (!optionalVpnInterface.isPresent()) {
988             LOG.warn("Deletion of vpninterface {}, optionalVpnInterface is not present()", infName);
989             return;
990         }
991         if (vpnId != null) {
992             VpnInterface vpnInterface = optionalVpnInterface.get();
993             List<VpnInstanceNames> vpnList = vpnInterface.getVpnInstanceNames();
994             if (vpnList != null
995                 && VpnHelper.doesVpnInterfaceBelongToVpnInstance(vpnId, vpnList)) {
996                 VpnHelper.removeVpnInterfaceVpnInstanceNamesFromList(vpnId, vpnList);
997                 if (!vpnList.isEmpty()) {
998                     LOG.debug("Deleting vpn interface {} not immediately since vpnInstanceName "
999                             + "List not empty", infName);
1000                     return;
1001                 }
1002                 VpnInterfaceBuilder vpnIfBuilder = new VpnInterfaceBuilder(optionalVpnInterface.get())
1003                         .setVpnInstanceNames(vpnList);
1004                 wrtConfigTxn.put(vpnIfIdentifier, vpnIfBuilder
1005                         .build());
1006             }
1007         }
1008         LOG.debug("Deleting vpn interface {}", infName);
1009         wrtConfigTxn.delete(vpnIfIdentifier);
1010     }
1011
1012     protected void removeInternetVpnFromVpnInterface(Uuid vpnId, Port port,
1013                                              TypedWriteTransaction<Configuration> writeConfigTxn,
1014                                              Subnetmap sm) {
1015         if (vpnId == null || port == null) {
1016             return;
1017         }
1018         String infName = port.getUuid().getValue();
1019         InstanceIdentifier<VpnInterface> vpnIfIdentifier = NeutronvpnUtils.buildVpnInterfaceIdentifier(infName);
1020         try {
1021             Optional<VpnInterface> optionalVpnInterface = SingleTransactionDataBroker
1022                     .syncReadOptional(dataBroker, LogicalDatastoreType
1023                     .CONFIGURATION, vpnIfIdentifier);
1024             if (optionalVpnInterface.isPresent()) {
1025                 List<VpnInstanceNames> listVpn = optionalVpnInterface.get().getVpnInstanceNames();
1026                 if (listVpn != null
1027                     && VpnHelper.doesVpnInterfaceBelongToVpnInstance(vpnId.getValue(), listVpn)) {
1028                     VpnHelper.removeVpnInterfaceVpnInstanceNamesFromList(vpnId.getValue(), listVpn);
1029                 }
1030                 VpnInterfaceBuilder vpnIfBuilder = new VpnInterfaceBuilder(optionalVpnInterface.get())
1031                          .setVpnInstanceNames(listVpn);
1032                 Adjacencies adjs = vpnIfBuilder.augmentation(Adjacencies.class);
1033                 LOG.debug("Updating vpn interface {}", infName);
1034                 List<Adjacency> adjacencyList = adjs != null ? adjs.getAdjacency() : new ArrayList<>();
1035                 Iterator<Adjacency> adjacencyIter = adjacencyList.iterator();
1036                 while (adjacencyIter.hasNext()) {
1037                     Adjacency adjacency = adjacencyIter.next();
1038                     if (adjacency.getAdjacencyType() == AdjacencyType.PrimaryAdjacency) {
1039                         continue;
1040                     }
1041                     String mipToQuery = adjacency.getIpAddress().split("/")[0];
1042                     InstanceIdentifier<LearntVpnVipToPort> id =
1043                         NeutronvpnUtils.buildLearntVpnVipToPortIdentifier(vpnId.getValue(), mipToQuery);
1044                     Optional<LearntVpnVipToPort> optionalVpnVipToPort =
1045                             SingleTransactionDataBroker.syncReadOptional(dataBroker,
1046                                     LogicalDatastoreType.OPERATIONAL, id);
1047                     if (optionalVpnVipToPort.isPresent()) {
1048                         LOG.trace("Removing adjacencies from vpninterface {} upon dissociation of router {}",
1049                              infName, vpnId);
1050                         if (listVpn == null || listVpn.isEmpty()) {
1051                             adjacencyIter.remove();
1052                         }
1053                         neutronvpnUtils.removeLearntVpnVipToPort(vpnId.getValue(), mipToQuery);
1054                         LOG.trace("Entry for fixedIP {} for port {} on VPN {} removed from VpnPortFixedIPToPortData",
1055                                 mipToQuery, infName, vpnId.getValue());
1056                     }
1057                 }
1058                 for (FixedIps ip : requireNonNullElse(port.getFixedIps(), Collections.<FixedIps>emptyList())) {
1059                     String ipValue = ip.getIpAddress().stringValue();
1060                     //skip IPv4 address
1061                     if (!NeutronvpnUtils.getIpVersionFromString(ipValue).isIpVersionChosen(IpVersionChoice.IPV6)) {
1062                         continue;
1063                     }
1064                     neutronvpnUtils.removeVpnPortFixedIpToPort(vpnId.getValue(),
1065                             ipValue, writeConfigTxn);
1066                 }
1067                 if (listVpn == null || listVpn.isEmpty()) {
1068                     if (sm != null && sm.getRouterId() != null) {
1069                         removeFromNeutronRouterInterfacesMap(sm.getRouterId(), port.getUuid().getValue());
1070                     }
1071                     deleteVpnInterface(port.getUuid().getValue(), null /* vpn-id */, writeConfigTxn);
1072                 } else {
1073                     writeConfigTxn.put(vpnIfIdentifier, vpnIfBuilder.build());
1074                 }
1075             } else {
1076                 LOG.info("removeVpnFromVpnInterface: VPN Interface {} not found", infName);
1077             }
1078         } catch (ReadFailedException ex) {
1079             LOG.error("Update of vpninterface {} failed", infName, ex);
1080         }
1081     }
1082
1083     protected void updateVpnInterface(Uuid vpnId, @Nullable Uuid oldVpnId, Port port, boolean isBeingAssociated,
1084                                       boolean isSubnetIp,
1085                                       TypedWriteTransaction<Configuration> writeConfigTxn,
1086                                       boolean isInternetVpn) {
1087         if (vpnId == null || port == null) {
1088             return;
1089         }
1090         boolean isLockAcquired = false;
1091         String infName = port.getUuid().getValue();
1092         InstanceIdentifier<VpnInterface> vpnIfIdentifier = NeutronvpnUtils.buildVpnInterfaceIdentifier(infName);
1093
1094         try {
1095             isLockAcquired = interfaceLock.tryLock(infName, LOCK_WAIT_TIME, TimeUnit.SECONDS);
1096             Optional<VpnInterface> optionalVpnInterface =
1097                     SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
1098                             vpnIfIdentifier);
1099             if (optionalVpnInterface.isPresent()) {
1100                 VpnInstanceNames vpnInstance = VpnHelper
1101                     .getVpnInterfaceVpnInstanceNames(vpnId.getValue(), AssociatedSubnetType.V4AndV6Subnets);
1102                 List<VpnInstanceNames> listVpn = new ArrayList<>(optionalVpnInterface
1103                            .get().getVpnInstanceNames());
1104                 if (oldVpnId != null
1105                     && VpnHelper.doesVpnInterfaceBelongToVpnInstance(oldVpnId.getValue(), listVpn)) {
1106                     VpnHelper.removeVpnInterfaceVpnInstanceNamesFromList(oldVpnId.getValue(), listVpn);
1107                 }
1108                 if (vpnId.getValue() != null
1109                     && !VpnHelper.doesVpnInterfaceBelongToVpnInstance(vpnId.getValue(), listVpn)) {
1110                     listVpn.add(vpnInstance);
1111                 }
1112                 VpnInterfaceBuilder vpnIfBuilder = new VpnInterfaceBuilder(optionalVpnInterface.get())
1113                         .setVpnInstanceNames(listVpn);
1114                 LOG.debug("Updating vpn interface {}", infName);
1115                 if (!isBeingAssociated) {
1116                     Adjacencies adjs = vpnIfBuilder.augmentation(Adjacencies.class);
1117                     List<Adjacency> adjacencyList = adjs != null ? adjs.getAdjacency() : new ArrayList<>();
1118                     Iterator<Adjacency> adjacencyIter = adjacencyList.iterator();
1119                     while (adjacencyIter.hasNext()) {
1120                         Adjacency adjacency = adjacencyIter.next();
1121                         String mipToQuery = adjacency.getIpAddress().split("/")[0];
1122                         InstanceIdentifier<LearntVpnVipToPort> id =
1123                             NeutronvpnUtils.buildLearntVpnVipToPortIdentifier(oldVpnId.getValue(), mipToQuery);
1124                         Optional<LearntVpnVipToPort> optionalVpnVipToPort =
1125                                 SingleTransactionDataBroker.syncReadOptional(dataBroker,
1126                                         LogicalDatastoreType.OPERATIONAL, id);
1127                         if (optionalVpnVipToPort.isPresent()) {
1128                             LOG.trace("Removing adjacencies from vpninterface {} upon dissociation of router {} "
1129                                 + "from VPN {}", infName, vpnId, oldVpnId);
1130                             adjacencyIter.remove();
1131                             neutronvpnUtils.removeLearntVpnVipToPort(oldVpnId.getValue(), mipToQuery);
1132                             LOG.trace(
1133                                     "Entry for fixedIP {} for port {} on VPN {} removed from VpnPortFixedIPToPortData",
1134                                     mipToQuery, infName, vpnId.getValue());
1135                         }
1136                     }
1137                     Adjacencies adjacencies = new AdjacenciesBuilder().setAdjacency(adjacencyList).build();
1138                     vpnIfBuilder.addAugmentation(Adjacencies.class, adjacencies);
1139                 }
1140                 for (FixedIps ip : requireNonNullElse(port.getFixedIps(), Collections.<FixedIps>emptyList())) {
1141                     String ipValue = ip.getIpAddress().stringValue();
1142                     if (oldVpnId != null) {
1143                         neutronvpnUtils.removeVpnPortFixedIpToPort(oldVpnId.getValue(),
1144                                 ipValue, writeConfigTxn);
1145                     }
1146                     if ((NeutronvpnUtils.getIpVersionFromString(ipValue) != IpVersionChoice.IPV6)
1147                          && (isInternetVpn == true)) {
1148                         continue;
1149                     }
1150
1151                     neutronvpnUtils.createVpnPortFixedIpToPort(vpnId.getValue(), ipValue, infName, port
1152                             .getMacAddress().getValue(), isSubnetIp, writeConfigTxn);
1153                 }
1154                 writeConfigTxn.put(vpnIfIdentifier, vpnIfBuilder.build());
1155             } else {
1156                 LOG.error("VPN Interface {} not found", infName);
1157             }
1158         } catch (ReadFailedException ex) {
1159             LOG.error("Updation of vpninterface {} failed", infName, ex);
1160         } finally {
1161             if (isLockAcquired) {
1162                 interfaceLock.unlock(infName);
1163             }
1164         }
1165     }
1166
1167     public void createL3InternalVpn(Uuid vpn, @Nullable String name, @Nullable Uuid tenant, @Nullable List<String> rd,
1168             @Nullable List<String> irt, @Nullable List<String> ert, @Nullable Uuid router,
1169             @Nullable List<Uuid> networks) {
1170
1171         IpVersionChoice ipVersChoices = neutronvpnUtils.getIpVersionChoicesFromRouterUuid(router);
1172
1173         // Update VPN Instance node
1174         updateVpnInstanceNode(vpn, rd, irt, ert, VpnInstance.Type.L3, 0 /*l3vni*/, ipVersChoices);
1175
1176         // Update local vpn-subnet DS
1177         updateVpnMaps(vpn, name, router, tenant, networks);
1178
1179         if (router != null) {
1180             Uuid existingVpn = neutronvpnUtils.getVpnForRouter(router, true);
1181             if (existingVpn != null) {
1182                 // use case when a cluster is rebooted and router add DCN is received, triggering #createL3InternalVpn
1183                 // if before reboot, router was already associated to VPN, should not proceed associating router to
1184                 // internal VPN. Adding to RouterInterfacesMap is also not needed since it's a config DS and will be
1185                 // preserved upon reboot.
1186                 // For a non-reboot case #associateRouterToInternalVPN already takes care of adding to
1187                 // RouterInterfacesMap via #createVPNInterface call.
1188                 LOG.info("Associating router to Internal VPN skipped for VPN {} due to router {} already associated "
1189                     + "to external VPN {}", vpn.getValue(), router.getValue(), existingVpn.getValue());
1190                 return;
1191             }
1192             associateRouterToInternalVpn(vpn, router);
1193         }
1194     }
1195
1196     /**
1197      * Performs the creation of a Neutron L3VPN, associating the new VPN to the
1198      * specified Neutron Networks and Routers.
1199      *
1200      * @param vpnId Uuid of the VPN tp be created
1201      * @param name Representative name of the new VPN
1202      * @param tenantId Uuid of the Tenant under which the VPN is going to be created
1203      * @param rdList Route-distinguisher for the VPN
1204      * @param irtList A list of Import Route Targets
1205      * @param ertList A list of Export Route Targets
1206      * @param routerIdsList ist of neutron router Id to associate with created VPN
1207      * @param networkList UUID of the neutron network the VPN may be associated to
1208      * @param type Type of the VPN Instance
1209      * @param l3vni L3VNI for the VPN Instance using VxLAN as the underlay
1210      * @throws Exception if association of L3VPN failed
1211      */
1212     public void createVpn(Uuid vpnId, String name, Uuid tenantId, List<String> rdList, List<String> irtList,
1213                     List<String> ertList,  @Nullable List<Uuid> routerIdsList, @Nullable List<Uuid> networkList,
1214                     VpnInstance.Type type, long l3vni) throws Exception {
1215
1216         IpVersionChoice ipVersChoices = IpVersionChoice.UNDEFINED;
1217
1218         if (routerIdsList != null && !routerIdsList.isEmpty()) {
1219             for (Uuid routerId : routerIdsList) {
1220                 IpVersionChoice vers = neutronvpnUtils.getIpVersionChoicesFromRouterUuid(routerId);
1221                 ipVersChoices = ipVersChoices.addVersion(vers);
1222             }
1223         }
1224         updateVpnInstanceNode(vpnId, rdList, irtList, ertList, type, l3vni, ipVersChoices);
1225
1226         // Please note that router and networks will be filled into VPNMaps
1227         // by subsequent calls here to associateRouterToVpn and
1228         // associateNetworksToVpn
1229         updateVpnMaps(vpnId, name, null, tenantId, null);
1230         LOG.debug("Created L3VPN with ID {}, name {}, tenantID {}, RDList {}, iRTList {}, eRTList{}, routerIdsList {}, "
1231                         + "networkList {}", vpnId.getValue(), name, tenantId, rdList, irtList, ertList, routerIdsList,
1232                 networkList);
1233
1234         if (routerIdsList != null && !routerIdsList.isEmpty()) {
1235             for (Uuid routerId : routerIdsList) {
1236                 associateRouterToVpn(vpnId, routerId);
1237             }
1238         }
1239         if (networkList != null) {
1240             List<String> failStrings = associateNetworksToVpn(vpnId, networkList);
1241             if (!failStrings.isEmpty()) {
1242                 LOG.error("VPN {} association to networks failed for networks: {}. ",
1243                         vpnId.getValue(), failStrings.toString());
1244                 throw new Exception(failStrings.toString());
1245             }
1246         }
1247     }
1248
1249     /**
1250      * It handles the invocations to the createVPN RPC method.
1251      */
1252     @Override
1253     // TODO Clean up the exception handling
1254     @SuppressWarnings("checkstyle:IllegalCatch")
1255     public ListenableFuture<RpcResult<CreateL3VPNOutput>> createL3VPN(CreateL3VPNInput input) {
1256
1257         CreateL3VPNOutputBuilder opBuilder = new CreateL3VPNOutputBuilder();
1258         SettableFuture<RpcResult<CreateL3VPNOutput>> result = SettableFuture.create();
1259         List<RpcError> errorList = new ArrayList<>();
1260         int failurecount = 0;
1261         int warningcount = 0;
1262
1263         List<L3vpn> vpns = input.getL3vpn();
1264         if (vpns == null) {
1265             vpns = Collections.emptyList();
1266         }
1267         for (L3vpn vpn : vpns) {
1268             if (neutronvpnUtils.doesVpnExist(vpn.getId())) {
1269                 errorList.add(RpcResultBuilder.newWarning(ErrorType.PROTOCOL, "invalid-input",
1270                         formatAndLog(LOG::warn,
1271                                 "Creation of L3VPN failed for VPN {} due to VPN with the same ID already present",
1272                                 vpn.getId().getValue())));
1273                 warningcount++;
1274                 continue;
1275             }
1276             if (vpn.getRouteDistinguisher() == null || vpn.getImportRT() == null || vpn.getExportRT() == null) {
1277                 errorList.add(RpcResultBuilder.newWarning(ErrorType.PROTOCOL, "invalid-input",
1278                         formatAndLog(LOG::warn,
1279                                 "Creation of L3VPN failed for VPN {} due to absence of RD/iRT/eRT input",
1280                                 vpn.getId().getValue())));
1281                 warningcount++;
1282                 continue;
1283             }
1284             VpnInstance.Type vpnInstanceType = VpnInstance.Type.L3;
1285             long l3vni = 0;
1286             if (vpn.getL3vni() != null) {
1287                 l3vni = vpn.getL3vni();
1288             }
1289
1290             List<String> existingRDs = neutronvpnUtils.getExistingRDs();
1291             if (existingRDs.contains(vpn.getRouteDistinguisher().get(0))) {
1292                 errorList.add(RpcResultBuilder.newWarning(ErrorType.PROTOCOL, "invalid-input",
1293                         formatAndLog(LOG::warn,
1294                                 "Creation of L3VPN failed for VPN {} as another VPN with the same RD {} "
1295                                         + "is already configured",
1296                                 vpn.getId().getValue(), vpn.getRouteDistinguisher().get(0))));
1297                 warningcount++;
1298                 continue;
1299             }
1300             Optional<String> operationalVpn = getExistingOperationalVpn(vpn.getRouteDistinguisher().get(0));
1301             if (operationalVpn.isPresent()) {
1302                 errorList.add(RpcResultBuilder.newError(ErrorType.APPLICATION, "application-error",
1303                         formatAndLog(LOG::error,
1304                                 "Creation of L3VPN failed for VPN {} as another VPN {} with the same RD {} "
1305                                         + "is still available. Please retry creation of a new vpn with the same RD"
1306                                         + " after a couple of minutes.", vpn.getId().getValue(), operationalVpn.get(),
1307                                 vpn.getRouteDistinguisher().get(0))));
1308                 warningcount++;
1309                 continue;
1310             }
1311             if (vpn.getRouterIds() != null && !vpn.getRouterIds().isEmpty()) {
1312                 List<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.vpn.instance.RouterIds>
1313                         routerIdsList = vpn.getRouterIds();
1314                 for (org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.vpn.instance.RouterIds
1315                         routerId : routerIdsList) {
1316                     if (neutronvpnUtils.getNeutronRouter(routerId.getRouterId()) == null) {
1317                         errorList.add(RpcResultBuilder.newWarning(ErrorType.PROTOCOL, "invalid-input",
1318                                 formatAndLog(LOG::warn, "Creation of L3VPN failed for VPN {} due to absense of routers"
1319                                         + "{}", vpn.getId(), routerId.getRouterId())));
1320                         warningcount++;
1321                         continue;
1322                     }
1323                     Uuid vpnId = neutronvpnUtils.getVpnForRouter(routerId.getRouterId(), true);
1324                     if (vpnId != null) {
1325                         errorList.add(RpcResultBuilder.newWarning(ErrorType.PROTOCOL, "invalid-input",
1326                                 formatAndLog(LOG::warn, "Creation of L3VPN failed for VPN {} due to router {} already "
1327                                                 + "associated to another VPN {}", vpn.getId(), routerId.getRouterId(),
1328                                         vpnId.getValue())));
1329                         warningcount++;
1330                         continue;
1331                     }
1332                 }
1333             }
1334             if (vpn.getNetworkIds() != null) {
1335                 int initialWarningCount = warningcount;
1336                 for (Uuid nw : vpn.getNetworkIds()) {
1337                     Network network = neutronvpnUtils.getNeutronNetwork(nw);
1338                     Uuid vpnId = neutronvpnUtils.getVpnForNetwork(nw);
1339                     if (network == null) {
1340                         errorList.add(RpcResultBuilder.newWarning(ErrorType.PROTOCOL, "invalid-input",
1341                                 formatAndLog(LOG::warn,
1342                                         "Creation of L3VPN failed for VPN {} due to network not found {}",
1343                                         vpn.getId().getValue(), nw.getValue())));
1344                         warningcount++;
1345                     } else if (vpnId != null) {
1346                         errorList.add(RpcResultBuilder.newWarning(ErrorType.PROTOCOL, "invalid-input",
1347                                 formatAndLog(LOG::warn,
1348                                         "Creation of L3VPN failed for VPN {} due to network {} already associated"
1349                                                 + " to another VPN {}", vpn.getId().getValue(), nw.getValue(),
1350                                         vpnId.getValue())));
1351                         warningcount++;
1352                     }
1353                 }
1354                 if (warningcount != initialWarningCount) {
1355                     continue;
1356                 }
1357             }
1358             List<Uuid> rtrIdsList = new ArrayList<>();
1359             if (vpn.getRouterIds() != null && !vpn.getRouterIds().isEmpty()) {
1360                 for (org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.vpn.instance.RouterIds
1361                         rtrId : vpn.getRouterIds()) {
1362                     rtrIdsList.add(rtrId.getRouterId());
1363                 }
1364             }
1365             try {
1366                 LOG.debug("L3VPN add RPC: VpnID {}, name {}, tenantID {}, RDList {}, iRTList {}, eRTList{}, "
1367                                 + "routerIdList {}, networksList {}", vpn.getId().getValue(), vpn.getName(),
1368                         vpn.getTenantId(), vpn.getRouteDistinguisher().toString(), vpn.getImportRT().toString(),
1369                         vpn.getExportRT().toString(), rtrIdsList, vpn.getNetworkIds());
1370                 createVpn(vpn.getId(), vpn.getName(), vpn.getTenantId(), vpn.getRouteDistinguisher(),
1371                         vpn.getImportRT(), vpn.getExportRT(), rtrIdsList, vpn.getNetworkIds(),
1372                         vpnInstanceType, l3vni);
1373             } catch (Exception ex) {
1374                 errorList.add(RpcResultBuilder.newError(ErrorType.APPLICATION,
1375                         formatAndLog(LOG::error, "Creation of VPN failed for VPN {}", vpn.getId().getValue(), ex),
1376                         ex.getMessage()));
1377                 failurecount++;
1378             }
1379         }
1380         // if at least one succeeds; result is success
1381         // if none succeeds; result is failure
1382         if (failurecount + warningcount == vpns.size()) {
1383             result.set(RpcResultBuilder.<CreateL3VPNOutput>failed().withRpcErrors(errorList).build());
1384         } else {
1385             List<String> errorResponseList = new ArrayList<>();
1386             if (!errorList.isEmpty()) {
1387                 for (RpcError rpcError : errorList) {
1388                     errorResponseList.add("ErrorType: " + rpcError.getErrorType() + ", ErrorTag: " + rpcError.getTag()
1389                             + ", ErrorMessage: " + rpcError.getMessage());
1390                 }
1391             } else {
1392                 errorResponseList.add("Operation successful with no errors");
1393             }
1394             opBuilder.setResponse(errorResponseList);
1395             result.set(RpcResultBuilder.<CreateL3VPNOutput>success().withResult(opBuilder.build()).build());
1396         }
1397         return result;
1398     }
1399
1400     /**
1401      * It handles the invocations to the neutronvpn:getL3VPN RPC method.
1402      */
1403     @Override
1404     public ListenableFuture<RpcResult<GetL3VPNOutput>> getL3VPN(GetL3VPNInput input) {
1405
1406         GetL3VPNOutputBuilder opBuilder = new GetL3VPNOutputBuilder();
1407         SettableFuture<RpcResult<GetL3VPNOutput>> result = SettableFuture.create();
1408         Uuid inputVpnId = input.getId();
1409         List<VpnInstance> vpns = new ArrayList<>();
1410         List<L3vpnInstances> l3vpnList = new ArrayList<>();
1411
1412         try {
1413             if (inputVpnId == null) {
1414                 // get all vpns
1415                 InstanceIdentifier<VpnInstances> vpnsIdentifier = InstanceIdentifier.builder(VpnInstances.class)
1416                         .build();
1417                 Optional<VpnInstances> optionalVpns =
1418                         SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
1419                                 vpnsIdentifier);
1420                 if (optionalVpns.isPresent() && !optionalVpns.get().getVpnInstance().isEmpty()) {
1421                     for (VpnInstance vpn : requireNonNullElse(optionalVpns.get().getVpnInstance(),
1422                             Collections.<VpnInstance>emptyList())) {
1423                         // eliminating implicitly created (router and VLAN provider external network specific) VPNs
1424                         // from getL3VPN output
1425                         if (vpn.getIpv4Family().getRouteDistinguisher() != null) {
1426                             vpns.add(vpn);
1427                         }
1428                         if (vpn.getIpv6Family().getRouteDistinguisher() != null) {
1429                             vpns.add(vpn);
1430                         }
1431                     }
1432                 } else {
1433                     // No VPN present
1434                     opBuilder.setL3vpnInstances(l3vpnList);
1435                     result.set(RpcResultBuilder.<GetL3VPNOutput>success().withResult(opBuilder.build()).build());
1436                     return result;
1437                 }
1438             } else {
1439                 String name = inputVpnId.getValue();
1440                 InstanceIdentifier<VpnInstance> vpnIdentifier = InstanceIdentifier.builder(VpnInstances.class)
1441                         .child(VpnInstance.class, new VpnInstanceKey(name)).build();
1442                 // read VpnInstance Info
1443                 Optional<VpnInstance> optionalVpn =
1444                         SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
1445                                 vpnIdentifier);
1446                 // eliminating implicitly created (router or VLAN provider external network specific) VPN from
1447                 // getL3VPN output
1448                 if (optionalVpn.isPresent() && optionalVpn.get().getIpv4Family().getRouteDistinguisher() != null
1449                         || optionalVpn.get().getIpv6Family().getRouteDistinguisher() != null) {
1450                     vpns.add(optionalVpn.get());
1451                 } else {
1452                     result.set(
1453                             RpcResultBuilder.<GetL3VPNOutput>failed().withWarning(ErrorType.PROTOCOL, "invalid-value",
1454                                     formatAndLog(LOG::error, "GetL3VPN failed because VPN {} is not present",
1455                                             name)).build());
1456                 }
1457             }
1458             for (VpnInstance vpnInstance : vpns) {
1459                 Uuid vpnId = new Uuid(vpnInstance.getVpnInstanceName());
1460                 // create VpnMaps id
1461                 L3vpnInstancesBuilder l3vpn = new L3vpnInstancesBuilder();
1462                 List<String> rd = Collections.EMPTY_LIST;
1463                 if (vpnInstance.getIpv4Family().getRouteDistinguisher() != null) {
1464                     rd = vpnInstance.getIpv4Family().getRouteDistinguisher();
1465                 } else if (vpnInstance.getIpv6Family().getRouteDistinguisher() != null) {
1466                     rd = vpnInstance.getIpv6Family().getRouteDistinguisher();
1467                 }
1468                 List<String> ertList = new ArrayList<>();
1469                 List<String> irtList = new ArrayList<>();
1470
1471                 if (vpnInstance.getIpv4Family().getVpnTargets() != null
1472                         || vpnInstance.getIpv6Family().getVpnTargets() != null) {
1473                     List<VpnTarget> vpnTargetList = Collections.EMPTY_LIST;
1474                     if (!vpnInstance.getIpv4Family().getVpnTargets().getVpnTarget().isEmpty()) {
1475                         vpnTargetList = vpnInstance.getIpv4Family().getVpnTargets().getVpnTarget();
1476                     } else if (!vpnInstance.getIpv6Family().getVpnTargets().getVpnTarget().isEmpty()) {
1477                         vpnTargetList = vpnInstance.getIpv6Family().getVpnTargets().getVpnTarget();
1478                     }
1479                     if (!vpnTargetList.isEmpty()) {
1480                         for (VpnTarget vpnTarget : vpnTargetList) {
1481                             if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.ExportExtcommunity) {
1482                                 ertList.add(vpnTarget.getVrfRTValue());
1483                             }
1484                             if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.ImportExtcommunity) {
1485                                 irtList.add(vpnTarget.getVrfRTValue());
1486                             }
1487                             if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.Both) {
1488                                 ertList.add(vpnTarget.getVrfRTValue());
1489                                 irtList.add(vpnTarget.getVrfRTValue());
1490                             }
1491                         }
1492                     }
1493                 }
1494
1495                 l3vpn.setId(vpnId).setRouteDistinguisher(rd).setImportRT(irtList).setExportRT(ertList);
1496
1497                 if (vpnInstance.getL3vni() != null) {
1498                     l3vpn.setL3vni(vpnInstance.getL3vni());
1499                 }
1500                 InstanceIdentifier<VpnMap> vpnMapIdentifier = InstanceIdentifier.builder(VpnMaps.class).child(VpnMap
1501                         .class, new VpnMapKey(vpnId)).build();
1502                 Optional<VpnMap> optionalVpnMap =
1503                         SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
1504                                 vpnMapIdentifier);
1505                 if (optionalVpnMap.isPresent()) {
1506                     VpnMap vpnMap = optionalVpnMap.get();
1507                     List<Uuid> rtrIds = new ArrayList<>();
1508                     if (vpnMap.getRouterIds() != null && !vpnMap.getRouterIds().isEmpty()) {
1509                         for (RouterIds rtrId : vpnMap.getRouterIds()) {
1510                             rtrIds.add(rtrId.getRouterId());
1511                         }
1512                     }
1513                     l3vpn.setRouterIds(NeutronvpnUtils.getVpnInstanceRouterIdsList(rtrIds))
1514                             .setNetworkIds(vpnMap.getNetworkIds()).setTenantId(vpnMap.getTenantId())
1515                             .setName(vpnMap.getName());
1516
1517                 }
1518                 l3vpnList.add(l3vpn.build());
1519             }
1520
1521             opBuilder.setL3vpnInstances(l3vpnList);
1522             result.set(RpcResultBuilder.<GetL3VPNOutput>success().withResult(opBuilder.build()).build());
1523
1524         } catch (ReadFailedException ex) {
1525             result.set(RpcResultBuilder.<GetL3VPNOutput>failed().withError(ErrorType.APPLICATION,
1526                     formatAndLog(LOG::error, "GetVPN failed due to {}", ex.getMessage())).build());
1527         }
1528         return result;
1529     }
1530
1531     /**
1532      * It handles the invocations to the neutronvpn:deleteL3VPN RPC method.
1533      */
1534     @Override
1535     public ListenableFuture<RpcResult<DeleteL3VPNOutput>> deleteL3VPN(DeleteL3VPNInput input) {
1536
1537         DeleteL3VPNOutputBuilder opBuilder = new DeleteL3VPNOutputBuilder();
1538         SettableFuture<RpcResult<DeleteL3VPNOutput>> result = SettableFuture.create();
1539         List<RpcError> errorList = new ArrayList<>();
1540
1541         int failurecount = 0;
1542         int warningcount = 0;
1543         List<Uuid> vpns = requireNonNullElse(input.getId(), Collections.emptyList());
1544         for (Uuid vpn : vpns) {
1545             try {
1546                 LOG.debug("L3VPN delete RPC: VpnID {}", vpn.getValue());
1547                 InstanceIdentifier<VpnInstance> vpnIdentifier =
1548                         InstanceIdentifier.builder(VpnInstances.class)
1549                             .child(VpnInstance.class, new VpnInstanceKey(vpn.getValue())).build();
1550                 Optional<VpnInstance> optionalVpn =
1551                         SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
1552                                 vpnIdentifier);
1553                 if (optionalVpn.isPresent()) {
1554                     removeVpn(vpn);
1555                 } else {
1556                     errorList.add(RpcResultBuilder.newWarning(ErrorType.PROTOCOL, "invalid-value",
1557                             formatAndLog(LOG::warn, "VPN with vpnid: {} does not exist", vpn.getValue())));
1558                     warningcount++;
1559                 }
1560             } catch (ReadFailedException ex) {
1561                 errorList.add(RpcResultBuilder.newError(ErrorType.APPLICATION,
1562                         formatAndLog(LOG::error, "Deletion of L3VPN failed when deleting for uuid {}", vpn.getValue()),
1563                         ex.getMessage()));
1564                 failurecount++;
1565             }
1566         }
1567         // if at least one succeeds; result is success
1568         // if none succeeds; result is failure
1569         if (failurecount + warningcount == vpns.size()) {
1570             result.set(RpcResultBuilder.<DeleteL3VPNOutput>failed().withRpcErrors(errorList).build());
1571         } else {
1572             List<String> errorResponseList = new ArrayList<>();
1573             if (!errorList.isEmpty()) {
1574                 for (RpcError rpcError : errorList) {
1575                     errorResponseList.add("ErrorType: " + rpcError.getErrorType() + ", ErrorTag: " + rpcError.getTag()
1576                             + ", ErrorMessage: " + rpcError.getMessage());
1577                 }
1578             } else {
1579                 errorResponseList.add("Operation successful with no errors");
1580             }
1581             opBuilder.setResponse(errorResponseList);
1582             result.set(RpcResultBuilder.<DeleteL3VPNOutput>success().withResult(opBuilder.build()).build());
1583         }
1584         return result;
1585     }
1586
1587     public void createVpnInstanceForSubnet(Uuid subnetId) {
1588         LOG.debug("Creating/Updating L3 internalVPN for subnetID {} ", subnetId);
1589         createL3InternalVpn(subnetId, subnetId.getValue(), null, null, null, null, null, null);
1590     }
1591
1592     public void removeVpnInstanceForSubnet(Uuid subnetId) {
1593         LOG.debug("Removing vpn-instance for subnetID {} ", subnetId);
1594         removeVpn(subnetId);
1595     }
1596
1597     protected void addSubnetToVpn(@Nullable final Uuid vpnId, Uuid subnet, @Nullable final Uuid internetVpnId) {
1598         LOG.debug("addSubnetToVpn: Adding subnet {} to vpn {}", subnet.getValue(),
1599                   vpnId != null ? vpnId.getValue() : internetVpnId.getValue());
1600         Subnetmap sn = updateSubnetNode(subnet, null, vpnId, internetVpnId);
1601         if (sn == null) {
1602             LOG.error("addSubnetToVpn: subnetmap is null, cannot add subnet {} to VPN {}", subnet.getValue(),
1603                 vpnId != null ? vpnId.getValue() : internetVpnId.getValue());
1604             return;
1605         }
1606         if (vpnId != null) {
1607             VpnMap vpnMap = neutronvpnUtils.getVpnMap(vpnId);
1608             if (vpnMap == null) {
1609                 LOG.error("addSubnetToVpn: No vpnMap for vpnId {},"
1610                      + " cannot add subnet {} to VPN", vpnId.getValue(),
1611                     subnet.getValue());
1612                 return;
1613             }
1614             final VpnInstance vpnInstance = VpnHelper.getVpnInstance(dataBroker, vpnId.getValue());
1615             LOG.debug("addSubnetToVpn: VpnInstance {}", vpnInstance.toString());
1616             if (isVpnOfTypeL2(vpnInstance)) {
1617                 neutronEvpnUtils.updateElanAndVpn(vpnInstance, sn.getNetworkId().getValue(),
1618                         NeutronEvpnUtils.Operation.ADD);
1619             }
1620         }
1621         if (internetVpnId != null) {
1622             VpnMap vpnMap = neutronvpnUtils.getVpnMap(internetVpnId);
1623             if (vpnMap == null) {
1624                 LOG.error("addSubnetToVpn: No vpnMap for InternetVpnId {}, cannot add "
1625                     + "subnet {} to VPN", internetVpnId.getValue(),
1626                     subnet.getValue());
1627                 return;
1628             }
1629         }
1630         final Uuid internetId = internetVpnId;
1631         // Check if there are ports on this subnet and add corresponding vpn-interfaces
1632         List<Uuid> portList = sn.getPortList();
1633         if (portList != null) {
1634             for (final Uuid portId : portList) {
1635                 String vpnInfName = portId.getValue();
1636                 VpnInterface vpnIface = VpnHelper.getVpnInterface(dataBroker, vpnInfName);
1637                 Port port = neutronvpnUtils.getNeutronPort(portId);
1638                 if (port == null) {
1639                     LOG.error("addSubnetToVpn: Cannot proceed with addSubnetToVpn for port {} in subnet {} "
1640                         + "since port is absent in Neutron config DS", portId.getValue(), subnet.getValue());
1641                     continue;
1642                 }
1643                 final Boolean isRouterInterface = port.getDeviceOwner()
1644                         .equals(NeutronConstants.DEVICE_OWNER_ROUTER_INF) ? true : false;
1645                 jobCoordinator.enqueueJob("PORT-" + portId.getValue(), () -> singletonList(
1646                     txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, wrtConfigTxn -> {
1647                         Adjacencies portAdj = createPortIpAdjacencies(port, isRouterInterface, wrtConfigTxn, sn,
1648                                     vpnIface);
1649                         if (vpnIface == null) {
1650                             LOG.trace("addSubnetToVpn: create new VpnInterface for Port {}", vpnInfName);
1651                             Set<Uuid> listVpn = new HashSet<>();
1652                             if (vpnId != null) {
1653                                 listVpn.add(vpnId);
1654                             }
1655                             if (internetId != null) {
1656                                 listVpn.add(internetId);
1657                             }
1658                             writeVpnInterfaceToDs(listVpn,
1659                                     vpnInfName, portAdj, port.getNetworkId(), isRouterInterface, wrtConfigTxn);
1660                             if (sn.getRouterId() != null) {
1661                                 addToNeutronRouterInterfacesMap(sn.getRouterId(), portId.getValue());
1662                             }
1663                         } else {
1664                             LOG.trace("update VpnInterface for Port {} with adj {}", vpnInfName, portAdj);
1665                             if (vpnId != null) {
1666                                 updateVpnInterfaceWithAdjacencies(vpnId, vpnInfName, portAdj, wrtConfigTxn);
1667                             }
1668                             if (internetId != null) {
1669                                 updateVpnInterfaceWithAdjacencies(internetId, vpnInfName, portAdj, wrtConfigTxn);
1670                             }
1671                         }
1672                     }))
1673                 );
1674             }
1675         }
1676     }
1677
1678     protected void removeSubnetFromVpn(final Uuid vpnId, Uuid subnet, @Nullable Uuid internetVpnId) {
1679         Preconditions.checkArgument(vpnId != null || internetVpnId != null,
1680                 "removeSubnetFromVpn: at least one VPN must be not null");
1681         LOG.debug("Removing subnet {} from vpn {}/{}", subnet.getValue(),
1682                   vpnId, internetVpnId);
1683         Subnetmap sn = neutronvpnUtils.getSubnetmap(subnet);
1684         if (sn == null) {
1685             LOG.error("removeSubnetFromVpn: Subnetmap for subnet {} not found", subnet.getValue());
1686             return;
1687         }
1688         VpnMap vpnMap = null;
1689         VpnInstance vpnInstance = null;
1690         if (vpnId != null) {
1691             vpnMap = neutronvpnUtils.getVpnMap(vpnId);
1692             if (vpnMap == null) {
1693                 LOG.error("No vpnMap for vpnId {}, cannot remove subnet {} from VPN",
1694                         vpnId.getValue(), subnet.getValue());
1695                 return;
1696             }
1697             vpnInstance = VpnHelper.getVpnInstance(dataBroker, vpnId.getValue());
1698         }
1699         if (internetVpnId == null) {
1700             internetVpnId = sn.getInternetVpnId();
1701         }
1702         if (internetVpnId != null) {
1703             vpnMap = neutronvpnUtils.getVpnMap(internetVpnId);
1704             if (vpnMap == null) {
1705                 LOG.error("No vpnMap for vpnId {}, cannot remove subnet {}"
1706                         + " from Internet VPN",
1707                         internetVpnId.getValue(), subnet.getValue());
1708                 return;
1709             }
1710         }
1711         if (vpnInstance != null && isVpnOfTypeL2(vpnInstance)) {
1712             neutronEvpnUtils.updateElanAndVpn(vpnInstance, sn.getNetworkId().getValue(),
1713                     NeutronEvpnUtils.Operation.DELETE);
1714         }
1715         boolean subnetVpnAssociation = false;
1716         if (vpnId != null && sn.getVpnId() != null
1717             && sn.getVpnId().getValue().equals(vpnId.getValue())) {
1718             subnetVpnAssociation = true;
1719         } else if (internetVpnId != null && sn.getInternetVpnId() != null
1720             && sn.getInternetVpnId().getValue().matches(internetVpnId.getValue())) {
1721             subnetVpnAssociation = true;
1722         }
1723         if (subnetVpnAssociation == false) {
1724             LOG.error("Removing subnet : Subnetmap is not in VPN {}/{}, owns {} and {}",
1725                       vpnId, internetVpnId, sn.getVpnId(), sn.getInternetVpnId());
1726             return;
1727         }
1728         // Check if there are ports on this subnet; remove corresponding vpn-interfaces
1729         List<Uuid> portList = sn.getPortList();
1730         final Uuid internetId = internetVpnId;
1731         if (portList != null) {
1732             for (final Uuid portId : portList) {
1733                 LOG.debug("withdrawing subnet IP {} from vpn-interface {}", sn.getSubnetIp(), portId.getValue());
1734                 final Port port = neutronvpnUtils.getNeutronPort(portId);
1735                 jobCoordinator.enqueueJob("PORT-" + portId.getValue(),
1736                     () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
1737                             CONFIGURATION, tx -> {
1738                             if (port != null) {
1739                                 withdrawPortIpFromVpnIface(vpnId, internetId, port, sn, tx);
1740                             } else {
1741                                 LOG.warn(
1742                                         "Cannot proceed with withdrawPortIpFromVpnIface for port {} in subnet {} since "
1743                                                 + "port is absent in Neutron config DS", portId.getValue(),
1744                                         subnet.getValue());
1745                             }
1746                         })));
1747             }
1748         }
1749         //update subnet-vpn association
1750         removeFromSubnetNode(subnet, null, null, vpnId, null);
1751     }
1752
1753     protected void updateVpnInternetForSubnet(Subnetmap sm, Uuid vpn, boolean isBeingAssociated) {
1754         LOG.debug("updateVpnInternetForSubnet: {} subnet {} with BGPVPN Internet {} ",
1755              isBeingAssociated ? "associating" : "dissociating", sm.getSubnetIp(),
1756              vpn.getValue());
1757         Uuid internalVpnId = sm.getVpnId();
1758         if (internalVpnId == null) {
1759             LOG.error("updateVpnInternetForSubnet: can not find Internal or BGPVPN Id for subnet {}, bailing out",
1760                       sm.getId().getValue());
1761             return;
1762         }
1763         if (isBeingAssociated) {
1764             updateSubnetNode(sm.getId(), null, sm.getVpnId(), vpn);
1765         } else {
1766             updateSubnetNode(sm.getId(), null, sm.getVpnId(), null);
1767         }
1768
1769         jobCoordinator.enqueueJob("VPN-" + vpn.getValue(), () -> singletonList(
1770             txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, wrtConfigTxn -> {
1771                 if (isBeingAssociated) {
1772                     updateVpnInterface(vpn, null, neutronvpnUtils.getNeutronPort(
1773                             sm.getRouterInterfacePortId()), true, true, wrtConfigTxn, true);
1774                 } else {
1775                     removeInternetVpnFromVpnInterface(vpn,
1776                             neutronvpnUtils.getNeutronPort(sm.getRouterInterfacePortId()), wrtConfigTxn, sm);
1777                 }
1778                 }
1779             )));
1780
1781         // Check for ports on this subnet and update association of
1782         // corresponding vpn-interfaces to internet vpn
1783         List<Uuid> portList = sm.getPortList();
1784         if (portList != null) {
1785             for (Uuid port : portList) {
1786                 LOG.debug("Updating vpn-interface for port {} isBeingAssociated {}",
1787                         port.getValue(), isBeingAssociated);
1788                 jobCoordinator.enqueueJob("PORT-" + port.getValue(),
1789                     () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
1790                         tx -> {
1791                             if (isBeingAssociated) {
1792                                 updateVpnInterface(vpn, null, neutronvpnUtils.getNeutronPort(port),
1793                                         true, false, tx, true);
1794                             } else {
1795                                 removeInternetVpnFromVpnInterface(vpn, neutronvpnUtils.getNeutronPort(port), tx, sm);
1796                             }
1797                         })));
1798             }
1799         }
1800     }
1801
1802     @Nullable
1803     private Subnetmap updateVpnForSubnet(Uuid oldVpnId, Uuid newVpnId, Uuid subnet, boolean isBeingAssociated) {
1804         LOG.debug("Moving subnet {} from oldVpn {} to newVpn {} ", subnet.getValue(),
1805                 oldVpnId.getValue(), newVpnId.getValue());
1806         Uuid networkUuid = neutronvpnUtils.getSubnetmap(subnet).getNetworkId();
1807         Network network = neutronvpnUtils.getNeutronNetwork(networkUuid);
1808         boolean netIsExternal = NeutronvpnUtils.getIsExternal(network);
1809         Uuid vpnExtUuid = netIsExternal ? null
1810                 : neutronvpnUtils.getInternetvpnUuidBoundToSubnetRouter(subnet);
1811         Subnetmap sn = updateSubnetNode(subnet, null, newVpnId, vpnExtUuid);
1812         if (sn == null) {
1813             LOG.error("Updating subnet {} with newVpn {} failed", subnet.getValue(), newVpnId.getValue());
1814             return sn;
1815         }
1816         /* vpnExtUuid will contain the value only on if the subnet is V6 and it is already been
1817          * associated with internet BGP-VPN.
1818          */
1819         if (vpnExtUuid != null) {
1820             /* Update V6 Internet default route match with new VPN metadata.
1821              * isBeingAssociated = true means oldVpnId is same as routerId
1822              * isBeingAssociated = false means newVpnId is same as routerId
1823             */
1824             if (isBeingAssociated) {
1825                 neutronvpnUtils.updateVpnInstanceWithFallback(oldVpnId, vpnExtUuid, true);
1826             } else {
1827                 neutronvpnUtils.updateVpnInstanceWithFallback(newVpnId, vpnExtUuid, true);
1828             }
1829         }
1830         //Update Router Interface first synchronously.
1831         //CAUTION:  Please DONOT make the router interface VPN Movement as an asynchronous commit again !
1832         ListenableFuture<Void> future =
1833                 txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
1834                     tx -> updateVpnInterface(newVpnId, oldVpnId,
1835                         neutronvpnUtils.getNeutronPort(sn.getRouterInterfacePortId()),
1836                         isBeingAssociated, true, tx, false));
1837         Futures.addCallback(future, new FutureCallback<Void>() {
1838             @Override
1839             public void onSuccess(Void result) {
1840                 // Check for ports on this subnet and update association of
1841                 // corresponding vpn-interfaces to external vpn
1842                 List<Uuid> portList = sn.getPortList();
1843                 if (portList != null) {
1844                     for (Uuid port : portList) {
1845                         LOG.debug("Updating vpn-interface for port {} isBeingAssociated {}",
1846                                 port.getValue(), isBeingAssociated);
1847                         jobCoordinator.enqueueJob("PORT-" + port.getValue(), () -> Collections.singletonList(
1848                                 txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
1849                                     tx -> updateVpnInterface(newVpnId, oldVpnId,
1850                                             neutronvpnUtils.getNeutronPort(port), isBeingAssociated, false,
1851                                             tx, false))));
1852                     }
1853                 }
1854             }
1855
1856             @Override
1857             public void onFailure(Throwable throwable) {
1858                 LOG.error(
1859                         "Failed to update router interface {} in subnet {} from oldVpnId {} to newVpnId {}, "
1860                                 + "returning",
1861                         sn.getRouterInterfacePortId().getValue(), subnet.getValue(), oldVpnId, newVpnId, throwable);
1862             }
1863         }, MoreExecutors.directExecutor());
1864
1865         return sn;
1866     }
1867
1868     public InstanceIdentifier<RouterInterfaces> getRouterInterfacesId(Uuid routerId) {
1869         return InstanceIdentifier.builder(RouterInterfacesMap.class)
1870                 .child(RouterInterfaces.class, new RouterInterfacesKey(routerId)).build();
1871     }
1872
1873     protected void addToNeutronRouterInterfacesMap(Uuid routerId, String interfaceName) {
1874         final InstanceIdentifier<RouterInterfaces> routerInterfacesId = getRouterInterfacesId(routerId);
1875         final ReentrantLock lock = lockForUuid(routerId);
1876         lock.lock();
1877         try {
1878             Optional<RouterInterfaces> optRouterInterfaces =
1879                     SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
1880                         routerInterfacesId);
1881             Interfaces routerInterface = new InterfacesBuilder().withKey(new InterfacesKey(interfaceName))
1882                     .setInterfaceId(interfaceName).build();
1883             if (optRouterInterfaces.isPresent()) {
1884                 SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION,
1885                     routerInterfacesId.child(Interfaces.class, new InterfacesKey(interfaceName)), routerInterface);
1886             } else {
1887                 // TODO Shouldn't we be doing something with builder and interfaces?
1888                 //          RouterInterfacesBuilder builder = new RouterInterfacesBuilder().setRouterId(routerId);
1889                 //          List<Interfaces> interfaces = new ArrayList<>();
1890                 //          interfaces.add(routerInterface);
1891
1892                 SingleTransactionDataBroker.syncUpdate(dataBroker, LogicalDatastoreType.CONFIGURATION,
1893                     routerInterfacesId.child(Interfaces.class, new InterfacesKey(interfaceName)), routerInterface);
1894             }
1895         } catch (ReadFailedException | TransactionCommitFailedException e) {
1896             LOG.error("Error reading router interfaces for {}", routerInterfacesId, e);
1897         } finally {
1898             lock.unlock();
1899         }
1900     }
1901
1902     protected void removeFromNeutronRouterInterfacesMap(Uuid routerId, String interfaceName) {
1903         final InstanceIdentifier<RouterInterfaces> routerInterfacesId = getRouterInterfacesId(routerId);
1904         final ReentrantLock lock = lockForUuid(routerId);
1905         lock.lock();
1906         try {
1907             Optional<RouterInterfaces> optRouterInterfaces =
1908                     SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
1909                         routerInterfacesId);
1910             Interfaces routerInterface = new InterfacesBuilder().withKey(new InterfacesKey(interfaceName))
1911                     .setInterfaceId(interfaceName).build();
1912             if (optRouterInterfaces.isPresent()) {
1913                 RouterInterfaces routerInterfaces = optRouterInterfaces.get();
1914                 List<Interfaces> interfaces = routerInterfaces.getInterfaces();
1915                 if (interfaces != null && interfaces.remove(routerInterface)) {
1916                     if (interfaces.isEmpty()) {
1917                         SingleTransactionDataBroker.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION,
1918                             routerInterfacesId);
1919                     } else {
1920                         SingleTransactionDataBroker.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION,
1921                             routerInterfacesId.child(Interfaces.class, new InterfacesKey(interfaceName)));
1922                     }
1923                 }
1924             }
1925         } catch (ReadFailedException | TransactionCommitFailedException e) {
1926             LOG.error("Error reading the router interfaces for {}", routerInterfacesId, e);
1927         } finally {
1928             lock.unlock();
1929         }
1930     }
1931
1932     /**
1933      * Creates the corresponding static routes in the specified VPN. These static routes must be point to an
1934      * InterVpnLink endpoint and the specified VPN must be the other end of the InterVpnLink. Otherwise the
1935      * route will be ignored.
1936      *
1937      * @param vpnName the VPN identifier
1938      * @param interVpnLinkRoutes The list of static routes
1939      * @param nexthopsXinterVpnLinks A Map with the correspondence nextHop-InterVpnLink
1940      */
1941     public void addInterVpnRoutes(Uuid vpnName, List<Routes> interVpnLinkRoutes,
1942                                   HashMap<String, InterVpnLink> nexthopsXinterVpnLinks) {
1943         for (Routes route : interVpnLinkRoutes) {
1944             String nexthop = route.getNexthop().stringValue();
1945             String destination = route.getDestination().stringValue();
1946             InterVpnLink interVpnLink = nexthopsXinterVpnLinks.get(nexthop);
1947             if (isNexthopTheOtherVpnLinkEndpoint(nexthop, vpnName.getValue(), interVpnLink)) {
1948                 AddStaticRouteInput rpcInput =
1949                         new AddStaticRouteInputBuilder().setDestination(destination).setNexthop(nexthop)
1950                                 .setVpnInstanceName(vpnName.getValue())
1951                                 .build();
1952                 Future<RpcResult<AddStaticRouteOutput>> labelOuputFtr = vpnRpcService.addStaticRoute(rpcInput);
1953                 RpcResult<AddStaticRouteOutput> rpcResult;
1954                 try {
1955                     rpcResult = labelOuputFtr.get();
1956                     if (rpcResult.isSuccessful()) {
1957                         LOG.debug("Label generated for destination {} is: {}",
1958                                 destination, rpcResult.getResult().getLabel());
1959                     } else {
1960                         LOG.error("RPC call to add a static Route to {} with nexthop {} returned with errors {}",
1961                                 destination, nexthop, rpcResult.getErrors());
1962                     }
1963                 } catch (InterruptedException | ExecutionException e) {
1964                     LOG.error("Error happened while invoking addStaticRoute RPC for nexthop {} with destination {} "
1965                             + "for VPN {}", nexthop, destination, vpnName.getValue(), e);
1966                 }
1967             } else {
1968                 // Any other case is a fault.
1969                 LOG.warn("route with destination {} and nexthop {} does not apply to any InterVpnLink",
1970                         route.getDestination().stringValue(), nexthop);
1971                 continue;
1972             }
1973         }
1974     }
1975
1976     /**
1977      * Removes the corresponding static routes from the specified VPN. These static routes point to an
1978      * InterVpnLink endpoint and the specified VPN must be the other end of the InterVpnLink.
1979      *
1980      * @param vpnName the VPN identifier
1981      * @param interVpnLinkRoutes The list of static routes
1982      * @param nexthopsXinterVpnLinks A Map with the correspondence nextHop-InterVpnLink
1983      */
1984     public void removeInterVpnRoutes(Uuid vpnName, List<Routes> interVpnLinkRoutes,
1985                                      HashMap<String, InterVpnLink> nexthopsXinterVpnLinks) {
1986         for (Routes route : interVpnLinkRoutes) {
1987             String nexthop = route.getNexthop().stringValue();
1988             String destination = route.getDestination().stringValue();
1989             InterVpnLink interVpnLink = nexthopsXinterVpnLinks.get(nexthop);
1990             if (isNexthopTheOtherVpnLinkEndpoint(nexthop, vpnName.getValue(), interVpnLink)) {
1991                 RemoveStaticRouteInput rpcInput =
1992                         new RemoveStaticRouteInputBuilder().setDestination(destination).setNexthop(nexthop)
1993                                 .setVpnInstanceName(vpnName.getValue())
1994                                 .build();
1995
1996                 ListenableFutures.addErrorLogging(JdkFutureAdapters.listenInPoolThread(
1997                         vpnRpcService.removeStaticRoute(rpcInput)), LOG, "Remove VPN routes");
1998             } else {
1999                 // Any other case is a fault.
2000                 LOG.warn("route with destination {} and nexthop {} does not apply to any InterVpnLink",
2001                         route.getDestination().stringValue(), nexthop);
2002                 continue;
2003             }
2004         }
2005     }
2006
2007     /*
2008      * Returns true if the specified nexthop is the other endpoint in an
2009      * InterVpnLink, regarding one of the VPN's point of view.
2010      */
2011     private boolean isNexthopTheOtherVpnLinkEndpoint(String nexthop, String thisVpnUuid, InterVpnLink interVpnLink) {
2012         return
2013                 interVpnLink != null
2014                         && (interVpnLink.getFirstEndpoint().getVpnUuid().getValue().equals(thisVpnUuid)
2015                         && interVpnLink.getSecondEndpoint().getIpAddress().getValue().equals(nexthop)
2016                         || interVpnLink.getSecondEndpoint().getVpnUuid().getValue().equals(thisVpnUuid)
2017                         && interVpnLink.getFirstEndpoint().getIpAddress().getValue().equals(nexthop));
2018     }
2019
2020     @Nonnull
2021     protected List<Adjacency> getAdjacencyforExtraRoute(List<Routes> routeList, String fixedIp) {
2022         List<Adjacency> adjList = new ArrayList<>();
2023         Map<String, List<String>> adjMap = new HashMap<>();
2024         for (Routes route : routeList) {
2025             if (route == null || route.getNexthop() == null || route.getDestination() == null) {
2026                 LOG.error("Incorrect input received for extra route. {}", route);
2027             } else {
2028                 String nextHop = route.getNexthop().stringValue();
2029                 String destination = route.getDestination().stringValue();
2030                 if (!nextHop.equals(fixedIp)) {
2031                     LOG.trace("FixedIP {} is not extra route nexthop for destination {}", fixedIp, destination);
2032                     continue;
2033                 }
2034                 LOG.trace("Adding extra route for destination {} with nexthop {} ", destination,
2035                         nextHop);
2036                 List<String> hops = adjMap.computeIfAbsent(destination, k -> new ArrayList<>());
2037                 if (!hops.contains(nextHop)) {
2038                     hops.add(nextHop);
2039                 }
2040             }
2041         }
2042
2043         for (Entry<String, List<String>> entry : adjMap.entrySet()) {
2044             final String destination = entry.getKey();
2045             final List<String> ipList = entry.getValue();
2046             Adjacency erAdj = new AdjacencyBuilder().setIpAddress(destination)
2047                     .setAdjacencyType(AdjacencyType.ExtraRoute).setNextHopIpList(ipList)
2048                     .withKey(new AdjacencyKey(destination)).build();
2049             adjList.add(erAdj);
2050         }
2051         return adjList;
2052     }
2053
2054     protected void updateVpnInterfaceWithExtraRouteAdjacency(Uuid vpnId, List<Routes> routeList) {
2055         checkAlarmExtraRoutes(vpnId, routeList);
2056
2057         for (Routes route : routeList) {
2058             if (route == null || route.getNexthop() == null || route.getDestination() == null) {
2059                 LOG.error("Incorrect input received for extra route. {}", route);
2060             } else {
2061                 String nextHop = route.getNexthop().stringValue();
2062                 String destination = route.getDestination().stringValue();
2063                 String infName = neutronvpnUtils.getNeutronPortNameFromVpnPortFixedIp(vpnId.getValue(),
2064                         nextHop);
2065                 if (infName != null) {
2066                     LOG.trace("Updating extra route for destination {} onto vpn {} with nexthop {} and infName {}",
2067                         destination, vpnId.getValue(), nextHop, infName);
2068                     boolean isLockAcquired = false;
2069                     try {
2070                         InstanceIdentifier<VpnInterface> identifier = InstanceIdentifier.builder(VpnInterfaces.class)
2071                                 .child(VpnInterface.class, new VpnInterfaceKey(infName)).build();
2072                         InstanceIdentifier<Adjacency> path = identifier.augmentation(Adjacencies.class)
2073                             .child(Adjacency.class, new AdjacencyKey(destination));
2074                         Optional<Adjacency> existingAdjacency = SingleTransactionDataBroker.syncReadOptional(dataBroker,
2075                                 LogicalDatastoreType.CONFIGURATION, path);
2076                         if (existingAdjacency.isPresent()
2077                                 && existingAdjacency.get().getAdjacencyType() == AdjacencyType.PrimaryAdjacency) {
2078                             LOG.error("The route with destination {} nextHop {} is already present as"
2079                                             + " a primary adjacency for interface {}. Skipping adjacency addition.",
2080                                     destination, nextHop, infName);
2081                             continue;
2082                         }
2083                         Adjacency erAdj = new AdjacencyBuilder().setIpAddress(destination)
2084                             .setNextHopIpList(Collections.singletonList(nextHop)).withKey(new AdjacencyKey(destination))
2085                             .setAdjacencyType(AdjacencyType.ExtraRoute).build();
2086                         isLockAcquired = interfaceLock.tryLock(infName, LOCK_WAIT_TIME, TimeUnit.SECONDS);
2087                         SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION,
2088                                 path, erAdj);
2089                     } catch (TransactionCommitFailedException e) {
2090                         LOG.error("exception in adding extra route with destination: {}, next hop: {}",
2091                             destination, nextHop, e);
2092                     } catch (ReadFailedException e) {
2093                         LOG.error("Exception on reading data-store ", e);
2094                     } finally {
2095                         if (isLockAcquired) {
2096                             interfaceLock.unlock(infName);
2097                         }
2098                     }
2099                 } else {
2100                     LOG.error("Unable to find VPN NextHop interface to apply extra-route destination {} on VPN {} "
2101                         + "with nexthop {}", destination, vpnId.getValue(), nextHop);
2102                 }
2103             }
2104         }
2105     }
2106
2107     /**
2108      * This method setup or down an alarm about extra route fault.
2109      * When extra routes are configured, through a router, if the number of nexthops is greater than the number of
2110      * available RDs, then an alarm and an error is generated.<br>
2111      * <b>Be careful</b> the routeList could be changed.
2112      *
2113      * @param vpnId the vpnId of vpn to control.
2114      * @param routeList the list of router to check, it could be modified.
2115      */
2116     private void checkAlarmExtraRoutes(Uuid vpnId, List<Routes> routeList) {
2117         if (!neutronvpnAlarm.isAlarmEnabled()) {
2118             LOG.debug("checkAlarmExtraRoutes is not enable for vpnId {} routeList {}", vpnId, routeList);
2119             return;
2120         }
2121         VpnInstance vpnInstance = neutronvpnUtils.getVpnInstance(dataBroker, vpnId);
2122         if (vpnInstance == null || routeList == null || routeList.isEmpty() || !neutronvpnAlarm.isAlarmEnabled()) {
2123             LOG.debug("checkAlarmExtraRoutes have args null as following : vpnId {} routeList {}",
2124                     vpnId, routeList);
2125             return;
2126         }
2127         String primaryRd = neutronvpnUtils.getVpnRd(vpnId.getValue());
2128         if (primaryRd == null || primaryRd.equals(vpnId.getValue())) {
2129             LOG.debug("checkAlarmExtraRoutes. vpn {} is not a BGPVPN. cancel checkExtraRoute",
2130                     vpnId);
2131             return;
2132         }
2133         for (Routes route : routeList) {
2134             // count  the number of nexthops for each same route.getDestingation().getValue()
2135             String destination = route.getDestination().stringValue();
2136             String nextHop = route.getNexthop().stringValue();
2137             List<String> nextHopList = new ArrayList<>();
2138             nextHopList.add(nextHop);
2139             int nbNextHops = 0;
2140             for (Routes routeTmp : routeList) {
2141                 String routeDest = routeTmp.getDestination().stringValue();
2142                 if (!destination.equals(routeDest)) {
2143                     continue;
2144                 }
2145                 String routeNextH = routeTmp.getNexthop().stringValue();
2146                 if (nextHop.equals(routeNextH)) {
2147                     continue;
2148                 }
2149                 nbNextHops++;
2150                 nextHopList.add(routeTmp.getNexthop().stringValue());
2151             }
2152             final List<String> rdList = new ArrayList<>();
2153             if (vpnInstance.getIpv4Family() != null
2154                     && vpnInstance.getIpv4Family().getRouteDistinguisher() != null) {
2155                 vpnInstance.getIpv4Family().getRouteDistinguisher().forEach(rd -> {
2156                     if (rd != null) {
2157                         rdList.add(rd);
2158                     }
2159                 });
2160             }
2161             if (vpnInstance.getIpv6Family() != null && vpnInstance.getIpv6Family().getRouteDistinguisher() != null) {
2162                 vpnInstance.getIpv6Family().getRouteDistinguisher().forEach(rd -> {
2163                     if (rd != null && !rdList.contains(rd)) {
2164                         rdList.add(rd);
2165                     }
2166                 });
2167             }
2168             // 1. VPN Instance Name
2169             String typeAlarm = "for vpnId: " + vpnId + " have exceeded next hops for prefixe";
2170
2171             // 2. Router ID
2172             List<Uuid> routerUuidList = neutronvpnUtils.getRouterIdListforVpn(vpnId);
2173             Uuid routerUuid = routerUuidList.get(0);
2174             StringBuilder detailsAlarm = new StringBuilder("routerUuid: ");
2175             detailsAlarm.append(routerUuid == null ? vpnId.toString() : routerUuid.getValue());
2176
2177             // 3. List of RDs associated with the VPN
2178             detailsAlarm.append(" List of RDs associated with the VPN: ");
2179             for (String s : rdList) {
2180                 detailsAlarm.append(s);
2181                 detailsAlarm.append(", ");
2182             }
2183
2184             // 4. Prefix in question
2185             detailsAlarm.append(" for prefix: ");
2186             detailsAlarm.append(route.getDestination().stringValue());
2187
2188             // 5. List of NHs for the prefix
2189             detailsAlarm.append(" for nextHops: ");
2190             for (String s : nextHopList) {
2191                 detailsAlarm.append(s);
2192                 detailsAlarm.append(", ");
2193             }
2194
2195             if (rdList.size() < nbNextHops) {
2196                 neutronvpnAlarm.raiseNeutronvpnAlarm(typeAlarm, detailsAlarm.toString());
2197             } else {
2198                 neutronvpnAlarm.clearNeutronvpnAlarm(typeAlarm, detailsAlarm.toString());
2199             }
2200         }
2201     }
2202
2203     // TODO Clean up the exception handling
2204     @SuppressWarnings("checkstyle:IllegalCatch")
2205     protected void removeAdjacencyforExtraRoute(Uuid vpnId, List<Routes> routeList) {
2206         for (Routes route : routeList) {
2207             if (route != null && route.getNexthop() != null && route.getDestination() != null) {
2208                 boolean isLockAcquired = false;
2209                 String nextHop = route.getNexthop().stringValue();
2210                 String destination = route.getDestination().stringValue();
2211                 String infName = neutronvpnUtils.getNeutronPortNameFromVpnPortFixedIp(vpnId.getValue(),
2212                         nextHop);
2213                 if (infName == null) {
2214                     LOG.error("Unable to find VPN NextHop interface to remove extra-route destination {} on VPN {} "
2215                             + "with nexthop {}", destination, vpnId.getValue(), nextHop);
2216                     // Proceed to remove the next extra-route
2217                     continue;
2218                 }
2219                 LOG.trace("Removing extra route for destination {} on vpn {} with nexthop {} and infName {}",
2220                         destination, vpnId.getValue(), nextHop, infName);
2221
2222                 InstanceIdentifier<Adjacency> adjacencyIdentifier =
2223                         InstanceIdentifier.builder(VpnInterfaces.class)
2224                                 .child(VpnInterface.class, new VpnInterfaceKey(infName))
2225                                 .augmentation(Adjacencies.class)
2226                                 .child(Adjacency.class, new AdjacencyKey(destination))
2227                                 .build();
2228
2229                 try {
2230                     // Looking for existing prefix in MDSAL database
2231                     Optional<Adjacency> adjacency = SingleTransactionDataBroker.syncReadOptional(dataBroker,
2232                             LogicalDatastoreType.CONFIGURATION, adjacencyIdentifier);
2233                     boolean updateNextHops = false;
2234                     List<String> nextHopList = new ArrayList<>();
2235                     if (adjacency.isPresent()) {
2236                         List<String> nhListRead = adjacency.get().getNextHopIpList();
2237                         if (nhListRead.size() > 1) { // ECMP case
2238                             for (String nextHopRead : nhListRead) {
2239                                 if (nextHopRead.equals(nextHop)) {
2240                                     updateNextHops = true;
2241                                 } else {
2242                                     nextHopList.add(nextHopRead);
2243                                 }
2244                             }
2245                         }
2246                     }
2247
2248                     isLockAcquired = interfaceLock.tryLock(infName, LOCK_WAIT_TIME, TimeUnit.SECONDS);
2249                     if (updateNextHops) {
2250                         // An update must be done, not including the current next hop
2251                         InstanceIdentifier<VpnInterface> vpnIfIdentifier = InstanceIdentifier.builder(
2252                                 VpnInterfaces.class).child(VpnInterface.class, new VpnInterfaceKey(infName)).build();
2253                         Adjacency newAdj = new AdjacencyBuilder(adjacency.get()).setIpAddress(destination)
2254                                 .setNextHopIpList(nextHopList)
2255                                 .withKey(new AdjacencyKey(destination))
2256                                 .build();
2257                         Adjacencies erAdjs =
2258                                 new AdjacenciesBuilder().setAdjacency(Collections.singletonList(newAdj)).build();
2259                         VpnInterface vpnIf = new VpnInterfaceBuilder().withKey(new VpnInterfaceKey(infName))
2260                                 .addAugmentation(Adjacencies.class, erAdjs).build();
2261                         SingleTransactionDataBroker.syncUpdate(dataBroker, LogicalDatastoreType.CONFIGURATION,
2262                                 vpnIfIdentifier, vpnIf);
2263                     } else {
2264                         // Remove the whole route
2265                         SingleTransactionDataBroker.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION,
2266                                 adjacencyIdentifier);
2267                         LOG.trace("extra route {} deleted successfully", route);
2268                     }
2269                 } catch (TransactionCommitFailedException | ReadFailedException e) {
2270                     LOG.error("exception in deleting extra route with destination {} for interface {}",
2271                             destination, infName, e);
2272                 } finally {
2273                     if (isLockAcquired) {
2274                         interfaceLock.unlock(infName);
2275                     }
2276                 }
2277             } else {
2278                 LOG.error("Incorrect input received for extra route: {}", route);
2279             }
2280         }
2281     }
2282
2283     public void removeVpn(Uuid vpnId) {
2284         // read VPNMaps
2285         VpnMap vpnMap = neutronvpnUtils.getVpnMap(vpnId);
2286         List<RouterIds> routerIdsList = vpnMap != null ? vpnMap.getRouterIds() : null;
2287         List<Uuid> routerUuidList = new ArrayList<>();
2288         // dissociate router
2289         if (routerIdsList != null && !routerIdsList.isEmpty()) {
2290             for (RouterIds router : routerIdsList) {
2291                 Uuid routerId = router.getRouterId();
2292                 routerUuidList.add(routerId);
2293                 dissociateRouterFromVpn(vpnId, routerId);
2294             }
2295         }
2296         if (!routerUuidList.contains(vpnId) && vpnMap.getNetworkIds() != null) {
2297             dissociateNetworksFromVpn(vpnId, vpnMap.getNetworkIds());
2298         }
2299         // remove entire vpnMaps node
2300         deleteVpnMapsNode(vpnId);
2301
2302         // remove vpn-instance
2303         deleteVpnInstance(vpnId);
2304         LOG.debug("Deleted L3VPN with ID {}", vpnId.getValue());
2305     }
2306
2307     private boolean isVpnOfTypeL2(VpnInstance vpnInstance) {
2308         return vpnInstance != null && vpnInstance.getType() == VpnInstance.Type.L2;
2309     }
2310
2311     // TODO Clean up the exception handling
2312     @SuppressWarnings("checkstyle:IllegalCatch")
2313     protected void associateRouterToVpn(Uuid vpnId, Uuid routerId) {
2314         updateVpnMaps(vpnId, null, routerId, null, null);
2315         LOG.debug("associateRouterToVpn: Updating association of subnets to external vpn {}", vpnId.getValue());
2316         List<Subnetmap> subMapList = neutronvpnUtils.getNeutronRouterSubnetMapList(routerId);
2317         IpVersionChoice ipVersion = IpVersionChoice.UNDEFINED;
2318         for (Subnetmap sn : subMapList) {
2319             IpVersionChoice ipVers = neutronvpnUtils.getIpVersionFromString(sn.getSubnetIp());
2320             if (!ipVersion.isIpVersionChosen(ipVers)) {
2321                 ipVersion = ipVersion.addVersion(ipVers);
2322             }
2323         }
2324         if (ipVersion != IpVersionChoice.UNDEFINED) {
2325             LOG.debug("associateRouterToVpn: Updating vpnInstanceOpDataEntrywith ip address family {} for VPN {} ",
2326                     ipVersion, vpnId);
2327             neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnId.getValue(), ipVersion, true);
2328         }
2329         for (Subnetmap sn : subMapList) {
2330             updateVpnForSubnet(routerId, vpnId, sn.getId(), true);
2331         }
2332     }
2333
2334     protected void associateRouterToInternalVpn(Uuid vpnId, Uuid routerId) {
2335         List<Uuid> routerSubnets = neutronvpnUtils.getNeutronRouterSubnetIds(routerId);
2336         Uuid internetVpnId = neutronvpnUtils.getInternetvpnUuidBoundToRouterId(routerId);
2337         LOG.debug("Adding subnets to internal vpn {}", vpnId.getValue());
2338         for (Uuid subnet : routerSubnets) {
2339             IpVersionChoice version = NeutronvpnUtils
2340                    .getIpVersionFromSubnet(neutronvpnUtils.getSubnetmap(subnet));
2341             if (version.isIpVersionChosen(IpVersionChoice.IPV4)) {
2342                 addSubnetToVpn(vpnId, subnet, null);
2343             } else {
2344                 addSubnetToVpn(vpnId, subnet, internetVpnId);
2345             }
2346         }
2347     }
2348
2349     // TODO Clean up the exception handling
2350     @SuppressWarnings("checkstyle:IllegalCatch")
2351     protected void dissociateRouterFromVpn(Uuid vpnId, Uuid routerId) {
2352
2353         clearFromVpnMaps(vpnId, routerId, null);
2354         List<Subnetmap> subMapList = neutronvpnUtils.getNeutronRouterSubnetMapList(routerId);
2355         IpVersionChoice ipVersion = IpVersionChoice.UNDEFINED;
2356         for (Subnetmap sn : subMapList) {
2357             IpVersionChoice ipVers = NeutronvpnUtils.getIpVersionFromString(sn.getSubnetIp());
2358             if (ipVersion.isIpVersionChosen(ipVers)) {
2359                 ipVersion = ipVersion.addVersion(ipVers);
2360             }
2361             LOG.debug("dissociateRouterFromVpn: Updating association of subnets to internal vpn {}",
2362                     routerId.getValue());
2363             updateVpnForSubnet(vpnId, routerId, sn.getId(), false);
2364         }
2365         if (ipVersion != IpVersionChoice.UNDEFINED) {
2366             LOG.debug("dissociateRouterFromVpn; Updating vpnInstanceOpDataEntry with ip address family {} for VPN {} ",
2367                     ipVersion, vpnId);
2368             neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnId.getValue(), ipVersion,
2369                     false);
2370         }
2371     }
2372
2373     /**
2374      * Parses and associates networks list with given VPN.
2375      *
2376      * @param vpnId Uuid of given VPN.
2377      * @param networkList List list of network Ids (Uuid), which will be associated.
2378      * @return list of formatted strings with detailed error messages.
2379      */
2380     @Nonnull
2381     protected List<String> associateNetworksToVpn(@Nonnull Uuid vpnId, @Nonnull List<Uuid> networkList) {
2382         List<String> failedNwList = new ArrayList<>();
2383         HashSet<Uuid> passedNwList = new HashSet<>();
2384         boolean isExternalNetwork = false;
2385         if (networkList.isEmpty()) {
2386             LOG.error("associateNetworksToVpn: Failed as given networks list is empty, VPN Id: {}", vpnId.getValue());
2387             failedNwList.add(String.format("Failed to associate networks with VPN %s as given networks list is empty",
2388                     vpnId.getValue()));
2389             return failedNwList;
2390         }
2391         VpnInstance vpnInstance = VpnHelper.getVpnInstance(dataBroker, vpnId.getValue());
2392         if (vpnInstance == null) {
2393             LOG.error("associateNetworksToVpn: Can not find vpnInstance for VPN {} in ConfigDS", vpnId.getValue());
2394             failedNwList.add(String.format("Failed to associate network: can not found vpnInstance for VPN %s "
2395                                            + "in ConfigDS", vpnId.getValue()));
2396             return failedNwList;
2397         }
2398         try {
2399             if (isVpnOfTypeL2(vpnInstance) && neutronEvpnUtils.isVpnAssociatedWithNetwork(vpnInstance)) {
2400                 LOG.error("associateNetworksToVpn: EVPN {} supports only one network to be associated with",
2401                           vpnId.getValue());
2402                 failedNwList.add(String.format("Failed to associate network: EVPN %s supports only one network to be "
2403                                                + "associated with", vpnId.getValue()));
2404                 return failedNwList;
2405             }
2406             Set<VpnTarget> routeTargets = vpnManager.getRtListForVpn(vpnId.getValue());
2407             for (Uuid nw : networkList) {
2408                 Network network = neutronvpnUtils.getNeutronNetwork(nw);
2409                 if (network == null) {
2410                     LOG.error("associateNetworksToVpn: Network {} not found in ConfigDS", nw.getValue());
2411                     failedNwList.add(String.format("Failed to associate network: network %s not found in ConfigDS",
2412                                                    nw.getValue()));
2413                     continue;
2414                 }
2415                 NetworkProviderExtension providerExtension = network.augmentation(NetworkProviderExtension.class);
2416                 if (providerExtension.getSegments() != null && providerExtension.getSegments().size() > 1) {
2417                     LOG.error("associateNetworksToVpn: MultiSegmented network {} not supported in BGPVPN {}",
2418                               nw.getValue(), vpnId.getValue());
2419                     failedNwList.add(String.format("Failed to associate multisegmented network %s with BGPVPN %s",
2420                                                    nw.getValue(), vpnId.getValue()));
2421                     continue;
2422                 }
2423                 Uuid networkVpnId = neutronvpnUtils.getVpnForNetwork(nw);
2424                 if (networkVpnId != null) {
2425                     LOG.error("associateNetworksToVpn: Network {} already associated with another VPN {}",
2426                               nw.getValue(), networkVpnId.getValue());
2427                     failedNwList.add(String.format("Failed to associate network %s as it is already associated to "
2428                                                    + "another VPN %s", nw.getValue(), networkVpnId.getValue()));
2429                     continue;
2430                 }
2431                 if (NeutronvpnUtils.getIsExternal(network) && !associateExtNetworkToVpn(vpnId, network)) {
2432                     LOG.error("associateNetworksToVpn: Failed to associate Provider Network {} with VPN {}",
2433                             nw.getValue(), vpnId.getValue());
2434                     failedNwList.add(String.format("Failed to associate Provider Network %s with VPN %s",
2435                             nw.getValue(), vpnId.getValue()));
2436                     continue;
2437                 }
2438                 if (NeutronvpnUtils.getIsExternal(network)) {
2439                     isExternalNetwork = true;
2440                 }
2441                 List<Subnetmap> subnetmapList = neutronvpnUtils.getSubnetmapListFromNetworkId(nw);
2442                 if (subnetmapList == null || subnetmapList.isEmpty()) {
2443                     passedNwList.add(nw);
2444                     continue;
2445                 }
2446                 if (vpnManager.checkForOverlappingSubnets(nw, subnetmapList, vpnId, routeTargets, failedNwList)) {
2447                     continue;
2448                 }
2449                 IpVersionChoice ipVersion = IpVersionChoice.UNDEFINED;
2450                 for (Subnetmap subnetmap : subnetmapList) {
2451                     IpVersionChoice ipVers = neutronvpnUtils.getIpVersionFromString(subnetmap.getSubnetIp());
2452                     if (!ipVersion.isIpVersionChosen(ipVers)) {
2453                         ipVersion = ipVersion.addVersion(ipVers);
2454                     }
2455                 }
2456                 if (ipVersion != IpVersionChoice.UNDEFINED) {
2457                     LOG.debug("associateNetworksToVpn: Updating vpnInstanceOpDataEntry with ip address family {}"
2458                             + " for VPN {} ", ipVersion, vpnId);
2459                     neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnId.getValue(), ipVersion, true);
2460                 }
2461                 for (Subnetmap subnetmap : subnetmapList) {
2462                     Uuid subnetId = subnetmap.getId();
2463                     Uuid subnetVpnId = neutronvpnUtils.getVpnForSubnet(subnetId);
2464                     if (subnetVpnId != null) {
2465                         LOG.error("associateNetworksToVpn: Failed to associate subnet {} with VPN {}"
2466                                 + " as it is already associated", subnetId.getValue(), subnetVpnId.getValue());
2467                         failedNwList.add(String.format("Failed to associate subnet %s with VPN %s"
2468                                 + " as it is already associated", subnetId.getValue(), vpnId.getValue()));
2469                         continue;
2470                     }
2471                     if (!NeutronvpnUtils.getIsExternal(network)) {
2472                         LOG.debug("associateNetworksToVpn: Add subnet {} to VPN {}", subnetId.getValue(),
2473                                 vpnId.getValue());
2474                         addSubnetToVpn(vpnId, subnetId, null);
2475                         vpnManager.updateRouteTargetsToSubnetAssociation(routeTargets, subnetmap.getSubnetIp(),
2476                                 vpnId.getValue());
2477                         passedNwList.add(nw);
2478                     }
2479                 }
2480                 passedNwList.add(nw);
2481             }
2482         } catch (ReadFailedException e) {
2483             LOG.error("associateNetworksToVpn: Failed to associate VPN {} with networks {}: ", vpnId.getValue(),
2484                     networkList, e);
2485             failedNwList.add(String.format("Failed to associate VPN %s with networks %s: %s", vpnId.getValue(),
2486                     networkList, e));
2487         }
2488         //VpnMap update for ext-nw is already done in associateExtNetworkToVpn() method.
2489         if (!isExternalNetwork) {
2490             updateVpnMaps(vpnId, null, null, null, new ArrayList<>(passedNwList));
2491         }
2492         LOG.info("Network(s) {} associated to L3VPN {} successfully", passedNwList.toString(), vpnId.getValue());
2493         return failedNwList;
2494     }
2495
2496     private boolean associateExtNetworkToVpn(@Nonnull Uuid vpnId, @Nonnull Network extNet) {
2497         if (!addExternalNetworkToVpn(extNet, vpnId)) {
2498             return false;
2499         }
2500         VpnInstanceOpDataEntry vpnOpDataEntry = neutronvpnUtils.getVpnInstanceOpDataEntryFromVpnId(vpnId.getValue());
2501         if (vpnOpDataEntry == null) {
2502             LOG.error("associateExtNetworkToVpn: can not find VpnOpDataEntry for VPN {}", vpnId.getValue());
2503             return false;
2504         }
2505         if (!vpnOpDataEntry.getBgpvpnType().equals(BgpvpnType.BGPVPNInternet)) {
2506             LOG.info("associateExtNetworkToVpn: set type {} for VPN {}", BgpvpnType.BGPVPNInternet, vpnId.getValue());
2507             neutronvpnUtils.updateVpnInstanceOpWithType(BgpvpnType.BGPVPNInternet, vpnId);
2508         }
2509         //Update VpnMap with ext-nw is needed first before processing V6 internet default fallback flows
2510         List<Uuid> extNwList = Collections.singletonList(extNet.key().getUuid());
2511         updateVpnMaps(vpnId, null, null, null, extNwList);
2512         IpVersionChoice ipVersion = IpVersionChoice.UNDEFINED;
2513         for (Uuid snId: neutronvpnUtils.getPrivateSubnetsToExport(extNet, vpnId)) {
2514             Subnetmap sm = neutronvpnUtils.getSubnetmap(snId);
2515             if (sm == null) {
2516                 LOG.error("associateExtNetworkToVpn: can not find subnet with Id {} in ConfigDS", snId.getValue());
2517                 continue;
2518             }
2519             IpVersionChoice ipVers = NeutronvpnUtils.getIpVersionFromString(sm.getSubnetIp());
2520             if (ipVers.isIpVersionChosen(IpVersionChoice.IPV4)) {
2521                 continue;
2522             }
2523             if (ipVers.isIpVersionChosen(IpVersionChoice.IPV6)) {
2524                 updateVpnInternetForSubnet(sm, vpnId, true);
2525             }
2526             if (!ipVersion.isIpVersionChosen(ipVers)) {
2527                 ipVersion = ipVersion.addVersion(ipVers);
2528             }
2529         }
2530         if (ipVersion != IpVersionChoice.UNDEFINED) {
2531             neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnId.getValue(), IpVersionChoice.IPV6, true);
2532             LOG.info("associateExtNetworkToVpn: add IPv6 Internet default route in VPN {}", vpnId.getValue());
2533             neutronvpnUtils.updateVpnInstanceWithFallback(/*routerId*/ null, vpnId, true);
2534         }
2535         return true;
2536     }
2537
2538     /**
2539      * Parses and disassociates networks list from given VPN.
2540      *
2541      * @param vpnId Uuid of given VPN.
2542      * @param networkList List list of network Ids (Uuid), which will be disassociated.
2543      * @return list of formatted strings with detailed error messages.
2544      */
2545     @Nonnull
2546     protected List<String> dissociateNetworksFromVpn(@Nonnull Uuid vpnId, @Nonnull List<Uuid> networkList) {
2547         List<String> failedNwList = new ArrayList<>();
2548         HashSet<Uuid> passedNwList = new HashSet<>();
2549         if (networkList.isEmpty()) {
2550             LOG.error("dissociateNetworksFromVpn: Failed as networks list is empty");
2551             failedNwList.add(String.format("Failed to disassociate networks from VPN %s as networks list is empty",
2552                              vpnId.getValue()));
2553             return failedNwList;
2554         }
2555         for (Uuid nw : networkList) {
2556             List<Uuid> networkSubnets = neutronvpnUtils.getSubnetIdsFromNetworkId(nw);
2557             if (networkSubnets == null) {
2558                 passedNwList.add(nw);
2559                 continue;
2560             }
2561             Network network = neutronvpnUtils.getNeutronNetwork(nw);
2562             if (network == null) {
2563                 LOG.error("dissociateNetworksFromVpn: Network {} not found in ConfigDS");
2564                 failedNwList.add(String.format("Failed to disassociate network %s as is not found in ConfigDS",
2565                         nw.getValue()));
2566                 continue;
2567             }
2568             Uuid networkVpnId = neutronvpnUtils.getVpnForNetwork(nw);
2569             if (networkVpnId == null) {
2570                 LOG.error("dissociateNetworksFromVpn: Network {} is not associated to any VPN", nw.getValue());
2571                 failedNwList.add(String.format("Failed to disassociate network %s as is not associated to any VPN",
2572                                                nw.getValue()));
2573                 continue;
2574             }
2575             if (!vpnId.equals(networkVpnId)) {
2576                 LOG.error("dissociateNetworksFromVpn: Network {} is associated to another VPN {} instead of given {}",
2577                           nw.getValue(), networkVpnId.getValue(), vpnId.getValue());
2578                 failedNwList.add(String.format("Failed to disassociate network %s as it is associated to another "
2579                                 + "vpn %s instead of given %s", nw.getValue(), networkVpnId.getValue(),
2580                                 vpnId.getValue()));
2581                 continue;
2582             }
2583             if (NeutronvpnUtils.getIsExternal(network)) {
2584                 if (disassociateExtNetworkFromVpn(vpnId, network)) {
2585                     passedNwList.add(nw);
2586                 } else {
2587                     LOG.error("dissociateNetworksFromVpn: Failed to withdraw Provider Network {} from VPN {}",
2588                               nw.getValue(), vpnId.getValue());
2589                     failedNwList.add(String.format("Failed to withdraw Provider Network %s from VPN %s", nw.getValue(),
2590                                                    vpnId.getValue()));
2591                     continue;
2592                 }
2593             }
2594             IpVersionChoice ipVersion = IpVersionChoice.UNDEFINED;
2595             for (Uuid subnet : networkSubnets) {
2596                 Subnetmap subnetmap = neutronvpnUtils.getSubnetmap(subnet);
2597                 IpVersionChoice ipVers = NeutronvpnUtils.getIpVersionFromString(subnetmap.getSubnetIp());
2598                 if (!ipVersion.isIpVersionChosen(ipVers)) {
2599                     ipVersion = ipVersion.addVersion(ipVers);
2600                 }
2601                 if (!NeutronvpnUtils.getIsExternal(network)) {
2602                     LOG.debug("dissociateNetworksFromVpn: Withdraw subnet {} from VPN {}", subnet.getValue(),
2603                             vpnId.getValue());
2604                     removeSubnetFromVpn(vpnId, subnet, null);
2605                     Set<VpnTarget> routeTargets = vpnManager.getRtListForVpn(vpnId.getValue());
2606                     vpnManager.removeRouteTargetsToSubnetAssociation(routeTargets, subnetmap.getSubnetIp(),
2607                             vpnId.getValue());
2608                     passedNwList.add(nw);
2609                 }
2610             }
2611             if (ipVersion != IpVersionChoice.UNDEFINED) {
2612                 LOG.debug("dissociateNetworksFromVpn: Updating vpnInstanceOpDataEntryupdate with ip address family {}"
2613                         + " for VPN {}", ipVersion, vpnId);
2614                 neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnId.getValue(), ipVersion, false);
2615             }
2616         }
2617         clearFromVpnMaps(vpnId, null, new ArrayList<>(passedNwList));
2618         LOG.info("dissociateNetworksFromVpn: Network(s) {} disassociated from L3VPN {} successfully",
2619                 passedNwList.toString(), vpnId.getValue());
2620         return failedNwList;
2621     }
2622
2623     private boolean disassociateExtNetworkFromVpn(@Nonnull Uuid vpnId, @Nonnull Network extNet) {
2624         if (!removeExternalNetworkFromVpn(extNet)) {
2625             return false;
2626         }
2627         // check, if there is another Provider Networks associated with given VPN
2628         List<Uuid> vpnNets = getNetworksForVpn(vpnId);
2629         if (vpnNets != null) {
2630             //Remove currently disassociated network from the list
2631             vpnNets.remove(extNet.getUuid());
2632             for (Uuid netId : vpnNets) {
2633                 if (NeutronvpnUtils.getIsExternal(getNeutronNetwork(netId))) {
2634                     LOG.error("dissociateExtNetworkFromVpn: Internet VPN {} is still associated with Provider Network "
2635                             + "{}", vpnId.getValue(), netId.getValue());
2636                     return true;
2637                 }
2638             }
2639         }
2640         //Set VPN Type is BGPVPNExternal from BGPVPNInternet
2641         LOG.info("disassociateExtNetworkFromVpn: set type {} for VPN {}",
2642                 VpnInstanceOpDataEntry.BgpvpnType.BGPVPNExternal, vpnId.getValue());
2643         neutronvpnUtils.updateVpnInstanceOpWithType(VpnInstanceOpDataEntry.BgpvpnType.BGPVPNExternal, vpnId);
2644         IpVersionChoice ipVersion = IpVersionChoice.UNDEFINED;
2645         for (Uuid snId : neutronvpnUtils.getPrivateSubnetsToExport(extNet, vpnId)) {
2646             Subnetmap sm = neutronvpnUtils.getSubnetmap(snId);
2647             if (sm == null) {
2648                 LOG.error("disassociateExtNetworkFromVpn: can not find subnet with Id {} in ConfigDS", snId.getValue());
2649                 continue;
2650             }
2651             IpVersionChoice ipVers = NeutronvpnUtils.getIpVersionFromString(sm.getSubnetIp());
2652             if (ipVers.isIpVersionChosen(IpVersionChoice.IPV4)) {
2653                 continue;
2654             }
2655             if (ipVers.isIpVersionChosen(IpVersionChoice.IPV6)) {
2656                 updateVpnInternetForSubnet(sm, vpnId, false);
2657             }
2658             if (!ipVersion.isIpVersionChosen(ipVers)) {
2659                 ipVersion = ipVersion.addVersion(ipVers);
2660             }
2661         }
2662         if (ipVersion != IpVersionChoice.UNDEFINED) {
2663             neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnId.getValue(), IpVersionChoice.IPV6, false);
2664             LOG.info("disassociateExtNetworkFromVpn: withdraw IPv6 Internet default route from VPN {}",
2665                     vpnId.getValue());
2666             neutronvpnUtils.updateVpnInstanceWithFallback(/*routerId*/ null, vpnId, false);
2667         }
2668         return true;
2669     }
2670
2671     /**
2672      * It handles the invocations to the neutronvpn:associateNetworks RPC method.
2673      */
2674     @Override
2675     // TODO Clean up the exception handling
2676     @SuppressWarnings("checkstyle:IllegalCatch")
2677     public ListenableFuture<RpcResult<AssociateNetworksOutput>> associateNetworks(AssociateNetworksInput input) {
2678
2679         AssociateNetworksOutputBuilder opBuilder = new AssociateNetworksOutputBuilder();
2680         SettableFuture<RpcResult<AssociateNetworksOutput>> result = SettableFuture.create();
2681         StringBuilder returnMsg = new StringBuilder();
2682         Uuid vpnId = input.getVpnId();
2683
2684         try {
2685             if (neutronvpnUtils.getVpnMap(vpnId) != null) {
2686                 LOG.debug("associateNetworks RPC: VpnId {}, networkList {}", vpnId.getValue(),
2687                         input.getNetworkId().toString());
2688                 List<Uuid> netIds = input.getNetworkId();
2689                 if (netIds != null && !netIds.isEmpty()) {
2690                     List<String> failed = associateNetworksToVpn(vpnId, netIds);
2691                     if (!failed.isEmpty()) {
2692                         returnMsg.append(failed);
2693                     }
2694                 }
2695             } else {
2696                 returnMsg.append("VPN not found : ").append(vpnId.getValue());
2697             }
2698             if (returnMsg.length() != 0) {
2699                 opBuilder.setResponse(
2700                         "ErrorType: PROTOCOL, ErrorTag: invalid-value, ErrorMessage: " + formatAndLog(LOG::error,
2701                                 "associate Networks to vpn {} failed due to {}", vpnId.getValue(), returnMsg));
2702                 result.set(RpcResultBuilder.<AssociateNetworksOutput>success().withResult(opBuilder.build()).build());
2703             } else {
2704                 result.set(RpcResultBuilder.<AssociateNetworksOutput>success().build());
2705             }
2706         } catch (Exception ex) {
2707             result.set(RpcResultBuilder.<AssociateNetworksOutput>failed().withError(ErrorType.APPLICATION,
2708                     formatAndLog(LOG::error, "associate Networks to vpn {} failed due to {}",
2709                             input.getVpnId().getValue(), ex.getMessage(), ex)).build());
2710         }
2711         LOG.debug("associateNetworks returns..");
2712         return result;
2713     }
2714
2715     /**
2716      * It handles the invocations to the neutronvpn:associateRouter RPC method.
2717      */
2718     @Override
2719     public ListenableFuture<RpcResult<AssociateRouterOutput>> associateRouter(AssociateRouterInput input) {
2720
2721         SettableFuture<RpcResult<AssociateRouterOutput>> result = SettableFuture.create();
2722         LOG.debug("associateRouter {}", input);
2723         StringBuilder returnMsg = new StringBuilder();
2724         Uuid vpnId = input.getVpnId();
2725         List<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.associaterouter.input.RouterIds>
2726                 routerIds = input.getRouterIds();
2727         Preconditions.checkArgument(!routerIds.isEmpty(), "associateRouter: RouterIds list is empty!");
2728         Preconditions.checkNotNull(vpnId, "associateRouter; VpnId not found!");
2729         Preconditions.checkNotNull(vpnId, "associateRouter; RouterIds not found!");
2730         for (org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.associaterouter.input
2731                 .RouterIds routerId : routerIds) {
2732             VpnMap vpnMap = neutronvpnUtils.getVpnMap(vpnId);
2733             Router rtr = neutronvpnUtils.getNeutronRouter(routerId.getRouterId());
2734             if (vpnMap != null) {
2735                 if (rtr != null) {
2736                     Uuid extVpnId = neutronvpnUtils.getVpnForRouter(routerId.getRouterId(), true);
2737                     if (vpnMap.getRouterIds() != null && vpnMap.getRouterIds().size() > 1) {
2738                         returnMsg.append("vpn ").append(vpnId.getValue()).append(" already associated to router ")
2739                                 .append(routerId.getRouterId());
2740                     } else if (extVpnId != null) {
2741                         returnMsg.append("router ").append(routerId.getRouterId()).append(" already associated to "
2742                                 + "another VPN ").append(extVpnId.getValue());
2743                     } else {
2744                         LOG.debug("associateRouter RPC: VpnId {}, routerId {}", vpnId.getValue(),
2745                                 routerId.getRouterId());
2746                         associateRouterToVpn(vpnId, routerId.getRouterId());
2747                     }
2748                 } else {
2749                     returnMsg.append("router not found : ").append(routerId.getRouterId());
2750                 }
2751             } else {
2752                 returnMsg.append("VPN not found : ").append(vpnId.getValue());
2753             }
2754             if (returnMsg.length() != 0) {
2755                 result.set(RpcResultBuilder.<AssociateRouterOutput>failed().withWarning(ErrorType.PROTOCOL,
2756                         "invalid-value", formatAndLog(LOG::error, "associate router to vpn {} failed "
2757                                 + "due to {}", routerId.getRouterId(), returnMsg)).build());
2758             } else {
2759                 result.set(RpcResultBuilder.success(new AssociateRouterOutputBuilder().build()).build());
2760             }
2761         }
2762         LOG.debug("associateRouter returns..");
2763         return result;
2764     }
2765
2766     /**
2767      * It handles the invocations to the neutronvpn:getFixedIPsForNeutronPort RPC method.
2768      */
2769     @Override
2770     // TODO Clean up the exception handling
2771     @SuppressWarnings("checkstyle:IllegalCatch")
2772     public ListenableFuture<RpcResult<GetFixedIPsForNeutronPortOutput>> getFixedIPsForNeutronPort(
2773         GetFixedIPsForNeutronPortInput input) {
2774         GetFixedIPsForNeutronPortOutputBuilder opBuilder = new GetFixedIPsForNeutronPortOutputBuilder();
2775         SettableFuture<RpcResult<GetFixedIPsForNeutronPortOutput>> result = SettableFuture.create();
2776         Uuid portId = input.getPortId();
2777         StringBuilder returnMsg = new StringBuilder();
2778         try {
2779             List<String> fixedIPList = new ArrayList<>();
2780             Port port = neutronvpnUtils.getNeutronPort(portId);
2781             if (port != null) {
2782                 for (FixedIps ip : requireNonNullElse(port.getFixedIps(), Collections.<FixedIps>emptyList())) {
2783                     fixedIPList.add(ip.getIpAddress().stringValue());
2784                 }
2785             } else {
2786                 returnMsg.append("neutron port: ").append(portId.getValue()).append(" not found");
2787             }
2788             if (returnMsg.length() != 0) {
2789                 result.set(RpcResultBuilder.<GetFixedIPsForNeutronPortOutput>failed().withWarning(ErrorType.PROTOCOL,
2790                         "invalid-value",
2791                         formatAndLog(LOG::error, "Retrieval of FixedIPList for neutron port failed due to {}",
2792                                 returnMsg)).build());
2793             } else {
2794                 opBuilder.setFixedIPs(fixedIPList);
2795                 result.set(RpcResultBuilder.<GetFixedIPsForNeutronPortOutput>success().withResult(opBuilder.build())
2796                         .build());
2797                 result.set(RpcResultBuilder.<GetFixedIPsForNeutronPortOutput>success().build());
2798             }
2799         } catch (Exception ex) {
2800             result.set(RpcResultBuilder.<GetFixedIPsForNeutronPortOutput>failed().withError(ErrorType.APPLICATION,
2801                     formatAndLog(LOG::error, "Retrieval of FixedIPList for neutron port {} failed due to {}",
2802                             portId.getValue(), ex.getMessage(), ex)).build());
2803         }
2804         return result;
2805     }
2806
2807     /**
2808      * It handles the invocations to the neutronvpn:dissociateNetworks RPC method.
2809      */
2810     @Override
2811     // TODO Clean up the exception handling
2812     @SuppressWarnings("checkstyle:IllegalCatch")
2813     public ListenableFuture<RpcResult<DissociateNetworksOutput>> dissociateNetworks(DissociateNetworksInput input) {
2814
2815         DissociateNetworksOutputBuilder opBuilder = new DissociateNetworksOutputBuilder();
2816         SettableFuture<RpcResult<DissociateNetworksOutput>> result = SettableFuture.create();
2817
2818         LOG.debug("dissociateNetworks {}", input);
2819         StringBuilder returnMsg = new StringBuilder();
2820         Uuid vpnId = input.getVpnId();
2821
2822         try {
2823             if (neutronvpnUtils.getVpnMap(vpnId) != null) {
2824                 LOG.debug("dissociateNetworks RPC: VpnId {}, networkList {}", vpnId.getValue(),
2825                         input.getNetworkId().toString());
2826                 List<Uuid> netIds = input.getNetworkId();
2827                 if (netIds != null && !netIds.isEmpty()) {
2828                     List<String> failed = dissociateNetworksFromVpn(vpnId, netIds);
2829                     if (!failed.isEmpty()) {
2830                         returnMsg.append(failed);
2831                     }
2832                 }
2833             } else {
2834                 returnMsg.append("VPN not found : ").append(vpnId.getValue());
2835             }
2836             if (returnMsg.length() != 0) {
2837                 opBuilder.setResponse(
2838                         "ErrorType: PROTOCOL, ErrorTag: invalid-value, ErrorMessage: " + formatAndLog(LOG::error,
2839                                 "dissociate Networks to vpn {} failed due to {}", vpnId.getValue(),
2840                                 returnMsg));
2841                 result.set(RpcResultBuilder.<DissociateNetworksOutput>success().withResult(opBuilder.build()).build());
2842             } else {
2843                 result.set(RpcResultBuilder.<DissociateNetworksOutput>success().build());
2844             }
2845         } catch (Exception ex) {
2846             result.set(RpcResultBuilder.<DissociateNetworksOutput>failed().withError(ErrorType.APPLICATION,
2847                     formatAndLog(LOG::error, "dissociate Networks to vpn {} failed due to {}",
2848                             input.getVpnId().getValue(), ex.getMessage(), ex)).build());
2849         }
2850         LOG.debug("dissociateNetworks returns..");
2851         return result;
2852     }
2853
2854     /**
2855      * It handles the invocations to the neutronvpn:dissociateRouter RPC method.
2856      */
2857     @Override
2858     // TODO Clean up the exception handling
2859     @SuppressWarnings("checkstyle:IllegalCatch")
2860     public ListenableFuture<RpcResult<DissociateRouterOutput>> dissociateRouter(DissociateRouterInput input) {
2861
2862         SettableFuture<RpcResult<DissociateRouterOutput>> result = SettableFuture.create();
2863
2864         LOG.debug("dissociateRouter {}", input);
2865         StringBuilder returnMsg = new StringBuilder();
2866         Uuid vpnId = input.getVpnId();
2867         List<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.dissociaterouter.input
2868                 .RouterIds> routerIdList = input.getRouterIds();
2869         String routerIdsString = "";
2870         Preconditions.checkArgument(!routerIdList.isEmpty(), "dissociateRouter: RouterIds list is empty!");
2871         Preconditions.checkNotNull(vpnId, "dissociateRouter: vpnId not found!");
2872         Preconditions.checkNotNull(routerIdList, "dissociateRouter: routerIdList not found!");
2873         if (neutronvpnUtils.getVpnMap(vpnId) != null) {
2874             for (org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.dissociaterouter.input
2875                     .RouterIds routerId : routerIdList) {
2876                 try {
2877                     if (routerId != null) {
2878                         routerIdsString += routerId.getRouterId() + ", ";
2879                         Router rtr = neutronvpnUtils.getNeutronRouter(routerId.getRouterId());
2880                         if (rtr != null) {
2881                             Uuid routerVpnId = neutronvpnUtils.getVpnForRouter(routerId.getRouterId(), true);
2882                             if (routerVpnId == null) {
2883                                 returnMsg.append("input router ").append(routerId.getRouterId())
2884                                         .append(" not associated to any vpn yet");
2885                             } else if (vpnId.equals(routerVpnId)) {
2886                                 dissociateRouterFromVpn(vpnId, routerId.getRouterId());
2887                             } else {
2888                                 returnMsg.append("input router ").append(routerId.getRouterId())
2889                                         .append(" associated to vpn ")
2890                                         .append(routerVpnId.getValue()).append("instead of the vpn given as input");
2891                             }
2892                         } else {
2893                             returnMsg.append("router not found : ").append(routerId.getRouterId());
2894                         }
2895                     }
2896                     if (returnMsg.length() != 0) {
2897                         result.set(RpcResultBuilder.<DissociateRouterOutput>failed().withWarning(ErrorType.PROTOCOL,
2898                                 "invalid-value", formatAndLog(LOG::error, "dissociate router {} to "
2899                                                 + "vpn {} failed due to {}", routerId.getRouterId(), vpnId.getValue(),
2900                                         returnMsg)).build());
2901                     } else {
2902                         result.set(RpcResultBuilder.success(new DissociateRouterOutputBuilder().build()).build());
2903                     }
2904                 } catch (Exception ex) {
2905                     result.set(RpcResultBuilder.<DissociateRouterOutput>failed().withError(ErrorType.APPLICATION,
2906                             formatAndLog(LOG::error, "disssociate router {} to vpn {} failed due to {}",
2907                                     routerId.getRouterId(), vpnId.getValue(), ex.getMessage(), ex)).build());
2908                 }
2909             }
2910         } else {
2911             returnMsg.append("VPN not found : ").append(vpnId.getValue());
2912         }
2913
2914         LOG.debug("dissociateRouter returns..");
2915         return result;
2916     }
2917
2918     protected void handleNeutronRouterDeleted(Uuid routerId, List<Uuid> routerSubnetIds) {
2919         // check if the router is associated to some VPN
2920         Uuid vpnId = neutronvpnUtils.getVpnForRouter(routerId, true);
2921         Uuid internetVpnId = neutronvpnUtils.getInternetvpnUuidBoundToRouterId(routerId);
2922         if (vpnId != null) {
2923             // remove existing external vpn interfaces
2924             for (Uuid subnetId : routerSubnetIds) {
2925                 removeSubnetFromVpn(vpnId, subnetId, internetVpnId);
2926             }
2927             clearFromVpnMaps(vpnId, routerId, null);
2928         } else {
2929             // remove existing internal vpn interfaces
2930             for (Uuid subnetId : routerSubnetIds) {
2931                 removeSubnetFromVpn(routerId, subnetId, internetVpnId);
2932             }
2933         }
2934         // delete entire vpnMaps node for internal VPN
2935         deleteVpnMapsNode(routerId);
2936
2937         // delete vpn-instance for internal VPN
2938         deleteVpnInstance(routerId);
2939     }
2940
2941     protected Subnet getNeutronSubnet(Uuid subnetId) {
2942         return neutronvpnUtils.getNeutronSubnet(subnetId);
2943     }
2944
2945     @Nullable
2946     protected IpAddress getNeutronSubnetGateway(Uuid subnetId) {
2947         Subnet sn = neutronvpnUtils.getNeutronSubnet(subnetId);
2948         if (null != sn) {
2949             return sn.getGatewayIp();
2950         }
2951         return null;
2952     }
2953
2954
2955     protected Network getNeutronNetwork(Uuid networkId) {
2956         return neutronvpnUtils.getNeutronNetwork(networkId);
2957     }
2958
2959     protected Port getNeutronPort(String name) {
2960         return neutronvpnUtils.getNeutronPort(new Uuid(name));
2961     }
2962
2963     protected Port getNeutronPort(Uuid portId) {
2964         return neutronvpnUtils.getNeutronPort(portId);
2965     }
2966
2967     protected Uuid getNetworkForSubnet(Uuid subnetId) {
2968         return neutronvpnUtils.getNetworkForSubnet(subnetId);
2969     }
2970
2971     protected List<Uuid> getNetworksForVpn(Uuid vpnId) {
2972         return neutronvpnUtils.getNetworksForVpn(vpnId);
2973     }
2974
2975     /**
2976      * Implementation of the "vpnservice:neutron-ports-show" Karaf CLI command.
2977      *
2978      * @return a List of String to be printed on screen
2979      * @throws ReadFailedException if there was a problem reading from the data store
2980      */
2981     public List<String> showNeutronPortsCLI() throws ReadFailedException {
2982         List<String> result = new ArrayList<>();
2983         result.add(String.format(" %-36s  %-19s  %-13s  %-20s ", "Port ID", "Mac Address", "Prefix Length",
2984             "IP Address"));
2985         result.add("-------------------------------------------------------------------------------------------");
2986         InstanceIdentifier<Ports> portidentifier = InstanceIdentifier.create(Neutron.class).child(Ports.class);
2987
2988         Optional<Ports> ports = syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION, portidentifier);
2989         if (ports.isPresent() && ports.get().getPort() != null) {
2990             for (Port port : requireNonNullElse(ports.get().getPort(), Collections.<Port>emptyList())) {
2991                 List<FixedIps> fixedIPs = port.getFixedIps();
2992                 if (fixedIPs != null && !fixedIPs.isEmpty()) {
2993                     List<String> ipList = new ArrayList<>();
2994                     for (FixedIps fixedIp : fixedIPs) {
2995                         IpAddress ipAddress = fixedIp.getIpAddress();
2996                         if (ipAddress.getIpv4Address() != null) {
2997                             ipList.add(ipAddress.getIpv4Address().getValue());
2998                         } else {
2999                             ipList.add(ipAddress.getIpv6Address().getValue());
3000                         }
3001                     }
3002                     result.add(String.format(" %-36s  %-19s  %-13s  %-20s ", port.getUuid().getValue(), port
3003                             .getMacAddress().getValue(), neutronvpnUtils.getIPPrefixFromPort(port),
3004                             ipList.toString()));
3005                 } else {
3006                     result.add(String.format(" %-36s  %-19s  %-13s  %-20s ", port.getUuid().getValue(), port
3007                             .getMacAddress().getValue(), "Not Assigned", "Not Assigned"));
3008                 }
3009             }
3010         }
3011
3012         return result;
3013     }
3014
3015     /**
3016      * Implementation of the "vpnservice:l3vpn-config-show" karaf CLI command.
3017      *
3018      * @param vpnuuid Uuid of the VPN whose config must be shown
3019      * @return formatted output list
3020      * @throws InterruptedException if there was a thread related problem getting the data to display
3021      * @throws ExecutionException if there was any other problem getting the data to display
3022      */
3023     public List<String> showVpnConfigCLI(Uuid vpnuuid) throws InterruptedException, ExecutionException {
3024         List<String> result = new ArrayList<>();
3025         if (vpnuuid == null) {
3026             result.add("");
3027             result.add("Displaying VPN config for all VPNs");
3028             result.add("To display VPN config for a particular VPN, use the following syntax");
3029             result.add(getshowVpnConfigCLIHelp());
3030         }
3031         RpcResult<GetL3VPNOutput> rpcResult = getL3VPN(new GetL3VPNInputBuilder().setId(vpnuuid).build()).get();
3032         if (rpcResult.isSuccessful()) {
3033             result.add("");
3034             result.add(String.format(" %-37s %-37s %-7s ", "VPN ID", "Tenant ID", "RD"));
3035             result.add("");
3036             result.add(String.format(" %-80s ", "Import-RTs"));
3037             result.add("");
3038             result.add(String.format(" %-80s ", "Export-RTs"));
3039             result.add("");
3040             result.add(String.format(" %-76s ", "Subnet IDs"));
3041             result.add("");
3042             result.add("------------------------------------------------------------------------------------");
3043             result.add("");
3044             for (org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.VpnInstance vpn :
3045                     requireNonNullElse(rpcResult.getResult().getL3vpnInstances(),
3046                         Collections.<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602
3047                             .VpnInstance>emptyList())) {
3048                 String tenantId = vpn.getTenantId() != null ? vpn.getTenantId().getValue()
3049                         : "\"                 " + "                  \"";
3050                 result.add(String.format(" %-37s %-37s %-7s ", vpn.getId().getValue(), tenantId,
3051                         vpn.getRouteDistinguisher()));
3052                 result.add("");
3053                 result.add(String.format(" %-80s ", vpn.getImportRT()));
3054                 result.add("");
3055                 result.add(String.format(" %-80s ", vpn.getExportRT()));
3056                 result.add("");
3057
3058                 Uuid vpnid = vpn.getId();
3059                 List<Uuid> subnetList = neutronvpnUtils.getSubnetsforVpn(vpnid);
3060                 if (!subnetList.isEmpty()) {
3061                     for (Uuid subnetuuid : subnetList) {
3062                         result.add(String.format(" %-76s ", subnetuuid.getValue()));
3063                     }
3064                 } else {
3065                     result.add(String.format(" %-76s ", "\"                                    \""));
3066                 }
3067                 result.add("");
3068                 result.add("----------------------------------------");
3069                 result.add("");
3070             }
3071         } else {
3072             String errortag = rpcResult.getErrors().iterator().next().getTag();
3073             if (Objects.equals(errortag, "")) {
3074                 result.add("");
3075                 result.add("No VPN has been configured yet");
3076             } else if (Objects.equals(errortag, "invalid-value")) {
3077                 result.add("");
3078                 result.add("VPN " + vpnuuid.getValue() + " is not present");
3079             } else {
3080                 result.add("error getting VPN info : " + rpcResult.getErrors());
3081                 result.add(getshowVpnConfigCLIHelp());
3082             }
3083         }
3084         return result;
3085     }
3086
3087     protected void createExternalVpnInterfaces(Uuid extNetId) {
3088         if (extNetId == null) {
3089             LOG.error("createExternalVpnInterfaces: external network is null");
3090             return;
3091         }
3092
3093         Collection<String> extElanInterfaces = elanService.getExternalElanInterfaces(extNetId.getValue());
3094         if (extElanInterfaces == null || extElanInterfaces.isEmpty()) {
3095             LOG.error("No external ports attached to external network {}", extNetId.getValue());
3096             return;
3097         }
3098
3099         ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
3100             for (String elanInterface : extElanInterfaces) {
3101                 createExternalVpnInterface(extNetId, elanInterface, tx);
3102             }
3103         }), LOG, "Error creating external VPN interfaces for {}", extNetId);
3104     }
3105
3106     // TODO Clean up the exception handling
3107     @SuppressWarnings("checkstyle:IllegalCatch")
3108     protected void removeExternalVpnInterfaces(Uuid extNetId) {
3109         Collection<String> extElanInterfaces = elanService.getExternalElanInterfaces(extNetId.getValue());
3110         if (extElanInterfaces == null || extElanInterfaces.isEmpty()) {
3111             LOG.error("No external ports attached for external network {}", extNetId.getValue());
3112             return;
3113         }
3114         ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
3115             for (String elanInterface : extElanInterfaces) {
3116                 InstanceIdentifier<VpnInterface> vpnIfIdentifier = NeutronvpnUtils
3117                         .buildVpnInterfaceIdentifier(elanInterface);
3118                 LOG.info("Removing vpn interface {}", elanInterface);
3119                 tx.delete(vpnIfIdentifier);
3120             }
3121         }), LOG, "Error removing external VPN interfaces for {}", extNetId);
3122     }
3123
3124     private void createExternalVpnInterface(Uuid vpnId, String infName,
3125                                             TypedWriteTransaction<Configuration> wrtConfigTxn) {
3126         writeVpnInterfaceToDs(Collections.singletonList(vpnId), infName, null, vpnId /* external network id */,
3127                 false /* not a router iface */, wrtConfigTxn);
3128     }
3129
3130     // TODO Clean up the exception handling
3131     @SuppressWarnings("checkstyle:IllegalCatch")
3132     private void writeVpnInterfaceToDs(@Nonnull Collection<Uuid> vpnIdList, String infName,
3133             @Nullable Adjacencies adjacencies, Uuid networkUuid, Boolean isRouterInterface,
3134             TypedWriteTransaction<Configuration> wrtConfigTxn) {
3135         if (vpnIdList.isEmpty() || infName == null) {
3136             LOG.error("vpnid is empty or interface({}) is null", infName);
3137             return;
3138         }
3139         if (wrtConfigTxn == null) {
3140             ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
3141                 tx -> writeVpnInterfaceToDs(vpnIdList, infName, adjacencies, networkUuid, isRouterInterface, tx)), LOG,
3142                 "Error writing VPN interface");
3143             return;
3144         }
3145         List<VpnInstanceNames> vpnIdListStruct = new ArrayList<>();
3146         for (Uuid vpnId: vpnIdList) {
3147             VpnInstanceNames vpnInstance = VpnHelper.getVpnInterfaceVpnInstanceNames(vpnId.getValue(),
3148                                    AssociatedSubnetType.V4AndV6Subnets);
3149             vpnIdListStruct.add(vpnInstance);
3150         }
3151
3152         InstanceIdentifier<VpnInterface> vpnIfIdentifier = NeutronvpnUtils.buildVpnInterfaceIdentifier(infName);
3153         VpnInterfaceBuilder vpnb = new VpnInterfaceBuilder().withKey(new VpnInterfaceKey(infName))
3154                 .setName(infName)
3155                 .setVpnInstanceNames(vpnIdListStruct)
3156                 .setRouterInterface(isRouterInterface);
3157         LOG.info("Network Id is {}", networkUuid);
3158         if (networkUuid != null) {
3159             Network portNetwork = neutronvpnUtils.getNeutronNetwork(networkUuid);
3160             ProviderTypes providerType = NeutronvpnUtils.getProviderNetworkType(portNetwork);
3161             NetworkAttributes.NetworkType networkType = providerType != null
3162                     ? NetworkAttributes.NetworkType.valueOf(providerType.getName()) : null;
3163             String segmentationId = NeutronvpnUtils.getSegmentationIdFromNeutronNetwork(portNetwork);
3164             vpnb.setNetworkId(networkUuid).setNetworkType(networkType)
3165                 .setSegmentationId(segmentationId != null ? Long.parseLong(segmentationId) : 0L);
3166         }
3167
3168         if (adjacencies != null) {
3169             vpnb.addAugmentation(Adjacencies.class, adjacencies);
3170         }
3171         VpnInterface vpnIf = vpnb.build();
3172         try {
3173             LOG.info("Creating vpn interface {}", vpnIf);
3174             wrtConfigTxn.put(vpnIfIdentifier, vpnIf);
3175         } catch (Exception ex) {
3176             LOG.error("Creation of vpninterface {} failed", infName, ex);
3177         }
3178     }
3179
3180     private void updateVpnInterfaceWithAdjacencies(Uuid vpnId, String infName, Adjacencies adjacencies,
3181                                                    TypedWriteTransaction<Configuration> wrtConfigTxn) {
3182         if (vpnId == null || infName == null) {
3183             LOG.error("vpn id or interface is null");
3184             return;
3185         }
3186         if (wrtConfigTxn == null) {
3187             LOG.error("updateVpnInterfaceWithAdjancies called with wrtConfigTxn as null");
3188             ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
3189                 updateVpnInterfaceWithAdjacencies(vpnId, infName, adjacencies, tx);
3190             }), LOG, "Error updating VPN interface with adjacencies");
3191             return;
3192         }
3193
3194         InstanceIdentifier<VpnInterface> vpnIfIdentifier = NeutronvpnUtils.buildVpnInterfaceIdentifier(infName);
3195         boolean isLockAcquired = false;
3196         try {
3197             isLockAcquired = interfaceLock.tryLock(infName, LOCK_WAIT_TIME, TimeUnit.SECONDS);
3198             Optional<VpnInterface> optionalVpnInterface = SingleTransactionDataBroker
3199                     .syncReadOptional(dataBroker, LogicalDatastoreType
3200                     .CONFIGURATION, vpnIfIdentifier);
3201             if (optionalVpnInterface.isPresent()) {
3202                 VpnInterfaceBuilder vpnIfBuilder = new VpnInterfaceBuilder(optionalVpnInterface.get());
3203                 LOG.debug("Updating vpn interface {} with new adjacencies", infName);
3204
3205                 if (adjacencies == null) {
3206                     if (isLockAcquired) {
3207                         interfaceLock.unlock(infName);
3208                     }
3209                     return;
3210                 }
3211                 vpnIfBuilder.addAugmentation(Adjacencies.class, adjacencies);
3212                 if (optionalVpnInterface.get().getVpnInstanceNames() != null) {
3213                     List<VpnInstanceNames> listVpnInstances = new ArrayList<>(
3214                         optionalVpnInterface.get().getVpnInstanceNames());
3215                     if (listVpnInstances.isEmpty() || !VpnHelper
3216                         .doesVpnInterfaceBelongToVpnInstance(vpnId.getValue(), listVpnInstances)) {
3217                         VpnInstanceNames vpnInstance = VpnHelper
3218                              .getVpnInterfaceVpnInstanceNames(vpnId.getValue(), AssociatedSubnetType.V4AndV6Subnets);
3219                         listVpnInstances.add(vpnInstance);
3220                         vpnIfBuilder.setVpnInstanceNames(listVpnInstances);
3221                     }
3222                 } else {
3223                     VpnInstanceNames vpnInstance = VpnHelper
3224                          .getVpnInterfaceVpnInstanceNames(vpnId.getValue(), AssociatedSubnetType.V4AndV6Subnets);
3225                     List<VpnInstanceNames> listVpnInstances = new ArrayList<>();
3226                     listVpnInstances.add(vpnInstance);
3227                     vpnIfBuilder.setVpnInstanceNames(listVpnInstances);
3228                 }
3229                 LOG.info("Updating vpn interface {} with new adjacencies", infName);
3230                 wrtConfigTxn.put(vpnIfIdentifier, vpnIfBuilder.build());
3231             }
3232         } catch (IllegalStateException | ReadFailedException ex) {
3233             LOG.error("Update of vpninterface {} failed", infName, ex);
3234         } finally {
3235             if (isLockAcquired) {
3236                 interfaceLock.unlock(infName);
3237             }
3238         }
3239     }
3240
3241     private String getshowVpnConfigCLIHelp() {
3242         StringBuilder help = new StringBuilder("Usage:");
3243         help.append("display vpn-config [-vid/--vpnid <id>]");
3244         return help.toString();
3245     }
3246
3247     protected void dissociatefixedIPFromFloatingIP(String fixedNeutronPortName) {
3248         floatingIpMapListener.dissociatefixedIPFromFloatingIP(fixedNeutronPortName);
3249     }
3250
3251     @Override
3252     public ListenableFuture<RpcResult<CreateEVPNOutput>> createEVPN(CreateEVPNInput input) {
3253         return neutronEvpnManager.createEVPN(input);
3254     }
3255
3256     @Override
3257     public ListenableFuture<RpcResult<GetEVPNOutput>> getEVPN(GetEVPNInput input) {
3258         return neutronEvpnManager.getEVPN(input);
3259     }
3260
3261     @Override
3262     public ListenableFuture<RpcResult<DeleteEVPNOutput>> deleteEVPN(DeleteEVPNInput input) {
3263         return neutronEvpnManager.deleteEVPN(input);
3264     }
3265
3266     private boolean addExternalNetworkToVpn(Network extNet, Uuid vpnId) {
3267         Uuid extNetId = extNet.getUuid();
3268         InstanceIdentifier<Networks> extNetIdentifier = InstanceIdentifier.builder(ExternalNetworks.class)
3269                 .child(Networks.class, new NetworksKey(extNetId)).build();
3270
3271         try {
3272             Optional<Networks> optionalExtNets =
3273                     SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
3274                                                                  extNetIdentifier);
3275             if (!optionalExtNets.isPresent()) {
3276                 LOG.error("addExternalNetworkToVpn: Provider Network {} is not present in ConfigDS",
3277                           extNetId.getValue());
3278                 return false;
3279             }
3280             NetworksBuilder builder = new NetworksBuilder(optionalExtNets.get());
3281             builder.setVpnid(vpnId);
3282             Networks networks = builder.build();
3283             // Add Networks object to the ExternalNetworks list
3284             LOG.trace("addExternalNetworkToVpn: Set VPN Id {} for Provider Network {}", vpnId.getValue(),
3285                       extNetId.getValue());
3286             SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, extNetIdentifier,
3287                                                   networks);
3288             return true;
3289         } catch (TransactionCommitFailedException | ReadFailedException ex) {
3290             LOG.error("addExternalNetworkToVpn: Failed to set VPN Id {} to Provider Network {}: ", vpnId.getValue(),
3291                       extNetId.getValue(), ex);
3292         }
3293         return false;
3294     }
3295
3296     private boolean removeExternalNetworkFromVpn(Network extNet) {
3297         Uuid extNetId = extNet.getUuid();
3298         InstanceIdentifier<Networks> extNetsId = InstanceIdentifier.builder(ExternalNetworks.class)
3299             .child(Networks.class, new NetworksKey(extNetId)).build();
3300         try {
3301             Optional<Networks> optionalNets =
3302                     SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
3303                             extNetsId);
3304             NetworksBuilder builder = null;
3305             if (optionalNets.isPresent()) {
3306                 builder = new NetworksBuilder(optionalNets.get());
3307             } else {
3308                 LOG.error("removeExternalNetworkFromVpn: Provider Network {} is not present in the ConfigDS",
3309                         extNetId.getValue());
3310                 return false;
3311             }
3312             builder.setVpnid(null);
3313             Networks networks = builder.build();
3314             LOG.info("removeExternalNetworkFromVpn: Withdraw VPN Id from Provider Network {} node",
3315                     extNetId.getValue());
3316             SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, extNetsId, networks);
3317             return true;
3318         } catch (TransactionCommitFailedException | ReadFailedException ex) {
3319             LOG.error("removeExternalNetworkFromVpn: Failed to withdraw VPN Id from Provider Network node {}: ",
3320                     extNetId.getValue(), ex);
3321         }
3322         return false;
3323     }
3324
3325     private Optional<String> getExistingOperationalVpn(String primaryRd) {
3326         Optional<String> existingVpnName = Optional.of(primaryRd);
3327         Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataOptional;
3328         try {
3329             vpnInstanceOpDataOptional = SingleTransactionDataBroker
3330                 .syncReadOptional(dataBroker, LogicalDatastoreType.OPERATIONAL,
3331                     neutronvpnUtils.getVpnOpDataIdentifier(primaryRd));
3332         } catch (ReadFailedException e) {
3333             LOG.error("getExistingOperationalVpn: Exception while checking operational status of vpn with rd {}",
3334                     primaryRd, e);
3335             /*Read failed. We don't know if a VPN exists or not.
3336             * Return primaryRd to halt caller execution, to be safe.*/
3337             return existingVpnName;
3338         }
3339         if (vpnInstanceOpDataOptional.isPresent()) {
3340             existingVpnName = Optional.of(vpnInstanceOpDataOptional.get().getVpnInstanceName());
3341         } else {
3342             existingVpnName = Optional.absent();
3343         }
3344         return existingVpnName;
3345     }
3346
3347     private static String formatAndLog(Consumer<String> logger, String template, Object arg) {
3348         return logAndReturnMessage(logger, MessageFormatter.format(template, arg));
3349     }
3350
3351     private static String formatAndLog(Consumer<String> logger, String template, Object arg1, Object arg2) {
3352         return logAndReturnMessage(logger, MessageFormatter.format(template, arg1, arg2));
3353     }
3354
3355     private static String formatAndLog(Consumer<String> logger, String template, Object... args) {
3356         return logAndReturnMessage(logger, MessageFormatter.arrayFormat(template, args));
3357     }
3358
3359     private static String logAndReturnMessage(Consumer<String> logger, FormattingTuple tuple) {
3360         String message = tuple.getMessage();
3361         logger.accept(message);
3362         return message;
3363     }
3364
3365     protected void addV6PrivateSubnetToExtNetwork(@Nonnull Uuid routerId, @Nonnull Uuid internetVpnId,
3366                                                   @Nonnull Subnetmap subnetMap) {
3367         updateVpnInternetForSubnet(subnetMap, internetVpnId, true);
3368         neutronvpnUtils.updateVpnInstanceWithFallback(routerId, internetVpnId, true);
3369         if (neutronvpnUtils.shouldVpnHandleIpVersionChoiceChange(IpVersionChoice.IPV6, routerId, true)) {
3370             neutronvpnUtils.updateVpnInstanceWithIpFamily(internetVpnId.getValue(), IpVersionChoice.IPV6, true);
3371             LOG.info("addV6PrivateSubnetToExtNetwork: Advertise IPv6 Private Subnet {} to Internet VPN {}",
3372                     subnetMap.getId().getValue(), internetVpnId.getValue());
3373         }
3374     }
3375
3376     protected void removeV6PrivateSubnetToExtNetwork(@Nonnull Uuid routerId, @Nonnull Uuid internetVpnId,
3377                                                      @Nonnull Subnetmap subnetMap) {
3378         updateVpnInternetForSubnet(subnetMap, internetVpnId, false);
3379         neutronvpnUtils.updateVpnInstanceWithFallback(routerId, internetVpnId, false);
3380     }
3381
3382     protected void programV6InternetFallbackFlow(Uuid routerId, Uuid internetVpnId, int addOrRemove) {
3383         if (neutronvpnUtils.isV6SubnetPartOfRouter(routerId)) {
3384             LOG.debug("processV6InternetFlowsForRtr: Successfully {} V6 internet vpn {} default fallback rule "
3385                             + "for the router {}", addOrRemove == NwConstants.ADD_FLOW ? "added" : "removed",
3386                     internetVpnId.getValue(), routerId.getValue());
3387             neutronvpnUtils.updateVpnInstanceWithFallback(routerId, internetVpnId, addOrRemove == NwConstants.ADD_FLOW
3388                     ? true : false);
3389         }
3390     }
3391
3392     private static ReentrantLock lockForUuid(Uuid uuid) {
3393         // FIXME: prove that this locks only on Uuids and not some other entity or create a separate lock domain
3394         return JvmGlobalLocks.getLockForString(uuid.getValue());
3395     }
3396 }