Skip to content

Commit

Permalink
Merge pull request #3 from epics-containers/RTEMS
Browse files Browse the repository at this point in the history
first working draft
  • Loading branch information
gilesknap committed Mar 25, 2024
2 parents b6a6b37 + bf35637 commit 9d8550e
Show file tree
Hide file tree
Showing 15 changed files with 587 additions and 84 deletions.
4 changes: 2 additions & 2 deletions .copier-answers.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ component_owner: group:default/sscc
description: Support for a K8S proxy container in controlling and monitoring RTEMS
EPICS IOCs
distribution_name: rtems-proxy
docker: true
docker: false
docs_type: README
git_platform: github.com
github_org: epics-containers
package_name: rtems_proxy
pypi: true
repo_name: rtems-proxy
type_checker: pyright
type_checker: mypy
3 changes: 1 addition & 2 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
{
"name": "Python 3 Developer Container",
"build": {
"dockerfile": "../Dockerfile",
"target": "developer"
"dockerfile": "../Dockerfile"
},
"remoteEnv": {
// Allow X11 apps to run inside the container
Expand Down
56 changes: 0 additions & 56 deletions .github/workflows/_container.yml

This file was deleted.

5 changes: 5 additions & 0 deletions .github/workflows/_pypi.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
on:
workflow_call:
secrets:
PYPI_TOKEN:
required: true

jobs:
upload:
Expand All @@ -15,3 +18,5 @@ jobs:

- name: Publish to PyPI using trusted publishing
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.PYPI_TOKEN }}
13 changes: 4 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,25 +34,20 @@ jobs:
secrets:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

container:
needs: check
if: needs.check.outputs.branch-pr == ''
uses: ./.github/workflows/_container.yml
permissions:
packages: write

dist:
needs: check
if: needs.check.outputs.branch-pr == ''
uses: ./.github/workflows/_dist.yml

pypi:
if: github.ref_type == 'tag'
needs: dist
uses: ./.github/workflows/_pypi.yml
permissions:
id-token: write

secrets:
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}

release:
if: github.ref_type == 'tag'
needs: [dist]
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ how it does it, and why people should use it.
Source | <https://github.com/epics-containers/rtems-proxy>
:---: | :---:
PyPI | `pip install rtems-proxy`
Docker | `docker run ghcr.io/epics-containers/rtems-proxy:latest`
Releases | <https://github.com/epics-containers/rtems-proxy/releases>

This is where you should put some images or code snippets that illustrate
Expand All @@ -28,6 +27,8 @@ print(f"Hello rtems_proxy {__version__}")

Or if it is a commandline tool then you might put some example commands here:


```
python -m rtems_proxy --version
```

25 changes: 25 additions & 0 deletions proxy-start.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/bin/bash

set -x

# This is the folder the PVC for the nfsv2tftp shared volume is mounted into.
export RTEMS_TFTP_PATH=${RTEMS_TFTP_PATH:-/nfsv2-tftp}

if [ ! -d ${RTEMS_TFTP_PATH} ]; then
echo "ERROR: No PVC folder found."
# make a folder for testing outside of the cluster
mkdir -p ${RTEMS_TFTP_PATH}
fi

