<jupyter_start><jupyter_text>Advanced Schedd InteractionLaunch this tutorial in a Jupyter Notebook on Binder: [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/htcondor/htcondor-python-bindings-tutorials/master?urlpath=lab/tree/Advanced-Schedd-Interactions.ipynb)The introductory tutorial only scratches the surface of what the Python bindingscan do with the ``condor_schedd``; this module focuses on covering a wider rangeof functionality:*  Job and history querying.*  Advanced job submission.*  Python-based negotiation with the Schedd.As usual, we start by importing the relevant modules:<jupyter_code>import htcondor
import classad<jupyter_output><empty_output><jupyter_text>Job and History QueryingIn [HTCondor Introduction](HTCondor-Introduction.ipynb), we covered the `Schedd.xquery` methodand its two most important keywords:*  ``requirements``: Filters the jobs the schedd should return.*  ``projection``: Filters the attributes returned for each job.For those familiar with SQL queries, ``requirements`` performs the equivalentas the ``WHERE`` clause while ``projection`` performs the equivalent of the columnlisting in ``SELECT``.There are two other keywords worth mentioning:*  ``limit``: Limits the number of returned ads; equivalent to SQL's ``LIMIT``.*  ``opts``: Additional flags to send to the schedd to alter query behavior.   The only flag currently defined is `QueryOpts.AutoCluster`; this   groups the returned results by the current set of "auto-cluster" attributes   used by the pool.  It's analogous to ``GROUP BY`` in SQL, except the columns   used for grouping are controlled by the schedd.To illustrate these additional keywords, let's first submit a few jobs:<jupyter_code>schedd = htcondor.Schedd()
sub = htcondor.Submit({
    "executable": "/bin/sleep",
    "arguments": "5m",
    "hold": "True",
})
with schedd.transaction() as txn:
    cluster_id = sub.queue(txn, 10)
print(cluster_id)<jupyter_output>13<jupyter_text>**Note:** In this example, we used the ``hold`` submit command to indicate thatthe jobs should start out in the ``condor_schedd`` in the *Hold* state; thisis used simply to prevent the jobs from running to completion while you arerunning the tutorial.We now have 10 jobs running under ``cluster_id``; they should all be identical:<jupyter_code>print(len(schedd.query(projection=["ProcID"], constraint=f"ClusterId=={cluster_id}")))<jupyter_output>10<jupyter_text>The ``sum(1 for _ in ...)`` syntax is a simple way to count the number of itemsproduced by an iterator without buffering all the objects in memory.  Querying many ScheddsOn larger pools, it's common to write Python scripts that interact with not one but many schedds.  For example,if you want to implement a "global query" (equivalent to ``condor_q -g``; concatenates all jobs in all schedds),it might be tempting to write code like this:<jupyter_code>jobs = []
for schedd_ad in htcondor.Collector().locateAll(htcondor.DaemonTypes.Schedd):
    schedd = htcondor.Schedd(schedd_ad)
    jobs += schedd.xquery()
print(len(jobs))<jupyter_output>10<jupyter_text>This is sub-optimal for two reasons:*  ``xquery`` is not given any projection, meaning it will pull all attributes for all jobs -   much more data than is needed for simply counting jobs.*  The querying across all schedds is serialized: we may wait for painfully long on one or two   "bad apples."We can instead begin the query for all schedds simultaneously, then read the responses asthey are sent back.  First, we start all the queries without reading responses:<jupyter_code>queries = []
coll_query = htcondor.Collector().locateAll(htcondor.DaemonTypes.Schedd)
for schedd_ad in coll_query:
    schedd_obj = htcondor.Schedd(schedd_ad)
    queries.append(schedd_obj.xquery())<jupyter_output><empty_output><jupyter_text>The iterators will yield the matching jobs; to return the autoclusters instead of jobs, usethe ``AutoCluster`` option (``schedd_obj.xquery(opts=htcondor.QueryOpts.AutoCluster)``).  Oneauto-cluster ad is returned for each set of jobs that have identical values for all significantattributes.  A sample auto-cluster looks like:       [        RequestDisk = DiskUsage;        Rank = 0.0;        FileSystemDomain = "hcc-briantest7.unl.edu";        MemoryUsage = ( ( ResidentSetSize + 1023 ) / 1024 );        ImageSize = 1000;        JobUniverse = 5;        DiskUsage = 1000;        JobCount = 1;        Requirements = ( TARGET.Arch == "X86_64" ) && ( TARGET.OpSys == "LINUX" ) && ( TARGET.Disk >= RequestDisk ) && ( TARGET.Memory >= RequestMemory ) && ( ( TARGET.HasFileTransfer ) || ( TARGET.FileSystemDomain == MY.FileSystemDomain ) );        RequestMemory = ifthenelse(MemoryUsage isnt undefined,MemoryUsage,( ImageSize + 1023 ) / 1024);        ResidentSetSize = 0;        ServerTime = 1483758177;        AutoClusterId = 2       ]We use the `poll` function, which will return when a query has available results:<jupyter_code>job_counts = {}
for query in htcondor.poll(queries):
    schedd_name = query.tag()
    job_counts.setdefault(schedd_name, 0)
    count = len(query.nextAdsNonBlocking())
    job_counts[schedd_name] += count
    print("Got {} results from {}.".format(count, schedd_name))
print(job_counts)<jupyter_output>Got 10 results from jovyan@70b2e14c06bc.
{'jovyan@70b2e14c06bc': 10}<jupyter_text>The `QueryIterator.tag` method is used to identify which query is returned; thetag defaults to the Schedd's name but can be manually set through the ``tag`` keyword argumentto `Schedd.xquery`.  History QueriesAfter a job has finished in the Schedd, it moves from the queue to the history file.  Thehistory can be queried (locally or remotely) with the `Schedd.history` method:<jupyter_code>schedd = htcondor.Schedd()
for ad in schedd.history(
    constraint='true',
    projection=['ProcId', 'ClusterId', 'JobStatus'],
    match=2,  # limit to 2 returned results
):
    print(ad)<jupyter_output>[
        JobStatus = 3; 
        ProcId = 9; 
        ClusterId = 10
    ]

    [
        JobStatus = 3; 
        ProcId = 8; 
        ClusterId = 10
    ]
{'fraction_non_alphanumeric': 0.09695473251028806, 'fraction_numerical': 0.014320987654320988, 'mean_word_length': 4.646840148698884, 'pattern_counts': {'":': 3, '<': 24, '<?xml version=': 0, '>': 26, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '7', 'dir': 'jupyter-structured-clean-dedup', 'id': '229455', 'license': 'Apache-2.0', 'n_tokens_mistral': 1900, 'n_tokens_neox': 1777, 'n_words': 627, 'path': 'docs/apis/python-bindings/tutorials/Advanced-Schedd-Interactions.ipynb', 'repo_name': 'shamoya/htcondor', 'size': '6075'}