Merge branch 'main' of gitea.psi.ch:sls/hla_framework_bd into feature/prod-cicd-runner

This commit is contained in:
Benjamin Labrecque
2026-08-27 15:50:27 +02:00
90 changed files with 670 additions and 905 deletions
+46
View File
@@ -0,0 +1,46 @@
#!/bin/bash
# Shared helpers for the scripts run by the gitea runner.
# Source it as: source "$(dirname "$0")/common.sh"
# Overridable so the scripts can be exercised outside of the runner
HLA_ROOT=${HLA_ROOT:-/sls/bd/hla}
# Sets AGEBD_ENV_SUFFIX_UPPER/LOWER for the given environment.
# Usage: set_env_suffix <dev|prod>
set_env_suffix() {
local ENV=$1
if [ "${ENV}" = "dev" ]; then
AGEBD_ENV_SUFFIX_UPPER="-DEV"
AGEBD_ENV_SUFFIX_LOWER="-dev"
elif [ "${ENV}" = "prod" ]; then
AGEBD_ENV_SUFFIX_UPPER=""
AGEBD_ENV_SUFFIX_LOWER=""
else
echo "Unknown environment '${ENV}', expected 'dev' or 'prod'" >&2
exit 1
fi
}
# Sets the naming and path variables used by the deploy and ioc scripts.
# Usage: init_service_env <service-name> <dev|prod>
init_service_env() {
local SERVICE_NAME=$1
AGEBD_ENV=$2
if [ -z "${SERVICE_NAME}" ] || [ -z "${AGEBD_ENV}" ]; then
echo "Usage: $0 <service-name> <dev|prod>" >&2
exit 1
fi
set_env_suffix "${AGEBD_ENV}"
SERVICE_NAME_LOWER=$(echo "${SERVICE_NAME}" | tr '[:upper:]' '[:lower:]')
SERVICE_NAME_UPPER=$(echo "${SERVICE_NAME}" | tr '[:lower:]' '[:upper:]')
SERVICE_DIR=${HLA_ROOT}/${AGEBD_ENV}/services/${SERVICE_NAME_LOWER}/current
IOC_DIR=${SERVICE_DIR}/ioc
IOC_NAME=AGEBD-CPCL-${SERVICE_NAME_UPPER}${AGEBD_ENV_SUFFIX_UPPER}
# Host running shellbox, i.e. the host the ioc itself runs on
IOC_HOST=sls-vserv-bd-01${AGEBD_ENV_SUFFIX_LOWER}
}
@@ -1,58 +0,0 @@
#!/bin/bash
SERVICE_NAME=$1
AGEBD_ENV=$2
echo "=========================================="
echo "Deploying service: $SERVICE_NAME (${AGEBD_ENV})"
echo "=========================================="
SERVICE_NAME_LOWER=$(echo "$SERVICE_NAME" | tr '[:upper:]' '[:lower:]')
SERVICE_NAME_UPPER=$(echo "$SERVICE_NAME" | tr '[:lower:]' '[:upper:]')
SERVICE_DIR=/sls/bd/hla/${AGEBD_ENV}/services/${SERVICE_NAME_LOWER}/current
SYSTEMD_UNIT_NAME=AGEBD-SERVICE-${SERVICE_NAME_UPPER}.service
SYSTEMD_UNIT_FILE_PATH=${SERVICE_DIR}/systemd/${SYSTEMD_UNIT_NAME}
IOC_DIR=${SERVICE_DIR}/ioc
IOC_PORT=$(uv run --directory scripts ci/get_ioc_port.py ${SERVICE_NAME_LOWER})
if [ "${AGEBD_ENV}" = "dev" ]; then
AGEBD_ENV_SUFFIX_UPPER="-DEV"
AGEBD_ENV_SUFFIX_LOWER="-dev"
else
AGEBD_ENV_SUFFIX_UPPER=""
AGEBD_ENV_SUFFIX_LOWER=""
fi
# TODO: see https://gitea.psi.ch/sls/hla_framework_bd/issues/50
# rm -rf "${SERVICE_DIR}"
# mkdir -p ${SERVICE_DIR}
# cp -r ./services/${SERVICE_NAME_LOWER}/current/* ${SERVICE_DIR}/
rsync -avz --delete \
--exclude='.nfs*' \
--exclude='.git/' \
--exclude='.venv/' \
--exclude='__pycache__/' \
--exclude='*.pyc' \
--exclude='.pytest_cache/' \
./services/${SERVICE_NAME_LOWER}/current/* ${SERVICE_DIR}/
### SUBSTITUTIONS
### IOC
sed -i "s/{{ *agebd_env_suffix_upper* }}/${AGEBD_ENV_SUFFIX_UPPER}/g" "${IOC_DIR}/"*
sed -i "s/{{ *agebd_env_suffix_lower* }}/${AGEBD_ENV_SUFFIX_LOWER}/g" "${IOC_DIR}/"*
sed -i "s/{{ *ioc_port* }}/${IOC_PORT}/g" "${IOC_DIR}/"*
### SYSTEMD
if [ -f "${SYSTEMD_UNIT_FILE_PATH}" ]; then
sed -i "s/{{ *agebd_env* }}/${AGEBD_ENV}/g" "${SYSTEMD_UNIT_FILE_PATH}"
fi
mkdir -p ${HOME}/.config/systemd/user
ln -sf "${SYSTEMD_UNIT_FILE_PATH}" "${HOME}/.config/systemd/user/${SYSTEMD_UNIT_NAME}"
export XDG_RUNTIME_DIR="/run/user/$(id -u)"
systemctl --user daemon-reload
systemctl --user enable "${SYSTEMD_UNIT_NAME}"
systemctl --user restart "${SYSTEMD_UNIT_NAME}"
+20
View File
@@ -0,0 +1,20 @@
#!/bin/bash
# Deploys and restarts a single service or all services.
#
# Usage: ./.gitea/scripts/deploy-for-services.sh <service-name|all> <dev|prod>
set -euo pipefail
SERVICE=${1:-}
AGEBD_ENV=${2:-}
SCRIPT_DIR=$(dirname "$0")
if [ "${SERVICE}" = "all" ]; then
SERVICES=$(ls -d services/*/ | xargs -n1 basename)
else
SERVICES=${SERVICE}
fi
for SERVICE_NAME in ${SERVICES}; do
"${SCRIPT_DIR}/deploy-service.sh" "${SERVICE_NAME}" "${AGEBD_ENV}" true
done
+76
View File
@@ -0,0 +1,76 @@
#!/bin/bash
# Deploys a service to /sls/bd/hla and (re)starts its systemd unit.
#
# Usage: ./.gitea/scripts/deploy-service.sh <service-name> <dev|prod> [restart]
source "$(dirname "$0")/common.sh"
init_service_env "${1:-}" "${2:-}"
DO_RESTART_SERVICE=$3
NOW=$(date +%Y-%m-%dT%H:%M:%S%Z)
echo "=========================================="
echo "Deploying service: $SERVICE_NAME_LOWER (${AGEBD_ENV})"
echo "=========================================="
SYSTEMD_UNIT_NAME=AGEBD-SERVICE-${SERVICE_NAME_UPPER}.service
SYSTEMD_UNIT_FILE_PATH=${SERVICE_DIR}/systemd/${SYSTEMD_UNIT_NAME}
IOC_PORT=$(uv run --directory scripts ci/get_ioc_port.py ${SERVICE_NAME_LOWER})
# TODO: see https://gitea.psi.ch/sls/hla_framework_bd/issues/50
# rm -rf "${SERVICE_DIR}"
# cp -r ./services/${SERVICE_NAME_LOWER}/current/* ${SERVICE_DIR}/
mkdir -p "${SERVICE_DIR}"
rsync -avz --delete \
--exclude='.nfs*' \
--exclude='.git/' \
--exclude='.venv/' \
--exclude='__pycache__/' \
--exclude='*.pyc' \
--exclude='.pytest_cache/' \
./services/${SERVICE_NAME_LOWER}/current/* ${SERVICE_DIR}/
### SUBSTITUTIONS
### IOC
sed -i "s/{{ *agebd_env_suffix_upper* }}/${AGEBD_ENV_SUFFIX_UPPER}/g" "${IOC_DIR}/"*
sed -i "s/{{ *agebd_env_suffix_lower* }}/${AGEBD_ENV_SUFFIX_LOWER}/g" "${IOC_DIR}/"*
sed -i "s/{{ *ioc_port* }}/${IOC_PORT}/g" "${IOC_DIR}/"*
### RENAMING
### The ioc files carry the environment suffix in their name, e.g. in dev
### AGEBD-CPCL-NTURNS_main.subs -> AGEBD-CPCL-NTURNS-DEV_main.subs
### (rsync --delete restores the original names on every deploy, so this runs every time)
if [ -n "${AGEBD_ENV_SUFFIX_UPPER}" ]; then
for FILE_SUFFIX in "_main.subs" "_parameters.yaml"; do
ORIG_FILE=${IOC_DIR}/AGEBD-CPCL-${SERVICE_NAME_UPPER}${FILE_SUFFIX}
if [ -f "${ORIG_FILE}" ]; then
mv "${ORIG_FILE}" "${IOC_DIR}/AGEBD-CPCL-${SERVICE_NAME_UPPER}${AGEBD_ENV_SUFFIX_UPPER}${FILE_SUFFIX}"
fi
done
fi
VERSION_DEPLOYED_PV="AGEBD-ALH${AGEBD_ENV_SUFFIX_UPPER}:${SERVICE_NAME_UPPER}-VERSION-DEPLOYED"
echo "Putting ${NOW} in ${VERSION_DEPLOYED_PV}"
caput "${VERSION_DEPLOYED_PV}" "${NOW}"
### SYSTEMD
# Not every service has a systemd unit (e.g. IOC-only services like params) -
# skip enabling/restarting entirely when there's nothing to enable/restart.
if [ -f "${SYSTEMD_UNIT_FILE_PATH}" ]; then
sed -i "s/{{ *agebd_env* }}/${AGEBD_ENV}/g" "${SYSTEMD_UNIT_FILE_PATH}"
mkdir -p ${HOME}/.config/systemd/user
ln -sf "${SYSTEMD_UNIT_FILE_PATH}" "${HOME}/.config/systemd/user/${SYSTEMD_UNIT_NAME}"
if [ "$DO_RESTART_SERVICE" ]; then
echo "Enabling and restarting systemd service ${SYSTEMD_UNIT_NAME}"
export XDG_RUNTIME_DIR="/run/user/$(id -u)"
systemctl --user daemon-reload
systemctl --user enable "${SYSTEMD_UNIT_NAME}"
systemctl --user restart "${SYSTEMD_UNIT_NAME}"
fi
else
echo "No systemd unit for ${SERVICE_NAME_LOWER}, skipping enable/restart"
fi
+27
View File
@@ -0,0 +1,27 @@
#!/bin/bash
# Runs an ioc action for a single service or for all services.
#
# Usage: ./.gitea/scripts/ioc-for-services.sh <install|start|restart> <service-name|all> <dev|prod>
set -euo pipefail
IOC_ACTION=${1:-}
SERVICE=${2:-}
AGEBD_ENV=${3:-}
SCRIPT_DIR=$(dirname "$0")
IOC_SCRIPT=${SCRIPT_DIR}/ioc-${IOC_ACTION}.sh
if [ ! -f "${IOC_SCRIPT}" ]; then
echo "Unknown ioc action '${IOC_ACTION}', expected 'install', 'start' or 'restart'" >&2
exit 1
fi
if [ "${SERVICE}" = "all" ]; then
SERVICES=$(ls -d services/*/ | xargs -n1 basename)
else
SERVICES=${SERVICE}
fi
for SERVICE_NAME in ${SERVICES}; do
"${IOC_SCRIPT}" "${SERVICE_NAME}" "${AGEBD_ENV}"
done
+37
View File
@@ -0,0 +1,37 @@
#!/bin/bash
# Installs the ioc of a service from its deployed ioc dir on /sls.
# Requires the service to have been deployed first (deploy-and-restart-service.sh),
# which is what puts the substituted and environment-renamed ioc files in place.
#
# Usage: ./.gitea/scripts/ioc-install.sh <service-name> <dev|prod>
set -euo pipefail
source "$(dirname "$0")/common.sh"
init_service_env "${1:-}" "${2:-}"
echo "=========================================="
echo "Installing ioc: ${IOC_NAME} (${AGEBD_ENV})"
echo "=========================================="
if [ ! -d "${IOC_DIR}" ]; then
echo "Ioc dir '${IOC_DIR}' not found, deploy the service first" >&2
exit 1
fi
STDERR_FILE=$(mktemp)
trap 'rm -f "${STDERR_FILE}"' EXIT
set +e
(cd "${IOC_DIR}" && ioc install -V --facility sls --ioc "${IOC_NAME}" --clean) 2>"${STDERR_FILE}"
INSTALL_RC=$?
set -e
cat "${STDERR_FILE}" >&2
# 'ioc install ...' returns exit code 0 even on failure, so its stderr is
# inspected for error keywords to force a pipeline failure.
# Note: -a because some responses of the 'ioc ...' cli are non-utf8.
if [ ${INSTALL_RC} -ne 0 ] || grep -qa -e "\[error\]" -e "Unable to write" "${STDERR_FILE}"; then
echo "Failed to install ioc ${IOC_NAME}" >&2
exit 1
fi
+15
View File
@@ -0,0 +1,15 @@
#!/bin/bash
# Restarts the ioc of a service.
#
# Usage: ./.gitea/scripts/ioc-restart.sh <service-name> <dev|prod>
set -euo pipefail
source "$(dirname "$0")/common.sh"
init_service_env "${1:-}" "${2:-}"
echo "=========================================="
echo "Restarting ioc: ${IOC_NAME} (${AGEBD_ENV})"
echo "=========================================="
cd "${IOC_DIR}"
ioc restart "${IOC_NAME}"
+23
View File
@@ -0,0 +1,23 @@
#!/bin/bash
# Starts the ioc of a service through shellbox on the ioc host.
# Needed once after the initial install of a new ioc.
#
# Usage: ./.gitea/scripts/ioc-start.sh <service-name> <dev|prod>
set -euo pipefail
source "$(dirname "$0")/common.sh"
init_service_env "${1:-}" "${2:-}"
IOC_PORT=$(uv run --directory scripts ci/get_ioc_port.py "${SERVICE_NAME_LOWER}")
echo "=========================================="
echo "Starting ioc: ${IOC_NAME} on ${IOC_HOST}:${IOC_PORT}"
echo "=========================================="
ssh -o BatchMode=yes "${IOC_HOST}" bash -s <<REMOTE
set -e
sudo shellbox status
sudo shellbox reload
sudo shellbox status
sudo shellbox start ${IOC_PORT}
REMOTE
+21 -9
View File
@@ -9,7 +9,7 @@ env:
UV_CACHE_DIR: /var/cache/uv
UV_LINK_MODE: hardlink
# TODO: when to deploy to prod? Manual (i.e. cli command)?
# TODO: when to deploy to prod? Manual (i.e. the 'Ioc' workflow)?
jobs:
deploy:
runs-on: hla-dev
@@ -31,19 +31,31 @@ jobs:
SERVICE_NAME="${BRANCH#feature/add-service-}"
echo "service_name_lower=$SERVICE_NAME" >> $GITHUB_OUTPUT
# - name: Install Master IOC
# - name: Restart Master IOC
# The ioc steps read the deployed ioc files on /sls, so the service
# has to be deployed before its ioc is installed.
- name: Deploy and Restart Master Service
run: |
./.gitea/scripts/deploy-and-restart-service.sh "master" "dev"
./.gitea/scripts/deploy-service.sh "master" "dev" true
# - name: Install New IOC
- name: Install Master IOC
run: |
./.gitea/scripts/ioc-install.sh "master" "dev"
# - name: Start New IOC
- name: Restart Master IOC
run: |
./.gitea/scripts/ioc-restart.sh "master" "dev"
- name: Deploy and Restart New Service
run: |
SERVICE_NAME=${{ steps.parse_branch.outputs.service_name_lower }}
./.gitea/scripts/deploy-and-restart-service.sh "${SERVICE_NAME}" "dev"
./.gitea/scripts/deploy-service.sh "${SERVICE_NAME}" "dev" true
- name: Install New IOC
run: |
SERVICE_NAME=${{ steps.parse_branch.outputs.service_name_lower }}
./.gitea/scripts/ioc-install.sh "${SERVICE_NAME}" "dev"
- name: Start New IOC
run: |
SERVICE_NAME=${{ steps.parse_branch.outputs.service_name_lower }}
./.gitea/scripts/ioc-start.sh "${SERVICE_NAME}" "dev"
+7 -1
View File
@@ -116,6 +116,12 @@ jobs:
--exclude='.pytest_cache/' \
./qt/* "${DEST_DIR}/"
### SUBSTITUTIONS
source "${{ github.workspace }}/.gitea/scripts/common.sh"
set_env_suffix "${AGEBD_ENV}"
sed -i "s/{{ *agebd_env_suffix_upper* }}/${AGEBD_ENV_SUFFIX_UPPER}/g" "${DEST_DIR}/"*
sed -i "s/{{ *agebd_env_suffix_lower* }}/${AGEBD_ENV_SUFFIX_LOWER}/g" "${DEST_DIR}/"*
# -----------------------------------------------------------------
# Services Detection, Testing, and Deployment
# -----------------------------------------------------------------
@@ -169,5 +175,5 @@ jobs:
CHANGED_SERVICES=$(git diff --name-only "$BEFORE" "${{ github.sha }}" | awk -F/ '$1=="services" && $2!="" {print $2}' | sort -u)
for SERVICE in $CHANGED_SERVICES; do
./.gitea/scripts/deploy-and-restart-service.sh "${SERVICE}" "${AGEBD_ENV}"
./.gitea/scripts/deploy-service.sh "${SERVICE}" "${AGEBD_ENV}" false
done
+56
View File
@@ -0,0 +1,56 @@
name: Ioc
# Manually install / start / restart iocs.
# The ioc files are read from the deployed dir on /sls,
# so the service has to be deployed (see the 'Deploy' workflow) beforehand.
on:
workflow_dispatch:
inputs:
action:
description: 'What to do with the ioc(s)'
type: choice
required: true
default: 'install'
options:
- install
- restart
- start
service:
description: "Service name in lower case, or 'all' for every service"
type: string
required: true
agebd_env:
description: 'Environment'
type: choice
required: true
default: 'dev'
options:
- dev
- prod
env:
UV_CACHE_DIR: /var/cache/uv
UV_LINK_MODE: hardlink
jobs:
ioc-dev:
if: ${{ inputs.agebd_env == 'dev' }}
runs-on: hla-dev
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Run ioc ${{ inputs.action }}
run: |
./.gitea/scripts/ioc-for-services.sh "${{ inputs.action }}" "${{ inputs.service }}" "dev"
ioc-prod:
if: ${{ inputs.agebd_env == 'prod' }}
runs-on: hla-prod
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Run ioc ${{ inputs.action }}
run: |
./.gitea/scripts/ioc-for-services.sh "${{ inputs.action }}" "${{ inputs.service }}" "prod"
+27
View File
@@ -0,0 +1,27 @@
name: Service
# Manually re-deploy (and restart) individual services, or all of them, in dev.
# There is deliberately no prod option here - prod deploys go through the
# normal push-to-main flow (see the 'Deploy' workflow), which requires review.
on:
workflow_dispatch:
inputs:
service:
description: "Service name in lower case, or 'all' for every service"
type: string
required: true
env:
UV_CACHE_DIR: /var/cache/uv
UV_LINK_MODE: hardlink
jobs:
deploy-dev:
runs-on: hla-dev
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Deploy and restart ${{ inputs.service }}
run: |
./.gitea/scripts/deploy-for-services.sh "${{ inputs.service }}" "dev"
-8
View File
@@ -1,8 +0,0 @@
[defaults]
remote_tmp = /tmp/ansible-$USER
# some responses of 'ioc ...' cli are non-utf8
module_strict_utf8_response = False
# ./ refers to dir in which this file is located
collections_path = ./installed-collections
-3
View File
@@ -1,3 +0,0 @@
collections:
- name: ansible.posix
version: 1.6.0
-19
View File
@@ -1,19 +0,0 @@
all:
vars:
agebd_env: "dev"
env_suffix_upper: "-DEV"
env_suffix_lower: "-dev"
children:
lc:
hosts:
sls-lc:
ansible_remote_tmp: /tmp/.ansible_${ansible_user}_tmp
vserv:
hosts:
sls-vserv-bd-01-dev:
ansible_ssh_pipelining: true
ansible_ssh_common_args: '-o ForwardAgent=yes'
vserv-hla:
hosts:
sls-vserv-bd-hla01-dev:
-18
View File
@@ -1,18 +0,0 @@
all:
vars:
agebd_env: "prod"
env_suffix_upper: ""
env_suffix_lower: ""
children:
lc:
hosts:
sls-lca:
ansible_remote_tmp: /tmp/.ansible_${ansible_user}_tmp
vserv:
hosts:
sls-vserv-bd-01:
ansible_ssh_pipelining: true
vserv-hla:
hosts:
sls-vserv-bd-hla01:
-42
View File
@@ -1,42 +0,0 @@
---
- name: Add a new service
hosts: vserv-hla
connection: local
gather_facts: true # Gather once here, available to all included tasks!
vars:
repo_root: "{{ playbook_dir }}/../.."
tasks:
# - name: Install Master IOC
# vars:
# preferred_service_name_lower: master
# branch_name: "feature/add-service-{{ service_name_lower }}"
# import_tasks: ../tasks/ioc/ioc-install.yml
# - name: Restart Master IOC
# vars:
# preferred_service_name_lower: master
# branch_name: "feature/add-service-{{ service_name_lower }}"
# import_tasks: ../tasks/ioc/ioc-restart.yml
- name: Deploy and Restart Master Service
vars:
preferred_service_name_lower: master
branch_name: "feature/add-service-{{ service_name_lower }}"
import_tasks: ../tasks/service/service-deploy-and-restart.yml
# - name: Install New IOC
# vars:
# branch_name: "feature/add-service-{{ service_name_lower }}"
# import_tasks: ../tasks/ioc/ioc-install.yml
# - name: Start New IOC
# vars:
# branch_name: "feature/add-service-{{ service_name_lower }}"
# import_tasks: ../tasks/ioc/ioc-start.yml
- name: Deploy and Restart New Service
vars:
branch_name: "feature/add-service-{{ service_name_lower }}"
import_tasks: ../tasks/service/service-deploy-and-restart.yml
@@ -1,24 +0,0 @@
---
- name: Deploy and Restart Modified Services
hosts: vserv-hla
connection: local
vars:
# Passed from the Gitea workflow, e.g., "master playground"
changed_services_raw: ""
agebd_env: "dev"
repo_root: "{{ playbook_dir }}/../.."
tasks:
# Fail early if no services were modified
- name: Check if there are services to deploy
ansible.builtin.meta: end_play
when: changed_services_raw | trim == ""
# Main Loop: loop through the list of modified service names
- name: Deploy and Restart Modified Services
include_tasks: ../tasks/service/service-deploy-and-restart.yml
loop: "{{ changed_services_raw.split() }}"
loop_control:
loop_var: service_name_lower
extended: yes
-11
View File
@@ -1,11 +0,0 @@
---
- name: Install an IOC
hosts: lc
gather_facts: false
vars:
workspace_dir: "/tmp/hla_framework_bd"
tasks:
- name: Run ioc install tasks
ansible.builtin.import_tasks: ../tasks/ioc/ioc-install.yml
-11
View File
@@ -1,11 +0,0 @@
---
- name: Restart an IOC
hosts: lc
gather_facts: false
vars:
workspace_dir: "/tmp/hla_framework_bd"
tasks:
- name: Run ioc restart tasks
ansible.builtin.include_tasks: ../tasks/ioc/ioc-restart.yml
-14
View File
@@ -1,14 +0,0 @@
# TODO: ansible seems to be very slow on this machine
# each step takes quite a while (e.g. cloning repo, which
# is significantly faster on other machines).
---
- name: Start an IOC
hosts: vserv
gather_facts: false
vars:
workspace_dir: "/tmp/hla_framework_bd"
tasks:
- name: Run ioc start tasks
ansible.builtin.import_tasks: ../tasks/ioc/ioc-start.yml
-63
View File
@@ -1,63 +0,0 @@
---
- name: Init service name
ansible.builtin.import_tasks: ../utils/init-service-name.yml
- name: Clone repo
ansible.builtin.import_tasks: ../utils/clone-repo.yml
- name: Get and set ioc_port
ansible.builtin.import_tasks: ../utils/get-ioc-port.yml
- name: "[{{ target_service_name_lower }}] Establish environment naming conventions"
ansible.builtin.set_fact:
service_name_upper: "{{ target_service_name_lower | upper }}"
- name: "[{{ target_service_name_lower }}] Define file paths"
ansible.builtin.set_fact:
ioc_base_dir: "{{ workspace_dir }}/services/{{ target_service_name_lower }}/current/ioc"
orig_subs_name: "AGEBD-CPCL-{{ service_name_upper }}_main.subs"
target_subs_name: "AGEBD-CPCL-{{ service_name_upper }}{{ env_suffix_upper }}_main.subs"
orig_params_name: "AGEBD-CPCL-{{ service_name_upper }}_parameters.yaml"
target_params_name: "AGEBD-CPCL-{{ service_name_upper }}{{ env_suffix_upper }}_parameters.yaml"
# Rename the subs file if we are in DEV
- name: "[{{ target_service_name_lower }}] Rename substitution file for environment"
ansible.builtin.command:
cmd: "mv {{ ioc_base_dir }}/{{ orig_subs_name }} {{ ioc_base_dir }}/{{ target_subs_name }}"
removes: "{{ ioc_base_dir }}/{{ orig_subs_name }}" # Only runs if the original file exists
# Rename the paramters file if we are in DEV
- name: "[{{ target_service_name_lower }}] Rename parameters file for environment"
ansible.builtin.command:
cmd: "mv {{ ioc_base_dir }}/{{ orig_params_name }} {{ ioc_base_dir }}/{{ target_params_name }}"
removes: "{{ ioc_base_dir }}/{{ orig_params_name }}" # Only runs if the original file exists
# Replace {{ agebd_env_suffix_upper }} inside the newly renamed subs file
- name: "[{{ target_service_name_lower }}] Substitute environment tokens inside subs file"
ansible.builtin.replace:
path: "{{ ioc_base_dir }}/{{ target_subs_name }}"
regexp: '\{\{\s*agebd_env_suffix_upper\s*\}\}'
replace: "{{ env_suffix_upper }}"
# Replace {{ agebd_env_suffix_lower }} and {{ ioc_port }} inside the newly renamed params file
- name: "[{{ target_service_name_lower }}] Substitute environment token inside params file"
ansible.builtin.replace:
path: "{{ ioc_base_dir }}/{{ target_params_name }}"
regexp: "{{ item.regexp }}"
replace: "{{ item.replace | string }}"
with_items:
- { regexp: '\{\{\s*agebd_env_suffix_lower\s*\}\}', replace: "{{ env_suffix_lower }}" }
- { regexp: '\{\{\s*ioc_port\s*\}\}', replace: "{{ ioc_port }}" }
- name: "[{{ target_service_name_lower }}] Run ioc install"
ansible.builtin.shell:
cmd: "ioc install -V --facility sls --ioc AGEBD-CPCL-{{ service_name_upper }}{{ env_suffix_upper }} --clean"
chdir: "{{ ioc_base_dir }}"
changed_when: true
register: ioc_install_result
# Force a pipeline failure if the command output contains error keywords
# (ioc install ... returns exit code 0 even on failure...)
failed_when: >
ioc_install_result.rc != 0 or
"[error]" in ioc_install_result.stderr or
"Unable to write" in ioc_install_result.stderr
-20
View File
@@ -1,20 +0,0 @@
---
- name: Init service name
ansible.builtin.import_tasks: ../utils/init-service-name.yml
- name: Clone repo
ansible.builtin.import_tasks: ../utils/clone-repo.yml
- name: "[{{ target_service_name_lower }}] Establish environment naming conventions"
ansible.builtin.set_fact:
service_name_upper: "{{ target_service_name_lower | upper }}"
- name: "[{{ target_service_name_lower }}] Define file paths"
ansible.builtin.set_fact:
ioc_base_dir: "{{ workspace_dir }}/services/{{ target_service_name_lower }}/current/ioc"
- name: "[{{ target_service_name_lower }}] Restart IOC"
ansible.builtin.shell:
cmd: "ioc restart AGEBD-CPCL-{{ service_name_upper }}{{ env_suffix_upper }}"
chdir: "{{ ioc_base_dir }}"
changed_when: true
-17
View File
@@ -1,17 +0,0 @@
---
- name: Init service name
ansible.builtin.import_tasks: ../utils/init-service-name.yml
- name: Clone repo
ansible.builtin.import_tasks: ../utils/clone-repo.yml
- name: Get and set ioc_port
ansible.builtin.import_tasks: ../utils/get-ioc-port.yml
- name: "[{{ ioc_port }}] Start IOC"
ansible.builtin.shell: |
sudo shellbox status
sudo shellbox reload
sudo shellbox status
sudo shellbox start {{ ioc_port }}
changed_when: true
@@ -1,53 +0,0 @@
---
- name: Initialize service name
ansible.builtin.set_fact:
# Uses preferred_service_name_lower if defined in vars, otherwise falls back to the CLI extra-vars
target_service_name_lower: "{{ preferred_service_name_lower | default(service_name_lower) }}"
- name: "[{{ target_service_name_lower }}] Define service specific paths and names"
ansible.builtin.set_fact:
svc_current_dir: "/sls/bd/hla/{{ agebd_env }}/services/{{ target_service_name_lower }}/current"
service_unit_name: "AGEBD-SERVICE-{{ target_service_name_lower | upper }}.service"
- name: "[{{ target_service_name_lower }}] Ensure target directory structure exists"
ansible.builtin.file:
path: "{{ svc_current_dir }}"
state: directory
# ansible.builtin.shell: |
# mkdir -p "{{ svc_current_dir }}"
# TODO: make sure this is what we want (essentially wipes existing dest dir). Does it remove .venv?
- name: "[{{ target_service_name_lower }}] Sync service deployment files"
ansible.posix.synchronize:
src: "{{ repo_root }}/services/{{ target_service_name_lower }}/current/"
dest: "{{ svc_current_dir }}/"
delete: yes # This removes stale files on the target that no longer exist locally
recursive: yes
- name: "[{{ target_service_name_lower }}] Substitute environment variable inline"
ansible.builtin.replace:
path: "{{ svc_current_dir }}/systemd/{{ service_unit_name }}"
regexp: '\{\{\s*agebd_env\s*\}\}'
replace: "{{ agebd_env }}"
# systemctl --user link --force "./{{ service_unit_name }}"
- name: "[{{ target_service_name_lower }}] Symlink systemd unit file (Absolute Path)"
ansible.builtin.file:
src: "{{ svc_current_dir }}/systemd/{{ service_unit_name }}"
dest: "~/.config/systemd/user/{{ service_unit_name }}"
state: link
force: yes # This overwrites the broken/existing symlink cleanly without error
# export XDG_RUNTIME_DIR=/run/user/$(id -u)
# systemctl --user daemon-reload
# systemctl --user enable "{{ service_unit_name }}"
# systemctl --user restart "{{ service_unit_name }}"
- name: "[{{ target_service_name_lower }}] Enable and Restart Systemd Service"
ansible.builtin.systemd_service:
name: "{{ service_unit_name }}"
scope: user
daemon_reload: yes
enabled: yes
state: restarted
environment:
XDG_RUNTIME_DIR: "/run/user/{{ ansible_user_uid }}"
-8
View File
@@ -1,8 +0,0 @@
---
- name: Clone this repository
ansible.builtin.git:
repo: "git@gitea.psi.ch:sls/hla_framework_bd.git"
dest: "{{ workspace_dir }}"
accept_hostkey: true
version: "{{ branch_name }}"
force: true
-18
View File
@@ -1,18 +0,0 @@
---
- name: Read the service registry file
ansible.builtin.slurp:
src: "{{ workspace_dir }}/config/services_registry.yml"
register: remote_registry_file
- name: Parse the service registry YAML content into a fact
ansible.builtin.set_fact:
services_registry: "{{ remote_registry_file.content | b64decode | from_yaml }}"
- name: Extract the port from the services registry and save it as a persistent fact
ansible.builtin.set_fact:
ioc_port: "{{ services_registry.services | selectattr('name', 'equalto', target_service_name_lower) | map(attribute='ioc_port') | first | default(none) }}"
- name: Fail if the service or port wasn't found in the registry
ansible.builtin.fail:
msg: "Service '{{ target_service_name_lower }}' not found in services registry or port not set"
when: ioc_port | trim == "" or ioc_port is none
@@ -1,5 +0,0 @@
---
- name: Initialize service name
ansible.builtin.set_fact:
# Uses preferred_service_name_lower if defined in vars, otherwise falls back to the CLI extra-vars
target_service_name_lower: "{{ preferred_service_name_lower | default(service_name_lower) }}"
+1 -4
View File
@@ -5,11 +5,8 @@ AGEBD_ENV=$2 # 'dev' for dev environment, any other string launches prod.
REMAINING_ARGS=( "${@:3}" )
if [ "$AGEBD_ENV" = "dev" ]; then
AGEBD_ENV_SUFFIX="-DEV"
EPICS_CA_ADDR_LIST="$EPICS_CA_ADDR_LIST 129.129.146.255"
else
AGEBD_ENV_SUFFIX=""
fi
export EPICS_CA_ADDR_LIST
caqtdm -macro "AGEBD_ENV_SUFFIX=${AGEBD_ENV_SUFFIX}" "${REMAINING_ARGS[@]}" "${UI_FILENAME}"
caqtdm "${REMAINING_ARGS[@]}" "${UI_FILENAME}"
+10 -6
View File
@@ -3,9 +3,6 @@
SVC_NAME=$1
set --
# TODO: remove?
# PSI_FACILITY=SLS
SERVICES_DIR=/sls/bd/hla/${AGEBD_ENV}/services
SVC_NAME_LOWER=${SVC_NAME,,}
SVC_NAME_LOWER_UNDERSCORES="${SVC_NAME_LOWER//-/_}"
@@ -14,11 +11,19 @@ SVC_DIR=${SERVICES_DIR}/${SVC_NAME_LOWER}
# gfa_12_epics.sh died on the 16.04.2026 due to required AFS write access
#. /etc/profile.d/gfa_12_epics.sh
#. /sls/bd/exchange/BeamData/user/felix/workarounds/epicsRHEL8.rc # temporary fix
# cas_12_epics.sh can only fall back to the CA gateway when its broadcast-address
# auto-detection fails (e.g. non-accelerator-network hosts) if PSI_GFA_FACILITY is
# set; systemd --user units don't inherit it from an interactive login shell so we need
# to set PSI_GFA_FACILITY=SLS explicitely.
export PSI_GFA_FACILITY=SLS
. /etc/profile.d/cas_12_epics.sh
if [ "${AGEBD_ENV}" = "dev" ]; then
export EPICS_CA_ADDR_LIST="$EPICS_CA_ADDR_LIST 129.129.146.255"
fi
echo "EPICS_CA_ADDR_LIST:"
echo "$EPICS_CA_ADDR_LIST"
# activate conda environment
. /opt/gfa/python-3.10/latest/bin/activate
@@ -40,7 +45,6 @@ uv sync --frozen --project ${APP_DIR}
# activate .venv, this works in conjunction with the conda environment
source ${APP_DIR}/.venv/bin/activate
# TODO: use `exec python3 ...` here?
python -u -B -m agebd_${SVC_NAME_LOWER_UNDERSCORES}.main
exec python -u -B -m agebd_${SVC_NAME_LOWER_UNDERSCORES}.main
# TODO: remove: cicd test 54
# TODO: remove: cicd test 57
-2
View File
@@ -9,8 +9,6 @@ authors = [
dependencies = [
"agebd",
"ansible>=8.7.0",
"ansible-runner>=2.4.3",
"copier>=9.10.3",
"pydantic>=2.13.4",
"pydantic-settings>=2.14.2",
-40
View File
@@ -1,40 +0,0 @@
import os
import sys
import ansible_runner
import typer
from agebd.utils import get_git_root
from core.enums import IOC_ENV
ioc = typer.Typer(no_args_is_help=True)
REPO_ROOT = get_git_root(__file__)
def run_playbook(playbook: str, env: IOC_ENV, vars: list[str]):
env_str = str(env)
if env_str not in ["dev", "prod"]:
raise ValueError("env must be 'dev' or 'prod'")
current_env = os.environ.copy()
current_env["ANSIBLE_CONFIG"] = f"{REPO_ROOT}/ansible/ansible.cfg"
extra_vars = [f"--extra-vars={var}" for var in vars]
out, err, rc = ansible_runner.run_command(
executable_cmd=f"ansible-playbook",
cmdline_args=[
playbook,
"-i",
f"{REPO_ROOT}/ansible/inventories/{env_str}/hosts.yml",
*extra_vars,
"-v",
],
envvars=current_env,
input_fd=sys.stdin,
output_fd=sys.stdout,
error_fd=sys.stderr,
)
print("rc: {}".format(rc))
print("out: {}".format(out))
print("err: {}".format(err))
-8
View File
@@ -6,14 +6,6 @@ class ServiceStatus(str, Enum):
DISABLED = "disabled"
class IOC_ENV(str, Enum):
PROD = "prod"
DEV = "dev"
def __str__(self) -> str:
return self.value
class GuiOption(str, Enum):
NEW = "new"
EXISTING = "existing"
-112
View File
@@ -1,112 +0,0 @@
import typer
from agebd.utils import get_git_root
from config.paths import Paths
from core.ansible import run_playbook
from core.enums import IOC_ENV
from models import ServiceRegistry
ioc = typer.Typer(no_args_is_help=True)
REPO_ROOT = get_git_root(__file__)
ANSIBLE_PLAYBOOKS_DIR = REPO_ROOT / "ansible" / "playbooks"
IOC_PLAYBOOK_INSTALL = ANSIBLE_PLAYBOOKS_DIR / "ioc-install.yml"
IOC_PLAYBOOK_RESTART = ANSIBLE_PLAYBOOKS_DIR / "ioc-restart.yml"
IOC_PLAYBOOK_START = ANSIBLE_PLAYBOOKS_DIR / "ioc-start.yml"
SERVICE_NAME_HELP = "Service name in lower case"
def _resolve_branch_name(branch_name: str | None, service_name_lower: str) -> str:
return branch_name or f"feature/add-service-{service_name_lower}"
def _get_registry_services():
paths = Paths()
registry = ServiceRegistry.read_from_file(paths.service_registry_file)
return registry.get_services()
@ioc.command(no_args_is_help=True)
def install(
env: IOC_ENV = typer.Option(..., "--env"),
service_name_lower: str = typer.Option(..., "--service-name", "-s", help=SERVICE_NAME_HELP),
branch_name: str | None = typer.Option(
None, "--branch-name", "-b", help="Default is 'feature/add-service-{service-name}'"
),
):
branch_name = _resolve_branch_name(branch_name, service_name_lower)
run_playbook(
playbook=str(IOC_PLAYBOOK_INSTALL),
env=env,
vars=[f"branch_name={branch_name}", f"service_name_lower={service_name_lower}"],
)
@ioc.command(no_args_is_help=True)
def install_all(
env: IOC_ENV = typer.Option(..., "--env"),
):
for service in _get_registry_services():
run_playbook(
playbook=str(IOC_PLAYBOOK_INSTALL),
env=env,
vars=["branch_name=main", f"service_name_lower={service.name_lower}"],
)
@ioc.command(no_args_is_help=True)
def start(
env: IOC_ENV = typer.Option(..., "--env"),
service_name_lower: str = typer.Option(..., "--service-name", "-s", help=SERVICE_NAME_HELP),
branch_name: str | None = typer.Option(
None, "--branch-name", "-b", help="Default is 'feature/add-service-{service-name}'"
),
):
branch_name = _resolve_branch_name(branch_name, service_name_lower)
run_playbook(
playbook=str(IOC_PLAYBOOK_START),
env=env,
vars=[f"service_name_lower={service_name_lower}", f"branch_name={branch_name}"],
)
@ioc.command()
def start_all(
env: IOC_ENV = typer.Option(..., "--env"),
):
for service in _get_registry_services():
run_playbook(
playbook=str(IOC_PLAYBOOK_START),
env=env,
vars=[f"service_name_lower={service.name_lower}"],
)
@ioc.command(no_args_is_help=True)
def restart(
env: IOC_ENV = typer.Option(..., "--env"),
service_name_lower: str = typer.Option(..., "--service-name", "-s", help=SERVICE_NAME_HELP),
branch_name: str | None = typer.Option(
None, "--branch-name", "-b", help="Default is 'feature/add-service-{service-name}'"
),
):
branch_name = _resolve_branch_name(branch_name, service_name_lower)
run_playbook(
playbook=str(IOC_PLAYBOOK_RESTART),
env=env,
vars=[f"branch_name={branch_name}", f"service_name_lower={service_name_lower}"],
)
@ioc.command(no_args_is_help=True)
def restart_all(
env: IOC_ENV = typer.Option(..., "--env"),
):
for service in _get_registry_services():
run_playbook(
playbook=str(IOC_PLAYBOOK_RESTART),
env=env,
vars=["branch_name=main", f"service_name_lower={service.name_lower}"],
)
-2
View File
@@ -2,7 +2,6 @@ import logging
import typer
import ioc
import service
logger = logging.getLogger(__name__)
@@ -10,7 +9,6 @@ logger = logging.getLogger(__name__)
cli = typer.Typer(no_args_is_help=True, add_completion=False)
cli.add_typer(service.service, name="service")
cli.add_typer(ioc.ioc, name="ioc")
if __name__ == "__main__":
@@ -1,3 +1,4 @@
<!--TODO: remove: cicd test 57-->
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
Generated
-147
View File
@@ -37,8 +37,6 @@ version = "0.1.0"
source = { editable = "." }
dependencies = [
{ name = "agebd" },
{ name = "ansible" },
{ name = "ansible-runner" },
{ name = "copier" },
{ name = "pydantic" },
{ name = "pydantic-settings" },
@@ -60,8 +58,6 @@ dev = [
[package.metadata]
requires-dist = [
{ name = "agebd", editable = "../packages/agebd" },
{ name = "ansible", specifier = ">=8.7.0" },
{ name = "ansible-runner", specifier = ">=2.4.3" },
{ name = "copier", specifier = ">=9.10.3" },
{ name = "pydantic", specifier = ">=2.13.4" },
{ name = "pydantic-settings", specifier = ">=2.14.2" },
@@ -98,49 +94,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" },
]
[[package]]
name = "ansible"
version = "10.7.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "ansible-core" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d4/64/29fdff6fe7682342adb54802c1cd90b2272d382e1743089af88f90a1d986/ansible-10.7.0.tar.gz", hash = "sha256:59d29e3de1080e740dfa974517d455217601b16d16880314d9be26145c68dc22", size = 41256974, upload-time = "2024-12-03T18:04:25.794Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f3/95/cb8944902a2cdd94b1e19ff73695548679a388b9c473dc63c8dc64ffea3a/ansible-10.7.0-py3-none-any.whl", hash = "sha256:0089f08e047ceb70edd011be009f5c6273add613fbe491e9697c0556c989d8ea", size = 51576038, upload-time = "2024-12-03T18:04:20.065Z" },
]
[[package]]
name = "ansible-core"
version = "2.17.14"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cryptography" },
{ name = "jinja2" },
{ name = "packaging" },
{ name = "pyyaml" },
{ name = "resolvelib" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ff/80/2925a0564f6f99a8002c3be3885b83c3a1dc5f57ebf00163f528889865f5/ansible_core-2.17.14.tar.gz", hash = "sha256:7c17fee39f8c29d70e3282a7e9c10bd70d5cd4fd13ddffc5dcaa52adbd142ff8", size = 3119687, upload-time = "2025-09-08T18:28:03.158Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/86/29/d694562f1a875b50aa74f691521fe493704f79cf1938cd58f28f7e2327d2/ansible_core-2.17.14-py3-none-any.whl", hash = "sha256:34a49582a57c2f2af17ede2cefd3b3602a2d55d22089f3928570d52030cafa35", size = 2189656, upload-time = "2025-09-08T18:28:00.375Z" },
]
[[package]]
name = "ansible-runner"
version = "2.4.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "packaging" },
{ name = "pexpect" },
{ name = "python-daemon" },
{ name = "pyyaml" },
]
sdist = { url = "https://files.pythonhosted.org/packages/68/4f/62222b42b52cc771db51ccc77ffac19cc5f0196ff61de7c93585845a819b/ansible_runner-2.4.3.tar.gz", hash = "sha256:5f3025529bb968fdc3b627457dd8d418dbf9d53bc73735d50e69c28544d53031", size = 154148, upload-time = "2026-03-16T18:42:14.861Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e3/89/fa7f81db6134caa455c9351373ee934b5fdeed212eb939d6c42d80d683d7/ansible_runner-2.4.3-py3-none-any.whl", hash = "sha256:cdac6daa151a50084ffda710e769db23fa975fc0507796191d7708831b286e37", size = 80257, upload-time = "2026-03-16T18:42:13.72Z" },
]
[[package]]
name = "asttokens"
version = "3.0.2"
@@ -150,29 +103,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/d4/2b/04b8a15f3a1c77bc79ddf5c73875327f34b4fa75982df2b76e45e402d364/asttokens-3.0.2-py3-none-any.whl", hash = "sha256:9da13157f5b28becde0bd374fc677dcd3c290614264eff096f167c469cd9f933", size = 28702, upload-time = "2026-07-12T03:31:47.542Z" },
]
[[package]]
name = "cffi"
version = "2.1.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pycparser", marker = "implementation_name != 'PyPy'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/57/5f/ff100cae70ebe9d8df1c01a00e510e45d9adb5c1fdda84791b199141de97/cffi-2.1.0.tar.gz", hash = "sha256:efc1cdd798b1aaf39b4610bba7aad28c9bea9b910f25c784ccf9ec1fa719d1f9", size = 531036, upload-time = "2026-07-06T21:34:30.382Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c0/e9/6d7724983b3d5a0908dbf74f64038ade77c18646ff6636ec7894fd392ce1/cffi-2.1.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:b65f590ef2a44640f9a05dbb548a429b4ade77913ce683ac8b1480777658a6c0", size = 183837, upload-time = "2026-07-06T21:32:09.655Z" },
{ url = "https://files.pythonhosted.org/packages/69/aa/24580a278de21fd7322635556334d9b535f1cbc00b0a3919447cdf464c65/cffi-2.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164bff1657b2a74f0b6d54e11c9b375bc97b931f2ca9c43fcf875838da1570dd", size = 184226, upload-time = "2026-07-06T21:32:11.196Z" },
{ url = "https://files.pythonhosted.org/packages/88/a9/02cae418ec4beb282ace11958d9d4737793439d561fadc7e6d56f2e2b354/cffi-2.1.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:c941bb58d5a6e1c3892d86e42927ed6c180302f07e6d395d08c416e594b98b46", size = 211107, upload-time = "2026-07-06T21:32:12.328Z" },
{ url = "https://files.pythonhosted.org/packages/3b/30/c806937ed5e4c2c7ac30d9d6b76b5dc57ff8b75d83800d9bb11a8253cf2a/cffi-2.1.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a016194dbe13d14ee9556e734b772d8d67b947092b268d757fd4290e3ba2dfc2", size = 218733, upload-time = "2026-07-06T21:32:13.67Z" },
{ url = "https://files.pythonhosted.org/packages/f9/cf/398272b8bbfd58aa314fda5a7f1cdbb26d1d78ae324a11211521315dd1f0/cffi-2.1.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:03e9810d18c646077e501f661b682fbf5dee4676048527ca3cffe66faa9960dd", size = 205543, upload-time = "2026-07-06T21:32:15.148Z" },
{ url = "https://files.pythonhosted.org/packages/45/ca/f91641185cdd90c36d317a9dc7f85e88ef8682d8b300977baff5e23c35d8/cffi-2.1.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:19c54ac121cad98450b4896fa9a43ee0180d57bc4bc911a33db6cab1efab6cd3", size = 205460, upload-time = "2026-07-06T21:32:16.479Z" },
{ url = "https://files.pythonhosted.org/packages/38/66/04781a77b411f0bb5b234d62c1814754ab75ebe455ccff1b08e8d7aae98f/cffi-2.1.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d433a51f1870e43a13b6732f92aaf540ff77c2015097c78556f75a2d6c030e0", size = 218760, upload-time = "2026-07-06T21:32:17.98Z" },
{ url = "https://files.pythonhosted.org/packages/d0/9a/bb1d5ed9c3fcae158e9f6391bf309c95d98c2ac37ed56573228471d0af5e/cffi-2.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3d7f118b5adbfdfead90c25822690b02bc8074fba949bb7858bec4ebd55adb43", size = 221230, upload-time = "2026-07-06T21:32:19.407Z" },
{ url = "https://files.pythonhosted.org/packages/41/aa/3c1409cdd26094efacd1c36c66e0a6eb9d4296e4fd4f9901b8b2042f4323/cffi-2.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c5f5df567f6eb216de69be06ce55c8b714090fae02b18a3b40da8163b8c5fa9c", size = 213524, upload-time = "2026-07-06T21:32:20.828Z" },
{ url = "https://files.pythonhosted.org/packages/fa/75/74dfb7c3fc6ebbd408038476bd4c1d7e925c62614e7b9c534ecc34218288/cffi-2.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:11b3fb55f4f8ad92274ed26705f65d8f91457de71f5380061eb6d125a768fecd", size = 220341, upload-time = "2026-07-06T21:32:21.9Z" },
{ url = "https://files.pythonhosted.org/packages/70/b6/9003c33a3e7d2c1306f5962e646457dcfe5a8cd8fce6bbe02d7af25db783/cffi-2.1.0-cp310-cp310-win32.whl", hash = "sha256:9d72af0cf10a76a600a9690078fe31c63b9588c8e86bf9fd353f713c84b5db0f", size = 174578, upload-time = "2026-07-06T21:32:23.073Z" },
{ url = "https://files.pythonhosted.org/packages/8a/26/710688310447531c7a22f857c7f79d9855ec18b03e04494ced723fb37e2f/cffi-2.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:fb62edb5bb52cca65fab91a63afa7561607120d26090a7e8fda6fb9f064726da", size = 185071, upload-time = "2026-07-06T21:32:24.671Z" },
]
[[package]]
name = "colorama"
version = "0.4.6"
@@ -207,44 +137,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/22/2c/68dec88ee26f96af2d4d8ee9d8567313a0826f4fd6bc5ae4527be670d354/copier-9.17.0-py3-none-any.whl", hash = "sha256:fe4e7b59faf4c0e1386eccbb79c6797e6df33ca20d5de49a8372571d8669f2ad", size = 65954, upload-time = "2026-07-13T14:33:03.301Z" },
]
[[package]]
name = "cryptography"
version = "49.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/1f/99/d1c90d6041656cc6ee229dc99cd67fd0cd5aec3c5f7d72fffc27cc750054/cryptography-49.0.0.tar.gz", hash = "sha256:f89660a348f4f78a92366240a61404e337586ef7f5909a2fef59ca88ef505493", size = 854345, upload-time = "2026-06-12T20:02:30.512Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/9b/22/adf66990e63584a68dfb50c24f48a125c07b1699899381c8151e63ed458c/cryptography-49.0.0-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:966fe0e9c67490071f14c0d2b1cb2dfb3023c5ce39457343931415f08382f2db", size = 4032100, upload-time = "2026-06-12T20:02:32.143Z" },
{ url = "https://files.pythonhosted.org/packages/09/41/3797cfaf69cae04a13ee78ebd83f0678d9c02b4779d21ce24445326f1a69/cryptography-49.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:36d1709f992593689b45bda411498d62c6e365f2ca00b84657d4dadd24de16db", size = 4692978, upload-time = "2026-06-12T20:01:21.305Z" },
{ url = "https://files.pythonhosted.org/packages/e6/8b/43011f7ebe515a8aa20d61f290a326cd890c2e738e16e59eaff8d9c3a412/cryptography-49.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0e959b578856a3924bc0cbb710fc12c387b9412a951389f3ca61704a9e25f325", size = 4716422, upload-time = "2026-06-12T20:01:48.566Z" },
{ url = "https://files.pythonhosted.org/packages/4a/91/01ce7303a4579e6d3a6abef01bd322848e9ea7a219adcabc5048b9033571/cryptography-49.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:53ecee2e23f7169b6117e99fc8a944e5e50f79e69758a83b52a00cb98ab2b2d2", size = 4700503, upload-time = "2026-06-12T20:02:47.091Z" },
{ url = "https://files.pythonhosted.org/packages/62/99/a2c95cf8293f07491e9e27c20cc4dcd18176d944e674679adeb1d0173fd6/cryptography-49.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:2eda353d8a27bcbcaa4cbed18994a74ab4d19a2ca897db188ea269ab9b71419b", size = 5309779, upload-time = "2026-06-12T20:02:08.987Z" },
{ url = "https://files.pythonhosted.org/packages/20/2c/0622f20ff02b2ef32558733443805dc82fd4c275be01b2d19d14676f3a1b/cryptography-49.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2afe9051da7ae7bd5905da5a949280c7d2bb75682e188f650a9d0f2756b834c6", size = 4749683, upload-time = "2026-06-12T20:02:03.335Z" },
{ url = "https://files.pythonhosted.org/packages/a3/5b/c5246635d5fd3b64e0d45ae10e99fd32fe9676a79915ccfe5a61ba9af1a5/cryptography-49.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:0b82e28ee398a386f0807bba7884d30f25218855690f45115831bcce5d90822c", size = 4337874, upload-time = "2026-06-12T20:02:54.323Z" },
{ url = "https://files.pythonhosted.org/packages/6d/88/05563c7fe2e914e87d1a536d06fe83e66b4e1d95cb593e05aea375531da8/cryptography-49.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:ccac2bfebc306b862133e3bb71f3f6ee8bb525240089b2d952e4144b3a6d5da7", size = 4700283, upload-time = "2026-06-12T20:01:34.822Z" },
{ url = "https://files.pythonhosted.org/packages/c4/b6/d7696e4e890d6ae1469935164c9e5215c557671cb78d6e3f458ccceaa632/cryptography-49.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:d0527ce944105f257f605a827d6ebead966c752038b6e8656abb9c5edee6fc68", size = 5265844, upload-time = "2026-06-12T20:01:24.09Z" },
{ url = "https://files.pythonhosted.org/packages/a9/3c/f3ad17eecc1a57b0ba236dc01f90e783c51f4a2f35f64777cc4f47a184b2/cryptography-49.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:cbc77da8c523d5abd028635ba850a6966fcee2c82e2bf65a41d1d8afe0f98be9", size = 4749290, upload-time = "2026-06-12T20:01:30.848Z" },
{ url = "https://files.pythonhosted.org/packages/4f/01/339573cf1023163a400b0b5d16f6d507de413b9f60be6fd1b77feeaf6737/cryptography-49.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b87e65d263b3e5d3bb92a57e2a6638e2f31110fa7aa890c7b2dbba42248d0a3f", size = 4834612, upload-time = "2026-06-12T20:01:29.246Z" },
{ url = "https://files.pythonhosted.org/packages/71/fd/577302e213a1be9468f92d1afef66fcf1ef83d516819d9992ca547f592bd/cryptography-49.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:66ec79c3904820572d7e987abdf304281f141d37ad9a489b8e97066e7b9b6459", size = 4980804, upload-time = "2026-06-12T20:01:42.853Z" },
{ url = "https://files.pythonhosted.org/packages/1f/09/f42b1d190c5ba75f72062a387f8030d1d75f6ab035788f1d9c4b01de6525/cryptography-49.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:e5dfc1e64de5677cec922ffa8da89c546d0415bf6efdf081842e5d44c84e1f0e", size = 3810026, upload-time = "2026-06-12T20:02:39.262Z" },
{ url = "https://files.pythonhosted.org/packages/19/2a/5bb823f5bedcf80718cea7fbc95ec5515cca3769633c4b01a32be7f30e7c/cryptography-49.0.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:ec5e529fb80935c94fe7b729f9972b50e351a0e6b50aa294fd5cabb109fcc29a", size = 4025947, upload-time = "2026-06-12T20:01:25.745Z" },
{ url = "https://files.pythonhosted.org/packages/3d/df/40577043ca124e17012f408ddddaeb213b856336ac82ddb3bc915f39e29f/cryptography-49.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f78ff2c9ed8dc2d036b0f4d640e22522213d047c1b14e61205a7e55c80a494d4", size = 4692429, upload-time = "2026-06-12T20:01:53.628Z" },
{ url = "https://files.pythonhosted.org/packages/2c/99/2d13299eb3dd27b02dcfaafcc91d6b5cb3329f7cbd6d8f51921acd566c1a/cryptography-49.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:35b151772baff2c74cba7fa290ceaff4c3b11c0c881eb93eb5dbc05a7cfbba18", size = 4700968, upload-time = "2026-06-12T20:02:45.383Z" },
{ url = "https://files.pythonhosted.org/packages/a5/4d/9c0cd02f95e2602dd5e563da149ee0830abef3537be8b34dc56281ebe27a/cryptography-49.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:0f21641cf4b30fca7aee061ced0ec7ad7b073518088b7c9969a297c0ae796c69", size = 4697758, upload-time = "2026-06-12T20:01:41.13Z" },
{ url = "https://files.pythonhosted.org/packages/24/01/186c825898477d77e2324d5360fefe622ff1d8d1963ec0554e2cada8ec77/cryptography-49.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:9e82dcc8e56052715fb18b2429e3bca4823b1629136a2084fc45a9a5cecb9b64", size = 5298863, upload-time = "2026-06-12T20:02:24.579Z" },
{ url = "https://files.pythonhosted.org/packages/b8/7b/62cbbab75d0659865bf0273790031544a0b16c8072d258f9428dcd8190dc/cryptography-49.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:6f2debedf9ca60cf1d5bd466475638af5130f89965605cd818484d19987d3a21", size = 4735983, upload-time = "2026-06-12T20:01:50.14Z" },
{ url = "https://files.pythonhosted.org/packages/6c/72/3e798c064bc39e471008075d0f9bc9daf77a80879c092e4a8e170c585ed4/cryptography-49.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:8c25ceb16df5b9435f3f6a9829204985b0e0cbee3b48aacd432c7d2c850b44d9", size = 4334173, upload-time = "2026-06-12T20:01:44.743Z" },
{ url = "https://files.pythonhosted.org/packages/f0/ee/6fca21d1ac73e06f8bef71940abfd4d2f6472b4bca284d770f32bd4086f6/cryptography-49.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:28d8b15e6275f12c8a207dc309dfa957903c927d08d0cc937ee3f63f200693cc", size = 4697298, upload-time = "2026-06-12T20:02:20.918Z" },
{ url = "https://files.pythonhosted.org/packages/67/d0/a5fcd3515f0bae49a7b6d0413cc1bdccdcc1fc0047037a0d480642cdc5d6/cryptography-49.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:6fc361c34fb6aac015ce19435876635e5c6d21db31998b0920f675f131e043b8", size = 5254338, upload-time = "2026-06-12T20:02:22.737Z" },
{ url = "https://files.pythonhosted.org/packages/a0/84/84fe36f19caf857d61cb7fc9c63035a47ffabd84ea12d1d393148efa3615/cryptography-49.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:2400ef9c9e2299a25614eb1dea3db54a69b1349efd043bfac9c67630d136df36", size = 4735650, upload-time = "2026-06-12T20:02:41.389Z" },
{ url = "https://files.pythonhosted.org/packages/6c/a0/db537264e234f7273a73ec020873d6d6b39dfd8a53db78b550ca8320440e/cryptography-49.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:67e1d20ad9ef3a563c59ef22e7a8a0b8210bd26604369ea4a30a7c66aefe504e", size = 4834820, upload-time = "2026-06-12T20:01:51.847Z" },
{ url = "https://files.pythonhosted.org/packages/93/77/8df9eb486495979bccecd1062e2eaf435250e84437040295b57d09048b0b/cryptography-49.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:42b0684e0e40cf26122427802486f6d93aea593612603a94fbf260c7eb1e9c1b", size = 4967968, upload-time = "2026-06-12T20:02:12.524Z" },
{ url = "https://files.pythonhosted.org/packages/c2/e6/f60198ea8d9dfa15fff9ed4ca02ce362f6eadd9ba757dcc50634c4257b63/cryptography-49.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:026ac7423e6fa66872d3bf889be5974507da3944f866f704fa200eadacd00001", size = 3785547, upload-time = "2026-06-12T20:02:26.847Z" },
]
[[package]]
name = "decorator"
version = "5.3.1"
@@ -406,15 +298,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/72/b9/313e8f2f2e9517ae050a692ae7b3e4b3f17cc5e6dfea0db51fe14e586580/jinja2_ansible_filters-1.3.2-py3-none-any.whl", hash = "sha256:e1082f5564917649c76fed239117820610516ec10f87735d0338688800a55b34", size = 18975, upload-time = "2022-06-30T14:08:49.571Z" },
]
[[package]]
name = "lockfile"
version = "0.12.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/17/47/72cb04a58a35ec495f96984dddb48232b551aafb95bde614605b754fe6f7/lockfile-0.12.2.tar.gz", hash = "sha256:6aed02de03cba24efabcd600b30540140634fc06cfa603822d508d5361e9f799", size = 20874, upload-time = "2015-11-25T18:29:58.279Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c8/22/9460e311f340cb62d26a38c419b1381b8593b0bb6b5d1f056938b086d362/lockfile-0.12.2-py2.py3-none-any.whl", hash = "sha256:6c3cb24f344923d30b2785d5ad75182c8ea7ac1b6171b08657258ec7429d50fa", size = 13564, upload-time = "2015-11-25T18:29:51.462Z" },
]
[[package]]
name = "markdown-it-py"
version = "4.2.0"
@@ -597,15 +480,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842, upload-time = "2024-07-21T12:58:20.04Z" },
]
[[package]]
name = "pycparser"
version = "3.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" },
]
[[package]]
name = "pydantic"
version = "2.13.4"
@@ -748,18 +622,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/5a/cc/06253936f4a7fa2e0f48dfe6d851d9c56df896a9ab09ac019d70b760619c/pytest_mock-3.15.1-py3-none-any.whl", hash = "sha256:0a25e2eb88fe5168d535041d09a4529a188176ae608a6d249ee65abc0949630d", size = 10095, upload-time = "2025-09-16T16:37:25.734Z" },
]
[[package]]
name = "python-daemon"
version = "3.1.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "lockfile" },
]
sdist = { url = "https://files.pythonhosted.org/packages/3d/37/4f10e37bdabc058a32989da2daf29e57dc59dbc5395497f3d36d5f5e2694/python_daemon-3.1.2.tar.gz", hash = "sha256:f7b04335adc473de877f5117e26d5f1142f4c9f7cd765408f0877757be5afbf4", size = 71576, upload-time = "2024-12-03T08:41:07.843Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/45/3c/b88167e2d6785c0e781ee5d498b07472aeb9b6765da3b19e7cc9e0813841/python_daemon-3.1.2-py3-none-any.whl", hash = "sha256:b906833cef63502994ad48e2eab213259ed9bb18d54fa8774dcba2ff7864cec6", size = 30872, upload-time = "2024-12-03T08:41:03.322Z" },
]
[[package]]
name = "python-dotenv"
version = "1.2.2"
@@ -798,15 +660,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/3c/26/1062c7ec1b053db9e499b4d2d5bc231743201b74051c973dadeac80a8f43/questionary-2.1.1-py3-none-any.whl", hash = "sha256:a51af13f345f1cdea62347589fbb6df3b290306ab8930713bfae4d475a7d4a59", size = 36753, upload-time = "2025-08-28T19:00:19.56Z" },
]
[[package]]
name = "resolvelib"
version = "1.0.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/ce/10/f699366ce577423cbc3df3280063099054c23df70856465080798c6ebad6/resolvelib-1.0.1.tar.gz", hash = "sha256:04ce76cbd63fded2078ce224785da6ecd42b9564b1390793f64ddecbe997b309", size = 21065, upload-time = "2023-03-09T05:10:38.292Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d2/fc/e9ccf0521607bcd244aa0b3fbd574f71b65e9ce6a112c83af988bbbe2e23/resolvelib-1.0.1-py2.py3-none-any.whl", hash = "sha256:d2da45d1a8dfee81bdd591647783e340ef3bcb104b54c383f70d422ef5cc7dbf", size = 17194, upload-time = "2023-03-09T05:10:36.214Z" },
]
[[package]]
name = "rich"
version = "15.0.0"
+15 -12
View File
@@ -1,4 +1,4 @@
next_available_ioc_port: 50029
next_available_ioc_port: 50030
services:
- name: Master
ioc_port: 50000
@@ -54,36 +54,39 @@ services:
- name: TuneFBy
ioc_port: 50017
status: active
- name: OpticsFF-X02S
- name: Params
ioc_port: 50018
status: inactive
- name: OpticsFF-X03M
- name: OpticsFF-X02S
ioc_port: 50019
status: inactive
- name: OpticsFF-X04S
- name: OpticsFF-X03M
ioc_port: 50020
status: inactive
- name: OpticsFF-X05L
- name: OpticsFF-X04S
ioc_port: 50021
status: inactive
- name: OpticsFF-X06S
- name: OpticsFF-X05L
ioc_port: 50022
status: inactive
- name: OpticsFF-X07M
- name: OpticsFF-X06S
ioc_port: 50023
status: inactive
- name: OpticsFF-X08S
- name: OpticsFF-X07M
ioc_port: 50024
status: inactive
- name: OpticsFF-X09L
- name: OpticsFF-X08S
ioc_port: 50025
status: inactive
- name: OpticsFF-X10S
- name: OpticsFF-X09L
ioc_port: 50026
status: inactive
- name: OpticsFF-X11M
- name: OpticsFF-X10S
ioc_port: 50027
status: inactive
- name: OpticsFF-X12S
- name: OpticsFF-X11M
ioc_port: 50028
status: inactive
- name: OpticsFF-X12S
ioc_port: 50029
status: inactive
-36
View File
@@ -1,36 +0,0 @@
# Ansible
## Running a playbook manually
Set vars on command line:
```
ansible-playbook ansible/playbooks/add-new-service.yml \
-i ansible/hosts.yml \
--extra-vars="repo_root=${{ github.workspace }} agebd_env=dev service_name_lower=<service dir name>" \
-v
```
### Install an IOC
```
ansible-playbook ansible/playbooks/ioc-install.yml \
-i ansible/hosts.yml \
--extra-vars="agebd_env=dev branch_name=feature/add-service-playground service_name_lower=playground ioc_port=50003" \
-v
```
### Start an IOC
```
ansible-playbook ansible/playbooks/ioc-start.yml \
-i ansible/hosts.yml \
--extra-vars="service_name_lower=playground ioc_port=50003" \
-v
```
### Restart an IOC
```
ansible-playbook ansible/playbooks/ioc-restart.yml \
-i ansible/hosts.yml \
--extra-vars="agebd_env=dev branch_name=feature/add-service-playground service_name_lower=playground" \
-v
```
+29 -2
View File
@@ -3,9 +3,11 @@
## Hosts
- `sls-vserv-bd-01(-dev)`
- Runs shellbox commands (and `ioc install ...`?)
- Runs the shellbox commands, which the runner triggers over `ssh`
(see `.gitea/scripts/ioc-start.sh`)
- `sls-vserv-bd-hla01(-dev)`
- Runs everything else
- Runs the runner itself, and therefore everything else: the deployments
to `/sls/bd/hla/<env>/` and the `ioc ...` commands
## Install the runner
@@ -191,3 +193,28 @@ The gitea actions run as user `svcusr-sls2hla`
### #TODO: this is a temporary workaround
I created an ssh key on `sls-vserv-bd-hla01-dev` (where the runner runs), and added the public key to my
gitea profile ssh keys. That way the `svcusr` can clone the repos I can clone.
## Setup `sls-vserv-bd-01-dev`
On `sls-vserv-bd-hla01-dev`
```
sudo su - svcusr-sls2hla
cat ~/.ssh/id_ed25519.pub
```
If it does not exist yet:
```
sudo su - svcusr-sls2hla
ssh-keygen -t ed25519 -N "" -C "svcusr-sls2hla@sls-vserv-bd-hla01-dev" -f ~/.ssh/id_ed25519
cat ~/.ssh/id_ed25519.pub
```
```
ssh sls-vserv-bd-01-dev # lands as your own user
sudo install -d -m 700 -o svcusr-sls2hla -g unx-nogroup /home/svcusr-sls2hla /home/svcusr-sls2hla/.ssh
echo '<paste the pubkey>' | sudo tee /home/svcusr-sls2hla/.ssh/authorized_keys
sudo chown svcusr-sls2hla:unx-nogroup /home/svcusr-sls2hla/.ssh/authorized_keys
sudo chmod 600 /home/svcusr-sls2hla/.ssh/authorized_keys
```
+27 -1
View File
@@ -1,6 +1,8 @@
# IOC
The following instructions explain the basics of starting and managing IOCs on the IOC host.
The following instructions explain the basics of starting and managing IOCs on the IOC host.
They describe the manual steps. In practice the CI/CD runner does this for you, see
[Automation](#automation) below.
- TODO: some iocs not associated to a service, where to put?
- TODO: need to dev/prod template them too
@@ -92,3 +94,27 @@ sudo shellbox start 50045 # start ioc
sudo shellbox log 50045 -f # log file (tail)
exit # exit ssh
```
## Automation
Installing, starting and restarting IOCs is done by the gitea runner, not by hand:
- `.gitea/scripts/ioc-install.sh <service-name> <dev|prod>`
- `.gitea/scripts/ioc-start.sh <service-name> <dev|prod>` (shellbox, over `ssh`)
- `.gitea/scripts/ioc-restart.sh <service-name> <dev|prod>`
The scripts operate on the deployed IOC files in
`/sls/bd/hla/<env>/services/<service-name>/current/ioc`, so a service has to be deployed
before its IOC can be installed.
They run automatically when a new service is added (`.gitea/workflows/add-new-service.yml`).
## Manual CICD Workflow trigger
IOC commands can be triggered manually for any service (or `all` services) through the `Ioc` workflow
(`.gitea/workflows/ioc.yml`) in the gitea Actions tab:
```
Gitea Repo > Actions tab > Ioc (row on left side) > Run Workflow
```
+27 -2
View File
@@ -62,9 +62,34 @@ Type "help", "copyright", "credits" or "license" for more information.
## Systemd Services
When ssh-ing into a machine, `/etc/profile.d/*` automatically gets sourced.
When ssh-ing into a machine, `/etc/profile.d/*` automatically gets sourced. And thus we have:
```
[sls-vserv-bd-hla01-dev ~]$ env | grep EPICS
EPICS_CA_ADDR_LIST=sls-cagw.psi.ch:5062
```
When running a `systemd` service, this does not happen.
Note -- it seems that the following does not work as expected:
```
That's why we need to source `/etc/profile.d/cas_12_epics.sh` in our startup script
to make sure that `EPICS` is configured properly.
to make sure that `EPICS` is configured properly.
```
After sourcing the script, we still have: `EPICS_CA_ADDR_LIST=` (i.e. the script does not set `EPICS_CA_ADDR_LIST=sls-cagw.psi.ch:5062`) in `systemd` service.
Whereas in the shell `unset EPICS_CA_ADDR_LIST` and then `. /etc/profile/cas_12_epics.sh` properly sets `EPICS_CA_ADDR_LIST=sls-cagw.psi.ch:5062`.
Check which env variables a `systemd` service is running with:
```
[sls-vserv-bd-hla01-dev ~]$ systemctl --user restart AGEBD-SERVICE-SCRUBBING.service && sleep 1 && PID=$(systemctl --user show -p MainPID --value AGEBD-SERVICE-SCRUBBING.service)
[sls-vserv-bd-hla01-dev ~]$ tr '\0' '\n' < /proc/$PID/environ | grep EPICS
EPICS_CAS_IGNORE_ADDR_LIST=
EPICS_CA_ADDR_LIST= 129.129.146.255
EPICS_HOST_ARCH=linux-x86_64
EPICS_BASE_HOST_BIN=/opt/gfa/python-3.10/20220602/epics/bin/linux-x86_64
PYEPICS_LIBCA=/opt/gfa/python-3.10/20220602/epics/lib/linux-x86_64/libca.so
EPICS_BASE_VERSION=7.0.5.0
EPICS_BASE=/opt/gfa/python-3.10/20220602/epics
```
+30 -6
View File
@@ -23,6 +23,24 @@ agebd --help
agebd service add --help
```
**This command has side effects you should know about before running it.** It requires a
clean working tree, then it:
1. Assigns the service the next free IOC port and registers it in
[`config/services_registry.yml`](https://gitea.psi.ch/sls/hla_framework_bd/src/branch/main/config/services_registry.yml).
2. Adds the service to `AGEBD-CPCL-MASTER`'s IOC pattern file, so it gets the standard
ALH lifecycle PVs (`AGEBD-ALH$(SUFFIX):$(SERVICE)-*`) and appears in the `ServiceManager`
GUI screen.
3. Appends a row to [`iocs_overview.md`](ioc/iocs_overview.md).
4. Creates a new branch **`feature/add-service-<name>`**, scaffolds the new service's files
from the templates, and **pushes the branch automatically** — you don't run `git push`
yourself.
Pushing that branch triggers the `Add new service` Gitea Actions workflow
(`.gitea/workflows/add-new-service.yml`), which deploys `master`, installs/restarts its IOC,
deploys your new service, and installs/starts its own IOC — all in `dev`. Check the Actions
tab for that run to confirm it succeeded before doing anything else with the new service.
## Environments
The services are deployed to 2 environments:
@@ -42,17 +60,23 @@ We distinguish between
#### PVs - Service Specific
You create them, see (# TODO: docs...)
These are defined as EPICS records in your service's own `services/<name>/current/ioc/*.template`
file, instantiated by `services/<name>/current/ioc/AGEBD-CPCL-<NAME>_main.subs`.
`prod` and `dev` PVs automatically get created.
`prod` and `dev` PVs automatically get created: at deploy time, `{{ agebd_env_suffix_upper }}`/
`{{ agebd_env_suffix_lower }}` placeholders in those files are substituted with `-DEV`/`-dev`
in `dev`, or left empty in `prod` (see `.gitea/scripts/deploy-service.sh`).
In your code, you only use the `prod` name of a PV. The framework will automatically use the
`dev` name/PV when a service runs in the `dev` environment.
In your code, you only use the `prod` name of a PV, e.g. `PV("AGEBD-MYSERVICE:MYPVNAME")`.
The `PVLink`/`DevPVLink` framework (`packages/agebd/src/agebd/pv.py`) automatically appends
`-DEV` when the service runs in `dev` — but **only** for PVs belonging to your own service,
plus `AGEBD-ALH`/`AGEBD-MASTER`. See "External" below for PVs owned by another service.
#### PVs - External
The framework will use the same PV in `prod` and `dev`, i.e. there is no `dev` PV.
However, in `dev` you can only read from the PV, whereas in `prod` you can also write to it.
PVs owned by another service (including another AGEBD service, e.g. `AGEBD-PARAMS:...` used
from a different service) or by real accelerator hardware (e.g. `ARS01-MOCT-...`) are used
verbatim — no `-DEV` suffix is ever added, even in `dev`.
#### PVs - Examples
+15 -8
View File
@@ -2,22 +2,29 @@
## Launch
```
cd <repo-root>
```
In `prod`:
`ssh sls-vserv-bd-hla01`
```
./bin/sls_hla_launch_gui.sh qt/A_BD_InjectionGuard.ui
cd /sls/bd/hla/prod/qt
../bin/sls_hla_launch_gui.sh A_BD_ServiceManager.ui
```
In `dev`:
`ssh sls-vserv-bd-hla01-dev`
```
./bin/sls_hla_launch_gui.sh qt/A_BD_InjectionGuard.ui dev
cd /sls/bd/hla/dev/qt
../bin/sls_hla_launch_gui.sh A_BD_ServiceManager.ui "dev"
```
If you need to pass another macro variable in `dev`, you will need to set `AGEBD_ENV_SUFFIX` explicitely:
Any extra arguments are passed straight through to `caqtdm`, e.g. to set a macro:
```
./bin/sls_hla_launch_gui.sh qt/A_BD_Tune.ui dev -macro "AGEBD_ENV_SUFFIX=-DEV, OTHER_ENV_VAR=HEYHEY"
../bin/sls_hla_launch_gui.sh A_BD_ServiceManager.ui "dev" -macro "OTHER_ENV_VAR=HEYHEY"
```
**Note:** launching straight from a git checkout, as above, uses the raw `.ui` files as
committed - including the literal `{{ agebd_env_suffix_upper }}`/`{{ agebd_env_suffix_lower }}`
placeholders in their PV names. Those only get substituted with `-DEV`/`-dev` (or emptied out,
in `prod`) at deploy time, by `.gitea/workflows/deploy.yml`'s `"Qt - deploy"` step, on the
*deployed* copy at `/sls/bd/hla/<env>/qt/`. If widgets show up blank or unresponsive, check
whether you're accidentally pointing at the raw checkout instead of the deployed `.ui` file.
+21 -14
View File
@@ -1,11 +1,10 @@
# Overview of IOCs for Beam Dynamics
The ports are configured in the [service registry](../../../config/services_registry.yml)
The ports are configured in the [service registry](https://gitea.psi.ch/sls/hla_framework_bd/src/branch/main/config/services_registry.yml)
| IOC NAME | Description |
|---|---|
| AGEBD-CPCL-MASTER | IOC providing PVs for the service master application and IOC enabling to monitor the health status of BD High Level Application Services with the alarm handler ALH |
| AGEBD-CPCL-ALH | IOC enabling to monitor the health status of BD High Level Application Services with the alarm handler ALH |
| AGEBD-CPCL-MASTER | IOC providing PVs for the service master application, and monitoring the health status of BD High Level Application Services via the alarm handler ALH. ALH is not a separate IOC - its `AGEBD-ALH$(SUFFIX):$(SERVICE)-*` PVs are defined directly in `MASTER.template`, one row per registered service. |
| AGEBD-CPCL-TUNE | IOC providing PVs for the tune measurement |
| AGEBD-CPCL-SCRUBBING | IOC providing PVs for the vacuum scrubbing service |
| AGEBD-CPCL-NTURNS | IOC providing PVs for the DBPM3 stage0 and stage1 nr. of turns calculation service |
@@ -15,15 +14,23 @@ The ports are configured in the [service registry](../../../config/services_regi
| AGEBD-CPCL-TUNEBUMP | IOC providing PVs for the tune bump service application |
| AGEBD-CPCL-PLOTS | IOC providing Buffers of PVs for plots |
| AGEBD-CPCL-ORBITBUMP | IOC providing PVs for orbit bumps for the beamlines |
| AGEBD-CPCL-TAUBPM | IOC providing PVs for the lifetime measurement |
| AGEBD-CPCL-TAUBPM | IOC providing PVs for the lifetime measurement (BPM based) |
| AGEBD-CPCL-TAUPCT | IOC providing PVs for PCT based lifetime measurement |
| AGEBD-CPCL-TOPUPTOOL | IOC providing PVs for the top up tool |
| AGEBD-CPCL-TUNEFBX | IOC providing PVs for the top up tool |
# TODO:
| IOC NAME | Description |
|---|---|
| AGEBD-CPCL-PARAMS | IOC providing PVs for machine parameters |
| AGEBD-CPCL-PARAMS | IOC providing PVs for PCT base lifetime measurement |
| AGEBD-CPCL-SHIFTTOOL | |
| AGEBD-CPCL-DBPM3CURR | |
| AGEBD-CPCL-DBPM3 | IOC providing PVs for the DBPM3 stage2 average current calculation service |
| AGEBD-CPCL-TUNEFBX | IOC providing PVs for the horizontal tune feedback |
| AGEBD-CPCL-TUNEFBY | IOC providing PVs for the vertical tune feedback |
| AGEBD-CPCL-PARAMS | IOC providing PVs for machine parameters (e.g. injection state, lifetime, current limit) |
| AGEBD-CPCL-DBPM3CURR | IOC providing PVs for the DBPM3 stage2 average current calculation service |
| AGEBD-CPCL-SHIFTTOOL | IOC providing PVs for the shift log / handover tool |
| AGEBD-CPCL-BEAMTRANSFERCHECKS | IOC providing PVs for beam transfer checks |
| AGEBD-CPCL-OPTICSFF-X02S | IOC providing PVs for optics feed-forward on the I-TOMCAT beamline |
| AGEBD-CPCL-OPTICSFF-X03M | IOC providing PVs for optics feed-forward on the ADRESS beamline |
| AGEBD-CPCL-OPTICSFF-X04S | IOC providing PVs for optics feed-forward on the ADDAMS beamline |
| AGEBD-CPCL-OPTICSFF-X05L | IOC providing PVs for optics feed-forward on the QUEST beamline |
| AGEBD-CPCL-OPTICSFF-X06S | IOC providing PVs for optics feed-forward on the PXI beamline |
| AGEBD-CPCL-OPTICSFF-X07M | IOC providing PVs for optics feed-forward on the PHOENIX/XTREME beamline |
| AGEBD-CPCL-OPTICSFF-X08S | IOC providing PVs for optics feed-forward on the MicroXAS beamline |
| AGEBD-CPCL-OPTICSFF-X09L | IOC providing PVs for optics feed-forward on the OPERA beamline |
| AGEBD-CPCL-OPTICSFF-X10S | IOC providing PVs for optics feed-forward on the PXII beamline |
| AGEBD-CPCL-OPTICSFF-X11M | IOC providing PVs for optics feed-forward on the SIM beamline |
| AGEBD-CPCL-OPTICSFF-X12S | IOC providing PVs for optics feed-forward on the cSAXS beamline |
+7 -1
View File
@@ -12,6 +12,9 @@ class PVLink:
def __init__(self, pvname, **kwargs):
self.link = self.connect(pvname, **kwargs)
self.value = np.nan
self.timestamp = time()
# sleep(0.1)
# print(self.pvname, self.link.connected)
@@ -60,7 +63,10 @@ class PVLink:
sleep(5)
self.link.reconnect()
try:
self.link.reconnect()
except Exception:
print("{:} reconnect attempt raised an error, retrying ...".format(self.pvname))
sleep(1)
+82 -80
View File
@@ -9,107 +9,109 @@ from agebd.service.base import BaseService
logger = logging.getLogger(__name__)
class CallbackRunner:
class _BaseRunner:
"""
Shared control loop: poll abort/onoff PVs, dispatch to the active or
paused tick, ping the alive watchdog, and stop cleanly on exception.
"""
def __init__(self, service: BaseService):
self.svc = service
self.pvs = service.pvs
def start(self):
logger.debug(f"Starting {type(self).__name__} for service: {self.svc.name}")
while not self.pvs.abort.get():
try:
# if service is not paused
if self.pvs.onoff.get():
self._tick_active()
# if service is paused
else:
self._tick_paused()
# inform watchdog of service alive state
self.pvs.alive.put(0)
except Exception as e:
self.svc.exception = e
self.svc.crashed = True
break
self._on_stop()
self.svc.finalize_termination()
def _tick_active(self):
raise NotImplementedError
def _tick_paused(self):
raise NotImplementedError
def _on_stop(self):
pass
class CallbackRunner(_BaseRunner):
"""
Executes service updates asynchronously, triggered by EPICS PV events.
"""
def __init__(self, service: BaseService):
self.svc = service
self.pvs = service.pvs
self._callback_active = False
def _tick_active(self):
# add callback for triggering the loop if not active
if not self.svc.CallbackActive:
self.svc.transition_to_running()
self.pvs.CallbackPV.add_callback(callback=self.svc.CallbackFun, index=1)
self.svc.CallbackActive = 1
def start(self):
logger.debug(f"Starting CallbackRunner for service: {self.svc.name}")
while not self.pvs.abort.get():
try:
# if service is not paused
if self.pvs.onoff.get():
# add callback for triggering the loop if not active
if not self._callback_active:
self.svc.transition_to_running()
self.pvs.CallbackPV.add_callback(callback=self.svc.CallbackFun, index=1)
self.svc.CallbackActive = 1
# run mainloop of service
self.svc.update()
# run mainloop of service
self.svc.update()
# inform watchdog of service run state
self.pvs.running.put(0)
# inform watchdog of service run state
self.pvs.running.put(0)
# wait for callback to fire
poll(evt=1.0e-5, iot=0.1)
# wait for callback to fire
poll(evt=1.0e-5, iot=0.1)
def _tick_paused(self):
# remove callback for triggering the loop
if self.svc.CallbackActive:
self.svc.transition_to_paused()
self.pvs.CallbackPV.remove_callback(index=1)
self.svc.CallbackActive = 0
self.svc.CallbackFired = 0
# if service is paused
else:
# remove callback for triggering the loop
if self.svc.CallbackActive:
self.svc.transition_to_paused()
self.pvs.CallbackPV.remove_callback(index=1)
self.svc.CallbackActive = 0
self.svc.CallbackFired = 0
# wait until service is unpaused
sleep(0.1)
# inform watchdog of service alive state
self.pvs.alive.put(0)
except Exception as e:
self.svc.exception = e
self.svc.crashed = True
break
# wait until service is unpaused
sleep(0.1)
def _on_stop(self):
# remove callback(s) from event(s)
self.pvs.CallbackPV.remove_callback(index=1)
self.svc.finalize_termination()
class PeriodicRunner:
class PeriodicRunner(_BaseRunner):
"""
Executes service updates at a fixed frequency.
"""
def __init__(self, service: BaseService):
self.svc = service
self.pvs = service.pvs
def _tick_active(self):
if not self.svc.status == ServiceStatus.RUNNING:
self.svc.transition_to_running()
def start(self):
logger.debug(f"Starting PeriodicRunner for service: {self.svc.name}")
while not self.pvs.abort.get():
try:
# if service is not paused
if self.pvs.onoff.get():
if not self.svc.status == ServiceStatus.RUNNING:
self.svc.transition_to_running()
# run mainloop of service
self.svc.update()
# run mainloop of service
self.svc.update()
# inform watchdog of service run state
self.pvs.running.put(0)
# inform watchdog of service run state
self.pvs.running.put(0)
# wait for timer
sleep(self.svc.sleep_interval)
# wait for timer
sleep(self.svc.sleep_interval)
def _tick_paused(self):
if not self.svc.status == ServiceStatus.PAUSED:
self.svc.transition_to_paused()
# if service is paused
else:
if not self.svc.status == ServiceStatus.PAUSED:
self.svc.transition_to_paused()
self.svc.CallbackFired = 0
self.svc.CallbackFired = 0
# wait until service is unpaused
sleep(0.1)
# inform watchdog of service alive state
self.pvs.alive.put(0)
except Exception as e:
self.svc.exception = e
self.svc.crashed = True
break
self.svc.finalize_termination()
# wait until service is unpaused
sleep(0.1)
+1 -2
View File
@@ -1,6 +1,6 @@
from agebd.pv import PVLink
# TODO: remove: cicd test 54
# TODO: remove: cicd test 58
class BasePVs:
@@ -16,4 +16,3 @@ class BasePVs:
self.abort = pv_factory(f'AGEBD-MASTER:{svc}-ABORT.VAL')
self.abortreq = pv_factory(f'AGEBD-MASTER:{svc}-ABORT-REQ.VAL')
# fmt: on
+1
View File
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--TODO: remove: cicd test 57-->
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
+1
View File
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--TODO: remove: cicd test 57-->
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
+1
View File
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--TODO: remove: cicd test 57-->
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
+1
View File
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--TODO: remove: cicd test 57-->
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
+1
View File
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--TODO: remove: cicd test 57-->
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
+1
View File
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--TODO: remove: cicd test 57-->
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
+1
View File
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--TODO: remove: cicd test 57-->
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
+1
View File
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--TODO: remove: cicd test 57-->
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
+1
View File
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--TODO: remove: cicd test 57-->
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
+1
View File
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--TODO: remove: cicd test 57-->
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
+1
View File
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--TODO: remove: cicd test 57-->
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
+1
View File
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--TODO: remove: cicd test 57-->
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
+1
View File
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--TODO: remove: cicd test 57-->
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
+1
View File
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--TODO: remove: cicd test 57-->
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
+1
View File
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--TODO: remove: cicd test 57-->
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
+1
View File
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--TODO: remove: cicd test 57-->
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
+2 -2
View File
@@ -986,7 +986,7 @@ caLabel[statusTip="Operation"]{
<bool>false</bool>
</property>
<property name="scriptCommand">
<string notr="true">ssh svcusr-sls2hla@sls-vserv-bd-hla01 systemctl --user restart AGEBD-SERVICE-MASTER.service</string>
<string notr="true">ssh svcusr-sls2hla@sls-vserv-bd-hla01{{ agebd_env_suffix_lower }} systemctl --user restart AGEBD-SERVICE-MASTER.service</string>
</property>
</widget>
<widget class="caScriptButton" name="cascriptbutton_2">
@@ -1015,7 +1015,7 @@ caLabel[statusTip="Operation"]{
<bool>false</bool>
</property>
<property name="scriptCommand">
<string notr="true">terminator -e "ssh svcusr-sls2hla@sls-vserv-bd-hla01"</string>
<string notr="true">terminator -e "ssh svcusr-sls2hla@sls-vserv-bd-hla01{{ agebd_env_suffix_lower }}"</string>
</property>
</widget>
</widget>
+1
View File
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--TODO: remove: cicd test 57-->
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
+1
View File
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--TODO: remove: cicd test 57-->
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
+1
View File
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--TODO: remove: cicd test 57-->
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
+1
View File
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--TODO: remove: cicd test 57-->
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
+1
View File
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--TODO: remove: cicd test 57-->
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
+1
View File
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--TODO: remove: cicd test 57-->
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
+1
View File
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--TODO: remove: cicd test 57-->
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
@@ -6,7 +6,7 @@ from agebd.utils import init_logging
from agebd_dbpm3curr import PVs, Service
from agebd_dbpm3curr.service import SERVICE_NAME
# TODO: remove: cicd test 54
# TODO: remove: cicd test 58
def main(
@@ -6,7 +6,7 @@ from agebd.utils import init_logging
from agebd_injectionguard import PVs, Service
from agebd_injectionguard.service import SERVICE_NAME
# TODO: remove: cicd test 54
# TODO: remove: cicd test 58
def main(
@@ -6,7 +6,7 @@ from agebd.utils import init_logging
from agebd_master import PVs, Service
from agebd_master.service import SERVICE_NAME
# TODO: remove: cicd test 54
# TODO: remove: cicd test 58
def main(
+6 -1
View File
@@ -175,6 +175,11 @@ record(stringout, "AGEBD-ALH$(SUFFIX):$(SERVICE)-VERSION"){
field(ASG, "PROTECTED")
}
record(stringout, "AGEBD-ALH$(SUFFIX):$(SERVICE)-VERSION-DEPLOYED"){
field(DESC, "$(SERVICE) deployed version")
field(ASG, "PROTECTED")
}
record(bo, "AGEBD-ALH$(SUFFIX):$(SERVICE)-ONOFF") {
field(DESC, "pause/start service")
field(ASG, "PROTECTED")
@@ -202,4 +207,4 @@ record (bo, "AGEBD-ALH$(SUFFIX):$(SERVICE)-GUIRELOAD"){
field(ZNAM, "idle")
field(ONAM, "reload")
field(HIGH, "0.5")
}
}
@@ -6,6 +6,8 @@ from agebd.utils import init_logging
from agebd_nturns import PVs, Service
from agebd_nturns.service import SERVICE_NAME
# TODO: remove: cicd test 58
def main(
log_level: LogLevel = typer.Option(
@@ -6,7 +6,7 @@ from agebd.utils import init_logging
from agebd_plots import PVs, Service
from agebd_plots.service import SERVICE_NAME
# TODO: remove: cicd test 54
# TODO: remove: cicd test 58
def main(
@@ -6,7 +6,7 @@ from agebd.utils import init_logging
from agebd_postmortemlog import PVs, Service
from agebd_postmortemlog.service import SERVICE_NAME
# TODO: remove: cicd test 54
# TODO: remove: cicd test 58
def main(
@@ -6,7 +6,7 @@ from agebd_scrubbing import PVs, Service
from agebd_scrubbing.runner import Runner
from agebd_scrubbing.service import SERVICE_NAME
# TODO: remove: cicd test 54
# TODO: remove: cicd test 58
def main(
@@ -6,7 +6,7 @@ from agebd.utils import init_logging
from agebd_shifttool import PVs, Service
from agebd_shifttool.service import SERVICE_NAME
# TODO: remove: cicd test 54
# TODO: remove: cicd test 58
def main(
@@ -6,7 +6,7 @@ from agebd.utils import init_logging
from agebd_taubpm import PVs, Service
from agebd_taubpm.service import SERVICE_NAME
# TODO: remove: cicd test 54
# TODO: remove: cicd test 58
def main(
@@ -6,7 +6,7 @@ from agebd.utils import init_logging
from agebd_taupct import PVs, Service
from agebd_taupct.service import SERVICE_NAME
# TODO: remove: cicd test 54
# TODO: remove: cicd test 58
def main(
@@ -6,7 +6,7 @@ from agebd.utils import init_logging
from agebd_timing import PVs, Service
from agebd_timing.service import SERVICE_NAME
# TODO: remove: cicd test 54
# TODO: remove: cicd test 58
def main(
@@ -6,7 +6,7 @@ from agebd_topuptool import PVs, Service
from agebd_topuptool.runner import Runner
from agebd_topuptool.service import SERVICE_NAME
# TODO: remove: cicd test 54
# TODO: remove: cicd test 58
def main(
@@ -6,7 +6,7 @@ from agebd.utils import init_logging
from agebd_tune import PVs, Service
from agebd_tune.service import SERVICE_NAME
# TODO: remove: cicd test 54
# TODO: remove: cicd test 58
def main(
@@ -6,7 +6,7 @@ from agebd.utils import init_logging
from agebd_tunebump import PVs, Service
from agebd_tunebump.service import SERVICE_NAME
# TODO: remove: cicd test 54
# TODO: remove: cicd test 58
def main(
@@ -6,7 +6,7 @@ from agebd.utils import init_logging
from agebd_tunefbx import PVs, Service
from agebd_tunefbx.service import SERVICE_NAME
# TODO: remove: cicd test 54
# TODO: remove: cicd test 58
def main(
@@ -7,7 +7,7 @@ from agebd_tunefby import PVs, Service
from agebd_tunefby.service import SERVICE_NAME
# TODO: remove: cicd test 54
# TODO: remove: cicd test 58
def main(
log_level: LogLevel = typer.Option(
@@ -1,3 +1,4 @@
<!--TODO: remove: cicd test 57-->
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>