示例#1
0
    def kill(self, workflow, force, jobids, killwarning, userdn, userproxy=None):
        """Request to Abort a workflow.

           :arg str workflow: a workflow name"""

        retmsg = "ok"
        self.logger.info("About to kill workflow: %s. Getting status first.", workflow)
        row = self.api.query(None, None, self.Task.ID_sql, taskname = workflow)
        try:
            #just one row is picked up by the previous query
            row = self.Task.ID_tuple(*next(row))
        except StopIteration:
            raise ExecutionError("Impossible to find task %s in the database." % workflow)
        warnings = literal_eval(row.task_warnings.read() if row.task_warnings else '[]') #there should actually is a default, but just in case
        if killwarning:
            warnings += [killwarning]
        warnings = str(warnings)

        args = {'ASOURL' : getattr(row, 'asourl', '')}

        if row.task_status in ['SUBMITTED', 'KILLFAILED', 'RESUBMITFAILED', 'FAILED', 'KILLED', 'TAPERECALL']:
            args.update({"killList": jobids})
            #Set arguments first so in case of failure we don't do any "damage"
            self.api.modify(self.Task.SetArgumentsTask_sql, taskname = [workflow], arguments = [str(args)])
            self.api.modify(self.Task.SetStatusWarningTask_sql, status = ["NEW"], command = ["KILL"], taskname = [workflow], warnings = [str(warnings)])
        elif row.task_status == 'NEW' and row.task_command == 'SUBMIT':
            #if the task has just been submitted and not acquired by the TW
            self.api.modify(self.Task.SetStatusWarningTask_sql, status = ["KILLED"], command = ["KILL"], taskname = [workflow], warnings = [str(warnings)])
        else:
            raise ExecutionError("You cannot kill a task if it is in the %s status" % row.task_status)

        return [{"result":retmsg}]
    def _getAsoConfig(self, asourl, asodb):
        """ Figures out which asourl and asodb to use. Here some rules:
            1) If ASOURL is set up in the client then use it. If ASODB is None then default to 'asynctransfer' (for old clients).
               If ASODB is defined but ASOURL is not, then give an error (somebody is doing it wrong!).
            2) If ASOURL is not defined in the client then look at the external configuration of the server which looks like:
                ...
                "ASOURL" : "https://cmsweb.cern.ch/couchdb",
                "asoConfig" : [
                    {"couchURL" : "https://cmsweb.cern.ch/couchdb", "couchDBName" : "asynctransfer"},
                    {"couchURL" : "https://cmsweb-testbed.cern.ch/couchdb2", "couchDBName" : "asynctransfer1"}
                ]
                ...

                2.1) The external configuration contains asoConfig (the new and default thing now):
                     Pick up a random element from the list and figures out the asourl and asodb.
                     No validation of the configuration is done,
                     we assume asocConfig is a list that contains dicts (at least one),
                     and each dict has both couchURL and couchDBName.
                     Documentation about the external conf is here:
                         https://twiki.cern.ch/twiki/bin/view/CMSPublic/CMSCrabRESTInterface#External_dynamic_configuration
                2.2) Else if the external configuration contains the old key ASOURL:
                     ASOURL could either be a string (the value for asourl that we need to use) or a list (in which case
                     we will use a random element)
            3) If asourl is not defined in the client nor in the external config give an error.
        """

        ASYNC_DEFAULT_DB = 'asynctransfer'

        #1) The user is trying to pass something to us
        if asourl:
            self.logger.info("ASO url and database defined in the client configuration")
            if not asodb:
                asodb = ASYNC_DEFAULT_DB
        if asodb and not asourl:
            raise ExecutionError("You set up Debug.ASODB but you did not set up Debug.ASOURL. Please specify ASOURL as well or remove ASODB.")

        #2) We need to figure out the values ourselves
        if not asourl: #just checking asourl here because either both asourl and asodb are set, or neither are
            extconf = self.centralcfg.centralconfig.get("backend-urls", {})
            #2.1) Get asourl and asodb from the new extrnal configuration (that's what we usually do for most of the users)
            if 'asoConfig' in extconf:
                asoconf = random.choice(extconf['asoConfig'])
                asourl = asoconf['couchURL']
                asodb = asoconf['couchDBName']
            #2.2) No asoConfig defined, let's look for the old ASOURL.
            elif 'ASOURL' in extconf:
                msg = "You are using the old ASOURL parameter in your external configuration, please use asoConfig instead."
                msg += "\nSee https://twiki.cern.ch/twiki/bin/view/CMSPublic/CMSCrabRESTInterface#External_dynamic_configuration"
                self.logger.warning(msg)
                if isinstance(extconf['ASOURL'], list):
                    asourl = random.choice(asourl)
                else:
                    asourl = extconf['ASOURL']
                asodb = ASYNC_DEFAULT_DB

        #3) Give an error if we cannot figure out aso configuration
        if not (asourl and asodb):
            raise ExecutionError("The server configuration is wrong (asoConfig missing): cannot figure out ASO urls.")

        return asourl, asodb
