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 _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_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_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_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(database_tasks.UpdateAmphoraVIPData( 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.AmphoraPostVIPPlug( requires=(constants.LOADBALANCER, constants.AMPHORAE_NETWORK_CONFIG))) return new_LB_net_subflow
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_rack_vthunder_for_lb_subflow(self, vthunder_conf, prefix, role=constants.ROLE_STANDALONE): """ reload the loadbalancer and make entry in database""" sf_name = prefix + '-' + constants.GET_AMPHORA_FOR_LB_SUBFLOW amp_for_lb_flow = linear_flow.Flow(sf_name) amp_for_lb_flow.add( database_tasks.ReloadLoadBalancer( name=sf_name + '-' + 'reload_loadbalancer', requires=constants.LOADBALANCER_ID, provides=constants.LOADBALANCER)) amp_for_lb_flow.add( a10_database_tasks.CreateRackVthunderEntry( name=sf_name + '-' + 'create_rack_vThunder_entry_in_database', inject={a10constants.VTHUNDER_CONFIG: vthunder_conf}, requires=(constants.LOADBALANCER, a10constants.VTHUNDER_CONFIG))) amp_for_lb_flow.add( a10_database_tasks.GetVThunderByLoadBalancer( requires=constants.LOADBALANCER, provides=a10constants.VTHUNDER)) amp_for_lb_flow.add( vthunder_tasks.HandleACOSPartitionChange( name=sf_name + '-' + a10constants.CHANGE_PARTITION, requires=a10constants.VTHUNDER)) return amp_for_lb_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_create_rack_vthunder_load_balancer_flow(self, vthunder_conf, topology, listeners=None): """Flow to create rack load balancer""" 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)) lb_create_flow.add( database_tasks.ReloadLoadBalancer( requires=constants.LOADBALANCER_ID, provides=constants.LOADBALANCER)) lb_create_flow.add( a10_database_tasks.CheckExistingProjectToThunderMappedEntries( inject={a10constants.VTHUNDER_CONFIG: vthunder_conf}, requires=(constants.LOADBALANCER, a10constants.VTHUNDER_CONFIG), provides=a10constants.VTHUNDER_CONFIG)) lb_create_flow.add( a10_database_tasks.CheckExistingThunderToProjectMappedEntries( requires=(constants.LOADBALANCER, a10constants.VTHUNDER_CONFIG))) lb_create_flow.add( self.vthunder_flows.get_rack_vthunder_for_lb_subflow( vthunder_conf=a10constants.VTHUNDER_CONFIG, prefix=constants.ROLE_STANDALONE, role=constants.ROLE_STANDALONE)) post_amp_prefix = constants.POST_LB_AMP_ASSOCIATION_SUBFLOW lb_create_flow.add( self.get_post_lb_rack_vthunder_association_flow( post_amp_prefix, topology, mark_active=(not listeners))) lb_create_flow.add( a10_database_tasks.GetFlavorData( rebind={a10constants.LB_RESOURCE: constants.LOADBALANCER}, provides=constants.FLAVOR_DATA)) lb_create_flow.add( nat_pool_tasks.NatPoolCreate(requires=(constants.LOADBALANCER, a10constants.VTHUNDER, constants.FLAVOR_DATA))) lb_create_flow.add( virtual_server_tasks.CreateVirtualServerTask( requires=(constants.LOADBALANCER, a10constants.VTHUNDER, constants.FLAVOR_DATA), provides=a10constants.STATUS)) lb_create_flow.add( vthunder_tasks.WriteMemory(requires=a10constants.VTHUNDER)) lb_create_flow.add( a10_database_tasks.SetThunderUpdatedAt( requires=a10constants.VTHUNDER)) return lb_create_flow
def test_reload_load_balancer(self, mock_lb_get, mock_generate_uuid, mock_LOG, mock_get_session, mock_loadbalancer_repo_update, mock_listener_repo_update, mock_amphora_repo_update, mock_amphora_repo_delete): reload_lb = database_tasks.ReloadLoadBalancer() lb = reload_lb.execute(LB_ID) repo.LoadBalancerRepository.get.assert_called_once_with('TEST', id=LB_ID) self.assertEqual(_loadbalancer_mock, lb)
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)) 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
def get_vthunder_for_lb_subflow( self, prefix, role=constants.ROLE_STANDALONE): """Tries to allocate a spare amphora to a loadbalancer if none exists, create a new amphora. """ 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, provides=constants.AMPHORA_ID) # Define a subflow for if we successfully map an amphora # map_lb_to_amp = self._get_post_map_lb_subflow(prefix, role) # Define a subflow for if we can't map an amphora 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 an amphora 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 for the path if we can't map an amphora 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
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
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
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)) # 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))) failover_amphora_flow.add(database_tasks.ReloadLoadBalancer( requires=constants.LOADBALANCER_ID, provides=constants.LOADBALANCER)) 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))) return failover_amphora_flow
def get_extension_flow(self, distributor): """Extend a amphora for load balancer. :returns: The flow for loadbalancer extension """ extension_flow = linear_flow.Flow( constants.EXTENSION_LOADBALANCER_FLOW) get_amp_subflow = self.get_amphora_for_lb_subflow( prefix=constants.EXTENSION_LOADBALANCER_FLOW, role=constants.ROLE_ACTIVE) extension_flow.add(get_amp_subflow) # Update the data stored in the flow from the database extension_flow.add( database_tasks.ReloadLoadBalancer( name='first', requires=constants.LOADBALANCER_ID, provides=constants.LOADBALANCER)) extension_flow.add( network_tasks.PlugAmphoraVIP(requires=(constants.AMPHORA, constants.LOADBALANCER), provides=constants.AMPS_DATA)) extension_flow.add( database_tasks.UpdateAmphoraVIPData(requires=constants.AMPS_DATA)) # Update the data stored in the flow from the database extension_flow.add( database_tasks.ReloadLoadBalancer( name='second', requires=constants.LOADBALANCER_ID, provides=constants.LOADBALANCER)) extension_flow.add( database_tasks.ReloadAmphora(requires=constants.AMPHORA_ID, provides=constants.AMPHORA)) # Prepare to reconnect the network interface(s) extension_flow.add( network_tasks.GetAmphoraeNetworkConfigs( requires=constants.LOADBALANCER, provides=constants.AMPHORAE_NETWORK_CONFIG)) extension_flow.add( database_tasks.GetListenersFromLoadbalancer( requires=constants.LOADBALANCER, provides=constants.LISTENERS)) extension_flow.add( database_tasks.GetAmphoraeFromLoadbalancer( requires=constants.LOADBALANCER, provides=constants.AMPHORAE)) extension_flow.add( amphora_driver_tasks.AmphoraPostVIPPlug( requires=(constants.AMPHORA, constants.LOADBALANCER, constants.AMPHORAE_NETWORK_CONFIG))) timeout_dict = { constants.CONN_MAX_RETRIES: CONF.haproxy_amphora.active_connection_max_retries, constants.CONN_RETRY_INTERVAL: CONF.haproxy_amphora.active_connection_rety_interval } extension_flow.add( amphora_driver_tasks.AmpsListenersUpdate( name=constants.AMP_LISTENER_UPDATE, requires=(constants.LISTENERS, constants.LOADBALANCER), inject=({ constants.TIMEOUT_DICT: timeout_dict }))) # Plug the member networks into the new amphora extension_flow.add( network_tasks.CalculateAmphoraDelta( requires=(constants.LOADBALANCER, constants.AMPHORA), provides=constants.DELTA)) extension_flow.add( network_tasks.HandleNetworkDelta(requires=(constants.AMPHORA, constants.DELTA), provides=constants.ADDED_PORTS)) extension_flow.add( amphora_driver_tasks.AmphoraePostNetworkPlug( requires=(constants.LOADBALANCER, constants.ADDED_PORTS))) extension_flow.add( database_tasks.ReloadLoadBalancer( name='octavia-extension-LB-reload-2', requires=constants.LOADBALANCER_ID, provides=constants.LOADBALANCER)) extension_flow.add( self.get_distributor_flows('octavia-extension-distributor-flow', distributor)) extension_flow.add( amphora_driver_tasks.ListenersStart( requires=(constants.LOADBALANCER, constants.LISTENERS, constants.AMPHORA))) return extension_flow
def get_new_lb_networking_subflow(self, topology): """Subflow to setup networking for amphora""" new_LB_net_subflow = linear_flow.Flow( constants.LOADBALANCER_NETWORKING_SUBFLOW) new_LB_net_subflow.add( a10_network_tasks.PlugVIP(requires=constants.LOADBALANCER, provides=constants.AMPS_DATA)) new_LB_net_subflow.add( a10_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( a10_network_tasks.GetAmphoraeNetworkConfigs( requires=constants.LOADBALANCER, provides=constants.AMPHORAE_NETWORK_CONFIG)) new_LB_net_subflow.add( database_tasks.GetAmphoraeFromLoadbalancer( requires=constants.LOADBALANCER, provides=constants.AMPHORA)) new_LB_net_subflow.add( vthunder_tasks.VThunderComputeConnectivityWait( name=a10constants.MASTER_CONNECTIVITY_WAIT, requires=(a10constants.VTHUNDER, constants.AMPHORA))) new_LB_net_subflow.add( vthunder_tasks.EnableInterface(requires=a10constants.VTHUNDER)) new_LB_net_subflow.add( a10_database_tasks.MarkVThunderStatusInDB( name=a10constants.MARK_VTHUNDER_MASTER_ACTIVE_IN_DB, requires=a10constants.VTHUNDER, inject={a10constants.STATUS: constants.ACTIVE})) if topology == constants.TOPOLOGY_ACTIVE_STANDBY: new_LB_net_subflow.add( vthunder_tasks.CreateHealthMonitorOnVThunder( name=a10constants.CREATE_HEALTH_MONITOR_ON_VTHUNDER_MASTER, requires=a10constants.VTHUNDER)) new_LB_net_subflow.add( a10_database_tasks.GetBackupVThunderByLoadBalancer( name=a10constants.GET_BACKUP_VTHUNDER_BY_LB, requires=constants.LOADBALANCER, provides=a10constants.BACKUP_VTHUNDER)) new_LB_net_subflow.add( vthunder_tasks.VThunderComputeConnectivityWait( name=a10constants.BACKUP_CONNECTIVITY_WAIT, rebind={ a10constants.VTHUNDER: a10constants.BACKUP_VTHUNDER }, requires=constants.AMPHORA)) new_LB_net_subflow.add( vthunder_tasks.EnableInterface( name=a10constants.BACKUP_ENABLE_INTERFACE, rebind={ a10constants.VTHUNDER: a10constants.BACKUP_VTHUNDER })) new_LB_net_subflow.add( a10_database_tasks.MarkVThunderStatusInDB( name=a10constants.MARK_VTHUNDER_BACKUP_ACTIVE_IN_DB, rebind={ a10constants.VTHUNDER: a10constants.BACKUP_VTHUNDER }, inject={a10constants.STATUS: constants.ACTIVE})) return new_LB_net_subflow
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 + '-' + constants.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 })) # Get VThunder details from database create_amp_for_lb_subflow.add( a10_database_tasks.GetVThunderByLoadBalancer( name=sf_name + '-' + a10constants.VTHUNDER_BY_LB, requires=constants.LOADBALANCER, provides=a10constants.VTHUNDER)) create_amp_for_lb_subflow.add( vthunder_tasks.VThunderComputeConnectivityWait( name=sf_name + '-' + a10constants.WAIT_FOR_VTHUNDER_CONNECTIVITY, requires=(a10constants.VTHUNDER, constants.AMPHORA))) 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)) create_amp_for_lb_subflow.add( a10_network_tasks.GetLBResourceSubnet( name=sf_name + '-' + a10constants.GET_LB_RESOURCE, rebind={a10constants.LB_RESOURCE: constants.LOADBALANCER}, provides=constants.SUBNET)) create_amp_for_lb_subflow.add( vthunder_tasks.AllowL2DSR( name=sf_name + '-' + a10constants.ALLOW_L2DSR, requires=(constants.SUBNET, 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)) return create_amp_for_lb_subflow
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)) # 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.LISTENERS, 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.LISTENERS, constants.AMPHORA))) failover_amphora_flow.add( database_tasks.DisableAmphoraHealthMonitoring( rebind={constants.AMPHORA: constants.FAILED_AMPHORA}, requires=constants.AMPHORA)) return failover_amphora_flow
def get_failover_flow(self): """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( network_tasks.RetrievePortIDsOnAmphoraExceptLBNetwork( requires=constants.AMPHORA, provides=constants.PORTS)) failover_amphora_flow.add( network_tasks.FailoverPreparationForAmphora( requires=constants.AMPHORA)) failover_amphora_flow.add( compute_tasks.ComputeDelete(requires=constants.AMPHORA)) failover_amphora_flow.add( database_tasks.MarkAmphoraDeletedInDB(requires=constants.AMPHORA)) failover_amphora_flow.add( database_tasks.CreateAmphoraInDB(provides=constants.AMPHORA_ID)) failover_amphora_flow.add( database_tasks.GetUpdatedFailoverAmpNetworkDetailsAsList( requires=(constants.AMPHORA_ID, constants.AMPHORA), provides=constants.AMPS_DATA)) if self.REST_AMPHORA_DRIVER: failover_amphora_flow.add( cert_task.GenerateServerPEMTask(provides=constants.SERVER_PEM)) failover_amphora_flow.add( compute_tasks.CertComputeCreate( requires=(constants.AMPHORA_ID, constants.SERVER_PEM), provides=constants.COMPUTE_ID)) else: failover_amphora_flow.add( compute_tasks.ComputeCreate(requires=constants.AMPHORA_ID, provides=constants.COMPUTE_ID)) failover_amphora_flow.add( database_tasks.UpdateAmphoraComputeId( requires=(constants.AMPHORA_ID, constants.COMPUTE_ID))) failover_amphora_flow.add( database_tasks.AssociateFailoverAmphoraWithLBID( requires=(constants.AMPHORA_ID, constants.LOADBALANCER_ID))) failover_amphora_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)) failover_amphora_flow.add(wait_flow) failover_amphora_flow.add( database_tasks.ReloadAmphora(requires=constants.AMPHORA_ID, provides=constants.FAILOVER_AMPHORA)) failover_amphora_flow.add( amphora_driver_tasks.AmphoraFinalize( rebind={constants.AMPHORA: constants.FAILOVER_AMPHORA}, requires=constants.AMPHORA)) failover_amphora_flow.add( database_tasks.UpdateAmphoraVIPData(requires=constants.AMPS_DATA)) failover_amphora_flow.add( database_tasks.ReloadLoadBalancer( requires=constants.LOADBALANCER_ID, provides=constants.LOADBALANCER)) 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.GetVipFromLoadbalancer( requires=constants.LOADBALANCER, provides=constants.VIP)) failover_amphora_flow.add( amphora_driver_tasks.ListenersUpdate(requires=(constants.LISTENERS, constants.VIP))) failover_amphora_flow.add( amphora_driver_tasks.AmphoraPostVIPPlug( requires=(constants.LOADBALANCER, constants.AMPHORAE_NETWORK_CONFIG))) failover_amphora_flow.add( network_tasks.GetMemberPorts( rebind={constants.AMPHORA: constants.FAILOVER_AMPHORA}, requires=(constants.LOADBALANCER, constants.AMPHORA), provides=constants.MEMBER_PORTS)) failover_amphora_flow.add( amphora_driver_tasks.AmphoraPostNetworkPlug( rebind={ constants.AMPHORA: constants.FAILOVER_AMPHORA, constants.PORTS: constants.MEMBER_PORTS }, requires=(constants.AMPHORA, constants.PORTS))) failover_amphora_flow.add( amphora_driver_tasks.ListenersStart(requires=(constants.LISTENERS, constants.VIP))) failover_amphora_flow.add( database_tasks.MarkAmphoraAllocatedInDB( rebind={constants.AMPHORA: constants.FAILOVER_AMPHORA}, requires=(constants.AMPHORA, constants.LOADBALANCER_ID))) return failover_amphora_flow
def get_failover_flow(self, role=constants.ROLE_STANDALONE, load_balancer_id=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)) # 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_id: 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( 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.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 + '-' + constants.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)) vthunder_for_amphora_subflow.add( vthunder_tasks.AllowL2DSR( name=sf_name + '-' + a10constants.ALLOW_L2DSR, requires=(constants.SUBNET, 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
def get_failover_flow(self, role=constants.ROLE_STANDALONE): """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( network_tasks.RetrievePortIDsOnAmphoraExceptLBNetwork( rebind={constants.AMPHORA: constants.FAILED_AMPHORA}, requires=constants.AMPHORA, provides=constants.PORTS)) failover_amphora_flow.add( network_tasks.FailoverPreparationForAmphora( rebind={constants.AMPHORA: constants.FAILED_AMPHORA}, requires=constants.AMPHORA)) # 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( 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)) # 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))) failover_amphora_flow.add( database_tasks.ReloadLoadBalancer( requires=constants.LOADBALANCER_ID, provides=constants.LOADBALANCER)) 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.GetVipFromLoadbalancer( requires=constants.LOADBALANCER, provides=constants.VIP)) failover_amphora_flow.add( amphora_driver_tasks.ListenersUpdate( requires=(constants.LOADBALANCER, constants.LISTENERS))) failover_amphora_flow.add( network_tasks.PlugPorts(requires=(constants.AMPHORA, constants.PORTS))) failover_amphora_flow.add( amphora_driver_tasks.AmphoraPostVIPPlug( requires=(constants.LOADBALANCER, constants.AMPHORAE_NETWORK_CONFIG))) failover_amphora_flow.add( network_tasks.GetMemberPorts(requires=(constants.LOADBALANCER, constants.AMPHORA), provides=constants.MEMBER_PORTS)) failover_amphora_flow.add( amphora_driver_tasks.AmphoraPostNetworkPlug( rebind={constants.PORTS: constants.MEMBER_PORTS}, requires=(constants.AMPHORA, constants.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))) return failover_amphora_flow