Use JvmGlobalLocks in neutronmanager
[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             if (sn != null && !FibHelper.doesPrefixBelongToSubnet(ipPrefix, sn.getSubnetIp(), false)) {
831                 continue;
832             }
833             Subnetmap snTemp = sn != null ? sn : neutronvpnUtils.getSubnetmap(ip.getSubnetId());
834             Uuid vpnId = snTemp != null ? snTemp.getVpnId() : null;
835             if (vpnId != null) {
836                 neutronvpnUtils.createVpnPortFixedIpToPort(vpnId.getValue(), ipValue,
837                         infName, port.getMacAddress().getValue(), isRouterInterface, wrtConfigTxn);
838                 //Create Neutron port adjacency if VPN presence is existing for subnet
839                 Adjacency vmAdj = new AdjacencyBuilder().withKey(new AdjacencyKey(ipPrefix)).setIpAddress(ipPrefix)
840                         .setMacAddress(port.getMacAddress().getValue()).setAdjacencyType(AdjacencyType.PrimaryAdjacency)
841                         .setSubnetId(ip.getSubnetId()).build();
842                 if (!adjList.contains(vmAdj)) {
843                     adjList.add(vmAdj);
844                 }
845             }
846             Uuid routerId = snTemp != null ? snTemp.getRouterId() : null;
847             if (snTemp != null && snTemp.getInternetVpnId() != null) {
848                 neutronvpnUtils.createVpnPortFixedIpToPort(snTemp.getInternetVpnId().getValue(),
849                     ipValue, infName, port.getMacAddress().getValue(), isRouterInterface, wrtConfigTxn);
850             }
851             if (routerId != null) {
852                 Router rtr = neutronvpnUtils.getNeutronRouter(routerId);
853                 if (rtr != null && rtr.getRoutes() != null) {
854                     List<Routes> routeList = rtr.getRoutes();
855                     // create extraroute Adjacence for each ipValue,
856                     // because router can have IPv4 and IPv6 subnet ports, or can have
857                     // more that one IPv4 subnet port or more than one IPv6 subnet port
858                     List<Adjacency> erAdjList = getAdjacencyforExtraRoute(routeList, ipValue);
859                     if (!erAdjList.isEmpty()) {
860                         adjList.addAll(erAdjList);
861                     }
862                 }
863             }
864         }
865         return new AdjacenciesBuilder().setAdjacency(adjList).build();
866     }
867
868     protected void createVpnInterface(Collection<Uuid> vpnIds, Port port,
869                                       @Nullable TypedWriteTransaction<Configuration> wrtConfigTxn) {
870         boolean isRouterInterface = false;
871         if (port.getDeviceOwner() != null) {
872             isRouterInterface = NeutronConstants.DEVICE_OWNER_ROUTER_INF.equals(port.getDeviceOwner());
873         }
874         String infName = port.getUuid().getValue();
875         // Handling cluster reboot scenario where VpnInterface already exists in datastore.
876         VpnInterface vpnIface = VpnHelper.getVpnInterface(dataBroker, infName);
877         Adjacencies adjs = createPortIpAdjacencies(port, isRouterInterface, wrtConfigTxn, null, vpnIface);
878         LOG.trace("createVpnInterface for Port: {}, isRouterInterface: {}", infName, isRouterInterface);
879         writeVpnInterfaceToDs(vpnIds, infName, adjs, port.getNetworkId(), isRouterInterface, wrtConfigTxn);
880     }
881
882     protected void withdrawPortIpFromVpnIface(Uuid vpnId, Uuid internetVpnId,
883                        Port port, Subnetmap sn, TypedWriteTransaction<Configuration> wrtConfigTxn) {
884         String infName = port.getUuid().getValue();
885         InstanceIdentifier<VpnInterface> vpnIfIdentifier = NeutronvpnUtils.buildVpnInterfaceIdentifier(infName);
886         Optional<VpnInterface> optionalVpnInterface = null;
887         LOG.debug("withdrawPortIpFromVpnIface vpn {} internetVpn {} Port {}",
888                   vpnId, internetVpnId, infName);
889         try {
890             optionalVpnInterface = SingleTransactionDataBroker
891                     .syncReadOptional(dataBroker, LogicalDatastoreType
892                     .CONFIGURATION, vpnIfIdentifier);
893         } catch (ReadFailedException e) {
894             LOG.error("withdrawPortIpFromVpnIface: Error reading the VPN interface for {}", vpnIfIdentifier, e);
895             return;
896         }
897         if (!optionalVpnInterface.isPresent()) {
898             return;
899         }
900         LOG.trace("withdraw adjacencies for Port: {} subnet {}", port.getUuid().getValue(),
901                 sn != null ? sn.getSubnetIp() : "null");
902         List<Adjacency> vpnAdjsList =
903             requireNonNullElse(optionalVpnInterface.get().augmentation(Adjacencies.class).getAdjacency(),
904                 Collections.emptyList());
905         List<Adjacency> updatedAdjsList = new ArrayList<>();
906         boolean isIpFromAnotherSubnet = false;
907         for (Adjacency adj : vpnAdjsList) {
908             String adjString = FibHelper.getIpFromPrefix(adj.getIpAddress());
909             if (sn == null || !Objects.equals(adj.getSubnetId(), sn.getId())) {
910                 if (adj.getAdjacencyType() == AdjacencyType.PrimaryAdjacency) {
911                     isIpFromAnotherSubnet = true;
912                 }
913                 updatedAdjsList.add(adj);
914                 continue;
915             }
916             if (adj.getAdjacencyType() == AdjacencyType.PrimaryAdjacency) {
917                 LOG.error("withdrawPortIpFromVpnIface: suppressing primaryAdjacency {} FixedIp for vpnId {}",
918                       adjString, vpnId);
919                 if (vpnId != null) {
920                     neutronvpnUtils.removeVpnPortFixedIpToPort(vpnId.getValue(),
921                             String.valueOf(adjString), wrtConfigTxn);
922                 }
923                 if (internetVpnId != null) {
924                     neutronvpnUtils.removeVpnPortFixedIpToPort(internetVpnId.getValue(),
925                           String.valueOf(adjString), wrtConfigTxn);
926                 }
927             } else {
928                 if (NeutronConstants.DEVICE_OWNER_ROUTER_INF.equals(port.getDeviceOwner())
929                         && sn.getRouterId() != null) {
930                     Router rtr = neutronvpnUtils.getNeutronRouter(sn.getRouterId());
931                     if (rtr != null && rtr.getRoutes() != null) {
932                         List<Routes> extraRoutesToRemove = new ArrayList<>();
933                         for (Routes rt: rtr.getRoutes()) {
934                             if (rt.getNexthop().toString().equals(adjString)) {
935                                 extraRoutesToRemove.add(rt);
936                             }
937                         }
938                         if (vpnId != null) {
939                             LOG.error("withdrawPortIpFromVpnIface: suppressing extraRoute {} for vpnId {}",
940                                   extraRoutesToRemove, vpnId);
941                             removeAdjacencyforExtraRoute(vpnId, extraRoutesToRemove);
942                         }
943                         /* removeAdjacencyforExtraRoute done also for internet-vpn-id, in previous call */
944                     }
945                 }
946             }
947         }
948         Adjacencies adjacencies = new AdjacenciesBuilder().setAdjacency(updatedAdjsList).build();
949         if (vpnId != null) {
950             updateVpnInterfaceWithAdjacencies(vpnId, infName, adjacencies, wrtConfigTxn);
951         }
952         if (!isIpFromAnotherSubnet) {
953             // no more subnetworks for neutron port
954             if (sn != null && sn.getRouterId() != null) {
955                 removeFromNeutronRouterInterfacesMap(sn.getRouterId(), port.getUuid().getValue());
956             }
957             deleteVpnInterface(infName, null /* vpn-id */, wrtConfigTxn);
958             return;
959         }
960         return;
961     }
962
963     // TODO Clean up the exception handling
964     @SuppressWarnings("checkstyle:IllegalCatch")
965     protected void deleteVpnInterface(String infName, @Nullable String vpnId,
966                                       @Nullable TypedWriteTransaction<Configuration> wrtConfigTxn) {
967         if (wrtConfigTxn == null) {
968             ListenableFutures.addErrorLogging(
969                     txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
970                         tx -> deleteVpnInterface(infName, vpnId, tx)),
971                     LOG, "Error deleting VPN interface {} {}", infName, vpnId);
972             return;
973         }
974
975         InstanceIdentifier<VpnInterface> vpnIfIdentifier =
976             NeutronvpnUtils.buildVpnInterfaceIdentifier(infName);
977         Optional<VpnInterface> optionalVpnInterface;
978         try {
979             optionalVpnInterface =
980                 SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
981                             vpnIfIdentifier);
982         } catch (ReadFailedException ex) {
983             LOG.error("Error during deletion of vpninterface {}", infName, ex);
984             return;
985         }
986         if (!optionalVpnInterface.isPresent()) {
987             LOG.warn("Deletion of vpninterface {}, optionalVpnInterface is not present()", infName);
988             return;
989         }
990         if (vpnId != null) {
991             VpnInterface vpnInterface = optionalVpnInterface.get();
992             List<VpnInstanceNames> vpnList = vpnInterface.getVpnInstanceNames();
993             if (vpnList != null
994                 && VpnHelper.doesVpnInterfaceBelongToVpnInstance(vpnId, vpnList)) {
995                 VpnHelper.removeVpnInterfaceVpnInstanceNamesFromList(vpnId, vpnList);
996                 if (!vpnList.isEmpty()) {
997                     LOG.debug("Deleting vpn interface {} not immediately since vpnInstanceName "
998                             + "List not empty", infName);
999                     return;
1000                 }
1001                 VpnInterfaceBuilder vpnIfBuilder = new VpnInterfaceBuilder(optionalVpnInterface.get())
1002                         .setVpnInstanceNames(vpnList);
1003                 wrtConfigTxn.put(vpnIfIdentifier, vpnIfBuilder
1004                         .build());
1005             }
1006         }
1007         LOG.debug("Deleting vpn interface {}", infName);
1008         wrtConfigTxn.delete(vpnIfIdentifier);
1009     }
1010
1011     protected void removeInternetVpnFromVpnInterface(Uuid vpnId, Port port,
1012                                              TypedWriteTransaction<Configuration> writeConfigTxn,
1013                                              Subnetmap sm) {
1014         if (vpnId == null || port == null) {
1015             return;
1016         }
1017         String infName = port.getUuid().getValue();
1018         InstanceIdentifier<VpnInterface> vpnIfIdentifier = NeutronvpnUtils.buildVpnInterfaceIdentifier(infName);
1019         try {
1020             Optional<VpnInterface> optionalVpnInterface = SingleTransactionDataBroker
1021                     .syncReadOptional(dataBroker, LogicalDatastoreType
1022                     .CONFIGURATION, vpnIfIdentifier);
1023             if (optionalVpnInterface.isPresent()) {
1024                 List<VpnInstanceNames> listVpn = optionalVpnInterface.get().getVpnInstanceNames();
1025                 if (listVpn != null
1026                     && VpnHelper.doesVpnInterfaceBelongToVpnInstance(vpnId.getValue(), listVpn)) {
1027                     VpnHelper.removeVpnInterfaceVpnInstanceNamesFromList(vpnId.getValue(), listVpn);
1028                 }
1029                 VpnInterfaceBuilder vpnIfBuilder = new VpnInterfaceBuilder(optionalVpnInterface.get())
1030                          .setVpnInstanceNames(listVpn);
1031                 Adjacencies adjs = vpnIfBuilder.augmentation(Adjacencies.class);
1032                 LOG.debug("Updating vpn interface {}", infName);
1033                 List<Adjacency> adjacencyList = adjs != null ? adjs.getAdjacency() : new ArrayList<>();
1034                 Iterator<Adjacency> adjacencyIter = adjacencyList.iterator();
1035                 while (adjacencyIter.hasNext()) {
1036                     Adjacency adjacency = adjacencyIter.next();
1037                     if (adjacency.getAdjacencyType() == AdjacencyType.PrimaryAdjacency) {
1038                         continue;
1039                     }
1040                     String mipToQuery = adjacency.getIpAddress().split("/")[0];
1041                     InstanceIdentifier<LearntVpnVipToPort> id =
1042                         NeutronvpnUtils.buildLearntVpnVipToPortIdentifier(vpnId.getValue(), mipToQuery);
1043                     Optional<LearntVpnVipToPort> optionalVpnVipToPort =
1044                             SingleTransactionDataBroker.syncReadOptional(dataBroker,
1045                                     LogicalDatastoreType.OPERATIONAL, id);
1046                     if (optionalVpnVipToPort.isPresent()) {
1047                         LOG.trace("Removing adjacencies from vpninterface {} upon dissociation of router {}",
1048                              infName, vpnId);
1049                         if (listVpn == null || listVpn.isEmpty()) {
1050                             adjacencyIter.remove();
1051                         }
1052                         neutronvpnUtils.removeLearntVpnVipToPort(vpnId.getValue(), mipToQuery);
1053                         LOG.trace("Entry for fixedIP {} for port {} on VPN {} removed from VpnPortFixedIPToPortData",
1054                                 mipToQuery, infName, vpnId.getValue());
1055                     }
1056                 }
1057                 for (FixedIps ip : requireNonNullElse(port.getFixedIps(), Collections.<FixedIps>emptyList())) {
1058                     String ipValue = ip.getIpAddress().stringValue();
1059                     //skip IPv4 address
1060                     if (!NeutronvpnUtils.getIpVersionFromString(ipValue).isIpVersionChosen(IpVersionChoice.IPV6)) {
1061                         continue;
1062                     }
1063                     neutronvpnUtils.removeVpnPortFixedIpToPort(vpnId.getValue(),
1064                             ipValue, writeConfigTxn);
1065                 }
1066                 if (listVpn == null || listVpn.isEmpty()) {
1067                     if (sm != null && sm.getRouterId() != null) {
1068                         removeFromNeutronRouterInterfacesMap(sm.getRouterId(), port.getUuid().getValue());
1069                     }
1070                     deleteVpnInterface(port.getUuid().getValue(), null /* vpn-id */, writeConfigTxn);
1071                 } else {
1072                     writeConfigTxn.put(vpnIfIdentifier, vpnIfBuilder.build());
1073                 }
1074             } else {
1075                 LOG.info("removeVpnFromVpnInterface: VPN Interface {} not found", infName);
1076             }
1077         } catch (ReadFailedException ex) {
1078             LOG.error("Update of vpninterface {} failed", infName, ex);
1079         }
1080     }
1081
1082     protected void updateVpnInterface(Uuid vpnId, @Nullable Uuid oldVpnId, Port port, boolean isBeingAssociated,
1083                                       boolean isSubnetIp,
1084                                       TypedWriteTransaction<Configuration> writeConfigTxn,
1085                                       boolean isInternetVpn) {
1086         if (vpnId == null || port == null) {
1087             return;
1088         }
1089         boolean isLockAcquired = false;
1090         String infName = port.getUuid().getValue();
1091         InstanceIdentifier<VpnInterface> vpnIfIdentifier = NeutronvpnUtils.buildVpnInterfaceIdentifier(infName);
1092
1093         try {
1094             isLockAcquired = interfaceLock.tryLock(infName, LOCK_WAIT_TIME, TimeUnit.SECONDS);
1095             Optional<VpnInterface> optionalVpnInterface =
1096                     SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
1097                             vpnIfIdentifier);
1098             if (optionalVpnInterface.isPresent()) {
1099                 VpnInstanceNames vpnInstance = VpnHelper
1100                     .getVpnInterfaceVpnInstanceNames(vpnId.getValue(), AssociatedSubnetType.V4AndV6Subnets);
1101                 List<VpnInstanceNames> listVpn = new ArrayList<>(optionalVpnInterface
1102                            .get().getVpnInstanceNames());
1103                 if (oldVpnId != null
1104                     && VpnHelper.doesVpnInterfaceBelongToVpnInstance(oldVpnId.getValue(), listVpn)) {
1105                     VpnHelper.removeVpnInterfaceVpnInstanceNamesFromList(oldVpnId.getValue(), listVpn);
1106                 }
1107                 if (vpnId.getValue() != null
1108                     && !VpnHelper.doesVpnInterfaceBelongToVpnInstance(vpnId.getValue(), listVpn)) {
1109                     listVpn.add(vpnInstance);
1110                 }
1111                 VpnInterfaceBuilder vpnIfBuilder = new VpnInterfaceBuilder(optionalVpnInterface.get())
1112                         .setVpnInstanceNames(listVpn);
1113                 LOG.debug("Updating vpn interface {}", infName);
1114                 if (!isBeingAssociated) {
1115                     Adjacencies adjs = vpnIfBuilder.augmentation(Adjacencies.class);
1116                     List<Adjacency> adjacencyList = adjs != null ? adjs.getAdjacency() : new ArrayList<>();
1117                     Iterator<Adjacency> adjacencyIter = adjacencyList.iterator();
1118                     while (adjacencyIter.hasNext()) {
1119                         Adjacency adjacency = adjacencyIter.next();
1120                         String mipToQuery = adjacency.getIpAddress().split("/")[0];
1121                         InstanceIdentifier<LearntVpnVipToPort> id =
1122                             NeutronvpnUtils.buildLearntVpnVipToPortIdentifier(oldVpnId.getValue(), mipToQuery);
1123                         Optional<LearntVpnVipToPort> optionalVpnVipToPort =
1124                                 SingleTransactionDataBroker.syncReadOptional(dataBroker,
1125                                         LogicalDatastoreType.OPERATIONAL, id);
1126                         if (optionalVpnVipToPort.isPresent()) {
1127                             LOG.trace("Removing adjacencies from vpninterface {} upon dissociation of router {} "
1128                                 + "from VPN {}", infName, vpnId, oldVpnId);
1129                             adjacencyIter.remove();
1130                             neutronvpnUtils.removeLearntVpnVipToPort(oldVpnId.getValue(), mipToQuery);
1131                             LOG.trace(
1132                                     "Entry for fixedIP {} for port {} on VPN {} removed from VpnPortFixedIPToPortData",
1133                                     mipToQuery, infName, vpnId.getValue());
1134                         }
1135                     }
1136                     Adjacencies adjacencies = new AdjacenciesBuilder().setAdjacency(adjacencyList).build();
1137                     vpnIfBuilder.addAugmentation(Adjacencies.class, adjacencies);
1138                 }
1139                 for (FixedIps ip : requireNonNullElse(port.getFixedIps(), Collections.<FixedIps>emptyList())) {
1140                     String ipValue = ip.getIpAddress().stringValue();
1141                     if (oldVpnId != null) {
1142                         neutronvpnUtils.removeVpnPortFixedIpToPort(oldVpnId.getValue(),
1143                                 ipValue, writeConfigTxn);
1144                     }
1145                     if ((NeutronvpnUtils.getIpVersionFromString(ipValue) != IpVersionChoice.IPV6)
1146                          && (isInternetVpn == true)) {
1147                         continue;
1148                     }
1149
1150                     neutronvpnUtils.createVpnPortFixedIpToPort(vpnId.getValue(), ipValue, infName, port
1151                             .getMacAddress().getValue(), isSubnetIp, writeConfigTxn);
1152                 }
1153                 writeConfigTxn.put(vpnIfIdentifier, vpnIfBuilder.build());
1154             } else {
1155                 LOG.error("VPN Interface {} not found", infName);
1156             }
1157         } catch (ReadFailedException ex) {
1158             LOG.error("Updation of vpninterface {} failed", infName, ex);
1159         } finally {
1160             if (isLockAcquired) {
1161                 interfaceLock.unlock(infName);
1162             }
1163         }
1164     }
1165
1166     public void createL3InternalVpn(Uuid vpn, @Nullable String name, @Nullable Uuid tenant, @Nullable List<String> rd,
1167             @Nullable List<String> irt, @Nullable List<String> ert, @Nullable Uuid router,
1168             @Nullable List<Uuid> networks) {
1169
1170         IpVersionChoice ipVersChoices = neutronvpnUtils.getIpVersionChoicesFromRouterUuid(router);
1171
1172         // Update VPN Instance node
1173         updateVpnInstanceNode(vpn, rd, irt, ert, VpnInstance.Type.L3, 0 /*l3vni*/, ipVersChoices);
1174
1175         // Update local vpn-subnet DS
1176         updateVpnMaps(vpn, name, router, tenant, networks);
1177
1178         if (router != null) {
1179             Uuid existingVpn = neutronvpnUtils.getVpnForRouter(router, true);
1180             if (existingVpn != null) {
1181                 // use case when a cluster is rebooted and router add DCN is received, triggering #createL3InternalVpn
1182                 // if before reboot, router was already associated to VPN, should not proceed associating router to
1183                 // internal VPN. Adding to RouterInterfacesMap is also not needed since it's a config DS and will be
1184                 // preserved upon reboot.
1185                 // For a non-reboot case #associateRouterToInternalVPN already takes care of adding to
1186                 // RouterInterfacesMap via #createVPNInterface call.
1187                 LOG.info("Associating router to Internal VPN skipped for VPN {} due to router {} already associated "
1188                     + "to external VPN {}", vpn.getValue(), router.getValue(), existingVpn.getValue());
1189                 return;
1190             }
1191             associateRouterToInternalVpn(vpn, router);
1192         }
1193     }
1194
1195     /**
1196      * Performs the creation of a Neutron L3VPN, associating the new VPN to the
1197      * specified Neutron Networks and Routers.
1198      *
1199      * @param vpnId Uuid of the VPN tp be created
1200      * @param name Representative name of the new VPN
1201      * @param tenantId Uuid of the Tenant under which the VPN is going to be created
1202      * @param rdList Route-distinguisher for the VPN
1203      * @param irtList A list of Import Route Targets
1204      * @param ertList A list of Export Route Targets
1205      * @param routerIdsList ist of neutron router Id to associate with created VPN
1206      * @param networkList UUID of the neutron network the VPN may be associated to
1207      * @param type Type of the VPN Instance
1208      * @param l3vni L3VNI for the VPN Instance using VxLAN as the underlay
1209      * @throws Exception if association of L3VPN failed
1210      */
1211     public void createVpn(Uuid vpnId, String name, Uuid tenantId, List<String> rdList, List<String> irtList,
1212                     List<String> ertList,  @Nullable List<Uuid> routerIdsList, @Nullable List<Uuid> networkList,
1213                     VpnInstance.Type type, long l3vni) throws Exception {
1214
1215         IpVersionChoice ipVersChoices = IpVersionChoice.UNDEFINED;
1216
1217         if (routerIdsList != null && !routerIdsList.isEmpty()) {
1218             for (Uuid routerId : routerIdsList) {
1219                 IpVersionChoice vers = neutronvpnUtils.getIpVersionChoicesFromRouterUuid(routerId);
1220                 ipVersChoices = ipVersChoices.addVersion(vers);
1221             }
1222         }
1223         updateVpnInstanceNode(vpnId, rdList, irtList, ertList, type, l3vni, ipVersChoices);
1224
1225         // Please note that router and networks will be filled into VPNMaps
1226         // by subsequent calls here to associateRouterToVpn and
1227         // associateNetworksToVpn
1228         updateVpnMaps(vpnId, name, null, tenantId, null);
1229         LOG.debug("Created L3VPN with ID {}, name {}, tenantID {}, RDList {}, iRTList {}, eRTList{}, routerIdsList {}, "
1230                         + "networkList {}", vpnId.getValue(), name, tenantId, rdList, irtList, ertList, routerIdsList,
1231                 networkList);
1232
1233         if (routerIdsList != null && !routerIdsList.isEmpty()) {
1234             for (Uuid routerId : routerIdsList) {
1235                 associateRouterToVpn(vpnId, routerId);
1236             }
1237         }
1238         if (networkList != null) {
1239             List<String> failStrings = associateNetworksToVpn(vpnId, networkList);
1240             if (!failStrings.isEmpty()) {
1241                 LOG.error("VPN {} association to networks failed for networks: {}. ",
1242                         vpnId.getValue(), failStrings.toString());
1243                 throw new Exception(failStrings.toString());
1244             }
1245         }
1246     }
1247
1248     /**
1249      * It handles the invocations to the createVPN RPC method.
1250      */
1251     @Override
1252     // TODO Clean up the exception handling
1253     @SuppressWarnings("checkstyle:IllegalCatch")
1254     public ListenableFuture<RpcResult<CreateL3VPNOutput>> createL3VPN(CreateL3VPNInput input) {
1255
1256         CreateL3VPNOutputBuilder opBuilder = new CreateL3VPNOutputBuilder();
1257         SettableFuture<RpcResult<CreateL3VPNOutput>> result = SettableFuture.create();
1258         List<RpcError> errorList = new ArrayList<>();
1259         int failurecount = 0;
1260         int warningcount = 0;
1261
1262         List<L3vpn> vpns = input.getL3vpn();
1263         if (vpns == null) {
1264             vpns = Collections.emptyList();
1265         }
1266         for (L3vpn vpn : vpns) {
1267             if (neutronvpnUtils.doesVpnExist(vpn.getId())) {
1268                 errorList.add(RpcResultBuilder.newWarning(ErrorType.PROTOCOL, "invalid-input",
1269                         formatAndLog(LOG::warn,
1270                                 "Creation of L3VPN failed for VPN {} due to VPN with the same ID already present",
1271                                 vpn.getId().getValue())));
1272                 warningcount++;
1273                 continue;
1274             }
1275             if (vpn.getRouteDistinguisher() == null || vpn.getImportRT() == null || vpn.getExportRT() == null) {
1276                 errorList.add(RpcResultBuilder.newWarning(ErrorType.PROTOCOL, "invalid-input",
1277                         formatAndLog(LOG::warn,
1278                                 "Creation of L3VPN failed for VPN {} due to absence of RD/iRT/eRT input",
1279                                 vpn.getId().getValue())));
1280                 warningcount++;
1281                 continue;
1282             }
1283             VpnInstance.Type vpnInstanceType = VpnInstance.Type.L3;
1284             long l3vni = 0;
1285             if (vpn.getL3vni() != null) {
1286                 l3vni = vpn.getL3vni();
1287             }
1288
1289             List<String> existingRDs = neutronvpnUtils.getExistingRDs();
1290             if (existingRDs.contains(vpn.getRouteDistinguisher().get(0))) {
1291                 errorList.add(RpcResultBuilder.newWarning(ErrorType.PROTOCOL, "invalid-input",
1292                         formatAndLog(LOG::warn,
1293                                 "Creation of L3VPN failed for VPN {} as another VPN with the same RD {} "
1294                                         + "is already configured",
1295                                 vpn.getId().getValue(), vpn.getRouteDistinguisher().get(0))));
1296                 warningcount++;
1297                 continue;
1298             }
1299             Optional<String> operationalVpn = getExistingOperationalVpn(vpn.getRouteDistinguisher().get(0));
1300             if (operationalVpn.isPresent()) {
1301                 errorList.add(RpcResultBuilder.newError(ErrorType.APPLICATION, "application-error",
1302                         formatAndLog(LOG::error,
1303                                 "Creation of L3VPN failed for VPN {} as another VPN {} with the same RD {} "
1304                                         + "is still available. Please retry creation of a new vpn with the same RD"
1305                                         + " after a couple of minutes.", vpn.getId().getValue(), operationalVpn.get(),
1306                                 vpn.getRouteDistinguisher().get(0))));
1307                 warningcount++;
1308                 continue;
1309             }
1310             if (vpn.getRouterIds() != null && !vpn.getRouterIds().isEmpty()) {
1311                 List<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.vpn.instance.RouterIds>
1312                         routerIdsList = vpn.getRouterIds();
1313                 for (org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.vpn.instance.RouterIds
1314                         routerId : routerIdsList) {
1315                     if (neutronvpnUtils.getNeutronRouter(routerId.getRouterId()) == null) {
1316                         errorList.add(RpcResultBuilder.newWarning(ErrorType.PROTOCOL, "invalid-input",
1317                                 formatAndLog(LOG::warn, "Creation of L3VPN failed for VPN {} due to absense of routers"
1318                                         + "{}", vpn.getId(), routerId.getRouterId())));
1319                         warningcount++;
1320                         continue;
1321                     }
1322                     Uuid vpnId = neutronvpnUtils.getVpnForRouter(routerId.getRouterId(), true);
1323                     if (vpnId != null) {
1324                         errorList.add(RpcResultBuilder.newWarning(ErrorType.PROTOCOL, "invalid-input",
1325                                 formatAndLog(LOG::warn, "Creation of L3VPN failed for VPN {} due to router {} already "
1326                                                 + "associated to another VPN {}", vpn.getId(), routerId.getRouterId(),
1327                                         vpnId.getValue())));
1328                         warningcount++;
1329                         continue;
1330                     }
1331                 }
1332             }
1333             if (vpn.getNetworkIds() != null) {
1334                 int initialWarningCount = warningcount;
1335                 for (Uuid nw : vpn.getNetworkIds()) {
1336                     Network network = neutronvpnUtils.getNeutronNetwork(nw);
1337                     Uuid vpnId = neutronvpnUtils.getVpnForNetwork(nw);
1338                     if (network == null) {
1339                         errorList.add(RpcResultBuilder.newWarning(ErrorType.PROTOCOL, "invalid-input",
1340                                 formatAndLog(LOG::warn,
1341                                         "Creation of L3VPN failed for VPN {} due to network not found {}",
1342                                         vpn.getId().getValue(), nw.getValue())));
1343                         warningcount++;
1344                     } else if (vpnId != null) {
1345                         errorList.add(RpcResultBuilder.newWarning(ErrorType.PROTOCOL, "invalid-input",
1346                                 formatAndLog(LOG::warn,
1347                                         "Creation of L3VPN failed for VPN {} due to network {} already associated"
1348                                                 + " to another VPN {}", vpn.getId().getValue(), nw.getValue(),
1349                                         vpnId.getValue())));
1350                         warningcount++;
1351                     }
1352                 }
1353                 if (warningcount != initialWarningCount) {
1354                     continue;
1355                 }
1356             }
1357             List<Uuid> rtrIdsList = new ArrayList<>();
1358             if (vpn.getRouterIds() != null && !vpn.getRouterIds().isEmpty()) {
1359                 for (org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.vpn.instance.RouterIds
1360                         rtrId : vpn.getRouterIds()) {
1361                     rtrIdsList.add(rtrId.getRouterId());
1362                 }
1363             }
1364             try {
1365                 LOG.debug("L3VPN add RPC: VpnID {}, name {}, tenantID {}, RDList {}, iRTList {}, eRTList{}, "
1366                                 + "routerIdList {}, networksList {}", vpn.getId().getValue(), vpn.getName(),
1367                         vpn.getTenantId(), vpn.getRouteDistinguisher().toString(), vpn.getImportRT().toString(),
1368                         vpn.getExportRT().toString(), rtrIdsList, vpn.getNetworkIds());
1369                 createVpn(vpn.getId(), vpn.getName(), vpn.getTenantId(), vpn.getRouteDistinguisher(),
1370                         vpn.getImportRT(), vpn.getExportRT(), rtrIdsList, vpn.getNetworkIds(),
1371                         vpnInstanceType, l3vni);
1372             } catch (Exception ex) {
1373                 errorList.add(RpcResultBuilder.newError(ErrorType.APPLICATION,
1374                         formatAndLog(LOG::error, "Creation of VPN failed for VPN {}", vpn.getId().getValue(), ex),
1375                         ex.getMessage()));
1376                 failurecount++;
1377             }
1378         }
1379         // if at least one succeeds; result is success
1380         // if none succeeds; result is failure
1381         if (failurecount + warningcount == vpns.size()) {
1382             result.set(RpcResultBuilder.<CreateL3VPNOutput>failed().withRpcErrors(errorList).build());
1383         } else {
1384             List<String> errorResponseList = new ArrayList<>();
1385             if (!errorList.isEmpty()) {
1386                 for (RpcError rpcError : errorList) {
1387                     errorResponseList.add("ErrorType: " + rpcError.getErrorType() + ", ErrorTag: " + rpcError.getTag()
1388                             + ", ErrorMessage: " + rpcError.getMessage());
1389                 }
1390             } else {
1391                 errorResponseList.add("Operation successful with no errors");
1392             }
1393             opBuilder.setResponse(errorResponseList);
1394             result.set(RpcResultBuilder.<CreateL3VPNOutput>success().withResult(opBuilder.build()).build());
1395         }
1396         return result;
1397     }
1398
1399     /**
1400      * It handles the invocations to the neutronvpn:getL3VPN RPC method.
1401      */
1402     @Override
1403     public ListenableFuture<RpcResult<GetL3VPNOutput>> getL3VPN(GetL3VPNInput input) {
1404
1405         GetL3VPNOutputBuilder opBuilder = new GetL3VPNOutputBuilder();
1406         SettableFuture<RpcResult<GetL3VPNOutput>> result = SettableFuture.create();
1407         Uuid inputVpnId = input.getId();
1408         List<VpnInstance> vpns = new ArrayList<>();
1409         List<L3vpnInstances> l3vpnList = new ArrayList<>();
1410
1411         try {
1412             if (inputVpnId == null) {
1413                 // get all vpns
1414                 InstanceIdentifier<VpnInstances> vpnsIdentifier = InstanceIdentifier.builder(VpnInstances.class)
1415                         .build();
1416                 Optional<VpnInstances> optionalVpns =
1417                         SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
1418                                 vpnsIdentifier);
1419                 if (optionalVpns.isPresent() && !optionalVpns.get().getVpnInstance().isEmpty()) {
1420                     for (VpnInstance vpn : requireNonNullElse(optionalVpns.get().getVpnInstance(),
1421                             Collections.<VpnInstance>emptyList())) {
1422                         // eliminating implicitly created (router and VLAN provider external network specific) VPNs
1423                         // from getL3VPN output
1424                         if (vpn.getIpv4Family().getRouteDistinguisher() != null) {
1425                             vpns.add(vpn);
1426                         }
1427                         if (vpn.getIpv6Family().getRouteDistinguisher() != null) {
1428                             vpns.add(vpn);
1429                         }
1430                     }
1431                 } else {
1432                     // No VPN present
1433                     opBuilder.setL3vpnInstances(l3vpnList);
1434                     result.set(RpcResultBuilder.<GetL3VPNOutput>success().withResult(opBuilder.build()).build());
1435                     return result;
1436                 }
1437             } else {
1438                 String name = inputVpnId.getValue();
1439                 InstanceIdentifier<VpnInstance> vpnIdentifier = InstanceIdentifier.builder(VpnInstances.class)
1440                         .child(VpnInstance.class, new VpnInstanceKey(name)).build();
1441                 // read VpnInstance Info
1442                 Optional<VpnInstance> optionalVpn =
1443                         SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
1444                                 vpnIdentifier);
1445                 // eliminating implicitly created (router or VLAN provider external network specific) VPN from
1446                 // getL3VPN output
1447                 if (optionalVpn.isPresent() && optionalVpn.get().getIpv4Family().getRouteDistinguisher() != null
1448                         || optionalVpn.get().getIpv6Family().getRouteDistinguisher() != null) {
1449                     vpns.add(optionalVpn.get());
1450                 } else {
1451                     result.set(
1452                             RpcResultBuilder.<GetL3VPNOutput>failed().withWarning(ErrorType.PROTOCOL, "invalid-value",
1453                                     formatAndLog(LOG::error, "GetL3VPN failed because VPN {} is not present",
1454                                             name)).build());
1455                 }
1456             }
1457             for (VpnInstance vpnInstance : vpns) {
1458                 Uuid vpnId = new Uuid(vpnInstance.getVpnInstanceName());
1459                 // create VpnMaps id
1460                 L3vpnInstancesBuilder l3vpn = new L3vpnInstancesBuilder();
1461                 List<String> rd = Collections.EMPTY_LIST;
1462                 if (vpnInstance.getIpv4Family().getRouteDistinguisher() != null) {
1463                     rd = vpnInstance.getIpv4Family().getRouteDistinguisher();
1464                 } else if (vpnInstance.getIpv6Family().getRouteDistinguisher() != null) {
1465                     rd = vpnInstance.getIpv6Family().getRouteDistinguisher();
1466                 }
1467                 List<String> ertList = new ArrayList<>();
1468                 List<String> irtList = new ArrayList<>();
1469
1470                 if (vpnInstance.getIpv4Family().getVpnTargets() != null
1471                         || vpnInstance.getIpv6Family().getVpnTargets() != null) {
1472                     List<VpnTarget> vpnTargetList = Collections.EMPTY_LIST;
1473                     if (!vpnInstance.getIpv4Family().getVpnTargets().getVpnTarget().isEmpty()) {
1474                         vpnTargetList = vpnInstance.getIpv4Family().getVpnTargets().getVpnTarget();
1475                     } else if (!vpnInstance.getIpv6Family().getVpnTargets().getVpnTarget().isEmpty()) {
1476                         vpnTargetList = vpnInstance.getIpv6Family().getVpnTargets().getVpnTarget();
1477                     }
1478                     if (!vpnTargetList.isEmpty()) {
1479                         for (VpnTarget vpnTarget : vpnTargetList) {
1480                             if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.ExportExtcommunity) {
1481                                 ertList.add(vpnTarget.getVrfRTValue());
1482                             }
1483                             if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.ImportExtcommunity) {
1484                                 irtList.add(vpnTarget.getVrfRTValue());
1485                             }
1486                             if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.Both) {
1487                                 ertList.add(vpnTarget.getVrfRTValue());
1488                                 irtList.add(vpnTarget.getVrfRTValue());
1489                             }
1490                         }
1491                     }
1492                 }
1493
1494                 l3vpn.setId(vpnId).setRouteDistinguisher(rd).setImportRT(irtList).setExportRT(ertList);
1495
1496                 if (vpnInstance.getL3vni() != null) {
1497                     l3vpn.setL3vni(vpnInstance.getL3vni());
1498                 }
1499                 InstanceIdentifier<VpnMap> vpnMapIdentifier = InstanceIdentifier.builder(VpnMaps.class).child(VpnMap
1500                         .class, new VpnMapKey(vpnId)).build();
1501                 Optional<VpnMap> optionalVpnMap =
1502                         SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
1503                                 vpnMapIdentifier);
1504                 if (optionalVpnMap.isPresent()) {
1505                     VpnMap vpnMap = optionalVpnMap.get();
1506                     List<Uuid> rtrIds = new ArrayList<>();
1507                     if (vpnMap.getRouterIds() != null && !vpnMap.getRouterIds().isEmpty()) {
1508                         for (RouterIds rtrId : vpnMap.getRouterIds()) {
1509                             rtrIds.add(rtrId.getRouterId());
1510                         }
1511                     }
1512                     l3vpn.setRouterIds(NeutronvpnUtils.getVpnInstanceRouterIdsList(rtrIds))
1513                             .setNetworkIds(vpnMap.getNetworkIds()).setTenantId(vpnMap.getTenantId())
1514                             .setName(vpnMap.getName());
1515
1516                 }
1517                 l3vpnList.add(l3vpn.build());
1518             }
1519
1520             opBuilder.setL3vpnInstances(l3vpnList);
1521             result.set(RpcResultBuilder.<GetL3VPNOutput>success().withResult(opBuilder.build()).build());
1522
1523         } catch (ReadFailedException ex) {
1524             result.set(RpcResultBuilder.<GetL3VPNOutput>failed().withError(ErrorType.APPLICATION,
1525                     formatAndLog(LOG::error, "GetVPN failed due to {}", ex.getMessage())).build());
1526         }
1527         return result;
1528     }
1529
1530     /**
1531      * It handles the invocations to the neutronvpn:deleteL3VPN RPC method.
1532      */
1533     @Override
1534     public ListenableFuture<RpcResult<DeleteL3VPNOutput>> deleteL3VPN(DeleteL3VPNInput input) {
1535
1536         DeleteL3VPNOutputBuilder opBuilder = new DeleteL3VPNOutputBuilder();
1537         SettableFuture<RpcResult<DeleteL3VPNOutput>> result = SettableFuture.create();
1538         List<RpcError> errorList = new ArrayList<>();
1539
1540         int failurecount = 0;
1541         int warningcount = 0;
1542         List<Uuid> vpns = requireNonNullElse(input.getId(), Collections.emptyList());
1543         for (Uuid vpn : vpns) {
1544             try {
1545                 LOG.debug("L3VPN delete RPC: VpnID {}", vpn.getValue());
1546                 InstanceIdentifier<VpnInstance> vpnIdentifier =
1547                         InstanceIdentifier.builder(VpnInstances.class)
1548                             .child(VpnInstance.class, new VpnInstanceKey(vpn.getValue())).build();
1549                 Optional<VpnInstance> optionalVpn =
1550                         SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
1551                                 vpnIdentifier);
1552                 if (optionalVpn.isPresent()) {
1553                     removeVpn(vpn);
1554                 } else {
1555                     errorList.add(RpcResultBuilder.newWarning(ErrorType.PROTOCOL, "invalid-value",
1556                             formatAndLog(LOG::warn, "VPN with vpnid: {} does not exist", vpn.getValue())));
1557                     warningcount++;
1558                 }
1559             } catch (ReadFailedException ex) {
1560                 errorList.add(RpcResultBuilder.newError(ErrorType.APPLICATION,
1561                         formatAndLog(LOG::error, "Deletion of L3VPN failed when deleting for uuid {}", vpn.getValue()),
1562                         ex.getMessage()));
1563                 failurecount++;
1564             }
1565         }
1566         // if at least one succeeds; result is success
1567         // if none succeeds; result is failure
1568         if (failurecount + warningcount == vpns.size()) {
1569             result.set(RpcResultBuilder.<DeleteL3VPNOutput>failed().withRpcErrors(errorList).build());
1570         } else {
1571             List<String> errorResponseList = new ArrayList<>();
1572             if (!errorList.isEmpty()) {
1573                 for (RpcError rpcError : errorList) {
1574                     errorResponseList.add("ErrorType: " + rpcError.getErrorType() + ", ErrorTag: " + rpcError.getTag()
1575                             + ", ErrorMessage: " + rpcError.getMessage());
1576                 }
1577             } else {
1578                 errorResponseList.add("Operation successful with no errors");
1579             }
1580             opBuilder.setResponse(errorResponseList);
1581             result.set(RpcResultBuilder.<DeleteL3VPNOutput>success().withResult(opBuilder.build()).build());
1582         }
1583         return result;
1584     }
1585
1586     public void createVpnInstanceForSubnet(Uuid subnetId) {
1587         LOG.debug("Creating/Updating L3 internalVPN for subnetID {} ", subnetId);
1588         createL3InternalVpn(subnetId, subnetId.getValue(), null, null, null, null, null, null);
1589     }
1590
1591     public void removeVpnInstanceForSubnet(Uuid subnetId) {
1592         LOG.debug("Removing vpn-instance for subnetID {} ", subnetId);
1593         removeVpn(subnetId);
1594     }
1595
1596     protected void addSubnetToVpn(@Nullable final Uuid vpnId, Uuid subnet, @Nullable final Uuid internetVpnId) {
1597         LOG.debug("addSubnetToVpn: Adding subnet {} to vpn {}", subnet.getValue(),
1598                   vpnId != null ? vpnId.getValue() : internetVpnId.getValue());
1599         Subnetmap sn = updateSubnetNode(subnet, null, vpnId, internetVpnId);
1600         if (sn == null) {
1601             LOG.error("addSubnetToVpn: subnetmap is null, cannot add subnet {} to VPN {}", subnet.getValue(),
1602                 vpnId != null ? vpnId.getValue() : internetVpnId.getValue());
1603             return;
1604         }
1605         if (vpnId != null) {
1606             VpnMap vpnMap = neutronvpnUtils.getVpnMap(vpnId);
1607             if (vpnMap == null) {
1608                 LOG.error("addSubnetToVpn: No vpnMap for vpnId {},"
1609                      + " cannot add subnet {} to VPN", vpnId.getValue(),
1610                     subnet.getValue());
1611                 return;
1612             }
1613             final VpnInstance vpnInstance = VpnHelper.getVpnInstance(dataBroker, vpnId.getValue());
1614             LOG.debug("addSubnetToVpn: VpnInstance {}", vpnInstance.toString());
1615             if (isVpnOfTypeL2(vpnInstance)) {
1616                 neutronEvpnUtils.updateElanAndVpn(vpnInstance, sn.getNetworkId().getValue(),
1617                         NeutronEvpnUtils.Operation.ADD);
1618             }
1619         }
1620         if (internetVpnId != null) {
1621             VpnMap vpnMap = neutronvpnUtils.getVpnMap(internetVpnId);
1622             if (vpnMap == null) {
1623                 LOG.error("addSubnetToVpn: No vpnMap for InternetVpnId {}, cannot add "
1624                     + "subnet {} to VPN", internetVpnId.getValue(),
1625                     subnet.getValue());
1626                 return;
1627             }
1628         }
1629         final Uuid internetId = internetVpnId;
1630         // Check if there are ports on this subnet and add corresponding vpn-interfaces
1631         List<Uuid> portList = sn.getPortList();
1632         if (portList != null) {
1633             for (final Uuid portId : portList) {
1634                 String vpnInfName = portId.getValue();
1635                 VpnInterface vpnIface = VpnHelper.getVpnInterface(dataBroker, vpnInfName);
1636                 Port port = neutronvpnUtils.getNeutronPort(portId);
1637                 if (port == null) {
1638                     LOG.error("addSubnetToVpn: Cannot proceed with addSubnetToVpn for port {} in subnet {} "
1639                         + "since port is absent in Neutron config DS", portId.getValue(), subnet.getValue());
1640                     continue;
1641                 }
1642                 final Boolean isRouterInterface = port.getDeviceOwner()
1643                         .equals(NeutronConstants.DEVICE_OWNER_ROUTER_INF) ? true : false;
1644                 jobCoordinator.enqueueJob("PORT-" + portId.getValue(), () -> singletonList(
1645                     txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, wrtConfigTxn -> {
1646                         Adjacencies portAdj = createPortIpAdjacencies(port, isRouterInterface, wrtConfigTxn, sn,
1647                                     vpnIface);
1648                         if (vpnIface == null) {
1649                             LOG.trace("addSubnetToVpn: create new VpnInterface for Port {}", vpnInfName);
1650                             Set<Uuid> listVpn = new HashSet<>();
1651                             if (vpnId != null) {
1652                                 listVpn.add(vpnId);
1653                             }
1654                             if (internetId != null) {
1655                                 listVpn.add(internetId);
1656                             }
1657                             writeVpnInterfaceToDs(listVpn,
1658                                     vpnInfName, portAdj, port.getNetworkId(), isRouterInterface, wrtConfigTxn);
1659                             if (sn.getRouterId() != null) {
1660                                 addToNeutronRouterInterfacesMap(sn.getRouterId(), portId.getValue());
1661                             }
1662                         } else {
1663                             LOG.trace("update VpnInterface for Port {} with adj {}", vpnInfName, portAdj);
1664                             if (vpnId != null) {
1665                                 updateVpnInterfaceWithAdjacencies(vpnId, vpnInfName, portAdj, wrtConfigTxn);
1666                             }
1667                             if (internetId != null) {
1668                                 updateVpnInterfaceWithAdjacencies(internetId, vpnInfName, portAdj, wrtConfigTxn);
1669                             }
1670                         }
1671                     }))
1672                 );
1673             }
1674         }
1675     }
1676
1677     protected void removeSubnetFromVpn(final Uuid vpnId, Uuid subnet, @Nullable Uuid internetVpnId) {
1678         Preconditions.checkArgument(vpnId != null || internetVpnId != null,
1679                 "removeSubnetFromVpn: at least one VPN must be not null");
1680         LOG.debug("Removing subnet {} from vpn {}/{}", subnet.getValue(),
1681                   vpnId, internetVpnId);
1682         Subnetmap sn = neutronvpnUtils.getSubnetmap(subnet);
1683         if (sn == null) {
1684             LOG.error("removeSubnetFromVpn: Subnetmap for subnet {} not found", subnet.getValue());
1685             return;
1686         }
1687         VpnMap vpnMap = null;
1688         VpnInstance vpnInstance = null;
1689         if (vpnId != null) {
1690             vpnMap = neutronvpnUtils.getVpnMap(vpnId);
1691             if (vpnMap == null) {
1692                 LOG.error("No vpnMap for vpnId {}, cannot remove subnet {} from VPN",
1693                         vpnId.getValue(), subnet.getValue());
1694                 return;
1695             }
1696             vpnInstance = VpnHelper.getVpnInstance(dataBroker, vpnId.getValue());
1697         }
1698         if (internetVpnId == null) {
1699             internetVpnId = sn.getInternetVpnId();
1700         }
1701         if (internetVpnId != null) {
1702             vpnMap = neutronvpnUtils.getVpnMap(internetVpnId);
1703             if (vpnMap == null) {
1704                 LOG.error("No vpnMap for vpnId {}, cannot remove subnet {}"
1705                         + " from Internet VPN",
1706                         internetVpnId.getValue(), subnet.getValue());
1707                 return;
1708             }
1709         }
1710         if (vpnInstance != null && isVpnOfTypeL2(vpnInstance)) {
1711             neutronEvpnUtils.updateElanAndVpn(vpnInstance, sn.getNetworkId().getValue(),
1712                     NeutronEvpnUtils.Operation.DELETE);
1713         }
1714         boolean subnetVpnAssociation = false;
1715         if (vpnId != null && sn.getVpnId() != null
1716             && sn.getVpnId().getValue().equals(vpnId.getValue())) {
1717             subnetVpnAssociation = true;
1718         } else if (internetVpnId != null && sn.getInternetVpnId() != null
1719             && sn.getInternetVpnId().getValue().matches(internetVpnId.getValue())) {
1720             subnetVpnAssociation = true;
1721         }
1722         if (subnetVpnAssociation == false) {
1723             LOG.error("Removing subnet : Subnetmap is not in VPN {}/{}, owns {} and {}",
1724                       vpnId, internetVpnId, sn.getVpnId(), sn.getInternetVpnId());
1725             return;
1726         }
1727         // Check if there are ports on this subnet; remove corresponding vpn-interfaces
1728         List<Uuid> portList = sn.getPortList();
1729         final Uuid internetId = internetVpnId;
1730         if (portList != null) {
1731             for (final Uuid portId : portList) {
1732                 LOG.debug("withdrawing subnet IP {} from vpn-interface {}", sn.getSubnetIp(), portId.getValue());
1733                 final Port port = neutronvpnUtils.getNeutronPort(portId);
1734                 jobCoordinator.enqueueJob("PORT-" + portId.getValue(),
1735                     () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
1736                             CONFIGURATION, tx -> {
1737                             if (port != null) {
1738                                 withdrawPortIpFromVpnIface(vpnId, internetId, port, sn, tx);
1739                             } else {
1740                                 LOG.warn(
1741                                         "Cannot proceed with withdrawPortIpFromVpnIface for port {} in subnet {} since "
1742                                                 + "port is absent in Neutron config DS", portId.getValue(),
1743                                         subnet.getValue());
1744                             }
1745                         })));
1746             }
1747         }
1748         //update subnet-vpn association
1749         removeFromSubnetNode(subnet, null, null, vpnId, null);
1750     }
1751
1752     protected void updateVpnInternetForSubnet(Subnetmap sm, Uuid vpn, boolean isBeingAssociated) {
1753         LOG.debug("updateVpnInternetForSubnet: {} subnet {} with BGPVPN Internet {} ",
1754              isBeingAssociated ? "associating" : "dissociating", sm.getSubnetIp(),
1755              vpn.getValue());
1756         Uuid internalVpnId = sm.getVpnId();
1757         if (internalVpnId == null) {
1758             LOG.error("updateVpnInternetForSubnet: can not find Internal or BGPVPN Id for subnet {}, bailing out",
1759                       sm.getId().getValue());
1760             return;
1761         }
1762         if (isBeingAssociated) {
1763             updateSubnetNode(sm.getId(), null, sm.getVpnId(), vpn);
1764         } else {
1765             updateSubnetNode(sm.getId(), null, sm.getVpnId(), null);
1766         }
1767
1768         jobCoordinator.enqueueJob("VPN-" + vpn.getValue(), () -> singletonList(
1769             txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, wrtConfigTxn -> {
1770                 if (isBeingAssociated) {
1771                     updateVpnInterface(vpn, null, neutronvpnUtils.getNeutronPort(
1772                             sm.getRouterInterfacePortId()), true, true, wrtConfigTxn, true);
1773                 } else {
1774                     removeInternetVpnFromVpnInterface(vpn,
1775                             neutronvpnUtils.getNeutronPort(sm.getRouterInterfacePortId()), wrtConfigTxn, sm);
1776                 }
1777                 }
1778             )));
1779
1780         // Check for ports on this subnet and update association of
1781         // corresponding vpn-interfaces to internet vpn
1782         List<Uuid> portList = sm.getPortList();
1783         if (portList != null) {
1784             for (Uuid port : portList) {
1785                 LOG.debug("Updating vpn-interface for port {} isBeingAssociated {}",
1786                         port.getValue(), isBeingAssociated);
1787                 jobCoordinator.enqueueJob("PORT-" + port.getValue(),
1788                     () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
1789                         tx -> {
1790                             if (isBeingAssociated) {
1791                                 updateVpnInterface(vpn, null, neutronvpnUtils.getNeutronPort(port),
1792                                         true, false, tx, true);
1793                             } else {
1794                                 removeInternetVpnFromVpnInterface(vpn, neutronvpnUtils.getNeutronPort(port), tx, sm);
1795                             }
1796                         })));
1797             }
1798         }
1799     }
1800
1801     @Nullable
1802     private Subnetmap updateVpnForSubnet(Uuid oldVpnId, Uuid newVpnId, Uuid subnet, boolean isBeingAssociated) {
1803         LOG.debug("Moving subnet {} from oldVpn {} to newVpn {} ", subnet.getValue(),
1804                 oldVpnId.getValue(), newVpnId.getValue());
1805         Uuid networkUuid = neutronvpnUtils.getSubnetmap(subnet).getNetworkId();
1806         Network network = neutronvpnUtils.getNeutronNetwork(networkUuid);
1807         boolean netIsExternal = NeutronvpnUtils.getIsExternal(network);
1808         Uuid vpnExtUuid = netIsExternal ? null
1809                 : neutronvpnUtils.getInternetvpnUuidBoundToSubnetRouter(subnet);
1810         Subnetmap sn = updateSubnetNode(subnet, null, newVpnId, vpnExtUuid);
1811         if (sn == null) {
1812             LOG.error("Updating subnet {} with newVpn {} failed", subnet.getValue(), newVpnId.getValue());
1813             return sn;
1814         }
1815         /* vpnExtUuid will contain the value only on if the subnet is V6 and it is already been
1816          * associated with internet BGP-VPN.
1817          */
1818         if (vpnExtUuid != null) {
1819             /* Update V6 Internet default route match with new VPN metadata.
1820              * isBeingAssociated = true means oldVpnId is same as routerId
1821              * isBeingAssociated = false means newVpnId is same as routerId
1822             */
1823             if (isBeingAssociated) {
1824                 neutronvpnUtils.updateVpnInstanceWithFallback(oldVpnId, vpnExtUuid, true);
1825             } else {
1826                 neutronvpnUtils.updateVpnInstanceWithFallback(newVpnId, vpnExtUuid, true);
1827             }
1828         }
1829         //Update Router Interface first synchronously.
1830         //CAUTION:  Please DONOT make the router interface VPN Movement as an asynchronous commit again !
1831         ListenableFuture<Void> future =
1832                 txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
1833                     tx -> updateVpnInterface(newVpnId, oldVpnId,
1834                         neutronvpnUtils.getNeutronPort(sn.getRouterInterfacePortId()),
1835                         isBeingAssociated, true, tx, false));
1836         Futures.addCallback(future, new FutureCallback<Void>() {
1837             @Override
1838             public void onSuccess(Void result) {
1839                 // Check for ports on this subnet and update association of
1840                 // corresponding vpn-interfaces to external vpn
1841                 List<Uuid> portList = sn.getPortList();
1842                 if (portList != null) {
1843                     for (Uuid port : portList) {
1844                         LOG.debug("Updating vpn-interface for port {} isBeingAssociated {}",
1845                                 port.getValue(), isBeingAssociated);
1846                         jobCoordinator.enqueueJob("PORT-" + port.getValue(), () -> Collections.singletonList(
1847                                 txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
1848                                     tx -> updateVpnInterface(newVpnId, oldVpnId,
1849                                             neutronvpnUtils.getNeutronPort(port), isBeingAssociated, false,
1850                                             tx, false))));
1851                     }
1852                 }
1853             }
1854
1855             @Override
1856             public void onFailure(Throwable throwable) {
1857                 LOG.error(
1858                         "Failed to update router interface {} in subnet {} from oldVpnId {} to newVpnId {}, "
1859                                 + "returning",
1860                         sn.getRouterInterfacePortId().getValue(), subnet.getValue(), oldVpnId, newVpnId, throwable);
1861             }
1862         }, MoreExecutors.directExecutor());
1863
1864         return sn;
1865     }
1866
1867     public InstanceIdentifier<RouterInterfaces> getRouterInterfacesId(Uuid routerId) {
1868         return InstanceIdentifier.builder(RouterInterfacesMap.class)
1869                 .child(RouterInterfaces.class, new RouterInterfacesKey(routerId)).build();
1870     }
1871
1872     protected void addToNeutronRouterInterfacesMap(Uuid routerId, String interfaceName) {
1873         final InstanceIdentifier<RouterInterfaces> routerInterfacesId = getRouterInterfacesId(routerId);
1874         final ReentrantLock lock = lockForUuid(routerId);
1875         lock.lock();
1876         try {
1877             Optional<RouterInterfaces> optRouterInterfaces =
1878                     SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
1879                         routerInterfacesId);
1880             Interfaces routerInterface = new InterfacesBuilder().withKey(new InterfacesKey(interfaceName))
1881                     .setInterfaceId(interfaceName).build();
1882             if (optRouterInterfaces.isPresent()) {
1883                 SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION,
1884                     routerInterfacesId.child(Interfaces.class, new InterfacesKey(interfaceName)), routerInterface);
1885             } else {
1886                 // TODO Shouldn't we be doing something with builder and interfaces?
1887                 //          RouterInterfacesBuilder builder = new RouterInterfacesBuilder().setRouterId(routerId);
1888                 //          List<Interfaces> interfaces = new ArrayList<>();
1889                 //          interfaces.add(routerInterface);
1890
1891                 SingleTransactionDataBroker.syncUpdate(dataBroker, LogicalDatastoreType.CONFIGURATION,
1892                     routerInterfacesId.child(Interfaces.class, new InterfacesKey(interfaceName)), routerInterface);
1893             }
1894         } catch (ReadFailedException | TransactionCommitFailedException e) {
1895             LOG.error("Error reading router interfaces for {}", routerInterfacesId, e);
1896         } finally {
1897             lock.unlock();
1898         }
1899     }
1900
1901     protected void removeFromNeutronRouterInterfacesMap(Uuid routerId, String interfaceName) {
1902         final InstanceIdentifier<RouterInterfaces> routerInterfacesId = getRouterInterfacesId(routerId);
1903         final ReentrantLock lock = lockForUuid(routerId);
1904         lock.lock();
1905         try {
1906             Optional<RouterInterfaces> optRouterInterfaces =
1907                     SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
1908                         routerInterfacesId);
1909             Interfaces routerInterface = new InterfacesBuilder().withKey(new InterfacesKey(interfaceName))
1910                     .setInterfaceId(interfaceName).build();
1911             if (optRouterInterfaces.isPresent()) {
1912                 RouterInterfaces routerInterfaces = optRouterInterfaces.get();
1913                 List<Interfaces> interfaces = routerInterfaces.getInterfaces();
1914                 if (interfaces != null && interfaces.remove(routerInterface)) {
1915                     if (interfaces.isEmpty()) {
1916                         SingleTransactionDataBroker.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION,
1917                             routerInterfacesId);
1918                     } else {
1919                         SingleTransactionDataBroker.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION,
1920                             routerInterfacesId.child(Interfaces.class, new InterfacesKey(interfaceName)));
1921                     }
1922                 }
1923             }
1924         } catch (ReadFailedException | TransactionCommitFailedException e) {
1925             LOG.error("Error reading the router interfaces for {}", routerInterfacesId, e);
1926         } finally {
1927             lock.unlock();
1928         }
1929     }
1930
1931     /**
1932      * Creates the corresponding static routes in the specified VPN. These static routes must be point to an
1933      * InterVpnLink endpoint and the specified VPN must be the other end of the InterVpnLink. Otherwise the
1934      * route will be ignored.
1935      *
1936      * @param vpnName the VPN identifier
1937      * @param interVpnLinkRoutes The list of static routes
1938      * @param nexthopsXinterVpnLinks A Map with the correspondence nextHop-InterVpnLink
1939      */
1940     public void addInterVpnRoutes(Uuid vpnName, List<Routes> interVpnLinkRoutes,
1941                                   HashMap<String, InterVpnLink> nexthopsXinterVpnLinks) {
1942         for (Routes route : interVpnLinkRoutes) {
1943             String nexthop = route.getNexthop().stringValue();
1944             String destination = route.getDestination().stringValue();
1945             InterVpnLink interVpnLink = nexthopsXinterVpnLinks.get(nexthop);
1946             if (isNexthopTheOtherVpnLinkEndpoint(nexthop, vpnName.getValue(), interVpnLink)) {
1947                 AddStaticRouteInput rpcInput =
1948                         new AddStaticRouteInputBuilder().setDestination(destination).setNexthop(nexthop)
1949                                 .setVpnInstanceName(vpnName.getValue())
1950                                 .build();
1951                 Future<RpcResult<AddStaticRouteOutput>> labelOuputFtr = vpnRpcService.addStaticRoute(rpcInput);
1952                 RpcResult<AddStaticRouteOutput> rpcResult;
1953                 try {
1954                     rpcResult = labelOuputFtr.get();
1955                     if (rpcResult.isSuccessful()) {
1956                         LOG.debug("Label generated for destination {} is: {}",
1957                                 destination, rpcResult.getResult().getLabel());
1958                     } else {
1959                         LOG.error("RPC call to add a static Route to {} with nexthop {} returned with errors {}",
1960                                 destination, nexthop, rpcResult.getErrors());
1961                     }
1962                 } catch (InterruptedException | ExecutionException e) {
1963                     LOG.error("Error happened while invoking addStaticRoute RPC for nexthop {} with destination {} "
1964                             + "for VPN {}", nexthop, destination, vpnName.getValue(), e);
1965                 }
1966             } else {
1967                 // Any other case is a fault.
1968                 LOG.warn("route with destination {} and nexthop {} does not apply to any InterVpnLink",
1969                         route.getDestination().stringValue(), nexthop);
1970                 continue;
1971             }
1972         }
1973     }
1974
1975     /**
1976      * Removes the corresponding static routes from the specified VPN. These static routes point to an
1977      * InterVpnLink endpoint and the specified VPN must be the other end of the InterVpnLink.
1978      *
1979      * @param vpnName the VPN identifier
1980      * @param interVpnLinkRoutes The list of static routes
1981      * @param nexthopsXinterVpnLinks A Map with the correspondence nextHop-InterVpnLink
1982      */
1983     public void removeInterVpnRoutes(Uuid vpnName, List<Routes> interVpnLinkRoutes,
1984                                      HashMap<String, InterVpnLink> nexthopsXinterVpnLinks) {
1985         for (Routes route : interVpnLinkRoutes) {
1986             String nexthop = route.getNexthop().stringValue();
1987             String destination = route.getDestination().stringValue();
1988             InterVpnLink interVpnLink = nexthopsXinterVpnLinks.get(nexthop);
1989             if (isNexthopTheOtherVpnLinkEndpoint(nexthop, vpnName.getValue(), interVpnLink)) {
1990                 RemoveStaticRouteInput rpcInput =
1991                         new RemoveStaticRouteInputBuilder().setDestination(destination).setNexthop(nexthop)
1992                                 .setVpnInstanceName(vpnName.getValue())
1993                                 .build();
1994
1995                 ListenableFutures.addErrorLogging(JdkFutureAdapters.listenInPoolThread(
1996                         vpnRpcService.removeStaticRoute(rpcInput)), LOG, "Remove VPN routes");
1997             } else {
1998                 // Any other case is a fault.
1999                 LOG.warn("route with destination {} and nexthop {} does not apply to any InterVpnLink",
2000                         route.getDestination().stringValue(), nexthop);
2001                 continue;
2002             }
2003         }
2004     }
2005
2006     /*
2007      * Returns true if the specified nexthop is the other endpoint in an
2008      * InterVpnLink, regarding one of the VPN's point of view.
2009      */
2010     private boolean isNexthopTheOtherVpnLinkEndpoint(String nexthop, String thisVpnUuid, InterVpnLink interVpnLink) {
2011         return
2012                 interVpnLink != null
2013                         && (interVpnLink.getFirstEndpoint().getVpnUuid().getValue().equals(thisVpnUuid)
2014                         && interVpnLink.getSecondEndpoint().getIpAddress().getValue().equals(nexthop)
2015                         || interVpnLink.getSecondEndpoint().getVpnUuid().getValue().equals(thisVpnUuid)
2016                         && interVpnLink.getFirstEndpoint().getIpAddress().getValue().equals(nexthop));
2017     }
2018
2019     @Nonnull
2020     protected List<Adjacency> getAdjacencyforExtraRoute(List<Routes> routeList, String fixedIp) {
2021         List<Adjacency> adjList = new ArrayList<>();
2022         Map<String, List<String>> adjMap = new HashMap<>();
2023         for (Routes route : routeList) {
2024             if (route == null || route.getNexthop() == null || route.getDestination() == null) {
2025                 LOG.error("Incorrect input received for extra route. {}", route);
2026             } else {
2027                 String nextHop = route.getNexthop().stringValue();
2028                 String destination = route.getDestination().stringValue();
2029                 if (!nextHop.equals(fixedIp)) {
2030                     LOG.trace("FixedIP {} is not extra route nexthop for destination {}", fixedIp, destination);
2031                     continue;
2032                 }
2033                 LOG.trace("Adding extra route for destination {} with nexthop {} ", destination,
2034                         nextHop);
2035                 List<String> hops = adjMap.computeIfAbsent(destination, k -> new ArrayList<>());
2036                 if (!hops.contains(nextHop)) {
2037                     hops.add(nextHop);
2038                 }
2039             }
2040         }
2041
2042         for (Entry<String, List<String>> entry : adjMap.entrySet()) {
2043             final String destination = entry.getKey();
2044             final List<String> ipList = entry.getValue();
2045             Adjacency erAdj = new AdjacencyBuilder().setIpAddress(destination)
2046                     .setAdjacencyType(AdjacencyType.ExtraRoute).setNextHopIpList(ipList)
2047                     .withKey(new AdjacencyKey(destination)).build();
2048             adjList.add(erAdj);
2049         }
2050         return adjList;
2051     }
2052
2053     protected void updateVpnInterfaceWithExtraRouteAdjacency(Uuid vpnId, List<Routes> routeList) {
2054         checkAlarmExtraRoutes(vpnId, routeList);
2055
2056         for (Routes route : routeList) {
2057             if (route == null || route.getNexthop() == null || route.getDestination() == null) {
2058                 LOG.error("Incorrect input received for extra route. {}", route);
2059             } else {
2060                 String nextHop = route.getNexthop().stringValue();
2061                 String destination = route.getDestination().stringValue();
2062                 String infName = neutronvpnUtils.getNeutronPortNameFromVpnPortFixedIp(vpnId.getValue(),
2063                         nextHop);
2064                 if (infName != null) {
2065                     LOG.trace("Updating extra route for destination {} onto vpn {} with nexthop {} and infName {}",
2066                         destination, vpnId.getValue(), nextHop, infName);
2067                     boolean isLockAcquired = false;
2068                     try {
2069                         InstanceIdentifier<VpnInterface> identifier = InstanceIdentifier.builder(VpnInterfaces.class)
2070                                 .child(VpnInterface.class, new VpnInterfaceKey(infName)).build();
2071                         InstanceIdentifier<Adjacency> path = identifier.augmentation(Adjacencies.class)
2072                             .child(Adjacency.class, new AdjacencyKey(destination));
2073                         Optional<Adjacency> existingAdjacency = SingleTransactionDataBroker.syncReadOptional(dataBroker,
2074                                 LogicalDatastoreType.CONFIGURATION, path);
2075                         if (existingAdjacency.isPresent()
2076                                 && existingAdjacency.get().getAdjacencyType() == AdjacencyType.PrimaryAdjacency) {
2077                             LOG.error("The route with destination {} nextHop {} is already present as"
2078                                             + " a primary adjacency for interface {}. Skipping adjacency addition.",
2079                                     destination, nextHop, infName);
2080                             continue;
2081                         }
2082                         Adjacency erAdj = new AdjacencyBuilder().setIpAddress(destination)
2083                             .setNextHopIpList(Collections.singletonList(nextHop)).withKey(new AdjacencyKey(destination))
2084                             .setAdjacencyType(AdjacencyType.ExtraRoute).build();
2085                         isLockAcquired = interfaceLock.tryLock(infName, LOCK_WAIT_TIME, TimeUnit.SECONDS);
2086                         SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION,
2087                                 path, erAdj);
2088                     } catch (TransactionCommitFailedException e) {
2089                         LOG.error("exception in adding extra route with destination: {}, next hop: {}",
2090                             destination, nextHop, e);
2091                     } catch (ReadFailedException e) {
2092                         LOG.error("Exception on reading data-store ", e);
2093                     } finally {
2094                         if (isLockAcquired) {
2095                             interfaceLock.unlock(infName);
2096                         }
2097                     }
2098                 } else {
2099                     LOG.error("Unable to find VPN NextHop interface to apply extra-route destination {} on VPN {} "
2100                         + "with nexthop {}", destination, vpnId.getValue(), nextHop);
2101                 }
2102             }
2103         }
2104     }
2105
2106     /**
2107      * This method setup or down an alarm about extra route fault.
2108      * When extra routes are configured, through a router, if the number of nexthops is greater than the number of
2109      * available RDs, then an alarm and an error is generated.<br>
2110      * <b>Be careful</b> the routeList could be changed.
2111      *
2112      * @param vpnId the vpnId of vpn to control.
2113      * @param routeList the list of router to check, it could be modified.
2114      */
2115     private void checkAlarmExtraRoutes(Uuid vpnId, List<Routes> routeList) {
2116         if (!neutronvpnAlarm.isAlarmEnabled()) {
2117             LOG.debug("checkAlarmExtraRoutes is not enable for vpnId {} routeList {}", vpnId, routeList);
2118             return;
2119         }
2120         VpnInstance vpnInstance = neutronvpnUtils.getVpnInstance(dataBroker, vpnId);
2121         if (vpnInstance == null || routeList == null || routeList.isEmpty() || !neutronvpnAlarm.isAlarmEnabled()) {
2122             LOG.debug("checkAlarmExtraRoutes have args null as following : vpnId {} routeList {}",
2123                     vpnId, routeList);
2124             return;
2125         }
2126         String primaryRd = neutronvpnUtils.getVpnRd(vpnId.getValue());
2127         if (primaryRd == null || primaryRd.equals(vpnId.getValue())) {
2128             LOG.debug("checkAlarmExtraRoutes. vpn {} is not a BGPVPN. cancel checkExtraRoute",
2129                     vpnId);
2130             return;
2131         }
2132         for (Routes route : routeList) {
2133             // count  the number of nexthops for each same route.getDestingation().getValue()
2134             String destination = route.getDestination().stringValue();
2135             String nextHop = route.getNexthop().stringValue();
2136             List<String> nextHopList = new ArrayList<>();
2137             nextHopList.add(nextHop);
2138             int nbNextHops = 0;
2139             for (Routes routeTmp : routeList) {
2140                 String routeDest = routeTmp.getDestination().stringValue();
2141                 if (!destination.equals(routeDest)) {
2142                     continue;
2143                 }
2144                 String routeNextH = routeTmp.getNexthop().stringValue();
2145                 if (nextHop.equals(routeNextH)) {
2146                     continue;
2147                 }
2148                 nbNextHops++;
2149                 nextHopList.add(routeTmp.getNexthop().stringValue());
2150             }
2151             final List<String> rdList = new ArrayList<>();
2152             if (vpnInstance.getIpv4Family() != null
2153                     && vpnInstance.getIpv4Family().getRouteDistinguisher() != null) {
2154                 vpnInstance.getIpv4Family().getRouteDistinguisher().forEach(rd -> {
2155                     if (rd != null) {
2156                         rdList.add(rd);
2157                     }
2158                 });
2159             }
2160             if (vpnInstance.getIpv6Family() != null && vpnInstance.getIpv6Family().getRouteDistinguisher() != null) {
2161                 vpnInstance.getIpv6Family().getRouteDistinguisher().forEach(rd -> {
2162                     if (rd != null && !rdList.contains(rd)) {
2163                         rdList.add(rd);
2164                     }
2165                 });
2166             }
2167             // 1. VPN Instance Name
2168             String typeAlarm = "for vpnId: " + vpnId + " have exceeded next hops for prefixe";
2169
2170             // 2. Router ID
2171             List<Uuid> routerUuidList = neutronvpnUtils.getRouterIdListforVpn(vpnId);
2172             Uuid routerUuid = routerUuidList.get(0);
2173             StringBuilder detailsAlarm = new StringBuilder("routerUuid: ");
2174             detailsAlarm.append(routerUuid == null ? vpnId.toString() : routerUuid.getValue());
2175
2176             // 3. List of RDs associated with the VPN
2177             detailsAlarm.append(" List of RDs associated with the VPN: ");
2178             for (String s : rdList) {
2179                 detailsAlarm.append(s);
2180                 detailsAlarm.append(", ");
2181             }
2182
2183             // 4. Prefix in question
2184             detailsAlarm.append(" for prefix: ");
2185             detailsAlarm.append(route.getDestination().stringValue());
2186
2187             // 5. List of NHs for the prefix
2188             detailsAlarm.append(" for nextHops: ");
2189             for (String s : nextHopList) {
2190                 detailsAlarm.append(s);
2191                 detailsAlarm.append(", ");
2192             }
2193
2194             if (rdList.size() < nbNextHops) {
2195                 neutronvpnAlarm.raiseNeutronvpnAlarm(typeAlarm, detailsAlarm.toString());
2196             } else {
2197                 neutronvpnAlarm.clearNeutronvpnAlarm(typeAlarm, detailsAlarm.toString());
2198             }
2199         }
2200     }
2201
2202     // TODO Clean up the exception handling
2203     @SuppressWarnings("checkstyle:IllegalCatch")
2204     protected void removeAdjacencyforExtraRoute(Uuid vpnId, List<Routes> routeList) {
2205         for (Routes route : routeList) {
2206             if (route != null && route.getNexthop() != null && route.getDestination() != null) {
2207                 boolean isLockAcquired = false;
2208                 String nextHop = route.getNexthop().stringValue();
2209                 String destination = route.getDestination().stringValue();
2210                 String infName = neutronvpnUtils.getNeutronPortNameFromVpnPortFixedIp(vpnId.getValue(),
2211                         nextHop);
2212                 if (infName == null) {
2213                     LOG.error("Unable to find VPN NextHop interface to remove extra-route destination {} on VPN {} "
2214                             + "with nexthop {}", destination, vpnId.getValue(), nextHop);
2215                     // Proceed to remove the next extra-route
2216                     continue;
2217                 }
2218                 LOG.trace("Removing extra route for destination {} on vpn {} with nexthop {} and infName {}",
2219                         destination, vpnId.getValue(), nextHop, infName);
2220
2221                 InstanceIdentifier<Adjacency> adjacencyIdentifier =
2222                         InstanceIdentifier.builder(VpnInterfaces.class)
2223                                 .child(VpnInterface.class, new VpnInterfaceKey(infName))
2224                                 .augmentation(Adjacencies.class)
2225                                 .child(Adjacency.class, new AdjacencyKey(destination))
2226                                 .build();
2227
2228                 try {
2229                     // Looking for existing prefix in MDSAL database
2230                     Optional<Adjacency> adjacency = SingleTransactionDataBroker.syncReadOptional(dataBroker,
2231                             LogicalDatastoreType.CONFIGURATION, adjacencyIdentifier);
2232                     boolean updateNextHops = false;
2233                     List<String> nextHopList = new ArrayList<>();
2234                     if (adjacency.isPresent()) {
2235                         List<String> nhListRead = adjacency.get().getNextHopIpList();
2236                         if (nhListRead.size() > 1) { // ECMP case
2237                             for (String nextHopRead : nhListRead) {
2238                                 if (nextHopRead.equals(nextHop)) {
2239                                     updateNextHops = true;
2240                                 } else {
2241                                     nextHopList.add(nextHopRead);
2242                                 }
2243                             }
2244                         }
2245                     }
2246
2247                     isLockAcquired = interfaceLock.tryLock(infName, LOCK_WAIT_TIME, TimeUnit.SECONDS);
2248                     if (updateNextHops) {
2249                         // An update must be done, not including the current next hop
2250                         InstanceIdentifier<VpnInterface> vpnIfIdentifier = InstanceIdentifier.builder(
2251                                 VpnInterfaces.class).child(VpnInterface.class, new VpnInterfaceKey(infName)).build();
2252                         Adjacency newAdj = new AdjacencyBuilder(adjacency.get()).setIpAddress(destination)
2253                                 .setNextHopIpList(nextHopList)
2254                                 .withKey(new AdjacencyKey(destination))
2255                                 .build();
2256                         Adjacencies erAdjs =
2257                                 new AdjacenciesBuilder().setAdjacency(Collections.singletonList(newAdj)).build();
2258                         VpnInterface vpnIf = new VpnInterfaceBuilder().withKey(new VpnInterfaceKey(infName))
2259                                 .addAugmentation(Adjacencies.class, erAdjs).build();
2260                         SingleTransactionDataBroker.syncUpdate(dataBroker, LogicalDatastoreType.CONFIGURATION,
2261                                 vpnIfIdentifier, vpnIf);
2262                     } else {
2263                         // Remove the whole route
2264                         SingleTransactionDataBroker.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION,
2265                                 adjacencyIdentifier);
2266                         LOG.trace("extra route {} deleted successfully", route);
2267                     }
2268                 } catch (TransactionCommitFailedException | ReadFailedException e) {
2269                     LOG.error("exception in deleting extra route with destination {} for interface {}",
2270                             destination, infName, e);
2271                 } finally {
2272                     if (isLockAcquired) {
2273                         interfaceLock.unlock(infName);
2274                     }
2275                 }
2276             } else {
2277                 LOG.error("Incorrect input received for extra route: {}", route);
2278             }
2279         }
2280     }
2281
2282     public void removeVpn(Uuid vpnId) {
2283         // read VPNMaps
2284         VpnMap vpnMap = neutronvpnUtils.getVpnMap(vpnId);
2285         List<RouterIds> routerIdsList = vpnMap != null ? vpnMap.getRouterIds() : null;
2286         List<Uuid> routerUuidList = new ArrayList<>();
2287         // dissociate router
2288         if (routerIdsList != null && !routerIdsList.isEmpty()) {
2289             for (RouterIds router : routerIdsList) {
2290                 Uuid routerId = router.getRouterId();
2291                 routerUuidList.add(routerId);
2292                 dissociateRouterFromVpn(vpnId, routerId);
2293             }
2294         }
2295         if (!routerUuidList.contains(vpnId) && vpnMap.getNetworkIds() != null) {
2296             dissociateNetworksFromVpn(vpnId, vpnMap.getNetworkIds());
2297         }
2298         // remove entire vpnMaps node
2299         deleteVpnMapsNode(vpnId);
2300
2301         // remove vpn-instance
2302         deleteVpnInstance(vpnId);
2303         LOG.debug("Deleted L3VPN with ID {}", vpnId.getValue());
2304     }
2305
2306     private boolean isVpnOfTypeL2(VpnInstance vpnInstance) {
2307         return vpnInstance != null && vpnInstance.getType() == VpnInstance.Type.L2;
2308     }
2309
2310     // TODO Clean up the exception handling
2311     @SuppressWarnings("checkstyle:IllegalCatch")
2312     protected void associateRouterToVpn(Uuid vpnId, Uuid routerId) {
2313         updateVpnMaps(vpnId, null, routerId, null, null);
2314         LOG.debug("associateRouterToVpn: Updating association of subnets to external vpn {}", vpnId.getValue());
2315         List<Subnetmap> subMapList = neutronvpnUtils.getNeutronRouterSubnetMapList(routerId);
2316         IpVersionChoice ipVersion = IpVersionChoice.UNDEFINED;
2317         for (Subnetmap sn : subMapList) {
2318             IpVersionChoice ipVers = neutronvpnUtils.getIpVersionFromString(sn.getSubnetIp());
2319             if (!ipVersion.isIpVersionChosen(ipVers)) {
2320                 ipVersion = ipVersion.addVersion(ipVers);
2321             }
2322         }
2323         if (ipVersion != IpVersionChoice.UNDEFINED) {
2324             LOG.debug("associateRouterToVpn: Updating vpnInstanceOpDataEntrywith ip address family {} for VPN {} ",
2325                     ipVersion, vpnId);
2326             neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnId.getValue(), ipVersion, true);
2327         }
2328         for (Subnetmap sn : subMapList) {
2329             updateVpnForSubnet(routerId, vpnId, sn.getId(), true);
2330         }
2331     }
2332
2333     protected void associateRouterToInternalVpn(Uuid vpnId, Uuid routerId) {
2334         List<Uuid> routerSubnets = neutronvpnUtils.getNeutronRouterSubnetIds(routerId);
2335         Uuid internetVpnId = neutronvpnUtils.getInternetvpnUuidBoundToRouterId(routerId);
2336         LOG.debug("Adding subnets to internal vpn {}", vpnId.getValue());
2337         for (Uuid subnet : routerSubnets) {
2338             IpVersionChoice version = NeutronvpnUtils
2339                    .getIpVersionFromSubnet(neutronvpnUtils.getSubnetmap(subnet));
2340             if (version.isIpVersionChosen(IpVersionChoice.IPV4)) {
2341                 addSubnetToVpn(vpnId, subnet, null);
2342             } else {
2343                 addSubnetToVpn(vpnId, subnet, internetVpnId);
2344             }
2345         }
2346     }
2347
2348     // TODO Clean up the exception handling
2349     @SuppressWarnings("checkstyle:IllegalCatch")
2350     protected void dissociateRouterFromVpn(Uuid vpnId, Uuid routerId) {
2351
2352         clearFromVpnMaps(vpnId, routerId, null);
2353         List<Subnetmap> subMapList = neutronvpnUtils.getNeutronRouterSubnetMapList(routerId);
2354         IpVersionChoice ipVersion = IpVersionChoice.UNDEFINED;
2355         for (Subnetmap sn : subMapList) {
2356             IpVersionChoice ipVers = NeutronvpnUtils.getIpVersionFromString(sn.getSubnetIp());
2357             if (ipVersion.isIpVersionChosen(ipVers)) {
2358                 ipVersion = ipVersion.addVersion(ipVers);
2359             }
2360             LOG.debug("dissociateRouterFromVpn: Updating association of subnets to internal vpn {}",
2361                     routerId.getValue());
2362             updateVpnForSubnet(vpnId, routerId, sn.getId(), false);
2363         }
2364         if (ipVersion != IpVersionChoice.UNDEFINED) {
2365             LOG.debug("dissociateRouterFromVpn; Updating vpnInstanceOpDataEntry with ip address family {} for VPN {} ",
2366                     ipVersion, vpnId);
2367             neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnId.getValue(), ipVersion,
2368                     false);
2369         }
2370     }
2371
2372     /**
2373      * Parses and associates networks list with given VPN.
2374      *
2375      * @param vpnId Uuid of given VPN.
2376      * @param networkList List list of network Ids (Uuid), which will be associated.
2377      * @return list of formatted strings with detailed error messages.
2378      */
2379     @Nonnull
2380     protected List<String> associateNetworksToVpn(@Nonnull Uuid vpnId, @Nonnull List<Uuid> networkList) {
2381         List<String> failedNwList = new ArrayList<>();
2382         HashSet<Uuid> passedNwList = new HashSet<>();
2383         boolean isExternalNetwork = false;
2384         if (networkList.isEmpty()) {
2385             LOG.error("associateNetworksToVpn: Failed as given networks list is empty, VPN Id: {}", vpnId.getValue());
2386             failedNwList.add(String.format("Failed to associate networks with VPN %s as given networks list is empty",
2387                     vpnId.getValue()));
2388             return failedNwList;
2389         }
2390         VpnInstance vpnInstance = VpnHelper.getVpnInstance(dataBroker, vpnId.getValue());
2391         if (vpnInstance == null) {
2392             LOG.error("associateNetworksToVpn: Can not find vpnInstance for VPN {} in ConfigDS", vpnId.getValue());
2393             failedNwList.add(String.format("Failed to associate network: can not found vpnInstance for VPN %s "
2394                                            + "in ConfigDS", vpnId.getValue()));
2395             return failedNwList;
2396         }
2397         try {
2398             if (isVpnOfTypeL2(vpnInstance) && neutronEvpnUtils.isVpnAssociatedWithNetwork(vpnInstance)) {
2399                 LOG.error("associateNetworksToVpn: EVPN {} supports only one network to be associated with",
2400                           vpnId.getValue());
2401                 failedNwList.add(String.format("Failed to associate network: EVPN %s supports only one network to be "
2402                                                + "associated with", vpnId.getValue()));
2403                 return failedNwList;
2404             }
2405             Set<VpnTarget> routeTargets = vpnManager.getRtListForVpn(vpnId.getValue());
2406             for (Uuid nw : networkList) {
2407                 Network network = neutronvpnUtils.getNeutronNetwork(nw);
2408                 if (network == null) {
2409                     LOG.error("associateNetworksToVpn: Network {} not found in ConfigDS", nw.getValue());
2410                     failedNwList.add(String.format("Failed to associate network: network %s not found in ConfigDS",
2411                                                    nw.getValue()));
2412                     continue;
2413                 }
2414                 NetworkProviderExtension providerExtension = network.augmentation(NetworkProviderExtension.class);
2415                 if (providerExtension.getSegments() != null && providerExtension.getSegments().size() > 1) {
2416                     LOG.error("associateNetworksToVpn: MultiSegmented network {} not supported in BGPVPN {}",
2417                               nw.getValue(), vpnId.getValue());
2418                     failedNwList.add(String.format("Failed to associate multisegmented network %s with BGPVPN %s",
2419                                                    nw.getValue(), vpnId.getValue()));
2420                     continue;
2421                 }
2422                 Uuid networkVpnId = neutronvpnUtils.getVpnForNetwork(nw);
2423                 if (networkVpnId != null) {
2424                     LOG.error("associateNetworksToVpn: Network {} already associated with another VPN {}",
2425                               nw.getValue(), networkVpnId.getValue());
2426                     failedNwList.add(String.format("Failed to associate network %s as it is already associated to "
2427                                                    + "another VPN %s", nw.getValue(), networkVpnId.getValue()));
2428                     continue;
2429                 }
2430                 if (NeutronvpnUtils.getIsExternal(network) && !associateExtNetworkToVpn(vpnId, network)) {
2431                     LOG.error("associateNetworksToVpn: Failed to associate Provider Network {} with VPN {}",
2432                             nw.getValue(), vpnId.getValue());
2433                     failedNwList.add(String.format("Failed to associate Provider Network %s with VPN %s",
2434                             nw.getValue(), vpnId.getValue()));
2435                     continue;
2436                 }
2437                 if (NeutronvpnUtils.getIsExternal(network)) {
2438                     isExternalNetwork = true;
2439                 }
2440                 List<Subnetmap> subnetmapList = neutronvpnUtils.getSubnetmapListFromNetworkId(nw);
2441                 if (subnetmapList == null || subnetmapList.isEmpty()) {
2442                     passedNwList.add(nw);
2443                     continue;
2444                 }
2445                 if (vpnManager.checkForOverlappingSubnets(nw, subnetmapList, vpnId, routeTargets, failedNwList)) {
2446                     continue;
2447                 }
2448                 IpVersionChoice ipVersion = IpVersionChoice.UNDEFINED;
2449                 for (Subnetmap subnetmap : subnetmapList) {
2450                     IpVersionChoice ipVers = neutronvpnUtils.getIpVersionFromString(subnetmap.getSubnetIp());
2451                     if (!ipVersion.isIpVersionChosen(ipVers)) {
2452                         ipVersion = ipVersion.addVersion(ipVers);
2453                     }
2454                 }
2455                 if (ipVersion != IpVersionChoice.UNDEFINED) {
2456                     LOG.debug("associateNetworksToVpn: Updating vpnInstanceOpDataEntry with ip address family {}"
2457                             + " for VPN {} ", ipVersion, vpnId);
2458                     neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnId.getValue(), ipVersion, true);
2459                 }
2460                 for (Subnetmap subnetmap : subnetmapList) {
2461                     Uuid subnetId = subnetmap.getId();
2462                     Uuid subnetVpnId = neutronvpnUtils.getVpnForSubnet(subnetId);
2463                     if (subnetVpnId != null) {
2464                         LOG.error("associateNetworksToVpn: Failed to associate subnet {} with VPN {}"
2465                                 + " as it is already associated", subnetId.getValue(), subnetVpnId.getValue());
2466                         failedNwList.add(String.format("Failed to associate subnet %s with VPN %s"
2467                                 + " as it is already associated", subnetId.getValue(), vpnId.getValue()));
2468                         continue;
2469                     }
2470                     if (!NeutronvpnUtils.getIsExternal(network)) {
2471                         LOG.debug("associateNetworksToVpn: Add subnet {} to VPN {}", subnetId.getValue(),
2472                                 vpnId.getValue());
2473                         addSubnetToVpn(vpnId, subnetId, null);
2474                         vpnManager.updateRouteTargetsToSubnetAssociation(routeTargets, subnetmap.getSubnetIp(),
2475                                 vpnId.getValue());
2476                         passedNwList.add(nw);
2477                     }
2478                 }
2479                 passedNwList.add(nw);
2480             }
2481         } catch (ReadFailedException e) {
2482             LOG.error("associateNetworksToVpn: Failed to associate VPN {} with networks {}: ", vpnId.getValue(),
2483                     networkList, e);
2484             failedNwList.add(String.format("Failed to associate VPN %s with networks %s: %s", vpnId.getValue(),
2485                     networkList, e));
2486         }
2487         //VpnMap update for ext-nw is already done in associateExtNetworkToVpn() method.
2488         if (!isExternalNetwork) {
2489             updateVpnMaps(vpnId, null, null, null, new ArrayList<>(passedNwList));
2490         }
2491         LOG.info("Network(s) {} associated to L3VPN {} successfully", passedNwList.toString(), vpnId.getValue());
2492         return failedNwList;
2493     }
2494
2495     private boolean associateExtNetworkToVpn(@Nonnull Uuid vpnId, @Nonnull Network extNet) {
2496         if (!addExternalNetworkToVpn(extNet, vpnId)) {
2497             return false;
2498         }
2499         VpnInstanceOpDataEntry vpnOpDataEntry = neutronvpnUtils.getVpnInstanceOpDataEntryFromVpnId(vpnId.getValue());
2500         if (vpnOpDataEntry == null) {
2501             LOG.error("associateExtNetworkToVpn: can not find VpnOpDataEntry for VPN {}", vpnId.getValue());
2502             return false;
2503         }
2504         if (!vpnOpDataEntry.getBgpvpnType().equals(BgpvpnType.BGPVPNInternet)) {
2505             LOG.info("associateExtNetworkToVpn: set type {} for VPN {}", BgpvpnType.BGPVPNInternet, vpnId.getValue());
2506             neutronvpnUtils.updateVpnInstanceOpWithType(BgpvpnType.BGPVPNInternet, vpnId);
2507         }
2508         //Update VpnMap with ext-nw is needed first before processing V6 internet default fallback flows
2509         List<Uuid> extNwList = Collections.singletonList(extNet.key().getUuid());
2510         updateVpnMaps(vpnId, null, null, null, extNwList);
2511         IpVersionChoice ipVersion = IpVersionChoice.UNDEFINED;
2512         for (Uuid snId: neutronvpnUtils.getPrivateSubnetsToExport(extNet, vpnId)) {
2513             Subnetmap sm = neutronvpnUtils.getSubnetmap(snId);
2514             if (sm == null) {
2515                 LOG.error("associateExtNetworkToVpn: can not find subnet with Id {} in ConfigDS", snId.getValue());
2516                 continue;
2517             }
2518             IpVersionChoice ipVers = NeutronvpnUtils.getIpVersionFromString(sm.getSubnetIp());
2519             if (ipVers.isIpVersionChosen(IpVersionChoice.IPV4)) {
2520                 continue;
2521             }
2522             if (ipVers.isIpVersionChosen(IpVersionChoice.IPV6)) {
2523                 updateVpnInternetForSubnet(sm, vpnId, true);
2524             }
2525             if (!ipVersion.isIpVersionChosen(ipVers)) {
2526                 ipVersion = ipVersion.addVersion(ipVers);
2527             }
2528         }
2529         if (ipVersion != IpVersionChoice.UNDEFINED) {
2530             neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnId.getValue(), IpVersionChoice.IPV6, true);
2531             LOG.info("associateExtNetworkToVpn: add IPv6 Internet default route in VPN {}", vpnId.getValue());
2532             neutronvpnUtils.updateVpnInstanceWithFallback(/*routerId*/ null, vpnId, true);
2533         }
2534         return true;
2535     }
2536
2537     /**
2538      * Parses and disassociates networks list from given VPN.
2539      *
2540      * @param vpnId Uuid of given VPN.
2541      * @param networkList List list of network Ids (Uuid), which will be disassociated.
2542      * @return list of formatted strings with detailed error messages.
2543      */
2544     @Nonnull
2545     protected List<String> dissociateNetworksFromVpn(@Nonnull Uuid vpnId, @Nonnull List<Uuid> networkList) {
2546         List<String> failedNwList = new ArrayList<>();
2547         HashSet<Uuid> passedNwList = new HashSet<>();
2548         if (networkList.isEmpty()) {
2549             LOG.error("dissociateNetworksFromVpn: Failed as networks list is empty");
2550             failedNwList.add(String.format("Failed to disassociate networks from VPN %s as networks list is empty",
2551                              vpnId.getValue()));
2552             return failedNwList;
2553         }
2554         for (Uuid nw : networkList) {
2555             List<Uuid> networkSubnets = neutronvpnUtils.getSubnetIdsFromNetworkId(nw);
2556             if (networkSubnets == null) {
2557                 passedNwList.add(nw);
2558                 continue;
2559             }
2560             Network network = neutronvpnUtils.getNeutronNetwork(nw);
2561             if (network == null) {
2562                 LOG.error("dissociateNetworksFromVpn: Network {} not found in ConfigDS");
2563                 failedNwList.add(String.format("Failed to disassociate network %s as is not found in ConfigDS",
2564                         nw.getValue()));
2565                 continue;
2566             }
2567             Uuid networkVpnId = neutronvpnUtils.getVpnForNetwork(nw);
2568             if (networkVpnId == null) {
2569                 LOG.error("dissociateNetworksFromVpn: Network {} is not associated to any VPN", nw.getValue());
2570                 failedNwList.add(String.format("Failed to disassociate network %s as is not associated to any VPN",
2571                                                nw.getValue()));
2572                 continue;
2573             }
2574             if (!vpnId.equals(networkVpnId)) {
2575                 LOG.error("dissociateNetworksFromVpn: Network {} is associated to another VPN {} instead of given {}",
2576                           nw.getValue(), networkVpnId.getValue(), vpnId.getValue());
2577                 failedNwList.add(String.format("Failed to disassociate network %s as it is associated to another "
2578                                 + "vpn %s instead of given %s", nw.getValue(), networkVpnId.getValue(),
2579                                 vpnId.getValue()));
2580                 continue;
2581             }
2582             if (NeutronvpnUtils.getIsExternal(network)) {
2583                 if (disassociateExtNetworkFromVpn(vpnId, network)) {
2584                     passedNwList.add(nw);
2585                 } else {
2586                     LOG.error("dissociateNetworksFromVpn: Failed to withdraw Provider Network {} from VPN {}",
2587                               nw.getValue(), vpnId.getValue());
2588                     failedNwList.add(String.format("Failed to withdraw Provider Network %s from VPN %s", nw.getValue(),
2589                                                    vpnId.getValue()));
2590                     continue;
2591                 }
2592             }
2593             IpVersionChoice ipVersion = IpVersionChoice.UNDEFINED;
2594             for (Uuid subnet : networkSubnets) {
2595                 Subnetmap subnetmap = neutronvpnUtils.getSubnetmap(subnet);
2596                 IpVersionChoice ipVers = NeutronvpnUtils.getIpVersionFromString(subnetmap.getSubnetIp());
2597                 if (!ipVersion.isIpVersionChosen(ipVers)) {
2598                     ipVersion = ipVersion.addVersion(ipVers);
2599                 }
2600                 if (!NeutronvpnUtils.getIsExternal(network)) {
2601                     LOG.debug("dissociateNetworksFromVpn: Withdraw subnet {} from VPN {}", subnet.getValue(),
2602                             vpnId.getValue());
2603                     removeSubnetFromVpn(vpnId, subnet, null);
2604                     Set<VpnTarget> routeTargets = vpnManager.getRtListForVpn(vpnId.getValue());
2605                     vpnManager.removeRouteTargetsToSubnetAssociation(routeTargets, subnetmap.getSubnetIp(),
2606                             vpnId.getValue());
2607                     passedNwList.add(nw);
2608                 }
2609             }
2610             if (ipVersion != IpVersionChoice.UNDEFINED) {
2611                 LOG.debug("dissociateNetworksFromVpn: Updating vpnInstanceOpDataEntryupdate with ip address family {}"
2612                         + " for VPN {}", ipVersion, vpnId);
2613                 neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnId.getValue(), ipVersion, false);
2614             }
2615         }
2616         clearFromVpnMaps(vpnId, null, new ArrayList<>(passedNwList));
2617         LOG.info("dissociateNetworksFromVpn: Network(s) {} disassociated from L3VPN {} successfully",
2618                 passedNwList.toString(), vpnId.getValue());
2619         return failedNwList;
2620     }
2621
2622     private boolean disassociateExtNetworkFromVpn(@Nonnull Uuid vpnId, @Nonnull Network extNet) {
2623         if (!removeExternalNetworkFromVpn(extNet)) {
2624             return false;
2625         }
2626         // check, if there is another Provider Networks associated with given VPN
2627         List<Uuid> vpnNets = getNetworksForVpn(vpnId);
2628         if (vpnNets != null) {
2629             //Remove currently disassociated network from the list
2630             vpnNets.remove(extNet.getUuid());
2631             for (Uuid netId : vpnNets) {
2632                 if (NeutronvpnUtils.getIsExternal(getNeutronNetwork(netId))) {
2633                     LOG.error("dissociateExtNetworkFromVpn: Internet VPN {} is still associated with Provider Network "
2634                             + "{}", vpnId.getValue(), netId.getValue());
2635                     return true;
2636                 }
2637             }
2638         }
2639         //Set VPN Type is BGPVPNExternal from BGPVPNInternet
2640         LOG.info("disassociateExtNetworkFromVpn: set type {} for VPN {}",
2641                 VpnInstanceOpDataEntry.BgpvpnType.BGPVPNExternal, vpnId.getValue());
2642         neutronvpnUtils.updateVpnInstanceOpWithType(VpnInstanceOpDataEntry.BgpvpnType.BGPVPNExternal, vpnId);
2643         IpVersionChoice ipVersion = IpVersionChoice.UNDEFINED;
2644         for (Uuid snId : neutronvpnUtils.getPrivateSubnetsToExport(extNet, vpnId)) {
2645             Subnetmap sm = neutronvpnUtils.getSubnetmap(snId);
2646             if (sm == null) {
2647                 LOG.error("disassociateExtNetworkFromVpn: can not find subnet with Id {} in ConfigDS", snId.getValue());
2648                 continue;
2649             }
2650             IpVersionChoice ipVers = NeutronvpnUtils.getIpVersionFromString(sm.getSubnetIp());
2651             if (ipVers.isIpVersionChosen(IpVersionChoice.IPV4)) {
2652                 continue;
2653             }
2654             if (ipVers.isIpVersionChosen(IpVersionChoice.IPV6)) {
2655                 updateVpnInternetForSubnet(sm, vpnId, false);
2656             }
2657             if (!ipVersion.isIpVersionChosen(ipVers)) {
2658                 ipVersion = ipVersion.addVersion(ipVers);
2659             }
2660         }
2661         if (ipVersion != IpVersionChoice.UNDEFINED) {
2662             neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnId.getValue(), IpVersionChoice.IPV6, false);
2663             LOG.info("disassociateExtNetworkFromVpn: withdraw IPv6 Internet default route from VPN {}",
2664                     vpnId.getValue());
2665             neutronvpnUtils.updateVpnInstanceWithFallback(/*routerId*/ null, vpnId, false);
2666         }
2667         return true;
2668     }
2669
2670     /**
2671      * It handles the invocations to the neutronvpn:associateNetworks RPC method.
2672      */
2673     @Override
2674     // TODO Clean up the exception handling
2675     @SuppressWarnings("checkstyle:IllegalCatch")
2676     public ListenableFuture<RpcResult<AssociateNetworksOutput>> associateNetworks(AssociateNetworksInput input) {
2677
2678         AssociateNetworksOutputBuilder opBuilder = new AssociateNetworksOutputBuilder();
2679         SettableFuture<RpcResult<AssociateNetworksOutput>> result = SettableFuture.create();
2680         StringBuilder returnMsg = new StringBuilder();
2681         Uuid vpnId = input.getVpnId();
2682
2683         try {
2684             if (neutronvpnUtils.getVpnMap(vpnId) != null) {
2685                 LOG.debug("associateNetworks RPC: VpnId {}, networkList {}", vpnId.getValue(),
2686                         input.getNetworkId().toString());
2687                 List<Uuid> netIds = input.getNetworkId();
2688                 if (netIds != null && !netIds.isEmpty()) {
2689                     List<String> failed = associateNetworksToVpn(vpnId, netIds);
2690                     if (!failed.isEmpty()) {
2691                         returnMsg.append(failed);
2692                     }
2693                 }
2694             } else {
2695                 returnMsg.append("VPN not found : ").append(vpnId.getValue());
2696             }
2697             if (returnMsg.length() != 0) {
2698                 opBuilder.setResponse(
2699                         "ErrorType: PROTOCOL, ErrorTag: invalid-value, ErrorMessage: " + formatAndLog(LOG::error,
2700                                 "associate Networks to vpn {} failed due to {}", vpnId.getValue(), returnMsg));
2701                 result.set(RpcResultBuilder.<AssociateNetworksOutput>success().withResult(opBuilder.build()).build());
2702             } else {
2703                 result.set(RpcResultBuilder.<AssociateNetworksOutput>success().build());
2704             }
2705         } catch (Exception ex) {
2706             result.set(RpcResultBuilder.<AssociateNetworksOutput>failed().withError(ErrorType.APPLICATION,
2707                     formatAndLog(LOG::error, "associate Networks to vpn {} failed due to {}",
2708                             input.getVpnId().getValue(), ex.getMessage(), ex)).build());
2709         }
2710         LOG.debug("associateNetworks returns..");
2711         return result;
2712     }
2713
2714     /**
2715      * It handles the invocations to the neutronvpn:associateRouter RPC method.
2716      */
2717     @Override
2718     public ListenableFuture<RpcResult<AssociateRouterOutput>> associateRouter(AssociateRouterInput input) {
2719
2720         SettableFuture<RpcResult<AssociateRouterOutput>> result = SettableFuture.create();
2721         LOG.debug("associateRouter {}", input);
2722         StringBuilder returnMsg = new StringBuilder();
2723         Uuid vpnId = input.getVpnId();
2724         List<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.associaterouter.input.RouterIds>
2725                 routerIds = input.getRouterIds();
2726         Preconditions.checkArgument(!routerIds.isEmpty(), "associateRouter: RouterIds list is empty!");
2727         Preconditions.checkNotNull(vpnId, "associateRouter; VpnId not found!");
2728         Preconditions.checkNotNull(vpnId, "associateRouter; RouterIds not found!");
2729         for (org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.associaterouter.input
2730                 .RouterIds routerId : routerIds) {
2731             VpnMap vpnMap = neutronvpnUtils.getVpnMap(vpnId);
2732             Router rtr = neutronvpnUtils.getNeutronRouter(routerId.getRouterId());
2733             if (vpnMap != null) {
2734                 if (rtr != null) {
2735                     Uuid extVpnId = neutronvpnUtils.getVpnForRouter(routerId.getRouterId(), true);
2736                     if (vpnMap.getRouterIds() != null && vpnMap.getRouterIds().size() > 1) {
2737                         returnMsg.append("vpn ").append(vpnId.getValue()).append(" already associated to router ")
2738                                 .append(routerId.getRouterId());
2739                     } else if (extVpnId != null) {
2740                         returnMsg.append("router ").append(routerId.getRouterId()).append(" already associated to "
2741                                 + "another VPN ").append(extVpnId.getValue());
2742                     } else {
2743                         LOG.debug("associateRouter RPC: VpnId {}, routerId {}", vpnId.getValue(),
2744                                 routerId.getRouterId());
2745                         associateRouterToVpn(vpnId, routerId.getRouterId());
2746                     }
2747                 } else {
2748                     returnMsg.append("router not found : ").append(routerId.getRouterId());
2749                 }
2750             } else {
2751                 returnMsg.append("VPN not found : ").append(vpnId.getValue());
2752             }
2753             if (returnMsg.length() != 0) {
2754                 result.set(RpcResultBuilder.<AssociateRouterOutput>failed().withWarning(ErrorType.PROTOCOL,
2755                         "invalid-value", formatAndLog(LOG::error, "associate router to vpn {} failed "
2756                                 + "due to {}", routerId.getRouterId(), returnMsg)).build());
2757             } else {
2758                 result.set(RpcResultBuilder.success(new AssociateRouterOutputBuilder().build()).build());
2759             }
2760         }
2761         LOG.debug("associateRouter returns..");
2762         return result;
2763     }
2764
2765     /**
2766      * It handles the invocations to the neutronvpn:getFixedIPsForNeutronPort RPC method.
2767      */
2768     @Override
2769     // TODO Clean up the exception handling
2770     @SuppressWarnings("checkstyle:IllegalCatch")
2771     public ListenableFuture<RpcResult<GetFixedIPsForNeutronPortOutput>> getFixedIPsForNeutronPort(
2772         GetFixedIPsForNeutronPortInput input) {
2773         GetFixedIPsForNeutronPortOutputBuilder opBuilder = new GetFixedIPsForNeutronPortOutputBuilder();
2774         SettableFuture<RpcResult<GetFixedIPsForNeutronPortOutput>> result = SettableFuture.create();
2775         Uuid portId = input.getPortId();
2776         StringBuilder returnMsg = new StringBuilder();
2777         try {
2778             List<String> fixedIPList = new ArrayList<>();
2779             Port port = neutronvpnUtils.getNeutronPort(portId);
2780             if (port != null) {
2781                 for (FixedIps ip : requireNonNullElse(port.getFixedIps(), Collections.<FixedIps>emptyList())) {
2782                     fixedIPList.add(ip.getIpAddress().stringValue());
2783                 }
2784             } else {
2785                 returnMsg.append("neutron port: ").append(portId.getValue()).append(" not found");
2786             }
2787             if (returnMsg.length() != 0) {
2788                 result.set(RpcResultBuilder.<GetFixedIPsForNeutronPortOutput>failed().withWarning(ErrorType.PROTOCOL,
2789                         "invalid-value",
2790                         formatAndLog(LOG::error, "Retrieval of FixedIPList for neutron port failed due to {}",
2791                                 returnMsg)).build());
2792             } else {
2793                 opBuilder.setFixedIPs(fixedIPList);
2794                 result.set(RpcResultBuilder.<GetFixedIPsForNeutronPortOutput>success().withResult(opBuilder.build())
2795                         .build());
2796                 result.set(RpcResultBuilder.<GetFixedIPsForNeutronPortOutput>success().build());
2797             }
2798         } catch (Exception ex) {
2799             result.set(RpcResultBuilder.<GetFixedIPsForNeutronPortOutput>failed().withError(ErrorType.APPLICATION,
2800                     formatAndLog(LOG::error, "Retrieval of FixedIPList for neutron port {} failed due to {}",
2801                             portId.getValue(), ex.getMessage(), ex)).build());
2802         }
2803         return result;
2804     }
2805
2806     /**
2807      * It handles the invocations to the neutronvpn:dissociateNetworks RPC method.
2808      */
2809     @Override
2810     // TODO Clean up the exception handling
2811     @SuppressWarnings("checkstyle:IllegalCatch")
2812     public ListenableFuture<RpcResult<DissociateNetworksOutput>> dissociateNetworks(DissociateNetworksInput input) {
2813
2814         DissociateNetworksOutputBuilder opBuilder = new DissociateNetworksOutputBuilder();
2815         SettableFuture<RpcResult<DissociateNetworksOutput>> result = SettableFuture.create();
2816
2817         LOG.debug("dissociateNetworks {}", input);
2818         StringBuilder returnMsg = new StringBuilder();
2819         Uuid vpnId = input.getVpnId();
2820
2821         try {
2822             if (neutronvpnUtils.getVpnMap(vpnId) != null) {
2823                 LOG.debug("dissociateNetworks RPC: VpnId {}, networkList {}", vpnId.getValue(),
2824                         input.getNetworkId().toString());
2825                 List<Uuid> netIds = input.getNetworkId();
2826                 if (netIds != null && !netIds.isEmpty()) {
2827                     List<String> failed = dissociateNetworksFromVpn(vpnId, netIds);
2828                     if (!failed.isEmpty()) {
2829                         returnMsg.append(failed);
2830                     }
2831                 }
2832             } else {
2833                 returnMsg.append("VPN not found : ").append(vpnId.getValue());
2834             }
2835             if (returnMsg.length() != 0) {
2836                 opBuilder.setResponse(
2837                         "ErrorType: PROTOCOL, ErrorTag: invalid-value, ErrorMessage: " + formatAndLog(LOG::error,
2838                                 "dissociate Networks to vpn {} failed due to {}", vpnId.getValue(),
2839                                 returnMsg));
2840                 result.set(RpcResultBuilder.<DissociateNetworksOutput>success().withResult(opBuilder.build()).build());
2841             } else {
2842                 result.set(RpcResultBuilder.<DissociateNetworksOutput>success().build());
2843             }
2844         } catch (Exception ex) {
2845             result.set(RpcResultBuilder.<DissociateNetworksOutput>failed().withError(ErrorType.APPLICATION,
2846                     formatAndLog(LOG::error, "dissociate Networks to vpn {} failed due to {}",
2847                             input.getVpnId().getValue(), ex.getMessage(), ex)).build());
2848         }
2849         LOG.debug("dissociateNetworks returns..");
2850         return result;
2851     }
2852
2853     /**
2854      * It handles the invocations to the neutronvpn:dissociateRouter RPC method.
2855      */
2856     @Override
2857     // TODO Clean up the exception handling
2858     @SuppressWarnings("checkstyle:IllegalCatch")
2859     public ListenableFuture<RpcResult<DissociateRouterOutput>> dissociateRouter(DissociateRouterInput input) {
2860
2861         SettableFuture<RpcResult<DissociateRouterOutput>> result = SettableFuture.create();
2862
2863         LOG.debug("dissociateRouter {}", input);
2864         StringBuilder returnMsg = new StringBuilder();
2865         Uuid vpnId = input.getVpnId();
2866         List<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.dissociaterouter.input
2867                 .RouterIds> routerIdList = input.getRouterIds();
2868         String routerIdsString = "";
2869         Preconditions.checkArgument(!routerIdList.isEmpty(), "dissociateRouter: RouterIds list is empty!");
2870         Preconditions.checkNotNull(vpnId, "dissociateRouter: vpnId not found!");
2871         Preconditions.checkNotNull(routerIdList, "dissociateRouter: routerIdList not found!");
2872         if (neutronvpnUtils.getVpnMap(vpnId) != null) {
2873             for (org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.dissociaterouter.input
2874                     .RouterIds routerId : routerIdList) {
2875                 try {
2876                     if (routerId != null) {
2877                         routerIdsString += routerId.getRouterId() + ", ";
2878                         Router rtr = neutronvpnUtils.getNeutronRouter(routerId.getRouterId());
2879                         if (rtr != null) {
2880                             Uuid routerVpnId = neutronvpnUtils.getVpnForRouter(routerId.getRouterId(), true);
2881                             if (routerVpnId == null) {
2882                                 returnMsg.append("input router ").append(routerId.getRouterId())
2883                                         .append(" not associated to any vpn yet");
2884                             } else if (vpnId.equals(routerVpnId)) {
2885                                 dissociateRouterFromVpn(vpnId, routerId.getRouterId());
2886                             } else {
2887                                 returnMsg.append("input router ").append(routerId.getRouterId())
2888                                         .append(" associated to vpn ")
2889                                         .append(routerVpnId.getValue()).append("instead of the vpn given as input");
2890                             }
2891                         } else {
2892                             returnMsg.append("router not found : ").append(routerId.getRouterId());
2893                         }
2894                     }
2895                     if (returnMsg.length() != 0) {
2896                         result.set(RpcResultBuilder.<DissociateRouterOutput>failed().withWarning(ErrorType.PROTOCOL,
2897                                 "invalid-value", formatAndLog(LOG::error, "dissociate router {} to "
2898                                                 + "vpn {} failed due to {}", routerId.getRouterId(), vpnId.getValue(),
2899                                         returnMsg)).build());
2900                     } else {
2901                         result.set(RpcResultBuilder.success(new DissociateRouterOutputBuilder().build()).build());
2902                     }
2903                 } catch (Exception ex) {
2904                     result.set(RpcResultBuilder.<DissociateRouterOutput>failed().withError(ErrorType.APPLICATION,
2905                             formatAndLog(LOG::error, "disssociate router {} to vpn {} failed due to {}",
2906                                     routerId.getRouterId(), vpnId.getValue(), ex.getMessage(), ex)).build());
2907                 }
2908             }
2909         } else {
2910             returnMsg.append("VPN not found : ").append(vpnId.getValue());
2911         }
2912
2913         LOG.debug("dissociateRouter returns..");
2914         return result;
2915     }
2916
2917     protected void handleNeutronRouterDeleted(Uuid routerId, List<Uuid> routerSubnetIds) {
2918         // check if the router is associated to some VPN
2919         Uuid vpnId = neutronvpnUtils.getVpnForRouter(routerId, true);
2920         Uuid internetVpnId = neutronvpnUtils.getInternetvpnUuidBoundToRouterId(routerId);
2921         if (vpnId != null) {
2922             // remove existing external vpn interfaces
2923             for (Uuid subnetId : routerSubnetIds) {
2924                 removeSubnetFromVpn(vpnId, subnetId, internetVpnId);
2925             }
2926             clearFromVpnMaps(vpnId, routerId, null);
2927         } else {
2928             // remove existing internal vpn interfaces
2929             for (Uuid subnetId : routerSubnetIds) {
2930                 removeSubnetFromVpn(routerId, subnetId, internetVpnId);
2931             }
2932         }
2933         // delete entire vpnMaps node for internal VPN
2934         deleteVpnMapsNode(routerId);
2935
2936         // delete vpn-instance for internal VPN
2937         deleteVpnInstance(routerId);
2938     }
2939
2940     protected Subnet getNeutronSubnet(Uuid subnetId) {
2941         return neutronvpnUtils.getNeutronSubnet(subnetId);
2942     }
2943
2944     @Nullable
2945     protected IpAddress getNeutronSubnetGateway(Uuid subnetId) {
2946         Subnet sn = neutronvpnUtils.getNeutronSubnet(subnetId);
2947         if (null != sn) {
2948             return sn.getGatewayIp();
2949         }
2950         return null;
2951     }
2952
2953
2954     protected Network getNeutronNetwork(Uuid networkId) {
2955         return neutronvpnUtils.getNeutronNetwork(networkId);
2956     }
2957
2958     protected Port getNeutronPort(String name) {
2959         return neutronvpnUtils.getNeutronPort(new Uuid(name));
2960     }
2961
2962     protected Port getNeutronPort(Uuid portId) {
2963         return neutronvpnUtils.getNeutronPort(portId);
2964     }
2965
2966     protected Uuid getNetworkForSubnet(Uuid subnetId) {
2967         return neutronvpnUtils.getNetworkForSubnet(subnetId);
2968     }
2969
2970     protected List<Uuid> getNetworksForVpn(Uuid vpnId) {
2971         return neutronvpnUtils.getNetworksForVpn(vpnId);
2972     }
2973
2974     /**
2975      * Implementation of the "vpnservice:neutron-ports-show" Karaf CLI command.
2976      *
2977      * @return a List of String to be printed on screen
2978      * @throws ReadFailedException if there was a problem reading from the data store
2979      */
2980     public List<String> showNeutronPortsCLI() throws ReadFailedException {
2981         List<String> result = new ArrayList<>();
2982         result.add(String.format(" %-36s  %-19s  %-13s  %-20s ", "Port ID", "Mac Address", "Prefix Length",
2983             "IP Address"));
2984         result.add("-------------------------------------------------------------------------------------------");
2985         InstanceIdentifier<Ports> portidentifier = InstanceIdentifier.create(Neutron.class).child(Ports.class);
2986
2987         Optional<Ports> ports = syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION, portidentifier);
2988         if (ports.isPresent() && ports.get().getPort() != null) {
2989             for (Port port : requireNonNullElse(ports.get().getPort(), Collections.<Port>emptyList())) {
2990                 List<FixedIps> fixedIPs = port.getFixedIps();
2991                 if (fixedIPs != null && !fixedIPs.isEmpty()) {
2992                     List<String> ipList = new ArrayList<>();
2993                     for (FixedIps fixedIp : fixedIPs) {
2994                         IpAddress ipAddress = fixedIp.getIpAddress();
2995                         if (ipAddress.getIpv4Address() != null) {
2996                             ipList.add(ipAddress.getIpv4Address().getValue());
2997                         } else {
2998                             ipList.add(ipAddress.getIpv6Address().getValue());
2999                         }
3000                     }
3001                     result.add(String.format(" %-36s  %-19s  %-13s  %-20s ", port.getUuid().getValue(), port
3002                             .getMacAddress().getValue(), neutronvpnUtils.getIPPrefixFromPort(port),
3003                             ipList.toString()));
3004                 } else {
3005                     result.add(String.format(" %-36s  %-19s  %-13s  %-20s ", port.getUuid().getValue(), port
3006                             .getMacAddress().getValue(), "Not Assigned", "Not Assigned"));
3007                 }
3008             }
3009         }
3010
3011         return result;
3012     }
3013
3014     /**
3015      * Implementation of the "vpnservice:l3vpn-config-show" karaf CLI command.
3016      *
3017      * @param vpnuuid Uuid of the VPN whose config must be shown
3018      * @return formatted output list
3019      * @throws InterruptedException if there was a thread related problem getting the data to display
3020      * @throws ExecutionException if there was any other problem getting the data to display
3021      */
3022     public List<String> showVpnConfigCLI(Uuid vpnuuid) throws InterruptedException, ExecutionException {
3023         List<String> result = new ArrayList<>();
3024         if (vpnuuid == null) {
3025             result.add("");
3026             result.add("Displaying VPN config for all VPNs");
3027             result.add("To display VPN config for a particular VPN, use the following syntax");
3028             result.add(getshowVpnConfigCLIHelp());
3029         }
3030         RpcResult<GetL3VPNOutput> rpcResult = getL3VPN(new GetL3VPNInputBuilder().setId(vpnuuid).build()).get();
3031         if (rpcResult.isSuccessful()) {
3032             result.add("");
3033             result.add(String.format(" %-37s %-37s %-7s ", "VPN ID", "Tenant ID", "RD"));
3034             result.add("");
3035             result.add(String.format(" %-80s ", "Import-RTs"));
3036             result.add("");
3037             result.add(String.format(" %-80s ", "Export-RTs"));
3038             result.add("");
3039             result.add(String.format(" %-76s ", "Subnet IDs"));
3040             result.add("");
3041             result.add("------------------------------------------------------------------------------------");
3042             result.add("");
3043             for (org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.VpnInstance vpn :
3044                     requireNonNullElse(rpcResult.getResult().getL3vpnInstances(),
3045                         Collections.<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602
3046                             .VpnInstance>emptyList())) {
3047                 String tenantId = vpn.getTenantId() != null ? vpn.getTenantId().getValue()
3048                         : "\"                 " + "                  \"";
3049                 result.add(String.format(" %-37s %-37s %-7s ", vpn.getId().getValue(), tenantId,
3050                         vpn.getRouteDistinguisher()));
3051                 result.add("");
3052                 result.add(String.format(" %-80s ", vpn.getImportRT()));
3053                 result.add("");
3054                 result.add(String.format(" %-80s ", vpn.getExportRT()));
3055                 result.add("");
3056
3057                 Uuid vpnid = vpn.getId();
3058                 List<Uuid> subnetList = neutronvpnUtils.getSubnetsforVpn(vpnid);
3059                 if (!subnetList.isEmpty()) {
3060                     for (Uuid subnetuuid : subnetList) {
3061                         result.add(String.format(" %-76s ", subnetuuid.getValue()));
3062                     }
3063                 } else {
3064                     result.add(String.format(" %-76s ", "\"                                    \""));
3065                 }
3066                 result.add("");
3067                 result.add("----------------------------------------");
3068                 result.add("");
3069             }
3070         } else {
3071             String errortag = rpcResult.getErrors().iterator().next().getTag();
3072             if (Objects.equals(errortag, "")) {
3073                 result.add("");
3074                 result.add("No VPN has been configured yet");
3075             } else if (Objects.equals(errortag, "invalid-value")) {
3076                 result.add("");
3077                 result.add("VPN " + vpnuuid.getValue() + " is not present");
3078             } else {
3079                 result.add("error getting VPN info : " + rpcResult.getErrors());
3080                 result.add(getshowVpnConfigCLIHelp());
3081             }
3082         }
3083         return result;
3084     }
3085
3086     protected void createExternalVpnInterfaces(Uuid extNetId) {
3087         if (extNetId == null) {
3088             LOG.error("createExternalVpnInterfaces: external network is null");
3089             return;
3090         }
3091
3092         Collection<String> extElanInterfaces = elanService.getExternalElanInterfaces(extNetId.getValue());
3093         if (extElanInterfaces == null || extElanInterfaces.isEmpty()) {
3094             LOG.error("No external ports attached to external network {}", extNetId.getValue());
3095             return;
3096         }
3097
3098         ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
3099             for (String elanInterface : extElanInterfaces) {
3100                 createExternalVpnInterface(extNetId, elanInterface, tx);
3101             }
3102         }), LOG, "Error creating external VPN interfaces for {}", extNetId);
3103     }
3104
3105     // TODO Clean up the exception handling
3106     @SuppressWarnings("checkstyle:IllegalCatch")
3107     protected void removeExternalVpnInterfaces(Uuid extNetId) {
3108         Collection<String> extElanInterfaces = elanService.getExternalElanInterfaces(extNetId.getValue());
3109         if (extElanInterfaces == null || extElanInterfaces.isEmpty()) {
3110             LOG.error("No external ports attached for external network {}", extNetId.getValue());
3111             return;
3112         }
3113         ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
3114             for (String elanInterface : extElanInterfaces) {
3115                 InstanceIdentifier<VpnInterface> vpnIfIdentifier = NeutronvpnUtils
3116                         .buildVpnInterfaceIdentifier(elanInterface);
3117                 LOG.info("Removing vpn interface {}", elanInterface);
3118                 tx.delete(vpnIfIdentifier);
3119             }
3120         }), LOG, "Error removing external VPN interfaces for {}", extNetId);
3121     }
3122
3123     private void createExternalVpnInterface(Uuid vpnId, String infName,
3124                                             TypedWriteTransaction<Configuration> wrtConfigTxn) {
3125         writeVpnInterfaceToDs(Collections.singletonList(vpnId), infName, null, vpnId /* external network id */,
3126                 false /* not a router iface */, wrtConfigTxn);
3127     }
3128
3129     // TODO Clean up the exception handling
3130     @SuppressWarnings("checkstyle:IllegalCatch")
3131     private void writeVpnInterfaceToDs(@Nonnull Collection<Uuid> vpnIdList, String infName,
3132             @Nullable Adjacencies adjacencies, Uuid networkUuid, Boolean isRouterInterface,
3133             TypedWriteTransaction<Configuration> wrtConfigTxn) {
3134         if (vpnIdList.isEmpty() || infName == null) {
3135             LOG.error("vpnid is empty or interface({}) is null", infName);
3136             return;
3137         }
3138         if (wrtConfigTxn == null) {
3139             ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
3140                 tx -> writeVpnInterfaceToDs(vpnIdList, infName, adjacencies, networkUuid, isRouterInterface, tx)), LOG,
3141                 "Error writing VPN interface");
3142             return;
3143         }
3144         List<VpnInstanceNames> vpnIdListStruct = new ArrayList<>();
3145         for (Uuid vpnId: vpnIdList) {
3146             VpnInstanceNames vpnInstance = VpnHelper.getVpnInterfaceVpnInstanceNames(vpnId.getValue(),
3147                                    AssociatedSubnetType.V4AndV6Subnets);
3148             vpnIdListStruct.add(vpnInstance);
3149         }
3150
3151         InstanceIdentifier<VpnInterface> vpnIfIdentifier = NeutronvpnUtils.buildVpnInterfaceIdentifier(infName);
3152         VpnInterfaceBuilder vpnb = new VpnInterfaceBuilder().withKey(new VpnInterfaceKey(infName))
3153                 .setName(infName)
3154                 .setVpnInstanceNames(vpnIdListStruct)
3155                 .setRouterInterface(isRouterInterface);
3156         LOG.info("Network Id is {}", networkUuid);
3157         if (networkUuid != null) {
3158             Network portNetwork = neutronvpnUtils.getNeutronNetwork(networkUuid);
3159             ProviderTypes providerType = NeutronvpnUtils.getProviderNetworkType(portNetwork);
3160             NetworkAttributes.NetworkType networkType = providerType != null
3161                     ? NetworkAttributes.NetworkType.valueOf(providerType.getName()) : null;
3162             String segmentationId = NeutronvpnUtils.getSegmentationIdFromNeutronNetwork(portNetwork);
3163             vpnb.setNetworkId(networkUuid).setNetworkType(networkType)
3164                 .setSegmentationId(segmentationId != null ? Long.parseLong(segmentationId) : 0L);
3165         }
3166
3167         if (adjacencies != null) {
3168             vpnb.addAugmentation(Adjacencies.class, adjacencies);
3169         }
3170         VpnInterface vpnIf = vpnb.build();
3171         try {
3172             LOG.info("Creating vpn interface {}", vpnIf);
3173             wrtConfigTxn.put(vpnIfIdentifier, vpnIf);
3174         } catch (Exception ex) {
3175             LOG.error("Creation of vpninterface {} failed", infName, ex);
3176         }
3177     }
3178
3179     private void updateVpnInterfaceWithAdjacencies(Uuid vpnId, String infName, Adjacencies adjacencies,
3180                                                    TypedWriteTransaction<Configuration> wrtConfigTxn) {
3181         if (vpnId == null || infName == null) {
3182             LOG.error("vpn id or interface is null");
3183             return;
3184         }
3185         if (wrtConfigTxn == null) {
3186             LOG.error("updateVpnInterfaceWithAdjancies called with wrtConfigTxn as null");
3187             ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
3188                 updateVpnInterfaceWithAdjacencies(vpnId, infName, adjacencies, tx);
3189             }), LOG, "Error updating VPN interface with adjacencies");
3190             return;
3191         }
3192
3193         InstanceIdentifier<VpnInterface> vpnIfIdentifier = NeutronvpnUtils.buildVpnInterfaceIdentifier(infName);
3194         boolean isLockAcquired = false;
3195         try {
3196             isLockAcquired = interfaceLock.tryLock(infName, LOCK_WAIT_TIME, TimeUnit.SECONDS);
3197             Optional<VpnInterface> optionalVpnInterface = SingleTransactionDataBroker
3198                     .syncReadOptional(dataBroker, LogicalDatastoreType
3199                     .CONFIGURATION, vpnIfIdentifier);
3200             if (optionalVpnInterface.isPresent()) {
3201                 VpnInterfaceBuilder vpnIfBuilder = new VpnInterfaceBuilder(optionalVpnInterface.get());
3202                 LOG.debug("Updating vpn interface {} with new adjacencies", infName);
3203
3204                 if (adjacencies == null) {
3205                     if (isLockAcquired) {
3206                         interfaceLock.unlock(infName);
3207                     }
3208                     return;
3209                 }
3210                 vpnIfBuilder.addAugmentation(Adjacencies.class, adjacencies);
3211                 if (optionalVpnInterface.get().getVpnInstanceNames() != null) {
3212                     List<VpnInstanceNames> listVpnInstances = new ArrayList<>(
3213                         optionalVpnInterface.get().getVpnInstanceNames());
3214                     if (listVpnInstances.isEmpty() || !VpnHelper
3215                         .doesVpnInterfaceBelongToVpnInstance(vpnId.getValue(), listVpnInstances)) {
3216                         VpnInstanceNames vpnInstance = VpnHelper
3217                              .getVpnInterfaceVpnInstanceNames(vpnId.getValue(), AssociatedSubnetType.V4AndV6Subnets);
3218                         listVpnInstances.add(vpnInstance);
3219                         vpnIfBuilder.setVpnInstanceNames(listVpnInstances);
3220                     }
3221                 } else {
3222                     VpnInstanceNames vpnInstance = VpnHelper
3223                          .getVpnInterfaceVpnInstanceNames(vpnId.getValue(), AssociatedSubnetType.V4AndV6Subnets);
3224                     List<VpnInstanceNames> listVpnInstances = new ArrayList<>();
3225                     listVpnInstances.add(vpnInstance);
3226                     vpnIfBuilder.setVpnInstanceNames(listVpnInstances);
3227                 }
3228                 LOG.info("Updating vpn interface {} with new adjacencies", infName);
3229                 wrtConfigTxn.put(vpnIfIdentifier, vpnIfBuilder.build());
3230             }
3231         } catch (IllegalStateException | ReadFailedException ex) {
3232             LOG.error("Update of vpninterface {} failed", infName, ex);
3233         } finally {
3234             if (isLockAcquired) {
3235                 interfaceLock.unlock(infName);
3236             }
3237         }
3238     }
3239
3240     private String getshowVpnConfigCLIHelp() {
3241         StringBuilder help = new StringBuilder("Usage:");
3242         help.append("display vpn-config [-vid/--vpnid <id>]");
3243         return help.toString();
3244     }
3245
3246     protected void dissociatefixedIPFromFloatingIP(String fixedNeutronPortName) {
3247         floatingIpMapListener.dissociatefixedIPFromFloatingIP(fixedNeutronPortName);
3248     }
3249
3250     @Override
3251     public ListenableFuture<RpcResult<CreateEVPNOutput>> createEVPN(CreateEVPNInput input) {
3252         return neutronEvpnManager.createEVPN(input);
3253     }
3254
3255     @Override
3256     public ListenableFuture<RpcResult<GetEVPNOutput>> getEVPN(GetEVPNInput input) {
3257         return neutronEvpnManager.getEVPN(input);
3258     }
3259
3260     @Override
3261     public ListenableFuture<RpcResult<DeleteEVPNOutput>> deleteEVPN(DeleteEVPNInput input) {
3262         return neutronEvpnManager.deleteEVPN(input);
3263     }
3264
3265     private boolean addExternalNetworkToVpn(Network extNet, Uuid vpnId) {
3266         Uuid extNetId = extNet.getUuid();
3267         InstanceIdentifier<Networks> extNetIdentifier = InstanceIdentifier.builder(ExternalNetworks.class)
3268                 .child(Networks.class, new NetworksKey(extNetId)).build();
3269
3270         try {
3271             Optional<Networks> optionalExtNets =
3272                     SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
3273                                                                  extNetIdentifier);
3274             if (!optionalExtNets.isPresent()) {
3275                 LOG.error("addExternalNetworkToVpn: Provider Network {} is not present in ConfigDS",
3276                           extNetId.getValue());
3277                 return false;
3278             }
3279             NetworksBuilder builder = new NetworksBuilder(optionalExtNets.get());
3280             builder.setVpnid(vpnId);
3281             Networks networks = builder.build();
3282             // Add Networks object to the ExternalNetworks list
3283             LOG.trace("addExternalNetworkToVpn: Set VPN Id {} for Provider Network {}", vpnId.getValue(),
3284                       extNetId.getValue());
3285             SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, extNetIdentifier,
3286                                                   networks);
3287             return true;
3288         } catch (TransactionCommitFailedException | ReadFailedException ex) {
3289             LOG.error("addExternalNetworkToVpn: Failed to set VPN Id {} to Provider Network {}: ", vpnId.getValue(),
3290                       extNetId.getValue(), ex);
3291         }
3292         return false;
3293     }
3294
3295     private boolean removeExternalNetworkFromVpn(Network extNet) {
3296         Uuid extNetId = extNet.getUuid();
3297         InstanceIdentifier<Networks> extNetsId = InstanceIdentifier.builder(ExternalNetworks.class)
3298             .child(Networks.class, new NetworksKey(extNetId)).build();
3299         try {
3300             Optional<Networks> optionalNets =
3301                     SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
3302                             extNetsId);
3303             NetworksBuilder builder = null;
3304             if (optionalNets.isPresent()) {
3305                 builder = new NetworksBuilder(optionalNets.get());
3306             } else {
3307                 LOG.error("removeExternalNetworkFromVpn: Provider Network {} is not present in the ConfigDS",
3308                         extNetId.getValue());
3309                 return false;
3310             }
3311             builder.setVpnid(null);
3312             Networks networks = builder.build();
3313             LOG.info("removeExternalNetworkFromVpn: Withdraw VPN Id from Provider Network {} node",
3314                     extNetId.getValue());
3315             SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, extNetsId, networks);
3316             return true;
3317         } catch (TransactionCommitFailedException | ReadFailedException ex) {
3318             LOG.error("removeExternalNetworkFromVpn: Failed to withdraw VPN Id from Provider Network node {}: ",
3319                     extNetId.getValue(), ex);
3320         }
3321         return false;
3322     }
3323
3324     private Optional<String> getExistingOperationalVpn(String primaryRd) {
3325         Optional<String> existingVpnName = Optional.of(primaryRd);
3326         Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataOptional;
3327         try {
3328             vpnInstanceOpDataOptional = SingleTransactionDataBroker
3329                 .syncReadOptional(dataBroker, LogicalDatastoreType.OPERATIONAL,
3330                     neutronvpnUtils.getVpnOpDataIdentifier(primaryRd));
3331         } catch (ReadFailedException e) {
3332             LOG.error("getExistingOperationalVpn: Exception while checking operational status of vpn with rd {}",
3333                     primaryRd, e);
3334             /*Read failed. We don't know if a VPN exists or not.
3335             * Return primaryRd to halt caller execution, to be safe.*/
3336             return existingVpnName;
3337         }
3338         if (vpnInstanceOpDataOptional.isPresent()) {
3339             existingVpnName = Optional.of(vpnInstanceOpDataOptional.get().getVpnInstanceName());
3340         } else {
3341             existingVpnName = Optional.absent();
3342         }
3343         return existingVpnName;
3344     }
3345
3346     private static String formatAndLog(Consumer<String> logger, String template, Object arg) {
3347         return logAndReturnMessage(logger, MessageFormatter.format(template, arg));
3348     }
3349
3350     private static String formatAndLog(Consumer<String> logger, String template, Object arg1, Object arg2) {
3351         return logAndReturnMessage(logger, MessageFormatter.format(template, arg1, arg2));
3352     }
3353
3354     private static String formatAndLog(Consumer<String> logger, String template, Object... args) {
3355         return logAndReturnMessage(logger, MessageFormatter.arrayFormat(template, args));
3356     }
3357
3358     private static String logAndReturnMessage(Consumer<String> logger, FormattingTuple tuple) {
3359         String message = tuple.getMessage();
3360         logger.accept(message);
3361         return message;
3362     }
3363
3364     protected void addV6PrivateSubnetToExtNetwork(@Nonnull Uuid routerId, @Nonnull Uuid internetVpnId,
3365                                                   @Nonnull Subnetmap subnetMap) {
3366         updateVpnInternetForSubnet(subnetMap, internetVpnId, true);
3367         neutronvpnUtils.updateVpnInstanceWithFallback(routerId, internetVpnId, true);
3368         if (neutronvpnUtils.shouldVpnHandleIpVersionChoiceChange(IpVersionChoice.IPV6, routerId, true)) {
3369             neutronvpnUtils.updateVpnInstanceWithIpFamily(internetVpnId.getValue(), IpVersionChoice.IPV6, true);
3370             LOG.info("addV6PrivateSubnetToExtNetwork: Advertise IPv6 Private Subnet {} to Internet VPN {}",
3371                     subnetMap.getId().getValue(), internetVpnId.getValue());
3372         }
3373     }
3374
3375     protected void removeV6PrivateSubnetToExtNetwork(@Nonnull Uuid routerId, @Nonnull Uuid internetVpnId,
3376                                                      @Nonnull Subnetmap subnetMap) {
3377         updateVpnInternetForSubnet(subnetMap, internetVpnId, false);
3378         neutronvpnUtils.updateVpnInstanceWithFallback(routerId, internetVpnId, false);
3379     }
3380
3381     protected void programV6InternetFallbackFlow(Uuid routerId, Uuid internetVpnId, int addOrRemove) {
3382         if (neutronvpnUtils.isV6SubnetPartOfRouter(routerId)) {
3383             LOG.debug("processV6InternetFlowsForRtr: Successfully {} V6 internet vpn {} default fallback rule "
3384                             + "for the router {}", addOrRemove == NwConstants.ADD_FLOW ? "added" : "removed",
3385                     internetVpnId.getValue(), routerId.getValue());
3386             neutronvpnUtils.updateVpnInstanceWithFallback(routerId, internetVpnId, addOrRemove == NwConstants.ADD_FLOW
3387                     ? true : false);
3388         }
3389     }
3390
3391     private static ReentrantLock lockForUuid(Uuid uuid) {
3392         // FIXME: prove that this locks only on Uuids and not some other entity or create a separate lock domain
3393         return JvmGlobalLocks.getLockForString(uuid.getValue());
3394     }
3395 }