Example #1
0
 def _create_listeners_flow(self):
     flows = []
     flows.append(
         database_tasks.ReloadLoadBalancer(
             name=constants.RELOAD_LB_AFTER_AMP_ASSOC_FULL_GRAPH,
             requires=constants.LOADBALANCER_ID,
             provides=constants.LOADBALANCER
         )
     )
     flows.append(
         network_tasks.CalculateDelta(
             requires=(constants.LOADBALANCER, constants.AVAILABILITY_ZONE),
             provides=constants.DELTAS
         )
     )
     flows.append(
         network_tasks.HandleNetworkDeltas(
             requires=constants.DELTAS, provides=constants.ADDED_PORTS
         )
     )
     flows.append(
         amphora_driver_tasks.AmphoraePostNetworkPlug(
             requires=(constants.LOADBALANCER, constants.ADDED_PORTS)
         )
     )
     flows.append(
         self.listener_flows.get_create_all_listeners_flow()
     )
     flows.append(
         database_tasks.MarkLBActiveInDB(
             mark_subobjects=True,
             requires=constants.LOADBALANCER
         )
     )
     return flows
Example #2
0
    def get_post_lb_amp_association_flow(self,
                                         prefix,
                                         topology,
                                         mark_active=True):
        """Reload the loadbalancer and create networking subflows for

        created/allocated amphorae.
        :return: Post amphorae association subflow
        """
        sf_name = prefix + '-' + constants.POST_LB_AMP_ASSOCIATION_SUBFLOW
        post_create_LB_flow = linear_flow.Flow(sf_name)
        post_create_LB_flow.add(
            database_tasks.ReloadLoadBalancer(
                name=sf_name + '-' + constants.RELOAD_LB_AFTER_AMP_ASSOC,
                requires=constants.LOADBALANCER_ID,
                provides=constants.LOADBALANCER))

        if topology == constants.TOPOLOGY_ACTIVE_STANDBY:
            post_create_LB_flow.add(
                database_tasks.GetAmphoraeFromLoadbalancer(
                    requires=constants.LOADBALANCER_ID,
                    provides=constants.AMPHORAE))

            vrrp_subflow = self.amp_flows.get_vrrp_subflow(prefix)
            post_create_LB_flow.add(vrrp_subflow)

        post_create_LB_flow.add(
            database_tasks.UpdateLoadbalancerInDB(
                requires=[constants.LOADBALANCER, constants.UPDATE_DICT]))
        if mark_active:
            post_create_LB_flow.add(
                database_tasks.MarkLBActiveInDB(
                    name=sf_name + '-' + constants.MARK_LB_ACTIVE_INDB,
                    requires=constants.LOADBALANCER))
        return post_create_LB_flow
Example #3
0
    def _get_amp_net_subflow(self, sf_name):
        flows = []
        flows.append(network_tasks.PlugVIPAmpphora(
            name=sf_name + '-' + constants.PLUG_VIP_AMPHORA,
            requires=(constants.LOADBALANCER, constants.AMPHORA,
                      constants.SUBNET),
            provides=constants.AMP_DATA))

        flows.append(network_tasks.ApplyQosAmphora(
            name=sf_name + '-' + constants.APPLY_QOS_AMP,
            requires=(constants.LOADBALANCER, constants.AMP_DATA,
                      constants.UPDATE_DICT)))
        flows.append(database_tasks.UpdateAmphoraVIPData(
            name=sf_name + '-' + constants.UPDATE_AMPHORA_VIP_DATA,
            requires=constants.AMP_DATA))
        flows.append(database_tasks.ReloadAmphora(
            name=sf_name + '-' + constants.RELOAD_AMP_AFTER_PLUG_VIP,
            requires=constants.AMPHORA_ID,
            provides=constants.AMPHORA))
        flows.append(database_tasks.ReloadLoadBalancer(
            name=sf_name + '-' + constants.RELOAD_LB_AFTER_PLUG_VIP,
            requires=constants.LOADBALANCER_ID,
            provides=constants.LOADBALANCER))
        flows.append(network_tasks.GetAmphoraNetworkConfigs(
            name=sf_name + '-' + constants.GET_AMP_NETWORK_CONFIG,
            requires=(constants.LOADBALANCER, constants.AMPHORA),
            provides=constants.AMPHORA_NETWORK_CONFIG))
        flows.append(amphora_driver_tasks.AmphoraPostVIPPlug(
            name=sf_name + '-' + constants.AMP_POST_VIP_PLUG,
            rebind={constants.AMPHORAE_NETWORK_CONFIG:
                    constants.AMPHORA_NETWORK_CONFIG},
            requires=(constants.LOADBALANCER,
                      constants.AMPHORAE_NETWORK_CONFIG)))
        return flows
Example #4
0
    def get_create_load_balancer_flow(self, topology, listeners=None):
        """Creates a conditional graph flow that allocates a loadbalancer to

        two spare amphorae.
        :raises InvalidTopology: Invalid topology specified
        :return: The graph flow for creating a loadbalancer.
        """
        f_name = constants.CREATE_LOADBALANCER_FLOW
        lb_create_flow = linear_flow.Flow(f_name)

        lb_create_flow.add(
            lifecycle_tasks.LoadBalancerIDToErrorOnRevertTask(
                requires=constants.LOADBALANCER_ID))

        # allocate VIP
        lb_create_flow.add(
            database_tasks.ReloadLoadBalancer(
                name=constants.RELOAD_LB_BEFOR_ALLOCATE_VIP,
                requires=constants.LOADBALANCER_ID,
                provides=constants.LOADBALANCER))
        lb_create_flow.add(
            network_tasks.AllocateVIP(requires=constants.LOADBALANCER,
                                      provides=constants.VIP))
        lb_create_flow.add(
            database_tasks.UpdateVIPAfterAllocation(
                requires=(constants.LOADBALANCER_ID, constants.VIP),
                provides=constants.LOADBALANCER))
        lb_create_flow.add(
            network_tasks.UpdateVIPSecurityGroup(
                requires=constants.LOADBALANCER_ID))
        lb_create_flow.add(
            network_tasks.GetSubnetFromVIP(requires=constants.LOADBALANCER,
                                           provides=constants.SUBNET))

        if topology == constants.TOPOLOGY_ACTIVE_STANDBY:
            lb_create_flow.add(*self._create_active_standby_topology())
        elif topology == constants.TOPOLOGY_SINGLE:
            lb_create_flow.add(*self._create_single_topology())
        else:
            LOG.error("Unknown topology: %s.  Unable to build load balancer.",
                      topology)
            raise exceptions.InvalidTopology(topology=topology)

        post_amp_prefix = constants.POST_LB_AMP_ASSOCIATION_SUBFLOW
        lb_create_flow.add(
            self.get_post_lb_amp_association_flow(post_amp_prefix,
                                                  topology,
                                                  mark_active=(not listeners)))

        if listeners:
            lb_create_flow.add(*self._create_listeners_flow())

        return lb_create_flow
