---
# System prepended metadata

title: bkdnOJ.v2 - Thêm và khởi động máy chấm hàng loạt (2022/10/21)

---

# bkdnOJ.v2 - Thêm và khởi động máy chấm hàng loạt (2022/10/21)

## Thêm máy chấm:
### 1. Dán script sau đây vào `/home/ubuntu/backend/add_judges.py`:
```python3=
# CHANGE BELOW TO YOUR JUDGE CONFIG
YAML_TEMPLATE = """
problem_storage_root:
    - /home/ubuntu/backend/media/problem_data
runtime:
    as_x64: /usr/bin/x86_64-linux-gnu-as
    as_x86: /usr/bin/as
    awk: /usr/bin/mawk
    cat: /usr/bin/cat
    g++: /usr/bin/g++
    g++11: /usr/bin/g++
    g++14: /usr/bin/g++
    g++17: /usr/bin/g++
    gcc: /usr/bin/gcc
    gcc11: /usr/bin/gcc
    ld_x64: /usr/bin/x86_64-linux-gnu-ld
    ld_x86: /usr/bin/ld
    perl: /usr/bin/perl
    python3: /usr/bin/python3
    sed: /usr/bin/sed
"""

JUDGE_PREFIX = 'judge'
JUDGE_SECRET = 'GTjFfvbHYTRFghj23456YGhu5redCVBHJuytrdcgVSWertyujmKI&^trfdcfvgbvCDSErtgbnjuy6trdFGH'
JUDGE_COUNT = 3

# Will write configs file to this location
JUDGE_CONFIG_LOCATION = "/home/ubuntu/judge/configs"

def gen_secret(key=0): # Implement hash here if want unique secret per judge
    return JUDGE_SECRET

import os, sys
if os.path.exists(JUDGE_CONFIG_LOCATION):
    print("Ok. Found config folder")
else:
    print(f"Cannot find path '{JUDGE_CONFIG_LOCATION}', please create a folder there")
    sys.exit(0)

from judger.models.runtime import Judge
from django.db import transaction

with transaction.atomic():
    try:
        for jidx in range(1, JUDGE_COUNT+1):
            name = f"{JUDGE_PREFIX}_{jidx}"
            secret = gen_secret(jidx)
            Judge.objects.create(name=name, auth_key=secret)
            print(f"Created judge {name}.")
    except Exception as e:
        print(f"Error {e}")
        print(f"Error while creating judge {name}. Rollback.")

print("Done creating Judges. Now creating configs")
try:
    for jidx in range(1, JUDGE_COUNT+1):
        filename = f"{JUDGE_PREFIX}_{jidx}_config.yaml"
        name = f"{JUDGE_PREFIX}_{jidx}"
        secret = gen_secret(jidx)
        with open(f"{JUDGE_CONFIG_LOCATION}/{filename}", "w") as f:
            f.write(
                f"id: '{name}'\n"+
                f"key: '{secret}'\n"+
                YAML_TEMPLATE
            )
except Exception as e:
    print(f"Error while creating configs for judge {name}. Err: {e}")
```