示例#3
0
    def __init__(self, app, config, mount):
        DatabaseRESTApi.__init__(self, app, config, mount)

        self.formats = [('application/json', JSONFormat())]

        status, serverdn = getstatusoutput(
            'openssl x509 -noout -subject -in %s | cut -f2- -d\ ' %
            config.serverhostcert)
        if status is not 0:
            raise ExecutionError(
                "Internal issue when retrieving crabserver service DN.")

        hbuf = StringIO.StringIO()
        bbuf = StringIO.StringIO()

        curl = pycurl.Curl()
        curl.setopt(pycurl.URL, config.extconfigurl)
        curl.setopt(pycurl.WRITEFUNCTION, bbuf.write)
        curl.setopt(pycurl.HEADERFUNCTION, hbuf.write)
        curl.setopt(pycurl.FOLLOWLOCATION, 1)
        curl.perform()
        curl.close()

        header = ResponseHeader(hbuf.getvalue())
        if header.status < 200 or header.status >= 300:
            cherrypy.log("Problem %d reading from %s." %
                         (config.extconfigurl, header.status))
            raise ExecutionError(
                "Internal issue when retrieving external confifuration")
        extconfig = json.decode(bbuf.getvalue())

        #Global initialization of Data objects. Parameters coming from the config should go here
        DataUserWorkflow.globalinit(config.workflowManager)
        DataWorkflow.globalinit(dbapi=self, phedexargs={'endpoint': config.phedexurl}, dbsurl=config.dbsurl,\
                                        credpath=config.credpath, transformation=config.transformation)
        DataFileMetadata.globalinit(dbapi=self)
        Utils.globalinit(config.serverhostkey, config.serverhostcert, serverdn,
                         config.credpath)

        ## TODO need a check to verify the format depending on the resource
        ##      the RESTFileMetadata has the specifc requirement of getting xml reports
        self._add({
            'workflow':
            RESTUserWorkflow(app, self, config, mount),
            'campaign':
            RESTCampaign(app, self, config, mount),
            'info':
            RESTServerInfo(app, self, config, mount, serverdn,
                           extconfig.get('delegate-dn', [])),
            'filemetadata':
            RESTFileMetadata(app, self, config, mount),
        })

        self._initLogger(getattr(config, 'loggingFile', None),
                         getattr(config, 'loggingLevel', None))
示例#4
0
    def webdirprx(self, **kwargs):
        """ Returns the proxied url for the schedd if the schedd has any, returns an empty list instead. Raises in case of other errors.
            To test it use:
            curl -X GET 'https://mmascher-dev6.cern.ch/crabserver/dev/task?subresource=webdirprx&workflow=150224_230633:mmascher_crab_testecmmascher-dev6_3'\
                -k --key /tmp/x509up_u8440 --cert /tmp/x509up_u8440 -v
        """
        if 'workflow' not in kwargs or not kwargs['workflow']:
            raise InvalidParameter(
                "Task name not found in the input parameters")
        workflow = kwargs['workflow']
        self.logger.info("Getting proxied url for %s" % workflow)

        try:
            row = self.Task.ID_tuple(*next(
                self.api.query(None, None, self.Task.ID_sql,
                               taskname=workflow)))
        except StopIteration:
            raise ExecutionError(
                "Impossible to find task %s in the database." %
                kwargs["workflow"])

        if row.user_webdir:
            #extract /cms1425/taskname from the user webdir
            suffix = re.search(r"(/[^/]+/[^/]+/?)$", row.user_webdir).group(0)
        else:
            raise ExecutionError(
                "Webdir not set in the database. Cannot build proxied webdir")

        #=============================================================================
        # scheddObj is a dictionary composed like this (see the value of htcondorSchedds):
        # "htcondorSchedds": {
        #  "*****@*****.**": {
        #      "proxiedurl": "https://cmsweb.cern.ch/scheddmon/5"
        #  },
        #  ...
        # }
        # so that they have a "proxied URL" to be used in case the schedd is
        # behind a firewall.
        #=============================================================================
        scheddsObj = self.centralcfg.centralconfig['backend-urls'].get(
            'htcondorSchedds', {})
        self.logger.info(
            "ScheddObj for task %s is: %s\nSchedd used for submission %s" %
            (workflow, scheddsObj, row.schedd))
        #be careful that htcondorSchedds could be a list (backward compatibility). We might want to remove this in the future
        if row.schedd in list(scheddsObj) and isinstance(scheddsObj, dict):
            self.logger.debug("Found schedd %s" % row.schedd)
            proxiedurlbase = scheddsObj[row.schedd].get('proxiedurl')
            self.logger.debug("Proxied url base is %s" % proxiedurlbase)
            if proxiedurlbase:
                yield proxiedurlbase + suffix
        else:
            self.logger.info("Could not determine proxied url for task %s" %
                             workflow)