Example #5
0
    def get_vthunder_for_lb_subflow(self,
                                    prefix,
                                    role=constants.ROLE_STANDALONE):
        """Flow to get vThunder for lb"""

        sf_name = prefix + '-' + constants.GET_AMPHORA_FOR_LB_SUBFLOW

        # We need a graph flow here for a conditional flow
        amp_for_lb_flow = graph_flow.Flow(sf_name)

        amp_for_lb_flow.add(
            database_tasks.ReloadLoadBalancer(
                name=sf_name + '-' + 'reload_loadbalancer',
                requires=constants.LOADBALANCER_ID,
                provides=constants.LOADBALANCER))

        # Setup the task that maps an amphora to a load balancer
        allocate_and_associate_amp = a10_database_tasks.MapLoadbalancerToAmphora(
            name=sf_name + '-' + constants.MAP_LOADBALANCER_TO_AMPHORA,
            requires=constants.LOADBALANCER,
            inject={a10constants.ROLE: role},
            provides=constants.AMPHORA_ID)

        create_amp = self._get_create_amp_for_lb_subflow(prefix, role)

        map_lb_to_vthunder = self._get_vthunder_for_amphora_subflow(
            prefix, role)

        # Add them to the graph flow
        amp_for_lb_flow.add(allocate_and_associate_amp, map_lb_to_vthunder,
                            create_amp)

        # Setup the decider for the path if we can map vThunder
        amp_for_lb_flow.link(allocate_and_associate_amp,
                             map_lb_to_vthunder,
                             decider=self._allocate_amp_to_lb_decider,
                             decider_depth='flow')
        # Setup the decider to create a vThunder
        amp_for_lb_flow.link(allocate_and_associate_amp,
                             create_amp,
                             decider=self._create_new_amp_for_lb_decider,
                             decider_depth='flow')

        return amp_for_lb_flow
Example #6
0
    def get_create_all_listeners_flow(self):
        """Create a flow to create all listeners

        :returns: The flow for creating all listeners
        """
        create_all_listeners_flow = linear_flow.Flow(
            constants.CREATE_LISTENERS_FLOW)
        create_all_listeners_flow.add(
            database_tasks.GetListenersFromLoadbalancer(
                requires=constants.LOADBALANCER,
                provides=constants.LISTENERS))
        create_all_listeners_flow.add(database_tasks.ReloadLoadBalancer(
            requires=constants.LOADBALANCER_ID,
            provides=constants.LOADBALANCER))
        create_all_listeners_flow.add(amphora_driver_tasks.ListenersUpdate(
            requires=[constants.LOADBALANCER, constants.LISTENERS]))
        create_all_listeners_flow.add(network_tasks.UpdateVIP(
            requires=constants.LOADBALANCER))
        return create_all_listeners_flow
Example #7
0
    def get_new_LB_networking_subflow(self):
        """Create a sub-flow to setup networking.

        :returns: The flow to setup networking for a new amphora
        """

        new_LB_net_subflow = linear_flow.Flow(
            constants.LOADBALANCER_NETWORKING_SUBFLOW)
        new_LB_net_subflow.add(
            network_tasks.AllocateVIP(requires=constants.LOADBALANCER,
                                      provides=constants.VIP))
        new_LB_net_subflow.add(
            database_tasks.UpdateVIPAfterAllocation(
                requires=(constants.LOADBALANCER_ID, constants.VIP),
                provides=constants.LOADBALANCER))
        new_LB_net_subflow.add(
            network_tasks.PlugVIP(requires=constants.LOADBALANCER,
                                  provides=constants.AMPS_DATA))
        new_LB_net_subflow.add(
            network_tasks.ApplyQos(requires=(constants.LOADBALANCER,
                                             constants.AMPS_DATA,
                                             constants.UPDATE_DICT)))
        new_LB_net_subflow.add(
            database_tasks.UpdateAmphoraeVIPData(requires=constants.AMPS_DATA))
        new_LB_net_subflow.add(
            database_tasks.ReloadLoadBalancer(
                name=constants.RELOAD_LB_AFTER_PLUG_VIP,
                requires=constants.LOADBALANCER_ID,
                provides=constants.LOADBALANCER))
        new_LB_net_subflow.add(
            network_tasks.GetAmphoraeNetworkConfigs(
                requires=constants.LOADBALANCER,
                provides=constants.AMPHORAE_NETWORK_CONFIG))
        new_LB_net_subflow.add(
            amphora_driver_tasks.AmphoraePostVIPPlug(
                requires=(constants.LOADBALANCER,
                          constants.AMPHORAE_NETWORK_CONFIG)))

        return new_LB_net_subflow
Example #8
0
    def get_post_lb_amp_association_flow(self,
                                         prefix,
                                         topology,
                                         mark_active=True):
        """Reload the loadbalancer and create networking subflows for

        created/allocated amphorae.
        :return: Post amphorae association subflow
        """

        # Note: If any task in this flow failed, the created amphorae will be
        #  left ''incorrectly'' allocated to the loadbalancer. Likely,
        # the get_new_LB_networking_subflow is the most prune to failure
        # shall deallocate the amphora from its loadbalancer and put it in a
        # READY state.

        sf_name = prefix + '-' + constants.POST_LB_AMP_ASSOCIATION_SUBFLOW
        post_create_LB_flow = linear_flow.Flow(sf_name)
        post_create_LB_flow.add(
            database_tasks.ReloadLoadBalancer(
                name=sf_name + '-' + constants.RELOAD_LB_AFTER_AMP_ASSOC,
                requires=constants.LOADBALANCER_ID,
                provides=constants.LOADBALANCER))

        if topology == constants.TOPOLOGY_ACTIVE_STANDBY:
            vrrp_subflow = self.amp_flows.get_vrrp_subflow(prefix)
            post_create_LB_flow.add(vrrp_subflow)

        post_create_LB_flow.add(
            database_tasks.UpdateLoadbalancerInDB(
                requires=[constants.LOADBALANCER, constants.UPDATE_DICT]))
        if mark_active:
            post_create_LB_flow.add(
                database_tasks.MarkLBActiveInDB(
                    name=sf_name + '-' + constants.MARK_LB_ACTIVE_INDB,
                    requires=constants.LOADBALANCER))
        return post_create_LB_flow
