Exemple #1
0
def manage_manual_action():
    logger.debug("manage_manual_action")
    try:
        #Create lock file to prevent more than one celery task for manual action (export or archive)
        lockfile = '/var/run/celery/manual_action.lock'
        applock = ApplicationLock(lockfile)
        if not(applock.lock()):
            logger.debug("failed to acquire lock file: %d" % os.getpid())
            logger.info("manage_manual_action task still executing")
            return

        logger.info("Worker PID %d lockfile created %s" % (os.getpid(),lockfile))

    except Exception as e:
        logger.exception(e)

    try:
        #
        # Check for manually selected Archive and Export actions - action_state == 'SA' or 'SE'
        # These jobs should execute even when auto action is disabled.
        # Note: manual actions will not be executed in the order they are selected, but by age.
        #
        user_comment = "Manual Action"
        manualSelects = DMFileStat.objects.filter(action_state__in=['SA','SE']).order_by('created')
        if manualSelects.exists():
            actiondmfilestat = manualSelects[0]
            dmfileset = actiondmfilestat.dmfileset
            project_msg = {}
            msg_dict = {}
            msg_dict[dmfileset.type] = "Success"
            project_msg[actiondmfilestat.result_id] = msg_dict
            if actiondmfilestat.action_state == 'SA':
                logger.info("Manual Archive Action: %s from %s" % (dmfileset.type,actiondmfilestat.result.resultsName))
                archive_action('dm_agent', user_comment, actiondmfilestat)
                datatasks.project_msg_banner('', project_msg, dmactions.ARCHIVE)
            elif actiondmfilestat.action_state == 'SE':
                logger.info("Manual Export Action: %s from %s" % (dmfileset.type,actiondmfilestat.result.resultsName))
                export_action('dm_agent', user_comment, actiondmfilestat)
                datatasks.project_msg_banner('', project_msg, dmactions.EXPORT)
            else:
                logger.warn("Dev Error: we don't handle this '%s' here" % actiondmfilestat.action_state)
            return
#NOTE: all these exceptions are also handled in manage_data() below.  beaucoup de duplication de code
    except (DMExceptions.FilePermission,
            DMExceptions.InsufficientDiskSpace,
            DMExceptions.MediaNotSet,
            DMExceptions.MediaNotAvailable,
            DMExceptions.FilesInUse) as e:
        message  = Message.objects.filter(tags__contains=e.tag)
        if not message:
            Message.error(e.message,tags=e.tag)
            #TODO: TS-6525: This logentry will repeat for an Archive action every 30 seconds until the cause of the exception is fixed.
            #at least, while the message banner is raised, suppress additional Log Entries.
            dmactions.add_eventlog(actiondmfilestat, "%s - %s" % (dmfileset.type, e.message), username='******')
        # Revert this dmfilestat object action-state to Local
        actiondmfilestat.setactionstate('L')
    except DMExceptions.SrcDirDoesNotExist as e:
        if actiondmfilestat.dmfileset.type == dmactions_types.SIG:
            msg = "Src Dir not found: %s. Setting action_state to Deleted" % e.message
            EventLog.objects.add_entry(actiondmfilestat.result,msg,username='******')
            actiondmfilestat.setactionstate('DD')
            logger.info(msg)
    except Exception as e:
        msg = "action error on %s " % actiondmfilestat.result.resultsName
        msg += " Error: %s" % str(e)
        logger.exception("%s - %s" % (dmfileset.type, msg))
        dmactions.add_eventlog(actiondmfilestat,"%s - %s" % (dmfileset.type, msg),username='******')
        # Revert this dmfilestat object action-state to Local
        actiondmfilestat.setactionstate('L')
    finally:
        applock.unlock()
        logger.debug("(%05d,%05d) Release lock file" % (os.getppid(),os.getpid()))

    return
