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