示例#5
0
    def kill(self, workflow, force, jobids, userdn, userproxy=None):
        """Request to Abort a workflow.

           :arg str workflow: a workflow name"""

        retmsg = "ok"
        self.logger.info("About to kill workflow: %s. Getting status first." %
                         workflow)
        statusRes = self.status(workflow, userdn, userproxy)[0]

        args = {'ASOURL': statusRes.get("ASOURL", "")}
        # Hm...
        dbSerializer = str

        if statusRes['status'] in [
                'SUBMITTED', 'KILLFAILED', 'RESUBMITFAILED', 'FAILED'
        ]:
            killList = [
                jobid for jobstatus, jobid in statusRes['jobList']
                if jobstatus not in self.successList
            ]
            if jobids:
                #if the user wants to kill specific jobids make the intersection
                killList = list(set(killList) & set(jobids))
                #check if all the requested jobids can be resubmitted
                if len(killList) != len(jobids):
                    retmsg = "Cannot request kill for %s" % (set(jobids) -
                                                             set(killList))
            if not killList:
                raise ExecutionError(
                    "There are no jobs to kill. Only jobs not in %s states can be killed"
                    % self.successList)
            self.logger.info("Jobs to kill: %s" % killList)

            args.update({"killList": killList, "killAll": jobids == []})
            self.api.modify(self.Task.SetStatusTask_sql,
                            status=["KILL"],
                            taskname=[workflow])
            self.api.modify(self.Task.SetArgumentsTask_sql,
                            taskname=[workflow],
                            arguments=[dbSerializer(args)])
        elif statusRes['status'] == 'NEW':
            self.api.modify(self.Task.SetStatusTask_sql,
                            status=["KILLED"],
                            taskname=[workflow])
        else:
            raise ExecutionError(
                "You cannot kill a task if it is in the %s state" %
                statusRes['status'])

        return [{"result": retmsg}]
示例#6
0
    def resubmit(self, workflow, siteblacklist, sitewhitelist, jobids, userdn,
                 userproxy):
        """Request to reprocess what the workflow hasn't finished to reprocess.
           This needs to create a new workflow in the same campaign

           :arg str workflow: a valid workflow name
           :arg str list siteblacklist: black list of sites, with CMS name;
           :arg str list sitewhitelist: white list of sites, with CMS name."""

        retmsg = "ok"
        self.logger.info(
            "About to resubmit workflow: %s. Getting status first." % workflow)
        statusRes = self.status(workflow, userdn, userproxy)[0]

        #if there are failed jobdef submission we fail
        if statusRes['failedJobdefs']:
            raise ExecutionError(
                "You cannot resubmit a task if not all the jobs have been submitted. The feature will be available in the future"
            )

        if statusRes['status'] in ['SUBMITTED', 'KILLED', 'FAILED']:
            resubmitList = [
                jobid for jobstatus, jobid in statusRes['jobList']
                if jobstatus in self.failedList
            ]
            if jobids:
                #if the user wants to kill specific jobids make the intersection
                resubmitList = list(set(resubmitList) & set(jobids))
                #check if all the requested jobids can be resubmitted
                if len(resubmitList) != len(jobids):
                    retmsg = "Cannot request resubmission for %s" % (
                        set(jobids) - set(resubmitList))
            if not resubmitList:
                raise ExecutionError(
                    "There are no jobs to resubmit. Only jobs in %s states are resubmitted"
                    % self.failedList)
            self.logger.info("Jobs to resubmit: %s" % resubmitList)
            self.api.modify(SetStatusTask.sql,
                            status=["RESUBMIT"],
                            taskname=[workflow])
            self.api.modify(SetArgumentsTask.sql, taskname = [workflow],\
                            arguments = [str({"siteBlackList":siteblacklist, "siteWhiteList":sitewhitelist, "resubmitList":resubmitList})])
        else:
            raise ExecutionError(
                "You cannot resubmit a task if it is in the %s state" %
                statusRes['status'])

        return [{"result": retmsg}]
示例#7
0
    def kill(self, workflow, force, userdn, userproxy=None):
        """Request to Abort a workflow.

           :arg str workflow: a workflow name"""

        self.logger.info("About to kill workflow: %s. Getting status first." %
                         workflow)
        statusRes = self.status(workflow, userdn, userproxy)[0]

        if statusRes['status'] == 'SUBMITTED':
            killList = [
                jobid for jobstatus, jobid in statusRes['jobList']
                if jobstatus not in self.successList
            ]
            self.logger.info("Jobs to kill: %s" % killList)

            self.api.modify(SetStatusTask.sql,
                            status=["KILL"],
                            taskname=[workflow])
            self.api.modify(SetArgumentsTask.sql, taskname = [workflow],\
                            arguments = [str({"killList": killList, "killAll": True})])
        elif statusRes['status'] == 'NEW':
            self.api.modify(SetStatusTask.sql,
                            status=["KILLED"],
                            taskname=[workflow])
        else:
            raise ExecutionError(
                "You cannot kill a task if it is in the %s state" %
                statusRes['status'])
