diff --git a/ansible/playbooks/add-new-service.yml b/ansible/playbooks/add-new-service.yml index d7a2741..7ca7465 100644 --- a/ansible/playbooks/add-new-service.yml +++ b/ansible/playbooks/add-new-service.yml @@ -19,6 +19,7 @@ branch_name: "feature/add-service-{{ new_service_dir_name }}" import_playbook: ../plays/service/service-deploy-and-restart.yml +# TODO: ioc_port # - name: Install New IOC # vars: # branch_name: "feature/add-service-{{ new_service_dir_name }}" diff --git a/ansible/plays/ioc/ioc-install.yml b/ansible/plays/ioc/ioc-install.yml index d54d285..e7f4965 100644 --- a/ansible/plays/ioc/ioc-install.yml +++ b/ansible/plays/ioc/ioc-install.yml @@ -50,11 +50,14 @@ removes: "{{ ioc_base_dir }}/{{ orig_params_name }}" # Only runs if the original file exists # Replace {{ agebd_env }} inside the newly renamed subs file - - name: "[{{ service_dir_name }}] Substitute environment token inside subs file" + - name: "[{{ service_dir_name }}] Substitute environment tokens inside subs file" ansible.builtin.replace: path: "{{ ioc_base_dir }}/{{ target_subs_name }}" - regexp: '\{\{\s*agebd_env\s*\}\}' - replace: "{{ env_suffix_upper }}" + regexp: "{{ item.regexp }}" + replace: "{{ item.replace | string }}" + with_items: + - { regexp: '\{\{\s*agebd_env\s*\}\}', replace: "{{ env_suffix_upper }}" } + - { regexp: '\{\{\s*ioc_port\s*\}\}', replace: "{{ ioc_port }}" } # Replace {{ agebd_env }} inside the newly renamed params file - name: "[{{ service_dir_name }}] Substitute environment token inside params file" diff --git a/cli/src/core/models.py b/cli/src/core/models.py index 310ea30..ecc0508 100644 --- a/cli/src/core/models.py +++ b/cli/src/core/models.py @@ -15,10 +15,13 @@ MASTER_SERVICE_DIR = REPO_ROOT / "services" / "000-master" / "current" MASTER_SERVICE_CONFIG_DIR = MASTER_SERVICE_DIR / "app" / "config" MASTER_HLA_NAMES_FILENAME = MASTER_SERVICE_CONFIG_DIR / "hla_names.yml" +IOCS_OVERVIEW_FILENAME = REPO_ROOT / "docs" / "ioc" / "ioc_overview.md" + class Service(BaseModel): id: int name: str + ioc_port: int status: ServiceStatus @property @@ -39,7 +42,7 @@ class Service(BaseModel): return f"{self.id:03d}" def to_dict(self): - return {"name": self.name_lower, "status": str(self.status)} + return {"name": self.name_lower, "ioc_port": self.ioc_port, "status": str(self.status)} class Config: use_enum_values = True @@ -56,7 +59,8 @@ class ServiceRegistry(BaseModel): config_path: ClassVar[str] = str(SERVICE_REGISTRY_FILENAME) next_available_id: int - services: dict[int, dict] + next_available_ioc_port: int + services: dict[str, dict] @classmethod def read_from_config(cls) -> "ServiceRegistry": @@ -67,12 +71,18 @@ class ServiceRegistry(BaseModel): return registry def get_services(self) -> list[Service]: - return [Service(id=svc_id, **svc_data) for svc_id, svc_data in self.services.items()] + return [Service(id=int(svc_id), **svc_data) for svc_id, svc_data in self.services.items()] def add_service(self, name: str) -> Service: - svc = Service(id=self.next_available_id, name=name, status=ServiceStatus.ACTIVE) - self.services.update({svc.id: svc.to_dict()}) + svc = Service( + id=self.next_available_id, + name=name, + ioc_port=self.next_available_ioc_port, + status=ServiceStatus.ACTIVE, + ) + self.services.update({svc.id_str: svc.to_dict()}) self.next_available_id += 1 + self.next_available_ioc_port += 1 return svc @@ -100,3 +110,29 @@ class MasterHLANames(BaseModel): def write_to_config(self) -> None: with open(self.config_path, "w") as f: yaml.safe_dump(self.model_dump(), f, indent=4) + + +class IocsOverview(BaseModel): + config_path: ClassVar[str] = str(IOCS_OVERVIEW_FILENAME) + + content: str + + @classmethod + def read_from_file(cls) -> "IocsOverview": + with open(cls.config_path, "r", encoding="utf-8") as f: + s = f.read() + + return IocsOverview(content=s) + + def add_ioc(self, service: Service, description: str): + new_row = f"| AGEBD-CPCL-{service.name_upper} | {service.ioc_port} | {description} |\n" + + # Ensuring it starts on a clean new line if the file didn't end with one + if not self.content.endswith("\n"): + self.content += "\n" + + self.content += new_row + + def write_to_file(self): + with open(self.config_path, "w") as f: + f.write(self.content) diff --git a/cli/src/core/service_creator.py b/cli/src/core/service_creator.py index efd64c4..f5e1e82 100644 --- a/cli/src/core/service_creator.py +++ b/cli/src/core/service_creator.py @@ -14,7 +14,7 @@ from core.git import ( git_push_changes, switch_branch, ) -from core.models import MasterHLANames, Service, ServiceRegistry +from core.models import IocsOverview, MasterHLANames, Service, ServiceRegistry from core.utils import get_git_root logger = logging.getLogger(__name__) @@ -30,13 +30,9 @@ MASTER_IOC_SUBS_FILE = MASTER_SERVICE_DIR / "ioc" / "AGEBD-CPCL-MASTER_main.subs class ServiceCreator: - def __init__( - self, - ioc_port: int, - user: str, - ) -> None: - self.ioc_port = ioc_port + def __init__(self, user: str, ioc_description: str) -> None: self.user = user + self.ioc_description = ioc_description def add_service(self, name: str): repo = git.Repo(REPO_ROOT) @@ -50,6 +46,9 @@ class ServiceCreator: hla_names_old = MasterHLANames.read_from_config() hla_names = MasterHLANames.read_from_config() hla_names.add_hla_name(service.name_upper) + iocs_overview_old = IocsOverview.read_from_file() + iocs_overview = IocsOverview.read_from_file() + iocs_overview.add_ioc(service, description=self.ioc_description) branch_name = f"feature/add-service-{service.dir_name}" delete_local_branch(repo, branch_name) @@ -58,12 +57,12 @@ class ServiceCreator: try: self._render_new_service_templates( service=service, - ioc_port=self.ioc_port, user=self.user, ) self._generate_uv_lock(service=service) registry.write_to_config() hla_names.write_to_config() + iocs_overview.write_to_file() self._append_to_master_ioc_subs( service_name=service.name_upper, @@ -87,13 +86,13 @@ class ServiceCreator: shutil.rmtree(Path(SERVICE_DEST_DIR / service.dir_name), ignore_errors=True) registry_old.write_to_config() hla_names_old.write_to_config() + iocs_overview_old.write_to_file() switch_branch(repo, original_branch) raise e def _render_new_service_templates( self, service: Service, - ioc_port: int, user: str, ): """ @@ -106,7 +105,6 @@ class ServiceCreator: "service_id": service.id_str, "service_name_upper": service.name_upper, "service_name_lower": service.name_lower, - "ioc_port": ioc_port, "user": user, }, ) diff --git a/cli/src/service.py b/cli/src/service.py index 60970ca..96da23e 100644 --- a/cli/src/service.py +++ b/cli/src/service.py @@ -12,13 +12,15 @@ REPO_ROOT = get_git_root(__file__) @service.command() def add( name: str = typer.Option(..., "--name", "-n"), - ioc_port: int = typer.Option(..., "--ioc-port", "-p"), user: str = typer.Option(..., "--user", "-u"), + ioc_description: str = typer.Option( + ..., "--ioc-description", "-d", help="See /docs/ioc/ioc_overview.md for examples" + ), ): init_logging() ServiceCreator( - ioc_port=ioc_port, # TODO: read from registry user=user, # TODO: read from 'whoami'? + ioc_description=ioc_description, ).add_service(name=name) diff --git a/cli/uv.lock b/cli/uv.lock index 8de78fc..85af3b3 100644 --- a/cli/uv.lock +++ b/cli/uv.lock @@ -2,7 +2,16 @@ version = 1 revision = 3 requires-python = ">=3.9" resolution-markers = [ - "python_full_version >= '3.10'", + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.10.*'", "python_full_version < '3.10'", ] @@ -117,7 +126,16 @@ name = "copier" version = "9.16.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.10'", + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.10.*'", ] dependencies = [ { name = "colorama", marker = "python_full_version >= '3.10'" }, @@ -166,7 +184,7 @@ name = "exceptiongroup" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ @@ -224,7 +242,16 @@ name = "iniconfig" version = "2.3.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.10'", + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.10.*'", ] sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } wheels = [ @@ -276,7 +303,16 @@ name = "markdown-it-py" version = "4.2.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.10'", + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.10.*'", ] dependencies = [ { name = "mdurl", marker = "python_full_version >= '3.10'" }, @@ -435,7 +471,16 @@ name = "platformdirs" version = "4.10.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.10'", + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.10.*'", ] sdist = { url = "https://files.pythonhosted.org/packages/d7/47/e4501f49c178ae1d9f4a75073fda4204f52647993f075a9db4d14930e0c5/platformdirs-4.10.0.tar.gz", hash = "sha256:31e761a6a0ca04faf7353ea759bdba55652be214725111e5aac52dfa29d4bef7", size = 31224, upload-time = "2026-05-28T03:32:53.587Z" } wheels = [ @@ -668,7 +713,16 @@ name = "pytest" version = "9.1.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.10'", + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.10.*'", ] dependencies = [ { name = "colorama", marker = "python_full_version >= '3.10' and sys_platform == 'win32'" }, @@ -903,7 +957,16 @@ name = "typer" version = "0.26.8" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.10'", + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.10.*'", ] dependencies = [ { name = "annotated-doc", marker = "python_full_version >= '3.10'" }, diff --git a/config/services_registry.yml b/config/services_registry.yml index 938f5bc..d3d2664 100644 --- a/config/services_registry.yml +++ b/config/services_registry.yml @@ -1,5 +1,7 @@ next_available_id: 1 +next_available_ioc_port: 50003 services: - 0: + 000: name: master + ioc_port: 50011 status: active diff --git a/docs/ansible.md b/docs/ansible.md index 5845e48..ef9a586 100644 --- a/docs/ansible.md +++ b/docs/ansible.md @@ -15,7 +15,7 @@ ansible-playbook ansible/playbooks/add-new-service.yml \ ``` ansible-playbook ansible/plays/ioc/ioc-install.yml \ -i ansible/hosts.yml \ - --extra-vars="agebd_env=dev branch_name=feature/add-service-001-playground new_service_dir_name=001-playground" \ + --extra-vars="agebd_env=dev branch_name=feature/add-service-001-playground new_service_dir_name=001-playground ioc_port=50003" \ -v ``` diff --git a/docs/ioc.md b/docs/ioc/ioc.md similarity index 100% rename from docs/ioc.md rename to docs/ioc/ioc.md diff --git a/docs/ioc/ioc_overview.md b/docs/ioc/ioc_overview.md new file mode 100644 index 0000000..cd14da3 --- /dev/null +++ b/docs/ioc/ioc_overview.md @@ -0,0 +1,5 @@ +# Overview of IOCs for Beam Dynamics + +| IOC NAME | Port | Description | +|---|---|---| +| AGEBD-CPCL-MASTER | 50011 | IOC providing PVs for the service master application IOC enabling to monitor the health status of BD High Level Application Services with the alarm handler ALH | \ No newline at end of file diff --git a/services/000-master/current/ioc/AGEBD-CPCL-MASTER_parameters.yaml b/services/000-master/current/ioc/AGEBD-CPCL-MASTER_parameters.yaml index def2bde..57d725a 100644 --- a/services/000-master/current/ioc/AGEBD-CPCL-MASTER_parameters.yaml +++ b/services/000-master/current/ioc/AGEBD-CPCL-MASTER_parameters.yaml @@ -1,6 +1,6 @@ cpu_architecture: x86_64 epics_version: 7.0.8 ioc_host: sls-vserv-bd-01{{ agebd_env }} -ioc_port: 50011 +ioc_port: {{ ioc_port }} os: RHEL8 os_id: rhel diff --git a/templates/service/{{service_id}}-{{service_name_lower}}/current/app/src/AGEBD-SERVICE-{{service_name_upper}}.py.jinja b/templates/service/{{service_id}}-{{service_name_lower}}/current/app/src/AGEBD-SERVICE-{{service_name_upper}}.py.jinja index 403a4cd..1fb17ea 100644 --- a/templates/service/{{service_id}}-{{service_name_lower}}/current/app/src/AGEBD-SERVICE-{{service_name_upper}}.py.jinja +++ b/templates/service/{{service_id}}-{{service_name_lower}}/current/app/src/AGEBD-SERVICE-{{service_name_upper}}.py.jinja @@ -87,8 +87,6 @@ class Service(BaseService[PVs]): # get some pvs/vals self.val1 = self.pvs.my_pv1.get() self.val2 = self.pvs.my_pv2.get() - print(self.val1) - print(self.val2) # maybe sleep to limit the update loop execution rate sleep(1) diff --git a/templates/service/{{service_id}}-{{service_name_lower}}/current/ioc/AGEBD-CPCL-{{service_name_upper}}_parameters.yaml.jinja b/templates/service/{{service_id}}-{{service_name_lower}}/current/ioc/AGEBD-CPCL-{{service_name_upper}}_parameters.yaml.jinja index cb07a0e..9706056 100644 --- a/templates/service/{{service_id}}-{{service_name_lower}}/current/ioc/AGEBD-CPCL-{{service_name_upper}}_parameters.yaml.jinja +++ b/templates/service/{{service_id}}-{{service_name_lower}}/current/ioc/AGEBD-CPCL-{{service_name_upper}}_parameters.yaml.jinja @@ -1,6 +1,6 @@ cpu_architecture: x86_64 epics_version: 7.0.8 ioc_host: sls-vserv-bd-01{{ '{{ agebd_env }}' }} -ioc_port: {{ ioc_port }} +ioc_port: {{ '{{ ioc_port }}' }} os: RHEL8 os_id: rhel