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;
14 import com.google.common.base.Optional;
15 import com.google.common.util.concurrent.Futures;
16 import com.google.common.util.concurrent.FutureCallback;
18 import org.opendaylight.bgpmanager.api.IBgpManager;
19 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
20 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
21 import org.opendaylight.vpnservice.interfacemgr.interfaces.IInterfaceManager;
22 import org.opendaylight.vpnservice.mdsalutil.FlowEntity;
23 import org.opendaylight.vpnservice.mdsalutil.InstructionInfo;
24 import org.opendaylight.vpnservice.mdsalutil.InstructionType;
25 import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
26 import org.opendaylight.vpnservice.mdsalutil.MatchFieldType;
27 import org.opendaylight.vpnservice.mdsalutil.MatchInfo;
28 import org.opendaylight.vpnservice.mdsalutil.MetaDataUtil;
29 import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager;
30 import org.opendaylight.yangtools.concepts.ListenerRegistration;
31 import org.opendaylight.yangtools.yang.binding.DataObject;
32 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
33 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
34 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
35 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
36 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
37 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
38 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
39 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
40 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.AdjacencyList;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.adjacency.list.Adjacency;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.adjacency.list.AdjacencyBuilder;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.VpnInstance1;
45 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInterfaces;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.Adjacencies;
47 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnAfConfig;
48 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInstances;
49 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstance;
50 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstanceKey;
51 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
52 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceKey;
53 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceBuilder;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.AdjacenciesBuilder;
55 import org.slf4j.Logger;
56 import org.slf4j.LoggerFactory;
58 public class VpnInterfaceManager extends AbstractDataChangeListener<VpnInterface> implements AutoCloseable {
59 private static final Logger LOG = LoggerFactory.getLogger(VpnInterfaceManager.class);
60 private ListenerRegistration<DataChangeListener> listenerRegistration;
61 private final DataBroker broker;
62 private final IBgpManager bgpManager;
63 private IMdsalApiManager mdsalManager;
64 private IInterfaceManager interfaceManager;
66 private static final FutureCallback<Void> DEFAULT_CALLBACK =
67 new FutureCallback<Void>() {
68 public void onSuccess(Void result) {
69 LOG.debug("Success in Datastore operation");
72 public void onFailure(Throwable error) {
73 LOG.error("Error in Datastore operation", error);
78 * Responsible for listening to data change related to VPN Interface
79 * Bind VPN Service on the interface and informs the BGP service
81 * @param db - dataBroker service reference
83 public VpnInterfaceManager(final DataBroker db, final IBgpManager bgpManager) {
84 super(VpnInterface.class);
86 this.bgpManager = bgpManager;
90 public void setMdsalManager(IMdsalApiManager mdsalManager) {
91 this.mdsalManager = mdsalManager;
95 public void close() throws Exception {
96 if (listenerRegistration != null) {
98 listenerRegistration.close();
99 } catch (final Exception e) {
100 LOG.error("Error when cleaning up DataChangeListener.", e);
102 listenerRegistration = null;
104 LOG.info("VPN Interface Manager Closed");
107 private void registerListener(final DataBroker db) {
109 listenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
110 getWildCardPath(), VpnInterfaceManager.this, DataChangeScope.SUBTREE);
111 } catch (final Exception e) {
112 LOG.error("VPN Service DataChange listener registration fail!", e);
113 throw new IllegalStateException("VPN Service registration Listener failed.", e);
118 protected void add(final InstanceIdentifier<VpnInterface> identifier,
119 final VpnInterface vpnInterface) {
120 LOG.info("key: {} , value: {}", identifier, vpnInterface );
121 addInterface(identifier, vpnInterface);
124 private void addInterface(final InstanceIdentifier<VpnInterface> identifier,
125 final VpnInterface vpnInterface) {
126 final VpnInterfaceKey key = identifier.firstKeyOf(VpnInterface.class, VpnInterfaceKey.class);
127 String interfaceName = key.getName();
128 InstanceIdentifierBuilder<Interface> idBuilder =
129 InstanceIdentifier.builder(Interfaces.class).child(Interface.class, new InterfaceKey(interfaceName));
130 InstanceIdentifier<Interface> id = idBuilder.build();
131 Optional<Interface> port = read(LogicalDatastoreType.CONFIGURATION, id);
132 if (port.isPresent()) {
133 Interface interf = port.get();
134 bindServiceOnInterface(interf, getVpnId(vpnInterface.getVpnInstanceName()));
135 updateNextHops(identifier, vpnInterface);
139 private void updateNextHops(final InstanceIdentifier<VpnInterface> identifier, VpnInterface intf) {
141 InstanceIdentifier<Adjacencies> path = identifier.augmentation(Adjacencies.class);
142 Optional<Adjacencies> adjacencies = read(LogicalDatastoreType.CONFIGURATION, path);
143 String intfName = intf.getName();
145 if (adjacencies.isPresent()) {
146 List<Adjacency> nextHops = adjacencies.get().getAdjacency();
147 List<Adjacency> value = new ArrayList<>();
149 //Get the rd of the vpn instance
150 String rd = getRouteDistinguisher(intf.getVpnInstanceName());
151 //TODO: Get the endpoint IP from interface manager
152 String nextHopIp = "10.0.0.1";
154 if (!nextHops.isEmpty()) {
155 LOG.info("NextHops are {}", nextHops);
156 for (Adjacency nextHop : nextHops) {
157 //TODO: Generate label for the prefix and store it in the next hop model
161 updatePrefixToBGP(rd, nextHop, nextHopIp);
162 value.add(new AdjacencyBuilder(nextHop).setLabel(label).build());
165 Adjacencies aug = VpnUtil.getVpnInterfaceAugmentation(value);
166 VpnInterface opInterface = VpnUtil.getVpnInterface(intfName, intf.getVpnInstanceName(), aug);
167 InstanceIdentifier<VpnInterface> interfaceId = VpnUtil.getVpnInterfaceIdentifier(intfName);
168 asyncWrite(LogicalDatastoreType.OPERATIONAL, interfaceId, opInterface, DEFAULT_CALLBACK);
172 private long getVpnId(String vpnName) {
173 InstanceIdentifier<VpnInstance1> id = InstanceIdentifier.builder(VpnInstances.class)
174 .child(VpnInstance.class, new VpnInstanceKey(vpnName)).augmentation(VpnInstance1.class).build();
175 Optional<VpnInstance1> vpnInstance = read(LogicalDatastoreType.CONFIGURATION, id);
177 if(vpnInstance.isPresent()) {
178 vpnId = vpnInstance.get().getVpnId();
183 private String getRouteDistinguisher(String vpnName) {
184 InstanceIdentifier<VpnInstance> id = InstanceIdentifier.builder(VpnInstances.class)
185 .child(VpnInstance.class, new VpnInstanceKey(vpnName)).build();
186 Optional<VpnInstance> vpnInstance = read(LogicalDatastoreType.CONFIGURATION, id);
188 if(vpnInstance.isPresent()) {
189 VpnInstance instance = vpnInstance.get();
190 VpnAfConfig config = instance.getIpv4Family();
191 rd = config.getRouteDistinguisher();
196 private void bindServiceOnInterface(Interface intf, long vpnId) {
197 LOG.info("Bind service on interface {} for VPN: {}", intf, vpnId);
198 //TODO: Create Ingress flow on the interface to bind the VPN service
199 //TODO: Get dpn ID from the interface manager
201 short LPORT_INGRESS_TABLE = 0;
202 //TODO: Get the port no from interface manager
204 String flowRef = getL3InterfaceFlowRef(dpId, LPORT_INGRESS_TABLE, vpnId, portNo);
206 String flowName = intf.getName();
207 BigInteger COOKIE_VM_INGRESS_TABLE = new BigInteger("8000001", 16);
209 int priority = 10; //L3Constants.DEFAULT_L3_FLOW_PRIORITY;
210 short gotoTableId = 21; //L3Constants.L3_FIB_TABLE;
212 List<InstructionInfo> mkInstructions = new ArrayList<InstructionInfo>();
213 mkInstructions.add(new InstructionInfo(InstructionType.write_metadata, new BigInteger[] {
214 BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID }));
216 mkInstructions.add(new InstructionInfo(InstructionType.goto_table, new long[] { gotoTableId }));
218 List<MatchInfo> matches = new ArrayList<MatchInfo>();
219 matches.add(new MatchInfo(MatchFieldType.in_port, new long[] {
222 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, LPORT_INGRESS_TABLE, flowRef,
223 priority, flowName, 0, 0, COOKIE_VM_INGRESS_TABLE, matches, mkInstructions);
225 mdsalManager.installFlow(flowEntity);
228 private String getL3InterfaceFlowRef(long dpId, short tableId,
229 long vpnId, int portNo) {
230 return new StringBuilder().append(dpId).append(tableId).append(vpnId).append(portNo).toString();
233 private void updatePrefixToBGP(String rd, Adjacency nextHop, String nextHopIp) {
234 //TODO: Update the Prefix to BGP
235 //public void addPrefix(String rd, String prefix, String nextHop, int vpnLabel)
236 int label = nextHop.getLabel().intValue();
238 bgpManager.addPrefix(rd, nextHop.getIpAddress(), nextHopIp, label);
239 } catch(Exception e) {
240 LOG.error("Add prefix failed", e);
244 private <T extends DataObject> Optional<T> read(LogicalDatastoreType datastoreType,
245 InstanceIdentifier<T> path) {
247 ReadOnlyTransaction tx = broker.newReadOnlyTransaction();
249 Optional<T> result = Optional.absent();
251 result = tx.read(datastoreType, path).get();
252 } catch (Exception e) {
253 throw new RuntimeException(e);
259 private InstanceIdentifier<VpnInterface> getWildCardPath() {
260 return InstanceIdentifier.create(VpnInterfaces.class).child(VpnInterface.class);
264 protected void remove( InstanceIdentifier<VpnInterface> identifier, VpnInterface vpnInterface) {
265 LOG.info("Remove event - key: {}, value: {}" ,identifier, vpnInterface );
266 final VpnInterfaceKey key = identifier.firstKeyOf(VpnInterface.class, VpnInterfaceKey.class);
267 String interfaceName = key.getName();
268 InstanceIdentifierBuilder<Interface> idBuilder =
269 InstanceIdentifier.builder(Interfaces.class).child(Interface.class, new InterfaceKey(interfaceName));
270 InstanceIdentifier<Interface> id = idBuilder.build();
271 Optional<Interface> port = read(LogicalDatastoreType.CONFIGURATION, id);
272 if (port.isPresent()) {
273 Interface interf = port.get();
274 unbindServiceOnInterface(interf);
275 removeNextHops(identifier, vpnInterface);
277 LOG.info("No nexthops were available to handle remove event {}", interfaceName);
281 private void removeNextHops(final InstanceIdentifier<VpnInterface> identifier, VpnInterface intf) {
283 InstanceIdentifier<Adjacencies> path = identifier.augmentation(Adjacencies.class);
284 Optional<Adjacencies> adjacencies = read(LogicalDatastoreType.OPERATIONAL, path);
285 String intfName = intf.getName();
287 if (adjacencies.isPresent()) {
288 List<Adjacency> nextHops = adjacencies.get().getAdjacency();
290 if (!nextHops.isEmpty()) {
291 LOG.trace("NextHops are " + nextHops);
292 for (Adjacency nextHop : nextHops) {
294 removePrefixFromBGP(nextHop);
298 InstanceIdentifier<VpnInterface> interfaceId = VpnUtil.getVpnInterfaceIdentifier(intfName);
299 delete(LogicalDatastoreType.OPERATIONAL, interfaceId);
303 private <T extends DataObject> void delete(LogicalDatastoreType datastoreType, InstanceIdentifier<T> path) {
304 WriteTransaction tx = broker.newWriteOnlyTransaction();
305 tx.delete(datastoreType, path);
306 Futures.addCallback(tx.submit(), DEFAULT_CALLBACK);
309 private void unbindServiceOnInterface(Interface intf) {
310 //TODO: Remove Ingress flow on the interface to unbind the VPN service
313 private void removePrefixFromBGP(Adjacency nextHop) {
314 //TODO: Update the Prefix to BGP
318 protected void update(InstanceIdentifier<VpnInterface> identifier,
319 VpnInterface original, VpnInterface update) {
320 // TODO Auto-generated method stub
324 private <T extends DataObject> void asyncWrite(LogicalDatastoreType datastoreType,
325 InstanceIdentifier<T> path, T data, FutureCallback<Void> callback) {
326 WriteTransaction tx = broker.newWriteOnlyTransaction();
327 tx.put(datastoreType, path, data, true);
328 Futures.addCallback(tx.submit(), callback);