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.ConfigInterface;
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.PortNumber;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionKey;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.address.address.Ipv4Builder;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
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.inventory.rev130819.tables.table.FlowKey;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCaseBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActionsBuilder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionKey;
44 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
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.l2.types.rev130827.VlanId;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg1;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg2;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.dst.choice.grouping.dst.choice.DstNxRegCaseBuilder;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.action.rev140421.OfjNxHashFields;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.action.rev140421.OfjNxMpAlgorithm;
55 import org.osgi.framework.BundleContext;
56 import org.osgi.framework.ServiceReference;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
60 import com.google.common.collect.Lists;
62 public class LoadBalancerService extends AbstractServiceInstance implements LoadBalancerProvider, ConfigInterface {
64 private static final Logger LOG = LoggerFactory.getLogger(LoadBalancerProvider.class);
65 private static final int DEFAULT_FLOW_PRIORITY = 32768;
66 private static final Long FIRST_PASS_REGA_MATCH_VALUE = 0L;
67 private static final Long SECOND_PASS_REGA_MATCH_VALUE = 1L;
69 private static final Class<? extends NxmNxReg> REG_FIELD_A = NxmNxReg1.class;
70 private static final Class<? extends NxmNxReg> REG_FIELD_B = NxmNxReg2.class;
72 public LoadBalancerService() {
73 super(Service.LOAD_BALANCER);
76 public LoadBalancerService(Service service) {
81 * When this method is called, we do the following for minimizing flow updates:
82 * 1. Overwrite the solo multipath rule that applies to all members
83 * 2. Append second pass rule for the header rewriting specific to this member
84 * 3. Append reverse rules specific to this member
87 public Status programLoadBalancerPoolMemberRules(Node node,
88 LoadBalancerConfiguration lbConfig, LoadBalancerPoolMember member,
89 org.opendaylight.ovsdb.openstack.netvirt.api.Action action) {
90 if (lbConfig == null || member == null) {
91 LOG.error("Null value for LB config {} or Member {}", lbConfig, member);
92 return new Status(StatusCode.BADREQUEST);
94 if (!lbConfig.isValid()) {
95 LOG.error("LB config is invalid: {}", lbConfig);
96 return new Status(StatusCode.BADREQUEST);
98 LOG.debug("Performing {} rules for member {} with index {} on LB with VIP {} and total members {}",
99 action, member.getIP(), member.getIndex(), lbConfig.getVip(), lbConfig.getMembers().size());
101 NodeBuilder nodeBuilder = new NodeBuilder();
102 nodeBuilder.setId(new NodeId(Constants.OPENFLOW_NODE_PREFIX + node.getNodeId().getValue()));
103 nodeBuilder.setKey(new NodeKey(nodeBuilder.getId()));
105 //Update the multipath rule
106 manageLoadBalancerVIPRulesFirstPass(nodeBuilder, lbConfig, true);
108 if (action.equals(org.opendaylight.ovsdb.openstack.netvirt.api.Action.ADD)) {
109 manageLoadBalancerMemberVIPRulesSecondPass(nodeBuilder, lbConfig, member, true);
110 manageLoadBalancerMemberReverseRules(nodeBuilder, lbConfig, member, true);
111 return new Status(StatusCode.SUCCESS);
113 /* TODO: Delete single member.
114 * For now, removing a member requires deleting the full LB instance and re-adding
116 return new Status(StatusCode.NOTIMPLEMENTED);
120 * When this method is called, we perform the following:
121 * 1. Write the solo multipath rule that applies to all members
122 * 2. Append second pass rules for the header rewriting for all members
123 * 3. Append reverse rules for all the members, specific to the protocol/port
126 public Status programLoadBalancerRules(Node node, LoadBalancerConfiguration lbConfig,
127 org.opendaylight.ovsdb.openstack.netvirt.api.Action action) {
128 if (lbConfig == null || !lbConfig.isValid()) {
129 LOG.error("LB config is invalid: {}", lbConfig);
130 return new Status(StatusCode.BADREQUEST);
132 LOG.debug("Performing {} rules for VIP {} and {} members", action, lbConfig.getVip(), lbConfig.getMembers().size());
134 NodeBuilder nodeBuilder = new NodeBuilder();
135 nodeBuilder.setId(new NodeId(Constants.OPENFLOW_NODE_PREFIX + node.getNodeId().getValue()));
136 nodeBuilder.setKey(new NodeKey(nodeBuilder.getId()));
138 if (action.equals(org.opendaylight.ovsdb.openstack.netvirt.api.Action.ADD)) {
139 manageLoadBalancerVIPRulesFirstPass(nodeBuilder, lbConfig, true);
140 manageLoadBalancerVIPRulesSecondPass(nodeBuilder, lbConfig, true);
141 manageLoadBalancerReverseRules(nodeBuilder, lbConfig, true);
142 return new Status(StatusCode.SUCCESS);
144 else if (action.equals(org.opendaylight.ovsdb.openstack.netvirt.api.Action.DELETE)) {
145 manageLoadBalancerVIPRulesFirstPass(nodeBuilder, lbConfig, false);
146 manageLoadBalancerVIPRulesSecondPass(nodeBuilder, lbConfig, false);
147 manageLoadBalancerReverseRules(nodeBuilder, lbConfig, false);
148 return new Status(StatusCode.SUCCESS);
151 return new Status(StatusCode.NOTIMPLEMENTED);
155 * Method to insert/remove default rule for traffic destined to the VIP and no
156 * server selection performed yet
157 * @param nodeBuilder NodeBuilder
158 * @param lbConfig LoadBalancerConfiguration
159 * @param write Boolean to indicate of the flow is to be inserted or removed
161 private void manageLoadBalancerVIPRulesFirstPass(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig, boolean write) {
162 MatchBuilder matchBuilder = new MatchBuilder();
163 FlowBuilder flowBuilder = new FlowBuilder();
165 // Match Tunnel-ID, VIP, and Reg0==0
166 if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VXLAN) ||
167 lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_GRE)) {
168 MatchUtils.createTunnelIDMatch(matchBuilder, new BigInteger(lbConfig.getProviderSegmentationId()));
169 } else if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VLAN)) {
170 MatchUtils.createVlanIdMatch(matchBuilder, new VlanId(Integer.valueOf(lbConfig.getProviderSegmentationId())), true);
172 return; //Should not get here. TODO: Other types
175 MatchUtils.createDstL3IPv4Match(matchBuilder, MatchUtils.iPv4PrefixFromIPv4Address(lbConfig.getVip()));
176 MatchUtils.addNxRegMatch(matchBuilder, new MatchUtils.RegMatch(REG_FIELD_A, FIRST_PASS_REGA_MATCH_VALUE));
178 String flowId = "LOADBALANCER_FORWARD_FLOW1_" + lbConfig.getProviderSegmentationId() + "_" + lbConfig.getVip();
179 flowBuilder.setId(new FlowId(flowId));
180 FlowKey key = new FlowKey(new FlowId(flowId));
181 flowBuilder.setMatch(matchBuilder.build());
182 flowBuilder.setPriority(DEFAULT_FLOW_PRIORITY);
183 flowBuilder.setBarrier(true);
184 flowBuilder.setTableId(this.getTable());
185 flowBuilder.setKey(key);
186 flowBuilder.setFlowName(flowId);
187 flowBuilder.setHardTimeout(0);
188 flowBuilder.setIdleTimeout(0);
191 // Create the OF Actions and Instructions
192 InstructionsBuilder isb = new InstructionsBuilder();
194 // Instructions List Stores Individual Instructions
195 List<Instruction> instructions = Lists.newArrayList();
197 List<Action> actionList = Lists.newArrayList();
199 ActionBuilder ab = new ActionBuilder();
200 ab.setAction(ActionUtils.nxLoadRegAction(new DstNxRegCaseBuilder().setNxReg(REG_FIELD_A).build(),
201 BigInteger.valueOf(SECOND_PASS_REGA_MATCH_VALUE)));
203 ab.setKey(new ActionKey(0));
204 actionList.add(ab.build());
206 ab = new ActionBuilder();
207 ab.setAction(ActionUtils.nxMultipathAction(OfjNxHashFields.NXHASHFIELDSSYMMETRICL4,
208 0, OfjNxMpAlgorithm.NXMPALGMODULON,
209 lbConfig.getMembers().size()-1, //By Nicira-Ext spec, this field is max_link minus 1
210 0L, new DstNxRegCaseBuilder().setNxReg(REG_FIELD_B).build(),
213 ab.setKey(new ActionKey(1));
214 actionList.add(ab.build());
216 ab = new ActionBuilder();
217 ab.setAction(ActionUtils.nxResubmitAction(null, this.getTable()));
219 ab.setKey(new ActionKey(2));
220 actionList.add(ab.build());
222 // Create an Apply Action
223 ApplyActionsBuilder aab = new ApplyActionsBuilder();
224 aab.setAction(actionList);
225 InstructionBuilder ib = new InstructionBuilder();
226 ib.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(aab.build()).build());
228 // Call the InstructionBuilder Methods Containing Actions
230 ib.setKey(new InstructionKey(0));
231 instructions.add(ib.build());
233 // Add InstructionBuilder to the Instruction(s)Builder List
234 isb.setInstruction(instructions);
236 // Add InstructionsBuilder to FlowBuilder
237 flowBuilder.setInstructions(isb.build());
239 writeFlow(flowBuilder, nodeBuilder);
242 removeFlow(flowBuilder, nodeBuilder);
247 * Method to program each rule that matches on Reg0 and Reg1 to insert appropriate header rewriting
248 * rules for all members. This function calls manageLoadBalancerMemberVIPRulesSecondPass in turn.
249 * @param nodeBuilder Node to insert rule to
250 * @param lbConfig Configuration for this LoadBalancer instance
251 * @param write Boolean to indicate of the flow is to be inserted or removed
253 private void manageLoadBalancerVIPRulesSecondPass(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig, boolean write) {
254 for(Map.Entry<String, LoadBalancerPoolMember> entry : lbConfig.getMembers().entrySet()){
255 manageLoadBalancerMemberVIPRulesSecondPass(nodeBuilder, lbConfig, entry.getValue(), write);
259 private void manageLoadBalancerMemberVIPRulesSecondPass(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig, LoadBalancerPoolMember member, boolean write) {
260 String vip = lbConfig.getVip();
262 MatchBuilder matchBuilder = new MatchBuilder();
263 FlowBuilder flowBuilder = new FlowBuilder();
265 // Match Tunnel-ID, VIP, Reg0==1 and Reg1==Index of member
266 if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VXLAN) ||
267 lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_GRE)) {
268 MatchUtils.createTunnelIDMatch(matchBuilder, new BigInteger(lbConfig.getProviderSegmentationId()));
269 } else if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VLAN)) {
270 MatchUtils.createVlanIdMatch(matchBuilder, new VlanId(Integer.valueOf(lbConfig.getProviderSegmentationId())), true);
272 return; //Should not get here. TODO: Other types
275 MatchUtils.createDstL3IPv4Match(matchBuilder, MatchUtils.iPv4PrefixFromIPv4Address(vip));
276 MatchUtils.addNxRegMatch(matchBuilder, new MatchUtils.RegMatch(REG_FIELD_A, SECOND_PASS_REGA_MATCH_VALUE),
277 new MatchUtils.RegMatch(REG_FIELD_B, (long)member.getIndex()));
279 String flowId = "LOADBALANCER_FORWARD_FLOW2_" + lbConfig.getProviderSegmentationId() + "_" +
280 vip + "_" + member.getIP();
281 flowBuilder.setId(new FlowId(flowId));
282 FlowKey key = new FlowKey(new FlowId(flowId));
283 flowBuilder.setMatch(matchBuilder.build());
284 flowBuilder.setPriority(DEFAULT_FLOW_PRIORITY+1);
285 flowBuilder.setBarrier(true);
286 flowBuilder.setTableId(this.getTable());
287 flowBuilder.setKey(key);
288 flowBuilder.setFlowName(flowId);
289 flowBuilder.setHardTimeout(0);
290 flowBuilder.setIdleTimeout(0);
293 // Create the OF Actions and Instructions
294 InstructionsBuilder isb = new InstructionsBuilder();
296 // Instructions List Stores Individual Instructions
297 List<Instruction> instructions = Lists.newArrayList();
299 List<Action> actionList = Lists.newArrayList();
300 ActionBuilder ab = new ActionBuilder();
301 ab.setAction(ActionUtils.setDlDstAction(new MacAddress(member.getMAC())));
303 ab.setKey(new ActionKey(0));
304 actionList.add(ab.build());
306 ab = new ActionBuilder();
307 Ipv4Builder ipb = new Ipv4Builder().setIpv4Address(MatchUtils.iPv4PrefixFromIPv4Address(member.getIP()));
308 ab.setAction(ActionUtils.setNwDstAction(ipb.build()));
310 ab.setKey(new ActionKey(1));
311 actionList.add(ab.build());
313 // Create an Apply Action
314 ApplyActionsBuilder aab = new ApplyActionsBuilder();
315 aab.setAction(actionList);
317 // Call the InstructionBuilder Methods Containing Actions
318 InstructionBuilder ib = new InstructionBuilder();
319 ib.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(aab.build()).build());
321 ib.setKey(new InstructionKey(0));
322 instructions.add(ib.build());
324 // Call the InstructionBuilder Methods Containing Actions
325 ib = this.getMutablePipelineInstructionBuilder();
327 ib.setKey(new InstructionKey(1));
328 instructions.add(ib.build());
330 // Add InstructionBuilder to the Instruction(s)Builder List
331 isb.setInstruction(instructions);
333 // Add InstructionsBuilder to FlowBuilder
334 flowBuilder.setInstructions(isb.build());
336 writeFlow(flowBuilder, nodeBuilder);
339 removeFlow(flowBuilder, nodeBuilder);
344 * Method to program all reverse rules that matches member {IP, Protocol, Port} for all members.
345 * This function calls manageLoadBalancerMemberReverseRules in turn.
346 * @param nodeBuilder Node to insert rule to
347 * @param lbConfig Configuration for this LoadBalancer instance
349 private void manageLoadBalancerReverseRules(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig, boolean write) {
350 for(Map.Entry<String, LoadBalancerPoolMember> entry : lbConfig.getMembers().entrySet()){
351 manageLoadBalancerMemberReverseRules(nodeBuilder, lbConfig, entry.getValue(), write);
355 private void manageLoadBalancerMemberReverseRules(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig,
356 LoadBalancerPoolMember member, boolean write) {
358 String vip = lbConfig.getVip();
359 String vmac = lbConfig.getVmac();
361 MatchBuilder matchBuilder = new MatchBuilder();
362 FlowBuilder flowBuilder = new FlowBuilder();
364 // Match Tunnel-ID, MemberIP, and Protocol/Port
365 if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VXLAN) ||
366 lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_GRE)) {
367 MatchUtils.createTunnelIDMatch(matchBuilder, new BigInteger(lbConfig.getProviderSegmentationId()));
368 } else if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VLAN)) {
369 MatchUtils.createVlanIdMatch(matchBuilder, new VlanId(Integer.valueOf(lbConfig.getProviderSegmentationId())), true);
371 return; //Should not get here. TODO: Other types
374 MatchUtils.createSrcL3IPv4Match(matchBuilder, MatchUtils.iPv4PrefixFromIPv4Address(member.getIP()));
375 MatchUtils.createSetSrcTcpMatch(matchBuilder, new PortNumber(member.getPort()));
377 String flowId = "LOADBALANCER_REVERSE_FLOW_" + lbConfig.getProviderSegmentationId() +
378 vip + "_" + member.getIP();
379 flowBuilder.setId(new FlowId(flowId));
380 FlowKey key = new FlowKey(new FlowId(flowId));
381 flowBuilder.setMatch(matchBuilder.build());
382 flowBuilder.setPriority(DEFAULT_FLOW_PRIORITY);
383 flowBuilder.setBarrier(true);
384 flowBuilder.setTableId(this.getTable());
385 flowBuilder.setKey(key);
386 flowBuilder.setFlowName(flowId);
387 flowBuilder.setHardTimeout(0);
388 flowBuilder.setIdleTimeout(0);
391 // Create the OF Actions and Instructions
392 InstructionsBuilder isb = new InstructionsBuilder();
394 // Instructions List Stores Individual Instructions
395 List<Instruction> instructions = Lists.newArrayList();
397 List<Action> actionList = Lists.newArrayList();
398 ActionBuilder ab = new ActionBuilder();
399 Ipv4Builder ipb = new Ipv4Builder().setIpv4Address(MatchUtils.iPv4PrefixFromIPv4Address(vip));
400 ab.setAction(ActionUtils.setNwSrcAction(ipb.build()));
402 ab.setKey(new ActionKey(0));
403 actionList.add(ab.build());
405 /* If a dummy MAC is assigned to the VIP, we use that as the
406 * source MAC for the reverse traffic.
409 ab = new ActionBuilder();
410 ab.setAction(ActionUtils.setDlDstAction(new MacAddress(vmac)));
412 ab.setKey(new ActionKey(1));
413 actionList.add(ab.build());
416 // Create an Apply Action
417 ApplyActionsBuilder aab = new ApplyActionsBuilder();
418 aab.setAction(actionList);
420 // Call the InstructionBuilder Methods Containing Actions
421 InstructionBuilder ib = new InstructionBuilder();
422 ib.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(aab.build()).build());
424 ib.setKey(new InstructionKey(0));
425 instructions.add(ib.build());
427 // Call the InstructionBuilder Methods Containing Actions
428 ib = this.getMutablePipelineInstructionBuilder();
430 ib.setKey(new InstructionKey(1));
431 instructions.add(ib.build());
433 // Add InstructionBuilder to the Instruction(s)Builder List
434 isb.setInstruction(instructions);
436 // Add InstructionsBuilder to FlowBuilder
437 flowBuilder.setInstructions(isb.build());
439 writeFlow(flowBuilder, nodeBuilder);
442 removeFlow(flowBuilder, nodeBuilder);
447 public void setDependencies(BundleContext bundleContext, ServiceReference serviceReference) {
448 super.setDependencies(bundleContext.getServiceReference(LoadBalancerProvider.class.getName()), this);
452 public void setDependencies(Object impl) {