Unblock the Netvirt CSIT Issue from VPNManager
[netvirt.git] / vpnmanager / impl / src / main / java / org / opendaylight / netvirt / vpnmanager / VpnNodeListener.java
1 /*
2  * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.netvirt.vpnmanager;
9
10 import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
11
12 import java.util.ArrayList;
13 import java.util.Arrays;
14 import java.util.Collections;
15 import java.util.List;
16 import java.util.concurrent.CopyOnWriteArrayList;
17 import java.util.concurrent.ExecutionException;
18 import javax.annotation.PreDestroy;
19 import javax.inject.Inject;
20 import javax.inject.Singleton;
21 import org.opendaylight.genius.infra.Datastore.Configuration;
22 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
23 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
24 import org.opendaylight.genius.infra.TypedReadWriteTransaction;
25 import org.opendaylight.genius.infra.TypedWriteTransaction;
26 import org.opendaylight.genius.mdsalutil.ActionInfo;
27 import org.opendaylight.genius.mdsalutil.FlowEntity;
28 import org.opendaylight.genius.mdsalutil.InstructionInfo;
29 import org.opendaylight.genius.mdsalutil.MDSALUtil;
30 import org.opendaylight.genius.mdsalutil.MatchInfo;
31 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
32 import org.opendaylight.genius.mdsalutil.NwConstants;
33 import org.opendaylight.genius.mdsalutil.NwConstants.NxmOfFieldType;
34 import org.opendaylight.genius.mdsalutil.actions.ActionLearn;
35 import org.opendaylight.genius.mdsalutil.actions.ActionNxResubmit;
36 import org.opendaylight.genius.mdsalutil.actions.ActionPuntToController;
37 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
38 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
39 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
40 import org.opendaylight.genius.mdsalutil.matches.MatchArpOp;
41 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
42 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
43 import org.opendaylight.infrautils.utils.concurrent.Executors;
44 import org.opendaylight.mdsal.binding.api.DataBroker;
45 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
46 import org.opendaylight.netvirt.elan.arp.responder.ArpResponderUtil;
47 import org.opendaylight.serviceutils.tools.listener.AbstractAsyncDataTreeChangeListener;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.config.rev161130.VpnConfig;
51 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
52 import org.opendaylight.yangtools.yang.common.Uint64;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
55
56 @Singleton
57 public class VpnNodeListener extends AbstractAsyncDataTreeChangeListener<Node> {
58
59     private static final Logger LOG = LoggerFactory.getLogger(VpnNodeListener.class);
60     private static final String FLOWID_PREFIX = "L3.";
61
62     private final DataBroker broker;
63     private final ManagedNewTransactionRunner txRunner;
64     private final IMdsalApiManager mdsalManager;
65     private final JobCoordinator jobCoordinator;
66     private final List<Uint64> connectedDpnIds;
67     private final VpnConfig vpnConfig;
68
69     @Inject
70     public VpnNodeListener(DataBroker dataBroker, IMdsalApiManager mdsalManager, JobCoordinator jobCoordinator,
71                            VpnConfig vpnConfig) {
72         super(dataBroker, LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.create(Nodes.class).child(Node.class),
73                 Executors.newListeningSingleThreadExecutor("VpnNodeListener", LOG));
74         this.broker = dataBroker;
75         this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
76         this.mdsalManager = mdsalManager;
77         this.jobCoordinator = jobCoordinator;
78         this.connectedDpnIds = new CopyOnWriteArrayList<>();
79         this.vpnConfig = vpnConfig;
80         start();
81     }
82
83     public void start() {
84         LOG.info("{} start", getClass().getSimpleName());
85     }
86
87     @Override
88     @PreDestroy
89     public void close() {
90         super.close();
91         Executors.shutdownAndAwaitTermination(getExecutorService());
92     }
93
94     @Override
95     public void add(InstanceIdentifier<Node> identifier, Node add) {
96         Uint64 dpId = MDSALUtil.getDpnIdFromNodeName(add.getId());
97         if (!connectedDpnIds.contains(dpId)) {
98             connectedDpnIds.add(dpId);
99         }
100         processNodeAdd(dpId);
101     }
102
103     @Override
104     public void remove(InstanceIdentifier<Node> identifier, Node del) {
105         Uint64 dpId = MDSALUtil.getDpnIdFromNodeName(del.getId());
106         connectedDpnIds.remove(dpId);
107     }
108
109     @Override
110     public void update(InstanceIdentifier<Node> identifier, Node original, Node update) {
111     }
112
113     public boolean isConnectedNode(Uint64 nodeId) {
114         return nodeId != null && connectedDpnIds.contains(nodeId);
115     }
116
117     private void processNodeAdd(Uint64 dpId) {
118         jobCoordinator.enqueueJob("VPNNODE-" + dpId.toString(),
119             () -> Collections.singletonList(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
120                 LOG.debug("Received notification to install TableMiss entries for dpn {} ", dpId);
121                 makeTableMissFlow(tx, dpId, NwConstants.ADD_FLOW);
122                 makeL3IntfTblMissFlow(tx, dpId, NwConstants.ADD_FLOW);
123                 makeSubnetRouteTableMissFlow(tx, dpId, NwConstants.ADD_FLOW);
124                 makeIpv6SubnetRouteTableMissFlow(tx, dpId, NwConstants.ADD_FLOW);
125                 createTableMissForVpnGwFlow(tx, dpId);
126                 createL3GwMacArpFlows(tx, dpId);
127                 programTableMissForVpnVniDemuxTable(tx, dpId, NwConstants.ADD_FLOW);
128             })), 3);
129     }
130
131     private void makeTableMissFlow(TypedReadWriteTransaction<Configuration> confTx, Uint64 dpnId, int addOrRemove)
132             throws ExecutionException, InterruptedException {
133         final Uint64 cookieTableMiss = Uint64.valueOf("1030000", 16).intern();
134         // Instruction to goto L3 InterfaceTable
135         List<InstructionInfo> instructions =
136                 Collections.singletonList(new InstructionGotoTable(NwConstants.L3_INTERFACE_TABLE));
137         List<MatchInfo> matches = new ArrayList<>();
138         FlowEntity flowEntityLfib = MDSALUtil.buildFlowEntity(dpnId, NwConstants.L3_LFIB_TABLE,
139             getTableMissFlowRef(dpnId, NwConstants.L3_LFIB_TABLE, NwConstants.TABLE_MISS_FLOW),
140             NwConstants.TABLE_MISS_PRIORITY, "Table Miss", 0, 0, cookieTableMiss, matches, instructions);
141
142         if (addOrRemove == NwConstants.ADD_FLOW) {
143             LOG.debug("Invoking MDSAL to install Table Miss Entry");
144             mdsalManager.addFlow(confTx, flowEntityLfib);
145         } else {
146             mdsalManager.removeFlow(confTx, flowEntityLfib);
147         }
148     }
149
150     private void makeL3IntfTblMissFlow(TypedReadWriteTransaction<Configuration> confTx, Uint64 dpnId,
151             int addOrRemove) throws ExecutionException, InterruptedException {
152         List<InstructionInfo> instructions = new ArrayList<>();
153         List<MatchInfo> matches = new ArrayList<>();
154         final Uint64 cookieTableMiss = Uint64.valueOf("1030000", 16).intern();
155         // Instruction to goto L3 InterfaceTable
156
157         List<ActionInfo> actionsInfos = new ArrayList<>();
158         actionsInfos.add(new ActionNxResubmit(NwConstants.LPORT_DISPATCHER_TABLE));
159         instructions.add(new InstructionApplyActions(actionsInfos));
160         //instructions.add(new InstructionGotoTable(NwConstants.LPORT_DISPATCHER_TABLE));
161
162         FlowEntity flowEntityL3Intf = MDSALUtil.buildFlowEntity(dpnId, NwConstants.L3_INTERFACE_TABLE,
163             getTableMissFlowRef(dpnId, NwConstants.L3_INTERFACE_TABLE, NwConstants.TABLE_MISS_FLOW),
164             NwConstants.TABLE_MISS_PRIORITY, "L3 Interface Table Miss", 0, 0, cookieTableMiss,
165             matches, instructions);
166         if (addOrRemove == NwConstants.ADD_FLOW) {
167             LOG.debug("Invoking MDSAL to install L3 interface Table Miss Entries");
168             mdsalManager.addFlow(confTx, flowEntityL3Intf);
169         } else {
170             mdsalManager.removeFlow(confTx, flowEntityL3Intf);
171         }
172     }
173
174     private void makeSubnetRouteTableMissFlow(TypedReadWriteTransaction<Configuration> confTx, Uint64 dpnId,
175             int addOrRemove) throws ExecutionException, InterruptedException {
176         List<ActionInfo> actionsInfos = new ArrayList<>();
177         List<InstructionInfo> instructions = new ArrayList<>();
178         actionsInfos.add(new ActionPuntToController());
179         int learnTimeout = vpnConfig.getSubnetRoutePuntTimeout().intValue();
180         if (learnTimeout != 0) {
181             actionsInfos.add(new ActionLearn(0, learnTimeout, VpnConstants.DEFAULT_FLOW_PRIORITY,
182                     NwConstants.COOKIE_SUBNET_ROUTE_TABLE_MISS, 0, NwConstants.L3_SUBNET_ROUTE_TABLE,
183                     0, 0,
184                     Arrays.asList(
185                             new ActionLearn.MatchFromValue(NwConstants.ETHTYPE_IPV4,
186                                     NwConstants.NxmOfFieldType.NXM_OF_ETH_TYPE.getType(),
187                                     NwConstants.NxmOfFieldType.NXM_OF_ETH_TYPE.getFlowModHeaderLenInt()),
188                             new ActionLearn.MatchFromField(NwConstants.NxmOfFieldType.NXM_OF_IP_DST.getType(),
189                                     NwConstants.NxmOfFieldType.NXM_OF_IP_DST.getType(),
190                                     NwConstants.NxmOfFieldType.NXM_OF_IP_DST.getFlowModHeaderLenInt()),
191                             new ActionLearn.MatchFromField(NxmOfFieldType.OXM_OF_METADATA.getType(),
192                                     MetaDataUtil.METADATA_VPN_ID_OFFSET,
193                                     NxmOfFieldType.OXM_OF_METADATA.getType(), MetaDataUtil.METADATA_VPN_ID_OFFSET,
194                                     MetaDataUtil.METADATA_VPN_ID_BITLEN))));
195         }
196
197         instructions.add(new InstructionApplyActions(actionsInfos));
198         List<MatchInfo> matches = new ArrayList<>();
199         matches.add(MatchEthernetType.IPV4);
200         String flowRef = getSubnetRouteTableMissFlowRef(dpnId, NwConstants.L3_SUBNET_ROUTE_TABLE,
201                 NwConstants.ETHTYPE_IPV4, NwConstants.TABLE_MISS_FLOW);
202         FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpnId, NwConstants.L3_SUBNET_ROUTE_TABLE, flowRef,
203             NwConstants.TABLE_MISS_PRIORITY, "Subnet Route Table Miss", 0, 0,
204                 NwConstants.COOKIE_SUBNET_ROUTE_TABLE_MISS, matches, instructions);
205
206         if (addOrRemove == NwConstants.ADD_FLOW) {
207             mdsalManager.addFlow(confTx, flowEntity);
208         } else {
209             mdsalManager.removeFlow(confTx, flowEntity);
210         }
211     }
212
213     private void makeIpv6SubnetRouteTableMissFlow(TypedReadWriteTransaction<Configuration> confTx, Uint64 dpnId,
214             int addOrRemove) throws ExecutionException, InterruptedException {
215         List<ActionInfo> actionsInfos = new ArrayList<>();
216         List<InstructionInfo> instructions = new ArrayList<>();
217         actionsInfos.add(new ActionPuntToController());
218         int learnTimeout = vpnConfig.getSubnetRoutePuntTimeout().intValue();
219         if (learnTimeout != 0) {
220             actionsInfos.add(new ActionLearn(0, learnTimeout, VpnConstants.DEFAULT_FLOW_PRIORITY,
221                     NwConstants.COOKIE_SUBNET_ROUTE_TABLE_MISS, 0, NwConstants.L3_SUBNET_ROUTE_TABLE,
222                     0, 0,
223                     Arrays.asList(
224                             new ActionLearn.MatchFromValue(NwConstants.ETHTYPE_IPV6,
225                                     NwConstants.NxmOfFieldType.NXM_OF_ETH_TYPE.getType(),
226                                     NwConstants.NxmOfFieldType.NXM_OF_ETH_TYPE.getFlowModHeaderLenInt()),
227                             new ActionLearn.MatchFromField(NwConstants.NxmOfFieldType.NXM_NX_IPV6_DST.getType(),
228                                     NwConstants.NxmOfFieldType.NXM_NX_IPV6_DST.getType(),
229                                     NwConstants.NxmOfFieldType.NXM_NX_IPV6_DST.getFlowModHeaderLenInt()),
230                             new ActionLearn.MatchFromField(NwConstants.NxmOfFieldType.OXM_OF_METADATA.getType(),
231                                     MetaDataUtil.METADATA_VPN_ID_OFFSET,
232                                     NwConstants.NxmOfFieldType.OXM_OF_METADATA.getType(),
233                                     MetaDataUtil.METADATA_VPN_ID_OFFSET,
234                                     MetaDataUtil.METADATA_VPN_ID_BITLEN))));
235         }
236
237         instructions.add(new InstructionApplyActions(actionsInfos));
238         List<MatchInfo> matches = new ArrayList<>();
239         matches.add(MatchEthernetType.IPV6);
240         String flowRef = getSubnetRouteTableMissFlowRef(dpnId, NwConstants.L3_SUBNET_ROUTE_TABLE,
241                 NwConstants.ETHTYPE_IPV6, NwConstants.TABLE_MISS_FLOW);
242         FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpnId, NwConstants.L3_SUBNET_ROUTE_TABLE, flowRef,
243                 NwConstants.TABLE_MISS_PRIORITY, "IPv6 Subnet Route Table Miss", 0, 0,
244                 NwConstants.COOKIE_SUBNET_ROUTE_TABLE_MISS, matches, instructions);
245         if (addOrRemove == NwConstants.ADD_FLOW) {
246             LOG.debug("makeIpv6SubnetRouteTableMissFlow: Install Ipv6 Subnet Route Table Miss entry");
247             mdsalManager.addFlow(confTx, flowEntity);
248         } else {
249             LOG.debug("makeIpv6SubnetRouteTableMissFlow: Remove Ipv6 Subnet Route Table Miss entry");
250             mdsalManager.removeFlow(confTx, flowEntity);
251         }
252     }
253
254     private void programTableMissForVpnVniDemuxTable(TypedReadWriteTransaction<Configuration> confTx, Uint64 dpnId,
255             int addOrRemove) throws ExecutionException, InterruptedException {
256         List<ActionInfo> actionsInfos = Collections.singletonList(new ActionNxResubmit(NwConstants
257                 .LPORT_DISPATCHER_TABLE));
258         List<InstructionInfo> instructions = Collections.singletonList(new InstructionApplyActions(actionsInfos));
259         List<MatchInfo> matches = new ArrayList<>();
260         String flowRef = getTableMissFlowRef(dpnId, NwConstants.L3VNI_EXTERNAL_TUNNEL_DEMUX_TABLE,
261                 NwConstants.TABLE_MISS_FLOW);
262         FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpnId, NwConstants.L3VNI_EXTERNAL_TUNNEL_DEMUX_TABLE,
263                 flowRef, NwConstants.TABLE_MISS_PRIORITY, "VPN-VNI Demux Table Miss", 0, 0,
264                 Uint64.valueOf("1080000", 16).intern(), matches, instructions);
265
266         if (addOrRemove == NwConstants.ADD_FLOW) {
267             mdsalManager.addFlow(confTx, flowEntity);
268         } else {
269             mdsalManager.removeFlow(confTx, flowEntity);
270         }
271     }
272
273     private void createTableMissForVpnGwFlow(TypedWriteTransaction<Configuration> confTx, Uint64 dpId) {
274         List<MatchInfo> matches = new ArrayList<>();
275         List<ActionInfo> actionsInfos =
276                 Collections.singletonList(new ActionNxResubmit(NwConstants.LPORT_DISPATCHER_TABLE));
277         List<InstructionInfo> instructions = Collections.singletonList(new InstructionApplyActions(actionsInfos));
278         FlowEntity flowEntityMissforGw = MDSALUtil.buildFlowEntity(dpId, NwConstants.L3_GW_MAC_TABLE,
279             getTableMissFlowRef(dpId, NwConstants.L3_GW_MAC_TABLE, NwConstants.TABLE_MISS_FLOW),
280             NwConstants.TABLE_MISS_PRIORITY, "L3 Gw Mac Table Miss", 0, 0,
281                 Uint64.valueOf("1080000", 16).intern(), matches,
282             instructions);
283         LOG.trace("Invoking MDSAL to install L3 Gw Mac Table Miss Entry");
284         mdsalManager.addFlow(confTx, flowEntityMissforGw);
285     }
286
287     private void createL3GwMacArpFlows(TypedWriteTransaction<Configuration> confTx, Uint64 dpId) {
288         FlowEntity arpReqGwMacTbl = ArpResponderUtil.createArpDefaultFlow(dpId, NwConstants.L3_GW_MAC_TABLE,
289                 NwConstants.ARP_REQUEST, () -> Arrays.asList(MatchEthernetType.ARP, MatchArpOp.REQUEST),
290             () -> Collections.singletonList(new ActionNxResubmit(NwConstants.LPORT_DISPATCHER_TABLE)));
291         LOG.trace("Invoking MDSAL to install Arp Rquest Match Flow for table {}", NwConstants.L3_GW_MAC_TABLE);
292         mdsalManager.addFlow(confTx, arpReqGwMacTbl);
293         FlowEntity arpRepGwMacTbl = ArpResponderUtil.createArpDefaultFlow(dpId, NwConstants.L3_GW_MAC_TABLE,
294                 NwConstants.ARP_REPLY, () -> Arrays.asList(MatchEthernetType.ARP, MatchArpOp.REPLY),
295             () -> Collections.singletonList(new ActionNxResubmit(NwConstants.LPORT_DISPATCHER_TABLE)));
296         LOG.trace("Invoking MDSAL to install  Arp Reply Match Flow for Table {} ", NwConstants.L3_GW_MAC_TABLE);
297         mdsalManager.addFlow(confTx, arpRepGwMacTbl);
298     }
299
300     private String getTableMissFlowRef(Uint64 dpnId, short tableId, int tableMiss) {
301         return FLOWID_PREFIX + dpnId + NwConstants.FLOWID_SEPARATOR + tableId + NwConstants.FLOWID_SEPARATOR + tableMiss
302                 + FLOWID_PREFIX;
303     }
304
305     private String getSubnetRouteTableMissFlowRef(Uint64 dpnId, short tableId, int etherType, int tableMiss) {
306         return FLOWID_PREFIX + dpnId + NwConstants.FLOWID_SEPARATOR + tableId + NwConstants.FLOWID_SEPARATOR + etherType
307                 + NwConstants.FLOWID_SEPARATOR + tableMiss + FLOWID_PREFIX;
308     }
309 }