Exemple #2
0
def manage_data(deviceid, dmfileset, pathlist, auto_acknowledge_enabled, auto_action_enabled):
    logger.debug("manage_data: %s %s %s" % (dmfileset['auto_action'], hex(deviceid),dmfileset['type']))

    def getfirstnotpreserved(dmfilestats, action):
        '''QuerySet of DMFileStat objects.  Returns first instance of an object
        with preserve_data set to False and passes the action_validation test'''
        logger.debug("Function: %s()" % sys._getframe().f_code.co_name)
        logger.debug("Looking at %d dmfilestat objects" % dmfilestats.count())
        for archiveme in dmfilestats:
            if not archiveme.getpreserved():
                try:
                    dmactions.action_validation(archiveme,action)
                    return archiveme
                except:
                    logger.debug("%s Failed action_validation.  Try next fileset" % archiveme.result.resultsName)
            else:
                logger.debug("Skipped a preserved fileset: %s" % archiveme.result.resultsName)

        logger.info("%d filestat objects are preserved." % dmfilestats.count())
        raise DMExceptions.NoDMFileStat("NONE FOUND")

    try:
        #logger.debug("manage_data lock for %s (%d)" % (dmfileset['type'], os.getpid()))
        #Create lock file to prevent more than one celery task for each process_type and partition
        uniqid = "%s_%s" % (hex(deviceid),slugify(dmfileset['type']))
        lockfile = '/var/run/celery/%s.lock' % (uniqid)
        applock = ApplicationLock(lockfile)

        if not(applock.lock()):
            logger.info("failed to acquire lock file: %s" % uniqid)
            return

        logger.info("Worker PID %d lockfile created %s" % (os.getpid(),uniqid))

    except Exception as e:
        logger.exception(e)

    try:
        #---------------------------------------------------------------------------
        # Database object filtering
        #---------------------------------------------------------------------------
        actiondmfilestat = None
        user_comment = "Auto Action"
        # Order by incrementing pk.  This puts them in chronological order.
        # Select DMFileStat objects of category DMFileSet.type (1/4th of all objects)
        dmfilestats = DMFileStat.objects.filter(dmfileset__type=dmfileset['type']).order_by('created')
        tot_obj = dmfilestats.count()

        # Select objects not yet processed
        dmfilestats = dmfilestats.filter(action_state__in=['L','S','N','A'])
        tot_act = dmfilestats.count()
        logger.info("(%s)Total %s: %d Active: %d" %(hex(deviceid),dmfileset['type'],tot_obj,tot_act))

        # Select objects that are old enough
        threshdate = datetime.now(pytz.UTC) - timedelta(days=dmfileset['auto_trigger_age'])
        dmfilestats = dmfilestats.filter(created__lt=threshdate)

        # Select objects stored on the deviceid
        query = Q()
        for path in pathlist:
            if dmfileset['type'] == dmactions_types.SIG:
                dmfilestats = dmfilestats.filter(result__experiment__expDir__startswith=path)
            else:
                dmfilestats = dmfilestats.filter(result__reportstorage__dirPath__startswith=path)

        #    query |= Q(result__experiment__expDir__startswith=path) | Q(result__reportstorage__dirPath__startswith=path)
        #dmfilestats = dmfilestats.filter(query)


        #---------------------------------------------------------------------------
        # Archive
        #---------------------------------------------------------------------------
        if dmfileset['auto_action'] == 'ARC':
            '''
            Rules:
            1) archive a fileset as soon as age threshold reached, regardless of disk threshold
            2) select oldest fileset
            3) select unprocessed fileset
            4) Archiving does not require 'S' -> 'N' -> 'A' progression before action
            5) Do not include filesets marked 'E' in auto-action - can get stuck on that fileset forever
            '''
            logger.info("(%s)Exceed %d days. Eligible to archive: %d" %(hex(deviceid),dmfileset['auto_trigger_age'],dmfilestats.count()))
            actiondmfilestat = None
            #Bail out if disabled
            if auto_action_enabled != True:
                logger.info("Data management auto-action is disabled.")
                return

            # Select first object stored on the deviceid
            try:
                actiondmfilestat = getfirstnotpreserved(dmfilestats, dmactions.ARCHIVE)
                logger.info("(%s)Picked: %s" % (hex(deviceid),actiondmfilestat.result.resultsName))
            except DMExceptions.NoDMFileStat:
                logger.debug("No filesets to archive on this device: %s" % hex(deviceid))
            except:
                logger.error(traceback.format_exc())
            else:
                archive_action('dm_agent', user_comment, actiondmfilestat)

        #---------------------------------------------------------------------------
        # Delete
        #---------------------------------------------------------------------------
        elif dmfileset['auto_action'] == 'DEL':
            '''
            Rules: If auto-acknowledge is True:
            promote to Acknowledged and delete when age and disk threshold reached.
            If auto-acknowledge is False:
                If fileset type is SIG:
                    'S' -> 'N' -> 'A' progression
                Else:
                    promote to 'A'
                delete an 'A' fileset
            '''
            logger.info("(%s)Exceed %d days. Eligible to delete: %d" %(hex(deviceid),dmfileset['auto_trigger_age'],dmfilestats.count()))
            if auto_acknowledge_enabled:
                if dmfileset['type'] == dmactions_types.SIG:
                    logger.debug("Sig Proc Input Files auto acknowledge enabled")
                '''
                Do not require 'S' -> 'N' -> 'A' progression before action; mark 'A' and process
                '''
                A_list = dmfilestats.filter(action_state='A')
                if A_list.count() > 0:
                    # there are filesets acknowledged and ready to be deleted.  Covers situation where user has
                    #already acknowledged but recently enabled auto-acknowledge as well.
                    #deleteme = A_list[0]
                    try:
                        actiondmfilestat = getfirstnotpreserved(A_list, dmactions.DELETE)
                        logger.info("(%s)Picked: %s" % (hex(deviceid),actiondmfilestat.result.resultsName))
                    except DMExceptions.NoDMFileStat:
                        logger.info("(%s) No filesets to delete on this device" % hex(deviceid))
                    except:
                        logger.error(traceback.format_exc())

                if actiondmfilestat == None:
                    # Select oldest fileset regardless if its 'L','S','N','A'.  This covers situation where user
                    # recently enabled auto-acknowledge
                    try:
                        actiondmfilestat = getfirstnotpreserved(dmfilestats, dmactions.DELETE)
                        logger.info("(%s)Picked: %s" % (hex(deviceid),actiondmfilestat.result.resultsName))
                    except DMExceptions.NoDMFileStat:
                        logger.info("(%s) No filesets to delete on this device" % hex(deviceid))
                    except:
                        logger.error(traceback.format_exc())
            else:
                if dmfileset['type'] == dmactions_types.SIG:
                    logger.debug("Sig Proc Input Files auto acknowledge disabled")
                    '''
                    Need to select # of filesets and mark 'S'
                    Need to find 'S' filesets and notify (set to 'N')
                    Set queue length to 5
                    sum(S,N,A) == queue length
                    '''
                    #Get email recipient
                    try:
                        recipient = User.objects.get(username='******').email
                        recipient = recipient.replace(',',' ').replace(';',' ').split()
                    except:
                        recipient = None

                    # Need to select Experiments to process with appropriate dmfilestats action states.
                    exps = Experiment.objects.exclude(storage_options='KI').exclude(expDir='').order_by('date')
                    L_list = exps.filter(results_set__dmfilestat__in = dmfilestats.filter(action_state='L')).distinct()
                    S_list = exps.filter(results_set__dmfilestat__in = dmfilestats.filter(action_state='S')).distinct()
                    N_list = exps.filter(results_set__dmfilestat__in = dmfilestats.filter(action_state='N')).distinct()
                    A_list = exps.filter(results_set__dmfilestat__in = dmfilestats.filter(action_state='A')).distinct()

                    logger.info("Experiments (Keep=False) L:%d S:%d N:%d A:%d" % (L_list.count(),S_list.count(),N_list.count(),A_list.count()))
                    queue_length = 5
                    to_select = queue_length - (A_list.count() + N_list.count() + S_list.count())
                    if to_select > 0:
                        # Mark to_select number of oldest Experiments, from 'L', to 'S'
                        promoted = dmfilestats.filter(result__experiment__id__in=list(L_list[:to_select].values_list('id',flat=True)))
                        if auto_action_enabled:
                            promoted.update(action_state = 'S')
                            for dmfilestat in promoted:
                                EventLog.objects.add_entry(dmfilestat.result, "Signal Processing Input Selected for Deletion", username='******')

                    # Get updated list of Selected items
                    selected = dmfilestats.filter(action_state='S')

                    # Send Selected items to be notified
                    if selected.count() > 0 and auto_action_enabled:
                        logger.debug("notify recipient %s" % (recipient))
                        if notify([dmfilestat.result.resultsName for dmfilestat in selected],recipient):
                            selected.update(action_state = 'N')
                            for dmfilestat in selected:
                                EventLog.objects.add_entry(dmfilestat.result, "Notification for Deletion Sent", username='******')

                    try:
                        actiondmfilestat = getfirstnotpreserved(dmfilestats.filter(action_state='A'), dmactions.DELETE)
                        logger.info("(%s)Picked: %s" % (hex(deviceid),actiondmfilestat.result.resultsName))
                    except DMExceptions.NoDMFileStat:
                        logger.info("(%s) No filesets to delete on this device" % hex(deviceid))
                    except:
                        logger.error(traceback.format_exc())

                else:
                    try:
                        actiondmfilestat = getfirstnotpreserved(dmfilestats, dmactions.DELETE)
                        logger.info("(%s)Picked: %s" % (hex(deviceid),actiondmfilestat.result.resultsName))
                    except DMExceptions.NoDMFileStat:
                        logger.info("(%s) No filesets to delete on this device" % hex(deviceid))
                    except:
                        logger.error(traceback.format_exc())

            #Bail out if disabled
            if auto_action_enabled != True:
                logger.info("Data management auto-action is disabled.")
                return

            if actiondmfilestat is not None:
                delete_action('dm_agent', user_comment, actiondmfilestat)

        else:
            logger.error("Unknown or unhandled action: %s" % dmfileset['auto_action'])

    except (DMExceptions.FilePermission,
            DMExceptions.InsufficientDiskSpace,
            DMExceptions.MediaNotSet,
            DMExceptions.MediaNotAvailable,
            DMExceptions.FilesInUse) as e:
        message  = Message.objects.filter(tags__contains=e.tag)
        if not message:
            Message.error(e.message,tags=e.tag)
            #TODO: TS-6525: This logentry will repeat for an Archive action every 30 seconds until the cause of the exception is fixed.
            #at least, while the message banner is raised, suppress additional Log Entries.
            EventLog.objects.add_entry(actiondmfilestat.result,"%s - %s" % (dmfileset['type'], e.message),username='******')
    except DMExceptions.SrcDirDoesNotExist as e:
        if actiondmfilestat.dmfileset.type == dmactions_types.SIG:
            msg = "Src Dir not found: %s. Setting action_state to Deleted" % e.message
            EventLog.objects.add_entry(actiondmfilestat.result,msg,username='******')
            actiondmfilestat.setactionstate('DD')
            logger.info(msg)
    except Exception as inst:
        msg = ''
        if actiondmfilestat:
            msg = "Auto-action error on %s " % actiondmfilestat.result.resultsName
        msg += " Error: %s" % str(inst)
        logger.exception("%s - %s" % (dmfileset['type'], msg))

        if actiondmfilestat:
            EventLog.objects.add_entry(actiondmfilestat.result,"%s - %s" % (dmfileset['type'], msg),username='******')
    finally:
        applock.unlock()
        #logger.debug("(%05d,%05d) Release lock file" % (os.getppid(),os.getpid()))

    return