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
Esempio n. 3
0
    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)
Esempio n. 4
0
    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
Esempio n. 5
0
    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)
Esempio n. 6
0
    def get_create_load_balancer_flow(self, topology, listeners=None):
        """Creates a conditional graph flow that allocates a loadbalancer to

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

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

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

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

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

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

        return lb_create_flow
Esempio n. 7
0
    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)
Esempio n. 8
0
    def get_create_load_balancer_flow(self, topology, listeners=None):
        """Creates a conditional graph flow that allocates a loadbalancer to

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

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

        # 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
Esempio n. 9
0
    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)