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.NetworkHandler;
21 import org.opendaylight.ovsdb.openstack.netvirt.api.Constants;
22 import org.opendaylight.ovsdb.openstack.netvirt.api.LoadBalancerConfiguration;
23 import org.opendaylight.ovsdb.openstack.netvirt.api.LoadBalancerConfiguration.LoadBalancerPoolMember;
24 import org.opendaylight.ovsdb.openstack.netvirt.api.LoadBalancerProvider;
25 import org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.AbstractServiceInstance;
26 import org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.Service;
27 import org.opendaylight.ovsdb.utils.mdsal.openflow.ActionUtils;
28 import org.opendaylight.ovsdb.utils.mdsal.openflow.MatchUtils;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.PortNumber;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionKey;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.address.address.Ipv4Builder;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCaseBuilder;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActionsBuilder;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionKey;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.VlanId;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg1;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg2;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.dst.choice.grouping.dst.choice.DstNxRegCaseBuilder;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.ovs.nx.action.rev140421.OfjNxHashFields;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.ovs.nx.action.rev140421.OfjNxMpAlgorithm;
56 import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
59 import com.google.common.collect.Lists;
60 public class LoadBalancerService extends AbstractServiceInstance implements LoadBalancerProvider {
62 private static final Logger logger = LoggerFactory.getLogger(LoadBalancerProvider.class);
63 private static final int DEFAULT_FLOW_PRIORITY = 32768;
64 private static final Long FIRST_PASS_REGA_MATCH_VALUE = 0L;
65 private static final Long SECOND_PASS_REGA_MATCH_VALUE = 1L;
67 private static final Class<? extends NxmNxReg> REG_FIELD_A = NxmNxReg1.class;
68 private static final Class<? extends NxmNxReg> REG_FIELD_B = NxmNxReg2.class;
70 public LoadBalancerService() {
71 super(Service.LOAD_BALANCER);
74 public LoadBalancerService(Service service) {
79 * When this method is called, we do the following for minimizing flow updates:
80 * 1. Overwrite the solo multipath rule that applies to all members
81 * 2. Append second pass rule for the header rewriting specific to this member
82 * 3. Append reverse rules specific to this member
85 public Status programLoadBalancerPoolMemberRules(Node node,
86 LoadBalancerConfiguration lbConfig, LoadBalancerPoolMember member, org.opendaylight.ovsdb.openstack.netvirt.api.Action action) {
87 if (lbConfig == null || member == null) {
88 logger.error("Null value for LB config {} or Member {}", lbConfig, member);
89 return new Status(StatusCode.BADREQUEST);
91 if (!lbConfig.isValid()) {
92 logger.error("LB config is invalid: {}", lbConfig);
93 return new Status(StatusCode.BADREQUEST);
95 if (!node.getType().equals(NodeIDType.OPENFLOW)) {
96 logger.trace("Ignoring non-OpenFlow node {} from flow programming", node);
97 return new Status(StatusCode.BADREQUEST);
99 logger.debug("Performing {} rules for member {} with index {} on LB with VIP {} and total members {}",
100 action, member.getIP(), member.getIndex(), lbConfig.getVip(), lbConfig.getMembers().size());
102 NodeBuilder nodeBuilder = new NodeBuilder();
103 nodeBuilder.setId(new NodeId(Constants.OPENFLOW_NODE_PREFIX + String.valueOf(node.getID())));
104 nodeBuilder.setKey(new NodeKey(nodeBuilder.getId()));
106 //Update the multipath rule
107 manageLoadBalancerVIPRulesFirstPass(nodeBuilder, lbConfig, true);
109 if (action.equals(org.opendaylight.ovsdb.openstack.netvirt.api.Action.ADD)) {
110 manageLoadBalancerMemberVIPRulesSecondPass(nodeBuilder, lbConfig, member, true);
111 manageLoadBalancerMemberReverseRules(nodeBuilder, lbConfig, member, true);
112 return new Status(StatusCode.SUCCESS);
114 /* TODO: Delete single member.
115 * For now, removing a member requires deleting the full LB instance and re-adding
117 return new Status(StatusCode.NOTIMPLEMENTED);
121 * When this method is called, we perform the following:
122 * 1. Write the solo multipath rule that applies to all members
123 * 2. Append second pass rules for the header rewriting for all members
124 * 3. Append reverse rules for all the members, specific to the protocol/port
127 public Status programLoadBalancerRules(Node node, LoadBalancerConfiguration lbConfig, org.opendaylight.ovsdb.openstack.netvirt.api.Action action) {
128 if (lbConfig == null) {
129 logger.error("LB config is invalid: {}", lbConfig);
130 return new Status(StatusCode.BADREQUEST);
132 if (!lbConfig.isValid()) {
133 logger.error("LB config is invalid: {}", lbConfig);
134 return new Status(StatusCode.BADREQUEST);
136 if (!node.getType().equals(NodeIDType.OPENFLOW)) {
137 logger.trace("Ignoring non-OpenFlow node {} from flow programming", node);
138 return new Status(StatusCode.BADREQUEST);
140 logger.debug("Performing {} rules for VIP {} and {} members", action, lbConfig.getVip(), lbConfig.getMembers().size());
142 NodeBuilder nodeBuilder = new NodeBuilder();
143 nodeBuilder.setId(new NodeId(Constants.OPENFLOW_NODE_PREFIX + String.valueOf(node.getID())));
144 nodeBuilder.setKey(new NodeKey(nodeBuilder.getId()));
146 if (action.equals(org.opendaylight.ovsdb.openstack.netvirt.api.Action.ADD)) {
147 manageLoadBalancerVIPRulesFirstPass(nodeBuilder, lbConfig, true);
148 manageLoadBalancerVIPRulesSecondPass(nodeBuilder, lbConfig, true);
149 manageLoadBalancerReverseRules(nodeBuilder, lbConfig, true);
150 return new Status(StatusCode.SUCCESS);
152 else if (action.equals(org.opendaylight.ovsdb.openstack.netvirt.api.Action.DELETE)) {
153 manageLoadBalancerVIPRulesFirstPass(nodeBuilder, lbConfig, false);
154 manageLoadBalancerVIPRulesSecondPass(nodeBuilder, lbConfig, false);
155 manageLoadBalancerReverseRules(nodeBuilder, lbConfig, false);
156 return new Status(StatusCode.SUCCESS);
159 return new Status(StatusCode.NOTIMPLEMENTED);
163 * Method to insert/remove default rule for traffic destined to the VIP and no
164 * server selection performed yet
165 * @param nodeBuilder NodeBuilder
166 * @param lbConfig LoadBalancerConfiguration
167 * @param write Boolean to indicate of the flow is to be inserted or removed
169 private void manageLoadBalancerVIPRulesFirstPass(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig, boolean write) {
170 MatchBuilder matchBuilder = new MatchBuilder();
171 FlowBuilder flowBuilder = new FlowBuilder();
173 // Match Tunnel-ID, VIP, and Reg0==0
174 if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VXLAN) ||
175 lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_GRE))
176 MatchUtils.createTunnelIDMatch(matchBuilder, new BigInteger(lbConfig.getProviderSegmentationId()));
177 else if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VLAN))
178 MatchUtils.createVlanIdMatch(matchBuilder, new VlanId(Integer.valueOf(lbConfig.getProviderSegmentationId())), true);
180 return; //Should not get here. TODO: Other types
182 MatchUtils.createDstL3IPv4Match(matchBuilder, new Ipv4Prefix(lbConfig.getVip()));
183 MatchUtils.addNxRegMatch(matchBuilder, new MatchUtils.RegMatch(REG_FIELD_A, FIRST_PASS_REGA_MATCH_VALUE));
185 String flowId = "LOADBALANCER_FORWARD_FLOW1_" + lbConfig.getVip();
186 flowBuilder.setId(new FlowId(flowId));
187 FlowKey key = new FlowKey(new FlowId(flowId));
188 flowBuilder.setMatch(matchBuilder.build());
189 flowBuilder.setPriority(DEFAULT_FLOW_PRIORITY);
190 flowBuilder.setBarrier(true);
191 flowBuilder.setTableId(this.getTable());
192 flowBuilder.setKey(key);
193 flowBuilder.setFlowName(flowId);
194 flowBuilder.setHardTimeout(0);
195 flowBuilder.setIdleTimeout(0);
198 // Create the OF Actions and Instructions
199 InstructionsBuilder isb = new InstructionsBuilder();
201 // Instructions List Stores Individual Instructions
202 List<Instruction> instructions = Lists.newArrayList();
204 List<Action> actionList = Lists.newArrayList();
206 ActionBuilder ab = new ActionBuilder();
207 ab.setAction(ActionUtils.nxLoadRegAction(new DstNxRegCaseBuilder().setNxReg(REG_FIELD_A).build(),
208 BigInteger.valueOf(SECOND_PASS_REGA_MATCH_VALUE)));
210 ab.setKey(new ActionKey(0));
211 actionList.add(ab.build());
213 ab = new ActionBuilder();
214 ab.setAction(ActionUtils.nxMultipathAction(OfjNxHashFields.NXHASHFIELDSSYMMETRICL4,
215 0, OfjNxMpAlgorithm.NXMPALGMODULON,
216 lbConfig.getMembers().size()-1, //By Nicira-Ext spec, this field is max_link minus 1
217 0L, new DstNxRegCaseBuilder().setNxReg(REG_FIELD_B).build(),
220 ab.setKey(new ActionKey(1));
221 actionList.add(ab.build());
223 ab = new ActionBuilder();
224 ab.setAction(ActionUtils.nxResubmitAction(null, this.getTable()));
226 ab.setKey(new ActionKey(2));
227 actionList.add(ab.build());
229 // Create an Apply Action
230 ApplyActionsBuilder aab = new ApplyActionsBuilder();
231 aab.setAction(actionList);
232 InstructionBuilder ib = new InstructionBuilder();
233 ib.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(aab.build()).build());
235 // Call the InstructionBuilder Methods Containing Actions
237 ib.setKey(new InstructionKey(0));
238 instructions.add(ib.build());
240 // Add InstructionBuilder to the Instruction(s)Builder List
241 isb.setInstruction(instructions);
243 // Add InstructionsBuilder to FlowBuilder
244 flowBuilder.setInstructions(isb.build());
246 writeFlow(flowBuilder, nodeBuilder);
249 removeFlow(flowBuilder, nodeBuilder);
254 * Method to program each rule that matches on Reg0 and Reg1 to insert appropriate header rewriting
255 * rules for all members. This function calls manageLoadBalancerMemberVIPRulesSecondPass in turn.
256 * @param nodeBuilder Node to insert rule to
257 * @param lbConfig Configuration for this LoadBalancer instance
258 * @param write Boolean to indicate of the flow is to be inserted or removed
260 private void manageLoadBalancerVIPRulesSecondPass(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig, boolean write) {
261 for(Map.Entry<String, LoadBalancerPoolMember> entry : lbConfig.getMembers().entrySet()){
262 manageLoadBalancerMemberVIPRulesSecondPass(nodeBuilder, lbConfig, entry.getValue(), write);
266 private void manageLoadBalancerMemberVIPRulesSecondPass(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig, LoadBalancerPoolMember member, boolean write) {
267 String vip = lbConfig.getVip();
269 MatchBuilder matchBuilder = new MatchBuilder();
270 FlowBuilder flowBuilder = new FlowBuilder();
272 // Match Tunnel-ID, VIP, Reg0==1 and Reg1==Index of member
273 if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VXLAN) ||
274 lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_GRE))
275 MatchUtils.createTunnelIDMatch(matchBuilder, new BigInteger(lbConfig.getProviderSegmentationId()));
276 else if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VLAN))
277 MatchUtils.createVlanIdMatch(matchBuilder, new VlanId(Integer.valueOf(lbConfig.getProviderSegmentationId())), true);
279 return; //Should not get here. TODO: Other types
281 MatchUtils.createDstL3IPv4Match(matchBuilder, new Ipv4Prefix(vip));
282 MatchUtils.addNxRegMatch(matchBuilder, new MatchUtils.RegMatch(REG_FIELD_A, SECOND_PASS_REGA_MATCH_VALUE),
283 new MatchUtils.RegMatch(REG_FIELD_B, (long)member.getIndex()));
285 String flowId = "LOADBALANCER_FORWARD_FLOW2_" + vip + "_" + member.getIP();
286 flowBuilder.setId(new FlowId(flowId));
287 FlowKey key = new FlowKey(new FlowId(flowId));
288 flowBuilder.setMatch(matchBuilder.build());
289 flowBuilder.setPriority(DEFAULT_FLOW_PRIORITY+1);
290 flowBuilder.setBarrier(true);
291 flowBuilder.setTableId(this.getTable());
292 flowBuilder.setKey(key);
293 flowBuilder.setFlowName(flowId);
294 flowBuilder.setHardTimeout(0);
295 flowBuilder.setIdleTimeout(0);
298 // Create the OF Actions and Instructions
299 InstructionsBuilder isb = new InstructionsBuilder();
301 // Instructions List Stores Individual Instructions
302 List<Instruction> instructions = Lists.newArrayList();
304 List<Action> actionList = Lists.newArrayList();
305 ActionBuilder ab = new ActionBuilder();
306 ab.setAction(ActionUtils.setDlDstAction(new MacAddress(member.getMAC())));
308 ab.setKey(new ActionKey(0));
309 actionList.add(ab.build());
311 ab = new ActionBuilder();
312 Ipv4Builder ipb = new Ipv4Builder().setIpv4Address(new Ipv4Prefix(member.getIP()));
313 ab.setAction(ActionUtils.setNwDstAction(ipb.build()));
315 ab.setKey(new ActionKey(1));
316 actionList.add(ab.build());
318 // Create an Apply Action
319 ApplyActionsBuilder aab = new ApplyActionsBuilder();
320 aab.setAction(actionList);
322 // Call the InstructionBuilder Methods Containing Actions
323 InstructionBuilder ib = new InstructionBuilder();
324 ib.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(aab.build()).build());
326 ib.setKey(new InstructionKey(0));
327 instructions.add(ib.build());
329 // Call the InstructionBuilder Methods Containing Actions
330 ib = this.getMutablePipelineInstructionBuilder();
332 ib.setKey(new InstructionKey(1));
333 instructions.add(ib.build());
335 // Add InstructionBuilder to the Instruction(s)Builder List
336 isb.setInstruction(instructions);
338 // Add InstructionsBuilder to FlowBuilder
339 flowBuilder.setInstructions(isb.build());
341 writeFlow(flowBuilder, nodeBuilder);
344 removeFlow(flowBuilder, nodeBuilder);
349 * Method to program all reverse rules that matches member {IP, Protocol, Port} for all members.
350 * This function calls manageLoadBalancerMemberReverseRules in turn.
351 * @param nodeBuilder Node to insert rule to
352 * @param lbConfig Configuration for this LoadBalancer instance
354 private void manageLoadBalancerReverseRules(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig, boolean write) {
355 for(Map.Entry<String, LoadBalancerPoolMember> entry : lbConfig.getMembers().entrySet()){
356 manageLoadBalancerMemberReverseRules(nodeBuilder, lbConfig, entry.getValue(), write);
360 private void manageLoadBalancerMemberReverseRules(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig,
361 LoadBalancerPoolMember member, boolean write) {
363 String vip = lbConfig.getVip();
364 String vmac = lbConfig.getVmac();
366 MatchBuilder matchBuilder = new MatchBuilder();
367 FlowBuilder flowBuilder = new FlowBuilder();
369 // Match Tunnel-ID, MemberIP, and Protocol/Port
370 if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VXLAN) ||
371 lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_GRE))
372 MatchUtils.createTunnelIDMatch(matchBuilder, new BigInteger(lbConfig.getProviderSegmentationId()));
373 else if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VLAN))
374 MatchUtils.createVlanIdMatch(matchBuilder, new VlanId(Integer.valueOf(lbConfig.getProviderSegmentationId())), true);
376 return; //Should not get here. TODO: Other types
378 MatchUtils.createSrcL3IPv4Match(matchBuilder, new Ipv4Prefix(member.getIP()));
379 MatchUtils.createSetSrcTcpMatch(matchBuilder, new PortNumber(member.getPort()));
381 String flowId = "LOADBALANCER_REVERSE_FLOW_" + vip + "_" + member.getIP();
382 flowBuilder.setId(new FlowId(flowId));
383 FlowKey key = new FlowKey(new FlowId(flowId));
384 flowBuilder.setMatch(matchBuilder.build());
385 flowBuilder.setPriority(DEFAULT_FLOW_PRIORITY);
386 flowBuilder.setBarrier(true);
387 flowBuilder.setTableId(this.getTable());
388 flowBuilder.setKey(key);
389 flowBuilder.setFlowName(flowId);
390 flowBuilder.setHardTimeout(0);
391 flowBuilder.setIdleTimeout(0);
394 // Create the OF Actions and Instructions
395 InstructionsBuilder isb = new InstructionsBuilder();
397 // Instructions List Stores Individual Instructions
398 List<Instruction> instructions = Lists.newArrayList();
400 List<Action> actionList = Lists.newArrayList();
401 ActionBuilder ab = new ActionBuilder();
402 Ipv4Builder ipb = new Ipv4Builder().setIpv4Address(new Ipv4Prefix(vip));
403 ab.setAction(ActionUtils.setNwSrcAction(ipb.build()));
405 ab.setKey(new ActionKey(0));
406 actionList.add(ab.build());
408 /* If a dummy MAC is assigned to the VIP, we use that as the
409 * source MAC for the reverse traffic.
412 ab = new ActionBuilder();
413 ab.setAction(ActionUtils.setDlDstAction(new MacAddress(vmac)));
415 ab.setKey(new ActionKey(1));
416 actionList.add(ab.build());
419 // Create an Apply Action
420 ApplyActionsBuilder aab = new ApplyActionsBuilder();
421 aab.setAction(actionList);
423 // Call the InstructionBuilder Methods Containing Actions
424 InstructionBuilder ib = new InstructionBuilder();
425 ib.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(aab.build()).build());
427 ib.setKey(new InstructionKey(0));
428 instructions.add(ib.build());
430 // Call the InstructionBuilder Methods Containing Actions
431 ib = this.getMutablePipelineInstructionBuilder();
433 ib.setKey(new InstructionKey(1));
434 instructions.add(ib.build());
436 // Add InstructionBuilder to the Instruction(s)Builder List
437 isb.setInstruction(instructions);
439 // Add InstructionsBuilder to FlowBuilder
440 flowBuilder.setInstructions(isb.build());
442 writeFlow(flowBuilder, nodeBuilder);
445 removeFlow(flowBuilder, nodeBuilder);