Chú ý các giá trị:
- `YAML_TEMPLATE`: Là khối cấu hình của máy chấm mà không có id và auth_key. Cần thay vị trí problem và khối runtime tương ứng. Tham khảo phần [Thiết lập máy chấm](https://hackmd.io/X6YXdzLpTD2CC--oEYeWCg#C%E1%BA%A5u-h%C3%ACnh-m%C3%A1y-ch%E1%BA%A5m) để thiết lập. 
- `JUDGE_PREFIX`: Tiền tố tên của judge này
- `JUDGE_SECRET`: Chuỗi khóa bí mật của judge. Hiện tất cả dùng chung một secret. Có thể chỉnh lại hàm `gen_secret()` ở dòng 30 để có thể random khóa này với mỗi máy khác nhau.
- `JUDGE_COUNT`: Số lượng judge. Sẽ tạo ra `judge_1`...`judge_N`.
- `JUDGE_CONFIG_LOCATION`: Nơi mà script sẽ tạo ra các file cấu hình `.yaml`. Cần đảm bảo nơi này tồn tại. Còn không script sẽ kiểm tra giúp bạn.

### 2. Vào shell và thực hiện:
Vào shell:
```
cd ~/backend
source venv/bin/activate
python manage.py shell
```

Chạy file:
```python3=
exec(open("add_judges.py").read())
```

**Kết quả**:
- Output: 
    ![](https://i.imgur.com/lUb7lfe.png)
- Web: 
    ![](https://i.imgur.com/diotb7K.png)
- Thư mục configs:
    ![](https://i.imgur.com/NgvEWo3.png)
    Có thêm các file `.yaml` là các file ta dùng để start máy chấm.
    
## Khởi động hàng loạt:
### 1. Dán đoạn script bash này vào `/home/ubuntu/judge`:
- Đến nơi cài judge, dán đoạn mã này vào `start_judges.sh`:
```shell=
#!/bin/bash
set -e

CONFIG_PATH="/home/ubuntu/judge/configs"
CONFIG_PREFIX="judge_"
CONFIG_POSTFIX="_config.yaml"
CONFIG_FROM=1
CONFIG_TO=3


if ! command -v dmoj &> /dev/null
then
    echo "'dmoj' command could not be found. Check PATH or venv."
    exit
fi

echo "Terminating previous screens (judge session):"

i=$CONFIG_FROM
while [ $i -le $CONFIG_TO ]
do
    SESS_NAME="${CONFIG_PREFIX}${i}"
    echo $SESS_NAME
    screen -X -S $SESS_NAME quit || true
    true $((i=i+1))
done

echo "Creating screens (judge session):"
i=$CONFIG_FROM
while [ $i -le $CONFIG_TO ]
do
    SESS_NAME="${CONFIG_PREFIX}${i}"
    echo $SESS_NAME
    CONFIG_NAME="${CONFIG_PREFIX}${i}${CONFIG_POSTFIX}"
    screen -S $SESS_NAME -dm dmoj -c "${CONFIG_PATH}/${CONFIG_NAME}" -p 9999 127.0.0.1 
    true $((i=i+1))
done

echo "Done. Listing all screens:"
sleep 1.0
screen -ls
```

* Tương tự, chú ý các giá trị ở đầu file:
    * `CONFIG_PATH="/home/ubuntu/judge/configs"`: Nơi mà script sẽ tìm các file config để start máy chấm.
    * `CONFIG_PREFIX="judge_"`: Prefix của file config
    * `CONFIG_POSTFIX="_config.yaml"`: Postfix của file config
    * `CONFIG_FROM=1`: Config bắt đầu từ 
    * `CONFIG_TO=3`: Config kết thúc
    * Như trên, script sẽ tìm các file `"judge_i_config.yaml"` với i từ `CONFIG_FROM` tới `CONFIG_TO`.
    * Script cũng sẽ check nếu `dmoj` có hoạt động hay không trước khi chạy. Cần đảm bảo gọi được `dmoj` (check PATH hay check đã start venv chưa). 

* Thêm thuộc tính +x cho file:
```shell
chmod +x start_judges.sh
```

* Thực thi:
```shell=
./start_judges.sh
```

Output:
- Terminal:
    ![](https://i.imgur.com/R1LglzK.png)
- Script sẽ start các phiên screen, và trong đó thực hiện khởi động máy chấm bằng `dmoj`. Sau một lúc, trên web sẽ hiện online máy chấm:
    ![](https://i.imgur.com/N9JfvdN.png)
- Ta cũng có thể di chuyển vào screen để xem máy chấm:
```
screen -r judge_2
```
- Rời khỏi screen với tổ hợp `Ctrl+A` rồi `D`. Lưu ý nếu **tắt screen sẽ tắt máy chấm**.

### 2. Dừng hàng loạt máy chấm:
Ở script trên sẽ dừng các phiên screen `judge_x`... đã có từ trước, trước khi tạo mới. Ta có thể giữ mỗi đoạn đó để tạo thành một file script `stop_judges.sh` như dưới đây:
```shell=
#!/bin/bash
set -e

CONFIG_PATH="/home/ubuntu/judge/configs"
CONFIG_PREFIX="judge_"
CONFIG_POSTFIX="_config.yaml"
CONFIG_FROM=1
CONFIG_TO=3

echo "Terminating previous screens (judge session):"
i=$CONFIG_FROM
while [ $i -le $CONFIG_TO ]
do
    SESS_NAME="${CONFIG_PREFIX}${i}"
    echo $SESS_NAME
    screen -X -S $SESS_NAME quit || true
    true $((i=i+1))
done
echo "Done."
```

- Thêm thuộc tính `+x` và thực hiện sẽ đóng các screen tương ứng. (`judge_x`, `judge_y`,...)