2 * Copyright (c) 2015 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
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
8 package org.opendaylight.vpnservice;
10 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rev150331.L3tunnel;
12 import java.math.BigInteger;
13 import java.util.Collection;
14 import java.util.Collections;
15 import java.util.HashSet;
16 import java.util.List;
17 import java.util.ArrayList;
19 import java.util.concurrent.ConcurrentHashMap;
20 import java.util.concurrent.ExecutionException;
21 import java.util.concurrent.Future;
22 import com.google.common.base.Optional;
23 import com.google.common.collect.ImmutableList;
24 import com.google.common.util.concurrent.Futures;
25 import com.google.common.util.concurrent.FutureCallback;
26 import org.opendaylight.bgpmanager.api.IBgpManager;
27 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
28 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
29 import org.opendaylight.fibmanager.api.IFibManager;
30 import org.opendaylight.vpnservice.interfacemgr.interfaces.IInterfaceManager;
31 import org.opendaylight.vpnservice.mdsalutil.FlowEntity;
32 import org.opendaylight.vpnservice.mdsalutil.InstructionInfo;
33 import org.opendaylight.vpnservice.mdsalutil.InstructionType;
34 import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
35 import org.opendaylight.vpnservice.mdsalutil.MatchFieldType;
36 import org.opendaylight.vpnservice.mdsalutil.MatchInfo;
37 import org.opendaylight.vpnservice.mdsalutil.MetaDataUtil;
38 import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager;
39 import org.opendaylight.yangtools.concepts.ListenerRegistration;
40 import org.opendaylight.yangtools.yang.binding.DataObject;
41 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
42 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
43 import org.opendaylight.yangtools.yang.common.RpcResult;
44 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
45 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
46 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
47 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
48 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
49 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
50 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.AdjacencyList;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.adjacency.list.Adjacency;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.adjacency.list.AdjacencyBuilder;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.VpnInstance1;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.GetUniqueIdInput;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.GetUniqueIdInputBuilder;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.GetUniqueIdOutput;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.IdManagerService;
59 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInterfaces;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.Adjacencies;
61 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnAfConfig;
62 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInstances;
63 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstance;
64 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstanceKey;
65 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
66 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceKey;
67 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceBuilder;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.AdjacenciesBuilder;
69 import org.slf4j.Logger;
70 import org.slf4j.LoggerFactory;
72 public class VpnInterfaceManager extends AbstractDataChangeListener<VpnInterface> implements AutoCloseable {
73 private static final Logger LOG = LoggerFactory.getLogger(VpnInterfaceManager.class);
74 private ListenerRegistration<DataChangeListener> listenerRegistration;
75 private final DataBroker broker;
76 private final IBgpManager bgpManager;
77 private IFibManager fibManager;
78 private IMdsalApiManager mdsalManager;
79 private IInterfaceManager interfaceManager;
80 private IdManagerService idManager;
81 private Map<Long, Collection<BigInteger>> vpnToDpnsDb;
82 private Map<BigInteger, Collection<String>> dpnToInterfaceDb;
84 private static final FutureCallback<Void> DEFAULT_CALLBACK =
85 new FutureCallback<Void>() {
86 public void onSuccess(Void result) {
87 LOG.debug("Success in Datastore operation");
90 public void onFailure(Throwable error) {
91 LOG.error("Error in Datastore operation", error);
96 * Responsible for listening to data change related to VPN Interface
97 * Bind VPN Service on the interface and informs the BGP service
99 * @param db - dataBroker service reference
101 public VpnInterfaceManager(final DataBroker db, final IBgpManager bgpManager) {
102 super(VpnInterface.class);
104 this.bgpManager = bgpManager;
105 vpnToDpnsDb = new ConcurrentHashMap<>();
106 dpnToInterfaceDb = new ConcurrentHashMap<>();
107 registerListener(db);
110 public void setMdsalManager(IMdsalApiManager mdsalManager) {
111 this.mdsalManager = mdsalManager;
114 public void setInterfaceManager(IInterfaceManager interfaceManager) {
115 this.interfaceManager = interfaceManager;
118 public void setFibManager(IFibManager fibManager) {
119 this.fibManager = fibManager;
122 public void setIdManager(IdManagerService idManager) {
123 this.idManager = idManager;
127 public void close() throws Exception {
128 if (listenerRegistration != null) {
130 listenerRegistration.close();
131 } catch (final Exception e) {
132 LOG.error("Error when cleaning up DataChangeListener.", e);
134 listenerRegistration = null;
136 LOG.info("VPN Interface Manager Closed");
139 private void registerListener(final DataBroker db) {
141 listenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
142 getWildCardPath(), VpnInterfaceManager.this, DataChangeScope.SUBTREE);
143 } catch (final Exception e) {
144 LOG.error("VPN Service DataChange listener registration fail!", e);
145 throw new IllegalStateException("VPN Service registration Listener failed.", e);
150 protected void add(final InstanceIdentifier<VpnInterface> identifier,
151 final VpnInterface vpnInterface) {
152 LOG.trace("key: {} , value: {}", identifier, vpnInterface );
153 addInterface(identifier, vpnInterface);
156 private void addInterface(final InstanceIdentifier<VpnInterface> identifier,
157 final VpnInterface vpnInterface) {
158 final VpnInterfaceKey key = identifier.firstKeyOf(VpnInterface.class, VpnInterfaceKey.class);
159 String interfaceName = key.getName();
160 InstanceIdentifierBuilder<Interface> idBuilder =
161 InstanceIdentifier.builder(Interfaces.class).child(Interface.class, new InterfaceKey(interfaceName));
162 InstanceIdentifier<Interface> id = idBuilder.build();
163 Optional<Interface> port = read(LogicalDatastoreType.CONFIGURATION, id);
164 if (port.isPresent()) {
165 Interface interf = port.get();
166 bindServiceOnInterface(interf, vpnInterface.getVpnInstanceName());
167 updateNextHops(identifier, vpnInterface);
171 private void updateNextHops(final InstanceIdentifier<VpnInterface> identifier, VpnInterface intf) {
173 InstanceIdentifier<Adjacencies> path = identifier.augmentation(Adjacencies.class);
174 Optional<Adjacencies> adjacencies = read(LogicalDatastoreType.CONFIGURATION, path);
175 String intfName = intf.getName();
177 if (adjacencies.isPresent()) {
178 List<Adjacency> nextHops = adjacencies.get().getAdjacency();
179 List<Adjacency> value = new ArrayList<>();
181 //Get the rd of the vpn instance
182 String rd = getRouteDistinguisher(intf.getVpnInstanceName());
184 BigInteger dpnId = interfaceManager.getDpnForInterface(intfName);
185 String nextHopIp = interfaceManager.getEndpointIpForDpn(dpnId);
188 LOG.trace("NextHops are {}", nextHops);
189 for (Adjacency nextHop : nextHops) {
190 String key = nextHop.getIpAddress();
191 long label = getUniqueId(key);
192 value.add(new AdjacencyBuilder(nextHop).setLabel(label).build());
195 Adjacencies aug = VpnUtil.getVpnInterfaceAugmentation(value);
196 VpnInterface opInterface = VpnUtil.getVpnInterface(intfName, intf.getVpnInstanceName(), aug);
197 InstanceIdentifier<VpnInterface> interfaceId = VpnUtil.getVpnInterfaceIdentifier(intfName);
198 syncWrite(LogicalDatastoreType.OPERATIONAL, interfaceId, opInterface, DEFAULT_CALLBACK);
199 for (Adjacency nextHop : nextHops) {
200 String key = nextHop.getIpAddress();
201 long label = getUniqueId(key);
202 updatePrefixToBGP(rd, nextHop, nextHopIp, label);
207 private Integer getUniqueId(String idKey) {
208 GetUniqueIdInput getIdInput = new GetUniqueIdInputBuilder()
209 .setPoolName(VpnConstants.VPN_IDPOOL_NAME)
210 .setIdKey(idKey).build();
213 Future<RpcResult<GetUniqueIdOutput>> result = idManager.getUniqueId(getIdInput);
214 RpcResult<GetUniqueIdOutput> rpcResult = result.get();
215 if(rpcResult.isSuccessful()) {
216 return rpcResult.getResult().getIdValue().intValue();
218 LOG.warn("RPC Call to Get Unique Id returned with Errors {}", rpcResult.getErrors());
220 } catch (NullPointerException | InterruptedException | ExecutionException e) {
221 LOG.warn("Exception when getting Unique Id",e);
226 private long getVpnId(String vpnName) {
227 //TODO: This should be a Util function
228 InstanceIdentifier<VpnInstance1> id = InstanceIdentifier.builder(VpnInstances.class)
229 .child(VpnInstance.class, new VpnInstanceKey(vpnName)).augmentation(VpnInstance1.class).build();
230 Optional<VpnInstance1> vpnInstance = read(LogicalDatastoreType.OPERATIONAL, id);
232 long vpnId = VpnConstants.INVALID_ID;
233 if(vpnInstance.isPresent()) {
234 vpnId = vpnInstance.get().getVpnId();
239 private String getRouteDistinguisher(String vpnName) {
240 InstanceIdentifier<VpnInstance> id = InstanceIdentifier.builder(VpnInstances.class)
241 .child(VpnInstance.class, new VpnInstanceKey(vpnName)).build();
242 Optional<VpnInstance> vpnInstance = read(LogicalDatastoreType.CONFIGURATION, id);
244 if(vpnInstance.isPresent()) {
245 VpnInstance instance = vpnInstance.get();
246 VpnAfConfig config = instance.getIpv4Family();
247 rd = config.getRouteDistinguisher();
252 private synchronized void updateMappingDbs(long vpnId, BigInteger dpnId, String intfName, String rd) {
253 Collection<BigInteger> dpnIds = vpnToDpnsDb.get(vpnId);
255 dpnIds = new HashSet<>();
257 if(dpnIds.add(dpnId)) {
258 vpnToDpnsDb.put(vpnId, dpnIds);
259 fibManager.populateFibOnNewDpn(dpnId, vpnId, rd);
262 Collection<String> intfNames = dpnToInterfaceDb.get(dpnId);
263 if(intfNames == null) {
264 intfNames = new ArrayList<>();
266 intfNames.add(intfName);
267 dpnToInterfaceDb.put(dpnId, intfNames);
270 private synchronized void remoteFromMappingDbs(long vpnId, BigInteger dpnId, String inftName, String rd) {
271 Collection<String> intfNames = dpnToInterfaceDb.get(dpnId);
272 if(intfNames == null) {
275 intfNames.remove(inftName);
276 dpnToInterfaceDb.put(dpnId, intfNames);
277 //TODO: Delay 'DPN' removal so that other services can cleanup the entries for this dpn
278 if(intfNames.isEmpty()) {
279 Collection<BigInteger> dpnIds = vpnToDpnsDb.get(vpnId);
283 dpnIds.remove(dpnId);
284 vpnToDpnsDb.put(vpnId, dpnIds);
285 fibManager.cleanUpDpnForVpn(dpnId, vpnId, rd);
289 private void bindServiceOnInterface(Interface intf, String vpnName) {
290 LOG.trace("Bind service on interface {} for VPN: {}", intf, vpnName);
292 long vpnId = getVpnId(vpnName);
293 BigInteger dpId = interfaceManager.getDpnForInterface(intf.getName());
294 if(dpId.equals(BigInteger.ZERO)) {
295 LOG.warn("DPN for interface {} not found. Bind service on this interface aborted.", intf.getName());
298 String rd = getRouteDistinguisher(vpnName);
299 updateMappingDbs(vpnId, dpId, intf.getName(), rd);
302 long portNo = interfaceManager.getPortForInterface(intf.getName());
303 String flowRef = getVpnInterfaceFlowRef(dpId, VpnConstants.LPORT_INGRESS_TABLE, vpnId, portNo);
305 String flowName = intf.getName();
306 BigInteger COOKIE_VM_INGRESS_TABLE = new BigInteger("8000001", 16);
308 int priority = VpnConstants.DEFAULT_FLOW_PRIORITY;
309 short gotoTableId = VpnConstants.FIB_TABLE;
310 if(intf.getType().equals(L3tunnel.class)){
311 gotoTableId = VpnConstants.LFIB_TABLE;
314 List<InstructionInfo> mkInstructions = new ArrayList<InstructionInfo>();
315 mkInstructions.add(new InstructionInfo(InstructionType.write_metadata, new BigInteger[] {
316 BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID }));
318 mkInstructions.add(new InstructionInfo(InstructionType.goto_table, new long[] { gotoTableId }));
320 List<MatchInfo> matches = new ArrayList<MatchInfo>();
321 matches.add(new MatchInfo(MatchFieldType.in_port, new BigInteger[] {
322 dpId, BigInteger.valueOf(portNo) }));
324 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, VpnConstants.LPORT_INGRESS_TABLE, flowRef,
325 priority, flowName, 0, 0, COOKIE_VM_INGRESS_TABLE, matches, mkInstructions);
327 mdsalManager.installFlow(flowEntity);
330 private String getVpnInterfaceFlowRef(BigInteger dpId, short tableId,
331 long vpnId, long portNo) {
332 return new StringBuilder().append(dpId).append(tableId).append(vpnId).append(portNo).toString();
335 private void updatePrefixToBGP(String rd, Adjacency nextHop, String nextHopIp, long label) {
337 bgpManager.addPrefix(rd, nextHop.getIpAddress(), nextHopIp, (int)label);
338 } catch(Exception e) {
339 LOG.error("Add prefix failed", e);
343 private <T extends DataObject> Optional<T> read(LogicalDatastoreType datastoreType,
344 InstanceIdentifier<T> path) {
346 ReadOnlyTransaction tx = broker.newReadOnlyTransaction();
348 Optional<T> result = Optional.absent();
350 result = tx.read(datastoreType, path).get();
351 } catch (Exception e) {
352 throw new RuntimeException(e);
358 private InstanceIdentifier<VpnInterface> getWildCardPath() {
359 return InstanceIdentifier.create(VpnInterfaces.class).child(VpnInterface.class);
363 protected void remove( InstanceIdentifier<VpnInterface> identifier, VpnInterface vpnInterface) {
364 LOG.trace("Remove event - key: {}, value: {}" ,identifier, vpnInterface );
365 final VpnInterfaceKey key = identifier.firstKeyOf(VpnInterface.class, VpnInterfaceKey.class);
366 String interfaceName = key.getName();
367 InstanceIdentifierBuilder<Interface> idBuilder =
368 InstanceIdentifier.builder(Interfaces.class).child(Interface.class, new InterfaceKey(interfaceName));
369 InstanceIdentifier<Interface> id = idBuilder.build();
370 Optional<Interface> port = read(LogicalDatastoreType.CONFIGURATION, id);
371 if (port.isPresent()) {
372 Interface interf = port.get();
373 removeNextHops(identifier, vpnInterface);
374 unbindServiceOnInterface(interf, vpnInterface.getVpnInstanceName());
375 //InstanceIdentifier<VpnInterface> interfaceId = VpnUtil.getVpnInterfaceIdentifier(interfaceName);
376 delete(LogicalDatastoreType.OPERATIONAL, identifier);
378 LOG.warn("No nexthops were available to handle remove event {}", interfaceName);
382 private void removeNextHops(final InstanceIdentifier<VpnInterface> identifier, VpnInterface intf) {
384 InstanceIdentifier<Adjacencies> path = identifier.augmentation(Adjacencies.class);
385 Optional<Adjacencies> adjacencies = read(LogicalDatastoreType.OPERATIONAL, path);
386 String intfName = intf.getName();
387 String rd = getRouteDistinguisher(intf.getVpnInstanceName());
388 if (adjacencies.isPresent()) {
389 List<Adjacency> nextHops = adjacencies.get().getAdjacency();
391 if (!nextHops.isEmpty()) {
392 LOG.trace("NextHops are " + nextHops);
393 for (Adjacency nextHop : nextHops) {
394 removePrefixFromBGP(rd, nextHop);
398 // InstanceIdentifier<VpnInterface> interfaceId = VpnUtil.getVpnInterfaceIdentifier(intfName);
399 // delete(LogicalDatastoreType.OPERATIONAL, interfaceId);
402 private <T extends DataObject> void delete(LogicalDatastoreType datastoreType, InstanceIdentifier<T> path) {
403 WriteTransaction tx = broker.newWriteOnlyTransaction();
404 tx.delete(datastoreType, path);
405 Futures.addCallback(tx.submit(), DEFAULT_CALLBACK);
408 private void unbindServiceOnInterface(Interface intf, String vpnName) {
409 LOG.trace("Unbind service on interface {} for VPN: {}", intf, vpnName);
411 long vpnId = getVpnId(vpnName);
412 BigInteger dpId = interfaceManager.getDpnForInterface(intf.getName());
413 if(dpId.equals(BigInteger.ZERO)) {
414 LOG.warn("DPN for interface {} not found. Unbind service on this interface aborted.", intf.getName());
417 String rd = getRouteDistinguisher(vpnName);
418 remoteFromMappingDbs(vpnId, dpId, intf.getName(), rd);
421 long portNo = interfaceManager.getPortForInterface(intf.getName());
422 String flowRef = getVpnInterfaceFlowRef(dpId, VpnConstants.LPORT_INGRESS_TABLE, vpnId, portNo);
424 String flowName = intf.getName();
426 int priority = VpnConstants.DEFAULT_FLOW_PRIORITY;
428 List<MatchInfo> matches = new ArrayList<MatchInfo>();
429 matches.add(new MatchInfo(MatchFieldType.in_port, new BigInteger[] {
430 dpId, BigInteger.valueOf(portNo) }));
432 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, VpnConstants.LPORT_INGRESS_TABLE, flowRef,
433 priority, flowName, 0, 0, null, matches, null);
435 mdsalManager.removeFlow(flowEntity);
438 private void removePrefixFromBGP(String rd, Adjacency nextHop) {
439 //public void deletePrefix(String rd, String prefix) throws Exception;
441 bgpManager.deletePrefix(rd, nextHop.getIpAddress());
442 } catch(Exception e) {
443 LOG.error("Delete prefix failed", e);
448 protected void update(InstanceIdentifier<VpnInterface> identifier,
449 VpnInterface original, VpnInterface update) {
450 // TODO Auto-generated method stub
454 private <T extends DataObject> void asyncWrite(LogicalDatastoreType datastoreType,
455 InstanceIdentifier<T> path, T data, FutureCallback<Void> callback) {
456 WriteTransaction tx = broker.newWriteOnlyTransaction();
457 tx.put(datastoreType, path, data, true);
458 Futures.addCallback(tx.submit(), callback);
461 private <T extends DataObject> void syncWrite(LogicalDatastoreType datastoreType,
462 InstanceIdentifier<T> path, T data, FutureCallback<Void> callback) {
463 WriteTransaction tx = broker.newWriteOnlyTransaction();
464 tx.put(datastoreType, path, data, true);
468 synchronized Collection<BigInteger> getDpnsForVpn(long vpnId) {
469 Collection<BigInteger> dpnIds = vpnToDpnsDb.get(vpnId);
471 return ImmutableList.copyOf(dpnIds);
473 return Collections.emptyList();