Beispiel #1
0
def _getLFNRoot(lfn,
                namespace='',
                configVersion=0,
                bkClient=None,
                quick=False):
    """
  return the root path of a given lfn

  eg : /lhcb/data/CCRC08/00009909 = getLFNRoot(/lhcb/data/CCRC08/00009909/DST/0000/00009909_00003456_2.dst)
  eg : /lhcb/MC/<year>/  = getLFNRoot(None)
  """
    LFN_ROOT = ''

    if not lfn:
        LFN_ROOT = '/lhcb/%s/%s' % (namespace, configVersion)
        gLogger.debug('LFN_ROOT will be %s' % (LFN_ROOT))
        return LFN_ROOT

    lfn = [
        fname.replace(' ', '').replace('LFN:', '') for fname in lfn.split(';')
    ]
    lfnroot = [part for part in lfn[0].split('/') if part]

    if quick:
        for part in lfnroot[0:4]:
            LFN_ROOT += '/%s' % (part)

    else:
        if not bkClient:
            from LHCbDIRAC.BookkeepingSystem.Client.BookkeepingClient import BookkeepingClient
            bkClient = BookkeepingClient()

        dataTypes = bkClient.getFileTypes({})
        if not dataTypes['OK']:
            raise Exception(dataTypes['Message'])
        dataTypes = [x[0] for x in dataTypes['Value']['Records']]
        gLogger.verbose('DataTypes retrieved from the BKK are:\n%s' %
                        (', '.join(dataTypes)))
        gLogger.verbose('wf lfn: %s, namespace: %s, configVersion: %s' %
                        (lfn, namespace, configVersion))

        for part in lfnroot:
            if part not in dataTypes:
                LFN_ROOT += '/%s' % (part)
            else:
                break

    if re.search('//', LFN_ROOT):
        LFN_ROOT = LFN_ROOT.replace('//', '/')

    if namespace.lower() in ('test', 'debug'):
        tmpLfnRoot = LFN_ROOT.split(os.path.sep)
        if len(tmpLfnRoot) > 2:
            tmpLfnRoot[2] = namespace
        else:
            tmpLfnRoot[-1] = namespace

        LFN_ROOT = (os.path.sep).join(tmpLfnRoot, )

    return LFN_ROOT
