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);
182 LOG.trace("NextHops are {}", nextHops);
183 for (Adjacency nextHop : nextHops) {
184 String key = nextHop.getIpAddress();
185 long label = getUniqueId(key);
186 value.add(new AdjacencyBuilder(nextHop).setLabel(label).build());
189 Adjacencies aug = VpnUtil.getVpnInterfaceAugmentation(value);
190 VpnInterface opInterface = VpnUtil.getVpnInterface(intfName, intf.getVpnInstanceName(), aug);
191 InstanceIdentifier<VpnInterface> interfaceId = VpnUtil.getVpnInterfaceIdentifier(intfName);
192 asyncWrite(LogicalDatastoreType.OPERATIONAL, interfaceId, opInterface, DEFAULT_CALLBACK);
193 for (Adjacency nextHop : nextHops) {
194 String key = nextHop.getIpAddress();
195 long label = getUniqueId(key);
196 updatePrefixToBGP(rd, nextHop, nextHopIp, label);
201 private Integer getUniqueId(String idKey) {
202 GetUniqueIdInput getIdInput = new GetUniqueIdInputBuilder()
203 .setPoolName(VpnConstants.VPN_IDPOOL_NAME)
204 .setIdKey(idKey).build();
207 Future<RpcResult<GetUniqueIdOutput>> result = idManager.getUniqueId(getIdInput);
208 RpcResult<GetUniqueIdOutput> rpcResult = result.get();
209 if(rpcResult.isSuccessful()) {
210 return rpcResult.getResult().getIdValue().intValue();
212 LOG.warn("RPC Call to Get Unique Id returned with Errors {}", rpcResult.getErrors());
214 } catch (NullPointerException | InterruptedException | ExecutionException e) {
215 LOG.warn("Exception when getting Unique Id",e);
220 private long getVpnId(String vpnName) {
221 //TODO: This should be a Util function
222 InstanceIdentifier<VpnInstance1> id = InstanceIdentifier.builder(VpnInstances.class)
223 .child(VpnInstance.class, new VpnInstanceKey(vpnName)).augmentation(VpnInstance1.class).build();
224 Optional<VpnInstance1> vpnInstance = read(LogicalDatastoreType.OPERATIONAL, id);
225 //TODO: Default vpnid should be a constant.
227 if(vpnInstance.isPresent()) {
228 vpnId = vpnInstance.get().getVpnId();
233 private String getRouteDistinguisher(String vpnName) {
234 InstanceIdentifier<VpnInstance> id = InstanceIdentifier.builder(VpnInstances.class)
235 .child(VpnInstance.class, new VpnInstanceKey(vpnName)).build();
236 Optional<VpnInstance> vpnInstance = read(LogicalDatastoreType.CONFIGURATION, id);
238 if(vpnInstance.isPresent()) {
239 VpnInstance instance = vpnInstance.get();
240 VpnAfConfig config = instance.getIpv4Family();
241 rd = config.getRouteDistinguisher();
246 private synchronized void updateMappingDbs(long vpnId, long dpnId, String intfName) {
247 Collection<Long> dpnIds = vpnToDpnsDb.get(vpnId);
249 dpnIds = new HashSet<>();
251 if(dpnIds.add(dpnId)) {
252 vpnToDpnsDb.put(vpnId, dpnIds);
253 //TODO: Send an Event that new DPN added...
256 Collection<String> intfNames = dpnToInterfaceDb.get(dpnId);
257 if(intfNames == null) {
258 intfNames = new ArrayList<>();
260 intfNames.add(intfName);
261 dpnToInterfaceDb.put(dpnId, intfNames);
264 private synchronized void remoteFromMappingDbs(long vpnId, long dpnId, String inftName) {
265 Collection<String> intfNames = dpnToInterfaceDb.get(dpnId);
266 if(intfNames == null) {
269 intfNames.remove(inftName);
270 dpnToInterfaceDb.put(dpnId, intfNames);
271 if(intfNames.isEmpty()) {
272 Collection<Long> dpnIds = vpnToDpnsDb.get(vpnId);
276 dpnIds.remove(dpnId);
277 vpnToDpnsDb.put(vpnId, dpnIds);
281 private void bindServiceOnInterface(Interface intf, long vpnId) {
282 LOG.trace("Bind service on interface {} for VPN: {}", intf, vpnId);
284 long dpId = interfaceManager.getDpnForInterface(intf.getName());
286 LOG.warn("DPN for interface {} not found. Bind service on this interface aborted.", intf.getName());
289 updateMappingDbs(vpnId, dpId, intf.getName());
292 long portNo = interfaceManager.getPortForInterface(intf.getName());
293 String flowRef = getVpnInterfaceFlowRef(dpId, VpnConstants.LPORT_INGRESS_TABLE, vpnId, portNo);
295 String flowName = intf.getName();
296 BigInteger COOKIE_VM_INGRESS_TABLE = new BigInteger("8000001", 16);
298 int priority = VpnConstants.DEFAULT_FLOW_PRIORITY;
299 short gotoTableId = VpnConstants.FIB_TABLE;
301 List<InstructionInfo> mkInstructions = new ArrayList<InstructionInfo>();
302 mkInstructions.add(new InstructionInfo(InstructionType.write_metadata, new BigInteger[] {
303 BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID }));
305 mkInstructions.add(new InstructionInfo(InstructionType.goto_table, new long[] { gotoTableId }));
307 List<MatchInfo> matches = new ArrayList<MatchInfo>();
308 matches.add(new MatchInfo(MatchFieldType.in_port, new long[] {
311 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, VpnConstants.LPORT_INGRESS_TABLE, flowRef,
312 priority, flowName, 0, 0, COOKIE_VM_INGRESS_TABLE, matches, mkInstructions);
314 mdsalManager.installFlow(flowEntity);
317 private String getVpnInterfaceFlowRef(long dpId, short tableId,
318 long vpnId, long portNo) {
319 return new StringBuilder().append(dpId).append(tableId).append(vpnId).append(portNo).toString();
322 private void updatePrefixToBGP(String rd, Adjacency nextHop, String nextHopIp, long label) {
324 bgpManager.addPrefix(rd, nextHop.getIpAddress(), nextHopIp, (int)label);
325 } catch(Exception e) {
326 LOG.error("Add prefix failed", e);
330 private <T extends DataObject> Optional<T> read(LogicalDatastoreType datastoreType,
331 InstanceIdentifier<T> path) {
333 ReadOnlyTransaction tx = broker.newReadOnlyTransaction();
335 Optional<T> result = Optional.absent();
337 result = tx.read(datastoreType, path).get();
338 } catch (Exception e) {
339 throw new RuntimeException(e);
345 private InstanceIdentifier<VpnInterface> getWildCardPath() {
346 return InstanceIdentifier.create(VpnInterfaces.class).child(VpnInterface.class);
350 protected void remove( InstanceIdentifier<VpnInterface> identifier, VpnInterface vpnInterface) {
351 LOG.trace("Remove event - key: {}, value: {}" ,identifier, vpnInterface );
352 final VpnInterfaceKey key = identifier.firstKeyOf(VpnInterface.class, VpnInterfaceKey.class);
353 String interfaceName = key.getName();
354 InstanceIdentifierBuilder<Interface> idBuilder =
355 InstanceIdentifier.builder(Interfaces.class).child(Interface.class, new InterfaceKey(interfaceName));
356 InstanceIdentifier<Interface> id = idBuilder.build();
357 Optional<Interface> port = read(LogicalDatastoreType.CONFIGURATION, id);
358 if (port.isPresent()) {
359 Interface interf = port.get();
360 removeNextHops(identifier, vpnInterface);
361 unbindServiceOnInterface(interf, getVpnId(vpnInterface.getVpnInstanceName()));
363 LOG.warn("No nexthops were available to handle remove event {}", interfaceName);
367 private void removeNextHops(final InstanceIdentifier<VpnInterface> identifier, VpnInterface intf) {
369 InstanceIdentifier<Adjacencies> path = identifier.augmentation(Adjacencies.class);
370 Optional<Adjacencies> adjacencies = read(LogicalDatastoreType.OPERATIONAL, path);
371 String intfName = intf.getName();
372 String rd = getRouteDistinguisher(intf.getVpnInstanceName());
373 if (adjacencies.isPresent()) {
374 List<Adjacency> nextHops = adjacencies.get().getAdjacency();
376 if (!nextHops.isEmpty()) {
377 LOG.trace("NextHops are " + nextHops);
378 for (Adjacency nextHop : nextHops) {
379 removePrefixFromBGP(rd, nextHop);
383 InstanceIdentifier<VpnInterface> interfaceId = VpnUtil.getVpnInterfaceIdentifier(intfName);
384 delete(LogicalDatastoreType.OPERATIONAL, interfaceId);
387 private <T extends DataObject> void delete(LogicalDatastoreType datastoreType, InstanceIdentifier<T> path) {
388 WriteTransaction tx = broker.newWriteOnlyTransaction();
389 tx.delete(datastoreType, path);
390 Futures.addCallback(tx.submit(), DEFAULT_CALLBACK);
393 private void unbindServiceOnInterface(Interface intf, long vpnId) {
394 LOG.trace("Unbind service on interface {} for VPN: {}", intf, vpnId);
396 long dpId = interfaceManager.getDpnForInterface(intf.getName());
398 LOG.warn("DPN for interface {} not found. Unbind service on this interface aborted.", intf.getName());
401 remoteFromMappingDbs(vpnId, dpId, intf.getName());
404 long portNo = interfaceManager.getPortForInterface(intf.getName());
405 String flowRef = getVpnInterfaceFlowRef(dpId, VpnConstants.LPORT_INGRESS_TABLE, vpnId, portNo);
407 String flowName = intf.getName();
409 int priority = VpnConstants.DEFAULT_FLOW_PRIORITY;
411 List<MatchInfo> matches = new ArrayList<MatchInfo>();
412 matches.add(new MatchInfo(MatchFieldType.in_port, new long[] {
415 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, VpnConstants.LPORT_INGRESS_TABLE, flowRef,
416 priority, flowName, 0, 0, null, matches, null);
418 mdsalManager.removeFlow(flowEntity);
421 private void removePrefixFromBGP(String rd, Adjacency nextHop) {
422 //public void deletePrefix(String rd, String prefix) throws Exception;
424 bgpManager.deletePrefix(rd, nextHop.getIpAddress());
425 } catch(Exception e) {
426 LOG.error("Delete prefix failed", e);
431 protected void update(InstanceIdentifier<VpnInterface> identifier,
432 VpnInterface original, VpnInterface update) {
433 // TODO Auto-generated method stub
437 private <T extends DataObject> void asyncWrite(LogicalDatastoreType datastoreType,
438 InstanceIdentifier<T> path, T data, FutureCallback<Void> callback) {
439 WriteTransaction tx = broker.newWriteOnlyTransaction();
440 tx.put(datastoreType, path, data, true);
441 Futures.addCallback(tx.submit(), callback);
444 synchronized Collection<Long> getDpnsForVpn(long vpnId) {
445 Collection<Long> dpnIds = vpnToDpnsDb.get(vpnId);
447 return ImmutableList.copyOf(dpnIds);
449 return Collections.emptyList();