Offline Calibration Webservice
==============================

The offline calibration webservice interacts with the Metadata Catalogue (MDC),
such that migration of data to the offline cluster automatically triggers
calibration jobs on relevant files.

Installation
------------

Installation should only be performed by trained personal.

**Prerequisites**

The service needs to be installed under a functional user account which

* has read permission to the *raw* data folder on the Maxwell cluster
* has write permission to the *proc* folders for outputting corrected data
* is allowed to launch SLURM jobs on the cluster

The hosting system needs to be accessible via ZMQ calls from the MDC.
This requires appropriate DMZ settings. Additionally, it needs to be able
to interact with the MDC via the MDC client interface

**Installation of Dependencies**

The installation requirements can be found in the *requirements.txt* file.
Additionally, the *xfel-calibrate* environment needs to be installed:

1. clone the *pycalibration* repo into a directory of your choice:

   ``` bash
   git clone https://git.xfel.eu/gitlab/detectors/pycalibration.git .
   ```

2. pick the python environment to install into. On Maxwell the anaconda/3
   environment will work:

   ``` bash
   module load anaconda/3
   ```

3. install the *xfel-calibrate* environment

   ``` bash
   pip install --user -r requirements.txt
   ```

4. some correction notebooks require pyDetLib. It requires manual installation in
   a non-Karabo python environment

   ``` bash
   mkdir pydetlib
   cd pydetlib
   git clone https://git.xfel.eu/gitlab/karaboDevices/pyDetLib.git .
   pip install --user ./lib/requirements.txt
   pip install --user pycuda
   pip install --user ./lib/
   cd ..

5. install the separate requirements for the webservice:

   ``` bash
   cd webservice
   pip install --user -r requirements.txt
   ```

6. install the metadata_client library, according to instructions at

   https://git.xfel.eu/gitlab/ITDM/metadata_client


You are now good to go.

Configuration
-------------

Configuration is done through the *webservice.yaml* file in the webservice directory.
On a new installation you will likely need to change the following parameters.

In the **config-repo** section, the configuration repository needs to be configured:

``` YAML
config-repo:
    url: https://git.xfel.eu/gitlab/detectors/calibration_configurations.git
    local-path: /home/haufs/calibration_config/
```
Here you should prepend the *url* entry with a gitlab access token, that provides access
to the calibration_configurations repository.

In the **web-service** section, the webservice itself is configured:

``` YAML

web-service:
    port: 5555
    bind-to: tcp://*
    allowed-ips: '111.222.222.111', ''111.222.222.112'
    job-db: ./webservice_jobs.sqlite
    job-update-interval: 30
    job-timeout: 3600
```

In case you want to use authentication, add a list of *allowed-ips*.

In the **metadata-client** section, the client interface to the MDC is configured:

``` YAML

metadata-client:
    user-id:
    user-secret:
    user-email:
    metadata-web-app-url: 'https://in.xfel.eu/metadata'
    metadata-web-app-url: 'https://in.xfel.eu/metadata'
    token-url: 'https://in.xfel.eu/metadata/oauth/token'
    refresh-url: 'https://in.xfel.eu/metadata/oauth/token'
    auth-url: 'https://in.xfel.eu/metadata/oauth/authorize'
    scope: ''
    base-api-url: 'https://in.xfel.eu/metadata/api/'
```

Here, user-ids, secrets, email etc as provided by ITDM need to be entered.

Finally, sections for the individual *actions* the service knows can be configured.
Currently, the only action is *correct*:

``` YAML

correct:
    in-folder: /gpfs/exfel/exp/{instrument}/{cycle}/p{proposal}/raw
    out-folder: /gpfs/exfel/exp/{instrument}/{cycle}/p{proposal}/proc
```
It expects templates for the input and output file paths to be provided.

Starting the Service
--------------------

The webservice can be started as a normal python process:


   ``` bash
   python webservice.py --mode [prod | prod-auth | sim]
   ```
The available modes are:

* prod: production mode
* prod-auth: production mode with authentication on ZMQ
* sim: simulation mode, no actual *xfel-calibrate* jobs are launched.

Use

   ``` bash
   python webservice.py --help
   ```

to display a list of available options.

Testing
-------

There is a test environment on ``max-exfl-cal002``, separate from the production
instance.

```bash
ssh xcaltst@max-exfl-cal002.desy.de
cd /home/xcaltst/pycalibration

# Get the code you want to test
git pull
git checkout <branch-to-test>

# Stop the already running server, start a new one with your code
ps aux | grep webservice/webservice.py | awk '{ print $2}' | xargs kill
source ~/pycalibration_venv/bin/activate
nohup ~/pycalibration_venv/bin/python ~/pycalibration/webservice/webservice.py \
  --mode prod --logging INFO --config-file ~/pycalibration/webservice/webservice.yaml &

# Follow the logs
tail -f ~/pycalibration/web.log
```

The [test instance of myMdC](https://in.xfel.eu/test_metadata/) talks to this
test service. Using data in the [CALLAB proposal](https://in.xfel.eu/test_metadata/proposals/259)
there, request recalibration ([runs tab](https://in.xfel.eu/test_metadata/proposals/259#proposal-runs))
and processing of dark runs ([calibration constants tab](https://in.xfel.eu/test_metadata/proposals/259#proposal-calibration))
to send the 'correct' and 'dark_request' actions. The webservice logs and the
status in myMdC should update as the processing occurs.

The command ``squeue -u xcaltst`` will show running & pending Slurm jobs started
by this test system.

Manually Submitting Jobs
------------------------

A script `manual_launch.py` is provided to manually submit jobs to the service.

```bash
usage: manual_launch.py [-h] --proposal PROPOSAL [--delay DELAY] [--noconfirm] [--really] slices [slices ...]

Manually submit calibration jobs.

positional arguments:
  slices               slices (or single numbers) of runs to process, inclusive range, starting at 1 (e.g. 1:3 parsed to {1, 2, 3}, 10 parsed to {10}, :10
                       parsed to {1, 2, ..., 10})

optional arguments:
  -h, --help           show this help message and exit
  --proposal PROPOSAL  proposal number
  --delay DELAY        delay in seconds between submissions
  --noconfirm          skip confirmation
  --really             actually submit jobs instead of just printing them

To run in the background use `nohup PYTHONUNBUFFERED=1 python manual_launch.py ... &` followed by `disown`.
```

Slices inclusive, so `1:10` would mean runs 1 to 10 inclusive of 1 and 10. The
'slice' can also be a single number.

Example of usage would be `python3 ./manual_launch.py 1 10:12 160:-1 --delay 60
--proposal 2222 --really` to submit runs 1, 10 to 12, and 160+ for calibration,
for proposal 2222, with a 60 second delay between submissions.