示例#8
0
    def resubmit(self, workflow, siteblacklist, sitewhitelist, userdn,
                 userproxy):
        """Request to reprocess what the workflow hasn't finished to reprocess.
           This needs to create a new workflow in the same campaign

           :arg str workflow: a valid workflow name
           :arg str list siteblacklist: black list of sites, with CMS name;
           :arg str list sitewhitelist: white list of sites, with CMS name."""

        self.logger.info(
            "About to resubmit workflow: %s. Getting status first." % workflow)
        statusRes = self.status(workflow, userdn, userproxy)[0]

        if statusRes['status'] in ['SUBMITTED', 'KILLED']:
            resubmitList = [
                jobid for jobstatus, jobid in statusRes['jobList']
                if jobstatus in self.failedList
            ]
            self.logger.info("Jobs to resubmit: %s" % resubmitList)
            self.api.modify(SetStatusTask.sql,
                            status=["RESUBMIT"],
                            taskname=[workflow])
            self.api.modify(SetArgumentsTask.sql, taskname = [workflow],\
                            arguments = [str({"siteBlackList":siteblacklist, "siteWhiteList":sitewhitelist, "resubmitList":resubmitList})])
        else:
            raise ExecutionError(
                "You cannot resubmit a task if it is in the %s state" %
                statusRes['status'])
示例#9
0
文件: Format.py 项目: brij01/WMCore
    def stream_chunked(self, stream, etag, preamble, trailer):
        """Generator for actually producing the output."""
        comma = " "

        try:
            if preamble:
                etag.update(preamble)
                yield preamble

            try:
                for obj in stream:
                    chunk = comma + json.dumps(obj, indent=2)
                    etag.update(chunk)
                    yield chunk
                    comma = ","
            except GeneratorExit:
                etag.invalidate()
                trailer = None
                raise
            finally:
                if trailer:
                    etag.update(trailer)
                    yield trailer

            cherrypy.response.headers["X-REST-Status"] = 100
        except RESTError as e:
            etag.invalidate()
            report_rest_error(e, format_exc(), False)
        except Exception as e:
            etag.invalidate()
            report_rest_error(ExecutionError(), format_exc(), False)
示例#10
0
文件: Format.py 项目: vytjan/WMCore
 def format_obj(obj):
     """Render an object `obj` into HTML."""
     if isinstance(obj, type(None)):
         result = ""
     elif isinstance(obj, (unicode, str)):
         obj = xml.sax.saxutils.quoteattr(obj)
         result = "<pre>%s</pre>" % obj if '\n' in obj else obj
     elif isinstance(obj, (int, float, bool)):
         result = "%s" % obj
     elif isinstance(obj, dict):
         result = "<ul>"
         for k, v in obj.iteritems():
             result += "<li><b>%s</b>: %s</li>" % (
                 k, PrettyJSONHTMLFormat.format_obj(v))
         result += "</ul>"
     elif is_iterable(obj):
         empty = True
         result = "<details open><ul>"
         for v in obj:
             empty = False
             result += "<li>%s</li>" % PrettyJSONHTMLFormat.format_obj(v)
         result += "</ul></details>"
         if empty:
             result = ""
     else:
         cherrypy.log("cannot represent object of type %s in xml (%s)" %
                      (type(obj).__class__.__name__, repr(obj)))
         raise ExecutionError("cannot represent object in xml")
     return result
示例#11
0
文件: Format.py 项目: brij01/WMCore
 def format_obj(obj):
     """Render an object `obj` into XML."""
     if isinstance(obj, type(None)):
         result = ""
     elif isinstance(obj, (unicode, str)):
         result = xml.sax.saxutils.escape(obj).encode("utf-8")
     elif isinstance(obj, (int, float, bool)):
         result = xml.sax.saxutils.escape(str(obj)).encode("utf-8")
     elif isinstance(obj, dict):
         result = "<dict>"
         for k, v in obj.iteritems():
             result += "<key>%s</key><value>%s</value>" % \
               (xml.sax.saxutils.escape(k).encode("utf-8"),
                XMLFormat.format_obj(v))
         result += "</dict>"
     elif is_iterable(obj):
         result = "<array>"
         for v in obj:
             result += "<i>%s</i>" % XMLFormat.format_obj(v)
         result += "</array>"
     else:
         cherrypy.log("cannot represent object of type %s in xml (%s)" %
                      (type(obj).__class__.__name__, repr(obj)))
         raise ExecutionError("cannot represent object in xml")
     return result
