def spawn_appserver(self,
                        mark_active_on_success=False,
                        num_attempts=1,
                        success_tag=None,
                        failure_tag=None):
        """
        Provision a new AppServer

        Wrapper around the spawning function to allow for multiple attempts

        Optionally mark the new AppServer as active when the provisioning completes.
        Optionally retry up to 'num_attempts' times.
        Optionally tag the instance with 'success_tag' when the deployment succeeds,
        or failure_tag if it fails.

        Returns the ID of the new AppServer or None in case of failure.
        """
        for attempt in range(num_attempts):
            self.logger.info("Spawning new AppServer, attempt {} of {}".format(
                attempt + 1, num_attempts))
            app_server = self._spawn_appserver()

            if app_server and app_server.provision():
                break

            self.logger.error('Failed to provision new app server')

        else:
            self.logger.error(
                'Failed to provision new app server after {} attempts'.format(
                    num_attempts))
            if failure_tag:
                self.tags.add(failure_tag)
            if success_tag:
                self.tags.remove(success_tag)

            # Warn spawn failed after given attempts
            appserver_spawned.send(sender=self.__class__,
                                   instance=self,
                                   appserver=None)
            return

        self.logger.info('Provisioned new app server, %s', app_server.name)
        self.successfully_provisioned = True
        self.save()

        if failure_tag:
            self.tags.remove(failure_tag)
        if success_tag:
            self.tags.add(success_tag)

        if mark_active_on_success:
            # use task.make_appserver_active to allow disabling others
            app_server.make_active()

        appserver_spawned.send(sender=self.__class__,
                               instance=self,
                               appserver=app_server)

        return app_server.pk
    def spawn_appserver(self,
                        mark_active_on_success=False,
                        num_attempts=1,
                        success_tag=None,
                        failure_tag=None,
                        deployment_id=None):
        """
        Provision a new AppServer

        Wrapper around the spawning function to allow for multiple attempts

        Optionally mark the new AppServer as active when the provisioning completes.
        Optionally retry up to 'num_attempts' times.
        Optionally tag the instance with 'success_tag' when the deployment succeeds,
        or failure_tag if it fails.

        Returns the ID of the new AppServer or None in case of failure.
        """
        # pylint: disable=cyclic-import, useless-suppression
        from instance.models.openedx_deployment import OpenEdXDeployment

        for attempt in range(num_attempts):
            self.logger.info("Spawning new AppServer, attempt {} of {}".format(
                attempt + 1, num_attempts))
            app_server = self._spawn_appserver(deployment_id=deployment_id)

            if app_server and app_server.provision():
                break

            # Don't retry if the deployment was explicitly cancelled.
            if deployment_id:
                deployment = OpenEdXDeployment.objects.get(pk=deployment_id)
                if deployment.cancelled:
                    self.logger.info('Deployment %s was cancelled, returning.',
                                     deployment_id)
                    return None

            self.logger.error('Failed to provision new app server')

        else:
            self.logger.error(
                'Failed to provision new app server after {} attempts'.format(
                    num_attempts))
            if failure_tag:
                self.tags.add(failure_tag)
            if success_tag:
                self.tags.remove(success_tag)

            # Warn spawn failed after given attempts
            appserver_spawned.send(sender=self.__class__,
                                   instance=self,
                                   appserver=None)
            return None

        self.logger.info('Provisioned new app server, %s', app_server.name)
        self.successfully_provisioned = True
        self.save()

        if failure_tag:
            self.tags.remove(failure_tag)
        if success_tag:
            self.tags.add(success_tag)

        if mark_active_on_success:
            # use task.make_appserver_active to allow disabling others
            app_server.make_active()

        appserver_spawned.send(sender=self.__class__,
                               instance=self,
                               appserver=app_server)

        return app_server.pk