Squashed commit of the following:
[ovsdb.git] / openstack / net-virt-providers / src / main / java / org / opendaylight / ovsdb / openstack / netvirt / providers / openflow13 / services / LoadBalancerService.java
1 /*
2  * Copyright (C) 2014 SDN Hub, LLC.
3  *
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
7  *
8  * Authors : Srini Seetharaman, Madhu Venugopal
9  */
10 package org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.services;
11
12 import java.math.BigInteger;
13 import java.util.List;
14 import java.util.Map;
15
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.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.MatchUtils;
27 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.PortNumber;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionKey;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.address.address.Ipv4Builder;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
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.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56
57 import com.google.common.collect.Lists;
58
59 public class LoadBalancerService extends AbstractServiceInstance implements LoadBalancerProvider {
60
61     private static final Logger logger = LoggerFactory.getLogger(LoadBalancerProvider.class);
62     private static final int DEFAULT_FLOW_PRIORITY = 32768;
63     private static final Long FIRST_PASS_REGA_MATCH_VALUE = 0L;
64     private static final Long SECOND_PASS_REGA_MATCH_VALUE = 1L;
65
66     private static final Class<? extends NxmNxReg> REG_FIELD_A = NxmNxReg1.class;
67     private static final Class<? extends NxmNxReg> REG_FIELD_B = NxmNxReg2.class;
68
69     public LoadBalancerService() {
70         super(Service.LOAD_BALANCER);
71     }
72
73     public LoadBalancerService(Service service) {
74         super(service);
75     }
76
77     /**
78      * When this method is called, we do the following for minimizing flow updates:
79      * 1. Overwrite the solo multipath rule that applies to all members
80      * 2. Append second pass rule for the header rewriting specific to this member
81      * 3. Append reverse rules specific to this member
82      */
83     @Override
84     public Status programLoadBalancerPoolMemberRules(Node node,
85                                                      LoadBalancerConfiguration lbConfig, LoadBalancerPoolMember member,
86                                                      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);
90         }
91         if (!lbConfig.isValid()) {
92             logger.error("LB config is invalid: {}", lbConfig);
93             return new Status(StatusCode.BADREQUEST);
94         }
95         logger.debug("Performing {} rules for member {} with index {} on LB with VIP {} and total members {}",
96                 action, member.getIP(), member.getIndex(), lbConfig.getVip(), lbConfig.getMembers().size());
97
98         NodeBuilder nodeBuilder = new NodeBuilder();
99         nodeBuilder.setId(new NodeId(Constants.OPENFLOW_NODE_PREFIX + node.getNodeId().getValue()));
100         nodeBuilder.setKey(new NodeKey(nodeBuilder.getId()));
101
102         //Update the multipath rule
103         manageLoadBalancerVIPRulesFirstPass(nodeBuilder, lbConfig, true);
104
105         if (action.equals(org.opendaylight.ovsdb.openstack.netvirt.api.Action.ADD)) {
106             manageLoadBalancerMemberVIPRulesSecondPass(nodeBuilder, lbConfig, member, true);
107             manageLoadBalancerMemberReverseRules(nodeBuilder, lbConfig, member, true);
108             return new Status(StatusCode.SUCCESS);
109         }
110         /* TODO: Delete single member.
111          * For now, removing a member requires deleting the full LB instance and re-adding
112          */
113         return new Status(StatusCode.NOTIMPLEMENTED);
114     }
115
116     /**
117      * When this method is called, we perform the following:
118      * 1. Write the solo multipath rule that applies to all members
119      * 2. Append second pass rules for the header rewriting for all members
120      * 3. Append reverse rules for all the members, specific to the protocol/port
121      */
122     @Override
123     public Status programLoadBalancerRules(Node node, LoadBalancerConfiguration lbConfig,
124                                            org.opendaylight.ovsdb.openstack.netvirt.api.Action action) {
125         if (lbConfig == null) {
126             logger.error("LB config is invalid: {}", lbConfig);
127             return new Status(StatusCode.BADREQUEST);
128         }
129         if (!lbConfig.isValid()) {
130             logger.error("LB config is invalid: {}", lbConfig);
131             return new Status(StatusCode.BADREQUEST);
132         }
133         logger.debug("Performing {} rules for VIP {} and {} members", action, lbConfig.getVip(), lbConfig.getMembers().size());
134
135         NodeBuilder nodeBuilder = new NodeBuilder();
136         nodeBuilder.setId(new NodeId(Constants.OPENFLOW_NODE_PREFIX + node.getNodeId().getValue()));
137         nodeBuilder.setKey(new NodeKey(nodeBuilder.getId()));
138
139         if (action.equals(org.opendaylight.ovsdb.openstack.netvirt.api.Action.ADD)) {
140             manageLoadBalancerVIPRulesFirstPass(nodeBuilder, lbConfig, true);
141             manageLoadBalancerVIPRulesSecondPass(nodeBuilder, lbConfig, true);
142             manageLoadBalancerReverseRules(nodeBuilder, lbConfig, true);
143             return new Status(StatusCode.SUCCESS);
144         }
145         else if (action.equals(org.opendaylight.ovsdb.openstack.netvirt.api.Action.DELETE)) {
146             manageLoadBalancerVIPRulesFirstPass(nodeBuilder, lbConfig, false);
147             manageLoadBalancerVIPRulesSecondPass(nodeBuilder, lbConfig, false);
148             manageLoadBalancerReverseRules(nodeBuilder, lbConfig, false);
149             return new Status(StatusCode.SUCCESS);
150         }
151
152         return new Status(StatusCode.NOTIMPLEMENTED);
153     }
154
155     /**
156      * Method to insert/remove default rule for traffic destined to the VIP and no
157      * server selection performed yet
158      * @param nodeBuilder NodeBuilder
159      * @param lbConfig LoadBalancerConfiguration
160      * @param write Boolean to indicate of the flow is to be inserted or removed
161      */
162     private void manageLoadBalancerVIPRulesFirstPass(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig, boolean write) {
163         MatchBuilder matchBuilder = new MatchBuilder();
164         FlowBuilder flowBuilder = new FlowBuilder();
165
166         // Match Tunnel-ID, VIP, and Reg0==0
167         if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VXLAN) ||
168             lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_GRE))
169             MatchUtils.createTunnelIDMatch(matchBuilder, new BigInteger(lbConfig.getProviderSegmentationId()));
170         else if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VLAN))
171             MatchUtils.createVlanIdMatch(matchBuilder, new VlanId(Integer.valueOf(lbConfig.getProviderSegmentationId())), true);
172         else
173             return; //Should not get here. TODO: Other types
174
175         MatchUtils.createDstL3IPv4Match(matchBuilder, MatchUtils.iPv4PrefixFromIPv4Address(lbConfig.getVip()));
176         MatchUtils.addNxRegMatch(matchBuilder, new MatchUtils.RegMatch(REG_FIELD_A, FIRST_PASS_REGA_MATCH_VALUE));
177
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);
189
190         if (write) {
191             // Create the OF Actions and Instructions
192             InstructionsBuilder isb = new InstructionsBuilder();
193
194             // Instructions List Stores Individual Instructions
195             List<Instruction> instructions = Lists.newArrayList();
196
197             List<Action> actionList = Lists.newArrayList();
198
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)));
202             ab.setOrder(0);
203             ab.setKey(new ActionKey(0));
204             actionList.add(ab.build());
205
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(),
211                     0, 31));
212             ab.setOrder(1);
213             ab.setKey(new ActionKey(1));
214             actionList.add(ab.build());
215
216             ab = new ActionBuilder();
217             ab.setAction(ActionUtils.nxResubmitAction(null, this.getTable()));
218             ab.setOrder(2);
219             ab.setKey(new ActionKey(2));
220             actionList.add(ab.build());
221
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());
227
228             // Call the InstructionBuilder Methods Containing Actions
229             ib.setOrder(0);
230             ib.setKey(new InstructionKey(0));
231             instructions.add(ib.build());
232
233             // Add InstructionBuilder to the Instruction(s)Builder List
234             isb.setInstruction(instructions);
235
236             // Add InstructionsBuilder to FlowBuilder
237             flowBuilder.setInstructions(isb.build());
238
239             writeFlow(flowBuilder, nodeBuilder);
240
241         } else {
242             removeFlow(flowBuilder, nodeBuilder);
243         }
244     }
245
246     /*
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
252      */
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);
256         }
257     }
258
259     private void manageLoadBalancerMemberVIPRulesSecondPass(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig, LoadBalancerPoolMember member, boolean write) {
260         String vip = lbConfig.getVip();
261
262         MatchBuilder matchBuilder = new MatchBuilder();
263         FlowBuilder flowBuilder = new FlowBuilder();
264
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);
271         else
272             return; //Should not get here. TODO: Other types
273
274         MatchUtils.createDstL3IPv4Match(matchBuilder, MatchUtils.iPv4PrefixFromIPv4Address(vip));
275         MatchUtils.addNxRegMatch(matchBuilder, new MatchUtils.RegMatch(REG_FIELD_A, SECOND_PASS_REGA_MATCH_VALUE),
276                                                new MatchUtils.RegMatch(REG_FIELD_B, (long)member.getIndex()));
277
278         String flowId = "LOADBALANCER_FORWARD_FLOW2_" + lbConfig.getProviderSegmentationId() + "_" +
279                         vip + "_" + member.getIP();
280         flowBuilder.setId(new FlowId(flowId));
281         FlowKey key = new FlowKey(new FlowId(flowId));
282         flowBuilder.setMatch(matchBuilder.build());
283         flowBuilder.setPriority(DEFAULT_FLOW_PRIORITY+1);
284         flowBuilder.setBarrier(true);
285         flowBuilder.setTableId(this.getTable());
286         flowBuilder.setKey(key);
287         flowBuilder.setFlowName(flowId);
288         flowBuilder.setHardTimeout(0);
289         flowBuilder.setIdleTimeout(0);
290
291         if (write) {
292             // Create the OF Actions and Instructions
293             InstructionsBuilder isb = new InstructionsBuilder();
294
295             // Instructions List Stores Individual Instructions
296             List<Instruction> instructions = Lists.newArrayList();
297
298             List<Action> actionList = Lists.newArrayList();
299             ActionBuilder ab = new ActionBuilder();
300             ab.setAction(ActionUtils.setDlDstAction(new MacAddress(member.getMAC())));
301             ab.setOrder(0);
302             ab.setKey(new ActionKey(0));
303             actionList.add(ab.build());
304
305             ab = new ActionBuilder();
306             Ipv4Builder ipb = new Ipv4Builder().setIpv4Address(MatchUtils.iPv4PrefixFromIPv4Address(member.getIP()));
307             ab.setAction(ActionUtils.setNwDstAction(ipb.build()));
308             ab.setOrder(1);
309             ab.setKey(new ActionKey(1));
310             actionList.add(ab.build());
311
312             // Create an Apply Action
313             ApplyActionsBuilder aab = new ApplyActionsBuilder();
314             aab.setAction(actionList);
315
316             // Call the InstructionBuilder Methods Containing Actions
317             InstructionBuilder ib = new InstructionBuilder();
318             ib.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(aab.build()).build());
319             ib.setOrder(0);
320             ib.setKey(new InstructionKey(0));
321             instructions.add(ib.build());
322
323             // Call the InstructionBuilder Methods Containing Actions
324             ib = this.getMutablePipelineInstructionBuilder();
325             ib.setOrder(1);
326             ib.setKey(new InstructionKey(1));
327             instructions.add(ib.build());
328
329             // Add InstructionBuilder to the Instruction(s)Builder List
330             isb.setInstruction(instructions);
331
332             // Add InstructionsBuilder to FlowBuilder
333             flowBuilder.setInstructions(isb.build());
334
335             writeFlow(flowBuilder, nodeBuilder);
336
337         } else {
338             removeFlow(flowBuilder, nodeBuilder);
339         }
340     }
341
342     /**
343      * Method to program all reverse rules that matches member {IP, Protocol, Port} for all members.
344      * This function calls manageLoadBalancerMemberReverseRules in turn.
345      * @param nodeBuilder Node to insert rule to
346      * @param lbConfig Configuration for this LoadBalancer instance
347      */
348     private void manageLoadBalancerReverseRules(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig, boolean write) {
349         for(Map.Entry<String, LoadBalancerPoolMember> entry : lbConfig.getMembers().entrySet()){
350             manageLoadBalancerMemberReverseRules(nodeBuilder, lbConfig, entry.getValue(), write);
351         }
352     }
353
354     private void manageLoadBalancerMemberReverseRules(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig,
355             LoadBalancerPoolMember member, boolean write) {
356
357         String vip = lbConfig.getVip();
358         String vmac = lbConfig.getVmac();
359
360         MatchBuilder matchBuilder = new MatchBuilder();
361         FlowBuilder flowBuilder = new FlowBuilder();
362
363         // Match Tunnel-ID, MemberIP, and Protocol/Port
364         if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VXLAN) ||
365                    lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_GRE))
366             MatchUtils.createTunnelIDMatch(matchBuilder, new BigInteger(lbConfig.getProviderSegmentationId()));
367         else if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VLAN))
368             MatchUtils.createVlanIdMatch(matchBuilder, new VlanId(Integer.valueOf(lbConfig.getProviderSegmentationId())), true);
369         else
370             return; //Should not get here. TODO: Other types
371
372         MatchUtils.createSrcL3IPv4Match(matchBuilder, MatchUtils.iPv4PrefixFromIPv4Address(member.getIP()));
373         MatchUtils.createSetSrcTcpMatch(matchBuilder, new PortNumber(member.getPort()));
374
375         String flowId = "LOADBALANCER_REVERSE_FLOW_" + lbConfig.getProviderSegmentationId() +
376                         vip + "_" + member.getIP();
377         flowBuilder.setId(new FlowId(flowId));
378         FlowKey key = new FlowKey(new FlowId(flowId));
379         flowBuilder.setMatch(matchBuilder.build());
380         flowBuilder.setPriority(DEFAULT_FLOW_PRIORITY);
381         flowBuilder.setBarrier(true);
382         flowBuilder.setTableId(this.getTable());
383         flowBuilder.setKey(key);
384         flowBuilder.setFlowName(flowId);
385         flowBuilder.setHardTimeout(0);
386         flowBuilder.setIdleTimeout(0);
387
388         if (write) {
389             // Create the OF Actions and Instructions
390             InstructionsBuilder isb = new InstructionsBuilder();
391
392             // Instructions List Stores Individual Instructions
393             List<Instruction> instructions = Lists.newArrayList();
394
395             List<Action> actionList = Lists.newArrayList();
396             ActionBuilder ab = new ActionBuilder();
397             Ipv4Builder ipb = new Ipv4Builder().setIpv4Address(MatchUtils.iPv4PrefixFromIPv4Address(vip));
398             ab.setAction(ActionUtils.setNwSrcAction(ipb.build()));
399             ab.setOrder(0);
400             ab.setKey(new ActionKey(0));
401             actionList.add(ab.build());
402
403             /* If a dummy MAC is assigned to the VIP, we use that as the
404              * source MAC for the reverse traffic.
405              */
406             if (vmac != null) {
407                 ab = new ActionBuilder();
408                 ab.setAction(ActionUtils.setDlDstAction(new MacAddress(vmac)));
409                 ab.setOrder(1);
410                 ab.setKey(new ActionKey(1));
411                 actionList.add(ab.build());
412             }
413
414             // Create an Apply Action
415             ApplyActionsBuilder aab = new ApplyActionsBuilder();
416             aab.setAction(actionList);
417
418             // Call the InstructionBuilder Methods Containing Actions
419             InstructionBuilder ib = new InstructionBuilder();
420             ib.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(aab.build()).build());
421             ib.setOrder(0);
422             ib.setKey(new InstructionKey(0));
423             instructions.add(ib.build());
424
425             // Call the InstructionBuilder Methods Containing Actions
426             ib = this.getMutablePipelineInstructionBuilder();
427             ib.setOrder(1);
428             ib.setKey(new InstructionKey(1));
429             instructions.add(ib.build());
430
431             // Add InstructionBuilder to the Instruction(s)Builder List
432             isb.setInstruction(instructions);
433
434             // Add InstructionsBuilder to FlowBuilder
435             flowBuilder.setInstructions(isb.build());
436
437             writeFlow(flowBuilder, nodeBuilder);
438
439         } else {
440             removeFlow(flowBuilder, nodeBuilder);
441         }
442     }
443 }