示例#12
0
    def __init__(self, app, config, mount):
        DatabaseRESTApi.__init__(self, app, config, mount)

        self.formats = [ ('application/json', JSONFormat()) ]

        status, serverdn = getstatusoutput('openssl x509 -noout -subject -in %s | cut -f2- -d\ ' % config.serverhostcert)
        if status is not 0:
            raise ExecutionError("Internal issue when retrieving crabserver service DN.")

        extconfig = Utils.ConfigCache(centralconfig=Utils.getCentralConfig(extconfigurl=config.extconfigurl, mode=config.mode),
                                      cachetime=mktime(gmtime()))

        #Global initialization of Data objects. Parameters coming from the config should go here
        DataUserWorkflow.globalinit(config)
        DataWorkflow.globalinit(dbapi=self, phedexargs={'endpoint': config.phedexurl},\
                                credpath=config.credpath, centralcfg=extconfig, config=config)
        DataFileMetadata.globalinit(dbapi=self, config=config)
        RESTTask.globalinit(centralcfg=extconfig)
        Utils.globalinit(config.serverhostkey, config.serverhostcert, serverdn, config.credpath)

        ## TODO need a check to verify the format depending on the resource
        ##      the RESTFileMetadata has the specifc requirement of getting xml reports
        self._add( {'workflow': RESTUserWorkflow(app, self, config, mount, extconfig),
                    'info': RESTServerInfo(app, self, config, mount, serverdn, extconfig),
                    'filemetadata': RESTFileMetadata(app, self, config, mount),
                    'workflowdb': RESTWorkerWorkflow(app, self, config, mount),
                    'task': RESTTask(app, self, config, mount),
                    'filetransfers': RESTFileTransfers(app, self, config, mount),
                    'fileusertransfers': RESTFileUserTransfers(app, self, config, mount),
                   })

        self._initLogger( getattr(config, 'loggingFile', None), getattr(config, 'loggingLevel', None) )
示例#13
0
    def deletewarnings(self, **kwargs):
        """ Deleet warnings from the warning column in the database. Can be tested with:
            curl -X POST https://mmascher-poc.cern.ch/crabserver/dev/task -k --key /tmp/x509up_u8440 --cert /tmp/x509up_u8440 \
                    -d 'subresource=deletewarnings&workflow=140710_233424_crab3test-5:mmascher_crab_HCprivate12' -v
        """
        #check if the parameter is there
        if 'workflow' not in kwargs or not kwargs['workflow']:
            raise InvalidParameter("Task name not found in the input parameters")

        #decoding and setting the parameters
        workflow = kwargs['workflow']
        authz_owner_match(self.api, [workflow], self.Task) #check that I am modifying my own workflow

#        rows = self.api.query(None, None, "SELECT tm_task_warnings FROM tasks WHERE tm_taskname = :workflow", workflow=workflow)#self.Task.TASKSUMMARY_sql)
        rows = self.api.query(None, None, self.Task.ID_sql, taskname=workflow)#self.Task.TASKSUMMARY_sql)
        rows = list(rows) #from generator to list
        if len(rows)==0:
            raise InvalidParameter("Task %s not found in the task database" % workflow)

        row = self.Task.ID_tuple(*rows[0])
        warnings = literal_eval(row.task_warnings.read() if row.task_warnings else '[]')
        if len(warnings)<1:
            raise ExecutionError("No warnings to remove.")

        self.api.modify(self.Task.DeleteWarnings_sql, workflow=[workflow])

        return []
示例#14
0
文件: Format.py 项目: brij01/WMCore
    def stream_chunked(self, stream, etag, preamble, trailer):
        """Generator for actually producing the output."""
        comma = " "

        try:
            if preamble:
                etag.update(preamble)
                yield preamble

            try:
                for obj in stream:
                    chunk = comma + json.dumps(obj) + "\n"
                    etag.update(chunk)
                    yield chunk
                    comma = ","
            except GeneratorExit:
                etag.invalidate()
                trailer = None
                raise
            except Exception as exp:
                print("ERROR, json.dumps failed to serialize %s, type %s\nException: %s" \
                        % (obj, type(obj), str(exp)))
                raise
            finally:
                if trailer:
                    etag.update(trailer)
                    yield trailer

            cherrypy.response.headers["X-REST-Status"] = 100
        except RESTError as e:
            etag.invalidate()
            report_rest_error(e, format_exc(), False)
        except Exception as e:
            etag.invalidate()
            report_rest_error(ExecutionError(), format_exc(), False)
示例#15
0
    def retrieveConfig(externalLink):

        hbuf = StringIO.StringIO()
        bbuf = StringIO.StringIO()

        curl = pycurl.Curl()
        curl.setopt(pycurl.URL, externalLink)
        curl.setopt(pycurl.WRITEFUNCTION, bbuf.write)
        curl.setopt(pycurl.HEADERFUNCTION, hbuf.write)
        curl.setopt(pycurl.FOLLOWLOCATION, 1)
        curl.perform()
        curl.close()

        header = ResponseHeader(hbuf.getvalue())
        if (header.status < 200 or header.status >= 300):
            msg = "Reading %s returned %s." % (externalLink, header.status)
            if centralCfgFallback:
                msg += "\nUsing cached values for external configuration."
                cherrypy.log(msg)
                return centralCfgFallback
            else:
                cherrypy.log(msg)
                raise ExecutionError(
                    "Internal issue when retrieving external configuration from %s"
                    % externalLink)
        jsonConfig = bbuf.getvalue()

        return jsonConfig
