2 * Copyright (c) 2014 SDN Hub, LLC. and others. All rights reserved.
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 package org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.services;
10 import java.math.BigInteger;
11 import java.util.List;
14 import org.opendaylight.ovsdb.openstack.netvirt.NetworkHandler;
15 import org.opendaylight.ovsdb.openstack.netvirt.api.Constants;
16 import org.opendaylight.ovsdb.openstack.netvirt.api.LoadBalancerConfiguration;
17 import org.opendaylight.ovsdb.openstack.netvirt.api.LoadBalancerConfiguration.LoadBalancerPoolMember;
18 import org.opendaylight.ovsdb.openstack.netvirt.api.LoadBalancerProvider;
19 import org.opendaylight.ovsdb.openstack.netvirt.api.Southbound;
20 import org.opendaylight.ovsdb.openstack.netvirt.api.Status;
21 import org.opendaylight.ovsdb.openstack.netvirt.api.StatusCode;
22 import org.opendaylight.ovsdb.openstack.netvirt.providers.ConfigInterface;
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.FlowUtils;
27 import org.opendaylight.ovsdb.utils.mdsal.openflow.MatchUtils;
28 import org.opendaylight.ovsdb.utils.servicehelper.ServiceHelper;
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.tables.table.FlowBuilder;
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.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.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.osgi.framework.BundleContext;
55 import org.osgi.framework.ServiceReference;
56 import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
59 import com.google.common.collect.Lists;
61 public class LoadBalancerService extends AbstractServiceInstance implements LoadBalancerProvider, ConfigInterface {
63 private static final Logger LOG = LoggerFactory.getLogger(LoadBalancerProvider.class);
64 private static final int DEFAULT_FLOW_PRIORITY = 32768;
65 private static final Long FIRST_PASS_REGA_MATCH_VALUE = 0L;
66 private static final Long SECOND_PASS_REGA_MATCH_VALUE = 1L;
68 private static final Class<? extends NxmNxReg> REG_FIELD_A = NxmNxReg1.class;
69 private static final Class<? extends NxmNxReg> REG_FIELD_B = NxmNxReg2.class;
71 private volatile Southbound southbound;
73 public LoadBalancerService() {
74 super(Service.LOAD_BALANCER);
77 public LoadBalancerService(Service service) {
81 private String getDpid(Node node) {
82 long dpid = southbound.getDataPathId(node);
84 LOG.warn("getDpid: DPID could not be found for node: {}", node.getNodeId().getValue());
86 return String.valueOf(dpid);
90 * When this method is called, we do the following for minimizing flow updates:
91 * 1. Overwrite the solo multipath rule that applies to all members
92 * 2. Append second pass rule for the header rewriting specific to this member
93 * 3. Append reverse rules specific to this member
96 public Status programLoadBalancerPoolMemberRules(Node node,
97 LoadBalancerConfiguration lbConfig, LoadBalancerPoolMember member,
98 org.opendaylight.ovsdb.openstack.netvirt.api.Action action) {
99 if (lbConfig == null || member == null) {
100 LOG.error("Null value for LB config {} or Member {}", lbConfig, member);
101 return new Status(StatusCode.BADREQUEST);
103 if (!lbConfig.isValid()) {
104 LOG.error("LB config is invalid: {}", lbConfig);
105 return new Status(StatusCode.BADREQUEST);
107 LOG.debug("Performing {} rules for member {} with index {} on LB with VIP {} and total members {}",
108 action, member.getIP(), member.getIndex(), lbConfig.getVip(), lbConfig.getMembers().size());
110 NodeBuilder nodeBuilder = new NodeBuilder();
111 nodeBuilder.setId(new NodeId(Constants.OPENFLOW_NODE_PREFIX + getDpid(node)));
112 nodeBuilder.setKey(new NodeKey(nodeBuilder.getId()));
114 //Update the multipath rule
115 manageLoadBalancerVIPRulesFirstPass(nodeBuilder, lbConfig, true);
117 if (action.equals(org.opendaylight.ovsdb.openstack.netvirt.api.Action.ADD)) {
118 manageLoadBalancerMemberVIPRulesSecondPass(nodeBuilder, lbConfig, member, true);
119 manageLoadBalancerMemberReverseRules(nodeBuilder, lbConfig, member, true);
120 return new Status(StatusCode.SUCCESS);
122 /* TODO: Delete single member.
123 * For now, removing a member requires deleting the full LB instance and re-adding
125 return new Status(StatusCode.NOTIMPLEMENTED);
129 * When this method is called, we perform the following:
130 * 1. Write the solo multipath rule that applies to all members
131 * 2. Append second pass rules for the header rewriting for all members
132 * 3. Append reverse rules for all the members, specific to the protocol/port
135 public Status programLoadBalancerRules(Node node, LoadBalancerConfiguration lbConfig,
136 org.opendaylight.ovsdb.openstack.netvirt.api.Action action) {
137 if (lbConfig == null || !lbConfig.isValid()) {
138 LOG.error("LB config is invalid: {}", lbConfig);
139 return new Status(StatusCode.BADREQUEST);
141 LOG.debug("Performing {} rules for VIP {} and {} members", action, lbConfig.getVip(), lbConfig.getMembers().size());
143 NodeBuilder nodeBuilder = new NodeBuilder();
144 nodeBuilder.setId(new NodeId(Constants.OPENFLOW_NODE_PREFIX + getDpid(node)));
145 nodeBuilder.setKey(new NodeKey(nodeBuilder.getId()));
147 if (action.equals(org.opendaylight.ovsdb.openstack.netvirt.api.Action.ADD)) {
148 manageLoadBalancerVIPRulesFirstPass(nodeBuilder, lbConfig, true);
149 manageLoadBalancerVIPRulesSecondPass(nodeBuilder, lbConfig, true);
150 manageLoadBalancerReverseRules(nodeBuilder, lbConfig, true);
151 return new Status(StatusCode.SUCCESS);
153 else if (action.equals(org.opendaylight.ovsdb.openstack.netvirt.api.Action.DELETE)) {
154 manageLoadBalancerVIPRulesFirstPass(nodeBuilder, lbConfig, false);
155 manageLoadBalancerVIPRulesSecondPass(nodeBuilder, lbConfig, false);
156 manageLoadBalancerReverseRules(nodeBuilder, lbConfig, false);
157 return new Status(StatusCode.SUCCESS);
160 return new Status(StatusCode.NOTIMPLEMENTED);
164 * Method to insert/remove default rule for traffic destined to the VIP and no
165 * server selection performed yet
166 * @param nodeBuilder NodeBuilder
167 * @param lbConfig LoadBalancerConfiguration
168 * @param write Boolean to indicate of the flow is to be inserted or removed
170 private void manageLoadBalancerVIPRulesFirstPass(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig,
172 FlowBuilder flowBuilder = new FlowBuilder();
173 String flowName = "LOADBALANCER_FORWARD_FLOW1_" + lbConfig.getProviderSegmentationId() + "_" + lbConfig.getVip();
174 FlowUtils.initFlowBuilder(flowBuilder, flowName, getTable()).setPriority(DEFAULT_FLOW_PRIORITY);
176 MatchBuilder matchBuilder = new MatchBuilder();
178 // Match Tunnel-ID, VIP, and Reg0==0
179 if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VXLAN) ||
180 lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_GRE)) {
181 MatchUtils.createTunnelIDMatch(matchBuilder, new BigInteger(lbConfig.getProviderSegmentationId()));
182 } else if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VLAN)) {
183 MatchUtils.createVlanIdMatch(matchBuilder, new VlanId(Integer.valueOf(lbConfig.getProviderSegmentationId())), true);
185 return; //Should not get here. TODO: Other types
188 MatchUtils.createDstL3IPv4Match(matchBuilder, MatchUtils.iPv4PrefixFromIPv4Address(lbConfig.getVip()));
189 MatchUtils.addNxRegMatch(matchBuilder, new MatchUtils.RegMatch(REG_FIELD_A, FIRST_PASS_REGA_MATCH_VALUE));
190 flowBuilder.setMatch(matchBuilder.build());
193 // Create the OF Actions and Instructions
194 InstructionsBuilder isb = new InstructionsBuilder();
196 // Instructions List Stores Individual Instructions
197 List<Instruction> instructions = Lists.newArrayList();
199 List<Action> actionList = Lists.newArrayList();
201 ActionBuilder ab = new ActionBuilder();
202 ab.setAction(ActionUtils.nxLoadRegAction(new DstNxRegCaseBuilder().setNxReg(REG_FIELD_A).build(),
203 BigInteger.valueOf(SECOND_PASS_REGA_MATCH_VALUE)));
205 ab.setKey(new ActionKey(0));
206 actionList.add(ab.build());
208 ab = new ActionBuilder();
209 ab.setAction(ActionUtils.nxMultipathAction(OfjNxHashFields.NXHASHFIELDSSYMMETRICL4,
210 0, OfjNxMpAlgorithm.NXMPALGMODULON,
211 lbConfig.getMembers().size()-1, //By Nicira-Ext spec, this field is max_link minus 1
212 0L, new DstNxRegCaseBuilder().setNxReg(REG_FIELD_B).build(),
215 ab.setKey(new ActionKey(1));
216 actionList.add(ab.build());
218 ab = new ActionBuilder();
219 ab.setAction(ActionUtils.nxResubmitAction(null, this.getTable()));
221 ab.setKey(new ActionKey(2));
222 actionList.add(ab.build());
224 // Create an Apply Action
225 ApplyActionsBuilder aab = new ApplyActionsBuilder();
226 aab.setAction(actionList);
227 InstructionBuilder ib = new InstructionBuilder();
228 ib.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(aab.build()).build());
230 // Call the InstructionBuilder Methods Containing Actions
232 ib.setKey(new InstructionKey(0));
233 instructions.add(ib.build());
235 // Add InstructionBuilder to the Instruction(s)Builder List
236 isb.setInstruction(instructions);
238 // Add InstructionsBuilder to FlowBuilder
239 flowBuilder.setInstructions(isb.build());
241 writeFlow(flowBuilder, nodeBuilder);
243 removeFlow(flowBuilder, nodeBuilder);
248 * Method to program each rule that matches on Reg0 and Reg1 to insert appropriate header rewriting
249 * rules for all members. This function calls manageLoadBalancerMemberVIPRulesSecondPass in turn.
250 * @param nodeBuilder Node to insert rule to
251 * @param lbConfig Configuration for this LoadBalancer instance
252 * @param write Boolean to indicate of the flow is to be inserted or removed
254 private void manageLoadBalancerVIPRulesSecondPass(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig, boolean write) {
255 for(Map.Entry<String, LoadBalancerPoolMember> entry : lbConfig.getMembers().entrySet()){
256 manageLoadBalancerMemberVIPRulesSecondPass(nodeBuilder, lbConfig, entry.getValue(), write);
260 private void manageLoadBalancerMemberVIPRulesSecondPass(NodeBuilder nodeBuilder,
261 LoadBalancerConfiguration lbConfig,
262 LoadBalancerPoolMember member, boolean write) {
263 String vip = lbConfig.getVip();
265 FlowBuilder flowBuilder = new FlowBuilder();
266 String flowName = "LOADBALANCER_FORWARD_FLOW2_" + lbConfig.getProviderSegmentationId() + "_"
267 + vip + "_" + member.getIP();
268 FlowUtils.initFlowBuilder(flowBuilder, flowName, getTable()).setPriority(DEFAULT_FLOW_PRIORITY+1);
270 MatchBuilder matchBuilder = new MatchBuilder();
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
282 MatchUtils.createDstL3IPv4Match(matchBuilder, MatchUtils.iPv4PrefixFromIPv4Address(vip));
283 MatchUtils.addNxRegMatch(matchBuilder, new MatchUtils.RegMatch(REG_FIELD_A, SECOND_PASS_REGA_MATCH_VALUE),
284 new MatchUtils.RegMatch(REG_FIELD_B, (long)member.getIndex()));
285 flowBuilder.setMatch(matchBuilder.build());
288 // Create the OF Actions and Instructions
289 InstructionsBuilder isb = new InstructionsBuilder();
291 // Instructions List Stores Individual Instructions
292 List<Instruction> instructions = Lists.newArrayList();
294 List<Action> actionList = Lists.newArrayList();
295 ActionBuilder ab = new ActionBuilder();
296 ab.setAction(ActionUtils.setDlDstAction(new MacAddress(member.getMAC())));
298 ab.setKey(new ActionKey(0));
299 actionList.add(ab.build());
301 ab = new ActionBuilder();
302 Ipv4Builder ipb = new Ipv4Builder().setIpv4Address(MatchUtils.iPv4PrefixFromIPv4Address(member.getIP()));
303 ab.setAction(ActionUtils.setNwDstAction(ipb.build()));
305 ab.setKey(new ActionKey(1));
306 actionList.add(ab.build());
308 // Create an Apply Action
309 ApplyActionsBuilder aab = new ApplyActionsBuilder();
310 aab.setAction(actionList);
312 // Call the InstructionBuilder Methods Containing Actions
313 InstructionBuilder ib = new InstructionBuilder();
314 ib.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(aab.build()).build());
316 ib.setKey(new InstructionKey(0));
317 instructions.add(ib.build());
319 // Call the InstructionBuilder Methods Containing Actions
320 ib = this.getMutablePipelineInstructionBuilder();
322 ib.setKey(new InstructionKey(1));
323 instructions.add(ib.build());
325 // Add InstructionBuilder to the Instruction(s)Builder List
326 isb.setInstruction(instructions);
328 // Add InstructionsBuilder to FlowBuilder
329 flowBuilder.setInstructions(isb.build());
331 writeFlow(flowBuilder, nodeBuilder);
333 removeFlow(flowBuilder, nodeBuilder);
338 * Method to program all reverse rules that matches member {IP, Protocol, Port} for all members.
339 * This function calls manageLoadBalancerMemberReverseRules in turn.
340 * @param nodeBuilder Node to insert rule to
341 * @param lbConfig Configuration for this LoadBalancer instance
343 private void manageLoadBalancerReverseRules(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig, boolean write) {
344 for(Map.Entry<String, LoadBalancerPoolMember> entry : lbConfig.getMembers().entrySet()){
345 manageLoadBalancerMemberReverseRules(nodeBuilder, lbConfig, entry.getValue(), write);
349 private void manageLoadBalancerMemberReverseRules(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig,
350 LoadBalancerPoolMember member, boolean write) {
352 String vip = lbConfig.getVip();
353 String vmac = lbConfig.getVmac();
355 FlowBuilder flowBuilder = new FlowBuilder();
356 String flowName = "LOADBALANCER_REVERSE_FLOW_" + lbConfig.getProviderSegmentationId() +
357 vip + "_" + member.getIP();
358 FlowUtils.initFlowBuilder(flowBuilder, flowName, getTable()).setPriority(DEFAULT_FLOW_PRIORITY);
360 MatchBuilder matchBuilder = new MatchBuilder();
362 // Match Tunnel-ID, MemberIP, and Protocol/Port
363 if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VXLAN) ||
364 lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_GRE)) {
365 MatchUtils.createTunnelIDMatch(matchBuilder, new BigInteger(lbConfig.getProviderSegmentationId()));
366 } else if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VLAN)) {
367 MatchUtils.createVlanIdMatch(matchBuilder,
368 new VlanId(Integer.valueOf(lbConfig.getProviderSegmentationId())), true);
370 return; //Should not get here. TODO: Other types
373 MatchUtils.createSrcL3IPv4Match(matchBuilder, MatchUtils.iPv4PrefixFromIPv4Address(member.getIP()));
374 MatchUtils.createSetSrcTcpMatch(matchBuilder, new PortNumber(member.getPort()));
375 flowBuilder.setMatch(matchBuilder.build());
378 // Create the OF Actions and Instructions
379 InstructionsBuilder isb = new InstructionsBuilder();
381 // Instructions List Stores Individual Instructions
382 List<Instruction> instructions = Lists.newArrayList();
384 List<Action> actionList = Lists.newArrayList();
385 ActionBuilder ab = new ActionBuilder();
386 Ipv4Builder ipb = new Ipv4Builder().setIpv4Address(MatchUtils.iPv4PrefixFromIPv4Address(vip));
387 ab.setAction(ActionUtils.setNwSrcAction(ipb.build()));
389 ab.setKey(new ActionKey(0));
390 actionList.add(ab.build());
392 /* If a dummy MAC is assigned to the VIP, we use that as the
393 * source MAC for the reverse traffic.
396 ab = new ActionBuilder();
397 ab.setAction(ActionUtils.setDlDstAction(new MacAddress(vmac)));
399 ab.setKey(new ActionKey(1));
400 actionList.add(ab.build());
403 // Create an Apply Action
404 ApplyActionsBuilder aab = new ApplyActionsBuilder();
405 aab.setAction(actionList);
407 // Call the InstructionBuilder Methods Containing Actions
408 InstructionBuilder ib = new InstructionBuilder();
409 ib.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(aab.build()).build());
411 ib.setKey(new InstructionKey(0));
412 instructions.add(ib.build());
414 // Call the InstructionBuilder Methods Containing Actions
415 ib = this.getMutablePipelineInstructionBuilder();
417 ib.setKey(new InstructionKey(1));
418 instructions.add(ib.build());
420 // Add InstructionBuilder to the Instruction(s)Builder List
421 isb.setInstruction(instructions);
423 // Add InstructionsBuilder to FlowBuilder
424 flowBuilder.setInstructions(isb.build());
426 writeFlow(flowBuilder, nodeBuilder);
428 removeFlow(flowBuilder, nodeBuilder);
433 public void setDependencies(BundleContext bundleContext, ServiceReference serviceReference) {
434 super.setDependencies(bundleContext.getServiceReference(LoadBalancerProvider.class.getName()), this);
435 southbound =(Southbound) ServiceHelper.getGlobalInstance(Southbound.class, this);
439 public void setDependencies(Object impl) {