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.ovsdb.openstack.netvirt.NetworkHandler;
17 import org.opendaylight.ovsdb.openstack.netvirt.api.Constants;
18 import org.opendaylight.ovsdb.openstack.netvirt.api.LoadBalancerConfiguration;
19 import org.opendaylight.ovsdb.openstack.netvirt.api.LoadBalancerConfiguration.LoadBalancerPoolMember;
20 import org.opendaylight.ovsdb.openstack.netvirt.api.LoadBalancerProvider;
21 import org.opendaylight.ovsdb.openstack.netvirt.api.Status;
22 import org.opendaylight.ovsdb.openstack.netvirt.api.StatusCode;
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.PortNumber;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionKey;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.address.address.Ipv4Builder;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCaseBuilder;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActionsBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionKey;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
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.l2.types.rev130827.VlanId;
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.openflowjava.nx.action.rev140421.OfjNxHashFields;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.action.rev140421.OfjNxMpAlgorithm;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
57 import com.google.common.collect.Lists;
59 public class LoadBalancerService extends AbstractServiceInstance implements LoadBalancerProvider {
61 private static final Logger logger = LoggerFactory.getLogger(LoadBalancerProvider.class);
62 private static final int DEFAULT_FLOW_PRIORITY = 32768;
63 private static final Long FIRST_PASS_REGA_MATCH_VALUE = 0L;
64 private static final Long SECOND_PASS_REGA_MATCH_VALUE = 1L;
66 private static final Class<? extends NxmNxReg> REG_FIELD_A = NxmNxReg1.class;
67 private static final Class<? extends NxmNxReg> REG_FIELD_B = NxmNxReg2.class;
69 public LoadBalancerService() {
70 super(Service.LOAD_BALANCER);
73 public LoadBalancerService(Service service) {
78 * When this method is called, we do the following for minimizing flow updates:
79 * 1. Overwrite the solo multipath rule that applies to all members
80 * 2. Append second pass rule for the header rewriting specific to this member
81 * 3. Append reverse rules specific to this member
84 public Status programLoadBalancerPoolMemberRules(Node node,
85 LoadBalancerConfiguration lbConfig, LoadBalancerPoolMember member, org.opendaylight.ovsdb.openstack.netvirt.api.Action action) {
86 if (lbConfig == null || member == null) {
87 logger.error("Null value for LB config {} or Member {}", lbConfig, member);
88 return new Status(StatusCode.BADREQUEST);
90 if (!lbConfig.isValid()) {
91 logger.error("LB config is invalid: {}", lbConfig);
92 return new Status(StatusCode.BADREQUEST);
94 logger.debug("Performing {} rules for member {} with index {} on LB with VIP {} and total members {}",
95 action, member.getIP(), member.getIndex(), lbConfig.getVip(), lbConfig.getMembers().size());
97 NodeBuilder nodeBuilder = new NodeBuilder();
98 nodeBuilder.setId(new NodeId(Constants.OPENFLOW_NODE_PREFIX + node.getId().getValue()));
99 nodeBuilder.setKey(new NodeKey(nodeBuilder.getId()));
101 //Update the multipath rule
102 manageLoadBalancerVIPRulesFirstPass(nodeBuilder, lbConfig, true);
104 if (action.equals(org.opendaylight.ovsdb.openstack.netvirt.api.Action.ADD)) {
105 manageLoadBalancerMemberVIPRulesSecondPass(nodeBuilder, lbConfig, member, true);
106 manageLoadBalancerMemberReverseRules(nodeBuilder, lbConfig, member, true);
107 return new Status(StatusCode.SUCCESS);
109 /* TODO: Delete single member.
110 * For now, removing a member requires deleting the full LB instance and re-adding
112 return new Status(StatusCode.NOTIMPLEMENTED);
116 * When this method is called, we perform the following:
117 * 1. Write the solo multipath rule that applies to all members
118 * 2. Append second pass rules for the header rewriting for all members
119 * 3. Append reverse rules for all the members, specific to the protocol/port
122 public Status programLoadBalancerRules(Node node, LoadBalancerConfiguration lbConfig, org.opendaylight.ovsdb.openstack.netvirt.api.Action action) {
123 if (lbConfig == null) {
124 logger.error("LB config is invalid: {}", lbConfig);
125 return new Status(StatusCode.BADREQUEST);
127 if (!lbConfig.isValid()) {
128 logger.error("LB config is invalid: {}", lbConfig);
129 return new Status(StatusCode.BADREQUEST);
131 logger.debug("Performing {} rules for VIP {} and {} members", action, lbConfig.getVip(), lbConfig.getMembers().size());
133 NodeBuilder nodeBuilder = new NodeBuilder();
134 nodeBuilder.setId(new NodeId(Constants.OPENFLOW_NODE_PREFIX + node.getId().getValue()));
135 nodeBuilder.setKey(new NodeKey(nodeBuilder.getId()));
137 if (action.equals(org.opendaylight.ovsdb.openstack.netvirt.api.Action.ADD)) {
138 manageLoadBalancerVIPRulesFirstPass(nodeBuilder, lbConfig, true);
139 manageLoadBalancerVIPRulesSecondPass(nodeBuilder, lbConfig, true);
140 manageLoadBalancerReverseRules(nodeBuilder, lbConfig, true);
141 return new Status(StatusCode.SUCCESS);
143 else if (action.equals(org.opendaylight.ovsdb.openstack.netvirt.api.Action.DELETE)) {
144 manageLoadBalancerVIPRulesFirstPass(nodeBuilder, lbConfig, false);
145 manageLoadBalancerVIPRulesSecondPass(nodeBuilder, lbConfig, false);
146 manageLoadBalancerReverseRules(nodeBuilder, lbConfig, false);
147 return new Status(StatusCode.SUCCESS);
150 return new Status(StatusCode.NOTIMPLEMENTED);
154 * Method to insert/remove default rule for traffic destined to the VIP and no
155 * server selection performed yet
156 * @param nodeBuilder NodeBuilder
157 * @param lbConfig LoadBalancerConfiguration
158 * @param write Boolean to indicate of the flow is to be inserted or removed
160 private void manageLoadBalancerVIPRulesFirstPass(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig, boolean write) {
161 MatchBuilder matchBuilder = new MatchBuilder();
162 FlowBuilder flowBuilder = new FlowBuilder();
164 // Match Tunnel-ID, VIP, and Reg0==0
165 if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VXLAN) ||
166 lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_GRE))
167 MatchUtils.createTunnelIDMatch(matchBuilder, new BigInteger(lbConfig.getProviderSegmentationId()));
168 else if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VLAN))
169 MatchUtils.createVlanIdMatch(matchBuilder, new VlanId(Integer.valueOf(lbConfig.getProviderSegmentationId())), true);
171 return; //Should not get here. TODO: Other types
173 MatchUtils.createDstL3IPv4Match(matchBuilder, MatchUtils.iPv4PrefixFromIPv4Address(lbConfig.getVip()));
174 MatchUtils.addNxRegMatch(matchBuilder, new MatchUtils.RegMatch(REG_FIELD_A, FIRST_PASS_REGA_MATCH_VALUE));
176 String flowId = "LOADBALANCER_FORWARD_FLOW1_" + lbConfig.getVip();
177 flowBuilder.setId(new FlowId(flowId));
178 FlowKey key = new FlowKey(new FlowId(flowId));
179 flowBuilder.setMatch(matchBuilder.build());
180 flowBuilder.setPriority(DEFAULT_FLOW_PRIORITY);
181 flowBuilder.setBarrier(true);
182 flowBuilder.setTableId(this.getTable());
183 flowBuilder.setKey(key);
184 flowBuilder.setFlowName(flowId);
185 flowBuilder.setHardTimeout(0);
186 flowBuilder.setIdleTimeout(0);
189 // Create the OF Actions and Instructions
190 InstructionsBuilder isb = new InstructionsBuilder();
192 // Instructions List Stores Individual Instructions
193 List<Instruction> instructions = Lists.newArrayList();
195 List<Action> actionList = Lists.newArrayList();
197 ActionBuilder ab = new ActionBuilder();
198 ab.setAction(ActionUtils.nxLoadRegAction(new DstNxRegCaseBuilder().setNxReg(REG_FIELD_A).build(),
199 BigInteger.valueOf(SECOND_PASS_REGA_MATCH_VALUE)));
201 ab.setKey(new ActionKey(0));
202 actionList.add(ab.build());
204 ab = new ActionBuilder();
205 ab.setAction(ActionUtils.nxMultipathAction(OfjNxHashFields.NXHASHFIELDSSYMMETRICL4,
206 0, OfjNxMpAlgorithm.NXMPALGMODULON,
207 lbConfig.getMembers().size()-1, //By Nicira-Ext spec, this field is max_link minus 1
208 0L, new DstNxRegCaseBuilder().setNxReg(REG_FIELD_B).build(),
211 ab.setKey(new ActionKey(1));
212 actionList.add(ab.build());
214 ab = new ActionBuilder();
215 ab.setAction(ActionUtils.nxResubmitAction(null, this.getTable()));
217 ab.setKey(new ActionKey(2));
218 actionList.add(ab.build());
220 // Create an Apply Action
221 ApplyActionsBuilder aab = new ApplyActionsBuilder();
222 aab.setAction(actionList);
223 InstructionBuilder ib = new InstructionBuilder();
224 ib.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(aab.build()).build());
226 // Call the InstructionBuilder Methods Containing Actions
228 ib.setKey(new InstructionKey(0));
229 instructions.add(ib.build());
231 // Add InstructionBuilder to the Instruction(s)Builder List
232 isb.setInstruction(instructions);
234 // Add InstructionsBuilder to FlowBuilder
235 flowBuilder.setInstructions(isb.build());
237 writeFlow(flowBuilder, nodeBuilder);
240 removeFlow(flowBuilder, nodeBuilder);
245 * Method to program each rule that matches on Reg0 and Reg1 to insert appropriate header rewriting
246 * rules for all members. This function calls manageLoadBalancerMemberVIPRulesSecondPass in turn.
247 * @param nodeBuilder Node to insert rule to
248 * @param lbConfig Configuration for this LoadBalancer instance
249 * @param write Boolean to indicate of the flow is to be inserted or removed
251 private void manageLoadBalancerVIPRulesSecondPass(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig, boolean write) {
252 for(Map.Entry<String, LoadBalancerPoolMember> entry : lbConfig.getMembers().entrySet()){
253 manageLoadBalancerMemberVIPRulesSecondPass(nodeBuilder, lbConfig, entry.getValue(), write);
257 private void manageLoadBalancerMemberVIPRulesSecondPass(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig, LoadBalancerPoolMember member, boolean write) {
258 String vip = lbConfig.getVip();
260 MatchBuilder matchBuilder = new MatchBuilder();
261 FlowBuilder flowBuilder = new FlowBuilder();
263 // Match Tunnel-ID, VIP, Reg0==1 and Reg1==Index of member
264 if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VXLAN) ||
265 lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_GRE))
266 MatchUtils.createTunnelIDMatch(matchBuilder, new BigInteger(lbConfig.getProviderSegmentationId()));
267 else if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VLAN))
268 MatchUtils.createVlanIdMatch(matchBuilder, new VlanId(Integer.valueOf(lbConfig.getProviderSegmentationId())), true);
270 return; //Should not get here. TODO: Other types
272 MatchUtils.createDstL3IPv4Match(matchBuilder, MatchUtils.iPv4PrefixFromIPv4Address(vip));
273 MatchUtils.addNxRegMatch(matchBuilder, new MatchUtils.RegMatch(REG_FIELD_A, SECOND_PASS_REGA_MATCH_VALUE),
274 new MatchUtils.RegMatch(REG_FIELD_B, (long)member.getIndex()));
276 String flowId = "LOADBALANCER_FORWARD_FLOW2_" + vip + "_" + member.getIP();
277 flowBuilder.setId(new FlowId(flowId));
278 FlowKey key = new FlowKey(new FlowId(flowId));
279 flowBuilder.setMatch(matchBuilder.build());
280 flowBuilder.setPriority(DEFAULT_FLOW_PRIORITY+1);
281 flowBuilder.setBarrier(true);
282 flowBuilder.setTableId(this.getTable());
283 flowBuilder.setKey(key);
284 flowBuilder.setFlowName(flowId);
285 flowBuilder.setHardTimeout(0);
286 flowBuilder.setIdleTimeout(0);
289 // Create the OF Actions and Instructions
290 InstructionsBuilder isb = new InstructionsBuilder();
292 // Instructions List Stores Individual Instructions
293 List<Instruction> instructions = Lists.newArrayList();
295 List<Action> actionList = Lists.newArrayList();
296 ActionBuilder ab = new ActionBuilder();
297 ab.setAction(ActionUtils.setDlDstAction(new MacAddress(member.getMAC())));
299 ab.setKey(new ActionKey(0));
300 actionList.add(ab.build());
302 ab = new ActionBuilder();
303 Ipv4Builder ipb = new Ipv4Builder().setIpv4Address(MatchUtils.iPv4PrefixFromIPv4Address(member.getIP()));
304 ab.setAction(ActionUtils.setNwDstAction(ipb.build()));
306 ab.setKey(new ActionKey(1));
307 actionList.add(ab.build());
309 // Create an Apply Action
310 ApplyActionsBuilder aab = new ApplyActionsBuilder();
311 aab.setAction(actionList);
313 // Call the InstructionBuilder Methods Containing Actions
314 InstructionBuilder ib = new InstructionBuilder();
315 ib.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(aab.build()).build());
317 ib.setKey(new InstructionKey(0));
318 instructions.add(ib.build());
320 // Call the InstructionBuilder Methods Containing Actions
321 ib = this.getMutablePipelineInstructionBuilder();
323 ib.setKey(new InstructionKey(1));
324 instructions.add(ib.build());
326 // Add InstructionBuilder to the Instruction(s)Builder List
327 isb.setInstruction(instructions);
329 // Add InstructionsBuilder to FlowBuilder
330 flowBuilder.setInstructions(isb.build());
332 writeFlow(flowBuilder, nodeBuilder);
335 removeFlow(flowBuilder, nodeBuilder);
340 * Method to program all reverse rules that matches member {IP, Protocol, Port} for all members.
341 * This function calls manageLoadBalancerMemberReverseRules in turn.
342 * @param nodeBuilder Node to insert rule to
343 * @param lbConfig Configuration for this LoadBalancer instance
345 private void manageLoadBalancerReverseRules(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig, boolean write) {
346 for(Map.Entry<String, LoadBalancerPoolMember> entry : lbConfig.getMembers().entrySet()){
347 manageLoadBalancerMemberReverseRules(nodeBuilder, lbConfig, entry.getValue(), write);
351 private void manageLoadBalancerMemberReverseRules(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig,
352 LoadBalancerPoolMember member, boolean write) {
354 String vip = lbConfig.getVip();
355 String vmac = lbConfig.getVmac();
357 MatchBuilder matchBuilder = new MatchBuilder();
358 FlowBuilder flowBuilder = new FlowBuilder();
360 // Match Tunnel-ID, MemberIP, and Protocol/Port
361 if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VXLAN) ||
362 lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_GRE))
363 MatchUtils.createTunnelIDMatch(matchBuilder, new BigInteger(lbConfig.getProviderSegmentationId()));
364 else if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VLAN))
365 MatchUtils.createVlanIdMatch(matchBuilder, new VlanId(Integer.valueOf(lbConfig.getProviderSegmentationId())), true);
367 return; //Should not get here. TODO: Other types
369 MatchUtils.createSrcL3IPv4Match(matchBuilder, MatchUtils.iPv4PrefixFromIPv4Address(member.getIP()));
370 MatchUtils.createSetSrcTcpMatch(matchBuilder, new PortNumber(member.getPort()));
372 String flowId = "LOADBALANCER_REVERSE_FLOW_" + vip + "_" + member.getIP();
373 flowBuilder.setId(new FlowId(flowId));
374 FlowKey key = new FlowKey(new FlowId(flowId));
375 flowBuilder.setMatch(matchBuilder.build());
376 flowBuilder.setPriority(DEFAULT_FLOW_PRIORITY);
377 flowBuilder.setBarrier(true);
378 flowBuilder.setTableId(this.getTable());
379 flowBuilder.setKey(key);
380 flowBuilder.setFlowName(flowId);
381 flowBuilder.setHardTimeout(0);
382 flowBuilder.setIdleTimeout(0);
385 // Create the OF Actions and Instructions
386 InstructionsBuilder isb = new InstructionsBuilder();
388 // Instructions List Stores Individual Instructions
389 List<Instruction> instructions = Lists.newArrayList();
391 List<Action> actionList = Lists.newArrayList();
392 ActionBuilder ab = new ActionBuilder();
393 Ipv4Builder ipb = new Ipv4Builder().setIpv4Address(MatchUtils.iPv4PrefixFromIPv4Address(vip));
394 ab.setAction(ActionUtils.setNwSrcAction(ipb.build()));
396 ab.setKey(new ActionKey(0));
397 actionList.add(ab.build());
399 /* If a dummy MAC is assigned to the VIP, we use that as the
400 * source MAC for the reverse traffic.
403 ab = new ActionBuilder();
404 ab.setAction(ActionUtils.setDlDstAction(new MacAddress(vmac)));
406 ab.setKey(new ActionKey(1));
407 actionList.add(ab.build());
410 // Create an Apply Action
411 ApplyActionsBuilder aab = new ApplyActionsBuilder();
412 aab.setAction(actionList);
414 // Call the InstructionBuilder Methods Containing Actions
415 InstructionBuilder ib = new InstructionBuilder();
416 ib.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(aab.build()).build());
418 ib.setKey(new InstructionKey(0));
419 instructions.add(ib.build());
421 // Call the InstructionBuilder Methods Containing Actions
422 ib = this.getMutablePipelineInstructionBuilder();
424 ib.setKey(new InstructionKey(1));
425 instructions.add(ib.build());
427 // Add InstructionBuilder to the Instruction(s)Builder List
428 isb.setInstruction(instructions);
430 // Add InstructionsBuilder to FlowBuilder
431 flowBuilder.setInstructions(isb.build());
433 writeFlow(flowBuilder, nodeBuilder);
436 removeFlow(flowBuilder, nodeBuilder);