Fix broken ovsdb build due to commit in openflowplugin
[netvirt.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.controller.sal.core.Node;
17 import org.opendaylight.controller.sal.core.Node.NodeIDType;
18 import org.opendaylight.controller.sal.utils.Status;
19 import org.opendaylight.controller.sal.utils.StatusCode;
20 import org.opendaylight.ovsdb.openstack.netvirt.NetworkHandler;
21 import org.opendaylight.ovsdb.openstack.netvirt.api.Constants;
22 import org.opendaylight.ovsdb.openstack.netvirt.api.LoadBalancerConfiguration;
23 import org.opendaylight.ovsdb.openstack.netvirt.api.LoadBalancerConfiguration.LoadBalancerPoolMember;
24 import org.opendaylight.ovsdb.openstack.netvirt.api.LoadBalancerProvider;
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.Ipv4Prefix;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.PortNumber;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionKey;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.address.address.Ipv4Builder;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCaseBuilder;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActionsBuilder;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionKey;
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.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
58
59 import com.google.common.collect.Lists;
60 public class LoadBalancerService extends AbstractServiceInstance implements LoadBalancerProvider {
61
62     private static final Logger logger = LoggerFactory.getLogger(LoadBalancerProvider.class);
63     private static final int DEFAULT_FLOW_PRIORITY = 32768;
64     private static final Long FIRST_PASS_REGA_MATCH_VALUE = 0L;
65     private static final Long SECOND_PASS_REGA_MATCH_VALUE = 1L;
66
67     private static final Class<? extends NxmNxReg> REG_FIELD_A = NxmNxReg1.class;
68     private static final Class<? extends NxmNxReg> REG_FIELD_B = NxmNxReg2.class;
69
70     public LoadBalancerService() {
71         super(Service.LOAD_BALANCER);
72     }
73
74     public LoadBalancerService(Service service) {
75         super(service);
76     }
77
78     /**
79      * When this method is called, we do the following for minimizing flow updates:
80      * 1. Overwrite the solo multipath rule that applies to all members
81      * 2. Append second pass rule for the header rewriting specific to this member
82      * 3. Append reverse rules specific to this member
83      */
84     @Override
85     public Status programLoadBalancerPoolMemberRules(Node node,
86             LoadBalancerConfiguration lbConfig, LoadBalancerPoolMember member, 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         if (!node.getType().equals(NodeIDType.OPENFLOW)) {
96             logger.trace("Ignoring non-OpenFlow node {} from flow programming", node);
97             return new Status(StatusCode.BADREQUEST);
98         }
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());
101
102         NodeBuilder nodeBuilder = new NodeBuilder();
103         nodeBuilder.setId(new NodeId(Constants.OPENFLOW_NODE_PREFIX + String.valueOf(node.getID())));
104         nodeBuilder.setKey(new NodeKey(nodeBuilder.getId()));
105
106         //Update the multipath rule
107         manageLoadBalancerVIPRulesFirstPass(nodeBuilder, lbConfig, true);
108
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);
113         }
114         /* TODO: Delete single member.
115          * For now, removing a member requires deleting the full LB instance and re-adding
116          */
117         return new Status(StatusCode.NOTIMPLEMENTED);
118     }
119
120     /**
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
125      */
126     @Override
127     public Status programLoadBalancerRules(Node node, LoadBalancerConfiguration lbConfig, org.opendaylight.ovsdb.openstack.netvirt.api.Action action) {
128         if (lbConfig == null) {
129             logger.error("LB config is invalid: {}", lbConfig);
130             return new Status(StatusCode.BADREQUEST);
131         }
132         if (!lbConfig.isValid()) {
133             logger.error("LB config is invalid: {}", lbConfig);
134             return new Status(StatusCode.BADREQUEST);
135         }
136         if (!node.getType().equals(NodeIDType.OPENFLOW)) {
137             logger.trace("Ignoring non-OpenFlow node {} from flow programming", node);
138             return new Status(StatusCode.BADREQUEST);
139         }
140         logger.debug("Performing {} rules for VIP {} and {} members", action, lbConfig.getVip(), lbConfig.getMembers().size());
141
142         NodeBuilder nodeBuilder = new NodeBuilder();
143         nodeBuilder.setId(new NodeId(Constants.OPENFLOW_NODE_PREFIX + String.valueOf(node.getID())));
144         nodeBuilder.setKey(new NodeKey(nodeBuilder.getId()));
145
146         if (action.equals(org.opendaylight.ovsdb.openstack.netvirt.api.Action.ADD)) {
147             manageLoadBalancerVIPRulesFirstPass(nodeBuilder, lbConfig, true);
148             manageLoadBalancerVIPRulesSecondPass(nodeBuilder, lbConfig, true);
149             manageLoadBalancerReverseRules(nodeBuilder, lbConfig, true);
150             return new Status(StatusCode.SUCCESS);
151         }
152         else if (action.equals(org.opendaylight.ovsdb.openstack.netvirt.api.Action.DELETE)) {
153             manageLoadBalancerVIPRulesFirstPass(nodeBuilder, lbConfig, false);
154             manageLoadBalancerVIPRulesSecondPass(nodeBuilder, lbConfig, false);
155             manageLoadBalancerReverseRules(nodeBuilder, lbConfig, false);
156             return new Status(StatusCode.SUCCESS);
157         }
158
159         return new Status(StatusCode.NOTIMPLEMENTED);
160     }
161
162     /**
163      * Method to insert/remove default rule for traffic destined to the VIP and no
164      * server selection performed yet
165      * @param nodeBuilder NodeBuilder
166      * @param lbConfig LoadBalancerConfiguration
167      * @param write Boolean to indicate of the flow is to be inserted or removed
168      */
169     private void manageLoadBalancerVIPRulesFirstPass(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig, boolean write) {
170         MatchBuilder matchBuilder = new MatchBuilder();
171         FlowBuilder flowBuilder = new FlowBuilder();
172
173         // Match Tunnel-ID, VIP, and Reg0==0
174         if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VXLAN) ||
175             lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_GRE))
176             MatchUtils.createTunnelIDMatch(matchBuilder, new BigInteger(lbConfig.getProviderSegmentationId()));
177         else if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VLAN))
178             MatchUtils.createVlanIdMatch(matchBuilder, new VlanId(Integer.valueOf(lbConfig.getProviderSegmentationId())), true);
179         else
180             return; //Should not get here. TODO: Other types
181
182         MatchUtils.createDstL3IPv4Match(matchBuilder, MatchUtils.iPv4PrefixFromIPv4Address(lbConfig.getVip()));
183         MatchUtils.addNxRegMatch(matchBuilder, new MatchUtils.RegMatch(REG_FIELD_A, FIRST_PASS_REGA_MATCH_VALUE));
184
185         String flowId = "LOADBALANCER_FORWARD_FLOW1_" + lbConfig.getVip();
186         flowBuilder.setId(new FlowId(flowId));
187         FlowKey key = new FlowKey(new FlowId(flowId));
188         flowBuilder.setMatch(matchBuilder.build());
189         flowBuilder.setPriority(DEFAULT_FLOW_PRIORITY);
190         flowBuilder.setBarrier(true);
191         flowBuilder.setTableId(this.getTable());
192         flowBuilder.setKey(key);
193         flowBuilder.setFlowName(flowId);
194         flowBuilder.setHardTimeout(0);
195         flowBuilder.setIdleTimeout(0);
196
197         if (write) {
198             // Create the OF Actions and Instructions
199             InstructionsBuilder isb = new InstructionsBuilder();
200
201             // Instructions List Stores Individual Instructions
202             List<Instruction> instructions = Lists.newArrayList();
203
204             List<Action> actionList = Lists.newArrayList();
205
206             ActionBuilder ab = new ActionBuilder();
207             ab.setAction(ActionUtils.nxLoadRegAction(new DstNxRegCaseBuilder().setNxReg(REG_FIELD_A).build(),
208                     BigInteger.valueOf(SECOND_PASS_REGA_MATCH_VALUE)));
209             ab.setOrder(0);
210             ab.setKey(new ActionKey(0));
211             actionList.add(ab.build());
212
213             ab = new ActionBuilder();
214             ab.setAction(ActionUtils.nxMultipathAction(OfjNxHashFields.NXHASHFIELDSSYMMETRICL4,
215                     0, OfjNxMpAlgorithm.NXMPALGMODULON,
216                     lbConfig.getMembers().size()-1, //By Nicira-Ext spec, this field is max_link minus 1
217                     0L, new DstNxRegCaseBuilder().setNxReg(REG_FIELD_B).build(),
218                     0, 31));
219             ab.setOrder(1);
220             ab.setKey(new ActionKey(1));
221             actionList.add(ab.build());
222
223             ab = new ActionBuilder();
224             ab.setAction(ActionUtils.nxResubmitAction(null, this.getTable()));
225             ab.setOrder(2);
226             ab.setKey(new ActionKey(2));
227             actionList.add(ab.build());
228
229             // Create an Apply Action
230             ApplyActionsBuilder aab = new ApplyActionsBuilder();
231             aab.setAction(actionList);
232             InstructionBuilder ib = new InstructionBuilder();
233             ib.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(aab.build()).build());
234
235             // Call the InstructionBuilder Methods Containing Actions
236             ib.setOrder(0);
237             ib.setKey(new InstructionKey(0));
238             instructions.add(ib.build());
239
240             // Add InstructionBuilder to the Instruction(s)Builder List
241             isb.setInstruction(instructions);
242
243             // Add InstructionsBuilder to FlowBuilder
244             flowBuilder.setInstructions(isb.build());
245
246             writeFlow(flowBuilder, nodeBuilder);
247
248         } else {
249             removeFlow(flowBuilder, nodeBuilder);
250         }
251     }
252
253     /*
254      * Method to program each rule that matches on Reg0 and Reg1 to insert appropriate header rewriting
255      * rules for all members. This function calls manageLoadBalancerMemberVIPRulesSecondPass in turn.
256      * @param nodeBuilder Node to insert rule to
257      * @param lbConfig Configuration for this LoadBalancer instance
258      * @param write Boolean to indicate of the flow is to be inserted or removed
259      */
260     private void manageLoadBalancerVIPRulesSecondPass(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig, boolean write) {
261         for(Map.Entry<String, LoadBalancerPoolMember> entry : lbConfig.getMembers().entrySet()){
262             manageLoadBalancerMemberVIPRulesSecondPass(nodeBuilder, lbConfig, entry.getValue(), write);
263         }
264     }
265
266     private void manageLoadBalancerMemberVIPRulesSecondPass(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig, LoadBalancerPoolMember member, boolean write) {
267         String vip = lbConfig.getVip();
268
269         MatchBuilder matchBuilder = new MatchBuilder();
270         FlowBuilder flowBuilder = new FlowBuilder();
271
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);
278         else
279             return; //Should not get here. TODO: Other types
280
281         MatchUtils.createDstL3IPv4Match(matchBuilder, MatchUtils.iPv4PrefixFromIPv4Address(vip));
282         MatchUtils.addNxRegMatch(matchBuilder, new MatchUtils.RegMatch(REG_FIELD_A, SECOND_PASS_REGA_MATCH_VALUE),
283                                                new MatchUtils.RegMatch(REG_FIELD_B, (long)member.getIndex()));
284
285         String flowId = "LOADBALANCER_FORWARD_FLOW2_" + vip + "_" + member.getIP();
286         flowBuilder.setId(new FlowId(flowId));
287         FlowKey key = new FlowKey(new FlowId(flowId));
288         flowBuilder.setMatch(matchBuilder.build());
289         flowBuilder.setPriority(DEFAULT_FLOW_PRIORITY+1);
290         flowBuilder.setBarrier(true);
291         flowBuilder.setTableId(this.getTable());
292         flowBuilder.setKey(key);
293         flowBuilder.setFlowName(flowId);
294         flowBuilder.setHardTimeout(0);
295         flowBuilder.setIdleTimeout(0);
296
297         if (write) {
298             // Create the OF Actions and Instructions
299             InstructionsBuilder isb = new InstructionsBuilder();
300
301             // Instructions List Stores Individual Instructions
302             List<Instruction> instructions = Lists.newArrayList();
303
304             List<Action> actionList = Lists.newArrayList();
305             ActionBuilder ab = new ActionBuilder();
306             ab.setAction(ActionUtils.setDlDstAction(new MacAddress(member.getMAC())));
307             ab.setOrder(0);
308             ab.setKey(new ActionKey(0));
309             actionList.add(ab.build());
310
311             ab = new ActionBuilder();
312             Ipv4Builder ipb = new Ipv4Builder().setIpv4Address(MatchUtils.iPv4PrefixFromIPv4Address(member.getIP()));
313             ab.setAction(ActionUtils.setNwDstAction(ipb.build()));
314             ab.setOrder(1);
315             ab.setKey(new ActionKey(1));
316             actionList.add(ab.build());
317
318             // Create an Apply Action
319             ApplyActionsBuilder aab = new ApplyActionsBuilder();
320             aab.setAction(actionList);
321
322             // Call the InstructionBuilder Methods Containing Actions
323             InstructionBuilder ib = new InstructionBuilder();
324             ib.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(aab.build()).build());
325             ib.setOrder(0);
326             ib.setKey(new InstructionKey(0));
327             instructions.add(ib.build());
328
329             // Call the InstructionBuilder Methods Containing Actions
330             ib = this.getMutablePipelineInstructionBuilder();
331             ib.setOrder(1);
332             ib.setKey(new InstructionKey(1));
333             instructions.add(ib.build());
334
335             // Add InstructionBuilder to the Instruction(s)Builder List
336             isb.setInstruction(instructions);
337
338             // Add InstructionsBuilder to FlowBuilder
339             flowBuilder.setInstructions(isb.build());
340
341             writeFlow(flowBuilder, nodeBuilder);
342
343         } else {
344             removeFlow(flowBuilder, nodeBuilder);
345         }
346     }
347
348     /**
349      * Method to program all reverse rules that matches member {IP, Protocol, Port} for all members.
350      * This function calls manageLoadBalancerMemberReverseRules in turn.
351      * @param nodeBuilder Node to insert rule to
352      * @param lbConfig Configuration for this LoadBalancer instance
353      */
354     private void manageLoadBalancerReverseRules(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig, boolean write) {
355         for(Map.Entry<String, LoadBalancerPoolMember> entry : lbConfig.getMembers().entrySet()){
356             manageLoadBalancerMemberReverseRules(nodeBuilder, lbConfig, entry.getValue(), write);
357         }
358     }
359
360     private void manageLoadBalancerMemberReverseRules(NodeBuilder nodeBuilder, LoadBalancerConfiguration lbConfig,
361             LoadBalancerPoolMember member, boolean write) {
362
363         String vip = lbConfig.getVip();
364         String vmac = lbConfig.getVmac();
365
366         MatchBuilder matchBuilder = new MatchBuilder();
367         FlowBuilder flowBuilder = new FlowBuilder();
368
369         // Match Tunnel-ID, MemberIP, and Protocol/Port
370         if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VXLAN) ||
371                    lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_GRE))
372             MatchUtils.createTunnelIDMatch(matchBuilder, new BigInteger(lbConfig.getProviderSegmentationId()));
373         else if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VLAN))
374             MatchUtils.createVlanIdMatch(matchBuilder, new VlanId(Integer.valueOf(lbConfig.getProviderSegmentationId())), true);
375         else
376             return; //Should not get here. TODO: Other types
377
378         MatchUtils.createSrcL3IPv4Match(matchBuilder, MatchUtils.iPv4PrefixFromIPv4Address(member.getIP()));
379         MatchUtils.createSetSrcTcpMatch(matchBuilder, new PortNumber(member.getPort()));
380
381         String flowId = "LOADBALANCER_REVERSE_FLOW_" + vip + "_" + member.getIP();
382         flowBuilder.setId(new FlowId(flowId));
383         FlowKey key = new FlowKey(new FlowId(flowId));
384         flowBuilder.setMatch(matchBuilder.build());
385         flowBuilder.setPriority(DEFAULT_FLOW_PRIORITY);
386         flowBuilder.setBarrier(true);
387         flowBuilder.setTableId(this.getTable());
388         flowBuilder.setKey(key);
389         flowBuilder.setFlowName(flowId);
390         flowBuilder.setHardTimeout(0);
391         flowBuilder.setIdleTimeout(0);
392
393         if (write) {
394             // Create the OF Actions and Instructions
395             InstructionsBuilder isb = new InstructionsBuilder();
396
397             // Instructions List Stores Individual Instructions
398             List<Instruction> instructions = Lists.newArrayList();
399
400             List<Action> actionList = Lists.newArrayList();
401             ActionBuilder ab = new ActionBuilder();
402             Ipv4Builder ipb = new Ipv4Builder().setIpv4Address(MatchUtils.iPv4PrefixFromIPv4Address(vip));
403             ab.setAction(ActionUtils.setNwSrcAction(ipb.build()));
404             ab.setOrder(0);
405             ab.setKey(new ActionKey(0));
406             actionList.add(ab.build());
407
408             /* If a dummy MAC is assigned to the VIP, we use that as the
409              * source MAC for the reverse traffic.
410              */
411             if (vmac != null) {
412                 ab = new ActionBuilder();
413                 ab.setAction(ActionUtils.setDlDstAction(new MacAddress(vmac)));
414                 ab.setOrder(1);
415                 ab.setKey(new ActionKey(1));
416                 actionList.add(ab.build());
417             }
418
419             // Create an Apply Action
420             ApplyActionsBuilder aab = new ApplyActionsBuilder();
421             aab.setAction(actionList);
422
423             // Call the InstructionBuilder Methods Containing Actions
424             InstructionBuilder ib = new InstructionBuilder();
425             ib.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(aab.build()).build());
426             ib.setOrder(0);
427             ib.setKey(new InstructionKey(0));
428             instructions.add(ib.build());
429
430             // Call the InstructionBuilder Methods Containing Actions
431             ib = this.getMutablePipelineInstructionBuilder();
432             ib.setOrder(1);
433             ib.setKey(new InstructionKey(1));
434             instructions.add(ib.build());
435
436             // Add InstructionBuilder to the Instruction(s)Builder List
437             isb.setInstruction(instructions);
438
439             // Add InstructionsBuilder to FlowBuilder
440             flowBuilder.setInstructions(isb.build());
441
442             writeFlow(flowBuilder, nodeBuilder);
443
444         } else {
445             removeFlow(flowBuilder, nodeBuilder);
446         }
447     }
448 }