示例#16
0
def getCentralConfig(extconfigurl, mode):
    """Utility to retrieve the central configuration to be used for dynamic variables

    arg str extconfigurl: the url pointing to the exteranl configuration parameter
    arg str mode: also known as the variant of the rest (prod, preprod, dev, private)
    return: the dictionary containing the external configuration for the selected mode."""

    hbuf = StringIO.StringIO()
    bbuf = StringIO.StringIO()

    curl = pycurl.Curl()
    curl.setopt(pycurl.URL, extconfigurl)
    curl.setopt(pycurl.WRITEFUNCTION, bbuf.write)
    curl.setopt(pycurl.HEADERFUNCTION, hbuf.write)
    curl.setopt(pycurl.FOLLOWLOCATION, 1)
    curl.perform()
    curl.close()

    header = ResponseHeader(hbuf.getvalue())
    if header.status < 200 or header.status >= 300:
        cherrypy.log("Problem %d reading from %s." %
                     (extconfigurl, header.status))
        raise ExecutionError(
            "Internal issue when retrieving external confifuration")
    return json.decode(bbuf.getvalue())[mode]
示例#17
0
class RawFormat(RESTFormat):
    """Format an iterable of objects as raw data.

    Generates raw data completely unmodified, for example image data or
    streaming arbitrary external data files including even plain text.
    Computes an ETag on the output in the process. The result is always
    chunked, even simple strings on input. Usually small enough responses
    will automatically be converted back to a single string response post
    compression and ETag processing.

    Any exceptions raised by input stream are reported to `report_rest_error`
    and swallowed, as this is normally used to generate output for CherryPy
    responses, which cannot handle exceptions reasonably after the output
    generation begins; later processing may reconvert those back to exceptions
    however (cf. stream_maybe_etag()). A X-REST-Status trailer header is added
    if (and only if) an exception occurs; the client must inspect that to find
    out if it got the complete output. There is normally 'X-REST-Status: 100'
    in normal response headers, and it remains valid in case of success.
    No ETag header is generated in case of an exception."""
    def stream_chunked(self, stream, etag):
        """Generator for actually producing the output."""
        try:
            for chunk in stream:
                etag.update(chunk)
                yield chunk

        except RESTError, e:
            etag.invalidate()
            report_rest_error(e, format_exc(), False)
        except Exception, e:
            etag.invalidate()
            report_rest_error(ExecutionError(), format_exc(), False)
示例#18
0
    def addwarning(self, **kwargs):
        """ Add a warning to the wraning column in the database. Can be tested with:
            curl -X POST https://mmascher-poc.cern.ch/crabserver/dev/task -k --key /tmp/x509up_u8440 --cert /tmp/x509up_u8440 \
                    -d 'subresource=addwarning&workflow=140710_233424_crab3test-5:mmascher_crab_HCprivate12&warning=blahblah' -v
        """
        #check if the parameters are there
        if 'warning' not in kwargs or not kwargs['warning']:
            raise InvalidParameter("Warning message not found in the input parameters")
        if 'workflow' not in kwargs or not kwargs['workflow']:
            raise InvalidParameter("Task name not found in the input parameters")

        #decoding and setting the parameters
        workflow = kwargs['workflow']
        authz_owner_match(self.api, [workflow], self.Task) #check that I am modifying my own workflow
        try:
            warning = b64decode(kwargs['warning'])
        except TypeError:
            raise InvalidParameter("Failure message is not in the accepted format")

#        rows = self.api.query(None, None, "SELECT tm_task_warnings FROM tasks WHERE tm_taskname = :workflow", workflow=workflow)#self.Task.TASKSUMMARY_sql)
        rows = self.api.query(None, None, self.Task.ID_sql, taskname=workflow)#self.Task.TASKSUMMARY_sql)
        rows = list(rows) #from generator to list
        if len(rows)==0:
            raise InvalidParameter("Task %s not found in the task database" % workflow)

        row = self.Task.ID_tuple(*rows[0])
        warnings = literal_eval(row.task_warnings.read() if row.task_warnings else '[]')
        if len(warnings)>10:
            raise ExecutionError("You cannot add more than 10 warnings to a task")
        warnings.append(warning)

        self.api.modify(self.Task.SetWarnings_sql, warnings=[str(warnings)], workflow=[workflow])

        return []
示例#19
0
文件: Format.py 项目: brij01/WMCore
    def stream_chunked(self, stream, etag, preamble, trailer):
        """Generator for actually producing the output."""
        try:
            etag.update(preamble)
            yield preamble

            try:
                for obj in stream:
                    chunk = XMLFormat.format_obj(obj)
                    etag.update(chunk)
                    yield chunk
            except GeneratorExit:
                etag.invalidate()
                trailer = None
                raise
            finally:
                if trailer:
                    etag.update(trailer)
                    yield trailer

        except RESTError as e:
            etag.invalidate()
            report_rest_error(e, format_exc(), False)
        except Exception as e:
            etag.invalidate()
            report_rest_error(ExecutionError(), format_exc(), False)
