+ private String getDpid(Node node) {
+ long dpid = southbound.getDataPathId(node);
+ if (dpid == 0) {
+ LOG.warn("getDpid: DPID could not be found for node: {}", node.getNodeId().getValue());
+ }
+ return String.valueOf(dpid);
+ }
+
+ /**
+ * When this method is called, we do the following for minimizing flow updates:
+ * 1. Overwrite the solo multipath rule that applies to all members
+ * 2. Append second pass rule for the header rewriting specific to this member
+ * 3. Append reverse rules specific to this member
+ */
+ @Override
+ public Status programLoadBalancerPoolMemberRules(Node node,
+ LoadBalancerConfiguration lbConfig, LoadBalancerPoolMember member,
+ org.opendaylight.ovsdb.openstack.netvirt.api.Action action) {
+ if (lbConfig == null || member == null) {
+ LOG.error("Null value for LB config {} or Member {}", lbConfig, member);
+ return new Status(StatusCode.BADREQUEST);
+ }
+ if (!lbConfig.isValid()) {
+ LOG.error("LB config is invalid: {}", lbConfig);
+ return new Status(StatusCode.BADREQUEST);
+ }
+ LOG.debug("Performing {} rules for member {} with index {} on LB with VIP {} and total members {}",
+ action, member.getIP(), member.getIndex(), lbConfig.getVip(), lbConfig.getMembers().size());
+
+ NodeBuilder nodeBuilder = new NodeBuilder();
+ nodeBuilder.setId(new NodeId(Constants.OPENFLOW_NODE_PREFIX + getDpid(node)));
+ nodeBuilder.setKey(new NodeKey(nodeBuilder.getId()));
+
+ //Update the multipath rule
+ manageLoadBalancerVIPRulesFirstPass(nodeBuilder, lbConfig, true);
+
+ if (action.equals(org.opendaylight.ovsdb.openstack.netvirt.api.Action.ADD)) {
+ manageLoadBalancerMemberVIPRulesSecondPass(nodeBuilder, lbConfig, member, true);
+ manageLoadBalancerMemberReverseRules(nodeBuilder, lbConfig, member, true);
+ return new Status(StatusCode.SUCCESS);
+ }
+ /* TODO: Delete single member.
+ * For now, removing a member requires deleting the full LB instance and re-adding
+ */
+ return new Status(StatusCode.NOTIMPLEMENTED);
+ }
+
+ /**
+ * When this method is called, we perform the following:
+ * 1. Write the solo multipath rule that applies to all members
+ * 2. Append second pass rules for the header rewriting for all members
+ * 3. Append reverse rules for all the members, specific to the protocol/port
+ */
+ @Override
+ public Status programLoadBalancerRules(Node node, LoadBalancerConfiguration lbConfig,
+ org.opendaylight.ovsdb.openstack.netvirt.api.Action action) {
+ if (lbConfig == null || !lbConfig.isValid()) {
+ LOG.error("LB config is invalid: {}", lbConfig);
+ return new Status(StatusCode.BADREQUEST);
+ }
+ LOG.debug("Performing {} rules for VIP {} and {} members", action, lbConfig.getVip(), lbConfig.getMembers().size());
+
+ NodeBuilder nodeBuilder = new NodeBuilder();
+ nodeBuilder.setId(new NodeId(Constants.OPENFLOW_NODE_PREFIX + getDpid(node)));
+ nodeBuilder.setKey(new NodeKey(nodeBuilder.getId()));
+
+ if (action.equals(org.opendaylight.ovsdb.openstack.netvirt.api.Action.ADD)) {
+ manageLoadBalancerVIPRulesFirstPass(nodeBuilder, lbConfig, true);
+ manageLoadBalancerVIPRulesSecondPass(nodeBuilder, lbConfig, true);
+ manageLoadBalancerReverseRules(nodeBuilder, lbConfig, true);
+ return new Status(StatusCode.SUCCESS);
+ }
+ else if (action.equals(org.opendaylight.ovsdb.openstack.netvirt.api.Action.DELETE)) {
+ manageLoadBalancerVIPRulesFirstPass(nodeBuilder, lbConfig, false);
+ manageLoadBalancerVIPRulesSecondPass(nodeBuilder, lbConfig, false);
+ manageLoadBalancerReverseRules(nodeBuilder, lbConfig, false);
+ return new Status(StatusCode.SUCCESS);
+ }
+
+ return new Status(StatusCode.NOTIMPLEMENTED);
+ }
+
+ /**
+ * Method to insert/remove default rule for traffic destined to the VIP and no
+ * server selection performed yet
+ * @param nodeBuilder NodeBuilder
+ * @param lbConfig LoadBalancerConfiguration
+ * @param write Boolean to indicate of the flow is to be inserted or removed
+ */
+ private void manageLoadBalancerVIPRulesFirstPass(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig,
+ boolean write) {
+ FlowBuilder flowBuilder = new FlowBuilder();
+ String flowName = "LOADBALANCER_FORWARD_FLOW1_" + lbConfig.getProviderSegmentationId() + "_" + lbConfig.getVip();
+ FlowUtils.initFlowBuilder(flowBuilder, flowName, getTable()).setPriority(DEFAULT_FLOW_PRIORITY);
+
+ MatchBuilder matchBuilder = new MatchBuilder();
+
+ // Match Tunnel-ID, VIP, and Reg0==0
+ if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VXLAN) ||
+ lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_GRE)) {
+ MatchUtils.createTunnelIDMatch(matchBuilder, new BigInteger(lbConfig.getProviderSegmentationId()));
+ } else if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VLAN)) {
+ MatchUtils.createVlanIdMatch(matchBuilder, new VlanId(Integer.valueOf(lbConfig.getProviderSegmentationId())), true);
+ } else {
+ return; //Should not get here. TODO: Other types
+ }
+
+ MatchUtils.createDstL3IPv4Match(matchBuilder, MatchUtils.iPv4PrefixFromIPv4Address(lbConfig.getVip()));
+ MatchUtils.addNxRegMatch(matchBuilder, new MatchUtils.RegMatch(REG_FIELD_A, FIRST_PASS_REGA_MATCH_VALUE));
+ flowBuilder.setMatch(matchBuilder.build());
+
+ if (write) {
+ // Create the OF Actions and Instructions
+ InstructionsBuilder isb = new InstructionsBuilder();
+
+ // Instructions List Stores Individual Instructions
+ List<Instruction> instructions = Lists.newArrayList();
+
+ List<Action> actionList = Lists.newArrayList();
+
+ ActionBuilder ab = new ActionBuilder();
+ ab.setAction(ActionUtils.nxLoadRegAction(new DstNxRegCaseBuilder().setNxReg(REG_FIELD_A).build(),
+ BigInteger.valueOf(SECOND_PASS_REGA_MATCH_VALUE)));
+ ab.setOrder(0);
+ ab.setKey(new ActionKey(0));
+ actionList.add(ab.build());
+
+ ab = new ActionBuilder();
+ ab.setAction(ActionUtils.nxMultipathAction(OfjNxHashFields.NXHASHFIELDSSYMMETRICL4,
+ 0, OfjNxMpAlgorithm.NXMPALGMODULON,
+ lbConfig.getMembers().size()-1, //By Nicira-Ext spec, this field is max_link minus 1
+ 0L, new DstNxRegCaseBuilder().setNxReg(REG_FIELD_B).build(),
+ 0, 31));
+ ab.setOrder(1);
+ ab.setKey(new ActionKey(1));
+ actionList.add(ab.build());
+
+ ab = new ActionBuilder();
+ ab.setAction(ActionUtils.nxResubmitAction(null, this.getTable()));
+ ab.setOrder(2);
+ ab.setKey(new ActionKey(2));
+ actionList.add(ab.build());
+
+ // Create an Apply Action
+ ApplyActionsBuilder aab = new ApplyActionsBuilder();
+ aab.setAction(actionList);
+ InstructionBuilder ib = new InstructionBuilder();
+ ib.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(aab.build()).build());
+
+ // Call the InstructionBuilder Methods Containing Actions
+ ib.setOrder(0);
+ ib.setKey(new InstructionKey(0));
+ instructions.add(ib.build());
+
+ // Add InstructionBuilder to the Instruction(s)Builder List
+ isb.setInstruction(instructions);
+
+ // Add InstructionsBuilder to FlowBuilder
+ flowBuilder.setInstructions(isb.build());
+
+ writeFlow(flowBuilder, nodeBuilder);
+ } else {
+ removeFlow(flowBuilder, nodeBuilder);
+ }
+ }
+
+ /*
+ * Method to program each rule that matches on Reg0 and Reg1 to insert appropriate header rewriting
+ * rules for all members. This function calls manageLoadBalancerMemberVIPRulesSecondPass in turn.
+ * @param nodeBuilder Node to insert rule to
+ * @param lbConfig Configuration for this LoadBalancer instance
+ * @param write Boolean to indicate of the flow is to be inserted or removed
+ */
+ private void manageLoadBalancerVIPRulesSecondPass(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig, boolean write) {
+ for(Map.Entry<String, LoadBalancerPoolMember> entry : lbConfig.getMembers().entrySet()){
+ manageLoadBalancerMemberVIPRulesSecondPass(nodeBuilder, lbConfig, entry.getValue(), write);
+ }
+ }
+
+ private void manageLoadBalancerMemberVIPRulesSecondPass(NodeBuilder nodeBuilder,
+ LoadBalancerConfiguration lbConfig,
+ LoadBalancerPoolMember member, boolean write) {
+ String vip = lbConfig.getVip();
+
+ FlowBuilder flowBuilder = new FlowBuilder();
+ String flowName = "LOADBALANCER_FORWARD_FLOW2_" + lbConfig.getProviderSegmentationId() + "_"
+ + vip + "_" + member.getIP();
+ FlowUtils.initFlowBuilder(flowBuilder, flowName, getTable()).setPriority(DEFAULT_FLOW_PRIORITY+1);
+
+ MatchBuilder matchBuilder = new MatchBuilder();
+
+ // Match Tunnel-ID, VIP, Reg0==1 and Reg1==Index of member
+ if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VXLAN) ||
+ lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_GRE)) {
+ MatchUtils.createTunnelIDMatch(matchBuilder, new BigInteger(lbConfig.getProviderSegmentationId()));
+ } else if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VLAN)) {
+ MatchUtils.createVlanIdMatch(matchBuilder, new VlanId(Integer.valueOf(lbConfig.getProviderSegmentationId())), true);
+ } else {
+ return; //Should not get here. TODO: Other types
+ }
+
+ MatchUtils.createDstL3IPv4Match(matchBuilder, MatchUtils.iPv4PrefixFromIPv4Address(vip));
+ MatchUtils.addNxRegMatch(matchBuilder, new MatchUtils.RegMatch(REG_FIELD_A, SECOND_PASS_REGA_MATCH_VALUE),
+ new MatchUtils.RegMatch(REG_FIELD_B, (long)member.getIndex()));
+ flowBuilder.setMatch(matchBuilder.build());
+
+ if (write) {
+ // Create the OF Actions and Instructions
+ InstructionsBuilder isb = new InstructionsBuilder();
+
+ // Instructions List Stores Individual Instructions
+ List<Instruction> instructions = Lists.newArrayList();
+
+ List<Action> actionList = Lists.newArrayList();
+ ActionBuilder ab = new ActionBuilder();
+ ab.setAction(ActionUtils.setDlDstAction(new MacAddress(member.getMAC())));
+ ab.setOrder(0);
+ ab.setKey(new ActionKey(0));
+ actionList.add(ab.build());
+
+ ab = new ActionBuilder();
+ Ipv4Builder ipb = new Ipv4Builder().setIpv4Address(MatchUtils.iPv4PrefixFromIPv4Address(member.getIP()));
+ ab.setAction(ActionUtils.setNwDstAction(ipb.build()));
+ ab.setOrder(1);
+ ab.setKey(new ActionKey(1));
+ actionList.add(ab.build());
+
+ // Create an Apply Action
+ ApplyActionsBuilder aab = new ApplyActionsBuilder();
+ aab.setAction(actionList);
+
+ // Call the InstructionBuilder Methods Containing Actions
+ InstructionBuilder ib = new InstructionBuilder();
+ ib.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(aab.build()).build());
+ ib.setOrder(0);
+ ib.setKey(new InstructionKey(0));
+ instructions.add(ib.build());
+
+ // Call the InstructionBuilder Methods Containing Actions
+ ib = this.getMutablePipelineInstructionBuilder();
+ ib.setOrder(1);
+ ib.setKey(new InstructionKey(1));
+ instructions.add(ib.build());
+
+ // Add InstructionBuilder to the Instruction(s)Builder List
+ isb.setInstruction(instructions);
+
+ // Add InstructionsBuilder to FlowBuilder
+ flowBuilder.setInstructions(isb.build());
+
+ writeFlow(flowBuilder, nodeBuilder);
+ } else {
+ removeFlow(flowBuilder, nodeBuilder);
+ }
+ }
+
+ /**
+ * Method to program all reverse rules that matches member {IP, Protocol, Port} for all members.
+ * This function calls manageLoadBalancerMemberReverseRules in turn.
+ * @param nodeBuilder Node to insert rule to
+ * @param lbConfig Configuration for this LoadBalancer instance
+ */
+ private void manageLoadBalancerReverseRules(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig, boolean write) {
+ for(Map.Entry<String, LoadBalancerPoolMember> entry : lbConfig.getMembers().entrySet()){
+ manageLoadBalancerMemberReverseRules(nodeBuilder, lbConfig, entry.getValue(), write);
+ }
+ }
+
+ private void manageLoadBalancerMemberReverseRules(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig,
+ LoadBalancerPoolMember member, boolean write) {
+
+ String vip = lbConfig.getVip();
+ String vmac = lbConfig.getVmac();
+
+ FlowBuilder flowBuilder = new FlowBuilder();
+ String flowName = "LOADBALANCER_REVERSE_FLOW_" + lbConfig.getProviderSegmentationId() +
+ vip + "_" + member.getIP();
+ FlowUtils.initFlowBuilder(flowBuilder, flowName, getTable()).setPriority(DEFAULT_FLOW_PRIORITY);
+
+ MatchBuilder matchBuilder = new MatchBuilder();
+
+ // Match Tunnel-ID, MemberIP, and Protocol/Port
+ if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VXLAN) ||
+ lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_GRE)) {
+ MatchUtils.createTunnelIDMatch(matchBuilder, new BigInteger(lbConfig.getProviderSegmentationId()));
+ } else if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VLAN)) {
+ MatchUtils.createVlanIdMatch(matchBuilder,
+ new VlanId(Integer.valueOf(lbConfig.getProviderSegmentationId())), true);
+ } else {
+ return; //Should not get here. TODO: Other types
+ }
+
+ MatchUtils.createSrcL3IPv4Match(matchBuilder, MatchUtils.iPv4PrefixFromIPv4Address(member.getIP()));
+ MatchUtils.createSetSrcTcpMatch(matchBuilder, new PortNumber(member.getPort()));
+ flowBuilder.setMatch(matchBuilder.build());
+
+ if (write) {
+ // Create the OF Actions and Instructions
+ InstructionsBuilder isb = new InstructionsBuilder();
+
+ // Instructions List Stores Individual Instructions
+ List<Instruction> instructions = Lists.newArrayList();
+
+ List<Action> actionList = Lists.newArrayList();
+ ActionBuilder ab = new ActionBuilder();
+ Ipv4Builder ipb = new Ipv4Builder().setIpv4Address(MatchUtils.iPv4PrefixFromIPv4Address(vip));
+ ab.setAction(ActionUtils.setNwSrcAction(ipb.build()));
+ ab.setOrder(0);
+ ab.setKey(new ActionKey(0));
+ actionList.add(ab.build());
+
+ /* If a dummy MAC is assigned to the VIP, we use that as the
+ * source MAC for the reverse traffic.
+ */
+ if (vmac != null) {
+ ab = new ActionBuilder();
+ ab.setAction(ActionUtils.setDlDstAction(new MacAddress(vmac)));
+ ab.setOrder(1);
+ ab.setKey(new ActionKey(1));
+ actionList.add(ab.build());
+ }
+
+ // Create an Apply Action
+ ApplyActionsBuilder aab = new ApplyActionsBuilder();
+ aab.setAction(actionList);
+
+ // Call the InstructionBuilder Methods Containing Actions
+ InstructionBuilder ib = new InstructionBuilder();
+ ib.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(aab.build()).build());
+ ib.setOrder(0);
+ ib.setKey(new InstructionKey(0));
+ instructions.add(ib.build());
+
+ // Call the InstructionBuilder Methods Containing Actions
+ ib = this.getMutablePipelineInstructionBuilder();
+ ib.setOrder(1);
+ ib.setKey(new InstructionKey(1));
+ instructions.add(ib.build());
+
+ // Add InstructionBuilder to the Instruction(s)Builder List
+ isb.setInstruction(instructions);
+
+ // Add InstructionsBuilder to FlowBuilder
+ flowBuilder.setInstructions(isb.build());
+
+ writeFlow(flowBuilder, nodeBuilder);
+ } else {
+ removeFlow(flowBuilder, nodeBuilder);
+ }
+ }
+