# copy the IOC instance's runtime assets into the shared volume
cp -rL /epics/ioc ${RTEMS_TFTP_PATH}
cp -r /epics/runtime ${RTEMS_TFTP_PATH}
# move binary to the root for shorter paths
mv ${RTEMS_TFTP_PATH}/ioc/bin/*/ioc.boot ${RTEMS_TFTP_PATH}
# fix up the paths in st.cmd
sed -i "s|/epics/|/iocs/${IOC_LOCATION}/${IOC_NAME}/|" ${RTEMS_TFTP_PATH}/runtime/st.cmd

# keep the container running ...
while true; do
sleep 2
done
19 changes: 12 additions & 7 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ classifiers = [
"Programming Language :: Python :: 3.11",
]
description = "Support for a K8S proxy container in controlling and monitoring RTEMS EPICS IOCs"
dependencies = [] # Add project dependencies here, e.g. ["click", "numpy"]
dependencies = ["jinja2", "pexpect", "ruamel.yaml", "telnetlib3", "typer"]
dynamic = ["version"]
license.file = "LICENSE"
readme = "README.md"
Expand All @@ -23,9 +23,9 @@ requires-python = ">=3.7"
[project.optional-dependencies]
dev = [
"copier",
"mypy",
"pipdeptree",
"pre-commit",
"pyright",
"pytest",
"pytest-cov",
"ruff",
Expand All @@ -34,7 +34,7 @@ dev = [
]

[project.scripts]
rtems-proxy = "rtems_proxy.__main__:main"
rtems-proxy = "rtems_proxy.__main__:cli"

[project.urls]
GitHub = "https://github.com/epics-containers/rtems-proxy"
Expand All @@ -47,8 +47,8 @@ name = "Giles Knap"
[tool.setuptools_scm]
write_to = "src/rtems_proxy/_version.py"

[tool.pyright]
reportMissingImports = false # Ignore missing stubs in imported modules
[tool.mypy]
ignore_missing_imports = true # Ignore missing stubs in imported modules

[tool.pytest.ini_options]
# Run pytest with all our checkers, and don't spam us with massive tracebacks on error
Expand Down Expand Up @@ -81,14 +81,19 @@ passenv = *
allowlist_externals =
pytest
pre-commit
pyright
mypy
commands =
pre-commit: pre-commit run --all-files {posargs}
type-checking: pyright src tests {posargs}
type-checking: mypy src tests {posargs}
tests: pytest --cov=rtems_proxy --cov-report term --cov-report xml:cov.xml {posargs}
"""

[tool.ruff]
ignore = [
"B008", # Do not perform unnecessary work in __all__
"C408", # Unnecessary collection call - e.g. list(...) instead of [...]
"E501", # Line too long, should be fixed by black.
]
src = ["src", "tests"]
line-length = 88
lint.select = [
Expand Down
3 changes: 3 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# these requirements are for additional python packages to install into the
# container
ibek==1.7.2b6
146 changes: 139 additions & 7 deletions src/rtems_proxy/__main__.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,148 @@
from argparse import ArgumentParser
from pathlib import Path
from typing import Optional

import typer
from jinja2 import Template
from ruamel.yaml import YAML

from . import __version__
from .copy import copy_rtems
from .globals import GLOBALS
from .telnet import ioc_connect

__all__ = ["main"]

cli = typer.Typer()


def version_callback(value: bool):
if value:
typer.echo(__version__)
raise typer.Exit()


@cli.callback()
def main(
version: Optional[bool] = typer.Option(
None,
"--version",
callback=version_callback,
is_eager=True,
help="Print the version of ibek and exit",
),
):
"""
Proxy for RTEMS IOCs controlling and monitoring
"""


@cli.command()
def start(
copy: bool = typer.Option(
True, "--copy/--no-copy", help="copy binaries before connecting"
),
reboot: bool = typer.Option(
True, "--reboot/--no-reboot", help="reboot the IOC first"
),
):
"""
Starts an RTEMS IOC. Places the IOC binaries in the expected location,
restarts the IOC and connects stdio to the IOC console.
This should be called inside of a runtime IOC container after ibek
has generated the runtime assets for the IOC.
The standard 'start.sh' in the runtime IOC will call this entry point if
it detects that EPICS_HOST_ARCH==RTEMS-beatnik
args:
copy: Copy the RTEMS binaries to the IOCs TFTP and NFS directories first
reboot: Reboot the IOC once the binaries are copied and the connection is made
"""
print(
f"Remote control startup of RTEMS IOC {GLOBALS.IOC_NAME}"
f" at {GLOBALS.RTEMS_IOC_IP}"
)
if copy:
copy_rtems()
ioc_connect(GLOBALS.RTEMS_CONSOLE, reboot=reboot)


@cli.command()
def dev(
ioc_repo: Path = typer.Argument(
...,
help="The beamline/accelerator repo holding the IOC instance",
file_okay=False,
exists=True,
),
ioc_name: str = typer.Argument(
...,
help="The name of the IOC instance to work on",
),
):
"""
Sets up a devcontainer to work on an IOC instance. Must be run from within
the developer container for the generic IOC that the instance uses.
args:
ioc_repo: The path to the IOC repository that holds the instance
ioc_name: The name of the IOC instance to work on
"""

ioc_path = ioc_repo / "services" / ioc_name

values = ioc_repo / "helm/shared/values.yaml"
if not values.exists():
typer.echo(f"Global settings file {values} not found. Exiting")
raise typer.Exit(1)

ioc_values = ioc_path / "values.yaml"
if not ioc_values.exists():
typer.echo(f"Instance settings file {ioc_values} not found. Exiting")
raise typer.Exit(1)

env_vars = {}
# TODO in future use pydantic and make a model for this but for now let's cheese it.
with open(values) as fp:
yaml = YAML(typ="safe").load(fp)
try:
ioc_group = yaml["ioc-instance"]["ioc_group"]
for item in yaml["ioc-instance"]["globalEnv"]:
env_vars[item["name"]] = item["value"]
except KeyError:
typer.echo(f"{values} not in expected format")
raise typer.Exit(1) from None

with open(ioc_values) as fp:
yaml = YAML(typ="safe").load(fp)
try:
for item in yaml["shared"]["ioc-instance"]["iocEnv"]:
env_vars[item["name"]] = item["value"]
except KeyError:
typer.echo(f"{ioc_values} not in expected format")
raise typer.Exit(1) from None

this_dir = Path(__file__).parent
template = Path(this_dir / "rsync.sh.jinja").read_text()

script = Template(template).render(
env_vars=env_vars,
ioc_group=ioc_group,
ioc_name=ioc_name,
ioc_path=ioc_path,
)

script_file = Path("/tmp/dev_proxy.sh")
script_file.write_text(script)

def main(args=None):
parser = ArgumentParser()
parser.add_argument("-v", "--version", action="version", version=__version__)
args = parser.parse_args(args)
typer.echo(f"\nIOC {ioc_name} dev environment prepared for {ioc_repo}")
typer.echo("You can now change and compile support module or iocs.")
typer.echo("Then start the ioc with '/epics/ioc/start.sh'")
typer.echo(f"\n\nPlease first source {script_file} to set up the dev environment.")


# test with: python -m rtems_proxy
# test with:
# pipenv run python -m ibek
if __name__ == "__main__":
main()
cli()
Loading

0 comments on commit 9d8550e

Please sign in to comment.