示例#20
0
    def search(self, **kwargs):
        """Retrieves all the columns of a task in the task table (select * from task ...)
           The API is (only?) used in the monitor for operator.
           curl -X GET 'https://mmascher-dev6.cern.ch/crabserver/dev/task?subresource=search&workflow=150224_230633:mmascher_crab_testecmmascher-dev6_3' \
                        -k --key /tmp/x509up_u8440 --cert /tmp/x509up_u8440 -v"""

        if 'workflow' not in kwargs or not kwargs['workflow']:
            raise InvalidParameter(
                "Task name not found in the input parameters")

        try:
            row = next(
                self.api.query(None,
                               None,
                               self.Task.QuickSearch_sql,
                               taskname=kwargs["workflow"]))
        except StopIteration:
            raise ExecutionError(
                "Impossible to find task %s in the database." %
                kwargs["workflow"])

        def getval(col):
            """ Some columns in oracle can be CLOB and we need to call read on them.
            """
            #TODO move the function in ServerUtils and use it when required (e.g.: mysql LONGTEXT does not need read())
            try:
                return str(col)
            except:
                return col.read()

        return [getval(col) for col in row]
示例#21
0
 def getpublishurl(self, **kwargs):
     if 'workflow' not in kwargs or not kwargs['workflow']:
         raise InvalidParameter("Task name not found in the input parameters")
     try:
         row = next(self.api.query(None, None, self.Task.GetPublishUrl_sql, taskname=kwargs['workflow']))
     except StopIteration:
         raise ExecutionError("Impossible to find task %s in the database." % kwargs['workflow'])
     yield row
 def myPerform(cls, curl, url):
     try:
         curl.perform()
     except pycurl.error as e:
         raise ExecutionError(("Failed to contact Grid scheduler when getting URL %s. "
                               "This might be a temporary error, please retry later and "
                               "contact %s if the error persist. Error from curl: %s"
                               % (url, FEEDBACKMAIL, str(e))))
示例#23
0
 def __enter__(self):
     ctr = self.throttle._incUser(self.user)
     #self.throttle.logger.debug("Entering throttled function with counter %d for user %s" % (ctr, self.user))
     if ctr >= self.throttle.getLimit():
         self.throttle._decUser(self.user)
         raise ExecutionError(
             "The current number of active operations for this resource exceeds the limit of %d for user %s"
             % (self.throttle.getLimit(), self.user))
示例#24
0
 def webdir(self, **kwargs):
     if 'workflow' not in kwargs or not kwargs['workflow']:
         raise InvalidParameter("Task name not found in the input parameters")
     workflow = kwargs['workflow']
     try:
         row = self.Task.ID_tuple(*next(self.api.query(None, None, self.Task.ID_sql, taskname=workflow)))
     except StopIteration:
         raise ExecutionError("Impossible to find task %s in the database." % kwargs["workflow"])
     yield row.user_webdir
示例#25
0
 def scheddaddress(self, **kwargs):
     backendurl=self.centralcfg.centralconfig['backend-urls']
     workflow = kwargs['workflow']
     try:
         loc = HTCondorLocator.HTCondorLocator(backendurl)
         schedd, address = loc.getScheddObj(workflow) 
     except Exception as ex:
         self.logger.exception(ex)
         raise ExecutionError("Unable to get schedd address for task %s" % (workflow))(ex)
     yield loc.scheddAd['Machine']