Beispiel #2
0
class BKQuery():
    """
  It used to build a dictionary using a given Bookkeeping path
  which is used to query the Bookkeeping database.
  """
    def __init__(self,
                 bkQuery=None,
                 prods=None,
                 runs=None,
                 fileTypes=None,
                 visible=True,
                 eventTypes=None):
        prods = prods if prods is not None else []
        runs = runs if runs is not None else []
        fileTypes = fileTypes if fileTypes is not None else []

        self.extraBKitems = ("StartRun", "EndRun", "Production", "RunNumber")
        self.__bkClient = BookkeepingClient()
        bkPath = ''
        bkQueryDict = {}
        self.__bkFileTypes = set()
        self.__exceptFileTypes = set()
        self.__fakeAllDST = 'ZZZZZZZZALL.DST'
        self.__alreadyWarned = False
        if isinstance(bkQuery, BKQuery):
            bkQueryDict = bkQuery.getQueryDict().copy()
        elif isinstance(bkQuery, dict):
            bkQueryDict = bkQuery.copy()
        elif isinstance(bkQuery, basestring):
            bkPath = bkQuery
        bkQueryDict = self.buildBKQuery(bkPath=bkPath,
                                        bkQueryDict=bkQueryDict,
                                        prods=prods,
                                        runs=runs,
                                        fileTypes=fileTypes,
                                        eventTypes=eventTypes,
                                        visible=visible)
        self.__bkPath = bkPath
        self.__bkQueryDict = bkQueryDict
        if not bkQueryDict.get('Visible'):
            self.setVisible(visible)

    def __str__(self):
        return str(self.__bkQueryDict)

    def buildBKQuery(self,
                     bkPath='',
                     bkQueryDict=None,
                     prods=None,
                     runs=None,
                     fileTypes=None,
                     visible=True,
                     eventTypes=None):
        """ it builds a dictionary using a path
    """
        bkQueryDict = bkQueryDict if bkQueryDict is not None else {}
        prods = prods if prods is not None else []
        if not isinstance(prods, list):
            prods = [prods]
        runs = runs if runs is not None else []
        fileTypes = fileTypes if fileTypes is not None else []

        gLogger.verbose("BKQUERY.buildBKQuery: Path %s, Dict %s, \
      Prods %s, Runs %s, FileTypes %s, EventTypes %s, Visible %s" %
                        (bkPath, str(bkQueryDict), str(prods), str(runs),
                         str(fileTypes), str(eventTypes), visible))
        self.__bkQueryDict = {}
        if not bkPath and not prods and not bkQueryDict and not runs:
            return {}
        if bkQueryDict:
            bkQuery = bkQueryDict.copy()
        else:
            bkQuery = {}

        ###### Query given as a path /ConfigName/ConfigVersion/ConditionDescription/ProcessingPass/EventType/FileType ######
        # or if prefixed with evt: /ConfigName/ConfigVersion/EventType/ConditionDescription/ProcessingPass/FileType
        if bkPath:
            self.__getAllBKFileTypes()
            bkFields = ("ConfigName", "ConfigVersion", "ConditionDescription",
                        "ProcessingPass", "EventType", "FileType")
            url = bkPath.split(':', 1)
            if len(url) == 1:
                bkPath = url[0]
            else:
                if url[0] == 'evt':
                    bkFields = ("ConfigName", "ConfigVersion", "EventType",
                                "ConditionDescription", "ProcessingPass",
                                "FileType")
                elif url[0] == 'pp':
                    bkFields = ("ProcessingPass", "EventType", "FileType")
                elif url[0] == 'prod':
                    bkFields = ("Production", "ProcessingPass", "EventType",
                                "FileType")
                elif url[0] == 'runs':
                    bkFields = ("Runs", "ProcessingPass", "EventType",
                                "FileType")
                elif url[0] not in ('sim', 'daq', 'cond'):
                    gLogger.error('Invalid BK path:%s' % bkPath)
                    return self.__bkQueryDict
                bkPath = url[1]
            if bkPath[0] != '/':
                bkPath = '/' + bkPath
            if bkPath[0:2] == '//':
                bkPath = bkPath[1:]
            bkPath = bkPath.replace("RealData", "Real Data")
            i = 0
            processingPass = '******'
            defaultPP = False
            bk = bkPath.split('/')[1:] + len(bkFields) * ['']
            for bpath in bk:
                gLogger.verbose(
                    'buildBKQuery.1. Item #%d, Field %s, From Path %s, ProcessingPass %s'
                    % (i, bkFields[i], bpath, processingPass))
                if bkFields[i] == 'ProcessingPass':
                    if bpath != '' and bpath.upper() != 'ALL' and \
                        not bpath.split(',')[0].split(' ')[0].isdigit() and \
                            not bpath.upper() in self.__bkFileTypes:
                        processingPass = os.path.join(processingPass, bpath)
                        continue
                    # Set the PP
                    if processingPass != '/':
                        bkQuery['ProcessingPass'] = processingPass
                    else:
                        defaultPP = True
                    i += 1
                gLogger.verbose(
                    'buildBKQuery.2. Item #%d, Field %s, From Path %s, ProcessingPass %s'
                    % (i, bkFields[i], bpath, processingPass))
                if bkFields[i] == 'EventType' and bpath:
                    eventTypeList = []
                    # print b
                    if bpath.upper() == 'ALL':
                        bpath = 'ALL'
                    else:
                        for et in bpath.split(','):
                            try:
                                eventType = int(et.split(' ')[0])
                                eventTypeList.append(eventType)
                            except ValueError:
                                pass
                        if len(eventTypeList) == 1:
                            eventTypeList = eventTypeList[0]
                        bpath = eventTypeList
                        gLogger.verbose('buildBKQuery. Event types %s' %
                                        eventTypeList)
                # Set the BK dictionary item
                if bpath != '':
                    bkQuery[bkFields[i]] = bpath
                if defaultPP:
                    # PP was empty, try once more to get the Event Type
                    defaultPP = False
                else:
                    # Go to next item
                    i += 1
                if i == len(bkFields):
                    break

            gLogger.verbose('buildBKQuery. Query dict %s' % str(bkQuery))
            # Set default event type to real data
            if bkQuery.get('ConfigName') != 'MC' and not bkQuery.get(
                    'EventType'):
                bkQuery['EventType'] = '90000000'
            if bkQuery.get('EventType') == 'ALL':
                bkQuery.pop('EventType')

        # Run limits are given
        runs = bkQuery.pop('Runs', runs)
        if runs:
            try:
                bkQuery = parseRuns(bkQuery, runs)
            except BadRunRange:
                return self.__bkQueryDict

        ###### Query given as a list of production ######
        if prods and str(prods[0]).upper() != 'ALL':
            try:
                bkQuery.setdefault('Production',
                                   []).extend([int(prod) for prod in prods])
            except ValueError as ex:  # The prods list does not contains numbers
                gLogger.warn(ex)
                gLogger.error('Invalid production list', str(prods))
                return self.__bkQueryDict

        # If an event type is specified
        if eventTypes:
            bkQuery['EventType'] = eventTypes

        # Set the file type(s) taking into account excludes file types
        fileTypes = bkQuery.get('FileType', fileTypes)
        bkQuery.pop('FileType', None)
        self.__bkQueryDict = bkQuery.copy()
        fileType = self.__fileType(fileTypes)
        # print fileType
        if fileType:
            bkQuery['FileType'] = fileType

        # Remove all "ALL"'s in the dict, if any
        for i in self.__bkQueryDict:
            if isinstance(bkQuery[i], basestring) and bkQuery[i] == 'ALL':
                bkQuery.pop(i)

        # If there is only one production, make it faster with a single value rather than a list
        prodList = bkQuery.get('Production')
        if isinstance(prodList, list) and len(prodList) == 1:
            bkQuery['Production'] = prodList[0]
        self.__bkQueryDict = bkQuery.copy()
        self.setVisible(visible)

        # Set both event type entries
        # print "Before setEventType", self.__bkQueryDict
        if not self.setEventType(bkQuery.get('EventType')):
            self.__bkQueryDict = {}
            return self.__bkQueryDict
        # Set conditions
        # print "Before setConditions", self.__bkQueryDict
        self.setConditions(
            bkQuery.get(
                'ConditionDescription',
                bkQuery.get('DataTakingConditions',
                            bkQuery.get('SimulationConditions'))))
        # print "Returned value", self.__bkQueryDict
        return self.__bkQueryDict

    def setOption(self, key, val):
        """
    It insert an item to the dictionary. The key is an bookkeeping attribute (condition).
    """
        if val:
            self.__bkQueryDict[key] = val
        else:
            self.__bkQueryDict.pop(key, None)
        return self.__bkQueryDict

    def setConditions(self, cond=None):
        """ Set the dictionary items for a given condition, or remove it (cond=None) """
        if 'ConfigName' not in self.__bkQueryDict and cond:
            gLogger.warn(
                "Impossible to set Conditions to a BK Query without Configuration"
            )
            return self.__bkQueryDict
        # There are two items in the dictionary: ConditionDescription and Simulation/DataTaking-Conditions
        eventType = self.__bkQueryDict.get('EventType', 'ALL')
        if self.__bkQueryDict.get('ConfigName') == 'MC' or \
            (isinstance(eventType, basestring) and eventType.upper() != 'ALL' and
             eventType[0] != '9'):
            conditionsKey = 'SimulationConditions'
        else:
            conditionsKey = 'DataTakingConditions'
        self.setOption('ConditionDescription', cond)
        return self.setOption(conditionsKey, cond)

    def setFileType(self, fileTypes=None):
        """insert the file type to the Boookkeeping dictionary
    """
        return self.setOption('FileType', self.__fileType(fileTypes))

    def setDQFlag(self, dqFlag='OK'):
        """
    Sets the data quality.
    """
        if isinstance(dqFlag, basestring):
            dqFlag = dqFlag.upper()
        elif isinstance(dqFlag, list):
            dqFlag = [dq.upper() for dq in dqFlag]
        return self.setOption('DataQuality', dqFlag)

    def setStartDate(self, startDate):
        """
    Sets the start date.
    """
        return self.setOption('StartDate', startDate)

    def setEndDate(self, endDate):
        """
    Sets the end date
    """
        return self.setOption('EndDate', endDate)

    def setProcessingPass(self, processingPass):
        """
    Sets the processing pass
    """
        return self.setOption('ProcessingPass', processingPass)

    def setEventType(self, eventTypes=None):
        """
    Sets the event type
    """
        if eventTypes:
            if isinstance(eventTypes, basestring):
                eventTypes = eventTypes.split(',')
            elif not isinstance(eventTypes, list):
                eventTypes = [eventTypes]
            try:
                eventTypes = [str(int(et)) for et in eventTypes]
            except ValueError as ex:
                gLogger.warn(ex)
                gLogger.error('Invalid list of event types', eventTypes)
                return {}
            if isinstance(eventTypes, list) and len(eventTypes) == 1:
                eventTypes = eventTypes[0]
        return self.setOption('EventType', eventTypes)

    def setVisible(self, visible=None):
        """
    Sets the visibility flag
    """
        if visible is True or (isinstance(visible, basestring)
                               and visible[0].lower() == 'y'):
            visible = 'Yes'
        if visible is False:
            visible = 'No'
        return self.setOption('Visible', visible)

    def setExceptFileTypes(self, fileTypes):
        """
    Sets the expected file types
    """
        if not isinstance(fileTypes, list):
            fileTypes = [fileTypes]
        self.__exceptFileTypes.update(fileTypes)
        self.setFileType(
            [t for t in self.getFileTypeList() if t not in fileTypes])

    def getExceptFileTypes(self):
        return list(self.__exceptFileTypes)

    def getQueryDict(self):
        """
    Returns the bookkeeping dictionary
    """
        return self.__bkQueryDict

    def getPath(self):
        """
    Returns the Bookkeeping path
    """
        return self.__bkPath

    def makePath(self):
        """
    Builds a path from the dictionary
    """
        bk = self.__bkQueryDict
        fileType = bk.get('FileType', '')
        if isinstance(fileType, list):
            fileType = ','.join(fileType)
        path = os.path.join(
            '/', bk.get('ConfigName', ''), bk.get('ConfigVersion', ''),
            bk.get('ConditionDescription', '.'),
            bk.get('ProcessingPass', '.')[1:],
            str(bk.get('EventType', '.')).replace('90000000', '.'),
            fileType).replace('/./', '//')
        while True:
            if path.endswith('/'):
                path = path[:-1]
            else:
                return path

    def getFileTypeList(self):
        """
    Returns the file types
    """
        fileTypes = self.__bkQueryDict.get('FileType', [])
        if not isinstance(fileTypes, list):
            fileTypes = [fileTypes]
        return fileTypes

    def getEventTypeList(self):
        """
    Returns the event types
    """
        eventType = self.__bkQueryDict.get("EventType", [])
        if eventType:
            if not isinstance(eventType, list):
                eventType = [eventType]
        return eventType

    def getProcessingPass(self):
        """
    Returns the processing pass
    """
        return self.__bkQueryDict.get('ProcessingPass', '')

    def getConditions(self):
        """
    Returns the Simulation/data taking conditions
    """
        return self.__bkQueryDict.get('ConditionDescription', '')

    def getConfiguration(self):
        """
    Returns the configuration name and configuration version
    """
        configName = self.__bkQueryDict.get('ConfigName', '')
        configVersion = self.__bkQueryDict.get('ConfigVersion', '')
        if not configName or not configVersion:
            return ''
        return os.path.join('/', configName, configVersion)

    def isVisible(self):
        """
    Returns True/False depending on the visibility flag
    """
        return self.__bkQueryDict.get('Visible', 'All')

    def __fileType(self, fileType=None, returnList=False):
        """
    return the file types taking into account the expected file types
    """
        gLogger.verbose("BKQuery.__fileType: %s, fileType: %s" %
                        (self, fileType))
        if not fileType:
            return []
        self.__getAllBKFileTypes()
        if isinstance(fileType, list):
            fileTypes = fileType
        else:
            fileTypes = fileType.split(',')
        allRequested = None
        if fileTypes[0].lower() == "all":
            allRequested = True
            bkTypes = self.getBKFileTypes()
            gLogger.verbose('BKQuery.__fileType: bkTypes %s' % str(bkTypes))
            if bkTypes:
                fileTypes = list(set(bkTypes) - self.__exceptFileTypes)
            else:
                fileTypes = []
        expandedTypes = set()
        # print "Requested", fileTypes
        for fileType in fileTypes:
            if fileType.lower() == 'all.hist':
                allRequested = False
                expandedTypes.update([
                    t for t in self.__exceptFileTypes.union(self.__bkFileTypes)
                    if t.endswith('HIST')
                ])
            elif fileType.lower().find("all.") == 0:
                ext = '.' + fileType.split('.')[1]
                fileType = []
                if allRequested is None:
                    allRequested = True
                expandedTypes.update([
                    t for t in set(self.getBKFileTypes()) -
                    self.__exceptFileTypes if t.endswith(ext)
                ])
            else:
                expandedTypes.add(fileType)
        # Remove __exceptFileTypes only if not explicitly required
        # print "Obtained", fileTypes, expandedTypes
        gLogger.verbose(
            "BKQuery.__fileType: requested %s, expanded %s, except %s" %
            (allRequested, expandedTypes, self.__exceptFileTypes))
        if expandedTypes - self.__bkFileTypes and not self.__alreadyWarned:
            self.__alreadyWarned = True
            gLogger.always(
                "**** Take care: some requested file types do not exist!!",
                str(sorted(expandedTypes - self.__bkFileTypes)))
        if allRequested or not expandedTypes & self.__exceptFileTypes:
            expandedTypes -= self.__exceptFileTypes
        gLogger.verbose("BKQuery.__fileType: result %s" %
                        sorted(expandedTypes))
        if len(expandedTypes) == 1 and not returnList:
            return list(expandedTypes)[0]
        else:
            return list(expandedTypes)

    def __getAllBKFileTypes(self):
        """
    Returns the file types from the bookkeeping database
    """
        if not self.__bkFileTypes:
            self.__bkFileTypes = set([self.__fakeAllDST])
            warned = False
            while True:
                res = self.__bkClient.getAvailableFileTypes()
                if res['OK']:
                    dbresult = res['Value']
                    for record in dbresult['Records']:
                        if record[0].endswith('HIST') or \
                                record[0].endswith('ETC') or \
                                record[0] == 'LOG' or \
                                record[0].endswith('ROOT'):
                            self.__exceptFileTypes.add(record[0])
                        self.__bkFileTypes.add(record[0])
                    break
                if not warned:
                    gLogger.always('Error getting BK file types, retrying',
                                   res['Message'])
                    warned = True

    def __getBKFiles(self, bkQueryDict, retries=5):
        """
    Call BK getFiles() with some retries
    """
        if not retries:
            retries = sys.maxsize
        errorLogged = False
        while retries:
            res = self.__bkClient.getFiles(bkQueryDict)
            if res['OK']:
                break
            retries -= 1
            if not errorLogged:
                errorLogged = True
                gLogger.warn("Error getting files from BK, retrying...",
                             res['Message'])
        return res

    def getLFNsAndSize(self, getSize=True):
        """
    Returns the LFNs and their size for a given data set
    """
        self.__getAllBKFileTypes()
        res = self.__getBKFiles(self.__bkQueryDict)
        lfns = []
        lfnSize = 0
        if not res['OK']:
            gLogger.error("Error from BK for %s:" % self.__bkQueryDict,
                          res['Message'])
        else:
            lfns = set(res['Value'])
            exceptFiles = list(self.__exceptFileTypes)
            if exceptFiles and not self.__bkQueryDict.get('FileType'):
                res = self.__getBKFiles(
                    BKQuery(self.__bkQueryDict).setOption(
                        'FileType', exceptFiles))
                if res['OK']:
                    lfnsExcept = set(res['Value']) & lfns
                else:
                    gLogger.error(
                        "***** ERROR ***** Error in getting dataset from BK for %s files:"
                        % exceptFiles, res['Message'])
                    lfnsExcept = set()
                if lfnsExcept:
                    gLogger.warn(
                        "***** WARNING ***** Found %d files in BK query that will be \
          excluded (file type in %s)!" % (len(lfnsExcept), str(exceptFiles)))
                    gLogger.warn(
                        "                    If creating a transformation, set '--FileType ALL'"
                    )
                    lfns = lfns - lfnsExcept
                else:
                    exceptFiles = False
            if getSize:
                # Get size only if needed
                query = BKQuery(self.__bkQueryDict)
                query.setOption("FileSize", True)
                res = self.__getBKFiles(query.getQueryDict())
                if res['OK'] and isinstance(res['Value'],
                                            list) and res['Value'][0]:
                    lfnSize = res['Value'][0]
                if exceptFiles and not self.__bkQueryDict.get('FileType'):
                    res = self.__getBKFiles(
                        query.setOption('FileType', exceptFiles))
                    if res['OK'] and isinstance(res['Value'],
                                                list) and res['Value'][0]:
                        lfnSize -= res['Value'][0]

                lfnSize /= 1000000000000.
            else:
                lfnSize = 0.
        return {'LFNs': list(lfns), 'LFNSize': lfnSize}

    def getLFNSize(self, visible=None):
        """
    Returns the size of a  given data set
    """
        if visible is None:
            visible = self.isVisible()
        res = self.__getBKFiles(
            BKQuery(self.__bkQueryDict,
                    visible=visible).setOption('FileSize', True))
        if res['OK'] and isinstance(res['Value'], list) and res['Value'][0]:
            lfnSize = res['Value'][0]
        else:
            lfnSize = 0
        return lfnSize

    def getNumberOfLFNs(self, visible=None):
        """
    Returns the number of LFNs correspond to a given data set
    """
        if visible is None:
            visible = self.isVisible()
        if self.isVisible() != visible:
            query = BKQuery(self.__bkQueryDict, visible=visible)
        else:
            query = self
        fileTypes = query.getFileTypeList()
        nbFiles = 0
        size = 0
        for fileType in fileTypes:
            if fileType:
                res = self.__bkClient.getFilesSummary(
                    query.setFileType(fileType))
                # print query, res
                if res['OK']:
                    res = res['Value']
                    ind = res['ParameterNames'].index('NbofFiles')
                    if res['Records'][0][ind]:
                        nbFiles += res['Records'][0][ind]
                        ind1 = res['ParameterNames'].index('FileSize')
                        size += res['Records'][0][ind1]
                        # print 'Visible',query.isVisible(),fileType, 'Files:',
                        # res['Records'][0][ind], 'Size:', res['Records'][0][ind1]
        return {'NumberOfLFNs': nbFiles, 'LFNSize': size}

    def getLFNs(self, printSEUsage=False, printOutput=True, visible=None):
        """
    returns a list of lfns. It prints statistics about the data sets if it is requested.
    """
        if visible is None:
            visible = self.isVisible()

        if self.isVisible() != visible:
            query = BKQuery(self.__bkQueryDict, visible=visible)
        else:
            query = self

        # Loop for each production or each event type rather than make a single query
        loopItem = None
        prods = self.__bkQueryDict.get('Production')
        eventTypes = self.__bkQueryDict.get('EventType')
        if prods and isinstance(prods, list):
            loopItem = 'Production'
            loopList = prods
        elif eventTypes and isinstance(eventTypes, list):
            loopItem = 'EventType'
            loopList = eventTypes
        if loopItem:
            # It's faster to loop on a list of prods or event types than query the BK with a list as argument
            lfns = []
            lfnSize = 0
            if query == self:
                query = BKQuery(self.__bkQueryDict, visible=visible)
            for item in loopList:
                query.setOption(loopItem, item)
                lfnsAndSize = query.getLFNsAndSize(getSize=printOutput)
                lfns += lfnsAndSize['LFNs']
                lfnSize += lfnsAndSize['LFNSize']
        else:
            lfnsAndSize = query.getLFNsAndSize(getSize=printOutput)
            lfns = lfnsAndSize['LFNs']
            lfnSize = lfnsAndSize['LFNSize']

        if not lfns:
            gLogger.verbose("No files found for BK query %s" %
                            str(self.__bkQueryDict))
        else:
            lfns.sort()

            # Only for printing
            if printOutput:
                gLogger.notice("\n%d files (%.1f TB) in directories:" %
                               (len(lfns), lfnSize))
                dirs = {}
                for lfn in lfns:
                    directory = os.path.join(os.path.dirname(lfn), '')
                    dirs[directory] = dirs.setdefault(directory, 0) + 1
                for directory in sorted(dirs):
                    gLogger.notice("%s %s files" %
                                   (directory, dirs[directory]))
                if printSEUsage:
                    rpc = RPCClient('DataManagement/StorageUsage')
                    totalUsage = {}
                    totalSize = 0
                    for directory in dirs:
                        res = rpc.getStorageSummary(directory, '', '', [])
                        if res['OK']:
                            for se in [
                                    se for se in res['Value']
                                    if not se.endswith("-ARCHIVE")
                            ]:
                                totalUsage[se] = totalUsage.setdefault(
                                    se, 0) + res['Value'][se]['Size']
                                totalSize += res['Value'][se]['Size']
                    ses = sorted(totalUsage)
                    totalUsage['Total'] = totalSize
                    ses.append('Total')
                    gLogger.notice("\n%s %s" % ("SE".ljust(20), "Size (TB)"))
                    for se in ses:
                        gLogger.notice("%s %s" %
                                       (se.ljust(20),
                                        ('%.1f' %
                                         (totalUsage[se] / 1000000000000.))))
        return lfns

    def getDirs(self, printOutput=False, visible=None):
        """
    Returns the directories
    """
        if visible is None:
            visible = self.isVisible()
        lfns = self.getLFNs(printSEUsage=True,
                            printOutput=printOutput,
                            visible=visible)
        dirs = set()
        for lfn in lfns:
            dirs.add(os.path.dirname(lfn))
        return sorted(dirs)

    @staticmethod
    def __getProdStatus(prod):
        """
    Returns the status of a given transformation
    """
        res = TransformationClient().getTransformation(prod, extraParams=False)
        if not res['OK']:
            gLogger.error("Couldn't get information on production %d" % prod)
            return None
        return res['Value']['Status']

    def getBKRuns(self):
        """
    It returns a list of runs from the bookkeeping.
    """
        if self.getProcessingPass().replace('/', '') == 'Real Data':
            return self.getBKProductions()

    def getBKProductions(self, visible=None):
        """
    It returns a list of productions
    """
        if visible is None:
            visible = self.isVisible()
        prodList = self.__bkQueryDict.get('Production')
        if prodList:
            if not isinstance(prodList, list):
                prodList = [prodList]
            return sorted(prodList)
        if not self.getProcessingPass():
            gLogger.fatal(
                'Impossible to get a list of productions without the Processing Pass'
            )
            return []
        eventTypes = self.__bkQueryDict.get('EventType')
        if not isinstance(eventTypes, list):
            eventTypes = [eventTypes]
        fullList = set()
        for eventType in eventTypes:
            bkQ = BKQuery(self.__bkQueryDict)
            bkQ.setVisible(visible)
            bkDict = bkQ.setEventType(eventType)
            # gLogger.notice( 'Get productions for BK query', str( bkDict ) )
            res = self.__bkClient.getProductions(bkDict)
            if not res['OK']:
                gLogger.error('Error getting productions from BK',
                              res['Message'])
                return []
            if self.getProcessingPass().replace('/', '') != 'Real Data':
                fileTypes = self.getFileTypeList()
                prodList = set(prod for prods in res['Value']['Records']
                               for prod in prods
                               if self.__getProdStatus(prod) != 'Deleted')
                # print '\n', self.__bkQueryDict, res['Value']['Records'], '\nVisible:', visible, prodList
                pList = set()
                if fileTypes:
                    transClient = TransformationClient()
                    for prod in prodList:
                        res = transClient.getBookkeepingQuery(prod)
                        if res['OK'] and res['Value']['FileType'] in fileTypes:
                            pList.add(prod)
                if not pList:
                    pList = prodList
            else:
                runList = sorted(
                    [-run for r in res['Value']['Records'] for run in r])
                startRun = int(self.__bkQueryDict.get('StartRun', 0))
                endRun = int(self.__bkQueryDict.get('EndRun', sys.maxsize))
                pList = set(run for run in runList
                            if run >= startRun and run <= endRun)
            fullList.update(pList)
        return sorted(fullList)

    def getBKConditions(self):
        """
    It returns the data taking / simulation conditions
    """
        conditions = self.__bkQueryDict.get('ConditionDescription')
        if conditions:
            if not isinstance(conditions, list):
                conditions = [conditions]
            return conditions
        result = self.__bkClient.getConditions(self.__bkQueryDict)
        if result['OK']:
            resList = result['Value']
        else:
            return []
        conditions = []
        for res in resList:
            ind = res['ParameterNames'].index('Description')
            if res['Records']:
                conditions += [par[ind] for par in res['Records']]
                break
        return sorted(conditions)

    def getBKEventTypes(self):
        """
    It returns the event types
    """
        eventType = self.getEventTypeList()
        if eventType:
            return eventType
        res = self.__bkClient.getEventTypes(self.__bkQueryDict)['Value']
        ind = res['ParameterNames'].index('EventType')
        eventTypes = sorted([rec[ind] for rec in res['Records']])
        return eventTypes

    def getBKFileTypes(self, bkDict=None):
        """
    It returns the file types.
    """
        fileTypes = self.getFileTypeList()
        # print "Call getBKFileTypes:", self, fileTypes
        if not fileTypes:
            if not bkDict:
                bkDict = self.__bkQueryDict.copy()
            else:
                bkDict = bkDict.copy()
            bkDict.setdefault('Visible', 'All')
            bkDict.pop('RunNumber', None)
            fileTypes = []
            eventTypes = bkDict.get('EventType')
            if isinstance(eventTypes, list):
                for et in eventTypes:
                    bkDict['EventType'] = et
                    fileTypes += self.getBKFileTypes(bkDict)
            else:
                res = self.__bkClient.getFileTypes(bkDict)
                if res['OK']:
                    res = res['Value']
                    ind = res['ParameterNames'].index('FileTypes')
                    fileTypes = [
                        rec[ind] for rec in res['Records']
                        if rec[ind] not in self.__exceptFileTypes
                    ]
        if 'ALL.DST' in fileTypes:
            fileTypes.remove('ALL.DST')
            fileTypes.append(self.__fakeAllDST)
        # print 'FileTypes1', fileTypes
        fileTypes = self.__fileType(fileTypes, returnList=True)
        # print 'FileTypes2', fileTypes
        if self.__fakeAllDST in fileTypes:
            fileTypes.remove(self.__fakeAllDST)
            fileTypes.append('ALL.DST')
        # print 'FileTypes3', fileTypes
        return fileTypes

    def getBKProcessingPasses(self, queryDict=None, depth=None):
        """
    It returns the processing pass.
    """
        if depth is None:
            depth = sys.maxsize
        processingPasses = {}
        if not queryDict:
            queryDict = self.__bkQueryDict.copy()
        initialPP = queryDict.get('ProcessingPass', '/')
        # print "Start", initialPP, queryDict
        res = self.__bkClient.getProcessingPass(queryDict, initialPP)
        if not res['OK']:
            if 'Empty Directory' not in res['Message']:
                gLogger.error(
                    "ERROR getting processing passes for %s" % queryDict,
                    res['Message'])
            return {}
        ppRecords = res['Value'][0]
        if 'Name' in ppRecords['ParameterNames']:
            ind = ppRecords['ParameterNames'].index('Name')
            passes = sorted([
                os.path.join(initialPP, rec[ind])
                for rec in ppRecords['Records']
            ])
        else:
            passes = []
        evtRecords = res['Value'][1]
        if 'EventType' in evtRecords['ParameterNames']:
            ind = evtRecords['ParameterNames'].index('EventType')
            eventTypes = [str(rec[ind]) for rec in evtRecords['Records']]
        else:
            eventTypes = []

        if passes and depth:
            depth -= 1
            nextProcessingPasses = {}
            for pName in passes:
                processingPasses[pName] = []
                queryDict['ProcessingPass'] = pName
                nextProcessingPasses.update(
                    self.getBKProcessingPasses(queryDict, depth=depth))
            processingPasses.update(nextProcessingPasses)
        if eventTypes:
            processingPasses[initialPP] = eventTypes
        for pName in ('/Real Data', '/'):
            if pName in processingPasses:
                processingPasses.pop(pName)
        # print "End", initialPP, [( key, processingPasses[key] ) for key in sorted( processingPasses.keys() )]
        return processingPasses