Example #1
0
 def test_volume_index(self):
     """Test that supported RNASeq platforms setting is set correctly."""
     self.assertEqual(utils.get_volume_index(), "0")
     with open('/tmp/VOLUME_INDEX', 'wb') as f:
         f.write("123".encode())
     self.assertEqual(utils.get_volume_index(path='/tmp/VOLUME_INDEX'),
                      "123")
Example #2
0
def send_job(job_type: Enum, job, is_dispatch=False) -> bool:
    """Queues a worker job by sending a Nomad Job dispatch message.

    job_type must be a valid Enum for ProcessorPipelines or
    Downloaders as defined in data_refinery_common.job_lookup.
    job must be an existing ProcessorJob or DownloaderJob record.

    Returns True if the job was successfully dispatch, return False otherwise.
    """
    nomad_host = get_env_variable("NOMAD_HOST")
    nomad_port = get_env_variable("NOMAD_PORT", "4646")
    nomad_client = nomad.Nomad(nomad_host, port=int(nomad_port), timeout=30)

    is_processor = True
    if (job_type is ProcessorPipeline.TRANSCRIPTOME_INDEX_LONG
            or job_type is ProcessorPipeline.TRANSCRIPTOME_INDEX_SHORT):
        nomad_job = NOMAD_TRANSCRIPTOME_JOB
    elif job_type is ProcessorPipeline.SALMON or job_type is ProcessorPipeline.TXIMPORT:
        # Tximport uses the same job specification as Salmon.
        nomad_job = ProcessorPipeline.SALMON.value
    elif job_type is ProcessorPipeline.AFFY_TO_PCL:
        nomad_job = ProcessorPipeline.AFFY_TO_PCL.value
    elif job_type is ProcessorPipeline.NO_OP:
        nomad_job = ProcessorPipeline.NO_OP.value
    elif job_type is ProcessorPipeline.ILLUMINA_TO_PCL:
        nomad_job = ProcessorPipeline.ILLUMINA_TO_PCL.value
    elif job_type is ProcessorPipeline.SMASHER:
        nomad_job = ProcessorPipeline.SMASHER.value
    elif job_type is ProcessorPipeline.JANITOR:
        nomad_job = ProcessorPipeline.JANITOR.value
    elif job_type is ProcessorPipeline.QN_REFERENCE:
        nomad_job = ProcessorPipeline.QN_REFERENCE.value
    elif job_type is ProcessorPipeline.CREATE_COMPENDIA:
        nomad_job = ProcessorPipeline.CREATE_COMPENDIA.value
    elif job_type is ProcessorPipeline.CREATE_QUANTPENDIA:
        nomad_job = ProcessorPipeline.CREATE_QUANTPENDIA.value
    elif job_type is ProcessorPipeline.AGILENT_TWOCOLOR_TO_PCL:
        # Agilent twocolor uses the same job specification as Affy.
        nomad_job = ProcessorPipeline.AFFY_TO_PCL.value
    elif job_type in list(Downloaders):
        nomad_job = NOMAD_DOWNLOADER_JOB
        is_processor = False
    elif job_type in list(SurveyJobTypes):
        nomad_job = job_type.value
        is_processor = False
    elif job_type is Downloaders.NONE:
        logger.warn("Not queuing %s job.", job_type, job_id=job_id)
        raise ValueError(
            NONE_JOB_ERROR_TEMPLATE.format(job_type.value, "Downloader",
                                           job_id))
    elif job_type is ProcessorPipeline.NONE:
        logger.warn("Not queuing %s job.", job_type, job_id=job_id)
        raise ValueError(
            NONE_JOB_ERROR_TEMPLATE.format(job_type.value, "Processor",
                                           job_id))
    else:
        raise ValueError("Invalid job_type: {}".format(job_type.value))

    logger.debug("Queuing %s nomad job to run job %s with id %d.", nomad_job,
                 job_type.value, job.id)

    # We only want to dispatch processor jobs directly.
    # Everything else will be handled by the Foreman, which will increment the retry counter.
    if is_processor or is_dispatch or (not settings.RUNNING_IN_CLOUD):

        # Smasher doesn't need to be on a specific instance since it will
        # download all the data to its instance anyway.
        if isinstance(job, ProcessorJob) and job_type not in SMASHER_JOB_TYPES:
            # Make sure this job goes to the correct EBS resource.
            # If this is being dispatched for the first time, make sure that
            # we store the currently attached index.
            # If this is being dispatched by the Foreman, it should already
            # have an attached volume index, so use that.
            if job.volume_index is None:
                job.volume_index = get_volume_index()
                job.save()
            nomad_job = nomad_job + "_" + job.volume_index + "_" + str(
                job.ram_amount)
        elif isinstance(job, SurveyJob):
            nomad_job = nomad_job + "_" + str(job.ram_amount)
        elif isinstance(job, DownloaderJob):
            volume_index = job.volume_index if settings.RUNNING_IN_CLOUD else "0"
            nomad_job = nomad_job + "_" + volume_index + "_" + str(
                job.ram_amount)

        try:
            nomad_response = nomad_client.job.dispatch_job(nomad_job,
                                                           meta={
                                                               "JOB_NAME":
                                                               job_type.value,
                                                               "JOB_ID":
                                                               str(job.id)
                                                           })
            job.nomad_job_id = nomad_response["DispatchedJobID"]
            job.save()
            return True
        except URLNotFoundNomadException:
            logger.info(
                "Dispatching Nomad job of type %s for job spec %s to host %s and port %s failed.",
                job_type,
                nomad_job,
                nomad_host,
                nomad_port,
                job=str(job.id),
            )
            raise
        except Exception as e:
            logger.info(
                "Unable to Dispatch Nomad Job.",
                job_name=job_type.value,
                job_id=str(job.id),
                reason=str(e),
            )
            raise
    else:
        job.num_retries = job.num_retries - 1
        job.save()
    return True
