2 * Copyright (C) 2014 SDN Hub, LLC.
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 * Authors : Srini Seetharaman, Madhu Venugopal
10 package org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.services;
12 import java.math.BigInteger;
13 import java.util.List;
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;
57 import com.google.common.collect.Lists;
58 public class LoadBalancerService extends AbstractServiceInstance implements LoadBalancerProvider {
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;
65 private static final Class<? extends NxmNxReg> REG_FIELD_A = NxmNxReg1.class;
66 private static final Class<? extends NxmNxReg> REG_FIELD_B = NxmNxReg2.class;
68 public LoadBalancerService() {
69 super(Service.LOAD_BALANCER);
72 public LoadBalancerService(Service service) {
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
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);
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());
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()));
96 //Update the multipath rule
97 manageLoadBalancerVIPRulesFirstPass(nodeBuilder, lbConfig, true);
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);
104 /* TODO: Delete single member.
105 * For now, removing a member requires deleting the full LB instance and re-adding
107 return new Status(StatusCode.NOTIMPLEMENTED);
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
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);
122 logger.debug("Performing {} rules for VIP {} and {} members", action, lbConfig.getVip(), lbConfig.getMembers().size());
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()));
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);
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);
141 return new Status(StatusCode.NOTIMPLEMENTED);
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
151 private void manageLoadBalancerVIPRulesFirstPass(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig, boolean write) {
152 MatchBuilder matchBuilder = new MatchBuilder();
153 FlowBuilder flowBuilder = new FlowBuilder();
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));
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);
172 // Create the OF Actions and Instructions
173 InstructionsBuilder isb = new InstructionsBuilder();
175 // Instructions List Stores Individual Instructions
176 List<Instruction> instructions = Lists.newArrayList();
178 List<Action> actionList = Lists.newArrayList();
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)));
184 ab.setKey(new ActionKey(0));
185 actionList.add(ab.build());
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(),
194 ab.setKey(new ActionKey(1));
195 actionList.add(ab.build());
197 ab = new ActionBuilder();
198 ab.setAction(ActionUtils.nxResubmitAction(null, this.getTable()));
200 ab.setKey(new ActionKey(2));
201 actionList.add(ab.build());
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());
209 // Call the InstructionBuilder Methods Containing Actions
211 ib.setKey(new InstructionKey(0));
212 instructions.add(ib.build());
214 // Add InstructionBuilder to the Instruction(s)Builder List
215 isb.setInstruction(instructions);
217 // Add InstructionsBuilder to FlowBuilder
218 flowBuilder.setInstructions(isb.build());
220 writeFlow(flowBuilder, nodeBuilder);
223 removeFlow(flowBuilder, nodeBuilder);
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
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);
240 private void manageLoadBalancerMemberVIPRulesSecondPass(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig, LoadBalancerPoolMember member, boolean write) {
241 String vip = lbConfig.getVip();
243 MatchBuilder matchBuilder = new MatchBuilder();
244 FlowBuilder flowBuilder = new FlowBuilder();
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()));
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);
264 // Create the OF Actions and Instructions
265 InstructionsBuilder isb = new InstructionsBuilder();
267 // Instructions List Stores Individual Instructions
268 List<Instruction> instructions = Lists.newArrayList();
270 List<Action> actionList = Lists.newArrayList();
271 ActionBuilder ab = new ActionBuilder();
272 ab.setAction(ActionUtils.setDlDstAction(new MacAddress(member.getMAC())));
274 ab.setKey(new ActionKey(0));
275 actionList.add(ab.build());
277 ab = new ActionBuilder();
278 Ipv4Builder ipb = new Ipv4Builder().setIpv4Address(new Ipv4Prefix(member.getIP()));
279 ab.setAction(ActionUtils.setNwDstAction(ipb.build()));
281 ab.setKey(new ActionKey(1));
282 actionList.add(ab.build());
284 // Create an Apply Action
285 ApplyActionsBuilder aab = new ApplyActionsBuilder();
286 aab.setAction(actionList);
288 // Call the InstructionBuilder Methods Containing Actions
289 InstructionBuilder ib = new InstructionBuilder();
290 ib.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(aab.build()).build());
292 ib.setKey(new InstructionKey(0));
293 instructions.add(ib.build());
295 // Call the InstructionBuilder Methods Containing Actions
296 ib = this.getMutablePipelineInstructionBuilder();
298 ib.setKey(new InstructionKey(1));
299 instructions.add(ib.build());
301 // Add InstructionBuilder to the Instruction(s)Builder List
302 isb.setInstruction(instructions);
304 // Add InstructionsBuilder to FlowBuilder
305 flowBuilder.setInstructions(isb.build());
307 writeFlow(flowBuilder, nodeBuilder);
310 removeFlow(flowBuilder, nodeBuilder);
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
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);
326 private void manageLoadBalancerMemberReverseRules(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig,
327 LoadBalancerPoolMember member, boolean write) {
328 String vip = lbConfig.getVip();
329 String vmac = lbConfig.getVmac();
331 MatchBuilder matchBuilder = new MatchBuilder();
332 FlowBuilder flowBuilder = new FlowBuilder();
334 // Match MemberIP, and Protocol/Port
335 MatchUtils.createSrcL3IPv4Match(matchBuilder, new Ipv4Prefix(member.getIP()));
336 MatchUtils.createSetSrcTcpMatch(matchBuilder, new PortNumber(member.getPort()));
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);
351 // Create the OF Actions and Instructions
352 InstructionsBuilder isb = new InstructionsBuilder();
354 // Instructions List Stores Individual Instructions
355 List<Instruction> instructions = Lists.newArrayList();
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()));
362 ab.setKey(new ActionKey(0));
363 actionList.add(ab.build());
365 /* If a dummy MAC is assigned to the VIP, we use that as the
366 * source MAC for the reverse traffic.
369 ab = new ActionBuilder();
370 ab.setAction(ActionUtils.setDlDstAction(new MacAddress(vmac)));
372 ab.setKey(new ActionKey(1));
373 actionList.add(ab.build());
376 // Create an Apply Action
377 ApplyActionsBuilder aab = new ApplyActionsBuilder();
378 aab.setAction(actionList);
380 // Call the InstructionBuilder Methods Containing Actions
381 InstructionBuilder ib = new InstructionBuilder();
382 ib.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(aab.build()).build());
384 ib.setKey(new InstructionKey(0));
385 instructions.add(ib.build());
387 // Call the InstructionBuilder Methods Containing Actions
388 ib = this.getMutablePipelineInstructionBuilder();
390 ib.setKey(new InstructionKey(1));
391 instructions.add(ib.build());
393 // Add InstructionBuilder to the Instruction(s)Builder List
394 isb.setInstruction(instructions);
396 // Add InstructionsBuilder to FlowBuilder
397 flowBuilder.setInstructions(isb.build());
399 writeFlow(flowBuilder, nodeBuilder);
402 removeFlow(flowBuilder, nodeBuilder);