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.L3ForwardingProvider;
19 import org.opendaylight.ovsdb.openstack.netvirt.api.LoadBalancerConfiguration;
20 import org.opendaylight.ovsdb.openstack.netvirt.api.LoadBalancerConfiguration.LoadBalancerPoolMember;
21 import org.opendaylight.ovsdb.openstack.netvirt.api.LoadBalancerProvider;
22 import org.opendaylight.ovsdb.openstack.netvirt.api.Status;
23 import org.opendaylight.ovsdb.openstack.netvirt.api.StatusCode;
24 import org.opendaylight.ovsdb.openstack.netvirt.providers.ConfigInterface;
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.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.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
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.openflowjava.nx.action.rev140421.OfjNxHashFields;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.action.rev140421.OfjNxMpAlgorithm;
56 import org.osgi.framework.BundleContext;
57 import org.osgi.framework.ServiceReference;
58 import org.slf4j.Logger;
59 import org.slf4j.LoggerFactory;
61 import com.google.common.collect.Lists;
63 public class LoadBalancerService extends AbstractServiceInstance implements LoadBalancerProvider, ConfigInterface {
65 private static final Logger logger = LoggerFactory.getLogger(LoadBalancerProvider.class);
66 private static final int DEFAULT_FLOW_PRIORITY = 32768;
67 private static final Long FIRST_PASS_REGA_MATCH_VALUE = 0L;
68 private static final Long SECOND_PASS_REGA_MATCH_VALUE = 1L;
70 private static final Class<? extends NxmNxReg> REG_FIELD_A = NxmNxReg1.class;
71 private static final Class<? extends NxmNxReg> REG_FIELD_B = NxmNxReg2.class;
73 public LoadBalancerService() {
74 super(Service.LOAD_BALANCER);
77 public LoadBalancerService(Service service) {
82 * When this method is called, we do the following for minimizing flow updates:
83 * 1. Overwrite the solo multipath rule that applies to all members
84 * 2. Append second pass rule for the header rewriting specific to this member
85 * 3. Append reverse rules specific to this member
88 public Status programLoadBalancerPoolMemberRules(Node node,
89 LoadBalancerConfiguration lbConfig, LoadBalancerPoolMember member,
90 org.opendaylight.ovsdb.openstack.netvirt.api.Action action) {
91 if (lbConfig == null || member == null) {
92 logger.error("Null value for LB config {} or Member {}", lbConfig, member);
93 return new Status(StatusCode.BADREQUEST);
95 if (!lbConfig.isValid()) {
96 logger.error("LB config is invalid: {}", lbConfig);
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 + node.getNodeId().getValue()));
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,
128 org.opendaylight.ovsdb.openstack.netvirt.api.Action action) {
129 if (lbConfig == null) {
130 logger.error("LB config is invalid: {}", lbConfig);
131 return new Status(StatusCode.BADREQUEST);
133 if (!lbConfig.isValid()) {
134 logger.error("LB config is invalid: {}", lbConfig);
135 return new Status(StatusCode.BADREQUEST);
137 logger.debug("Performing {} rules for VIP {} and {} members", action, lbConfig.getVip(), lbConfig.getMembers().size());
139 NodeBuilder nodeBuilder = new NodeBuilder();
140 nodeBuilder.setId(new NodeId(Constants.OPENFLOW_NODE_PREFIX + node.getNodeId().getValue()));
141 nodeBuilder.setKey(new NodeKey(nodeBuilder.getId()));
143 if (action.equals(org.opendaylight.ovsdb.openstack.netvirt.api.Action.ADD)) {
144 manageLoadBalancerVIPRulesFirstPass(nodeBuilder, lbConfig, true);
145 manageLoadBalancerVIPRulesSecondPass(nodeBuilder, lbConfig, true);
146 manageLoadBalancerReverseRules(nodeBuilder, lbConfig, true);
147 return new Status(StatusCode.SUCCESS);
149 else if (action.equals(org.opendaylight.ovsdb.openstack.netvirt.api.Action.DELETE)) {
150 manageLoadBalancerVIPRulesFirstPass(nodeBuilder, lbConfig, false);
151 manageLoadBalancerVIPRulesSecondPass(nodeBuilder, lbConfig, false);
152 manageLoadBalancerReverseRules(nodeBuilder, lbConfig, false);
153 return new Status(StatusCode.SUCCESS);
156 return new Status(StatusCode.NOTIMPLEMENTED);
160 * Method to insert/remove default rule for traffic destined to the VIP and no
161 * server selection performed yet
162 * @param nodeBuilder NodeBuilder
163 * @param lbConfig LoadBalancerConfiguration
164 * @param write Boolean to indicate of the flow is to be inserted or removed
166 private void manageLoadBalancerVIPRulesFirstPass(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig, boolean write) {
167 MatchBuilder matchBuilder = new MatchBuilder();
168 FlowBuilder flowBuilder = new FlowBuilder();
170 // Match Tunnel-ID, VIP, and Reg0==0
171 if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VXLAN) ||
172 lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_GRE))
173 MatchUtils.createTunnelIDMatch(matchBuilder, new BigInteger(lbConfig.getProviderSegmentationId()));
174 else if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VLAN))
175 MatchUtils.createVlanIdMatch(matchBuilder, new VlanId(Integer.valueOf(lbConfig.getProviderSegmentationId())), true);
177 return; //Should not get here. TODO: Other types
179 MatchUtils.createDstL3IPv4Match(matchBuilder, MatchUtils.iPv4PrefixFromIPv4Address(lbConfig.getVip()));
180 MatchUtils.addNxRegMatch(matchBuilder, new MatchUtils.RegMatch(REG_FIELD_A, FIRST_PASS_REGA_MATCH_VALUE));
182 String flowId = "LOADBALANCER_FORWARD_FLOW1_" + lbConfig.getProviderSegmentationId() + "_" + lbConfig.getVip();
183 flowBuilder.setId(new FlowId(flowId));
184 FlowKey key = new FlowKey(new FlowId(flowId));
185 flowBuilder.setMatch(matchBuilder.build());
186 flowBuilder.setPriority(DEFAULT_FLOW_PRIORITY);
187 flowBuilder.setBarrier(true);
188 flowBuilder.setTableId(this.getTable());
189 flowBuilder.setKey(key);
190 flowBuilder.setFlowName(flowId);
191 flowBuilder.setHardTimeout(0);
192 flowBuilder.setIdleTimeout(0);
195 // Create the OF Actions and Instructions
196 InstructionsBuilder isb = new InstructionsBuilder();
198 // Instructions List Stores Individual Instructions
199 List<Instruction> instructions = Lists.newArrayList();
201 List<Action> actionList = Lists.newArrayList();
203 ActionBuilder ab = new ActionBuilder();
204 ab.setAction(ActionUtils.nxLoadRegAction(new DstNxRegCaseBuilder().setNxReg(REG_FIELD_A).build(),
205 BigInteger.valueOf(SECOND_PASS_REGA_MATCH_VALUE)));
207 ab.setKey(new ActionKey(0));
208 actionList.add(ab.build());
210 ab = new ActionBuilder();
211 ab.setAction(ActionUtils.nxMultipathAction(OfjNxHashFields.NXHASHFIELDSSYMMETRICL4,
212 0, OfjNxMpAlgorithm.NXMPALGMODULON,
213 lbConfig.getMembers().size()-1, //By Nicira-Ext spec, this field is max_link minus 1
214 0L, new DstNxRegCaseBuilder().setNxReg(REG_FIELD_B).build(),
217 ab.setKey(new ActionKey(1));
218 actionList.add(ab.build());
220 ab = new ActionBuilder();
221 ab.setAction(ActionUtils.nxResubmitAction(null, this.getTable()));
223 ab.setKey(new ActionKey(2));
224 actionList.add(ab.build());
226 // Create an Apply Action
227 ApplyActionsBuilder aab = new ApplyActionsBuilder();
228 aab.setAction(actionList);
229 InstructionBuilder ib = new InstructionBuilder();
230 ib.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(aab.build()).build());
232 // Call the InstructionBuilder Methods Containing Actions
234 ib.setKey(new InstructionKey(0));
235 instructions.add(ib.build());
237 // Add InstructionBuilder to the Instruction(s)Builder List
238 isb.setInstruction(instructions);
240 // Add InstructionsBuilder to FlowBuilder
241 flowBuilder.setInstructions(isb.build());
243 writeFlow(flowBuilder, nodeBuilder);
246 removeFlow(flowBuilder, nodeBuilder);
251 * Method to program each rule that matches on Reg0 and Reg1 to insert appropriate header rewriting
252 * rules for all members. This function calls manageLoadBalancerMemberVIPRulesSecondPass in turn.
253 * @param nodeBuilder Node to insert rule to
254 * @param lbConfig Configuration for this LoadBalancer instance
255 * @param write Boolean to indicate of the flow is to be inserted or removed
257 private void manageLoadBalancerVIPRulesSecondPass(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig, boolean write) {
258 for(Map.Entry<String, LoadBalancerPoolMember> entry : lbConfig.getMembers().entrySet()){
259 manageLoadBalancerMemberVIPRulesSecondPass(nodeBuilder, lbConfig, entry.getValue(), write);
263 private void manageLoadBalancerMemberVIPRulesSecondPass(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig, LoadBalancerPoolMember member, boolean write) {
264 String vip = lbConfig.getVip();
266 MatchBuilder matchBuilder = new MatchBuilder();
267 FlowBuilder flowBuilder = new FlowBuilder();
269 // Match Tunnel-ID, VIP, Reg0==1 and Reg1==Index of member
270 if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VXLAN) ||
271 lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_GRE))
272 MatchUtils.createTunnelIDMatch(matchBuilder, new BigInteger(lbConfig.getProviderSegmentationId()));
273 else if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VLAN))
274 MatchUtils.createVlanIdMatch(matchBuilder, new VlanId(Integer.valueOf(lbConfig.getProviderSegmentationId())), true);
276 return; //Should not get here. TODO: Other types
278 MatchUtils.createDstL3IPv4Match(matchBuilder, MatchUtils.iPv4PrefixFromIPv4Address(vip));
279 MatchUtils.addNxRegMatch(matchBuilder, new MatchUtils.RegMatch(REG_FIELD_A, SECOND_PASS_REGA_MATCH_VALUE),
280 new MatchUtils.RegMatch(REG_FIELD_B, (long)member.getIndex()));
282 String flowId = "LOADBALANCER_FORWARD_FLOW2_" + lbConfig.getProviderSegmentationId() + "_" +
283 vip + "_" + member.getIP();
284 flowBuilder.setId(new FlowId(flowId));
285 FlowKey key = new FlowKey(new FlowId(flowId));
286 flowBuilder.setMatch(matchBuilder.build());
287 flowBuilder.setPriority(DEFAULT_FLOW_PRIORITY+1);
288 flowBuilder.setBarrier(true);
289 flowBuilder.setTableId(this.getTable());
290 flowBuilder.setKey(key);
291 flowBuilder.setFlowName(flowId);
292 flowBuilder.setHardTimeout(0);
293 flowBuilder.setIdleTimeout(0);
296 // Create the OF Actions and Instructions
297 InstructionsBuilder isb = new InstructionsBuilder();
299 // Instructions List Stores Individual Instructions
300 List<Instruction> instructions = Lists.newArrayList();
302 List<Action> actionList = Lists.newArrayList();
303 ActionBuilder ab = new ActionBuilder();
304 ab.setAction(ActionUtils.setDlDstAction(new MacAddress(member.getMAC())));
306 ab.setKey(new ActionKey(0));
307 actionList.add(ab.build());
309 ab = new ActionBuilder();
310 Ipv4Builder ipb = new Ipv4Builder().setIpv4Address(MatchUtils.iPv4PrefixFromIPv4Address(member.getIP()));
311 ab.setAction(ActionUtils.setNwDstAction(ipb.build()));
313 ab.setKey(new ActionKey(1));
314 actionList.add(ab.build());
316 // Create an Apply Action
317 ApplyActionsBuilder aab = new ApplyActionsBuilder();
318 aab.setAction(actionList);
320 // Call the InstructionBuilder Methods Containing Actions
321 InstructionBuilder ib = new InstructionBuilder();
322 ib.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(aab.build()).build());
324 ib.setKey(new InstructionKey(0));
325 instructions.add(ib.build());
327 // Call the InstructionBuilder Methods Containing Actions
328 ib = this.getMutablePipelineInstructionBuilder();
330 ib.setKey(new InstructionKey(1));
331 instructions.add(ib.build());
333 // Add InstructionBuilder to the Instruction(s)Builder List
334 isb.setInstruction(instructions);
336 // Add InstructionsBuilder to FlowBuilder
337 flowBuilder.setInstructions(isb.build());
339 writeFlow(flowBuilder, nodeBuilder);
342 removeFlow(flowBuilder, nodeBuilder);
347 * Method to program all reverse rules that matches member {IP, Protocol, Port} for all members.
348 * This function calls manageLoadBalancerMemberReverseRules in turn.
349 * @param nodeBuilder Node to insert rule to
350 * @param lbConfig Configuration for this LoadBalancer instance
352 private void manageLoadBalancerReverseRules(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig, boolean write) {
353 for(Map.Entry<String, LoadBalancerPoolMember> entry : lbConfig.getMembers().entrySet()){
354 manageLoadBalancerMemberReverseRules(nodeBuilder, lbConfig, entry.getValue(), write);
358 private void manageLoadBalancerMemberReverseRules(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig,
359 LoadBalancerPoolMember member, boolean write) {
361 String vip = lbConfig.getVip();
362 String vmac = lbConfig.getVmac();
364 MatchBuilder matchBuilder = new MatchBuilder();
365 FlowBuilder flowBuilder = new FlowBuilder();
367 // Match Tunnel-ID, MemberIP, and Protocol/Port
368 if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VXLAN) ||
369 lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_GRE))
370 MatchUtils.createTunnelIDMatch(matchBuilder, new BigInteger(lbConfig.getProviderSegmentationId()));
371 else if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VLAN))
372 MatchUtils.createVlanIdMatch(matchBuilder, new VlanId(Integer.valueOf(lbConfig.getProviderSegmentationId())), true);
374 return; //Should not get here. TODO: Other types
376 MatchUtils.createSrcL3IPv4Match(matchBuilder, MatchUtils.iPv4PrefixFromIPv4Address(member.getIP()));
377 MatchUtils.createSetSrcTcpMatch(matchBuilder, new PortNumber(member.getPort()));
379 String flowId = "LOADBALANCER_REVERSE_FLOW_" + lbConfig.getProviderSegmentationId() +
380 vip + "_" + member.getIP();
381 flowBuilder.setId(new FlowId(flowId));
382 FlowKey key = new FlowKey(new FlowId(flowId));
383 flowBuilder.setMatch(matchBuilder.build());
384 flowBuilder.setPriority(DEFAULT_FLOW_PRIORITY);
385 flowBuilder.setBarrier(true);
386 flowBuilder.setTableId(this.getTable());
387 flowBuilder.setKey(key);
388 flowBuilder.setFlowName(flowId);
389 flowBuilder.setHardTimeout(0);
390 flowBuilder.setIdleTimeout(0);
393 // Create the OF Actions and Instructions
394 InstructionsBuilder isb = new InstructionsBuilder();
396 // Instructions List Stores Individual Instructions
397 List<Instruction> instructions = Lists.newArrayList();
399 List<Action> actionList = Lists.newArrayList();
400 ActionBuilder ab = new ActionBuilder();
401 Ipv4Builder ipb = new Ipv4Builder().setIpv4Address(MatchUtils.iPv4PrefixFromIPv4Address(vip));
402 ab.setAction(ActionUtils.setNwSrcAction(ipb.build()));
404 ab.setKey(new ActionKey(0));
405 actionList.add(ab.build());
407 /* If a dummy MAC is assigned to the VIP, we use that as the
408 * source MAC for the reverse traffic.
411 ab = new ActionBuilder();
412 ab.setAction(ActionUtils.setDlDstAction(new MacAddress(vmac)));
414 ab.setKey(new ActionKey(1));
415 actionList.add(ab.build());
418 // Create an Apply Action
419 ApplyActionsBuilder aab = new ApplyActionsBuilder();
420 aab.setAction(actionList);
422 // Call the InstructionBuilder Methods Containing Actions
423 InstructionBuilder ib = new InstructionBuilder();
424 ib.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(aab.build()).build());
426 ib.setKey(new InstructionKey(0));
427 instructions.add(ib.build());
429 // Call the InstructionBuilder Methods Containing Actions
430 ib = this.getMutablePipelineInstructionBuilder();
432 ib.setKey(new InstructionKey(1));
433 instructions.add(ib.build());
435 // Add InstructionBuilder to the Instruction(s)Builder List
436 isb.setInstruction(instructions);
438 // Add InstructionsBuilder to FlowBuilder
439 flowBuilder.setInstructions(isb.build());
441 writeFlow(flowBuilder, nodeBuilder);
444 removeFlow(flowBuilder, nodeBuilder);
449 public void setDependencies(BundleContext bundleContext, ServiceReference serviceReference) {
450 super.setOrchestrator(bundleContext.getServiceReference(LoadBalancerProvider.class.getName()), this);
454 public void setDependencies(Object impl) {