Example #3
0
import daiquiri

from data_refinery_common.utils import (
    get_env_variable_gracefully,
    get_instance_id,
    get_volume_index,
)

# Most of the formatting in this string is for the logging system. All
# that the call to format() does is replace the "{0}" in the string
# with the worker id.
FORMAT_STRING = (
    "%(asctime)s {0} [volume: {1}] %(name)s %(color)s%(levelname)s%(extras)s"
    ": %(message)s%(color_stop)s").format(get_instance_id(),
                                          get_volume_index())
LOG_LEVEL = None


def unconfigure_root_logger():
    """Prevents the root logger from duplicating our messages.

    The root handler comes preconfigured with a handler. This causes
    all our logs to be logged twice, once with our cool handler and
    one that lacks all context. This function removes that stupid
    extra handler.
    """
    root_logger = logging.getLogger(None)
    # Remove all handlers
    for handler in list(root_logger.handlers):
        root_logger.removeHandler(handler)
Example #4
0
def create_processor_job_for_original_files(
    original_files: List[OriginalFile],
    downloader_job: DownloaderJob = None,
    volume_index: str = None,
):
    """
    Create a processor job and queue a processor task for sample related to an experiment.
    """

    # If there's no original files then we've created all the jobs we need to!
    if len(original_files) == 0:
        return

    # For anything that has raw data there should only be one Sample per OriginalFile
    sample_object = original_files[0].samples.first()
    pipeline_to_apply = determine_processor_pipeline(sample_object, original_files[0])

    if pipeline_to_apply == ProcessorPipeline.NONE:
        logger.info(
            "No valid processor pipeline found to apply to sample.",
            sample=sample_object.id,
            original_file=original_files[0].id,
        )
        for original_file in original_files:
            original_file.delete_local_file()
    else:
        processor_job = ProcessorJob()
        processor_job.pipeline_applied = pipeline_to_apply.value
        processor_job.ram_amount = determine_ram_amount(sample_object, processor_job)

        if volume_index:
            processor_job.volume_index = volume_index
        elif downloader_job.volume_index:
            processor_job.volume_index = downloader_job.volume_index
        else:
            processor_job.volume_index = get_volume_index()

        processor_job.save()

        for original_file in original_files:
            if original_file.is_blacklisted():
                logger.debug(
                    "Original file had a blacklisted extension of %s, skipping",
                    extension=original_file.get_extension(),
                    original_file=original_file.id,
                )
                original_file.delete_local_file()
                continue

            assoc = ProcessorJobOriginalFileAssociation()
            assoc.original_file = original_file
            assoc.processor_job = processor_job
            assoc.save()

        logger.debug(
            "Queuing processor job.",
            processor_job=processor_job.id,
            downloader_job=downloader_job.id if downloader_job else None,
        )

        try:
            send_job(pipeline_to_apply, processor_job)
        except:
            # If we cannot queue the job now the Foreman will do
            # it later.
            pass