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 java.math.BigInteger;
11 import java.util.Collection;
12 import java.util.Collections;
13 import java.util.HashSet;
14 import java.util.List;
15 import java.util.ArrayList;
17 import java.util.concurrent.ConcurrentHashMap;
18 import java.util.concurrent.ExecutionException;
19 import java.util.concurrent.Future;
21 import com.google.common.base.Optional;
22 import com.google.common.collect.ImmutableList;
23 import com.google.common.util.concurrent.Futures;
24 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.vpnservice.interfacemgr.interfaces.IInterfaceManager;
30 import org.opendaylight.vpnservice.mdsalutil.FlowEntity;
31 import org.opendaylight.vpnservice.mdsalutil.InstructionInfo;
32 import org.opendaylight.vpnservice.mdsalutil.InstructionType;
33 import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
34 import org.opendaylight.vpnservice.mdsalutil.MatchFieldType;
35 import org.opendaylight.vpnservice.mdsalutil.MatchInfo;
36 import org.opendaylight.vpnservice.mdsalutil.MetaDataUtil;
37 import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager;
38 import org.opendaylight.yangtools.concepts.ListenerRegistration;
39 import org.opendaylight.yangtools.yang.binding.DataObject;
40 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
41 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
42 import org.opendaylight.yangtools.yang.common.RpcResult;
43 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
44 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
45 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
46 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
47 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
48 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
49 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.AdjacencyList;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.adjacency.list.Adjacency;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.adjacency.list.AdjacencyBuilder;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.VpnInstance1;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.GetUniqueIdInput;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.GetUniqueIdInputBuilder;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.GetUniqueIdOutput;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.IdManagerService;
58 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInterfaces;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.Adjacencies;
60 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnAfConfig;
61 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInstances;
62 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstance;
63 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstanceKey;
64 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
65 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceKey;
66 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceBuilder;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.AdjacenciesBuilder;
68 import org.slf4j.Logger;
69 import org.slf4j.LoggerFactory;
71 public class VpnInterfaceManager extends AbstractDataChangeListener<VpnInterface> implements AutoCloseable {
72 private static final Logger LOG = LoggerFactory.getLogger(VpnInterfaceManager.class);
73 private ListenerRegistration<DataChangeListener> listenerRegistration;
74 private final DataBroker broker;
75 private final IBgpManager bgpManager;
76 private IMdsalApiManager mdsalManager;
77 private IInterfaceManager interfaceManager;
78 private IdManagerService idManager;
79 private Map<Long, Collection<Long>> vpnToDpnsDb;
80 private Map<Long, Collection<String>> dpnToInterfaceDb;
82 private static final FutureCallback<Void> DEFAULT_CALLBACK =
83 new FutureCallback<Void>() {
84 public void onSuccess(Void result) {
85 LOG.debug("Success in Datastore operation");
88 public void onFailure(Throwable error) {
89 LOG.error("Error in Datastore operation", error);
94 * Responsible for listening to data change related to VPN Interface
95 * Bind VPN Service on the interface and informs the BGP service
97 * @param db - dataBroker service reference
99 public VpnInterfaceManager(final DataBroker db, final IBgpManager bgpManager) {
100 super(VpnInterface.class);
102 this.bgpManager = bgpManager;
103 vpnToDpnsDb = new ConcurrentHashMap<>();
104 dpnToInterfaceDb = new ConcurrentHashMap<>();
105 registerListener(db);
108 public void setMdsalManager(IMdsalApiManager mdsalManager) {
109 this.mdsalManager = mdsalManager;
112 public void setInterfaceManager(IInterfaceManager interfaceManager) {
113 this.interfaceManager = interfaceManager;
116 public void setIdManager(IdManagerService idManager) {
117 this.idManager = idManager;
121 public void close() throws Exception {
122 if (listenerRegistration != null) {
124 listenerRegistration.close();
125 } catch (final Exception e) {
126 LOG.error("Error when cleaning up DataChangeListener.", e);
128 listenerRegistration = null;
130 LOG.info("VPN Interface Manager Closed");
133 private void registerListener(final DataBroker db) {
135 listenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
136 getWildCardPath(), VpnInterfaceManager.this, DataChangeScope.SUBTREE);
137 } catch (final Exception e) {
138 LOG.error("VPN Service DataChange listener registration fail!", e);
139 throw new IllegalStateException("VPN Service registration Listener failed.", e);
144 protected void add(final InstanceIdentifier<VpnInterface> identifier,
145 final VpnInterface vpnInterface) {
146 LOG.trace("key: {} , value: {}", identifier, vpnInterface );
147 addInterface(identifier, vpnInterface);
150 private void addInterface(final InstanceIdentifier<VpnInterface> identifier,
151 final VpnInterface vpnInterface) {
152 final VpnInterfaceKey key = identifier.firstKeyOf(VpnInterface.class, VpnInterfaceKey.class);
153 String interfaceName = key.getName();
154 InstanceIdentifierBuilder<Interface> idBuilder =
155 InstanceIdentifier.builder(Interfaces.class).child(Interface.class, new InterfaceKey(interfaceName));
156 InstanceIdentifier<Interface> id = idBuilder.build();
157 Optional<Interface> port = read(LogicalDatastoreType.CONFIGURATION, id);
158 if (port.isPresent()) {
159 Interface interf = port.get();
160 bindServiceOnInterface(interf, getVpnId(vpnInterface.getVpnInstanceName()));
161 updateNextHops(identifier, vpnInterface);
165 private void updateNextHops(final InstanceIdentifier<VpnInterface> identifier, VpnInterface intf) {
167 InstanceIdentifier<Adjacencies> path = identifier.augmentation(Adjacencies.class);
168 Optional<Adjacencies> adjacencies = read(LogicalDatastoreType.CONFIGURATION, path);
169 String intfName = intf.getName();
171 if (adjacencies.isPresent()) {
172 List<Adjacency> nextHops = adjacencies.get().getAdjacency();
173 List<Adjacency> value = new ArrayList<>();
175 //Get the rd of the vpn instance
176 String rd = getRouteDistinguisher(intf.getVpnInstanceName());
178 long dpnId = interfaceManager.getDpnForInterface(intfName);
179 String nextHopIp = interfaceManager.getEndpointIpForDpn(dpnId);
181 if (!nextHops.isEmpty()) {
182 LOG.trace("NextHops are {}", nextHops);
183 for (Adjacency nextHop : nextHops) {
184 String key = nextHop.getIpAddress();
185 long label = getUniqueId(key);
187 updatePrefixToBGP(rd, nextHop, nextHopIp, label);
188 value.add(new AdjacencyBuilder(nextHop).setLabel(label).build());
191 Adjacencies aug = VpnUtil.getVpnInterfaceAugmentation(value);
192 VpnInterface opInterface = VpnUtil.getVpnInterface(intfName, intf.getVpnInstanceName(), aug);
193 InstanceIdentifier<VpnInterface> interfaceId = VpnUtil.getVpnInterfaceIdentifier(intfName);
194 asyncWrite(LogicalDatastoreType.OPERATIONAL, interfaceId, opInterface, DEFAULT_CALLBACK);
198 private Integer getUniqueId(String idKey) {
199 GetUniqueIdInput getIdInput = new GetUniqueIdInputBuilder()
200 .setPoolName(VpnConstants.VPN_IDPOOL_NAME)
201 .setIdKey(idKey).build();
204 Future<RpcResult<GetUniqueIdOutput>> result = idManager.getUniqueId(getIdInput);
205 RpcResult<GetUniqueIdOutput> rpcResult = result.get();
206 if(rpcResult.isSuccessful()) {
207 return rpcResult.getResult().getIdValue().intValue();
209 LOG.warn("RPC Call to Get Unique Id returned with Errors {}", rpcResult.getErrors());
211 } catch (NullPointerException | InterruptedException | ExecutionException e) {
212 LOG.warn("Exception when getting Unique Id",e);
217 private long getVpnId(String vpnName) {
218 //TODO: This should be a Util function
219 InstanceIdentifier<VpnInstance1> id = InstanceIdentifier.builder(VpnInstances.class)
220 .child(VpnInstance.class, new VpnInstanceKey(vpnName)).augmentation(VpnInstance1.class).build();
221 Optional<VpnInstance1> vpnInstance = read(LogicalDatastoreType.OPERATIONAL, id);
222 //TODO: Default vpnid should be a constant.
224 if(vpnInstance.isPresent()) {
225 vpnId = vpnInstance.get().getVpnId();
230 private String getRouteDistinguisher(String vpnName) {
231 InstanceIdentifier<VpnInstance> id = InstanceIdentifier.builder(VpnInstances.class)
232 .child(VpnInstance.class, new VpnInstanceKey(vpnName)).build();
233 Optional<VpnInstance> vpnInstance = read(LogicalDatastoreType.CONFIGURATION, id);
235 if(vpnInstance.isPresent()) {
236 VpnInstance instance = vpnInstance.get();
237 VpnAfConfig config = instance.getIpv4Family();
238 rd = config.getRouteDistinguisher();
243 private synchronized void updateMappingDbs(long vpnId, long dpnId, String intfName) {
244 Collection<Long> dpnIds = vpnToDpnsDb.get(vpnId);
246 dpnIds = new HashSet<>();
248 if(dpnIds.add(dpnId)) {
249 vpnToDpnsDb.put(vpnId, dpnIds);
250 //TODO: Send an Event that new DPN added...
253 Collection<String> intfNames = dpnToInterfaceDb.get(dpnId);
254 if(intfNames == null) {
255 intfNames = new ArrayList<>();
257 intfNames.add(intfName);
258 dpnToInterfaceDb.put(dpnId, intfNames);
261 private synchronized void remoteFromMappingDbs(long vpnId, long dpnId, String inftName) {
262 Collection<String> intfNames = dpnToInterfaceDb.get(dpnId);
263 if(intfNames == null) {
266 intfNames.remove(inftName);
267 dpnToInterfaceDb.put(dpnId, intfNames);
268 if(intfNames.isEmpty()) {
269 Collection<Long> dpnIds = vpnToDpnsDb.get(vpnId);
273 dpnIds.remove(dpnId);
274 vpnToDpnsDb.put(vpnId, dpnIds);
278 private void bindServiceOnInterface(Interface intf, long vpnId) {
279 LOG.trace("Bind service on interface {} for VPN: {}", intf, vpnId);
281 long dpId = interfaceManager.getDpnForInterface(intf.getName());
283 LOG.warn("DPN for interface {} not found. Bind service on this interface aborted.", intf.getName());
286 updateMappingDbs(vpnId, dpId, intf.getName());
289 long portNo = interfaceManager.getPortForInterface(intf.getName());
290 String flowRef = getVpnInterfaceFlowRef(dpId, VpnConstants.LPORT_INGRESS_TABLE, vpnId, portNo);
292 String flowName = intf.getName();
293 BigInteger COOKIE_VM_INGRESS_TABLE = new BigInteger("8000001", 16);
295 int priority = VpnConstants.DEFAULT_FLOW_PRIORITY;
296 short gotoTableId = VpnConstants.FIB_TABLE;
298 List<InstructionInfo> mkInstructions = new ArrayList<InstructionInfo>();
299 mkInstructions.add(new InstructionInfo(InstructionType.write_metadata, new BigInteger[] {
300 BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID }));
302 mkInstructions.add(new InstructionInfo(InstructionType.goto_table, new long[] { gotoTableId }));
304 List<MatchInfo> matches = new ArrayList<MatchInfo>();
305 matches.add(new MatchInfo(MatchFieldType.in_port, new long[] {
308 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, VpnConstants.LPORT_INGRESS_TABLE, flowRef,
309 priority, flowName, 0, 0, COOKIE_VM_INGRESS_TABLE, matches, mkInstructions);
311 mdsalManager.installFlow(flowEntity);
314 private String getVpnInterfaceFlowRef(long dpId, short tableId,
315 long vpnId, long portNo) {
316 return new StringBuilder().append(dpId).append(tableId).append(vpnId).append(portNo).toString();
319 private void updatePrefixToBGP(String rd, Adjacency nextHop, String nextHopIp, long label) {
321 bgpManager.addPrefix(rd, nextHop.getIpAddress(), nextHopIp, (int)label);
322 } catch(Exception e) {
323 LOG.error("Add prefix failed", e);
327 private <T extends DataObject> Optional<T> read(LogicalDatastoreType datastoreType,
328 InstanceIdentifier<T> path) {
330 ReadOnlyTransaction tx = broker.newReadOnlyTransaction();
332 Optional<T> result = Optional.absent();
334 result = tx.read(datastoreType, path).get();
335 } catch (Exception e) {
336 throw new RuntimeException(e);
342 private InstanceIdentifier<VpnInterface> getWildCardPath() {
343 return InstanceIdentifier.create(VpnInterfaces.class).child(VpnInterface.class);
347 protected void remove( InstanceIdentifier<VpnInterface> identifier, VpnInterface vpnInterface) {
348 LOG.trace("Remove event - key: {}, value: {}" ,identifier, vpnInterface );
349 final VpnInterfaceKey key = identifier.firstKeyOf(VpnInterface.class, VpnInterfaceKey.class);
350 String interfaceName = key.getName();
351 InstanceIdentifierBuilder<Interface> idBuilder =
352 InstanceIdentifier.builder(Interfaces.class).child(Interface.class, new InterfaceKey(interfaceName));
353 InstanceIdentifier<Interface> id = idBuilder.build();
354 Optional<Interface> port = read(LogicalDatastoreType.CONFIGURATION, id);
355 if (port.isPresent()) {
356 Interface interf = port.get();
357 removeNextHops(identifier, vpnInterface);
358 unbindServiceOnInterface(interf, getVpnId(vpnInterface.getVpnInstanceName()));
360 LOG.warn("No nexthops were available to handle remove event {}", interfaceName);
364 private void removeNextHops(final InstanceIdentifier<VpnInterface> identifier, VpnInterface intf) {
366 InstanceIdentifier<Adjacencies> path = identifier.augmentation(Adjacencies.class);
367 Optional<Adjacencies> adjacencies = read(LogicalDatastoreType.OPERATIONAL, path);
368 String intfName = intf.getName();
369 String rd = getRouteDistinguisher(intf.getVpnInstanceName());
370 if (adjacencies.isPresent()) {
371 List<Adjacency> nextHops = adjacencies.get().getAdjacency();
373 if (!nextHops.isEmpty()) {
374 LOG.trace("NextHops are " + nextHops);
375 for (Adjacency nextHop : nextHops) {
376 removePrefixFromBGP(rd, nextHop);
380 InstanceIdentifier<VpnInterface> interfaceId = VpnUtil.getVpnInterfaceIdentifier(intfName);
381 delete(LogicalDatastoreType.OPERATIONAL, interfaceId);
384 private <T extends DataObject> void delete(LogicalDatastoreType datastoreType, InstanceIdentifier<T> path) {
385 WriteTransaction tx = broker.newWriteOnlyTransaction();
386 tx.delete(datastoreType, path);
387 Futures.addCallback(tx.submit(), DEFAULT_CALLBACK);
390 private void unbindServiceOnInterface(Interface intf, long vpnId) {
391 LOG.trace("Unbind service on interface {} for VPN: {}", intf, vpnId);
393 long dpId = interfaceManager.getDpnForInterface(intf.getName());
395 LOG.warn("DPN for interface {} not found. Unbind service on this interface aborted.", intf.getName());
398 remoteFromMappingDbs(vpnId, dpId, intf.getName());
401 long portNo = interfaceManager.getPortForInterface(intf.getName());
402 String flowRef = getVpnInterfaceFlowRef(dpId, VpnConstants.LPORT_INGRESS_TABLE, vpnId, portNo);
404 String flowName = intf.getName();
406 int priority = VpnConstants.DEFAULT_FLOW_PRIORITY;
408 List<MatchInfo> matches = new ArrayList<MatchInfo>();
409 matches.add(new MatchInfo(MatchFieldType.in_port, new long[] {
412 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, VpnConstants.LPORT_INGRESS_TABLE, flowRef,
413 priority, flowName, 0, 0, null, matches, null);
415 mdsalManager.removeFlow(flowEntity);
418 private void removePrefixFromBGP(String rd, Adjacency nextHop) {
419 //public void deletePrefix(String rd, String prefix) throws Exception;
421 bgpManager.deletePrefix(rd, nextHop.getIpAddress());
422 } catch(Exception e) {
423 LOG.error("Delete prefix failed", e);
428 protected void update(InstanceIdentifier<VpnInterface> identifier,
429 VpnInterface original, VpnInterface update) {
430 // TODO Auto-generated method stub
434 private <T extends DataObject> void asyncWrite(LogicalDatastoreType datastoreType,
435 InstanceIdentifier<T> path, T data, FutureCallback<Void> callback) {
436 WriteTransaction tx = broker.newWriteOnlyTransaction();
437 tx.put(datastoreType, path, data, true);
438 Futures.addCallback(tx.submit(), callback);
441 synchronized Collection<Long> getDpnsForVpn(long vpnId) {
442 Collection<Long> dpnIds = vpnToDpnsDb.get(vpnId);
444 return ImmutableList.copyOf(dpnIds);
446 return Collections.emptyList();