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, 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
def get_delete_listener_flow(self, topology): """Flow to delete a listener""" delete_listener_flow = linear_flow.Flow(constants.DELETE_LISTENER_FLOW) delete_listener_flow.add(lifecycle_tasks.ListenerToErrorOnRevertTask( requires=constants.LISTENER)) delete_listener_flow.add(vthunder_tasks.VthunderInstanceBusy( requires=a10constants.COMPUTE_BUSY)) delete_listener_flow.add(a10_database_tasks.GetVThunderByLoadBalancer( requires=constants.LOADBALANCER, provides=a10constants.VTHUNDER)) if topology == constants.TOPOLOGY_ACTIVE_STANDBY: delete_listener_flow.add(vthunder_tasks.GetMasterVThunder( name=a10constants.GET_MASTER_VTHUNDER, requires=a10constants.VTHUNDER, provides=a10constants.VTHUNDER)) delete_listener_flow.add(self.handle_ssl_cert_flow(flow_type='delete')) delete_listener_flow.add(virtual_port_tasks.ListenerDelete( requires=[constants.LOADBALANCER, constants.LISTENER, a10constants.VTHUNDER])) delete_listener_flow.add(network_tasks.UpdateVIPForDelete( requires=constants.LOADBALANCER)) delete_listener_flow.add(database_tasks.DeleteListenerInDB( requires=constants.LISTENER)) delete_listener_flow.add(database_tasks.DecrementListenerQuota( requires=constants.LISTENER)) delete_listener_flow.add(database_tasks.MarkLBActiveInDB( requires=constants.LOADBALANCER)) delete_listener_flow.add(vthunder_tasks.WriteMemory( requires=a10constants.VTHUNDER)) delete_listener_flow.add(a10_database_tasks.SetThunderUpdatedAt( requires=a10constants.VTHUNDER)) return delete_listener_flow
def get_update_load_balancer_flow(self): """Creates a flow to update a load balancer. :returns: The flow for update a load balancer """ update_LB_flow = linear_flow.Flow(constants.UPDATE_LOADBALANCER_FLOW) update_LB_flow.add( lifecycle_tasks.LoadBalancerToErrorOnRevertTask( requires=constants.LOADBALANCER)) update_LB_flow.add( a10_database_tasks.GetVThunderByLoadBalancer( requires=constants.LOADBALANCER, provides=a10constants.VTHUNDER)) update_LB_flow.add( a10_database_tasks.MarkVThunderStatusInDB( name="set load balancer status PENDING_UPDATE", requires=a10constants.VTHUNDER, inject={"status": constants.PENDING_UPDATE})) update_LB_flow.add( network_tasks.ApplyQos(requires=(constants.LOADBALANCER, constants.UPDATE_DICT))) # update_LB_flow.add(amphora_driver_tasks.ListenersUpdate( # requires=[constants.LOADBALANCER, constants.LISTENERS])) update_LB_flow.add( virtual_server_tasks.UpdateVirtualServerTask( requires=(constants.LOADBALANCER, a10constants.VTHUNDER))) update_LB_flow.add( database_tasks.UpdateLoadbalancerInDB( requires=[constants.LOADBALANCER, constants.UPDATE_DICT])) update_LB_flow.add( database_tasks.MarkLBActiveInDB(requires=constants.LOADBALANCER)) return update_LB_flow
def get_delete_listener_flow(self): """Flow to delete a listener""" delete_listener_flow = linear_flow.Flow(constants.DELETE_LISTENER_FLOW) delete_listener_flow.add( lifecycle_tasks.ListenerToErrorOnRevertTask( requires=constants.LISTENER)) delete_listener_flow.add( a10_database_tasks.GetVThunderByLoadBalancer( requires=constants.LOADBALANCER, provides=a10constants.VTHUNDER)) delete_listener_flow.add(self.handle_ssl_cert_flow(flow_type='delete')) delete_listener_flow.add( virtual_port_tasks.ListenerDelete(requires=[ constants.LOADBALANCER, constants.LISTENER, a10constants.VTHUNDER ])) delete_listener_flow.add( network_tasks.UpdateVIPForDelete(requires=constants.LOADBALANCER)) delete_listener_flow.add( database_tasks.DeleteListenerInDB(requires=constants.LISTENER)) delete_listener_flow.add( database_tasks.DecrementListenerQuota(requires=constants.LISTENER)) delete_listener_flow.add( database_tasks.MarkLBActiveInDB(requires=constants.LOADBALANCER)) delete_listener_flow.add( vthunder_tasks.WriteMemory(requires=a10constants.VTHUNDER)) return delete_listener_flow
def get_create_amphora_for_lb_flow(self): """Creates a flow to create an amphora for a load balancer. This flow is used when there are no spare amphora available for a new load balancer. It builds an amphora and allocates for the specific load balancer. :returns: The The flow for creating the amphora """ create_amp_for_lb_flow = linear_flow.Flow( constants.CREATE_AMPHORA_FOR_LB_FLOW) create_amp_for_lb_flow.add( database_tasks.CreateAmphoraInDB(provides=constants.AMPHORA_ID)) if self.REST_AMPHORA_DRIVER: create_amp_for_lb_flow.add( cert_task.GenerateServerPEMTask(provides=constants.SERVER_PEM)) create_amp_for_lb_flow.add( compute_tasks.CertComputeCreate( requires=(constants.AMPHORA_ID, constants.SERVER_PEM), provides=constants.COMPUTE_ID)) else: create_amp_for_lb_flow.add( compute_tasks.ComputeCreate(requires=constants.AMPHORA_ID, provides=constants.COMPUTE_ID)) create_amp_for_lb_flow.add( database_tasks.UpdateAmphoraComputeId( requires=(constants.AMPHORA_ID, constants.COMPUTE_ID))) create_amp_for_lb_flow.add( database_tasks.MarkAmphoraBootingInDB( requires=(constants.AMPHORA_ID, constants.COMPUTE_ID))) wait_flow = linear_flow.Flow( constants.WAIT_FOR_AMPHORA, retry=retry.Times(CONF.controller_worker.amp_active_retries)) wait_flow.add( compute_tasks.ComputeWait(requires=constants.COMPUTE_ID, provides=constants.COMPUTE_OBJ)) wait_flow.add( database_tasks.UpdateAmphoraInfo(requires=(constants.AMPHORA_ID, constants.COMPUTE_OBJ), provides=constants.AMPHORA)) create_amp_for_lb_flow.add(wait_flow) create_amp_for_lb_flow.add( amphora_driver_tasks.AmphoraFinalize(requires=constants.AMPHORA)) create_amp_for_lb_flow.add( database_tasks.MarkAmphoraAllocatedInDB( requires=(constants.AMPHORA, constants.LOADBALANCER_ID))) create_amp_for_lb_flow.add( database_tasks.ReloadAmphora(requires=constants.AMPHORA_ID, provides=constants.AMPHORA)) create_amp_for_lb_flow.add( database_tasks.ReloadLoadBalancer( name=constants.RELOAD_LB_AFTER_AMP_ASSOC, requires=constants.LOADBALANCER_ID, provides=constants.LOADBALANCER)) new_LB_net_subflow = self._lb_flows.get_new_LB_networking_subflow() create_amp_for_lb_flow.add(new_LB_net_subflow) create_amp_for_lb_flow.add( database_tasks.MarkLBActiveInDB(requires=constants.LOADBALANCER)) return create_amp_for_lb_flow
def get_create_load_balancer_flow(self): """Creates a flow to create a load balancer. :returns: The flow for creating a load balancer """ # Note this flow is a bit strange in how it handles building # Amphora if there are no spares. TaskFlow has a spec for # a conditional flow that would make this cleaner once implemented. # https://review.openstack.org/#/c/98946/ create_LB_flow = linear_flow.Flow(constants.CREATE_LOADBALANCER_FLOW) create_LB_flow.add(database_tasks.MapLoadbalancerToAmphora( requires=constants.LOADBALANCER_ID, provides=constants.AMPHORA_ID)) create_LB_flow.add(database_tasks.ReloadAmphora( requires=constants.AMPHORA_ID, provides=constants.AMPHORA)) create_LB_flow.add(database_tasks.ReloadLoadBalancer( name=constants.RELOAD_LB_AFTER_AMP_ASSOC, requires=constants.LOADBALANCER_ID, provides=constants.LOADBALANCER)) new_LB_net_subflow = self.get_new_LB_networking_subflow() create_LB_flow.add(new_LB_net_subflow) create_LB_flow.add(database_tasks.MarkLBActiveInDB( requires=constants.LOADBALANCER)) return create_LB_flow
def get_post_lb_vthunder_association_flow(self, prefix, topology, mark_active=True): """Flow to manage networking after lb creation""" 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)) # IMP: here we will inject network flow new_LB_net_subflow = self.get_new_lb_networking_subflow(topology) post_create_lb_flow.add(new_LB_net_subflow) if topology == constants.TOPOLOGY_ACTIVE_STANDBY: vrrp_subflow = self.vthunder_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
def get_create_load_balancer_graph_flows(self, topology, prefix): allocate_amphorae_flow = self.get_create_load_balancer_flow(topology) f_name = constants.CREATE_LOADBALANCER_GRAPH_FLOW lb_create_graph_flow = linear_flow.Flow(f_name) lb_create_graph_flow.add( self.get_post_lb_amp_association_flow(prefix, topology)) lb_create_graph_flow.add( database_tasks.ReloadLoadBalancer( name=constants.RELOAD_LB_AFTER_AMP_ASSOC_FULL_GRAPH, requires=constants.LOADBALANCER_ID, provides=constants.LOADBALANCER)) lb_create_graph_flow.add( network_tasks.CalculateDelta(requires=constants.LOADBALANCER, provides=constants.DELTAS)) lb_create_graph_flow.add( network_tasks.HandleNetworkDeltas(requires=constants.DELTAS, provides=constants.ADDED_PORTS)) lb_create_graph_flow.add( amphora_driver_tasks.AmphoraePostNetworkPlug( requires=(constants.LOADBALANCER, constants.ADDED_PORTS))) lb_create_graph_flow.add( self.listener_flows.get_create_all_listeners_flow()) lb_create_graph_flow.add( database_tasks.MarkLBActiveInDB(mark_listeners=True, requires=constants.LOADBALANCER)) return allocate_amphorae_flow, lb_create_graph_flow
def get_post_lb_rack_vthunder_association_flow(self, prefix, topology, mark_active=True): """Flow to manage networking after rack lb creation""" sf_name = prefix + '-' + constants.POST_LB_AMP_ASSOCIATION_SUBFLOW post_create_lb_flow = linear_flow.Flow(sf_name) post_create_lb_flow.add( vthunder_tasks.SetupDeviceNetworkMap( requires=a10constants.VTHUNDER, provides=a10constants.VTHUNDER)) post_create_lb_flow.add( database_tasks.ReloadLoadBalancer( name=sf_name + '-' + constants.RELOAD_LB_AFTER_AMP_ASSOC, requires=constants.LOADBALANCER_ID, provides=constants.LOADBALANCER)) post_create_lb_flow.add( database_tasks.UpdateLoadbalancerInDB( requires=[constants.LOADBALANCER, constants.UPDATE_DICT])) post_create_lb_flow.add(self.handle_vrid_for_loadbalancer_subflow()) if CONF.a10_global.network_type == 'vlan': post_create_lb_flow.add( vthunder_tasks.TagInterfaceForLB( requires=[constants.LOADBALANCER, a10constants.VTHUNDER])) 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
def get_delete_rack_listener_flow(self): """Create a flow to delete a rack listener :returns: The flow for deleting a rack listener """ delete_listener_flow = linear_flow.Flow(constants.DELETE_LISTENER_FLOW) delete_listener_flow.add( lifecycle_tasks.ListenerToErrorOnRevertTask( requires=constants.LISTENER)) delete_listener_flow.add( a10_database_tasks.GetVThunderByLoadBalancer( requires=constants.LOADBALANCER, provides=a10constants.VTHUNDER)) delete_listener_flow.add( virtual_port_tasks.ListenerDelete(requires=[ constants.LOADBALANCER, constants.LISTENER, a10constants.VTHUNDER ])) delete_listener_flow.add( database_tasks.DeleteListenerInDB(requires=constants.LISTENER)) delete_listener_flow.add( database_tasks.DecrementListenerQuota(requires=constants.LISTENER)) delete_listener_flow.add( database_tasks.MarkLBActiveInDB(requires=constants.LOADBALANCER)) return delete_listener_flow
def get_update_load_balancer_flow(self): """Creates a flow to update a load balancer. :returns: The flow for update a load balancer """ update_LB_flow = linear_flow.Flow(constants.UPDATE_LOADBALANCER_FLOW) update_LB_flow.add( lifecycle_tasks.LoadBalancerToErrorOnRevertTask( requires=constants.LOADBALANCER)) update_LB_flow.add( model_tasks.UpdateAttributes( rebind={constants.OBJECT: constants.LOADBALANCER}, requires=[constants.UPDATE_DICT])) update_LB_flow.add( network_tasks.ApplyQos(requires=(constants.LOADBALANCER, constants.UPDATE_DICT))) update_LB_flow.add( amphora_driver_tasks.ListenersUpdate( requires=[constants.LOADBALANCER, constants.LISTENERS])) update_LB_flow.add( database_tasks.UpdateLoadbalancerInDB( requires=[constants.LOADBALANCER, constants.UPDATE_DICT])) update_LB_flow.add( database_tasks.MarkLBActiveInDB(requires=constants.LOADBALANCER)) return update_LB_flow
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
def get_update_load_balancer_flow(self): """Flow to update load balancer.""" update_LB_flow = linear_flow.Flow(constants.UPDATE_LOADBALANCER_FLOW) update_LB_flow.add( lifecycle_tasks.LoadBalancerToErrorOnRevertTask( requires=constants.LOADBALANCER)) update_LB_flow.add( a10_database_tasks.GetVThunderByLoadBalancer( requires=constants.LOADBALANCER, provides=a10constants.VTHUNDER)) update_LB_flow.add( a10_database_tasks.MarkVThunderStatusInDB( requires=a10constants.VTHUNDER, inject={"status": constants.PENDING_UPDATE})) update_LB_flow.add( vthunder_tasks.SetupDeviceNetworkMap( requires=a10constants.VTHUNDER, provides=a10constants.VTHUNDER)) update_LB_flow.add( network_tasks.ApplyQos(requires=(constants.LOADBALANCER, constants.UPDATE_DICT))) # update_LB_flow.add(amphora_driver_tasks.ListenersUpdate( # requires=[constants.LOADBALANCER, constants.LISTENERS])) # post_create_lb_flow.add(handle_vrid_for_loadbalancer_subflow()) update_LB_flow.add(self.handle_vrid_for_loadbalancer_subflow()) update_LB_flow.add( a10_database_tasks.GetFlavorData( rebind={a10constants.LB_RESOURCE: constants.LOADBALANCER}, provides=constants.FLAVOR_DATA)) update_LB_flow.add( virtual_server_tasks.UpdateVirtualServerTask( requires=(constants.LOADBALANCER, a10constants.VTHUNDER, constants.FLAVOR_DATA))) update_LB_flow.add( database_tasks.UpdateLoadbalancerInDB( requires=[constants.LOADBALANCER, constants.UPDATE_DICT])) if CONF.a10_global.network_type == 'vlan': update_LB_flow.add( vthunder_tasks.TagInterfaceForLB( requires=[constants.LOADBALANCER, a10constants.VTHUNDER])) update_LB_flow.add( database_tasks.MarkLBActiveInDB(requires=constants.LOADBALANCER)) update_LB_flow.add( vthunder_tasks.WriteMemory(requires=a10constants.VTHUNDER)) update_LB_flow.add( a10_database_tasks.MarkVThunderStatusInDB( name="pending_update_to_active", requires=a10constants.VTHUNDER, inject={"status": constants.ACTIVE})) update_LB_flow.add( a10_database_tasks.SetThunderUpdatedAt( requires=a10constants.VTHUNDER)) return update_LB_flow
def get_update_load_balancer_flow(self): """Creates a flow to update a load balancer. :returns: The flow for update a load balancer """ update_LB_flow = linear_flow.Flow(constants.UPDATE_LOADBALANCER_FLOW) update_LB_flow.add(controller_tasks.DisableEnableLB( requires=constants.LOADBALANCER)) update_LB_flow.add(database_tasks.UpdateLoadbalancerInDB( requires=[constants.LOADBALANCER, constants.UPDATE_DICT])) update_LB_flow.add(database_tasks.MarkLBActiveInDB( requires=constants.LOADBALANCER)) return update_LB_flow
def get_delete_listener_flow(self): """Create a flow to delete a listener :returns: The flow for deleting a listener """ delete_listener_flow = linear_flow.Flow(constants.DELETE_LISTENER_FLOW) delete_listener_flow.add(amphora_driver_tasks.ListenerDelete( requires=[constants.LOADBALANCER, constants.LISTENER])) delete_listener_flow.add(network_tasks.UpdateVIP( requires=constants.LOADBALANCER)) delete_listener_flow.add(database_tasks.DeleteListenerInDB( requires=constants.LISTENER)) delete_listener_flow.add(database_tasks.MarkLBActiveInDB( requires=constants.LOADBALANCER)) return delete_listener_flow
def test_mark_LB_active_in_db(self, mock_generate_uuid, mock_LOG, mock_get_session, mock_loadbalancer_repo_update, mock_listener_repo_update, mock_amphora_repo_update, mock_amphora_repo_delete): mark_loadbalancer_active = database_tasks.MarkLBActiveInDB() mark_loadbalancer_active.execute(self.loadbalancer_mock) repo.LoadBalancerRepository.update.assert_called_once_with( 'TEST', LB_ID, provisioning_status=constants.ACTIVE) # Test the revert mock_loadbalancer_repo_update.reset_mock() mark_loadbalancer_active.revert(self.loadbalancer_mock) repo.LoadBalancerRepository.update.assert_called_once_with( 'TEST', LB_ID, provisioning_status=constants.ERROR)
def get_update_load_balancer_flow(self): """Flow to update load balancer.""" update_LB_flow = linear_flow.Flow(constants.UPDATE_LOADBALANCER_FLOW) update_LB_flow.add( lifecycle_tasks.LoadBalancerToErrorOnRevertTask( requires=constants.LOADBALANCER)) update_LB_flow.add( a10_database_tasks.GetVThunderByLoadBalancer( requires=constants.LOADBALANCER, provides=a10constants.VTHUNDER)) update_LB_flow.add( a10_database_tasks.MarkVThunderStatusInDB( requires=a10constants.VTHUNDER, inject={"status": constants.PENDING_UPDATE})) update_LB_flow.add( vthunder_tasks.SetupDeviceNetworkMap( requires=a10constants.VTHUNDER, provides=a10constants.VTHUNDER)) update_LB_flow.add( network_tasks.ApplyQos(requires=(constants.LOADBALANCER, constants.UPDATE_DICT))) # update_LB_flow.add(amphora_driver_tasks.ListenersUpdate( # requires=[constants.LOADBALANCER, constants.LISTENERS])) update_LB_flow.add( virtual_server_tasks.UpdateVirtualServerTask( requires=(constants.LOADBALANCER, a10constants.VTHUNDER))) update_LB_flow.add( database_tasks.UpdateLoadbalancerInDB( requires=[constants.LOADBALANCER, constants.UPDATE_DICT])) if CONF.a10_global.network_type == 'vlan': update_LB_flow.add( vthunder_tasks.TagInterfaceForLB( requires=[constants.LOADBALANCER, a10constants.VTHUNDER])) update_LB_flow.add( database_tasks.MarkLBActiveInDB(requires=constants.LOADBALANCER)) update_LB_flow.add( vthunder_tasks.WriteMemory(requires=a10constants.VTHUNDER)) return update_LB_flow
def get_post_lb_rack_vthunder_association_flow(self, prefix, topology, mark_active=True): """Reload the loadbalancer and update loadbalancer in database.""" 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)) 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
def get_failover_flow(self, role=constants.ROLE_STANDALONE, status=constants.AMPHORA_READY): """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)) if status == constants.AMPHORA_ALLOCATED: failover_amphora_flow.add( database_tasks.TestLBStatusSetPendingInDB( requires=constants.LOADBALANCER_ID)) # 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.DisableAmphoraHealthMonitoring( 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 status != constants.AMPHORA_ALLOCATED: 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( amphora_driver_tasks.ListenersUpdate( requires=(constants.LOADBALANCER, constants.LISTENERS))) # Plug the VIP ports into the new 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))) # Plug the member networks into the new amphora failover_amphora_flow.add( network_tasks.CalculateDelta(requires=constants.LOADBALANCER, provides=constants.DELTAS)) failover_amphora_flow.add( network_tasks.HandleNetworkDeltas(requires=constants.DELTAS, provides=constants.ADDED_PORTS)) failover_amphora_flow.add( amphora_driver_tasks.AmphoraePostNetworkPlug( requires=(constants.LOADBALANCER, constants.ADDED_PORTS))) # 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.LISTENERS))) failover_amphora_flow.add( database_tasks.MarkLBActiveInDB(requires=[constants.LOADBALANCER])) return failover_amphora_flow
def get_failover_flow(self, role=constants.ROLE_STANDALONE, status=constants.AMPHORA_READY): """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)) if status == constants.AMPHORA_ALLOCATED: failover_amphora_flow.add( database_tasks.TestLBStatusSetPendingInDB( requires=constants.LOADBALANCER_ID)) # 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.DisableAmphoraHealthMonitoring( 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 status != constants.AMPHORA_ALLOCATED: 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( amphora_driver_tasks.ListenersUpdate( requires=(constants.LOADBALANCER, constants.LISTENERS))) # Plug the VIP ports into the new 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))) # Plug the member networks into the new amphora failover_amphora_flow.add( network_tasks.CalculateDelta(requires=constants.LOADBALANCER, provides=constants.DELTAS)) failover_amphora_flow.add( network_tasks.HandleNetworkDeltas(requires=constants.DELTAS, provides=constants.ADDED_PORTS)) failover_amphora_flow.add( amphora_driver_tasks.AmphoraePostNetworkPlug( requires=(constants.LOADBALANCER, constants.ADDED_PORTS))) # 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.LISTENERS))) failover_amphora_flow.add( database_tasks.MarkLBActiveInDB(requires=[constants.LOADBALANCER])) return failover_amphora_flow