Example #9
0
    def get_failover_LB_flow(self, amps, lb):
        """Failover a load balancer.

        1. Validate the VIP port is correct and present.
        2. Build a replacement amphora.
        3. Delete the failed amphora.
        4. Configure the replacement amphora listeners.
        5. Configure VRRP for the listeners.
        6. Build the second replacement amphora.
        7. Delete the second failed amphora.
        8. Delete any extraneous amphora.
        9. Configure the listeners on the new amphorae.
        10. Configure the VRRP on the new amphorae.
        11. Reload the listener configurations to pick up VRRP changes.
        12. Mark the load balancer back to ACTIVE.

        :returns: The flow that will provide the failover.
        """
        # Pick one amphora to be failed over if any exist.
        failed_amp = None
        if amps:
            failed_amp = amps.pop()

        failover_LB_flow = linear_flow.Flow(
            constants.FAILOVER_LOADBALANCER_FLOW)

        # Revert LB to provisioning_status ERROR if this flow goes wrong
        failover_LB_flow.add(
            lifecycle_tasks.LoadBalancerToErrorOnRevertTask(
                requires=constants.LOADBALANCER))

        # Setup timeouts for our requests to the amphorae
        timeout_dict = {
            constants.CONN_MAX_RETRIES:
            CONF.haproxy_amphora.active_connection_max_retries,
            constants.CONN_RETRY_INTERVAL:
            CONF.haproxy_amphora.active_connection_rety_interval
        }

        if failed_amp:
            if failed_amp.role in (constants.ROLE_MASTER,
                                   constants.ROLE_BACKUP):
                amp_role = 'master_or_backup'
            elif failed_amp.role == constants.ROLE_STANDALONE:
                amp_role = 'standalone'
            elif failed_amp.role is None:
                amp_role = 'spare'
            else:
                amp_role = 'undefined'
            LOG.info(
                "Performing failover for amphora: %s", {
                    "id": failed_amp.id,
                    "load_balancer_id": lb.id,
                    "lb_network_ip": failed_amp.lb_network_ip,
                    "compute_id": failed_amp.compute_id,
                    "role": amp_role
                })

            failover_LB_flow.add(
                database_tasks.MarkAmphoraPendingDeleteInDB(
                    requires=constants.AMPHORA,
                    inject={constants.AMPHORA: failed_amp}))

            failover_LB_flow.add(
                database_tasks.MarkAmphoraHealthBusy(
                    requires=constants.AMPHORA,
                    inject={constants.AMPHORA: failed_amp}))

        # Check that the VIP port exists and is ok
        failover_LB_flow.add(
            network_tasks.AllocateVIP(requires=constants.LOADBALANCER,
                                      provides=constants.VIP))

        # Update the database with the VIP information
        failover_LB_flow.add(
            database_tasks.UpdateVIPAfterAllocation(
                requires=(constants.LOADBALANCER_ID, constants.VIP),
                provides=constants.LOADBALANCER))

        # Make sure the SG has the correct rules and re-apply to the
        # VIP port. It is not used on the VIP port, but will help lock
        # the SG as in use.
        failover_LB_flow.add(
            network_tasks.UpdateVIPSecurityGroup(
                requires=constants.LOADBALANCER_ID,
                provides=constants.VIP_SG_ID))

        new_amp_role = constants.ROLE_STANDALONE
        if lb.topology == constants.TOPOLOGY_ACTIVE_STANDBY:
            new_amp_role = constants.ROLE_BACKUP

        # Get a replacement amphora and plug all of the networking.
        #
        # Do this early as the compute services have been observed to be
        # unreliable. The community decided the chance that deleting first
        # would open resources for an instance is less likely than the compute
        # service failing to boot an instance for other reasons.
        if failed_amp:
            failed_vrrp_is_ipv6 = False
            if failed_amp.vrrp_ip:
                failed_vrrp_is_ipv6 = utils.is_ipv6(failed_amp.vrrp_ip)
            failover_LB_flow.add(
                self.amp_flows.get_amphora_for_lb_failover_subflow(
                    prefix=constants.FAILOVER_LOADBALANCER_FLOW,
                    role=new_amp_role,
                    failed_amp_vrrp_port_id=failed_amp.vrrp_port_id,
                    is_vrrp_ipv6=failed_vrrp_is_ipv6))
        else:
            failover_LB_flow.add(
                self.amp_flows.get_amphora_for_lb_failover_subflow(
                    prefix=constants.FAILOVER_LOADBALANCER_FLOW,
                    role=new_amp_role))

        if lb.topology == constants.TOPOLOGY_ACTIVE_STANDBY:
            failover_LB_flow.add(
                database_tasks.MarkAmphoraBackupInDB(
                    name=constants.MARK_AMP_BACKUP_INDB,
                    requires=constants.AMPHORA))

        # Delete the failed amp
        if failed_amp:
            failover_LB_flow.add(
                self.amp_flows.get_delete_amphora_flow(failed_amp))

        # Update the data stored in the flow from the database
        failover_LB_flow.add(
            database_tasks.ReloadLoadBalancer(
                requires=constants.LOADBALANCER_ID,
                provides=constants.LOADBALANCER))

        # Configure the listener(s)
        # We will run update on this amphora again later if this is
        # an active/standby load balancer because we want this amp
        # functional as soon as possible. It must run again to update
        # the configurations for the new peers.
        failover_LB_flow.add(
            amphora_driver_tasks.AmpListenersUpdate(
                name=constants.AMP_LISTENER_UPDATE,
                requires=(constants.LOADBALANCER, constants.AMPHORA),
                inject={constants.TIMEOUT_DICT: timeout_dict}))

        # Bring up the new "backup" amphora VIP now to reduce the outage
        # on the final failover. This dropped the outage from 8-9 seconds
        # to less than one in my lab.
        # This does mean some steps have to be repeated later to reconfigure
        # for the second amphora as a peer.
        if lb.topology == constants.TOPOLOGY_ACTIVE_STANDBY:

            failover_LB_flow.add(
                database_tasks.CreateVRRPGroupForLB(
                    name=new_amp_role + '-' +
                    constants.CREATE_VRRP_GROUP_FOR_LB,
                    requires=constants.LOADBALANCER_ID))

            failover_LB_flow.add(
                network_tasks.GetAmphoraNetworkConfigsByID(
                    name=(new_amp_role + '-' +
                          constants.GET_AMPHORA_NETWORK_CONFIGS_BY_ID),
                    requires=(constants.LOADBALANCER_ID, constants.AMPHORA_ID),
                    provides=constants.FIRST_AMP_NETWORK_CONFIGS))

            failover_LB_flow.add(
                amphora_driver_tasks.AmphoraUpdateVRRPInterface(
                    name=new_amp_role + '-' + constants.AMP_UPDATE_VRRP_INTF,
                    requires=constants.AMPHORA,
                    inject={constants.TIMEOUT_DICT: timeout_dict},
                    provides=constants.FIRST_AMP_VRRP_INTERFACE))

            failover_LB_flow.add(
                amphora_driver_tasks.AmphoraVRRPUpdate(
                    name=new_amp_role + '-' + constants.AMP_VRRP_UPDATE,
                    requires=(constants.LOADBALANCER_ID, constants.AMPHORA),
                    rebind={
                        constants.AMPHORAE_NETWORK_CONFIG:
                        constants.FIRST_AMP_NETWORK_CONFIGS,
                        constants.AMP_VRRP_INT:
                        constants.FIRST_AMP_VRRP_INTERFACE
                    },
                    inject={constants.TIMEOUT_DICT: timeout_dict}))

            failover_LB_flow.add(
                amphora_driver_tasks.AmphoraVRRPStart(
                    name=new_amp_role + '-' + constants.AMP_VRRP_START,
                    requires=constants.AMPHORA,
                    inject={constants.TIMEOUT_DICT: timeout_dict}))

            # Start the listener. This needs to be done here because
            # it will create the required haproxy check scripts for
            # the VRRP deployed above.
            # A "V" or newer amphora-agent will remove the need for this
            # task here.
            # TODO(johnsom) Remove this in the "X" cycle
            failover_LB_flow.add(
                amphora_driver_tasks.ListenersStart(
                    name=new_amp_role + '-' + constants.AMP_LISTENER_START,
                    requires=(constants.LOADBALANCER, constants.AMPHORA)))

            #  #### Work on standby amphora if needed #####

            new_amp_role = constants.ROLE_MASTER
            failed_amp = None
            if amps:
                failed_amp = amps.pop()

            if failed_amp:
                if failed_amp.role in (constants.ROLE_MASTER,
                                       constants.ROLE_BACKUP):
                    amp_role = 'master_or_backup'
                elif failed_amp.role == constants.ROLE_STANDALONE:
                    amp_role = 'standalone'
                elif failed_amp.role is None:
                    amp_role = 'spare'
                else:
                    amp_role = 'undefined'
                LOG.info(
                    "Performing failover for amphora: %s", {
                        "id": failed_amp.id,
                        "load_balancer_id": lb.id,
                        "lb_network_ip": failed_amp.lb_network_ip,
                        "compute_id": failed_amp.compute_id,
                        "role": amp_role
                    })

                failover_LB_flow.add(
                    database_tasks.MarkAmphoraPendingDeleteInDB(
                        name=(new_amp_role + '-' +
                              constants.MARK_AMPHORA_PENDING_DELETE),
                        requires=constants.AMPHORA,
                        inject={constants.AMPHORA: failed_amp}))

                failover_LB_flow.add(
                    database_tasks.MarkAmphoraHealthBusy(
                        name=(new_amp_role + '-' +
                              constants.MARK_AMPHORA_HEALTH_BUSY),
                        requires=constants.AMPHORA,
                        inject={constants.AMPHORA: failed_amp}))

            # Get a replacement amphora and plug all of the networking.
            #
            # Do this early as the compute services have been observed to be
            # unreliable. The community decided the chance that deleting first
            # would open resources for an instance is less likely than the
            # compute service failing to boot an instance for other reasons.
            failover_LB_flow.add(
                self.amp_flows.get_amphora_for_lb_failover_subflow(
                    prefix=(new_amp_role + '-' +
                            constants.FAILOVER_LOADBALANCER_FLOW),
                    role=new_amp_role))

            failover_LB_flow.add(
                database_tasks.MarkAmphoraMasterInDB(
                    name=constants.MARK_AMP_MASTER_INDB,
                    requires=constants.AMPHORA))

            # Delete the failed amp
            if failed_amp:
                failover_LB_flow.add(
                    self.amp_flows.get_delete_amphora_flow(failed_amp))
                failover_LB_flow.add(
                    database_tasks.DisableAmphoraHealthMonitoring(
                        name=(new_amp_role + '-' +
                              constants.DISABLE_AMP_HEALTH_MONITORING),
                        requires=constants.AMPHORA,
                        inject={constants.AMPHORA: failed_amp}))

        # Remove any extraneous amphora
        # Note: This runs in all topology situations.
        #       It should run before the act/stdby final listener update so
        #       that we don't bother attempting to update dead amphorae.
        delete_extra_amps_flow = unordered_flow.Flow(
            constants.DELETE_EXTRA_AMPHORAE_FLOW)
        for amp in amps:
            LOG.debug(
                'Found extraneous amphora %s on load balancer %s. '
                'Deleting.', amp.id, lb.id)
            delete_extra_amps_flow.add(
                self.amp_flows.get_delete_amphora_flow(amp))

        failover_LB_flow.add(delete_extra_amps_flow)

        if lb.topology == constants.TOPOLOGY_ACTIVE_STANDBY:
            # Update the data stored in the flow from the database
            failover_LB_flow.add(
                database_tasks.ReloadLoadBalancer(
                    name=new_amp_role + '-' +
                    constants.RELOAD_LB_AFTER_AMP_ASSOC,
                    requires=constants.LOADBALANCER_ID,
                    provides=constants.LOADBALANCER))

            failover_LB_flow.add(
                database_tasks.GetAmphoraeFromLoadbalancer(
                    name=new_amp_role + '-' + constants.GET_AMPHORAE_FROM_LB,
                    requires=constants.LOADBALANCER_ID,
                    provides=constants.AMPHORAE))

            # Listeners update needs to be run on all amphora to update
            # their peer configurations. So parallelize this with an
            # unordered subflow.
            update_amps_subflow = unordered_flow.Flow(
                constants.UPDATE_AMPS_SUBFLOW)

            # Setup parallel flows for each amp. We don't know the new amp
            # details at flow creation time, so setup a subflow for each
            # amp on the LB, they let the task index into a list of amps
            # to find the amphora it should work on.
            update_amps_subflow.add(
                amphora_driver_tasks.AmphoraIndexListenerUpdate(
                    name=(constants.AMPHORA + '-0-' +
                          constants.AMP_LISTENER_UPDATE),
                    requires=(constants.LOADBALANCER, constants.AMPHORAE),
                    inject={
                        constants.AMPHORA_INDEX: 0,
                        constants.TIMEOUT_DICT: timeout_dict
                    }))
            update_amps_subflow.add(
                amphora_driver_tasks.AmphoraIndexListenerUpdate(
                    name=(constants.AMPHORA + '-1-' +
                          constants.AMP_LISTENER_UPDATE),
                    requires=(constants.LOADBALANCER, constants.AMPHORAE),
                    inject={
                        constants.AMPHORA_INDEX: 1,
                        constants.TIMEOUT_DICT: timeout_dict
                    }))

            failover_LB_flow.add(update_amps_subflow)

            # Configure and enable keepalived in the amphora
            failover_LB_flow.add(
                self.amp_flows.get_vrrp_subflow(new_amp_role + '-' +
                                                constants.GET_VRRP_SUBFLOW,
                                                timeout_dict,
                                                create_vrrp_group=False))

            # #### End of standby ####

            # Reload the listener. This needs to be done here because
            # it will create the required haproxy check scripts for
            # the VRRP deployed above.
            # A "V" or newer amphora-agent will remove the need for this
            # task here.
            # TODO(johnsom) Remove this in the "X" cycle
            failover_LB_flow.add(
                amphora_driver_tasks.AmphoraIndexListenersReload(
                    name=(new_amp_role + '-' +
                          constants.AMPHORA_RELOAD_LISTENER),
                    requires=(constants.LOADBALANCER, constants.AMPHORAE),
                    inject={
                        constants.AMPHORA_INDEX: 1,
                        constants.TIMEOUT_DICT: timeout_dict
                    }))

        # Remove any extraneous ports
        # Note: Nova sometimes fails to delete ports attached to an instance.
        #       For example, if you create an LB with a listener, then
        #       'openstack server delete' the amphora, you will see the vrrp
        #       port attached to that instance will remain after the instance
        #       is deleted.
        # TODO(johnsom) Fix this as part of
        #               https://storyboard.openstack.org/#!/story/2007077

        # Mark LB ACTIVE
        failover_LB_flow.add(
            database_tasks.MarkLBActiveInDB(mark_subobjects=True,
                                            requires=constants.LOADBALANCER))

        return failover_LB_flow
