def get_create_load_balancer_flow(self, topology, listeners=None): """Flow to create a 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)) # Attaching vThunder to LB in database if topology == constants.TOPOLOGY_ACTIVE_STANDBY: lb_create_flow.add(*self._create_active_standby_topology()) LOG.info("TOPOLOGY ===" + str(topology)) elif topology == constants.TOPOLOGY_SINGLE: lb_create_flow.add(*self._create_single_topology()) LOG.info("TOPOLOGY ===" + str(topology)) else: LOG.error("Unknown topology: %s. Unable to build load balancer.", topology) raise exceptions.InvalidTopology(topology=topology) # IMP: Now creating vThunder config here post_amp_prefix = constants.POST_LB_AMP_ASSOCIATION_SUBFLOW lb_create_flow.add( self.get_post_lb_vthunder_association_flow( post_amp_prefix, topology, mark_active=(not listeners))) lb_create_flow.add( virtual_server_tasks.CreateVirtualServerTask( requires=(constants.LOADBALANCER, a10constants.VTHUNDER))) lb_create_flow.add( vthunder_tasks.WriteMemory(requires=a10constants.VTHUNDER)) return lb_create_flow
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)) 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( _LE("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)) if listeners: lb_create_flow.add(*self._create_listeners_flow()) return lb_create_flow
def _get_amphorae_for_failover(load_balancer): """Returns an ordered list of amphora to failover. :param load_balancer: The load balancer being failed over. :returns: An ordered list of amphora to failover, first amp to failover is last in the list :raises octavia.common.exceptions.InvalidTopology: LB has an unknown topology. """ if load_balancer.topology == constants.TOPOLOGY_SINGLE: # In SINGLE topology, amp failover order does not matter return [a.to_dict() for a in load_balancer.amphorae if a.status != constants.DELETED] if load_balancer.topology == constants.TOPOLOGY_ACTIVE_STANDBY: # In Active/Standby we should preference the standby amp # for failover first in case the Active is still able to pass # traffic. # Note: The active amp can switch at any time and in less than a # second, so this is "best effort". amphora_driver = utils.get_amphora_driver() timeout_dict = { constants.CONN_MAX_RETRIES: CONF.haproxy_amphora.failover_connection_max_retries, constants.CONN_RETRY_INTERVAL: CONF.haproxy_amphora.failover_connection_retry_interval} amps = [] selected_amp = None for amp in load_balancer.amphorae: if amp.status == constants.DELETED: continue if selected_amp is None: try: if amphora_driver.get_interface_from_ip( amp, load_balancer.vip.ip_address, timeout_dict): # This is a potential ACTIVE, add it to the list amps.append(amp.to_dict()) else: # This one doesn't have the VIP IP, so start # failovers here. selected_amp = amp LOG.debug("Selected amphora %s as the initial " "failover amphora.", amp.id) except Exception: # This amphora is broken, so start failovers here. selected_amp = amp else: # We have already found a STANDBY, so add the rest to the # list without querying them. amps.append(amp.to_dict()) # Put the selected amphora at the end of the list so it is # first to failover. if selected_amp: amps.append(selected_amp.to_dict()) return amps LOG.error('Unknown load balancer topology found: %s, aborting ' 'failover.', load_balancer.topology) raise exceptions.InvalidTopology(topology=load_balancer.topology)
def get_create_load_balancer_flow(self, topology): """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. """ # create a linear flow as a wrapper lf_name = constants.PRE_CREATE_LOADBALANCER_FLOW create_lb_flow_wrapper = linear_flow.Flow(lf_name) f_name = constants.CREATE_LOADBALANCER_FLOW lb_create_flow = unordered_flow.Flow(f_name) if topology == constants.TOPOLOGY_ACTIVE_STANDBY: # When we boot up amphora for an active/standby topology, # we should leverage the Nova anti-affinity capabilities # to place the amphora on different hosts, also we need to check # if anti-affinity-flag is enabled or not: anti_affinity = CONF.nova.enable_anti_affinity if anti_affinity: # we need to create a server group first create_lb_flow_wrapper.add( compute_tasks.NovaServerGroupCreate( name=lf_name + '-' + constants.CREATE_SERVER_GROUP_FLOW, requires=(constants.LOADBALANCER_ID), provides=constants.SERVER_GROUP_ID)) # update server group id in lb table create_lb_flow_wrapper.add( database_tasks.UpdateLBServerGroupInDB( name=lf_name + '-' + constants.UPDATE_LB_SERVERGROUPID_FLOW, requires=(constants.LOADBALANCER_ID, constants.SERVER_GROUP_ID))) master_amp_sf = self.amp_flows.get_amphora_for_lb_subflow( prefix=constants.ROLE_MASTER, role=constants.ROLE_MASTER) backup_amp_sf = self.amp_flows.get_amphora_for_lb_subflow( prefix=constants.ROLE_BACKUP, role=constants.ROLE_BACKUP) lb_create_flow.add(master_amp_sf, backup_amp_sf) elif topology == constants.TOPOLOGY_SINGLE: amphora_sf = self.amp_flows.get_amphora_for_lb_subflow( prefix=constants.ROLE_STANDALONE, role=constants.ROLE_STANDALONE) lb_create_flow.add(amphora_sf) else: LOG.error( _LE("Unknown topology: %s. Unable to build load " "balancer."), topology) raise exceptions.InvalidTopology(topology=topology) create_lb_flow_wrapper.add(lb_create_flow) return create_lb_flow_wrapper
def execute(self, loadbalancer, topology): vthunder = self.vthunder_repo.get_vthunder_with_different_topology( db_apis.get_session(), loadbalancer.project_id, topology) if vthunder is not None and topology != vthunder.topology: LOG.error( 'Other loadbalancer exists in vthunder with: %s topology', vthunder.topology) msg = ('vthunder has other loadbalancer with {0} ' + 'topology').format(vthunder.topology) raise octavia_exceptions.InvalidTopology(topology=msg)
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
def execute(self, loadbalancer, topology): vthunder = self.vthunder_repo.get_vthunder_by_project_id( db_apis.get_session(), loadbalancer.project_id) if vthunder is not None: if topology != (vthunder.topology).encode('utf-8'): LOG.error( 'Other loadbalancer exists in vthunder with: %s topology', vthunder.topology) msg = ('vthunder has other loadbalancer with {0} ' + 'topology').format(vthunder.topology) raise octavia_exceptions.InvalidTopology(topology=msg)
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)) # Attaching vThunder to LB in database if topology == constants.TOPOLOGY_ACTIVE_STANDBY: lb_create_flow.add(*self._create_active_standby_topology()) LOG.info("TOPOLOGY ===" + str(topology)) elif topology == constants.TOPOLOGY_SINGLE: lb_create_flow.add(*self._create_single_topology()) LOG.info("TOPOLOGY ===" + str(topology)) else: LOG.error("Unknown topology: %s. Unable to build load balancer.", topology) raise exceptions.InvalidTopology(topology=topology) LOG.info("printing vthunder info" + str()) #IMP: Now creating vThunder config here post_amp_prefix = constants.POST_LB_AMP_ASSOCIATION_SUBFLOW lb_create_flow.add( self.get_post_lb_vthunder_association_flow( post_amp_prefix, topology, mark_active=(not listeners))) lb_create_flow.add( handler_virtual_server.CreateVitualServerTask( requires=(constants.LOADBALANCER_ID, constants.LOADBALANCER, a10constants.VTHUNDER), provides=a10constants.STATUS)) return lb_create_flow
def failover_loadbalancer(self, load_balancer_id): """Perform failover operations for a load balancer. Note: This expects the load balancer to already be in provisioning_status=PENDING_UPDATE state. :param load_balancer_id: ID for load balancer to failover :returns: None :raises octavia.commom.exceptions.NotFound: The load balancer was not found. """ try: lb = self._lb_repo.get(db_apis.get_session(), id=load_balancer_id) if lb is None: raise exceptions.NotFound(resource=constants.LOADBALANCER, id=load_balancer_id) # Get the ordered list of amphorae to failover for this LB. amps = self._get_amphorae_for_failover(lb) if lb.topology == constants.TOPOLOGY_SINGLE: if len(amps) != 1: LOG.warning( '%d amphorae found on load balancer %s where ' 'one should exist. Repairing.', len(amps), load_balancer_id) elif lb.topology == constants.TOPOLOGY_ACTIVE_STANDBY: if len(amps) != 2: LOG.warning( '%d amphorae found on load balancer %s where ' 'two should exist. Repairing.', len(amps), load_balancer_id) else: LOG.error( 'Unknown load balancer topology found: %s, aborting ' 'failover!', lb.topology) raise exceptions.InvalidTopology(topology=lb.topology) # We must provide a topology in the flavor definition # here for the amphora to be created with the correct # configuration. if lb.flavor_id: flavor = self._flavor_repo.get_flavor_metadata_dict( db_apis.get_session(), lb.flavor_id) flavor[constants.LOADBALANCER_TOPOLOGY] = lb.topology else: flavor = {constants.LOADBALANCER_TOPOLOGY: lb.topology} provider_lb_dict = ( provider_utils.db_loadbalancer_to_provider_loadbalancer( lb).to_dict() if lb else lb) provider_lb_dict[constants.FLAVOR] = flavor stored_params = { constants.LOADBALANCER: provider_lb_dict, constants.BUILD_TYPE_PRIORITY: constants.LB_CREATE_FAILOVER_PRIORITY, constants.SERVER_GROUP_ID: lb.server_group_id, constants.LOADBALANCER_ID: lb.id, constants.FLAVOR: flavor } if lb.availability_zone: stored_params[constants.AVAILABILITY_ZONE] = ( self._az_repo.get_availability_zone_metadata_dict( db_apis.get_session(), lb.availability_zone)) else: stored_params[constants.AVAILABILITY_ZONE] = {} self.run_flow(flow_utils.get_failover_LB_flow, amps, provider_lb_dict, store=stored_params, wait=True) LOG.info('Failover of load balancer %s completed successfully.', lb.id) except Exception as e: with excutils.save_and_reraise_exception(reraise=False): LOG.exception("LB %(lbid)s failover exception: %(exc)s", { 'lbid': load_balancer_id, 'exc': str(e) }) self._lb_repo.update(db_apis.get_session(), load_balancer_id, provisioning_status=constants.ERROR)