示例#26
0
 def publicationStatus(self, workflow, asourl):
     publication_info = {}
     if not asourl:
         raise ExecutionError("This CRAB server is not configured to publish; no publication status is available.")
     server = CMSCouch.CouchServer(dburl=asourl, ckey=self.serverKey, cert=self.serverCert)
     try:
         db = server.connectDatabase('asynctransfer')
     except Exception, ex:
         msg =  "Error while connecting to asynctransfer CouchDB"
         self.logger.exception(msg)
         publication_info = {'error' : msg}
         return  publication_info
    def publicationStatusCouch(self, workflow, asourl, asodb):
        publicationInfo = {'status': {}, 'failure_reasons': {}}
        if not asourl:
            raise ExecutionError("This CRAB server is not configured to publish; no publication status is available.")
        server = CMSCouch.CouchServer(dburl=asourl, ckey=self.serverKey, cert=self.serverCert)
        try:
            db = server.connectDatabase(asodb)
        except Exception:
            msg = "Error while connecting to asynctransfer CouchDB for workflow %s " % workflow
            msg += "\n asourl=%s asodb=%s" % (asourl, asodb)
            self.logger.exception(msg)
            publicationInfo['status'] = {'error': msg}
            return publicationInfo
        # Get the publication status for the given workflow. The next query to the
        # CouchDB view returns a list of 1 dictionary (row) with:
        # 'key'   : workflow,
        # 'value' : a dictionary with possible publication statuses as keys and the
        #           counts as values.
        query = {'reduce': True, 'key': workflow, 'stale': 'update_after'}
        try:
            publicationList = db.loadView('AsyncTransfer', 'PublicationStateByWorkflow', query)['rows']
        except Exception:
            msg = "Error while querying CouchDB for publication status information for workflow %s " % workflow
            self.logger.exception(msg)
            publicationInfo['status'] = {'error': msg}
            return publicationInfo
        if publicationList:
            publicationStatusDict = publicationList[0]['value']
            publicationInfo['status'] = publicationStatusDict
            # Get the publication failure reasons for the given workflow. The next query to
            # the CouchDB view returns a list of N_different_publication_failures
            # dictionaries (rows) with:
            # 'key'   : [workflow, publication failure],
            # 'value' : count.
            numFailedPublications = publicationStatusDict['publication_failed']
            if numFailedPublications:
                query = {'group': True, 'startkey': [workflow], 'endkey': [workflow, {}], 'stale': 'update_after'}
                try:
                    publicationFailedList = db.loadView('DBSPublisher', 'PublicationFailedByWorkflow', query)['rows']
                except Exception:
                    msg = "Error while querying CouchDB for publication failures information for workflow %s " % workflow
                    self.logger.exception(msg)
                    publicationInfo['failure_reasons']['error'] = msg
                    return publicationInfo
                publicationInfo['failure_reasons']['result'] = []
                for publicationFailed in publicationFailedList:
                    failureReason = publicationFailed['key'][1]
                    numFailedFiles = publicationFailed['value']
                    publicationInfo['failure_reasons']['result'].append((failureReason, numFailedFiles))

        return publicationInfo
示例#28
0
    def get(self, workflow, subresource, username, limit, shortformat,
            exitcode, jobids, verbose, timestamp):
        """Retrieves the workflow information, like a status summary, in case the workflow unique name is specified.
           Otherwise returns all workflows since (now - age) for which the user is the owner.
           The caller needs to be a valid CMS user.
           :arg str workflow: unique name identifier of workflow;
		   :arg str timestamp: max workflow age in hours;
           :arg str subresource: the specific workflow information to be accessed;
           :arg int limit: limit of return entries for some specific subresource;
           :arg int exitcode: exitcode for which the specific subresource is needed (eg log file of a job with that exitcode)
           :retrun: workflow with the relative status summary in case of per user request; or
                    the requested subresource."""
        result = []
        if workflow:
            userdn = cherrypy.request.headers['Cms-Authn-Dn']
            # if have the wf then retrieve the wf status summary
            if not subresource:
                result = self.userworkflowmgr.status(workflow,
                                                     verbose=verbose,
                                                     userdn=userdn)
            # if have a subresource then it should be one of these
            elif subresource == 'logs':
                result = self.userworkflowmgr.logs(workflow,
                                                   limit,
                                                   exitcode,
                                                   jobids,
                                                   userdn=userdn)
            elif subresource == 'data':
                result = self.userworkflowmgr.output(workflow,
                                                     limit,
                                                     jobids,
                                                     userdn=userdn)
            elif subresource == 'errors':
                result = self.userworkflowmgr.errors(workflow, shortformat)
            elif subresource == 'report':
                result = self.userworkflowmgr.report(workflow,
                                                     userdn=userdn,
                                                     usedbs=shortformat)
            # if here means that no valid subresource has been requested
            # flow should never pass through here since validation restrict this
            else:
                raise ExecutionError("Validation or method error")
        else:
            # retrieve the information about latest worfklows for that user
            # age can have a default: 1 week ?
            cherrypy.log("Found user '%s'" % cherrypy.request.user['login'])
            result = self.userworkflowmgr.getLatests(
                username or cherrypy.request.user['login'],
                timestamp)  #eric added timestamp to match username

        return result
示例#29
0
    def proceed(self, workflow):
        """Continue a task which was initialized with 'crab submit --dryrun'.

           :arg str workflow: a workflow name
        """
        row = self.Task.ID_tuple(*next(self.api.query(None, None, self.Task.ID_sql, taskname=workflow)))
        if row.task_status != 'UPLOADED':
            msg = 'Can only proceed if task is in the UPLOADED status, but it is in the %s status.' % row.task_status
            raise ExecutionError(msg)
        else:
            self.api.modify(self.Task.SetDryRun_sql, taskname=[workflow], dry_run=['F'])
            self.api.modify(self.Task.SetStatusTask_sql, taskname=[workflow], status=['NEW'], command=['SUBMIT'])

        return [{'result': 'ok'}]
示例#30
0
文件: Format.py 项目: brij01/WMCore
    def stream_chunked(self, stream, etag):
        """Generator for actually producing the output."""
        try:
            for chunk in stream:
                etag.update(chunk)
                yield chunk

        except RESTError as e:
            etag.invalidate()
            report_rest_error(e, format_exc(), False)
        except Exception as e:
            etag.invalidate()
            report_rest_error(ExecutionError(), format_exc(), False)
        except BaseException:
            etag.invalidate()
            raise