From d21d00e71ea8fe34af6111d5ee89a4a2e678a159 Mon Sep 17 00:00:00 2001 From: Jan Jasek Date: Fri, 21 Aug 2026 17:53:00 +0200 Subject: [PATCH] Add source-built HorizonTest UI test container Package HorizonTest as openstack-horizontest, matching the tcib image used by test-operator to run Horizon's pytest/selenium UI integration tests against a live dashboard. run_horizontest.sh and horizontest_sudoers are copied verbatim from tcib; runtime behavior (cloning the Horizon test suite at container start, sudo/crudini/openstack CLI usage) is unchanged. Differences from tcib's horizontest.yaml, and why: - Python dependencies (Django, selenium, pytest, horizon, etc.) are resolved via upper-constraints.txt instead of tcib's hardcoded pins, so they track whichever OpenStack release this image targets. - python-openstackclient is added explicitly. tcib's horizontest image gets the openstack CLI for free because horizontest.yaml is nested under base/os/, inheriting python3-openstackclient from os.yaml. This image has no such inherited layer, so it must be declared. - junit2html is dropped: it's unused (run_horizontest.sh only relies on pytest's built-in --junitxml and pytest-html's --html), and it ships no sdist on PyPI, which breaks pybuild-deps. - geckodriver is bumped to v0.37.1 (tcib pins v0.29.0, which only supports Firefox <=90) for compatibility with the current Firefox RPM. Assisted-By: Cursor Claude Sonnet 5 --- containers/horizontest/OWNERS | 12 + containers/horizontest/buildrequirements.lock | 1 + .../horizontest/buildrequirements.lock.master | 31 + .../horizontest/horizontest/Containerfile | 71 +++ .../horizontest/horizontest/bindeps.txt | 16 + .../horizontest/horizontest/builddeps.txt | 21 + .../config/etc/sudoers.d/horizontest_sudoers | 1 + .../horizontest/pythonbuilddeps.txt | 2 + .../horizontest/horizontest/pythondeps.txt | 11 + .../horizontest/scripts/run_horizontest.sh | 219 +++++++ .../horizontest/horizontest/src/.gitkeep | 0 containers/horizontest/requirements.lock | 1 + .../horizontest/requirements.lock.master | 136 ++++ containers/horizontest/rpms.in.yaml | 40 ++ containers/horizontest/sources.txt | 2 + containers/horizontest/src/.gitkeep | 0 containers/horizontest/upper-constraints.txt | 1 + .../horizontest/upper-constraints.txt.master | 582 ++++++++++++++++++ 18 files changed, 1147 insertions(+) create mode 100644 containers/horizontest/OWNERS create mode 120000 containers/horizontest/buildrequirements.lock create mode 100644 containers/horizontest/buildrequirements.lock.master create mode 100644 containers/horizontest/horizontest/Containerfile create mode 100644 containers/horizontest/horizontest/bindeps.txt create mode 100644 containers/horizontest/horizontest/builddeps.txt create mode 100644 containers/horizontest/horizontest/config/etc/sudoers.d/horizontest_sudoers create mode 100644 containers/horizontest/horizontest/pythonbuilddeps.txt create mode 100644 containers/horizontest/horizontest/pythondeps.txt create mode 100755 containers/horizontest/horizontest/scripts/run_horizontest.sh create mode 100644 containers/horizontest/horizontest/src/.gitkeep create mode 120000 containers/horizontest/requirements.lock create mode 100644 containers/horizontest/requirements.lock.master create mode 100644 containers/horizontest/rpms.in.yaml create mode 100644 containers/horizontest/sources.txt create mode 100644 containers/horizontest/src/.gitkeep create mode 120000 containers/horizontest/upper-constraints.txt create mode 100644 containers/horizontest/upper-constraints.txt.master diff --git a/containers/horizontest/OWNERS b/containers/horizontest/OWNERS new file mode 100644 index 00000000..f4c501c0 --- /dev/null +++ b/containers/horizontest/OWNERS @@ -0,0 +1,12 @@ +# See the OWNERS docs at https://www.kubernetes.dev/docs/guide/owners/ +approvers: + - mcgonago + - deshipu + - xtmprsqzntwlfb + - HanzJas + +reviewers: + - mcgonago + - deshipu + - xtmprsqzntwlfb + - HanzJas diff --git a/containers/horizontest/buildrequirements.lock b/containers/horizontest/buildrequirements.lock new file mode 120000 index 00000000..823367e2 --- /dev/null +++ b/containers/horizontest/buildrequirements.lock @@ -0,0 +1 @@ +buildrequirements.lock.master \ No newline at end of file diff --git a/containers/horizontest/buildrequirements.lock.master b/containers/horizontest/buildrequirements.lock.master new file mode 100644 index 00000000..9df84439 --- /dev/null +++ b/containers/horizontest/buildrequirements.lock.master @@ -0,0 +1,31 @@ +build==1.5.0 +calver==2025.10.20 +cython==3.3.0 +flit-core==3.12.0 +flit-core==4.0.2 +hatch-fancy-pypi-readme==25.1.0 +hatch-vcs==0.5.0 +hatchling==1.32.0 +maturin==1.14.1 +packaging==26.3 +pathspec==1.1.1 +pbr==7.0.3 +pluggy==1.6.0 +poetry-core==2.4.1 +pybind11==3.1.0 +pyproject-hooks==1.2.0 +scikit-build-core==1.0.3 +semantic-version==2.10.0 +setuptools-rust==1.13.0 +setuptools-scm==10.2.1 +setuptools-scm==7.1.0 +tomlkit==0.15.1 +trove-classifiers==2026.6.1.19 +typing-extensions==4.16.0 +vcs-versioning==2.3.1 +wheel==0.48.0 + +# The following packages are considered to be unsafe in a requirements file: +pip==26.2.1 +setuptools==83.0.0 +setuptools==84.0.0 diff --git a/containers/horizontest/horizontest/Containerfile b/containers/horizontest/horizontest/Containerfile new file mode 100644 index 00000000..162c9a5c --- /dev/null +++ b/containers/horizontest/horizontest/Containerfile @@ -0,0 +1,71 @@ +ARG BASE_IMAGE=localhost/openstack/openstack-base:latest + +# --- Build stage: pre-build wheels --- +FROM ${BASE_IMAGE} AS build + +ARG CONSTRAINTS_FILE=requirements.lock +COPY ${CONSTRAINTS_FILE} /deps-upper-constraints.txt + +ARG PIP_NO_BINARY="" +ENV PIP_NO_BINARY=${PIP_NO_BINARY} +# Required to build Rust-based Python packages from source +ENV MATURIN_NO_INSTALL_RUST=true + +COPY horizontest/builddeps.txt /tmp/builddeps.txt +RUN pkgs=$(cat /tmp/builddeps.txt | grep -v '^#' | grep -v '^$' | tr '\n' ' ') && \ + if [ -n "${pkgs}" ]; then microdnf -y install ${pkgs} && microdnf clean all; fi + +COPY horizontest/pythonbuilddeps.txt /tmp/pythonbuilddeps.txt +RUN pkgs=$(cat /tmp/pythonbuilddeps.txt | grep -v '^#' | grep -v '^$' | tr '\n' ' ') && \ + if [ -n "${pkgs}" ]; then pip3 install --no-cache-dir -c /deps-upper-constraints.txt ${pkgs}; fi && \ + rm /tmp/pythonbuilddeps.txt + +# Build wheels for all packages pinned in the lockfile so the runtime stage +# can install without build tools or network access. +RUN pip3 wheel --no-cache-dir --no-deps \ + --wheel-dir=/wheels -r /deps-upper-constraints.txt + +# --- Runtime stage --- +FROM ${BASE_IMAGE} + +LABEL summary="OpenStack Horizon Selenium test runner" \ + io.k8s.description="Runs Horizon's pytest/selenium UI integration tests against a live dashboard (test-operator HorizonTest)" + +RUN uid_gid_manage horizontest + +COPY horizontest/bindeps.txt /tmp/bindeps.txt +RUN pkgs=$(cat /tmp/bindeps.txt | grep -v '^#' | grep -v '^$' | tr '\n' ' ') && \ + if [ -n "${pkgs}" ]; then microdnf -y install ${pkgs} && microdnf clean all; fi && \ + rm /tmp/bindeps.txt + +COPY --from=build /wheels /wheels +COPY --from=build /deps-upper-constraints.txt /deps-upper-constraints.txt +COPY horizontest/pythondeps.txt /tmp/pythondeps.txt +RUN pkgs=$(cat /tmp/pythondeps.txt | grep -v '^#' | grep -v '^$' | tr '\n' ' ') && \ + pip3 install --no-cache-dir --prefix=/usr \ + -c /deps-upper-constraints.txt \ + --find-links=/wheels \ + ${pkgs} && \ + rm -rf /wheels /tmp/pythondeps.txt + +RUN curl -s -L \ + https://github.com/mozilla/geckodriver/releases/download/v0.37.1/geckodriver-v0.37.1-linux64.tar.gz \ + | tar -zxvf - -C /usr/local/bin/ + +RUN curl -OL http://download.cirros-cloud.net/0.6.2/cirros-0.6.2-x86_64-disk.img \ + -o /usr/local/share/cirros-0.6.2-x86_64-disk + +COPY horizontest/config/etc/sudoers.d/horizontest_sudoers /etc/sudoers.d/horizontest_sudoers +RUN chmod 440 /etc/sudoers.d/horizontest_sudoers + +RUN mkdir -p /var/lib/horizontest/external_files && \ + chown -R horizontest:horizontest /var/lib/horizontest + +COPY horizontest/scripts/run_horizontest.sh /usr/local/bin/run_horizontest.sh +RUN chmod +x /usr/local/bin/run_horizontest.sh + +ENV USE_EXTERNAL_FILES=true + +USER horizontest + +ENTRYPOINT ["/usr/local/bin/run_horizontest.sh"] diff --git a/containers/horizontest/horizontest/bindeps.txt b/containers/horizontest/horizontest/bindeps.txt new file mode 100644 index 00000000..0d84a90e --- /dev/null +++ b/containers/horizontest/horizontest/bindeps.txt @@ -0,0 +1,16 @@ +python3 +python3-pip +firefox +xorg-x11-server-Xvfb +git-core +tar +# Pinned to 43.x to match upper-constraints.txt (cryptography==43.0.3). +# The default el10 RPM ships 49.x which conflicts with pyOpenSSL 24.2.1 +# (requires cryptography<44), causing an ImportError at runtime. Bump this +# pin once pyOpenSSL >= 25.0 lands in upper-constraints (drops the <44 cap). +python3-cryptography-43.0.0-4.el10 +# Pillow runtime libraries (transitive dep of horizon) +libjpeg-turbo +freetype +libwebp +libtiff diff --git a/containers/horizontest/horizontest/builddeps.txt b/containers/horizontest/horizontest/builddeps.txt new file mode 100644 index 00000000..36d7cf89 --- /dev/null +++ b/containers/horizontest/horizontest/builddeps.txt @@ -0,0 +1,21 @@ +git-core +gcc +gcc-c++ +python3-devel +python3-setuptools +libffi-devel +openssl-devel +# Pinned to 43.x to match upper-constraints.txt (cryptography==43.0.3). +# The default el10 RPM ships 49.x which conflicts with pyOpenSSL 24.2.1 +# (requires cryptography<44), causing an ImportError at runtime. Bump this +# pin once pyOpenSSL >= 25.0 lands in upper-constraints (drops the <44 cap). +python3-cryptography-43.0.0-4.el10 +# Required to build Pillow from source (transitive dep of horizon) +libjpeg-turbo-devel +zlib-devel +freetype-devel +libwebp-devel +libtiff-devel +# To build bcrypt / rpds-py from source +rust +cargo diff --git a/containers/horizontest/horizontest/config/etc/sudoers.d/horizontest_sudoers b/containers/horizontest/horizontest/config/etc/sudoers.d/horizontest_sudoers new file mode 100644 index 00000000..8e3a3ba2 --- /dev/null +++ b/containers/horizontest/horizontest/config/etc/sudoers.d/horizontest_sudoers @@ -0,0 +1 @@ +horizontest ALL=(ALL) NOPASSWD: ALL diff --git a/containers/horizontest/horizontest/pythonbuilddeps.txt b/containers/horizontest/horizontest/pythonbuilddeps.txt new file mode 100644 index 00000000..f003e984 --- /dev/null +++ b/containers/horizontest/horizontest/pythonbuilddeps.txt @@ -0,0 +1,2 @@ +pbr +wheel diff --git a/containers/horizontest/horizontest/pythondeps.txt b/containers/horizontest/horizontest/pythondeps.txt new file mode 100644 index 00000000..631fcec5 --- /dev/null +++ b/containers/horizontest/horizontest/pythondeps.txt @@ -0,0 +1,11 @@ +pytest +pytest-django +pytest-html +Django +horizon +selenium +testtools +xvfbwrapper +urllib3 +# OpenStack CLI used by run_horizontest.sh (image/project/user/flavor mgmt) +python-openstackclient diff --git a/containers/horizontest/horizontest/scripts/run_horizontest.sh b/containers/horizontest/horizontest/scripts/run_horizontest.sh new file mode 100755 index 00000000..c0cbac14 --- /dev/null +++ b/containers/horizontest/horizontest/scripts/run_horizontest.sh @@ -0,0 +1,219 @@ +#!/bin/sh + +set -x + +HORIZONTEST_DIR=/var/lib/horizontest +HORIZON_LOGS_DIR_NAME=${HORIZON_LOGS_DIR_NAME:-"horizon"} +IMAGE_URL=http://download.cirros-cloud.net/0.6.2/cirros-0.6.2-x86_64-disk.img +PROJECT_NAME=horizontest +USER_NAME=horizontest +PASSWORD=horizontest +FLAVOR_NAME=m1.tiny +GROUP_NAME=admins +SELENIUM_MESSAGE_WAIT=120 +SELENIUM_EXPLICIT_WAIT=360 +SELENIUM_PAGE_TIMEOUT=240 +SELENIUM_IMPLICIT_WAIT=30 +HORIZONTEST_DEBUG_MODE="${HORIZONTEST_DEBUG_MODE:-false}" +EXTRA_FLAG="${EXTRA_FLAG:-"not pagination and not federation"}" +PROJECT_NAME_XPATH="${PROJECT_NAME_XPATH:-"//span[@class='rcueicon rcueicon-folder-open']/ancestor::li"}" +IMAGE_FILE_NAME=cirros-0.6.2-x86_64-disk +IMAGE_FILE_NAME_WITH_SIZE="cirros-0.6.2-x86_64-disk (20.4 MB)" +IMAGE_FILE="/usr/local/share/${IMAGE_FILE_NAME}" +if [[ ! -f "${IMAGE_FILE}" ]]; then + IMAGE_FILE="/var/lib/horizontest/${IMAGE_FILE_NAME}" +fi +SUBNET_NAME=public_subnet +HELP_SEQUENCE=".//*[normalize-space()='Help']" +HELP_URL="https://docs.redhat.com/en/documentation/red_hat_openstack_services_on_openshift/" +TEST_MATERIAL_THEME=False +USER_NAME_XPATH="//span[@class='rcueicon rcueicon-user']/ancestor::li" +BROWSE_LEFT_PANEL_MAIN="Project,Project,Project,Project,Project,Project,Project,Project,Project,Project,Project,Project,Project,Project,Project,Project,Admin,Admin,Admin,Admin,Admin,Admin,Admin,Admin,Admin,Admin,Admin,Admin,Admin,Admin,Admin,Admin,Admin,Admin,Identity,Identity,Identity,Identity,Identity" +BROWSE_LEFT_PANEL_SEC="Project,None,None,None,None,None,Volumes,Volumes,Network,Network,Network,Network,Network,Network,Network,Object Store,None,Compute,Compute,Compute,Compute,Compute,Volume,Volume,Volume,Volume,Network,Network,Network,Network,Network,System,System,System,None,None,None,None,None" +BLP_SEC_LINE_XPATH=".//*[@class='navbar primary persistent-secondary']" +BLP_SEC_LINE_REQ_BTN=".//*[@class='navbar primary persistent-secondary']//a[normalize-space()='{sec_panel}']//ancestor::li" +BLP_SIDEBAR_XPATH=".//*[@class='navbar primary persistent-secondary']//a[normalize-space()='{sec_panel}']//ancestor::li//*[@class='dropdown-menu']" +PROJECT_TEXT_XPATH="${PROJECT_TEXT_XPATH:-".//span[@class='rcueicon rcueicon-folder-open']/ancestor::li"}" +SERVICES_REGION_NAME_XPATH=".//li[@id='services_region_switcher']/a" +SERVICES_REGION_BTN_TEXT_PATTERN="Managing Region {region}" +SERVICES_REGION_DROPDOWN_XPATH=".//li[@id='services_region_switcher']" + +# assert mandatory variables have been set +if [[ -z "${ADMIN_USERNAME}" ]]; then + ADMIN_USERNAME=$(python3 -c "from openstack.config import OpenStackConfig; print(OpenStackConfig().get_one(cloud='default').auth['username'])") +fi +if [[ -z "${ADMIN_PASSWORD}" ]]; then + ADMIN_PASSWORD=$(python3 -c "from openstack.config import OpenStackConfig; print(OpenStackConfig().get_one(cloud='default').auth['password'])") +fi +[[ -z ${DASHBOARD_URL} ]] && echo "DASHBOARD_URL not set" && exit 1 +[[ -z ${AUTH_URL} ]] && echo "AUTH_URL not set" && exit 1 +[[ -z ${REPO_URL} ]] && REPO_URL="https://review.opendev.org/openstack/horizon" +[[ -z ${HORIZON_REPO_BRANCH} ]] && HORIZON_REPO_BRANCH="master" + +function catch_error_if_debug { + echo "File run_horizontest.sh has run into an error!" + sleep infinity +} + +# Catch errors when in debug mode +if [ ${HORIZONTEST_DEBUG_MODE} == true ]; then + trap catch_error_if_debug ERR +fi + +#This function is temporarily added until tempest cleanup is implemented +function clean_leftover_images { + openstack image list -c Name -f value --os-cloud default | xargs -I {} openstack image delete {} --os-cloud default +} + +function create_custom_resources { + if ! openstack image show --os-cloud default ${IMAGE_FILE_NAME} ; then + if [ ! -f "$IMAGE_FILE" ]; then + curl -o "$IMAGE_FILE" -OL ${IMAGE_URL} + fi + openstack image create \ + --os-cloud default \ + --disk-format qcow2 \ + --container-format bare \ + --file "$IMAGE_FILE" --public ${IMAGE_FILE_NAME} + fi + + if ! openstack project show --os-cloud default ${PROJECT_NAME} ; then + openstack project create \ + --os-cloud default \ + --description 'Horizon Selenium test project' ${PROJECT_NAME} + fi + + if ! openstack user show --os-cloud default ${USER_NAME}; then + openstack user create \ + --os-cloud default \ + --project ${PROJECT_NAME} \ + --password ${PASSWORD} ${USER_NAME} + fi + + openstack role add \ + --os-cloud default \ + --user ${USER_NAME} \ + --project ${PROJECT_NAME} member + + if ! openstack flavor show --os-cloud default ${FLAVOR_NAME}; then + openstack flavor create \ + --os-cloud default \ + --public ${FLAVOR_NAME} \ + --ram 512 --disk 1 --vcpus 1 + fi + + if ! openstack group show --os-cloud default ${GROUP_NAME}; then + openstack group create \ + --os-cloud default ${GROUP_NAME} + fi +} + +function delete_custom_resources { + if openstack image show --os-cloud default ${IMAGE_FILE_NAME}; then + openstack image delete \ + --os-cloud default ${IMAGE_FILE_NAME} + fi + + if openstack project show --os-cloud default ${PROJECT_NAME}; then + openstack project delete \ + --os-cloud default ${PROJECT_NAME} + fi + + if openstack user show --os-cloud default ${USER_NAME}; then + openstack user delete \ + --os-cloud default ${USER_NAME} + fi + + if openstack flavor show --os-cloud default ${FLAVOR_NAME}; then + openstack flavor delete \ + --os-cloud default ${FLAVOR_NAME} + fi + + if openstack group show --os-cloud default ${GROUP_NAME}; then + openstack group delete \ + --os-cloud default ${GROUP_NAME} + fi +} + +pushd ${HORIZONTEST_DIR} +GIT_CMD_ARGS=() +if [[ ${REPO_URL} == *redhat.com* ]]; then + GIT_CMD_ARGS+=(-c http.sslVerify=false) +fi + +_trial=0 +_limit=5 +_delay=30 + +GIT_CLONE_CMD=(git "${GIT_CMD_ARGS[@]}" clone "${REPO_URL}" "${HORIZONTEST_DIR}/horizon") + +until ("${GIT_CLONE_CMD[@]}"); do + if [ "${_trial}" -lt "${_limit}" ]; then + _trial=$(( _trial + 1 )) + echo "Git clone failed; retrying in ${_delay} seconds..." + sleep "${_delay}" + else + exit 1 + fi +done +chown -R horizontest:horizontest horizon +pushd horizon +git "${GIT_CMD_ARGS[@]}" pull --rebase +git checkout ${HORIZON_REPO_BRANCH} + +clean_leftover_images +create_custom_resources +pushd ${HORIZONTEST_DIR}/horizon/openstack_dashboard/test/integration_tests/ + +# set variables in horizon.conf +crudini --set horizon.conf dashboard dashboard_url ${DASHBOARD_URL}/dashboard/ +crudini --set horizon.conf dashboard auth_url ${AUTH_URL} +crudini --set horizon.conf dashboard help_url ${HELP_URL} +crudini --set horizon.conf identity username ${USER_NAME} +crudini --set horizon.conf identity password ${PASSWORD} +crudini --set horizon.conf identity home_project ${PROJECT_NAME} +crudini --set horizon.conf identity admin_username ${ADMIN_USERNAME} +crudini --set horizon.conf identity admin_password ${ADMIN_PASSWORD} +crudini --set horizon.conf image http_image ${IMAGE_URL} +crudini --set horizon.conf image images_list ${IMAGE_FILE_NAME} +crudini --set horizon.conf launch_instances image_name "${IMAGE_FILE_NAME_WITH_SIZE}" +crudini --set horizon.conf selenium message_wait ${SELENIUM_MESSAGE_WAIT} +crudini --set horizon.conf selenium explicit_wait ${SELENIUM_EXPLICIT_WAIT} +crudini --set horizon.conf selenium page_timeout ${SELENIUM_PAGE_TIMEOUT} +crudini --set horizon.conf selenium implicit_wait ${SELENIUM_IMPLICIT_WAIT} +crudini --set horizon.conf network subnet_name ${SUBNET_NAME} +crudini --set horizon.conf theme project_name_xpath "${PROJECT_NAME_XPATH}" +crudini --set horizon.conf theme help_sequence "${HELP_SEQUENCE}" +crudini --set horizon.conf theme test_material_theme "${TEST_MATERIAL_THEME}" +crudini --set horizon.conf theme user_name_xpath "${USER_NAME_XPATH}" +crudini --set horizon.conf theme browse_left_panel_main "${BROWSE_LEFT_PANEL_MAIN}" +crudini --set horizon.conf theme browse_left_panel_sec "${BROWSE_LEFT_PANEL_SEC}" +crudini --set horizon.conf theme b_l_p_sec_line_xpath "${BLP_SEC_LINE_XPATH}" +crudini --set horizon.conf theme b_l_p_sec_line_req_btn "${BLP_SEC_LINE_REQ_BTN}" +crudini --set horizon.conf theme b_l_p_sidebar_xpath "${BLP_SIDEBAR_XPATH}" +crudini --set horizon.conf theme project_text_xpath "${PROJECT_TEXT_XPATH}" +crudini --set horizon.conf services_regions region_name_xpath "${SERVICES_REGION_NAME_XPATH}" +crudini --set horizon.conf services_regions region_btn_text_pattern "${SERVICES_REGION_BTN_TEXT_PATTERN}" +crudini --set horizon.conf services_regions region_dropdown_xpath "${SERVICES_REGION_DROPDOWN_XPATH}" +popd + +# run horizon selenium tests +INTEGRATION_TESTS=1 SELENIUM_HEADLESS=True pytest openstack_dashboard/test/selenium/integration/ -k "${EXTRA_FLAG}" \ + --junitxml="test_reports/ui_integration_test_results.xml" \ + --html="test_reports/ui_integration_test_results.html" --self-contained-html +RETURN_VALUE=$? + +# copy logs to external_files +echo "Copying logs file" +LOG_DIR=${HORIZONTEST_DIR}/external_files/${HORIZON_LOGS_DIR_NAME}/ +mkdir -p ${LOG_DIR} +cp -rf ${HORIZONTEST_DIR}/horizon/test_reports/* ${LOG_DIR} + +delete_custom_resources + +# Keep pod in running state when in debug mode +if [ ${HORIZONTEST_DEBUG_MODE} == true ]; then + sleep infinity +fi + +exit ${RETURN_VALUE} diff --git a/containers/horizontest/horizontest/src/.gitkeep b/containers/horizontest/horizontest/src/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/containers/horizontest/requirements.lock b/containers/horizontest/requirements.lock new file mode 120000 index 00000000..c466dc02 --- /dev/null +++ b/containers/horizontest/requirements.lock @@ -0,0 +1 @@ +requirements.lock.master \ No newline at end of file diff --git a/containers/horizontest/requirements.lock.master b/containers/horizontest/requirements.lock.master new file mode 100644 index 00000000..eae9725a --- /dev/null +++ b/containers/horizontest/requirements.lock.master @@ -0,0 +1,136 @@ +asgiref==3.12.1 +attrs==26.1.0 +autopage==0.6.0 +babel==2.18.0 +certifi==2026.7.22 +cffi==2.1.1 +charset-normalizer==3.5.0 +cliff==4.15.0 +cmd2==4.2.0 +debtcollector==3.1.0 +decorator==5.3.1 +django==5.2.17 +django-appconf==1.2.0 +django-compressor==4.6.0 +django-debreach==2.1.0 +dogpile-cache==1.5.0 +fasteners==0.20 +futurist==3.5.0 +h11==0.16.0 +horizon==26.0.0 +idna==3.18 +iniconfig==2.3.0 +iso8601==2.1.0 +jinja2==3.1.6 +jmespath==1.1.0 +jsonpatch==1.33 +jsonpointer==3.1.1 +jsonschema==4.26.0 +jsonschema-specifications==2025.9.1 +keystoneauth1==5.15.0 +libsass==0.23.0 +markdown-it-py==4.2.0 +markupsafe==3.0.3 +mdurl==0.1.2 +msgpack==1.2.1 +netaddr==1.3.0 +openstacksdk==4.18.0 +os-service-types==1.9.0 +osc-lib==4.7.0 +oslo-concurrency==7.6.1 +oslo-config==10.7.0 +oslo-context==6.5.0 +oslo-i18n==6.9.0 +oslo-log==8.3.0 +oslo-policy==6.0.0 +oslo-serialization==5.11.0 +oslo-upgradecheck==2.8.0 +oslo-utils==10.1.1 +osprofiler==4.4.0 +outcome==1.3.0.post0 +packaging==26.3 +pbr==7.0.3 +pillow==12.3.0 +platformdirs==4.11.3 +pluggy==1.6.0 +prettytable==3.18.0 +prompt-toolkit==3.0.53 +psutil==7.2.2 +pycparser==3.0 +pygments==2.20.0 +pyopenssl==24.2.1 +pyparsing==3.3.2 +pyperclip==1.11.0 +pysocks==1.7.1 +pytest==9.1.1 +pytest-django==4.14.0 +pytest-html==4.2.0 +pytest-metadata==3.1.1 +python-cinderclient==9.9.0 +python-dateutil==2.9.0.post0 +python-glanceclient==4.12.0 +python-keystoneclient==5.8.0 +python-manilaclient==6.2.0 +python-novaclient==18.13.0 +python-openstackclient==10.2.1 +python-swiftclient==4.10.0 +pyyaml==6.0.3 +qrcode==8.2 +rcssmin==1.2.2 +referencing==0.37.0 +requests==2.34.2 +rfc3986==2.0.0 +rich==15.0.0 +rich-argparse==1.8.0 +rjsmin==1.2.5 +rpds-py==2026.6.3 +selenium==4.47.0 +semantic-version==2.10.0 +six==1.17.0 +sniffio==1.3.1 +sortedcontainers==2.4.0 +sqlparse==0.6.0 +stevedore==5.9.1 +testtools==2.9.1 +trio==0.34.0 +trio-websocket==0.12.2 +typing-extensions==4.16.0 +urllib3==2.7.0 +warlock==2.1.0 +wcwidth==0.8.2 +webob==1.8.11 +websocket-client==1.9.0 +wheel==0.48.0 +wrapt==2.3.0 +wsproto==1.3.2 +xstatic==1.0.3 +xstatic-angular==1.8.2.3 +xstatic-angular-bootstrap==2.5.0.1 +xstatic-angular-fileupload==12.2.13.2 +xstatic-angular-gettext==2.4.1.1 +xstatic-angular-lrdragndrop==1.0.2.7 +xstatic-angular-schema-form==0.8.13.1 +xstatic-bootstrap-datepicker==1.4.0.1 +xstatic-bootstrap-scss==3.4.1.1 +xstatic-bootswatch==3.3.7.1 +xstatic-d3==3.5.17.1 +xstatic-font-awesome==6.2.1.2 +xstatic-hogan==2.0.0.5 +xstatic-jasmine==2.4.1.3 +xstatic-jquery==3.7.1.1 +xstatic-jquery-migrate==3.3.2.2 +xstatic-jquery-quicksearch==2.0.3.3 +xstatic-jquery-tablesorter==2.14.5.3 +xstatic-jquery-ui==1.13.0.2 +xstatic-jsencrypt==2.3.1.2 +xstatic-mdi==1.6.50.3 +xstatic-objectpath==1.2.1.1 +xstatic-rickshaw==1.5.1.3 +xstatic-roboto-fontface==0.8.0.1 +xstatic-smart-table==1.4.13.3 +xstatic-spin==1.2.5.3 +xstatic-term-js==0.0.7.1 +xstatic-tv4==1.2.7.1 +xvfbwrapper==0.2.29 + +# The following packages are considered to be unsafe in a requirements file: diff --git a/containers/horizontest/rpms.in.yaml b/containers/horizontest/rpms.in.yaml new file mode 100644 index 00000000..96336eb2 --- /dev/null +++ b/containers/horizontest/rpms.in.yaml @@ -0,0 +1,40 @@ +contentOrigin: + repofiles: + - ./rpms.repo +context: + bare: true + +# +# To update rpms.lock.yaml: +# rpm-lockfile-prototype rpms.in.yaml +# + +arches: + - x86_64 + - aarch64 + +packages: + - cargo + - firefox + - freetype + - freetype-devel + - gcc + - gcc-c++ + - git-core + - libffi-devel + - libjpeg-turbo + - libjpeg-turbo-devel + - libtiff + - libtiff-devel + - libwebp + - libwebp-devel + - openssl-devel + - python3 + - python3-cryptography-43.0.0-4.el10 + - python3-devel + - python3-pip + - python3-setuptools + - rust + - tar + - xorg-x11-server-Xvfb + - zlib-devel diff --git a/containers/horizontest/sources.txt b/containers/horizontest/sources.txt new file mode 100644 index 00000000..732d3514 --- /dev/null +++ b/containers/horizontest/sources.txt @@ -0,0 +1,2 @@ +# +master upper-constraints https://opendev.org/openstack/requirements.git master 3353677727f8351bc58ba98efafe6bc884a0257c diff --git a/containers/horizontest/src/.gitkeep b/containers/horizontest/src/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/containers/horizontest/upper-constraints.txt b/containers/horizontest/upper-constraints.txt new file mode 120000 index 00000000..b2ebb3b7 --- /dev/null +++ b/containers/horizontest/upper-constraints.txt @@ -0,0 +1 @@ +upper-constraints.txt.master \ No newline at end of file diff --git a/containers/horizontest/upper-constraints.txt.master b/containers/horizontest/upper-constraints.txt.master new file mode 100644 index 00000000..24900a34 --- /dev/null +++ b/containers/horizontest/upper-constraints.txt.master @@ -0,0 +1,582 @@ +# WARNING: OpenStack makes no security guarantees about third-party +# dependencies listed here, and does not keep track of any +# vulnerabilities they contain. Versions of these dependencies are +# frozen at each coordinated release in order to stabilize upstream +# testing, and can contain known vulnerabilities. Consumers are +# *STRONGLY* encouraged to rely on curated distributions of OpenStack +# or manage security patching of dependencies themselves. +voluptuous===0.16.0 +chardet===6.0.0.post1 +enum-compat===0.0.3 +netmiko===4.7.0 +sshtunnel===0.4.0 +PasteDeploy===3.1.0 +Routes===2.5.1 +rtslib-fb===2.2.4 +oslo.limit===2.12.0 +restructuredtext_lint===2.0.2 +yarl===1.24.5 +tzdata===2026.3 +smmap===5.0.3 +confget===5.1.2 +XStatic-Angular-Bootstrap===2.5.0.1 +WebOb===1.8.11 +sphinxcontrib-actdiag===3.0.0 +pecan===1.8.0 +os-api-ref===3.2.0 +python-ldap===3.4.7 +oslo.concurrency===7.6.1 +websocket-client===1.9.0 +osprofiler===4.4.0 +os-resource-classes===1.1.0 +mypy_extensions===1.1.0 +tabulate===0.10.0 +python-ironic-inspector-client===5.4.0 +lxml===6.1.1 +vintage===0.4.1 +rst2txt===1.1.0 +setproctitle===1.3.7 +pytest===9.1.1 +a2wsgi===1.10.10 +python-slugify===8.0.4 +cursive===0.3.0 +oslo.service===4.8.0 +django-appconf===1.2.0 +ntc_templates===9.2.0 +sphinxcontrib-nwdiag===2.0.0 +rbd-iscsi-client===0.1.8 +alabaster===1.0.0 +multidict===6.7.1 +pbr===7.0.3 +munch===4.0.0 +waiting===1.5.0 +attrs===26.1.0 +jwcrypto===1.5.8 +Pint===0.25.3 +oslo.i18n===6.9.0 +jsonpath-rw-ext===1.2.2 +python-mistralclient===6.2.0 +oslo.context===6.5.0 +rcssmin===1.2.2 +pycadf===4.1.0 +grpcio===1.83.0 +sniffio===1.3.1 +fixtures===4.3.2 +neutron-lib===5.0.0 +XStatic-FileSaver===1.3.2.1 +jaraco.functools===4.6.0 +oslo.metrics===0.16.0 +storage-interfaces===1.0.5 +pydantic===2.13.4 +pystache===0.6.8 +XStatic-Font-Awesome===6.2.1.2 +aiohttp===3.14.3 +waitress===3.0.2 +os-refresh-config===14.0.1 +pysnmp===7.1.28 +Mako===1.4.1 +sphinxcontrib-htmlhelp===2.1.0 +XStatic-jQuery===3.7.1.1 +sphinx-copybutton===0.5.2 +beartype===0.22.9 +ddt===1.7.2 +pyserial===3.5 +moto===5.2.2 +infi.dtypes.wwn===0.2.0 +awscrt===0.36.2 +pcre2===0.7.1 +python-freezerclient===6.4.0 +python-vitrageclient===5.4.0 +py-pure-client===1.93.0 +krest===1.3.8 +psycopg2===2.9.12 +networkx===3.6.1 +cheroot===11.1.2 +XStatic-Angular===1.8.2.3 +zuul-sphinx===0.8.1 +ply===3.11 +google-api-core===2.34.0 +requests-toolbelt===1.0.0 +simplejson===4.1.1 +python-swiftclient===4.10.0 +pyOpenSSL===24.2.1 +typing-inspection===0.4.4 +monasca-common===3.8.0 +hyperframe===6.1.0 +zeroconf===0.150.0 +scipy===1.17.1 +opentelemetry-exporter-otlp===1.44.0 +rsd-lib===1.2.0 +XStatic-Jasmine===2.4.1.3 +googleapis-common-protos===1.75.1 +python-glanceclient===4.12.0 +prometheus_client===0.26.0 +jaraco.classes===3.4.0 +debtcollector===3.1.0 +responses===0.26.2 +prompt_toolkit===3.0.53 +croniter===6.2.4 +horizon===26.0.0 +octavia-lib===3.12.0 +python-watcherclient===4.10.0 +MarkupSafe===3.0.3 +doc8===2.0.0 +pymongo===4.17.0 +python-cloudkittyclient===6.1.0 +soupsieve===2.9.2 +sqlparse===0.6.0 +oslotest===6.1.1 +jsonpointer===3.1.1 +defusedxml===0.7.1 +opentelemetry-sdk===1.44.0 +netaddr===1.3.0 +pyghmi===1.6.19 +sphinxcontrib-blockdiag===3.0.0 +aiosqlite===0.22.1 +priority===2.0.0 +gnocchiclient===7.2.0 +wcwidth===0.8.2 +sphinxcontrib.datatemplates===0.11.0 +jsonpath-rw===1.4.0 +prettytable===3.18.0 +vine===5.1.0 +pathspec===1.1.1 +taskflow===6.3.0 +arrow===1.4.0 +semantic-version===2.10.0 +ConfigArgParse===1.7.5 +async-timeout===5.0.1 +virtualbmc===3.3.0 +SQLAlchemy===2.0.52 +pyroute2===0.8.1 +google-auth===2.56.3 +kazoo===2.11.0 +pyspnego===0.12.1 +trio-websocket===0.12.2 +XStatic-roboto-fontface===0.8.0.1 +pyudev===0.24.4 +eventlet===0.41.2 +openstack-doc-tools===4.0.3 +oslo.messaging===18.2.0 +jira===3.10.5 +PyJWT===2.13.0 +typing_extensions===4.16.0 +zVMCloudConnector===1.6.3 +paramiko===4.0.0 +ifaddr===0.2.0 +reno===4.1.0 +ncclient===0.7.1 +imagesize===2.0.0 +pydot===4.0.1 +urllib3===2.7.0 +graphviz===0.21 +PyKMIP===0.10.0 +python-observabilityclient===1.3.0 +whereto===0.6.0 +networking-generic-switch===10.0.0 +pywbem===1.9.0 +python-subunit===1.4.6 +pycparser===3.0 +mock===5.2.0 +PyYAML===6.0.3 +beautifulsoup4===4.15.0 +ovs===3.7.1 +cryptography===43.0.3 +httpcore===1.0.9 +URLObject===3.0.0 +psycopg2-binary===2.9.12 +glance_store===5.6.0 +openstack-release-test===8.5.0 +requests-mock===1.12.1 +os-apply-config===14.0.1 +gunicorn===26.0.0 +storpool===7.3.0 +textfsm===2.1.0 +python-3parclient===4.4 +libvirt-python===12.6.0 +python-zunclient===5.4.0 +tzlocal===5.4.4 +sysv_ipc===1.2.0 +sphinxcontrib-jsmath===1.0.1 +django_compressor===4.6.0 +awscurl===0.44 +trio===0.34.0 +python-novaclient===18.13.0 +pact===1.12.0 +bcrypt===5.0.0 +os-client-config===2.3.0 +XStatic-Angular-Gettext===2.4.1.1 +Deprecated===1.3.1 +h11===0.16.0 +Pygments===2.20.0 +XStatic-Hogan===2.0.0.5 +api_object_schema===2.0.0 +XStatic-objectpath===1.2.1.1 +python-manilaclient===6.2.0 +sphinxcontrib-serializinghtml===2.0.0 +requests===2.34.2 +snowballstemmer===3.1.1 +Jinja2===3.1.6 +XStatic-Bootstrap-SCSS===3.4.1.1 +pyzabbix===1.3.1 +ptyprocess===0.7.0 +amqp===5.3.1 +ruamel.yaml===0.19.1 +websockify===0.13.0 +gssapi===1.11.1 +XStatic-JQuery.quicksearch===2.0.3.3 +pyasn1_modules===0.4.2 +mpmath===1.3.0 +python-binary-memcached===0.32.0 +jaraco.context===6.1.2 +django-debreach===2.1.0 +sphinx-feature-classification===2.1.0 +XStatic-JQuery-Migrate===3.3.2.2 +pytest-html===4.2.0 +appdirs===1.4.4 +google-auth-httplib2===0.4.1 +daiquiri===3.4.0 +influxdb===5.3.2 +funcparserlib===2.0.0a0 +passlib===1.7.4 +cliff===4.15.0 +os-brick===7.1.0 +valkey===6.1.1 +scp===0.16.1 +lark===1.3.1 +python-zaqarclient===4.6.0 +ldappool===3.0.0 +hpack===4.2.0 +joblib===1.5.3 +roman-numerals===4.1.0 +google-api-python-client===2.198.0 +castellan===5.8.0 +oslo.versionedobjects===3.11.0 +enmerkar===0.7.1 +webcolors===25.10.0 +aodhclient===3.11.0 +autobahn===26.7.1 +SQLAlchemy-Utils===0.42.1 +retryz===0.1.9 +pluggy===1.6.0 +coverage===7.15.4 +pyee===13.0.1 +freezegun===1.5.5 +mdurl===0.1.2 +toml===0.10.2 +pycdlib===1.20.0 +pyperclip===1.11.0 +cassandra-driver===3.30.1 +XStatic-Angular-Schema-Form===0.8.13.1 +opentelemetry-exporter-otlp-proto-http===1.44.0 +gabbi===4.2.0 +nwdiag===3.0.0 +XStatic-bootswatch===3.3.7.1 +annotated-types===0.8.0 +pytest-xdist===3.8.0 +XStatic-JS-Yaml===3.13.1.2 +XStatic-term.js===0.0.7.1 +oslo.log===8.3.0 +nodeenv===1.10.0 +gossip===2.5.0 +suds-community===1.2.0 +os_vif===5.2.0 +qrcode===8.2 +oslo.middleware===8.2.0 +XStatic-mdi===1.6.50.3 +pydantic_core===2.46.4 +uritemplate===4.2.0 +docutils===0.21.2 +threadpoolctl===3.6.0 +os-ken===4.2.2 +ujson===5.13.0 +selenium===4.47.0 +pytest-subtests===0.15.0 +mistral-lib===3.5.1 +dogtag-pki===11.2.1 +XStatic-Angular-UUID===0.0.4.1 +dfs_sdk===1.2.27 +sphinxcontrib-seqdiag===3.0.0 +os-win===5.9.0 +capacity===1.3.14 +playwright===1.62.0 +markdown-it-py===4.2.0 +retrying===1.4.2 +python-discovery===1.5.2 +platformdirs===4.11.3 +pydotplus===2.0.2 +boto3===1.35.99 +jeepney===0.9.0 +stestr===4.2.1 +pillow===12.3.0 +infoblox-client===0.6.2 +oslo.serialization===5.11.0 +warlock===2.1.0 +exabgp===5.0.10 +aiomysql===0.3.2 +sphinxcontrib-httpdomain===2.0.0 +metalsmith===2.5.0 +s3transfer===0.10.4 +text-unidecode===1.3 +sphinxcontrib-svg2pdfconverter===2.1.0 +oslo.vmware===4.10.0 +autopage===0.6.0 +gitdb===4.0.12 +python-monascaclient===2.8.0 +opentelemetry-api===1.44.0 +frozenlist===1.8.0 +automaton===3.4.0 +os-service-types===1.9.0 +keyring===25.7.0 +elementpath===4.8.0 +wsgi_intercept===1.13.1 +jsonschema-specifications===2025.9.1 +testscenarios===0.6.2 +sphinxcontrib-pecanwsme===0.11.0 +sadisplay===0.4.9 +enum34===1.1.10 +infinisdk===289.1.4 +rich-argparse===1.8.0 +packaging===26.3 +opentelemetry-exporter-otlp-proto-grpc===1.44.0 +psutil===7.2.2 +txaio===26.6.1 +elasticsearch===9.5.0 +asgiref===3.12.1 +XStatic-JQuery.TableSorter===2.14.5.3 +pifpaf===3.4.0 +blockdiag===3.0.0 +testtools===2.9.1 +infi.dtypes.iqn===0.4.0 +jsonpath-ng===1.8.0 +XStatic-tv4===1.2.7.1 +XStatic-JSEncrypt===2.3.1.2 +python-cinderclient===9.9.0 +keystonemiddleware===13.0.0 +django-formtools===2.7 +XStatic-Spin===1.2.5.3 +rich===15.0.0 +os-traits===3.9.0 +typepy===1.3.5 +SecretStorage===3.5.0 +XStatic-Rickshaw===1.5.1.3 +iso8601===2.1.0 +tooz===9.0.1 +idna===3.18 +Hypercorn===0.18.0 +yamlloader===1.6.0 +protobuf===7.35.1 +sushy===5.13.0 +python-neutronclient===13.0.0 +pika===1.4.4 +oslo.cache===4.3.0 +WebTest===3.0.7 +os-collect-config===14.0.1 +edgegrid-python===2.0.7 +python-octaviaclient===3.14.0 +pysaml2===7.5.4 +requests-oauthlib===2.0.0 +oslo.reports===3.9.0 +bitmath===2.1.1 +ceilometermiddleware===3.13.0 +testrepository===0.0.22 +sympy===1.14.0 +Logbook===1.10.1 +PyNaCl===1.6.2 +osc-lib===4.7.0 +more-itertools===11.1.0 +seqdiag===3.0.0 +numpy===2.4.6 +msgpack===1.2.1 +Sphinx===9.0.4 +oslo.config===10.7.0 +openstackdocstheme===3.6.0 +osc-placement===4.9.0 +rpds-py===2026.6.3 +zake===0.2.2 +flux===1.4.0 +flexparser===0.4 +krb5===0.9.0 +PyMySQL===1.2.0 +uhashring===2.5 +kubernetes===36.0.3 +httplib2===0.32.0 +betamax===0.9.0 +construct===2.10.70 +pytest-metadata===3.1.1 +pyparsing===3.3.2 +geomet===1.1.0 +opentelemetry-exporter-otlp-proto-common===1.44.0 +distlib===0.4.3 +ast_serialize===0.8.0 +dogpile.cache===1.5.0 +python-barbicanclient===7.5.0 +salt===3008.0 +opentelemetry-semantic-conventions===0.65b0 +blinker===1.9.0 +WSME===0.13.0 +oslo.upgradecheck===2.8.0 +sherlock===0.4.1 +stevedore===5.9.1 +botocore===1.35.99 +xmltodict===1.0.4 +pyasn1===0.6.4 +oslo.rootwrap===7.10.0 +Django===5.2.17 +pexpect===4.9.0 +elastic-transport===9.4.2 +cmd2===4.2.0 +python-json-logger===4.1.0 +redis===8.1.0 +jmespath===1.1.0 +cbor2===6.1.4 +click===8.4.2 +XStatic-smart-table===1.4.13.3 +kuryr-lib===3.4.1 +jsonpatch===1.33 +respx===0.23.1 +libsass===0.23.0 +os-testr===3.0.0 +cotyledon===2.2.0 +xattr===1.3.0 +systemd-python===235 +python-memcached===1.62 +openstacksdk===4.18.0 +infi.dtypes.nqn===0.1.0 +six===1.17.0 +h2===4.4.1 +dulwich===1.2.12 +sentinels===1.1.1 +kombu===5.6.2 +distro===1.9.0 +zstd===1.5.7.2 +yaql===3.2.0 +durationpy===0.10 +requestsexceptions===1.4.0 +testresources===2.1.2 +falcon===4.3.1 +tomlkit===0.15.1 +etcd3gw===2.7.0 +Flask-RESTful===0.3.10 +GitPython===3.1.59 +python-ironicclient===6.2.0 +babel===2.18.0 +XStatic===1.0.3 +XStatic-Angular-FileUpload===12.2.13.2 +python-openstackclient===10.2.1 +pyzmq===27.1.0 +oslo.db===18.1.0 +simplegeneric===0.8.1 +yappi===1.7.6 +mbstrdecoder===1.1.5 +wsproto===1.3.2 +pymemcache===4.0.0 +wrapt===2.3.0 +PySocks===1.7.1 +oslo.privsep===3.12.0 +sphinxcontrib-apidoc===0.6.0 +oslo.policy===6.0.0 +hvac===2.4.0 +pyeclib===1.8.0 +rfc3986===2.0.0 +tenacity===9.1.4 +invoke===3.0.3 +python-designateclient===7.0.0 +pytest-cov===7.1.0 +reactivex===5.1.0 +Paste===3.10.1 +pytest-django===4.14.0 +XStatic-Json2yaml===0.1.1.1 +boto===2.49.0 +hyperlink===21.0.0 +mitba===1.1.1 +python-masakariclient===8.8.0 +Werkzeug===3.1.8 +outcome===1.3.0.post0 +APScheduler===3.11.3 +xmlschema===2.5.1 +python-troveclient===8.10.0 +hpe-storage-flowkit-py===0.9.5 +cachez===0.1.2 +XStatic-Bootstrap-Datepicker===1.4.0.1 +netifaces===0.11.0 +propcache===0.5.2 +cachetools===7.1.7 +flexcache===0.3 +sphinxcontrib-qthelp===2.0.0 +keystoneauth1===5.15.0 +statsd===4.0.1 +proto-plus===1.28.3 +python-keystoneclient===5.8.0 +diskimage-builder===3.42.0 +py-consul===1.7.1 +heat-translator===3.5.0 +python-magnumclient===4.11.0 +docker===7.2.0 +repoze-lru===0.8 +storops===1.2.12 +anyio===4.14.2 +aiosignal===1.4.0 +XStatic-Angular-lrdragndrop===1.0.2.7 +ovsdbapp===2.19.0 +aniso8601===10.0.1 +rjsmin===1.2.5 +icalendar===7.2.2 +configparser===7.2.0 +decorator===5.3.1 +DateTimeRange===2.3.2 +cffi===2.1.1 +python-cyborgclient===2.8.0 +futurist===3.5.0 +jsonschema===4.26.0 +sphinxcontrib-devhelp===2.0.0 +python-blazarclient===4.5.0 +alembic===1.19.1 +execnet===2.1.2 +sphinxcontrib-programoutput===0.20 +storpool.spopenstack===3.2.0 +dnspython===2.8.0 +oauthlib===3.3.1 +zipp===4.1.0 +greenlet===3.5.5 +XStatic-Angular-Vis===4.16.0.1 +iniconfig===2.3.0 +referencing===0.37.0 +confluent-kafka===2.15.0 +backports.tarfile===1.2.0 +narwhals===2.24.0 +xvfbwrapper===0.2.29 +influxdb-client===1.50.0 +tosca-parser===2.14.0 +python-consul2===0.1.5 +charset-normalizer===3.5.0 +Flask===3.1.3 +httpx===0.28.1 +sqlalchemy-filters===0.13.0 +sphinxcontrib-runcmd===0.2.0 +confspirator===0.3.0 +fasteners===0.20 +importlib_metadata===9.0.0 +sortedcontainers===2.4.0 +microversion_parse===2.1.0 +python-linstor===1.29.0 +filelock===3.32.3 +python-tackerclient===2.5.0 +python-heatclient===5.2.0 +oslo.utils===10.1.1 +requests-kerberos===0.15.0 +itsdangerous===2.2.0 +XStatic-jquery-ui===1.13.0.2 +monasca-statsd===2.7.0 +python-dateutil===2.9.0.post0 +virtualenv===21.7.4 +colorama===0.4.6 +confetti===2.5.3 +ironic-lib===7.0.0 +aiohappyeyeballs===2.7.1 +pytz===2026.3.post1 +opentelemetry-proto===1.44.0 +XStatic-D3===3.5.17.1 +actdiag===3.0.0 +sphinxcontrib-applehelp===2.0.0 +scikit-learn===1.9.0