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.List;
12 import java.util.ArrayList;
13 import java.util.concurrent.ExecutionException;
14 import java.util.concurrent.Future;
16 import com.google.common.base.Optional;
17 import com.google.common.util.concurrent.Futures;
18 import com.google.common.util.concurrent.FutureCallback;
20 import org.opendaylight.bgpmanager.api.IBgpManager;
21 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
22 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
23 import org.opendaylight.vpnservice.interfacemgr.interfaces.IInterfaceManager;
24 import org.opendaylight.vpnservice.mdsalutil.FlowEntity;
25 import org.opendaylight.vpnservice.mdsalutil.InstructionInfo;
26 import org.opendaylight.vpnservice.mdsalutil.InstructionType;
27 import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
28 import org.opendaylight.vpnservice.mdsalutil.MatchFieldType;
29 import org.opendaylight.vpnservice.mdsalutil.MatchInfo;
30 import org.opendaylight.vpnservice.mdsalutil.MetaDataUtil;
31 import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager;
32 import org.opendaylight.yangtools.concepts.ListenerRegistration;
33 import org.opendaylight.yangtools.yang.binding.DataObject;
34 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
35 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
36 import org.opendaylight.yangtools.yang.common.RpcResult;
37 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
38 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
39 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
40 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
41 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
42 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
43 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.AdjacencyList;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.adjacency.list.Adjacency;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.adjacency.list.AdjacencyBuilder;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.VpnInstance1;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.GetUniqueIdInput;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.GetUniqueIdInputBuilder;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.GetUniqueIdOutput;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.IdManagerService;
52 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInterfaces;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.Adjacencies;
54 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnAfConfig;
55 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInstances;
56 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstance;
57 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstanceKey;
58 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
59 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceKey;
60 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceBuilder;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.AdjacenciesBuilder;
62 import org.slf4j.Logger;
63 import org.slf4j.LoggerFactory;
65 public class VpnInterfaceManager extends AbstractDataChangeListener<VpnInterface> implements AutoCloseable {
66 private static final Logger LOG = LoggerFactory.getLogger(VpnInterfaceManager.class);
67 private ListenerRegistration<DataChangeListener> listenerRegistration;
68 private final DataBroker broker;
69 private final IBgpManager bgpManager;
70 private IMdsalApiManager mdsalManager;
71 private IInterfaceManager interfaceManager;
72 private IdManagerService idManager;
74 private static final FutureCallback<Void> DEFAULT_CALLBACK =
75 new FutureCallback<Void>() {
76 public void onSuccess(Void result) {
77 LOG.debug("Success in Datastore operation");
80 public void onFailure(Throwable error) {
81 LOG.error("Error in Datastore operation", error);
86 * Responsible for listening to data change related to VPN Interface
87 * Bind VPN Service on the interface and informs the BGP service
89 * @param db - dataBroker service reference
91 public VpnInterfaceManager(final DataBroker db, final IBgpManager bgpManager) {
92 super(VpnInterface.class);
94 this.bgpManager = bgpManager;
98 public void setMdsalManager(IMdsalApiManager mdsalManager) {
99 this.mdsalManager = mdsalManager;
102 public void setInterfaceManager(IInterfaceManager interfaceManager) {
103 this.interfaceManager = interfaceManager;
106 public void setIdManager(IdManagerService idManager) {
107 this.idManager = idManager;
111 public void close() throws Exception {
112 if (listenerRegistration != null) {
114 listenerRegistration.close();
115 } catch (final Exception e) {
116 LOG.error("Error when cleaning up DataChangeListener.", e);
118 listenerRegistration = null;
120 LOG.info("VPN Interface Manager Closed");
123 private void registerListener(final DataBroker db) {
125 listenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
126 getWildCardPath(), VpnInterfaceManager.this, DataChangeScope.SUBTREE);
127 } catch (final Exception e) {
128 LOG.error("VPN Service DataChange listener registration fail!", e);
129 throw new IllegalStateException("VPN Service registration Listener failed.", e);
134 protected void add(final InstanceIdentifier<VpnInterface> identifier,
135 final VpnInterface vpnInterface) {
136 LOG.info("key: {} , value: {}", identifier, vpnInterface );
137 addInterface(identifier, vpnInterface);
140 private void addInterface(final InstanceIdentifier<VpnInterface> identifier,
141 final VpnInterface vpnInterface) {
142 final VpnInterfaceKey key = identifier.firstKeyOf(VpnInterface.class, VpnInterfaceKey.class);
143 String interfaceName = key.getName();
144 InstanceIdentifierBuilder<Interface> idBuilder =
145 InstanceIdentifier.builder(Interfaces.class).child(Interface.class, new InterfaceKey(interfaceName));
146 InstanceIdentifier<Interface> id = idBuilder.build();
147 Optional<Interface> port = read(LogicalDatastoreType.CONFIGURATION, id);
148 if (port.isPresent()) {
149 Interface interf = port.get();
150 bindServiceOnInterface(interf, getVpnId(vpnInterface.getVpnInstanceName()));
151 updateNextHops(identifier, vpnInterface);
155 private void updateNextHops(final InstanceIdentifier<VpnInterface> identifier, VpnInterface intf) {
157 InstanceIdentifier<Adjacencies> path = identifier.augmentation(Adjacencies.class);
158 Optional<Adjacencies> adjacencies = read(LogicalDatastoreType.CONFIGURATION, path);
159 String intfName = intf.getName();
161 if (adjacencies.isPresent()) {
162 List<Adjacency> nextHops = adjacencies.get().getAdjacency();
163 List<Adjacency> value = new ArrayList<>();
165 //Get the rd of the vpn instance
166 String rd = getRouteDistinguisher(intf.getVpnInstanceName());
168 long dpnId = interfaceManager.getDpnForInterface(intfName);
169 String nextHopIp = interfaceManager.getEndpointIpForDpn(dpnId);
171 if (!nextHops.isEmpty()) {
172 LOG.info("NextHops are {}", nextHops);
173 for (Adjacency nextHop : nextHops) {
174 String key = nextHop.getIpAddress();
175 long label = getUniqueId(key);
177 updatePrefixToBGP(rd, nextHop, nextHopIp, label);
178 value.add(new AdjacencyBuilder(nextHop).setLabel(label).build());
181 Adjacencies aug = VpnUtil.getVpnInterfaceAugmentation(value);
182 VpnInterface opInterface = VpnUtil.getVpnInterface(intfName, intf.getVpnInstanceName(), aug);
183 InstanceIdentifier<VpnInterface> interfaceId = VpnUtil.getVpnInterfaceIdentifier(intfName);
184 asyncWrite(LogicalDatastoreType.OPERATIONAL, interfaceId, opInterface, DEFAULT_CALLBACK);
188 private Integer getUniqueId(String idKey) {
189 GetUniqueIdInput getIdInput = new GetUniqueIdInputBuilder()
190 .setPoolName(VpnConstants.VPN_IDPOOL_NAME)
191 .setIdKey(idKey).build();
194 Future<RpcResult<GetUniqueIdOutput>> result = idManager.getUniqueId(getIdInput);
195 RpcResult<GetUniqueIdOutput> rpcResult = result.get();
196 if(rpcResult.isSuccessful()) {
197 return rpcResult.getResult().getIdValue().intValue();
199 LOG.warn("RPC Call to Get Unique Id returned with Errors {}", rpcResult.getErrors());
201 } catch (NullPointerException | InterruptedException | ExecutionException e) {
202 LOG.warn("Exception when getting Unique Id",e);
207 private long getVpnId(String vpnName) {
208 InstanceIdentifier<VpnInstance1> id = InstanceIdentifier.builder(VpnInstances.class)
209 .child(VpnInstance.class, new VpnInstanceKey(vpnName)).augmentation(VpnInstance1.class).build();
210 Optional<VpnInstance1> vpnInstance = read(LogicalDatastoreType.CONFIGURATION, id);
212 if(vpnInstance.isPresent()) {
213 vpnId = vpnInstance.get().getVpnId();
218 private String getRouteDistinguisher(String vpnName) {
219 InstanceIdentifier<VpnInstance> id = InstanceIdentifier.builder(VpnInstances.class)
220 .child(VpnInstance.class, new VpnInstanceKey(vpnName)).build();
221 Optional<VpnInstance> vpnInstance = read(LogicalDatastoreType.CONFIGURATION, id);
223 if(vpnInstance.isPresent()) {
224 VpnInstance instance = vpnInstance.get();
225 VpnAfConfig config = instance.getIpv4Family();
226 rd = config.getRouteDistinguisher();
231 private void bindServiceOnInterface(Interface intf, long vpnId) {
232 LOG.info("Bind service on interface {} for VPN: {}", intf, vpnId);
234 long dpId = interfaceManager.getDpnForInterface(intf.getName());
236 LOG.warn("DPN for interface {} not found. Bind service on this interface aborted.", intf.getName());
240 long portNo = interfaceManager.getPortForInterface(intf.getName());
241 String flowRef = getVpnInterfaceFlowRef(dpId, VpnConstants.LPORT_INGRESS_TABLE, vpnId, portNo);
243 String flowName = intf.getName();
244 BigInteger COOKIE_VM_INGRESS_TABLE = new BigInteger("8000001", 16);
246 int priority = VpnConstants.DEFAULT_FLOW_PRIORITY;
247 short gotoTableId = VpnConstants.FIB_TABLE;
249 List<InstructionInfo> mkInstructions = new ArrayList<InstructionInfo>();
250 mkInstructions.add(new InstructionInfo(InstructionType.write_metadata, new BigInteger[] {
251 BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID }));
253 mkInstructions.add(new InstructionInfo(InstructionType.goto_table, new long[] { gotoTableId }));
255 List<MatchInfo> matches = new ArrayList<MatchInfo>();
256 matches.add(new MatchInfo(MatchFieldType.in_port, new long[] {
259 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, VpnConstants.LPORT_INGRESS_TABLE, flowRef,
260 priority, flowName, 0, 0, COOKIE_VM_INGRESS_TABLE, matches, mkInstructions);
262 mdsalManager.installFlow(flowEntity);
265 private String getVpnInterfaceFlowRef(long dpId, short tableId,
266 long vpnId, long portNo) {
267 return new StringBuilder().append(dpId).append(tableId).append(vpnId).append(portNo).toString();
270 private void updatePrefixToBGP(String rd, Adjacency nextHop, String nextHopIp, long label) {
272 bgpManager.addPrefix(rd, nextHop.getIpAddress(), nextHopIp, (int)label);
273 } catch(Exception e) {
274 LOG.error("Add prefix failed", e);
278 private <T extends DataObject> Optional<T> read(LogicalDatastoreType datastoreType,
279 InstanceIdentifier<T> path) {
281 ReadOnlyTransaction tx = broker.newReadOnlyTransaction();
283 Optional<T> result = Optional.absent();
285 result = tx.read(datastoreType, path).get();
286 } catch (Exception e) {
287 throw new RuntimeException(e);
293 private InstanceIdentifier<VpnInterface> getWildCardPath() {
294 return InstanceIdentifier.create(VpnInterfaces.class).child(VpnInterface.class);
298 protected void remove( InstanceIdentifier<VpnInterface> identifier, VpnInterface vpnInterface) {
299 LOG.info("Remove event - key: {}, value: {}" ,identifier, vpnInterface );
300 final VpnInterfaceKey key = identifier.firstKeyOf(VpnInterface.class, VpnInterfaceKey.class);
301 String interfaceName = key.getName();
302 InstanceIdentifierBuilder<Interface> idBuilder =
303 InstanceIdentifier.builder(Interfaces.class).child(Interface.class, new InterfaceKey(interfaceName));
304 InstanceIdentifier<Interface> id = idBuilder.build();
305 Optional<Interface> port = read(LogicalDatastoreType.CONFIGURATION, id);
306 if (port.isPresent()) {
307 Interface interf = port.get();
308 unbindServiceOnInterface(interf, getVpnId(vpnInterface.getVpnInstanceName()));
309 removeNextHops(identifier, vpnInterface);
311 LOG.info("No nexthops were available to handle remove event {}", interfaceName);
315 private void removeNextHops(final InstanceIdentifier<VpnInterface> identifier, VpnInterface intf) {
317 InstanceIdentifier<Adjacencies> path = identifier.augmentation(Adjacencies.class);
318 Optional<Adjacencies> adjacencies = read(LogicalDatastoreType.OPERATIONAL, path);
319 String intfName = intf.getName();
320 String rd = getRouteDistinguisher(intf.getVpnInstanceName());
321 if (adjacencies.isPresent()) {
322 List<Adjacency> nextHops = adjacencies.get().getAdjacency();
324 if (!nextHops.isEmpty()) {
325 LOG.trace("NextHops are " + nextHops);
326 for (Adjacency nextHop : nextHops) {
327 removePrefixFromBGP(rd, nextHop);
331 InstanceIdentifier<VpnInterface> interfaceId = VpnUtil.getVpnInterfaceIdentifier(intfName);
332 delete(LogicalDatastoreType.OPERATIONAL, interfaceId);
335 private <T extends DataObject> void delete(LogicalDatastoreType datastoreType, InstanceIdentifier<T> path) {
336 WriteTransaction tx = broker.newWriteOnlyTransaction();
337 tx.delete(datastoreType, path);
338 Futures.addCallback(tx.submit(), DEFAULT_CALLBACK);
341 private void unbindServiceOnInterface(Interface intf, long vpnId) {
342 LOG.info("Unbind service on interface {} for VPN: {}", intf, vpnId);
344 long dpId = interfaceManager.getDpnForInterface(intf.getName());
346 LOG.warn("DPN for interface {} not found. Unbind service on this interface aborted.", intf.getName());
350 long portNo = interfaceManager.getPortForInterface(intf.getName());
351 String flowRef = getVpnInterfaceFlowRef(dpId, VpnConstants.LPORT_INGRESS_TABLE, vpnId, portNo);
353 String flowName = intf.getName();
355 int priority = VpnConstants.DEFAULT_FLOW_PRIORITY;
357 List<MatchInfo> matches = new ArrayList<MatchInfo>();
358 matches.add(new MatchInfo(MatchFieldType.in_port, new long[] {
361 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, VpnConstants.LPORT_INGRESS_TABLE, flowRef,
362 priority, flowName, 0, 0, null, matches, null);
364 mdsalManager.removeFlow(flowEntity);
367 private void removePrefixFromBGP(String rd, Adjacency nextHop) {
368 //public void deletePrefix(String rd, String prefix) throws Exception;
370 bgpManager.deletePrefix(rd, nextHop.getIpAddress());
371 } catch(Exception e) {
372 LOG.error("Delete prefix failed", e);
377 protected void update(InstanceIdentifier<VpnInterface> identifier,
378 VpnInterface original, VpnInterface update) {
379 // TODO Auto-generated method stub
383 private <T extends DataObject> void asyncWrite(LogicalDatastoreType datastoreType,
384 InstanceIdentifier<T> path, T data, FutureCallback<Void> callback) {
385 WriteTransaction tx = broker.newWriteOnlyTransaction();
386 tx.put(datastoreType, path, data, true);
387 Futures.addCallback(tx.submit(), callback);