Example #10
0
    def _get_vthunder_for_amphora_subflow(self, prefix, role):
        """Subflow to create lb in existing vThunder."""

        sf_name = prefix + '-' + a10constants.LB_TO_VTHUNDER_SUBFLOW
        vthunder_for_amphora_subflow = linear_flow.Flow(sf_name)
        vthunder_for_amphora_subflow.add(
            database_tasks.CreateAmphoraInDB(name=sf_name + '-' +
                                             constants.CREATE_AMPHORA_INDB,
                                             provides=constants.AMPHORA_ID))
        vthunder_for_amphora_subflow.add(
            a10_database_tasks.ValidateComputeForProject(
                name=sf_name + '-' + a10constants.VALIDATE_COMPUTE_FOR_PROJECT,
                requires=constants.LOADBALANCER,
                inject={"role": role},
                provides=constants.COMPUTE_ID))

        # if no compute, use spare vThunder and prepare network for spare vThunder
        vthunder_for_amphora_subflow.add(
            a10_database_tasks.GetSpareComputeForProject(
                name=sf_name + '-' +
                a10constants.GET_SPARE_COMPUTE_FOR_PROJECT,
                requires=constants.COMPUTE_ID,
                provides=(constants.COMPUTE_ID, a10constants.SPARE_VTHUNDER)))
        vthunder_for_amphora_subflow.add(
            a10_network_tasks.PlugVipNetworkOnSpare(
                name=sf_name + '-' + a10constants.PLUG_VIP_NETWORK_ON_SPARE,
                requires=(a10constants.SPARE_VTHUNDER, constants.LOADBALANCER),
                provides=a10constants.ADDED_NETWORK))
        vthunder_for_amphora_subflow.add(
            vthunder_tasks.SparePostNetworkPlug(
                name=sf_name + '-' + a10constants.POST_SPARE_PLUG_NETWORK,
                rebind={a10constants.VTHUNDER: a10constants.SPARE_VTHUNDER},
                requires=(a10constants.ADDED_NETWORK)))
        vthunder_for_amphora_subflow.add(
            a10_database_tasks.GetVThunderAmphora(
                name=sf_name + '-' + a10constants.GET_VTHUNDER_AMPHORA,
                rebind={a10constants.VTHUNDER: a10constants.SPARE_VTHUNDER},
                provides=constants.AMPHORA))
        vthunder_for_amphora_subflow.add(
            vthunder_tasks.VThunderComputeConnectivityWait(
                name=sf_name + '-' + constants.AMP_COMPUTE_CONNECTIVITY_WAIT,
                rebind={a10constants.VTHUNDER: a10constants.SPARE_VTHUNDER},
                requires=(constants.AMPHORA)))
        vthunder_for_amphora_subflow.add(
            vthunder_tasks.EnableInterfaceOnSpare(
                name=sf_name + '-' + a10constants.ENABLE_VTHUNDER_INTERFACE,
                rebind={a10constants.VTHUNDER: a10constants.SPARE_VTHUNDER},
                requires=(a10constants.ADDED_NETWORK)))

        vthunder_for_amphora_subflow.add(
            database_tasks.UpdateAmphoraComputeId(
                name=sf_name + '-' + constants.UPDATE_AMPHORA_COMPUTEID,
                requires=(constants.AMPHORA_ID, constants.COMPUTE_ID)))
        vthunder_for_amphora_subflow.add(
            compute_tasks.ComputeActiveWait(
                name=sf_name + '-' + constants.COMPUTE_WAIT,
                requires=(constants.COMPUTE_ID, constants.AMPHORA_ID),
                provides=constants.COMPUTE_OBJ))
        vthunder_for_amphora_subflow.add(
            database_tasks.UpdateAmphoraInfo(
                name=sf_name + '-' + constants.UPDATE_AMPHORA_INFO,
                requires=(constants.AMPHORA_ID, constants.COMPUTE_OBJ),
                provides=constants.AMPHORA))
        # create vThunder entry in custom DB
        vthunder_for_amphora_subflow.add(
            a10_database_tasks.CreateVThunderEntry(
                name=sf_name + '-' + a10constants.CREATE_VTHUNDER_ENTRY,
                requires=(constants.AMPHORA, constants.LOADBALANCER),
                inject={
                    "role": role,
                    "status": constants.PENDING_CREATE
                }))
        # Get VThunder details from database
        vthunder_for_amphora_subflow.add(
            a10_database_tasks.GetVThunderByLoadBalancer(
                name=sf_name + '-' + a10constants.VTHUNDER_BY_LB,
                requires=constants.LOADBALANCER,
                provides=a10constants.VTHUNDER))
        vthunder_for_amphora_subflow.add(
            database_tasks.ReloadLoadBalancer(
                name=sf_name + '-' + a10constants.RELOADLOAD_BALANCER,
                requires=constants.LOADBALANCER_ID,
                provides=constants.LOADBALANCER))
        vthunder_for_amphora_subflow.add(
            a10_network_tasks.GetLBResourceSubnet(
                name=sf_name + '-' + a10constants.GET_LB_RESOURCE,
                rebind={a10constants.LB_RESOURCE: constants.LOADBALANCER},
                provides=constants.SUBNET))
        vthunder_for_amphora_subflow.add(
            a10_database_tasks.GetChildProjectsOfParentPartition(
                name=sf_name + '-' + a10constants.GET_PROJECT_COUNT,
                requires=[a10constants.VTHUNDER],
                rebind={a10constants.LB_RESOURCE: constants.LOADBALANCER},
                provides=a10constants.PARTITION_PROJECT_LIST))
        vthunder_for_amphora_subflow.add(
            a10_database_tasks.CountLoadbalancersInProjectBySubnet(
                name=sf_name + '-' + a10constants.GET_LB_COUNT_SUBNET,
                requires=[
                    constants.SUBNET, a10constants.PARTITION_PROJECT_LIST
                ],
                provides=a10constants.LB_COUNT_SUBNET))
        vthunder_for_amphora_subflow.add(
            a10_network_tasks.AllocateVIP(
                name=sf_name + '-' + a10constants.ALLOCATE_VIP,
                requires=[
                    constants.LOADBALANCER, a10constants.LB_COUNT_SUBNET
                ],
                provides=constants.VIP))
        vthunder_for_amphora_subflow.add(
            database_tasks.UpdateVIPAfterAllocation(
                name=sf_name + '-' + a10constants.UPDATE_VIP_AFTER_ALLOCATION,
                requires=(constants.LOADBALANCER_ID, constants.VIP),
                provides=constants.LOADBALANCER))
        vthunder_for_amphora_subflow.add(
            database_tasks.MarkAmphoraAllocatedInDB(
                name=sf_name + '-' + constants.MARK_AMPHORA_ALLOCATED_INDB,
                requires=(constants.AMPHORA, constants.LOADBALANCER_ID)))
        vthunder_for_amphora_subflow.add(
            database_tasks.ReloadAmphora(name=sf_name + '-' +
                                         constants.RELOAD_AMPHORA,
                                         requires=constants.AMPHORA_ID,
                                         provides=constants.AMPHORA))
        if role == constants.ROLE_MASTER:
            vthunder_for_amphora_subflow.add(
                database_tasks.MarkAmphoraMasterInDB(
                    name=sf_name + '-' + constants.MARK_AMP_MASTER_INDB,
                    requires=constants.AMPHORA))
            vthunder_for_amphora_subflow.add(
                vthunder_tasks.UpdateAcosVersionInVthunderEntry(
                    name=sf_name + '-' +
                    a10constants.UPDATE_ACOS_VERSION_FOR_BACKUP_VTHUNDER,
                    requires=(a10constants.VTHUNDER)))
        elif role == constants.ROLE_BACKUP:
            vthunder_for_amphora_subflow.add(
                database_tasks.MarkAmphoraBackupInDB(
                    name=sf_name + '-' + constants.MARK_AMP_BACKUP_INDB,
                    requires=constants.AMPHORA))
            vthunder_for_amphora_subflow.add(
                a10_database_tasks.GetBackupVThunderByLoadBalancer(
                    name=sf_name + '-' + a10constants.BACKUP_VTHUNDER,
                    requires=constants.LOADBALANCER,
                    provides=a10constants.BACKUP_VTHUNDER))
            vthunder_for_amphora_subflow.add(
                vthunder_tasks.UpdateAcosVersionInVthunderEntry(
                    name=sf_name + '-' +
                    a10constants.UPDATE_ACOS_VERSION_FOR_BACKUP_VTHUNDER,
                    rebind={
                        a10constants.VTHUNDER: a10constants.BACKUP_VTHUNDER
                    }))
        elif role == constants.ROLE_STANDALONE:
            vthunder_for_amphora_subflow.add(
                database_tasks.MarkAmphoraStandAloneInDB(
                    name=sf_name + '-' + constants.MARK_AMP_STANDALONE_INDB,
                    requires=constants.AMPHORA))
            vthunder_for_amphora_subflow.add(
                vthunder_tasks.UpdateAcosVersionInVthunderEntry(
                    name=sf_name + '-' +
                    a10constants.UPDATE_ACOS_VERSION_FOR_BACKUP_VTHUNDER,
                    requires=(a10constants.VTHUNDER)))

        # If spare vThunder is used, remove spare vThunder the database
        vthunder_for_amphora_subflow.add(
            a10_database_tasks.DeleteStaleSpareVThunder(
                name=sf_name + '-' + a10constants.DELETE_STALE_SPARE_VTHUNDER,
                requires=(a10constants.SPARE_VTHUNDER)))
        return vthunder_for_amphora_subflow
