Adding logic and code for pushing FlowMods in the LoadBalancerService.
[netvirt.git] / openstack / net-virt-providers / src / main / java / org / opendaylight / ovsdb / openstack / netvirt / providers / openflow13 / services / LoadBalancerService.java
1 /*
2  * Copyright (C) 2014 SDN Hub, LLC.
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  * Authors : Srini Seetharaman, Madhu Venugopal
9  */
10 package org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.services;
11
12 import java.math.BigInteger;
13 import java.util.Iterator;
14 import java.util.List;
15 import java.util.Map;
16
17 import org.opendaylight.controller.sal.utils.Status;
18 import org.opendaylight.controller.sal.utils.StatusCode;
19 import org.opendaylight.controller.sal.core.Node;
20 import org.opendaylight.ovsdb.openstack.netvirt.api.LoadBalancerConfiguration;
21 import org.opendaylight.ovsdb.openstack.netvirt.api.LoadBalancerConfiguration.LoadBalancerPoolMember;
22 import org.opendaylight.ovsdb.openstack.netvirt.api.LoadBalancerProvider;
23 import org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.AbstractServiceInstance;
24 import org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.Service;
25 import org.opendaylight.ovsdb.utils.mdsal.openflow.ActionUtils;
26 import org.opendaylight.ovsdb.utils.mdsal.openflow.MatchUtils;
27 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.PortNumber;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.address.address.Ipv4Builder;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionKey;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCaseBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActionsBuilder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionKey;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.dst.choice.grouping.dst.choice.DstNxRegCaseBuilder;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.ovs.nx.action.rev140421.OfjNxHashFields;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.ovs.nx.action.rev140421.OfjNxMpAlgorithm;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg0;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg1;
52
53 import com.google.common.collect.Lists;
54
55 public class LoadBalancerService extends AbstractServiceInstance implements LoadBalancerProvider {
56
57     private static final int DEFAULT_FLOW_PRIORITY = 32768;
58     private static final Long SECOND_PASS_REG0_MATCH_VALUE = 1L;
59
60     public LoadBalancerService() {
61         super(Service.LOAD_BALANCER);
62     }
63
64     public LoadBalancerService(Service service) {
65         super(service);
66     }
67
68     @Override
69     public boolean isBridgeInPipeline (String nodeId) {
70         return true;
71     }
72
73     /**
74      * When this method is called, we do the following for minimizing flow updates:
75      * 1. Overwrite the solo multipath rule that applies to all members
76      * 2. Append second pass rule for the header rewriting specific to this member
77      * 3. Append reverse rules specific to this member
78      */
79     @Override
80     public Status programLoadBalancerMemberRules(Node node,
81             LoadBalancerConfiguration lbConfig, LoadBalancerPoolMember member, org.opendaylight.ovsdb.openstack.netvirt.api.Action action) {
82         NodeBuilder nodeBuilder = new NodeBuilder();
83         nodeBuilder.setId((NodeId) node.getID());
84         nodeBuilder.setKey(new NodeKey(nodeBuilder.getId()));
85
86         //Update the multipath rule
87         insertLoadBalancerVIPRulesFirstPass(nodeBuilder, lbConfig);
88
89         if (action.equals(org.opendaylight.ovsdb.openstack.netvirt.api.Action.ADD)) {
90             insertLoadBalancerMemberVIPRulesSecondPass(nodeBuilder, lbConfig.getVip(), member);
91             insertLoadBalancerMemberReverseRules(nodeBuilder, lbConfig.getVip(), member);
92             return new Status(StatusCode.SUCCESS);
93         }
94         /* TODO: Delete single member.
95          * For now, removing a member requires deleting the full LB instance and re-adding
96          */
97         return new Status(StatusCode.NOTIMPLEMENTED);
98     }
99
100     /**
101      * When this method is called, we perform the following:
102      * 1. Write the solo multipath rule that applies to all members
103      * 2. Append second pass rules for the header rewriting for all members
104      * 3. Append reverse rules for all the members, specific to the protocol/port
105      */
106     @Override
107     public Status programLoadBalancerRules(Node node, LoadBalancerConfiguration lbConfig, org.opendaylight.ovsdb.openstack.netvirt.api.Action action) {
108         //for (org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node node:InventoryDataServiceUtil.readAllNodes()) {
109         NodeBuilder nodeBuilder = new NodeBuilder();
110         nodeBuilder.setId((NodeId) node.getID());
111         nodeBuilder.setKey(new NodeKey(nodeBuilder.getId()));
112
113         if (action.equals(org.opendaylight.ovsdb.openstack.netvirt.api.Action.ADD)) {
114             insertLoadBalancerVIPRulesFirstPass(nodeBuilder, lbConfig);
115             insertLoadBalancerVIPRulesSecondPass(nodeBuilder, lbConfig);
116             insertLoadBalancerReverseRules(nodeBuilder, lbConfig);
117             return new Status(StatusCode.SUCCESS);
118         }
119         else if (action.equals(org.opendaylight.ovsdb.openstack.netvirt.api.Action.DELETE)) {
120             removeLoadBalancerVIPRules(nodeBuilder, lbConfig);
121             removeLoadBalancerReverseRules(nodeBuilder, lbConfig);
122             return new Status(StatusCode.SUCCESS);
123         }
124
125         return new Status(StatusCode.NOTIMPLEMENTED);
126     }
127
128     private void insertLoadBalancerVIPRulesFirstPass(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig) {
129         MatchBuilder matchBuilder = new MatchBuilder();
130         FlowBuilder flowBuilder = new FlowBuilder();
131
132         // Match Only VIP
133         MatchUtils.createDstL3IPv4Match(matchBuilder, new Ipv4Prefix(lbConfig.getVip()));
134
135         // Create the OF Actions and Instructions
136         InstructionsBuilder isb = new InstructionsBuilder();
137
138         // Instructions List Stores Individual Instructions
139         List<Instruction> instructions = Lists.newArrayList();
140
141         List<Action> actionList = Lists.newArrayList();
142
143         ActionBuilder ab = new ActionBuilder();
144         ab.setAction(ActionUtils.nxLoadRegAction(new DstNxRegCaseBuilder().setNxReg(NxmNxReg0.class).build(),
145                 BigInteger.valueOf(SECOND_PASS_REG0_MATCH_VALUE)));
146         ab.setOrder(0);
147         ab.setKey(new ActionKey(0));
148         actionList.add(ab.build());
149
150         ab = new ActionBuilder();
151         ab.setAction(ActionUtils.nxMultipathAction(OfjNxHashFields.NXHASHFIELDSSYMMETRICL4,
152                 (Integer)0, OfjNxMpAlgorithm.NXMPALGMODULON, (Integer)lbConfig.getMembers().size(),
153                 (Long)0L, (Integer)0, new DstNxRegCaseBuilder().setNxReg(NxmNxReg1.class).build()));
154         ab.setOrder(1);
155         ab.setKey(new ActionKey(1));
156         actionList.add(ab.build());
157
158         ab = new ActionBuilder();
159         ab.setAction(ActionUtils.nxResubmitAction(null, this.getTable()));
160         ab.setOrder(2);
161         ab.setKey(new ActionKey(2));
162         actionList.add(ab.build());
163
164         // Create an Apply Action
165         ApplyActionsBuilder aab = new ApplyActionsBuilder();
166         aab.setAction(actionList);
167         InstructionBuilder ib = new InstructionBuilder();
168         ib.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(aab.build()).build());
169
170         // Call the InstructionBuilder Methods Containing Actions
171         ib.setOrder(0);
172         ib.setKey(new InstructionKey(0));
173         instructions.add(ib.build());
174
175         // Add InstructionBuilder to the Instruction(s)Builder List
176         isb.setInstruction(instructions);
177
178         // Add InstructionsBuilder to FlowBuilder
179         flowBuilder.setInstructions(isb.build());
180
181         String flowId = "LOADBALANCER_PIPELINE_FLOW_FORWARD1_" + lbConfig.getVip();
182         flowBuilder.setId(new FlowId(flowId));
183         FlowKey key = new FlowKey(new FlowId(flowId));
184         flowBuilder.setMatch(matchBuilder.build());
185         flowBuilder.setPriority(DEFAULT_FLOW_PRIORITY);
186         flowBuilder.setBarrier(true);
187         flowBuilder.setTableId((short) this.getTable());
188         flowBuilder.setKey(key);
189         flowBuilder.setFlowName(flowId);
190         flowBuilder.setHardTimeout(0);
191         flowBuilder.setIdleTimeout(0);
192         writeFlow(flowBuilder, nodeBuilder);
193     }
194
195     /*
196      * Method to program each rule that matches on Reg0 and Reg1 to insert appropriate header rewriting
197      * rules for all members. This function calls insertLoadBalancerMemberVIPRulesSecondPass in turn.
198      * @param nodeBuilder Node to insert rule to
199      * @param lbConfig Configuration for this LoadBalancer instance
200      */
201     private void insertLoadBalancerVIPRulesSecondPass(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig) {
202         Iterator<Map.Entry<String, LoadBalancerPoolMember>> it = lbConfig.getMembers().entrySet().iterator();
203         Map.Entry<String, LoadBalancerPoolMember> entry;
204         while (it.hasNext()) {
205             entry = (Map.Entry<String, LoadBalancerPoolMember>)it.next();
206             insertLoadBalancerMemberVIPRulesSecondPass(nodeBuilder, lbConfig.getVip(), (LoadBalancerPoolMember)entry.getValue());
207             it.remove();
208         }
209     }
210
211     private void insertLoadBalancerMemberVIPRulesSecondPass(NodeBuilder nodeBuilder, String vip, LoadBalancerPoolMember member) {
212         MatchBuilder matchBuilder = new MatchBuilder();
213         FlowBuilder flowBuilder = new FlowBuilder();
214
215         // Match VIP, Reg0==1 and Reg1==Index of member
216         MatchUtils.addNxRegMatch(matchBuilder, new MatchUtils.RegMatch(NxmNxReg0.class, SECOND_PASS_REG0_MATCH_VALUE));
217         MatchUtils.addNxRegMatch(matchBuilder, new MatchUtils.RegMatch(NxmNxReg1.class, (long)member.getIndex()));
218         MatchUtils.createDstL3IPv4Match(matchBuilder, new Ipv4Prefix(vip));
219
220         // Create the OF Actions and Instructions
221         InstructionsBuilder isb = new InstructionsBuilder();
222
223         // Instructions List Stores Individual Instructions
224         List<Instruction> instructions = Lists.newArrayList();
225
226         List<Action> actionList = Lists.newArrayList();
227         ActionBuilder ab = new ActionBuilder();
228         ab.setAction(ActionUtils.setDlDstAction(new MacAddress(member.getMAC())));
229         ab.setOrder(0);
230         ab.setKey(new ActionKey(0));
231         actionList.add(ab.build());
232
233         ab = new ActionBuilder();
234         Ipv4Builder ipb = new Ipv4Builder().setIpv4Address(new Ipv4Prefix(member.getIP()));
235         ab.setAction(ActionUtils.setNwDstAction(ipb.build()));
236         ab.setOrder(1);
237         ab.setKey(new ActionKey(1));
238         actionList.add(ab.build());
239
240         // Create an Apply Action
241         ApplyActionsBuilder aab = new ApplyActionsBuilder();
242         aab.setAction(actionList);
243
244         // Call the InstructionBuilder Methods Containing Actions
245         InstructionBuilder ib = new InstructionBuilder();
246         ib.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(aab.build()).build());
247         ib.setOrder(0);
248         ib.setKey(new InstructionKey(0));
249         instructions.add(ib.build());
250
251         // Call the InstructionBuilder Methods Containing Actions
252         ib = this.getMutablePipelineInstructionBuilder();
253         ib.setOrder(1);
254         ib.setKey(new InstructionKey(1));
255         instructions.add(ib.build());
256
257         // Add InstructionBuilder to the Instruction(s)Builder List
258         isb.setInstruction(instructions);
259
260         // Add InstructionsBuilder to FlowBuilder
261         flowBuilder.setInstructions(isb.build());
262
263         String flowId = "LOADBALANCER_PIPELINE_FORWARD2_" + vip + "_" + member.getIP();
264         flowBuilder.setId(new FlowId(flowId));
265         FlowKey key = new FlowKey(new FlowId(flowId));
266         flowBuilder.setMatch(matchBuilder.build());
267         flowBuilder.setPriority(DEFAULT_FLOW_PRIORITY + 1);
268         flowBuilder.setBarrier(true);
269         flowBuilder.setTableId((short) this.getTable());
270         flowBuilder.setKey(key);
271         flowBuilder.setFlowName(flowId);
272         flowBuilder.setHardTimeout(0);
273         flowBuilder.setIdleTimeout(0);
274         writeFlow(flowBuilder, nodeBuilder);
275     }
276
277     /**
278      * Method to program all reverse rules that matches member {IP, Protocol, Port} for all members.
279      * This function calls insertLoadBalancerMemberReverseRules in turn.
280      * @param nodeBuilder Node to insert rule to
281      * @param lbConfig Configuration for this LoadBalancer instance
282      */
283     private void insertLoadBalancerReverseRules(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig) {
284         Iterator<Map.Entry<String, LoadBalancerPoolMember>> it = lbConfig.getMembers().entrySet().iterator();
285         Map.Entry<String, LoadBalancerPoolMember> entry;
286         while (it.hasNext()) {
287             entry = (Map.Entry<String, LoadBalancerPoolMember>)it.next();
288             insertLoadBalancerMemberReverseRules(nodeBuilder, lbConfig.getVip(), (LoadBalancerPoolMember)entry.getValue());
289             it.remove();
290         }
291     }
292
293     private void insertLoadBalancerMemberReverseRules(NodeBuilder nodeBuilder, String vip, LoadBalancerPoolMember member) {
294         MatchBuilder matchBuilder = new MatchBuilder();
295         FlowBuilder flowBuilder = new FlowBuilder();
296
297         // Match MemberIP, and Protocol/Port
298         MatchUtils.createSrcL3IPv4Match(matchBuilder, new Ipv4Prefix(member.getIP()));
299         if (member.getProtocol().equalsIgnoreCase(LoadBalancerConfiguration.PROTOCOL_HTTP))
300             MatchUtils.createSetSrcTcpMatch(matchBuilder, new PortNumber(LoadBalancerConfiguration.PROTOCOL_HTTP_PORT));
301         else if (member.getProtocol().equalsIgnoreCase(LoadBalancerConfiguration.PROTOCOL_HTTPS))
302             MatchUtils.createSetSrcTcpMatch(matchBuilder, new PortNumber(LoadBalancerConfiguration.PROTOCOL_HTTPS_PORT));
303         else //Not possible
304             return;
305
306         // Create the OF Actions and Instructions
307         InstructionsBuilder isb = new InstructionsBuilder();
308
309         // Instructions List Stores Individual Instructions
310         List<Instruction> instructions = Lists.newArrayList();
311
312         List<Action> actionList = Lists.newArrayList();
313         ActionBuilder ab = new ActionBuilder();
314         Ipv4Builder ipb = new Ipv4Builder().setIpv4Address(new Ipv4Prefix(vip));
315         ab.setAction(ActionUtils.setNwSrcAction(ipb.build()));
316         ab.setOrder(0);
317         ab.setKey(new ActionKey(0));
318         actionList.add(ab.build());
319
320         // Create an Apply Action
321         ApplyActionsBuilder aab = new ApplyActionsBuilder();
322         aab.setAction(actionList);
323
324         // Call the InstructionBuilder Methods Containing Actions
325         InstructionBuilder ib = new InstructionBuilder();
326         ib.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(aab.build()).build());
327         ib.setOrder(0);
328         ib.setKey(new InstructionKey(0));
329         instructions.add(ib.build());
330
331         // Call the InstructionBuilder Methods Containing Actions
332         ib = this.getMutablePipelineInstructionBuilder();
333         ib.setOrder(1);
334         ib.setKey(new InstructionKey(1));
335         instructions.add(ib.build());
336
337         // Add InstructionBuilder to the Instruction(s)Builder List
338         isb.setInstruction(instructions);
339
340         // Add InstructionsBuilder to FlowBuilder
341         flowBuilder.setInstructions(isb.build());
342
343         String flowId = "LOADBALANCER_PIPELINE_REVERSE_" + vip + "_" + member.getIP();
344         flowBuilder.setId(new FlowId(flowId));
345         FlowKey key = new FlowKey(new FlowId(flowId));
346         flowBuilder.setMatch(matchBuilder.build());
347         flowBuilder.setPriority(DEFAULT_FLOW_PRIORITY);
348         flowBuilder.setBarrier(true);
349         flowBuilder.setTableId((short) this.getTable());
350         flowBuilder.setKey(key);
351         flowBuilder.setFlowName(flowId);
352         flowBuilder.setHardTimeout(0);
353         flowBuilder.setIdleTimeout(0);
354         writeFlow(flowBuilder, nodeBuilder);
355     }
356
357     /**
358      * Method to remove all rules that are regarding traffic destined to the VIP
359      * (both first and second pass rules)
360      * @param nodeBuilder NodeBuilder
361      * @param lbConfig LoadBalancerConfiguration
362      */
363     private void removeLoadBalancerVIPRules(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig) {
364         MatchBuilder matchBuilder = new MatchBuilder();
365         FlowBuilder flowBuilder = new FlowBuilder();
366
367         // Match all traffic with nw_dst==VIP
368         MatchUtils.createDstL3IPv4Match(matchBuilder, new Ipv4Prefix(lbConfig.getVip()));
369
370         flowBuilder.setMatch(matchBuilder.build());
371         flowBuilder.setPriority(DEFAULT_FLOW_PRIORITY);
372         flowBuilder.setBarrier(true);
373         flowBuilder.setTableId((short) this.getTable());
374         removeFlow(flowBuilder, nodeBuilder);
375     }
376
377     /**
378      * Method to remove all reverse traffic from LB member VMs
379      * @param nodeBuilder NodeBuilder
380      * @param lbConfig LoadBalancerConfiguration
381      */
382     private void removeLoadBalancerReverseRules(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig) {
383         Iterator<Map.Entry<String, LoadBalancerPoolMember>> it = lbConfig.getMembers().entrySet().iterator();
384         Map.Entry<String, LoadBalancerPoolMember> entry;
385         while (it.hasNext()) {
386             entry = (Map.Entry<String, LoadBalancerPoolMember>)it.next();
387             LoadBalancerPoolMember member = (LoadBalancerPoolMember) entry.getValue();
388
389             MatchBuilder matchBuilder = new MatchBuilder();
390             FlowBuilder flowBuilder = new FlowBuilder();
391
392             // Match MemberIP, and Protocol/Port
393             MatchUtils.createSrcL3IPv4Match(matchBuilder, new Ipv4Prefix(member.getIP()));
394             if (member.getProtocol().equalsIgnoreCase(LoadBalancerConfiguration.PROTOCOL_HTTP))
395                 MatchUtils.createSetSrcTcpMatch(matchBuilder, new PortNumber(LoadBalancerConfiguration.PROTOCOL_HTTP_PORT));
396             else if (member.getProtocol().equalsIgnoreCase(LoadBalancerConfiguration.PROTOCOL_HTTPS))
397                 MatchUtils.createSetSrcTcpMatch(matchBuilder, new PortNumber(LoadBalancerConfiguration.PROTOCOL_HTTPS_PORT));
398             else //Not possible
399                 return;
400
401             flowBuilder.setMatch(matchBuilder.build());
402             flowBuilder.setPriority(DEFAULT_FLOW_PRIORITY);
403             flowBuilder.setBarrier(true);
404             flowBuilder.setTableId((short) this.getTable());
405             removeFlow(flowBuilder, nodeBuilder);
406             it.remove();
407         }
408     }
409 }