{ "cells": [ { "cell_type": "markdown", "metadata": { "pycharm": {} }, "source": [ "# Interacting With Daemons" ] }, { "cell_type": "markdown", "metadata": { "pycharm": {} }, "source": [ "Launch this tutorial in a Jupyter Notebook on Binder: \n", "[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/htcondor/htcondor-python-bindings-tutorials/master?urlpath=lab/tree/Interacting-With-Daemons.ipynb)\n", "\n", "In this module, we'll look at how the HTCondor Python bindings can be used to\n", "interact with running daemons.\n", "\n", "As usual, we start by importing the relevant modules:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "pycharm": {} }, "outputs": [], "source": [ "import htcondor" ] }, { "cell_type": "markdown", "metadata": { "pycharm": {} }, "source": [ "## Configuration\n", "\n", "The HTCondor configuration is exposed to Python in two ways:\n", "\n", "* The local process's configuration is available in the module-level `param` object.\n", "* A remote daemon's configuration may be queried using a `RemoteParam`\n", "\n", "The `param` object emulates a Python dictionary:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "pycharm": {} }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "/home/jovyan/.condor/local/log/SchedLog\n", "None\n" ] } ], "source": [ "print(htcondor.param[\"SCHEDD_LOG\"]) # prints the schedd's current log file\n", "print(htcondor.param.get(\"TOOL_LOG\")) # print None, since TOOL_LOG isn't set by default" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "pycharm": {} }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "/tmp/log\n" ] } ], "source": [ "htcondor.param[\"TOOL_LOG\"] = \"/tmp/log\" # sets TOOL_LOG to /tmp/log\n", "print(htcondor.param[\"TOOL_LOG\"]) # prints /tmp/log, as set above" ] }, { "cell_type": "markdown", "metadata": { "pycharm": {} }, "source": [ "Note that assignments to `param` will persist only in memory; if we use `reload_config` to re-read the configuration files from disk, our change to `TOOL_LOG` disappears:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "pycharm": {} }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "/tmp/log\n", "None\n" ] } ], "source": [ "print(htcondor.param.get(\"TOOL_LOG\"))\n", "htcondor.reload_config()\n", "print(htcondor.param.get(\"TOOL_LOG\"))" ] }, { "cell_type": "markdown", "metadata": { "pycharm": {} }, "source": [ "In HTCondor, a configuration *prefix* may indicate that a setting is specific to that daemon. By default, the Python binding's prefix is ``TOOL``. If you would like to use the configuration of a different daemon, utilize the ``set_subsystem`` function:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "pycharm": {} }, "outputs": [], "source": [ "htcondor.param[\"TEST_FOO\"] = \"foo\" # sets the default value of TEST_FOO to foo\n", "htcondor.param[\"SCHEDD.TEST_FOO\"] = \"bar\" # the schedd has a special setting for TEST_FOO" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "pycharm": {} }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "foo\n" ] } ], "source": [ "print(htcondor.param['TEST_FOO']) # default access; should be 'foo'" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "pycharm": {} }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "bar\n" ] } ], "source": [ "htcondor.set_subsystem('SCHEDD') # changes the running process to identify as a schedd.\n", "print(htcondor.param['TEST_FOO']) # since we now identify as a schedd, should use the special setting of 'bar'" ] }, { "cell_type": "markdown", "metadata": { "pycharm": {} }, "source": [ "Between ``param``, ``reload_config``, and ``set_subsystem``, we can explore the configuration of the local host." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Remote Configuration" ] }, { "cell_type": "markdown", "metadata": { "pycharm": {} }, "source": [ "What happens if we want to test the configuration of a remote daemon? For that, we can use the `RemoteParam` class.\n", "\n", "The object is first initialized from the output of the `Collector.locate` method:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "pycharm": {} }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "<172.17.0.2:9618?addrs=172.17.0.2-9618&alias=fa6c829ace67&noUDP&sock=master_16_de02>\n" ] } ], "source": [ "master_ad = htcondor.Collector().locate(htcondor.DaemonTypes.Master)\n", "print(master_ad['MyAddress'])\n", "master_param = htcondor.RemoteParam(master_ad)" ] }, { "cell_type": "markdown", "metadata": { "pycharm": {} }, "source": [ "Once we have the ``master_param`` object, we can treat it like a local dictionary to access the remote daemon's configuration.\n", "\n", "**NOTE** that the `htcondor.param` object attempts to infer type information for configuration values from the compile-time metadata while the `RemoteParam` object does not:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "pycharm": {} }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "'5'\n", "5\n" ] } ], "source": [ "print(repr(master_param['UPDATE_INTERVAL'])) # returns a string\n", "print(repr(htcondor.param['UPDATE_INTERVAL'])) # returns an integer" ] }, { "cell_type": "markdown", "metadata": { "pycharm": {} }, "source": [ "In fact, we can even *set* the daemon's configuration using the `RemoteParam` object... if we have permission. By default, this is disabled for security reasons:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "pycharm": {} }, "outputs": [ { "ename": "HTCondorReplyError", "evalue": "Failed to set remote daemon parameter.", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mHTCondorReplyError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m/tmp/ipykernel_49/743935840.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mmaster_param\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'UPDATE_INTERVAL'\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'500'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;32m/opt/conda/lib/python3.9/site-packages/htcondor/_lock.py\u001b[0m in \u001b[0;36mwrapper\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 68\u001b[0m \u001b[0macquired\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mLOCK\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0macquire\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 69\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 70\u001b[0;31m \u001b[0mrv\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 71\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 72\u001b[0m \u001b[0;31m# if the function returned a context manager,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mHTCondorReplyError\u001b[0m: Failed to set remote daemon parameter." ] } ], "source": [ "master_param['UPDATE_INTERVAL'] = '500'" ] }, { "cell_type": "markdown", "metadata": { "pycharm": {} }, "source": [ "## Logging Subsystem\n", "\n", "The logging subsystem is available to the Python bindings; this is often useful for debugging network connection issues between the client and server.\n", "\n", "**NOTE** Jupyter notebooks discard output from library code; hence, you will not see the results of ``enable_debug`` below." ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "pycharm": {} }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n" ] } ], "source": [ "htcondor.set_subsystem(\"TOOL\")\n", "htcondor.param['TOOL_DEBUG'] = 'D_FULLDEBUG'\n", "htcondor.param['TOOL_LOG'] = '/tmp/log'\n", "htcondor.enable_log() # Send logs to the log file (/tmp/foo)\n", "htcondor.enable_debug() # Send logs to stderr; this is ignored by the web notebook.\n", "print(open(\"/tmp/log\").read()) # Print the log's contents." ] }, { "cell_type": "markdown", "metadata": { "pycharm": {} }, "source": [ "## Sending Daemon Commands\n", "\n", "An administrator can send administrative commands directly to the remote daemon. This is useful if you'd like a certain daemon restarted, drained, or reconfigured.\n", "\n", "Because we have a personal HTCondor instance, we are the administrator - and we can test this out!\n", "\n", "To send a command, use the top-level ``send_command`` function, provide a daemon location, and provide a specific command from the `DaemonCommands` enumeration. For example, we can *reconfigure*:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "pycharm": {} }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "<172.17.0.2:9618?addrs=172.17.0.2-9618&alias=fa6c829ace67&noUDP&sock=master_16_de02>\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "09/19/23 21:41:27 SharedPortClient: sent connection request to <172.17.0.2:9618> for shared port id master_16_de02\n" ] } ], "source": [ "print(master_ad['MyAddress'])\n", "\n", "htcondor.send_command(master_ad, htcondor.DaemonCommands.Reconfig)" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "pycharm": {} }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['09/19/23 21:41:27 Sent SIGHUP to NEGOTIATOR (pid 20)\\n', '09/19/23 21:41:27 Sent SIGHUP to SCHEDD (pid 21)\\n', '09/19/23 21:41:27 Sent SIGHUP to SHARED_PORT (pid 18)\\n', '09/19/23 21:41:27 Sent SIGHUP to STARTD (pid 24)\\n']\n" ] } ], "source": [ "import time\n", "\n", "time.sleep(1)\n", "\n", "log_lines = open(htcondor.param['MASTER_LOG']).readlines()\n", "print(log_lines[-4:])" ] }, { "cell_type": "markdown", "metadata": { "pycharm": {} }, "source": [ "We can also instruct the master to shut down a specific daemon:" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "pycharm": {} }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "09/19/23 21:41:28 SharedPortClient: sent connection request to <172.17.0.2:9618> for shared port id master_16_de02\n", "09/19/23 21:41:28 Can't open directory \"/etc/condor/passwords.d\" as PRIV_ROOT, errno: 13 (Permission denied)\n", "09/19/23 21:41:28 Can't open directory \"/etc/condor/passwords.d\" as PRIV_ROOT, errno: 13 (Permission denied)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "09/19/23 21:41:28 The SCHEDD (pid 21) exited with status 0\n", "\n" ] } ], "source": [ "htcondor.send_command(master_ad, htcondor.DaemonCommands.DaemonOff, \"SCHEDD\")\n", "\n", "time.sleep(1)\n", "\n", "log_lines = open(htcondor.param['MASTER_LOG']).readlines()\n", "print(log_lines[-1])" ] }, { "cell_type": "markdown", "metadata": { "pycharm": {} }, "source": [ "Or even turn off the whole HTCondor instance:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "pycharm": {} }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "09/19/23 21:41:29 SharedPortClient: sent connection request to <172.17.0.2:9618> for shared port id master_16_de02\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "09/19/23 21:41:29 **** condor_master (condor_MASTER) pid 16 EXITING WITH STATUS 0\n", "\n" ] } ], "source": [ "htcondor.send_command(master_ad, htcondor.DaemonCommands.OffFast)\n", "\n", "time.sleep(10)\n", "\n", "log_lines = open(htcondor.param['MASTER_LOG']).readlines()\n", "print(log_lines[-1])" ] }, { "cell_type": "markdown", "metadata": { "pycharm": {} }, "source": [ "Let's turn HTCondor back on for future tutorials:" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "pycharm": {} }, "outputs": [], "source": [ "import os\n", "os.system(\"condor_master\")\n", "time.sleep(10) # give condor a few seconds to get started" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.6" } }, "nbformat": 4, "nbformat_minor": 4 }