Example #11
0
    def _get_create_amp_for_lb_subflow(self, prefix, role):
        """Flow to create a new vThunder for lb."""

        sf_name = prefix + '-' + constants.CREATE_AMP_FOR_LB_SUBFLOW
        create_amp_for_lb_subflow = linear_flow.Flow(sf_name)
        create_amp_for_lb_subflow.add(
            database_tasks.CreateAmphoraInDB(name=sf_name + '-' +
                                             constants.CREATE_AMPHORA_INDB,
                                             provides=constants.AMPHORA_ID))
        # VIP subnet integration at bootup
        create_amp_for_lb_subflow.add(
            database_tasks.ReloadLoadBalancer(
                name=sf_name + '-' + a10constants.RELOADLOAD_BALANCER,
                requires=constants.LOADBALANCER_ID,
                provides=constants.LOADBALANCER))
        require_server_group_id_condition = (
            role in (constants.ROLE_BACKUP, constants.ROLE_MASTER)
            and CONF.a10_nova.enable_anti_affinity)

        if require_server_group_id_condition:
            create_amp_for_lb_subflow.add(
                compute_tasks.ComputeCreate(
                    name=sf_name + '-' + constants.COMPUTE_CREATE,
                    requires=(constants.AMPHORA_ID,
                              constants.BUILD_TYPE_PRIORITY,
                              constants.SERVER_GROUP_ID,
                              constants.LOADBALANCER),
                    provides=constants.COMPUTE_ID))
        else:
            create_amp_for_lb_subflow.add(
                compute_tasks.ComputeCreate(
                    name=sf_name + '-' + constants.COMPUTE_CREATE,
                    requires=(constants.AMPHORA_ID,
                              constants.BUILD_TYPE_PRIORITY,
                              constants.LOADBALANCER),
                    provides=constants.COMPUTE_ID))

        create_amp_for_lb_subflow.add(
            database_tasks.UpdateAmphoraComputeId(
                name=sf_name + '-' + constants.UPDATE_AMPHORA_COMPUTEID,
                requires=(constants.AMPHORA_ID, constants.COMPUTE_ID)))
        create_amp_for_lb_subflow.add(
            database_tasks.MarkAmphoraBootingInDB(
                name=sf_name + '-' + constants.MARK_AMPHORA_BOOTING_INDB,
                requires=(constants.AMPHORA_ID, constants.COMPUTE_ID)))
        create_amp_for_lb_subflow.add(
            compute_tasks.ComputeActiveWait(
                name=sf_name + '-' + constants.COMPUTE_WAIT,
                requires=(constants.COMPUTE_ID, constants.AMPHORA_ID),
                provides=constants.COMPUTE_OBJ))
        create_amp_for_lb_subflow.add(
            database_tasks.UpdateAmphoraInfo(
                name=sf_name + '-' + constants.UPDATE_AMPHORA_INFO,
                requires=(constants.AMPHORA_ID, constants.COMPUTE_OBJ),
                provides=constants.AMPHORA))
        # Create vThunder entry in custom DB
        create_amp_for_lb_subflow.add(
            a10_database_tasks.CreateVThunderEntry(
                name=sf_name + '-' + a10constants.CREATE_VTHUNDER_ENTRY,
                requires=(constants.AMPHORA, constants.LOADBALANCER),
                inject={
                    a10constants.ROLE: role,
                    a10constants.STATUS: constants.PENDING_CREATE
                }))
        # Rebind requires vthunder in store and vMaster requires vThunder
        create_amp_for_lb_subflow.add(
            a10_database_tasks.GetVThunderByLoadBalancer(
                name=sf_name + '-' + a10constants.VTHUNDER_BY_LB,
                requires=constants.LOADBALANCER,
                provides=a10constants.VTHUNDER))
        # Get VThunder details from database
        if role == constants.ROLE_BACKUP:
            create_amp_for_lb_subflow.add(
                a10_database_tasks.GetBackupVThunderByLoadBalancer(
                    requires=constants.LOADBALANCER,
                    provides=a10constants.BACKUP_VTHUNDER))
            create_amp_for_lb_subflow.add(
                vthunder_tasks.VThunderComputeConnectivityWait(
                    name=sf_name + '-' + a10constants.BACKUP_CONNECTIVITY_WAIT,
                    rebind={
                        a10constants.VTHUNDER: a10constants.BACKUP_VTHUNDER
                    },
                    requires=constants.AMPHORA))
        else:
            create_amp_for_lb_subflow.add(
                vthunder_tasks.VThunderComputeConnectivityWait(
                    name=sf_name + '-' +
                    a10constants.WAIT_FOR_VTHUNDER_CONNECTIVITY,
                    requires=(a10constants.VTHUNDER, constants.AMPHORA)))
        # License the vThunder-Amphora
        create_amp_for_lb_subflow.add(
            *self.get_glm_license_subflow(prefix + '-' + role, role))
        create_amp_for_lb_subflow.add(
            database_tasks.MarkAmphoraAllocatedInDB(
                name=sf_name + '-' + constants.MARK_AMPHORA_ALLOCATED_INDB,
                requires=(constants.AMPHORA, constants.LOADBALANCER_ID)))
        create_amp_for_lb_subflow.add(
            database_tasks.ReloadAmphora(name=sf_name + '-' +
                                         constants.RELOAD_AMPHORA,
                                         requires=constants.AMPHORA_ID,
                                         provides=constants.AMPHORA))
        if role == constants.ROLE_MASTER:
            create_amp_for_lb_subflow.add(
                database_tasks.MarkAmphoraMasterInDB(
                    name=sf_name + '-' + constants.MARK_AMP_MASTER_INDB,
                    requires=constants.AMPHORA))
        elif role == constants.ROLE_BACKUP:
            create_amp_for_lb_subflow.add(
                database_tasks.MarkAmphoraBackupInDB(
                    name=sf_name + '-' + constants.MARK_AMP_BACKUP_INDB,
                    requires=constants.AMPHORA))
        elif role == constants.ROLE_STANDALONE:
            create_amp_for_lb_subflow.add(
                database_tasks.MarkAmphoraStandAloneInDB(
                    name=sf_name + '-' + constants.MARK_AMP_STANDALONE_INDB,
                    requires=constants.AMPHORA))

        if role == constants.ROLE_BACKUP:
            create_amp_for_lb_subflow.add(
                a10_database_tasks.GetBackupVThunderByLoadBalancer(
                    name=sf_name + '-' + a10constants.BACKUP_VTHUNDER,
                    requires=constants.LOADBALANCER,
                    provides=a10constants.BACKUP_VTHUNDER))
            create_amp_for_lb_subflow.add(
                vthunder_tasks.UpdateAcosVersionInVthunderEntry(
                    name=sf_name + '-' +
                    a10constants.UPDATE_ACOS_VERSION_FOR_BACKUP_VTHUNDER,
                    rebind={
                        a10constants.VTHUNDER: a10constants.BACKUP_VTHUNDER
                    }))
        else:
            create_amp_for_lb_subflow.add(
                vthunder_tasks.UpdateAcosVersionInVthunderEntry(
                    name=sf_name + '-' +
                    a10constants.UPDATE_ACOS_VERSION_FOR_BACKUP_VTHUNDER,
                    requires=(a10constants.VTHUNDER)))

        return create_amp_for_lb_subflow
Example #12
0
    def get_failover_flow(self,
                          role=constants.ROLE_STANDALONE,
                          load_balancer=None):
        """Creates a flow to failover a stale amphora

        :returns: The flow for amphora failover
        """

        failover_amphora_flow = linear_flow.Flow(
            constants.FAILOVER_AMPHORA_FLOW)

        failover_amphora_flow.add(
            lifecycle_tasks.AmphoraToErrorOnRevertTask(
                rebind={constants.AMPHORA: constants.FAILED_AMPHORA},
                requires=constants.AMPHORA))

        failover_amphora_flow.add(
            network_tasks.FailoverPreparationForAmphora(
                rebind={constants.AMPHORA: constants.FAILED_AMPHORA},
                requires=constants.AMPHORA))

        # Note: It seems intuitive to boot an amphora prior to deleting
        #       the old amphora, however this is a complicated issue.
        #       If the target host (due to anit-affinity) is resource
        #       constrained, this will fail where a post-delete will
        #       succeed. Since this is async with the API it would result
        #       in the LB ending in ERROR though the amps are still alive.
        #       Consider in the future making this a complicated
        #       try-on-failure-retry flow, or move upgrade failovers to be
        #       synchronous with the API. For now spares pool and act/stdby
        #       will mitigate most of this delay.

        # Delete the old amphora
        failover_amphora_flow.add(
            database_tasks.MarkAmphoraPendingDeleteInDB(
                rebind={constants.AMPHORA: constants.FAILED_AMPHORA},
                requires=constants.AMPHORA))
        failover_amphora_flow.add(
            database_tasks.MarkAmphoraHealthBusy(
                rebind={constants.AMPHORA: constants.FAILED_AMPHORA},
                requires=constants.AMPHORA))
        failover_amphora_flow.add(
            compute_tasks.ComputeDelete(
                rebind={constants.AMPHORA: constants.FAILED_AMPHORA},
                requires=constants.AMPHORA))
        failover_amphora_flow.add(
            network_tasks.WaitForPortDetach(
                rebind={constants.AMPHORA: constants.FAILED_AMPHORA},
                requires=constants.AMPHORA))
        failover_amphora_flow.add(
            database_tasks.MarkAmphoraDeletedInDB(
                rebind={constants.AMPHORA: constants.FAILED_AMPHORA},
                requires=constants.AMPHORA))

        # If this is an unallocated amp (spares pool), we're done
        if not load_balancer:
            failover_amphora_flow.add(
                database_tasks.DisableAmphoraHealthMonitoring(
                    rebind={constants.AMPHORA: constants.FAILED_AMPHORA},
                    requires=constants.AMPHORA))
            return failover_amphora_flow

        # Save failed amphora details for later
        failover_amphora_flow.add(
            database_tasks.GetAmphoraDetails(
                rebind={constants.AMPHORA: constants.FAILED_AMPHORA},
                requires=constants.AMPHORA,
                provides=constants.AMP_DATA))

        # Get a new amphora
        # Note: Role doesn't matter here.  We will update it later.
        get_amp_subflow = self.get_amphora_for_lb_subflow(
            prefix=constants.FAILOVER_AMPHORA_FLOW)
        failover_amphora_flow.add(get_amp_subflow)

        # Update the new amphora with the failed amphora details
        failover_amphora_flow.add(
            database_tasks.UpdateAmpFailoverDetails(
                requires=(constants.AMPHORA, constants.AMP_DATA)))

        # Update the data stored in the flow from the database
        failover_amphora_flow.add(
            database_tasks.ReloadLoadBalancer(
                requires=constants.LOADBALANCER_ID,
                provides=constants.LOADBALANCER))

        failover_amphora_flow.add(
            database_tasks.ReloadAmphora(requires=constants.AMPHORA_ID,
                                         provides=constants.AMPHORA))

        # Prepare to reconnect the network interface(s)
        failover_amphora_flow.add(
            network_tasks.GetAmphoraeNetworkConfigs(
                requires=constants.LOADBALANCER,
                provides=constants.AMPHORAE_NETWORK_CONFIG))
        failover_amphora_flow.add(
            database_tasks.GetListenersFromLoadbalancer(
                requires=constants.LOADBALANCER, provides=constants.LISTENERS))
        failover_amphora_flow.add(
            database_tasks.GetAmphoraeFromLoadbalancer(
                requires=constants.LOADBALANCER, provides=constants.AMPHORAE))

        # Plug the VIP ports into the new amphora
        # The reason for moving these steps here is the udp listeners want to
        # do some kernel configuration before Listener update for forbidding
        # failure during rebuild amphora.
        failover_amphora_flow.add(
            network_tasks.PlugVIPPort(
                requires=(constants.AMPHORA,
                          constants.AMPHORAE_NETWORK_CONFIG)))
        failover_amphora_flow.add(
            amphora_driver_tasks.AmphoraPostVIPPlug(
                requires=(constants.AMPHORA, constants.LOADBALANCER,
                          constants.AMPHORAE_NETWORK_CONFIG)))

        # Listeners update needs to be run on all amphora to update
        # their peer configurations. So parallelize this with an
        # unordered subflow.
        update_amps_subflow = unordered_flow.Flow(
            constants.UPDATE_AMPS_SUBFLOW)

        timeout_dict = {
            constants.CONN_MAX_RETRIES:
            CONF.haproxy_amphora.active_connection_max_retries,
            constants.CONN_RETRY_INTERVAL:
            CONF.haproxy_amphora.active_connection_rety_interval
        }

        # Setup parallel flows for each amp. We don't know the new amp
        # details at flow creation time, so setup a subflow for each
        # amp on the LB, they let the task index into a list of amps
        # to find the amphora it should work on.
        amp_index = 0
        for amp in load_balancer.amphorae:
            if amp.status == constants.DELETED:
                continue
            update_amps_subflow.add(
                amphora_driver_tasks.AmpListenersUpdate(
                    name=constants.AMP_LISTENER_UPDATE + '-' + str(amp_index),
                    requires=(constants.LOADBALANCER, constants.AMPHORAE),
                    inject={
                        constants.AMPHORA_INDEX: amp_index,
                        constants.TIMEOUT_DICT: timeout_dict
                    }))
            amp_index += 1

        failover_amphora_flow.add(update_amps_subflow)

        # Plug the member networks into the new amphora
        failover_amphora_flow.add(
            network_tasks.CalculateAmphoraDelta(
                requires=(constants.LOADBALANCER, constants.AMPHORA),
                provides=constants.DELTA))

        failover_amphora_flow.add(
            network_tasks.HandleNetworkDelta(requires=(constants.AMPHORA,
                                                       constants.DELTA),
                                             provides=constants.ADDED_PORTS))

        failover_amphora_flow.add(
            amphora_driver_tasks.AmphoraePostNetworkPlug(
                requires=(constants.LOADBALANCER, constants.ADDED_PORTS)))

        failover_amphora_flow.add(
            database_tasks.ReloadLoadBalancer(
                name='octavia-failover-LB-reload-2',
                requires=constants.LOADBALANCER_ID,
                provides=constants.LOADBALANCER))

        # Handle the amphora role and VRRP if necessary
        if role == constants.ROLE_MASTER:
            failover_amphora_flow.add(
                database_tasks.MarkAmphoraMasterInDB(
                    name=constants.MARK_AMP_MASTER_INDB,
                    requires=constants.AMPHORA))
            vrrp_subflow = self.get_vrrp_subflow(role)
            failover_amphora_flow.add(vrrp_subflow)
        elif role == constants.ROLE_BACKUP:
            failover_amphora_flow.add(
                database_tasks.MarkAmphoraBackupInDB(
                    name=constants.MARK_AMP_BACKUP_INDB,
                    requires=constants.AMPHORA))
            vrrp_subflow = self.get_vrrp_subflow(role)
            failover_amphora_flow.add(vrrp_subflow)
        elif role == constants.ROLE_STANDALONE:
            failover_amphora_flow.add(
                database_tasks.MarkAmphoraStandAloneInDB(
                    name=constants.MARK_AMP_STANDALONE_INDB,
                    requires=constants.AMPHORA))

        failover_amphora_flow.add(
            amphora_driver_tasks.ListenersStart(
                requires=(constants.LOADBALANCER, constants.AMPHORA)))
        failover_amphora_flow.add(
            database_tasks.DisableAmphoraHealthMonitoring(
                rebind={constants.AMPHORA: constants.FAILED_AMPHORA},
                requires=constants.AMPHORA))

        return failover_amphora_flow
    def _get_vthunder_for_amphora_subflow(self, prefix, role):
        """Subflow to create lb in existing vThunder."""

        sf_name = prefix + '-' + a10constants.LB_TO_VTHUNDER_SUBFLOW
        vthunder_for_amphora_subflow = linear_flow.Flow(sf_name)
        vthunder_for_amphora_subflow.add(database_tasks.CreateAmphoraInDB(
            name=sf_name + '-' + constants.CREATE_AMPHORA_INDB,
            provides=constants.AMPHORA_ID))
        vthunder_for_amphora_subflow.add(a10_database_tasks.ValidateComputeForProject(
            name=sf_name + '-' + a10constants.VALIDATE_COMPUTE_FOR_PROJECT,
            requires=constants.LOADBALANCER,
            inject={"role": role},
            provides=constants.COMPUTE_ID))
        vthunder_for_amphora_subflow.add(database_tasks.UpdateAmphoraComputeId(
            name=sf_name + '-' + constants.UPDATE_AMPHORA_COMPUTEID,
            requires=(constants.AMPHORA_ID, constants.COMPUTE_ID)))
        vthunder_for_amphora_subflow.add(compute_tasks.ComputeActiveWait(
            name=sf_name + '-' + constants.COMPUTE_WAIT,
            requires=(constants.COMPUTE_ID, constants.AMPHORA_ID),
            provides=constants.COMPUTE_OBJ))
        vthunder_for_amphora_subflow.add(database_tasks.UpdateAmphoraInfo(
            name=sf_name + '-' + constants.UPDATE_AMPHORA_INFO,
            requires=(constants.AMPHORA_ID, constants.COMPUTE_OBJ),
            provides=constants.AMPHORA))
        # create vThunder entry in custom DB
        vthunder_for_amphora_subflow.add(a10_database_tasks.CreateVThunderEntry(
            name=sf_name + '-' + a10constants.CREATE_VTHUNDER_ENTRY,
            requires=(constants.AMPHORA, constants.LOADBALANCER),
            inject={"role": role, "status": constants.PENDING_CREATE}))
        # Get VThunder details from database
        vthunder_for_amphora_subflow.add(a10_database_tasks.GetVThunderByLoadBalancer(
            name=sf_name + '-' + a10constants.VTHUNDER_BY_LB,
            requires=constants.LOADBALANCER,
            provides=a10constants.VTHUNDER))
        vthunder_for_amphora_subflow.add(database_tasks.ReloadLoadBalancer(
            name=sf_name + '-' + a10constants.RELOADLOAD_BALANCER,
            requires=constants.LOADBALANCER_ID,
            provides=constants.LOADBALANCER))
        vthunder_for_amphora_subflow.add(a10_network_tasks.GetLBResourceSubnet(
            name=sf_name + '-' + a10constants.GET_LB_RESOURCE,
            rebind={a10constants.LB_RESOURCE: constants.LOADBALANCER},
            provides=constants.SUBNET))
        vthunder_for_amphora_subflow.add(
            a10_database_tasks.GetChildProjectsOfParentPartition(
                name=sf_name + '-' + a10constants.GET_PROJECT_COUNT,
                requires=[a10constants.VTHUNDER],
                rebind={a10constants.LB_RESOURCE: constants.LOADBALANCER},
                provides=a10constants.PARTITION_PROJECT_LIST
            ))
        vthunder_for_amphora_subflow.add(
            a10_database_tasks.CountLoadbalancersInProjectBySubnet(
                name=sf_name + '-' + a10constants.GET_LB_COUNT_SUBNET,
                requires=[constants.SUBNET, a10constants.PARTITION_PROJECT_LIST],
                provides=a10constants.LB_COUNT_SUBNET))
        vthunder_for_amphora_subflow.add(a10_network_tasks.AllocateVIP(
            name=sf_name + '-' + a10constants.ALLOCATE_VIP,
            requires=[constants.LOADBALANCER, a10constants.LB_COUNT_SUBNET],
            provides=constants.VIP))
        vthunder_for_amphora_subflow.add(database_tasks.UpdateVIPAfterAllocation(
            name=sf_name + '-' + a10constants.UPDATE_VIP_AFTER_ALLOCATION,
            requires=(constants.LOADBALANCER_ID, constants.VIP),
            provides=constants.LOADBALANCER))
        vthunder_for_amphora_subflow.add(
            database_tasks.MarkAmphoraAllocatedInDB(
                name=sf_name + '-' + constants.MARK_AMPHORA_ALLOCATED_INDB,
                requires=(constants.AMPHORA, constants.LOADBALANCER_ID)))
        vthunder_for_amphora_subflow.add(database_tasks.ReloadAmphora(
            name=sf_name + '-' + constants.RELOAD_AMPHORA,
            requires=constants.AMPHORA_ID,
            provides=constants.AMPHORA))
        if role == constants.ROLE_MASTER:
            vthunder_for_amphora_subflow.add(database_tasks.MarkAmphoraMasterInDB(
                name=sf_name + '-' + constants.MARK_AMP_MASTER_INDB,
                requires=constants.AMPHORA))
        elif role == constants.ROLE_BACKUP:
            vthunder_for_amphora_subflow.add(database_tasks.MarkAmphoraBackupInDB(
                name=sf_name + '-' + constants.MARK_AMP_BACKUP_INDB,
                requires=constants.AMPHORA))
        elif role == constants.ROLE_STANDALONE:
            vthunder_for_amphora_subflow.add(
                database_tasks.MarkAmphoraStandAloneInDB(
                    name=sf_name + '-' + constants.MARK_AMP_STANDALONE_INDB,
                    requires=constants.AMPHORA))
        return vthunder_for_amphora_subflow