diff --git a/.github/workflows/containers.yaml b/.github/workflows/containers.yaml index ce1eb3696..f977fd3a0 100644 --- a/.github/workflows/containers.yaml +++ b/.github/workflows/containers.yaml @@ -124,7 +124,6 @@ jobs: # if you add a container here, add it to the 'clean_containers' job below container: - name: ironic-nautobot-client - - name: nova-flavors - name: ansible - name: understack-tests @@ -186,7 +185,6 @@ jobs: - openstack-client - dnsmasq - ironic-nautobot-client - - nova-flavors - ansible - understack-tests diff --git a/components/ironic/values.yaml b/components/ironic/values.yaml index 22d38e957..3981f7708 100644 --- a/components/ironic/values.yaml +++ b/components/ironic/values.yaml @@ -88,7 +88,7 @@ conf: loader_file_paths: "snponly.efi:/usr/lib/ipxe/snponly.efi" inspector: extra_kernel_params: ipa-collect-lldp=1 - hooks: "$default_hooks,parse-lldp,local-link-connection,physical-network" + hooks: "$default_hooks,parse-lldp,local-link-connection,physical-network,resource-class" # enable sensors and metrics for redfish metrics - https://docs.openstack.org/ironic/latest/admin/drivers/redfish/metrics.html sensor_data: send_sensor_data: true @@ -217,6 +217,8 @@ pod: mountPath: /var/lib/dnsmasq/ - name: understack-data mountPath: /var/lib/understack + - name: device-types + mountPath: /var/lib/understack/device-types - name: ironic-etc-snippets mountPath: /etc/ironic/ironic.conf.d readOnly: true @@ -230,6 +232,9 @@ pod: - name: understack-data persistentVolumeClaim: claimName: understack-data + - name: device-types + configMap: + name: device-types - name: ironic-etc-snippets projected: sources: diff --git a/components/openstack/templates/automation-baremetal-manage.yaml.tpl b/components/openstack/templates/automation-baremetal-manage.yaml.tpl new file mode 100644 index 000000000..6558ef920 --- /dev/null +++ b/components/openstack/templates/automation-baremetal-manage.yaml.tpl @@ -0,0 +1,47 @@ +--- +apiVersion: generators.external-secrets.io/v1alpha1 +kind: Password +metadata: + name: "baremetal-manage-{{ .Values.regionName }}" +spec: + length: 32 + digits: 6 + symbols: 4 + symbolCharacters: "~!@#$%^*()_+-={}[]<>?" +--- +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: "baremetal-manage-{{ .Values.regionName }}" +spec: + refreshInterval: 20160m + target: + name: baremetal-manage + template: + engineVersion: v2 + type: Opaque + metadata: + labels: + understack.rackspace.com/keystone-role: infra-readwrite + understack.rackspace.com/keystone-user: "baremetal-manage-{{ .Values.regionName }}" + data: + password: "{{ `{{ .password }}` }}" + clouds.yaml: | + clouds: + understack: + auth: + auth_url: "{{ .Values.keystoneUrl }}" + user_domain_name: "service" + username: "baremetal-manage-{{ .Values.regionName }}" + password: "{{ `{{ .password }}` }}" + project_domain_name: "infra" + project_name: "baremetal" + region_name: "{{ .Values.regionName }}" + interface: "public" + identity_api_version: 3 + dataFrom: + - sourceRef: + generatorRef: + apiVersion: generators.external-secrets.io/v1alpha1 + kind: Password + name: "baremetal-manage-{{ .Values.regionName }}" diff --git a/components/openstack/templates/secretstore-openstack.yaml.tpl b/components/openstack/templates/secretstore-openstack.yaml.tpl index a70a6a107..d0483d1e4 100644 --- a/components/openstack/templates/secretstore-openstack.yaml.tpl +++ b/components/openstack/templates/secretstore-openstack.yaml.tpl @@ -25,6 +25,7 @@ rules: - list - watch resourceNames: + - baremetal-manage - svc-acct-argoworkflow - svc-acct-netapp - cinder-netapp-config diff --git a/containers/nova-flavors/Dockerfile b/containers/nova-flavors/Dockerfile deleted file mode 100644 index 437c10712..000000000 --- a/containers/nova-flavors/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -FROM ghcr.io/rackerlabs/understack/argo-python3.12.2-alpine3.19 AS builder - -RUN --mount=type=cache,target=/var/cache/apk apk add --virtual build-deps gcc python3-dev musl-dev linux-headers -RUN --mount=type=cache,target=/root/.cache/pip pip install 'wheel==0.43.0' -RUN --mount=type=cache,target=/root/.cache/pip \ - python -m venv /opt/poetry && \ - /opt/poetry/bin/pip install 'poetry==1.7.1' && \ - /opt/poetry/bin/poetry self add 'poetry-dynamic-versioning[plugin]==1.3.0' - -# copy in the code -COPY --chown=appuser:appgroup operators/nova-flavors /app -COPY --chown=appuser:appgroup python/understack-flavor-matcher /understack-flavor-matcher -# need watchdog and psutil built AS a wheel -RUN --mount=type=cache,target=/root/.cache/pip pip wheel --wheel-dir /app/dist watchdog psutil==6.1.1 -CMD ["nova-flavors-sync"] - -WORKDIR /app -RUN cd /app && /opt/poetry/bin/poetry build -f wheel && /opt/poetry/bin/poetry export --without-hashes -f requirements.txt -o dist/requirements.txt - -######################## PROD ######################## -FROM ghcr.io/rackerlabs/understack/argo-python3.12.2-alpine3.19 AS prod - -ENV FLAVORS_DIR="/flavors" -ENV NOVA_FLAVOR_MONITOR_LOGLEVEL="info" - -LABEL org.opencontainers.image.description="Nova-Flavors synchronizer" - -RUN mkdir -p /opt/venv/wheels/ -COPY --from=builder /app/dist/*.whl /app/dist/requirements.txt /opt/venv/wheels/ -COPY --chown=appuser:appgroup python/understack-flavor-matcher /python/understack-flavor-matcher - -RUN --mount=type=cache,target=/root/.cache/pip cd /app && /opt/venv/bin/pip install --find-links /opt/venv/wheels/ --only-binary watchdog --only-binary psutil -r /opt/venv/wheels/requirements.txt nova-flavors - -USER appuser -CMD ["nova-flavors-sync"] diff --git a/operators/nova-flavors/.env-example b/operators/nova-flavors/.env-example deleted file mode 100644 index 5b1a41990..000000000 --- a/operators/nova-flavors/.env-example +++ /dev/null @@ -1,8 +0,0 @@ -OS_USERNAME=flavorsync -OS_PASSWORD=abcd1234 -OS_AUTH_URL=https://keystone.environment.undercloud.rackspace.net/v3 -OS_USER_DOMAIN_NAME=service -OS_PROJECT_NAME=admin -OS_PROJECT_DOMAIN_NAME=default -FLAVORS_DIR=/home/someuser/flavors/ -FLAVORS_ENV=nonprod diff --git a/operators/nova-flavors/README.md b/operators/nova-flavors/README.md deleted file mode 100644 index e69de29bb..000000000 diff --git a/operators/nova-flavors/nova_flavors/__init__.py b/operators/nova-flavors/nova_flavors/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/operators/nova-flavors/nova_flavors/flavor_synchronizer.py b/operators/nova-flavors/nova_flavors/flavor_synchronizer.py deleted file mode 100644 index 8c144059e..000000000 --- a/operators/nova-flavors/nova_flavors/flavor_synchronizer.py +++ /dev/null @@ -1,99 +0,0 @@ -from functools import cached_property - -from flavor_matcher.flavor_spec import FlavorSpec -from novaclient import client as novaclient - -from nova_flavors.logger import setup_logger - -logger = setup_logger(__name__) - - -class FlavorSynchronizer: - def __init__( - self, - username: str | None = "", - password: str = "", - project_name: str | None = "admin", - project_domain_name: str = "default", - user_domain_name="service", - auth_url: str | None = None, - ) -> None: - self.username = username - self.password = password - self.project_name = str(project_name) - self.project_domain_name = str(project_domain_name) - self.user_domain_name = user_domain_name - self.auth_url = auth_url - - @cached_property - def _nova(self): - return novaclient.Client( - "2", - username=self.username, - password=self.password, - project_name=self.project_name, - project_domain_name=self.project_domain_name, - user_domain_name=self.user_domain_name, - auth_url=self.auth_url, - ) - - def reconcile(self, desired_flavors: list[FlavorSpec]): - if len(desired_flavors) < 1: - raise Exception(f"Empty desired_flavors list.") - - existing_flavors = self._nova.flavors.list() - for flavor in desired_flavors: - nova_flavor = next( - (flv for flv in existing_flavors if flv.name == flavor.name), - None, - ) - - update_needed = False - if nova_flavor: - logger.info(f"Flavor: {flavor.name} already exists. Syncing values") - if nova_flavor.ram != flavor.memory_mib: - logger.info( - f"{flavor.name} RAM mismatch - {nova_flavor.ram=} {flavor.memory_mib=}" - ) - update_needed = True - - if nova_flavor.disk != max(flavor.drives): - logger.info( - f"{flavor.name} Disk mismatch - {nova_flavor.disk=} {flavor.drives=}" - ) - update_needed = True - - if nova_flavor.vcpus != flavor.cpu_cores: - logger.info( - f"{flavor.name} CPU mismatch - {nova_flavor.vcpus=} {flavor.cpu_cores=}" - ) - update_needed = True - - if update_needed: - logger.debug( - f"{flavor.name} is outdated. Deleting so it can be recreated." - ) - nova_flavor.delete() - - else: - update_needed = True - - if update_needed: - logger.info(f"Creating {flavor.name}") - self._create(flavor) - - def _create(self, flavor: FlavorSpec): - nova_flavor = self._nova.flavors.create( - flavor.name, - flavor.memory_mib, - flavor.cpu_cores, - min(flavor.drives), - ) - nova_flavor.set_keys( - { - "resources:DISK_GB": 0, - "resources:MEMORY_MB": 0, - "resources:VCPU": 0, - flavor.baremetal_nova_resource_class: 1, - } - ) diff --git a/operators/nova-flavors/nova_flavors/logger.py b/operators/nova-flavors/nova_flavors/logger.py deleted file mode 100644 index c93b16910..000000000 --- a/operators/nova-flavors/nova_flavors/logger.py +++ /dev/null @@ -1,19 +0,0 @@ -import logging - - -def setup_logger(name: str | None = None, level: int = logging.DEBUG): - """Standardize our logging. - - Configures the root logger to prefix messages with a timestamp - and to output the log level we want to see by default. - - params: - name: logger hierarchy or root logger - level: default log level (DEBUG) - """ - logging.basicConfig( - format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", - datefmt="%Y-%m-%d %H:%M:%S %z", - level=level, - ) - return logging.getLogger(name) diff --git a/operators/nova-flavors/nova_flavors/reconcile.py b/operators/nova-flavors/nova_flavors/reconcile.py deleted file mode 100644 index f1d097eb4..000000000 --- a/operators/nova-flavors/nova_flavors/reconcile.py +++ /dev/null @@ -1,48 +0,0 @@ -import logging -import os -import time - -from flavor_matcher.flavor_spec import FlavorSpec -from watchdog.observers import Observer - -from nova_flavors.flavor_synchronizer import FlavorSynchronizer -from nova_flavors.logger import setup_logger -from nova_flavors.spec_changed_handler import SpecChangedHandler - -loglevel = getattr(logging, os.getenv("NOVA_FLAVOR_MONITOR_LOGLEVEL", "info").upper()) -logging.getLogger().setLevel(loglevel) -logger = setup_logger(__name__, level=loglevel) - - -def main(): - # nonprod vs prod - flavors_dir = os.getenv("FLAVORS_DIR", "") - if not os.path.isdir(flavors_dir): - raise ValueError(f"flavors_dir '{flavors_dir}' is not a directory") - synchronizer = FlavorSynchronizer( - username=os.getenv("OS_USERNAME", ""), - password=os.getenv("OS_PASSWORD", ""), - project_name=os.getenv("OS_PROJECT_NAME", "admin"), - project_domain_name=os.getenv("OS_PROJECT_DOMAIN_NAME", "default"), - user_domain_name=os.getenv("OS_USER_DOMAIN_NAME", "service"), - auth_url=os.getenv("OS_AUTH_URL"), - ) - - handler = SpecChangedHandler( - synchronizer, lambda: FlavorSpec.from_directory(flavors_dir) - ) - observer = Observer() - observer.schedule(handler, flavors_dir, recursive=True) - logger.info(f"Watching for changes in {flavors_dir}") - observer.start() - - try: - while True: - time.sleep(1) - finally: - observer.stop() - observer.join() - - -if __name__ == "__main__": - main() diff --git a/operators/nova-flavors/nova_flavors/spec_changed_handler.py b/operators/nova-flavors/nova_flavors/spec_changed_handler.py deleted file mode 100644 index 48e0e07c0..000000000 --- a/operators/nova-flavors/nova_flavors/spec_changed_handler.py +++ /dev/null @@ -1,38 +0,0 @@ -import time -from typing import Callable - -from watchdog.events import DirModifiedEvent -from watchdog.events import FileModifiedEvent -from watchdog.events import FileSystemEventHandler - -from nova_flavors.flavor_synchronizer import FlavorSynchronizer -from nova_flavors.logger import setup_logger - -logger = setup_logger(__name__) - - -class SpecChangedHandler(FileSystemEventHandler): - COOLDOWN_SECONDS = 30 - - def __init__( - self, synchronizer: FlavorSynchronizer, flavors_cback: Callable - ) -> None: - self.last_call = None - self.synchronizer = synchronizer - self.flavors_cback = flavors_cback - - def on_modified(self, event: DirModifiedEvent | FileModifiedEvent) -> None: - if isinstance(event, DirModifiedEvent): - self._run(event) - - def _run(self, event): - now = time.time() - if not self.last_call: - self.last_call = now - else: - if self.last_call + self.COOLDOWN_SECONDS > now: - logger.debug("Cooldown period.") - return - self.last_call = now - logger.info(f"Flavors directory {event.src_path} has changed.") - self.synchronizer.reconcile(self.flavors_cback()) diff --git a/operators/nova-flavors/poetry.lock b/operators/nova-flavors/poetry.lock deleted file mode 100644 index 7087e9e00..000000000 --- a/operators/nova-flavors/poetry.lock +++ /dev/null @@ -1,996 +0,0 @@ -# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand. - -[[package]] -name = "certifi" -version = "2024.12.14" -description = "Python package for providing Mozilla's CA Bundle." -optional = false -python-versions = ">=3.6" -groups = ["main"] -files = [ - {file = "certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56"}, - {file = "certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"}, -] - -[[package]] -name = "charset-normalizer" -version = "3.4.1" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-win32.whl", hash = "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-win32.whl", hash = "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765"}, - {file = "charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85"}, - {file = "charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3"}, -] - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["test"] -markers = "sys_platform == \"win32\"" -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "coverage" -version = "7.10.7" -description = "Code coverage measurement for Python" -optional = false -python-versions = ">=3.9" -groups = ["test"] -files = [ - {file = "coverage-7.10.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fc04cc7a3db33664e0c2d10eb8990ff6b3536f6842c9590ae8da4c614b9ed05a"}, - {file = "coverage-7.10.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e201e015644e207139f7e2351980feb7040e6f4b2c2978892f3e3789d1c125e5"}, - {file = "coverage-7.10.7-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:240af60539987ced2c399809bd34f7c78e8abe0736af91c3d7d0e795df633d17"}, - {file = "coverage-7.10.7-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8421e088bc051361b01c4b3a50fd39a4b9133079a2229978d9d30511fd05231b"}, - {file = "coverage-7.10.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6be8ed3039ae7f7ac5ce058c308484787c86e8437e72b30bf5e88b8ea10f3c87"}, - {file = "coverage-7.10.7-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e28299d9f2e889e6d51b1f043f58d5f997c373cc12e6403b90df95b8b047c13e"}, - {file = "coverage-7.10.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c4e16bd7761c5e454f4efd36f345286d6f7c5fa111623c355691e2755cae3b9e"}, - {file = "coverage-7.10.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b1c81d0e5e160651879755c9c675b974276f135558cf4ba79fee7b8413a515df"}, - {file = "coverage-7.10.7-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:606cc265adc9aaedcc84f1f064f0e8736bc45814f15a357e30fca7ecc01504e0"}, - {file = "coverage-7.10.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:10b24412692df990dbc34f8fb1b6b13d236ace9dfdd68df5b28c2e39cafbba13"}, - {file = "coverage-7.10.7-cp310-cp310-win32.whl", hash = "sha256:b51dcd060f18c19290d9b8a9dd1e0181538df2ce0717f562fff6cf74d9fc0b5b"}, - {file = "coverage-7.10.7-cp310-cp310-win_amd64.whl", hash = "sha256:3a622ac801b17198020f09af3eaf45666b344a0d69fc2a6ffe2ea83aeef1d807"}, - {file = "coverage-7.10.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a609f9c93113be646f44c2a0256d6ea375ad047005d7f57a5c15f614dc1b2f59"}, - {file = "coverage-7.10.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:65646bb0359386e07639c367a22cf9b5bf6304e8630b565d0626e2bdf329227a"}, - {file = "coverage-7.10.7-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5f33166f0dfcce728191f520bd2692914ec70fac2713f6bf3ce59c3deacb4699"}, - {file = "coverage-7.10.7-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:35f5e3f9e455bb17831876048355dca0f758b6df22f49258cb5a91da23ef437d"}, - {file = "coverage-7.10.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4da86b6d62a496e908ac2898243920c7992499c1712ff7c2b6d837cc69d9467e"}, - {file = "coverage-7.10.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6b8b09c1fad947c84bbbc95eca841350fad9cbfa5a2d7ca88ac9f8d836c92e23"}, - {file = "coverage-7.10.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4376538f36b533b46f8971d3a3e63464f2c7905c9800db97361c43a2b14792ab"}, - {file = "coverage-7.10.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:121da30abb574f6ce6ae09840dae322bef734480ceafe410117627aa54f76d82"}, - {file = "coverage-7.10.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:88127d40df529336a9836870436fc2751c339fbaed3a836d42c93f3e4bd1d0a2"}, - {file = "coverage-7.10.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ba58bbcd1b72f136080c0bccc2400d66cc6115f3f906c499013d065ac33a4b61"}, - {file = "coverage-7.10.7-cp311-cp311-win32.whl", hash = "sha256:972b9e3a4094b053a4e46832b4bc829fc8a8d347160eb39d03f1690316a99c14"}, - {file = "coverage-7.10.7-cp311-cp311-win_amd64.whl", hash = "sha256:a7b55a944a7f43892e28ad4bc0561dfd5f0d73e605d1aa5c3c976b52aea121d2"}, - {file = "coverage-7.10.7-cp311-cp311-win_arm64.whl", hash = "sha256:736f227fb490f03c6488f9b6d45855f8e0fd749c007f9303ad30efab0e73c05a"}, - {file = "coverage-7.10.7-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7bb3b9ddb87ef7725056572368040c32775036472d5a033679d1fa6c8dc08417"}, - {file = "coverage-7.10.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:18afb24843cbc175687225cab1138c95d262337f5473512010e46831aa0c2973"}, - {file = "coverage-7.10.7-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:399a0b6347bcd3822be369392932884b8216d0944049ae22925631a9b3d4ba4c"}, - {file = "coverage-7.10.7-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:314f2c326ded3f4b09be11bc282eb2fc861184bc95748ae67b360ac962770be7"}, - {file = "coverage-7.10.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c41e71c9cfb854789dee6fc51e46743a6d138b1803fab6cb860af43265b42ea6"}, - {file = "coverage-7.10.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc01f57ca26269c2c706e838f6422e2a8788e41b3e3c65e2f41148212e57cd59"}, - {file = "coverage-7.10.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a6442c59a8ac8b85812ce33bc4d05bde3fb22321fa8294e2a5b487c3505f611b"}, - {file = "coverage-7.10.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:78a384e49f46b80fb4c901d52d92abe098e78768ed829c673fbb53c498bef73a"}, - {file = "coverage-7.10.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:5e1e9802121405ede4b0133aa4340ad8186a1d2526de5b7c3eca519db7bb89fb"}, - {file = "coverage-7.10.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d41213ea25a86f69efd1575073d34ea11aabe075604ddf3d148ecfec9e1e96a1"}, - {file = "coverage-7.10.7-cp312-cp312-win32.whl", hash = "sha256:77eb4c747061a6af8d0f7bdb31f1e108d172762ef579166ec84542f711d90256"}, - {file = "coverage-7.10.7-cp312-cp312-win_amd64.whl", hash = "sha256:f51328ffe987aecf6d09f3cd9d979face89a617eacdaea43e7b3080777f647ba"}, - {file = "coverage-7.10.7-cp312-cp312-win_arm64.whl", hash = "sha256:bda5e34f8a75721c96085903c6f2197dc398c20ffd98df33f866a9c8fd95f4bf"}, - {file = "coverage-7.10.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:981a651f543f2854abd3b5fcb3263aac581b18209be49863ba575de6edf4c14d"}, - {file = "coverage-7.10.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:73ab1601f84dc804f7812dc297e93cd99381162da39c47040a827d4e8dafe63b"}, - {file = "coverage-7.10.7-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a8b6f03672aa6734e700bbcd65ff050fd19cddfec4b031cc8cf1c6967de5a68e"}, - {file = "coverage-7.10.7-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10b6ba00ab1132a0ce4428ff68cf50a25efd6840a42cdf4239c9b99aad83be8b"}, - {file = "coverage-7.10.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c79124f70465a150e89340de5963f936ee97097d2ef76c869708c4248c63ca49"}, - {file = "coverage-7.10.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:69212fbccdbd5b0e39eac4067e20a4a5256609e209547d86f740d68ad4f04911"}, - {file = "coverage-7.10.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7ea7c6c9d0d286d04ed3541747e6597cbe4971f22648b68248f7ddcd329207f0"}, - {file = "coverage-7.10.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b9be91986841a75042b3e3243d0b3cb0b2434252b977baaf0cd56e960fe1e46f"}, - {file = "coverage-7.10.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:b281d5eca50189325cfe1f365fafade89b14b4a78d9b40b05ddd1fc7d2a10a9c"}, - {file = "coverage-7.10.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:99e4aa63097ab1118e75a848a28e40d68b08a5e19ce587891ab7fd04475e780f"}, - {file = "coverage-7.10.7-cp313-cp313-win32.whl", hash = "sha256:dc7c389dce432500273eaf48f410b37886be9208b2dd5710aaf7c57fd442c698"}, - {file = "coverage-7.10.7-cp313-cp313-win_amd64.whl", hash = "sha256:cac0fdca17b036af3881a9d2729a850b76553f3f716ccb0360ad4dbc06b3b843"}, - {file = "coverage-7.10.7-cp313-cp313-win_arm64.whl", hash = "sha256:4b6f236edf6e2f9ae8fcd1332da4e791c1b6ba0dc16a2dc94590ceccb482e546"}, - {file = "coverage-7.10.7-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a0ec07fd264d0745ee396b666d47cef20875f4ff2375d7c4f58235886cc1ef0c"}, - {file = "coverage-7.10.7-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd5e856ebb7bfb7672b0086846db5afb4567a7b9714b8a0ebafd211ec7ce6a15"}, - {file = "coverage-7.10.7-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f57b2a3c8353d3e04acf75b3fed57ba41f5c0646bbf1d10c7c282291c97936b4"}, - {file = "coverage-7.10.7-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1ef2319dd15a0b009667301a3f84452a4dc6fddfd06b0c5c53ea472d3989fbf0"}, - {file = "coverage-7.10.7-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:83082a57783239717ceb0ad584de3c69cf581b2a95ed6bf81ea66034f00401c0"}, - {file = "coverage-7.10.7-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:50aa94fb1fb9a397eaa19c0d5ec15a5edd03a47bf1a3a6111a16b36e190cff65"}, - {file = "coverage-7.10.7-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2120043f147bebb41c85b97ac45dd173595ff14f2a584f2963891cbcc3091541"}, - {file = "coverage-7.10.7-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2fafd773231dd0378fdba66d339f84904a8e57a262f583530f4f156ab83863e6"}, - {file = "coverage-7.10.7-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:0b944ee8459f515f28b851728ad224fa2d068f1513ef6b7ff1efafeb2185f999"}, - {file = "coverage-7.10.7-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4b583b97ab2e3efe1b3e75248a9b333bd3f8b0b1b8e5b45578e05e5850dfb2c2"}, - {file = "coverage-7.10.7-cp313-cp313t-win32.whl", hash = "sha256:2a78cd46550081a7909b3329e2266204d584866e8d97b898cd7fb5ac8d888b1a"}, - {file = "coverage-7.10.7-cp313-cp313t-win_amd64.whl", hash = "sha256:33a5e6396ab684cb43dc7befa386258acb2d7fae7f67330ebb85ba4ea27938eb"}, - {file = "coverage-7.10.7-cp313-cp313t-win_arm64.whl", hash = "sha256:86b0e7308289ddde73d863b7683f596d8d21c7d8664ce1dee061d0bcf3fbb4bb"}, - {file = "coverage-7.10.7-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b06f260b16ead11643a5a9f955bd4b5fd76c1a4c6796aeade8520095b75de520"}, - {file = "coverage-7.10.7-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:212f8f2e0612778f09c55dd4872cb1f64a1f2b074393d139278ce902064d5b32"}, - {file = "coverage-7.10.7-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3445258bcded7d4aa630ab8296dea4d3f15a255588dd535f980c193ab6b95f3f"}, - {file = "coverage-7.10.7-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bb45474711ba385c46a0bfe696c695a929ae69ac636cda8f532be9e8c93d720a"}, - {file = "coverage-7.10.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:813922f35bd800dca9994c5971883cbc0d291128a5de6b167c7aa697fcf59360"}, - {file = "coverage-7.10.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:93c1b03552081b2a4423091d6fb3787265b8f86af404cff98d1b5342713bdd69"}, - {file = "coverage-7.10.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:cc87dd1b6eaf0b848eebb1c86469b9f72a1891cb42ac7adcfbce75eadb13dd14"}, - {file = "coverage-7.10.7-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:39508ffda4f343c35f3236fe8d1a6634a51f4581226a1262769d7f970e73bffe"}, - {file = "coverage-7.10.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:925a1edf3d810537c5a3abe78ec5530160c5f9a26b1f4270b40e62cc79304a1e"}, - {file = "coverage-7.10.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2c8b9a0636f94c43cd3576811e05b89aa9bc2d0a85137affc544ae5cb0e4bfbd"}, - {file = "coverage-7.10.7-cp314-cp314-win32.whl", hash = "sha256:b7b8288eb7cdd268b0304632da8cb0bb93fadcfec2fe5712f7b9cc8f4d487be2"}, - {file = "coverage-7.10.7-cp314-cp314-win_amd64.whl", hash = "sha256:1ca6db7c8807fb9e755d0379ccc39017ce0a84dcd26d14b5a03b78563776f681"}, - {file = "coverage-7.10.7-cp314-cp314-win_arm64.whl", hash = "sha256:097c1591f5af4496226d5783d036bf6fd6cd0cbc132e071b33861de756efb880"}, - {file = "coverage-7.10.7-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:a62c6ef0d50e6de320c270ff91d9dd0a05e7250cac2a800b7784bae474506e63"}, - {file = "coverage-7.10.7-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9fa6e4dd51fe15d8738708a973470f67a855ca50002294852e9571cdbd9433f2"}, - {file = "coverage-7.10.7-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8fb190658865565c549b6b4706856d6a7b09302c797eb2cf8e7fe9dabb043f0d"}, - {file = "coverage-7.10.7-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:affef7c76a9ef259187ef31599a9260330e0335a3011732c4b9effa01e1cd6e0"}, - {file = "coverage-7.10.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e16e07d85ca0cf8bafe5f5d23a0b850064e8e945d5677492b06bbe6f09cc699"}, - {file = "coverage-7.10.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:03ffc58aacdf65d2a82bbeb1ffe4d01ead4017a21bfd0454983b88ca73af94b9"}, - {file = "coverage-7.10.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1b4fd784344d4e52647fd7857b2af5b3fbe6c239b0b5fa63e94eb67320770e0f"}, - {file = "coverage-7.10.7-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:0ebbaddb2c19b71912c6f2518e791aa8b9f054985a0769bdb3a53ebbc765c6a1"}, - {file = "coverage-7.10.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:a2d9a3b260cc1d1dbdb1c582e63ddcf5363426a1a68faa0f5da28d8ee3c722a0"}, - {file = "coverage-7.10.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a3cc8638b2480865eaa3926d192e64ce6c51e3d29c849e09d5b4ad95efae5399"}, - {file = "coverage-7.10.7-cp314-cp314t-win32.whl", hash = "sha256:67f8c5cbcd3deb7a60b3345dffc89a961a484ed0af1f6f73de91705cc6e31235"}, - {file = "coverage-7.10.7-cp314-cp314t-win_amd64.whl", hash = "sha256:e1ed71194ef6dea7ed2d5cb5f7243d4bcd334bfb63e59878519be558078f848d"}, - {file = "coverage-7.10.7-cp314-cp314t-win_arm64.whl", hash = "sha256:7fe650342addd8524ca63d77b2362b02345e5f1a093266787d210c70a50b471a"}, - {file = "coverage-7.10.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fff7b9c3f19957020cac546c70025331113d2e61537f6e2441bc7657913de7d3"}, - {file = "coverage-7.10.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bc91b314cef27742da486d6839b677b3f2793dfe52b51bbbb7cf736d5c29281c"}, - {file = "coverage-7.10.7-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:567f5c155eda8df1d3d439d40a45a6a5f029b429b06648235f1e7e51b522b396"}, - {file = "coverage-7.10.7-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2af88deffcc8a4d5974cf2d502251bc3b2db8461f0b66d80a449c33757aa9f40"}, - {file = "coverage-7.10.7-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7315339eae3b24c2d2fa1ed7d7a38654cba34a13ef19fbcb9425da46d3dc594"}, - {file = "coverage-7.10.7-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:912e6ebc7a6e4adfdbb1aec371ad04c68854cd3bf3608b3514e7ff9062931d8a"}, - {file = "coverage-7.10.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f49a05acd3dfe1ce9715b657e28d138578bc40126760efb962322c56e9ca344b"}, - {file = "coverage-7.10.7-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:cce2109b6219f22ece99db7644b9622f54a4e915dad65660ec435e89a3ea7cc3"}, - {file = "coverage-7.10.7-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:f3c887f96407cea3916294046fc7dab611c2552beadbed4ea901cbc6a40cc7a0"}, - {file = "coverage-7.10.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:635adb9a4507c9fd2ed65f39693fa31c9a3ee3a8e6dc64df033e8fdf52a7003f"}, - {file = "coverage-7.10.7-cp39-cp39-win32.whl", hash = "sha256:5a02d5a850e2979b0a014c412573953995174743a3f7fa4ea5a6e9a3c5617431"}, - {file = "coverage-7.10.7-cp39-cp39-win_amd64.whl", hash = "sha256:c134869d5ffe34547d14e174c866fd8fe2254918cc0a95e99052903bc1543e07"}, - {file = "coverage-7.10.7-py3-none-any.whl", hash = "sha256:f7941f6f2fe6dd6807a1208737b8a0cbcf1cc6d7b07d24998ad2d63590868260"}, - {file = "coverage-7.10.7.tar.gz", hash = "sha256:f4ab143ab113be368a3e9b795f9cd7906c5ef407d6173fe9675a902e1fffc239"}, -] - -[package.extras] -toml = ["tomli ; python_full_version <= \"3.11.0a6\""] - -[[package]] -name = "debtcollector" -version = "3.0.0" -description = "A collection of Python deprecation patterns and strategies that help you collect your technical debt in a non-destructive manner." -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "debtcollector-3.0.0-py3-none-any.whl", hash = "sha256:46f9dacbe8ce49c47ebf2bf2ec878d50c9443dfae97cc7b8054be684e54c3e91"}, - {file = "debtcollector-3.0.0.tar.gz", hash = "sha256:2a8917d25b0e1f1d0d365d3c1c6ecfc7a522b1e9716e8a1a4a915126f7ccea6f"}, -] - -[package.dependencies] -wrapt = ">=1.7.0" - -[[package]] -name = "idna" -version = "3.10" -description = "Internationalized Domain Names in Applications (IDNA)" -optional = false -python-versions = ">=3.6" -groups = ["main"] -files = [ - {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, - {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, -] - -[package.extras] -all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] - -[[package]] -name = "iniconfig" -version = "2.0.0" -description = "brain-dead simple config-ini parsing" -optional = false -python-versions = ">=3.7" -groups = ["test"] -files = [ - {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, - {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, -] - -[[package]] -name = "iso8601" -version = "2.1.0" -description = "Simple module to parse ISO 8601 dates" -optional = false -python-versions = ">=3.7,<4.0" -groups = ["main"] -files = [ - {file = "iso8601-2.1.0-py3-none-any.whl", hash = "sha256:aac4145c4dcb66ad8b648a02830f5e2ff6c24af20f4f482689be402db2429242"}, - {file = "iso8601-2.1.0.tar.gz", hash = "sha256:6b1d3829ee8921c4301998c909f7829fa9ed3cbdac0d3b16af2d743aed1ba8df"}, -] - -[[package]] -name = "keystoneauth1" -version = "5.9.1" -description = "Authentication Library for OpenStack Identity" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "keystoneauth1-5.9.1-py3-none-any.whl", hash = "sha256:71b98835aec72a01f71c5b919c3193dac95342555e89aa35c86d3d86c4ff5f73"}, - {file = "keystoneauth1-5.9.1.tar.gz", hash = "sha256:fb0c66d842d5b964752264fff20b3b4ab73610d66d9b8d20d0dcf796ba09dc43"}, -] - -[package.dependencies] -iso8601 = ">=2.0.0" -os-service-types = ">=1.2.0" -pbr = ">=2.0.0" -requests = ">=2.14.2" -stevedore = ">=1.20.0" -typing-extensions = ">=4.12" - -[package.extras] -betamax = ["PyYAML (>=3.13)", "betamax (>=0.7.0)", "fixtures (>=3.0.0)"] -kerberos = ["requests-kerberos (>=0.8.0)"] -oauth1 = ["oauthlib (>=0.6.2)"] -saml2 = ["lxml (>=4.2.0)"] -test = ["PyYAML (>=3.12)", "bandit (>=1.7.6,<1.8.0)", "betamax (>=0.7.0)", "coverage (>=4.0)", "fixtures (>=3.0.0)", "flake8-docstrings (>=1.7.0,<1.8.0)", "flake8-import-order (>=0.18.2,<0.19.0)", "hacking (>=6.1.0,<6.2.0)", "lxml (>=4.2.0)", "oauthlib (>=0.6.2)", "oslo.config (>=5.2.0)", "oslo.utils (>=3.33.0)", "oslotest (>=3.2.0)", "requests-kerberos (>=0.8.0)", "requests-mock (>=1.2.0)", "stestr (>=1.0.0)", "testresources (>=2.0.0)", "testtools (>=2.2.0)"] - -[[package]] -name = "msgpack" -version = "1.1.0" -description = "MessagePack serializer" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "msgpack-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7ad442d527a7e358a469faf43fda45aaf4ac3249c8310a82f0ccff9164e5dccd"}, - {file = "msgpack-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:74bed8f63f8f14d75eec75cf3d04ad581da6b914001b474a5d3cd3372c8cc27d"}, - {file = "msgpack-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:914571a2a5b4e7606997e169f64ce53a8b1e06f2cf2c3a7273aa106236d43dd5"}, - {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c921af52214dcbb75e6bdf6a661b23c3e6417f00c603dd2070bccb5c3ef499f5"}, - {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8ce0b22b890be5d252de90d0e0d119f363012027cf256185fc3d474c44b1b9e"}, - {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:73322a6cc57fcee3c0c57c4463d828e9428275fb85a27aa2aa1a92fdc42afd7b"}, - {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e1f3c3d21f7cf67bcf2da8e494d30a75e4cf60041d98b3f79875afb5b96f3a3f"}, - {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:64fc9068d701233effd61b19efb1485587560b66fe57b3e50d29c5d78e7fef68"}, - {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:42f754515e0f683f9c79210a5d1cad631ec3d06cea5172214d2176a42e67e19b"}, - {file = "msgpack-1.1.0-cp310-cp310-win32.whl", hash = "sha256:3df7e6b05571b3814361e8464f9304c42d2196808e0119f55d0d3e62cd5ea044"}, - {file = "msgpack-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:685ec345eefc757a7c8af44a3032734a739f8c45d1b0ac45efc5d8977aa4720f"}, - {file = "msgpack-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3d364a55082fb2a7416f6c63ae383fbd903adb5a6cf78c5b96cc6316dc1cedc7"}, - {file = "msgpack-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:79ec007767b9b56860e0372085f8504db5d06bd6a327a335449508bbee9648fa"}, - {file = "msgpack-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6ad622bf7756d5a497d5b6836e7fc3752e2dd6f4c648e24b1803f6048596f701"}, - {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e59bca908d9ca0de3dc8684f21ebf9a690fe47b6be93236eb40b99af28b6ea6"}, - {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e1da8f11a3dd397f0a32c76165cf0c4eb95b31013a94f6ecc0b280c05c91b59"}, - {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:452aff037287acb1d70a804ffd022b21fa2bb7c46bee884dbc864cc9024128a0"}, - {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8da4bf6d54ceed70e8861f833f83ce0814a2b72102e890cbdfe4b34764cdd66e"}, - {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:41c991beebf175faf352fb940bf2af9ad1fb77fd25f38d9142053914947cdbf6"}, - {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a52a1f3a5af7ba1c9ace055b659189f6c669cf3657095b50f9602af3a3ba0fe5"}, - {file = "msgpack-1.1.0-cp311-cp311-win32.whl", hash = "sha256:58638690ebd0a06427c5fe1a227bb6b8b9fdc2bd07701bec13c2335c82131a88"}, - {file = "msgpack-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fd2906780f25c8ed5d7b323379f6138524ba793428db5d0e9d226d3fa6aa1788"}, - {file = "msgpack-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d46cf9e3705ea9485687aa4001a76e44748b609d260af21c4ceea7f2212a501d"}, - {file = "msgpack-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5dbad74103df937e1325cc4bfeaf57713be0b4f15e1c2da43ccdd836393e2ea2"}, - {file = "msgpack-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58dfc47f8b102da61e8949708b3eafc3504509a5728f8b4ddef84bd9e16ad420"}, - {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676e5be1b472909b2ee6356ff425ebedf5142427842aa06b4dfd5117d1ca8a2"}, - {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17fb65dd0bec285907f68b15734a993ad3fc94332b5bb21b0435846228de1f39"}, - {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a51abd48c6d8ac89e0cfd4fe177c61481aca2d5e7ba42044fd218cfd8ea9899f"}, - {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2137773500afa5494a61b1208619e3871f75f27b03bcfca7b3a7023284140247"}, - {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:398b713459fea610861c8a7b62a6fec1882759f308ae0795b5413ff6a160cf3c"}, - {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:06f5fd2f6bb2a7914922d935d3b8bb4a7fff3a9a91cfce6d06c13bc42bec975b"}, - {file = "msgpack-1.1.0-cp312-cp312-win32.whl", hash = "sha256:ad33e8400e4ec17ba782f7b9cf868977d867ed784a1f5f2ab46e7ba53b6e1e1b"}, - {file = "msgpack-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:115a7af8ee9e8cddc10f87636767857e7e3717b7a2e97379dc2054712693e90f"}, - {file = "msgpack-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:071603e2f0771c45ad9bc65719291c568d4edf120b44eb36324dcb02a13bfddf"}, - {file = "msgpack-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0f92a83b84e7c0749e3f12821949d79485971f087604178026085f60ce109330"}, - {file = "msgpack-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1964df7b81285d00a84da4e70cb1383f2e665e0f1f2a7027e683956d04b734"}, - {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59caf6a4ed0d164055ccff8fe31eddc0ebc07cf7326a2aaa0dbf7a4001cd823e"}, - {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0907e1a7119b337971a689153665764adc34e89175f9a34793307d9def08e6ca"}, - {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65553c9b6da8166e819a6aa90ad15288599b340f91d18f60b2061f402b9a4915"}, - {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7a946a8992941fea80ed4beae6bff74ffd7ee129a90b4dd5cf9c476a30e9708d"}, - {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4b51405e36e075193bc051315dbf29168d6141ae2500ba8cd80a522964e31434"}, - {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4c01941fd2ff87c2a934ee6055bda4ed353a7846b8d4f341c428109e9fcde8c"}, - {file = "msgpack-1.1.0-cp313-cp313-win32.whl", hash = "sha256:7c9a35ce2c2573bada929e0b7b3576de647b0defbd25f5139dcdaba0ae35a4cc"}, - {file = "msgpack-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:bce7d9e614a04d0883af0b3d4d501171fbfca038f12c77fa838d9f198147a23f"}, - {file = "msgpack-1.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c40ffa9a15d74e05ba1fe2681ea33b9caffd886675412612d93ab17b58ea2fec"}, - {file = "msgpack-1.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1ba6136e650898082d9d5a5217d5906d1e138024f836ff48691784bbe1adf96"}, - {file = "msgpack-1.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e0856a2b7e8dcb874be44fea031d22e5b3a19121be92a1e098f46068a11b0870"}, - {file = "msgpack-1.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:471e27a5787a2e3f974ba023f9e265a8c7cfd373632247deb225617e3100a3c7"}, - {file = "msgpack-1.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:646afc8102935a388ffc3914b336d22d1c2d6209c773f3eb5dd4d6d3b6f8c1cb"}, - {file = "msgpack-1.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:13599f8829cfbe0158f6456374e9eea9f44eee08076291771d8ae93eda56607f"}, - {file = "msgpack-1.1.0-cp38-cp38-win32.whl", hash = "sha256:8a84efb768fb968381e525eeeb3d92857e4985aacc39f3c47ffd00eb4509315b"}, - {file = "msgpack-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:879a7b7b0ad82481c52d3c7eb99bf6f0645dbdec5134a4bddbd16f3506947feb"}, - {file = "msgpack-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:53258eeb7a80fc46f62fd59c876957a2d0e15e6449a9e71842b6d24419d88ca1"}, - {file = "msgpack-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7e7b853bbc44fb03fbdba34feb4bd414322180135e2cb5164f20ce1c9795ee48"}, - {file = "msgpack-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3e9b4936df53b970513eac1758f3882c88658a220b58dcc1e39606dccaaf01c"}, - {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46c34e99110762a76e3911fc923222472c9d681f1094096ac4102c18319e6468"}, - {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a706d1e74dd3dea05cb54580d9bd8b2880e9264856ce5068027eed09680aa74"}, - {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:534480ee5690ab3cbed89d4c8971a5c631b69a8c0883ecfea96c19118510c846"}, - {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8cf9e8c3a2153934a23ac160cc4cba0ec035f6867c8013cc6077a79823370346"}, - {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3180065ec2abbe13a4ad37688b61b99d7f9e012a535b930e0e683ad6bc30155b"}, - {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c5a91481a3cc573ac8c0d9aace09345d989dc4a0202b7fcb312c88c26d4e71a8"}, - {file = "msgpack-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f80bc7d47f76089633763f952e67f8214cb7b3ee6bfa489b3cb6a84cfac114cd"}, - {file = "msgpack-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:4d1b7ff2d6146e16e8bd665ac726a89c74163ef8cd39fa8c1087d4e52d3a2325"}, - {file = "msgpack-1.1.0.tar.gz", hash = "sha256:dd432ccc2c72b914e4cb77afce64aab761c1137cc698be3984eee260bcb2896e"}, -] - -[[package]] -name = "netaddr" -version = "1.3.0" -description = "A network address manipulation library for Python" -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "netaddr-1.3.0-py3-none-any.whl", hash = "sha256:c2c6a8ebe5554ce33b7d5b3a306b71bbb373e000bbbf2350dd5213cc56e3dbbe"}, - {file = "netaddr-1.3.0.tar.gz", hash = "sha256:5c3c3d9895b551b763779ba7db7a03487dc1f8e3b385af819af341ae9ef6e48a"}, -] - -[package.extras] -nicer-shell = ["ipython"] - -[[package]] -name = "os-service-types" -version = "1.7.0" -description = "Python library for consuming OpenStack sevice-types-authority data" -optional = false -python-versions = "*" -groups = ["main"] -files = [ - {file = "os-service-types-1.7.0.tar.gz", hash = "sha256:31800299a82239363995b91f1ebf9106ac7758542a1e4ef6dc737a5932878c6c"}, - {file = "os_service_types-1.7.0-py2.py3-none-any.whl", hash = "sha256:0505c72205690910077fb72b88f2a1f07533c8d39f2fe75b29583481764965d6"}, -] - -[package.dependencies] -pbr = ">=2.0.0,<2.1.0 || >2.1.0" - -[[package]] -name = "oslo-i18n" -version = "6.5.0" -description = "Oslo i18n library" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "oslo.i18n-6.5.0-py3-none-any.whl", hash = "sha256:41e54addeb215bd70c5c76bcdd93f93dc563d11c51117531025f2d02a9d8f80d"}, - {file = "oslo.i18n-6.5.0.tar.gz", hash = "sha256:9393bcae92eadc5f771132d1c6ab239b19896ff6d885e3afc21a9faa4de924d3"}, -] - -[package.dependencies] -pbr = ">=2.0.0" - -[[package]] -name = "oslo-serialization" -version = "5.6.0" -description = "Oslo Serialization library" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "oslo.serialization-5.6.0-py3-none-any.whl", hash = "sha256:a30c30009db5ea8da5da31a4ac6d75256dcb719f48abc3132c36196eb5bb3821"}, - {file = "oslo.serialization-5.6.0.tar.gz", hash = "sha256:4c7d4e12da853cc4f04b9123041134e886e8c9ff57ab57c1962d3ad4a87b7f7c"}, -] - -[package.dependencies] -msgpack = ">=0.5.2" -"oslo.utils" = ">=3.33.0" -pbr = ">=2.0.0" -tzdata = ">=2022.4" - -[[package]] -name = "oslo-utils" -version = "8.0.0" -description = "Oslo Utility library" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "oslo.utils-8.0.0-py3-none-any.whl", hash = "sha256:a1a124aa6718e3c2d833c0b53543c90d41df72550eb5ac80f8175d78a9556cb3"}, - {file = "oslo.utils-8.0.0.tar.gz", hash = "sha256:906fcf1c86f671f224c1925b2a8d375a0539143fb6158b13e202a79dd8e6c694"}, -] - -[package.dependencies] -debtcollector = ">=1.2.0" -iso8601 = ">=0.1.11" -netaddr = ">=0.10.0" -"oslo.i18n" = ">=3.15.3" -packaging = ">=20.4" -psutil = ">=3.2.2" -pyparsing = ">=2.1.0" -PyYAML = ">=3.13" -tzdata = ">=2022.4" - -[[package]] -name = "packaging" -version = "24.2" -description = "Core utilities for Python packages" -optional = false -python-versions = ">=3.8" -groups = ["main", "test"] -files = [ - {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, - {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, -] - -[[package]] -name = "pbr" -version = "6.1.0" -description = "Python Build Reasonableness" -optional = false -python-versions = ">=2.6" -groups = ["main"] -files = [ - {file = "pbr-6.1.0-py2.py3-none-any.whl", hash = "sha256:a776ae228892d8013649c0aeccbb3d5f99ee15e005a4cbb7e61d55a067b28a2a"}, - {file = "pbr-6.1.0.tar.gz", hash = "sha256:788183e382e3d1d7707db08978239965e8b9e4e5ed42669bf4758186734d5f24"}, -] - -[[package]] -name = "pluggy" -version = "1.5.0" -description = "plugin and hook calling mechanisms for python" -optional = false -python-versions = ">=3.8" -groups = ["test"] -files = [ - {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, - {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, -] - -[package.extras] -dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] - -[[package]] -name = "prettytable" -version = "3.12.0" -description = "A simple Python library for easily displaying tabular data in a visually appealing ASCII table format" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "prettytable-3.12.0-py3-none-any.whl", hash = "sha256:77ca0ad1c435b6e363d7e8623d7cc4fcf2cf15513bf77a1c1b2e814930ac57cc"}, - {file = "prettytable-3.12.0.tar.gz", hash = "sha256:f04b3e1ba35747ac86e96ec33e3bb9748ce08e254dc2a1c6253945901beec804"}, -] - -[package.dependencies] -wcwidth = "*" - -[package.extras] -tests = ["pytest", "pytest-cov", "pytest-lazy-fixtures"] - -[[package]] -name = "psutil" -version = "6.1.1" -description = "Cross-platform lib for process and system monitoring in Python." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" -groups = ["main"] -files = [ - {file = "psutil-6.1.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:9ccc4316f24409159897799b83004cb1e24f9819b0dcf9c0b68bdcb6cefee6a8"}, - {file = "psutil-6.1.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ca9609c77ea3b8481ab005da74ed894035936223422dc591d6772b147421f777"}, - {file = "psutil-6.1.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:8df0178ba8a9e5bc84fed9cfa61d54601b371fbec5c8eebad27575f1e105c0d4"}, - {file = "psutil-6.1.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:1924e659d6c19c647e763e78670a05dbb7feaf44a0e9c94bf9e14dfc6ba50468"}, - {file = "psutil-6.1.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:018aeae2af92d943fdf1da6b58665124897cfc94faa2ca92098838f83e1b1bca"}, - {file = "psutil-6.1.1-cp27-none-win32.whl", hash = "sha256:6d4281f5bbca041e2292be3380ec56a9413b790579b8e593b1784499d0005dac"}, - {file = "psutil-6.1.1-cp27-none-win_amd64.whl", hash = "sha256:c777eb75bb33c47377c9af68f30e9f11bc78e0f07fbf907be4a5d70b2fe5f030"}, - {file = "psutil-6.1.1-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:fc0ed7fe2231a444fc219b9c42d0376e0a9a1a72f16c5cfa0f68d19f1a0663e8"}, - {file = "psutil-6.1.1-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:0bdd4eab935276290ad3cb718e9809412895ca6b5b334f5a9111ee6d9aff9377"}, - {file = "psutil-6.1.1-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6e06c20c05fe95a3d7302d74e7097756d4ba1247975ad6905441ae1b5b66003"}, - {file = "psutil-6.1.1-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97f7cb9921fbec4904f522d972f0c0e1f4fabbdd4e0287813b21215074a0f160"}, - {file = "psutil-6.1.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33431e84fee02bc84ea36d9e2c4a6d395d479c9dd9bba2376c1f6ee8f3a4e0b3"}, - {file = "psutil-6.1.1-cp36-cp36m-win32.whl", hash = "sha256:384636b1a64b47814437d1173be1427a7c83681b17a450bfc309a1953e329603"}, - {file = "psutil-6.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:8be07491f6ebe1a693f17d4f11e69d0dc1811fa082736500f649f79df7735303"}, - {file = "psutil-6.1.1-cp37-abi3-win32.whl", hash = "sha256:eaa912e0b11848c4d9279a93d7e2783df352b082f40111e078388701fd479e53"}, - {file = "psutil-6.1.1-cp37-abi3-win_amd64.whl", hash = "sha256:f35cfccb065fff93529d2afb4a2e89e363fe63ca1e4a5da22b603a85833c2649"}, - {file = "psutil-6.1.1.tar.gz", hash = "sha256:cf8496728c18f2d0b45198f06895be52f36611711746b7f30c464b422b50e2f5"}, -] - -[package.extras] -dev = ["abi3audit", "black", "check-manifest", "coverage", "packaging", "pylint", "pyperf", "pypinfo", "pytest-cov", "requests", "rstcheck", "ruff", "sphinx", "sphinx_rtd_theme", "toml-sort", "twine", "virtualenv", "vulture", "wheel"] -test = ["pytest", "pytest-xdist", "setuptools"] - -[[package]] -name = "pygments" -version = "2.19.1" -description = "Pygments is a syntax highlighting package written in Python." -optional = false -python-versions = ">=3.8" -groups = ["test"] -files = [ - {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, - {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, -] - -[package.extras] -windows-terminal = ["colorama (>=0.4.6)"] - -[[package]] -name = "pyparsing" -version = "3.2.1" -description = "pyparsing module - Classes and methods to define and execute parsing grammars" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "pyparsing-3.2.1-py3-none-any.whl", hash = "sha256:506ff4f4386c4cec0590ec19e6302d3aedb992fdc02c761e90416f158dacf8e1"}, - {file = "pyparsing-3.2.1.tar.gz", hash = "sha256:61980854fd66de3a90028d679a954d5f2623e83144b5afe5ee86f43d762e5f0a"}, -] - -[package.extras] -diagrams = ["jinja2", "railroad-diagrams"] - -[[package]] -name = "pytest" -version = "8.4.2" -description = "pytest: simple powerful testing with Python" -optional = false -python-versions = ">=3.9" -groups = ["test"] -files = [ - {file = "pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79"}, - {file = "pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01"}, -] - -[package.dependencies] -colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} -iniconfig = ">=1" -packaging = ">=20" -pluggy = ">=1.5,<2" -pygments = ">=2.7.2" - -[package.extras] -dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] - -[[package]] -name = "pytest-cov" -version = "7.0.0" -description = "Pytest plugin for measuring coverage." -optional = false -python-versions = ">=3.9" -groups = ["test"] -files = [ - {file = "pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861"}, - {file = "pytest_cov-7.0.0.tar.gz", hash = "sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1"}, -] - -[package.dependencies] -coverage = {version = ">=7.10.6", extras = ["toml"]} -pluggy = ">=1.2" -pytest = ">=7" - -[package.extras] -testing = ["process-tests", "pytest-xdist", "virtualenv"] - -[[package]] -name = "pytest-github-actions-annotate-failures" -version = "0.3.0" -description = "pytest plugin to annotate failed tests with a workflow command for GitHub Actions" -optional = false -python-versions = ">=3.8" -groups = ["test"] -files = [ - {file = "pytest_github_actions_annotate_failures-0.3.0-py3-none-any.whl", hash = "sha256:41ea558ba10c332c0bfc053daeee0c85187507b2034e990f21e4f7e5fef044cf"}, - {file = "pytest_github_actions_annotate_failures-0.3.0.tar.gz", hash = "sha256:d4c3177c98046c3900a7f8ddebb22ea54b9f6822201b5d3ab8fcdea51e010db7"}, -] - -[package.dependencies] -pytest = ">=6.0.0" - -[[package]] -name = "pytest-mock" -version = "3.15.1" -description = "Thin-wrapper around the mock package for easier use with pytest" -optional = false -python-versions = ">=3.9" -groups = ["test"] -files = [ - {file = "pytest_mock-3.15.1-py3-none-any.whl", hash = "sha256:0a25e2eb88fe5168d535041d09a4529a188176ae608a6d249ee65abc0949630d"}, - {file = "pytest_mock-3.15.1.tar.gz", hash = "sha256:1849a238f6f396da19762269de72cb1814ab44416fa73a8686deac10b0d87a0f"}, -] - -[package.dependencies] -pytest = ">=6.2.5" - -[package.extras] -dev = ["pre-commit", "pytest-asyncio", "tox"] - -[[package]] -name = "python-novaclient" -version = "18.11.0" -description = "Client library for OpenStack Compute API" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "python_novaclient-18.11.0-py3-none-any.whl", hash = "sha256:fc4ce032a6ce0fb911f4c640d260318d770ee171eedce7c12f887307fc24dc0c"}, - {file = "python_novaclient-18.11.0.tar.gz", hash = "sha256:0a31ae20779d4cd171bb29c1fe4e6b22b9c7d97c79670db5149bbdfac03f57dc"}, -] - -[package.dependencies] -iso8601 = ">=0.1.11" -keystoneauth1 = ">=3.5.0" -"oslo.i18n" = ">=3.15.3" -"oslo.serialization" = ">=2.20.0" -"oslo.utils" = ">=3.33.0" -pbr = ">=3.0.0" -PrettyTable = ">=0.7.2" -stevedore = ">=2.0.1" - -[[package]] -name = "pyyaml" -version = "6.0.2" -description = "YAML parser and emitter for Python" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, - {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, - {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, - {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, - {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, - {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, - {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, - {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, - {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, - {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, - {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, - {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, - {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, - {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, - {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, - {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, - {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, - {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, - {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, - {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, - {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, - {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, - {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, - {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, - {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, - {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, - {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, - {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, - {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, - {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, - {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, - {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, - {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, - {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, - {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, -] - -[[package]] -name = "requests" -version = "2.32.3" -description = "Python HTTP for Humans." -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, - {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, -] - -[package.dependencies] -certifi = ">=2017.4.17" -charset-normalizer = ">=2,<4" -idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<3" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] - -[[package]] -name = "stevedore" -version = "5.4.0" -description = "Manage dynamic plugins for Python applications" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "stevedore-5.4.0-py3-none-any.whl", hash = "sha256:b0be3c4748b3ea7b854b265dcb4caa891015e442416422be16f8b31756107857"}, - {file = "stevedore-5.4.0.tar.gz", hash = "sha256:79e92235ecb828fe952b6b8b0c6c87863248631922c8e8e0fa5b17b232c4514d"}, -] - -[package.dependencies] -pbr = ">=2.0.0" - -[[package]] -name = "typing-extensions" -version = "4.12.2" -description = "Backported and Experimental Type Hints for Python 3.8+" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, - {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, -] - -[[package]] -name = "tzdata" -version = "2024.2" -description = "Provider of IANA time zone data" -optional = false -python-versions = ">=2" -groups = ["main"] -files = [ - {file = "tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd"}, - {file = "tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc"}, -] - -[[package]] -name = "understack-flavor-matcher" -version = "0.0.0" -description = "Baremetal node flavor classifier" -optional = false -python-versions = "^3.10" -groups = ["main"] -files = [] -develop = false - -[package.dependencies] -pyyaml = "^6.0" - -[package.source] -type = "directory" -url = "../../python/understack-flavor-matcher" - -[[package]] -name = "urllib3" -version = "2.3.0" -description = "HTTP library with thread-safe connection pooling, file post, and more." -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, - {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, -] - -[package.extras] -brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] -h2 = ["h2 (>=4,<5)"] -socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] -zstd = ["zstandard (>=0.18.0)"] - -[[package]] -name = "watchdog" -version = "6.0.0" -description = "Filesystem events monitoring" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "watchdog-6.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d1cdb490583ebd691c012b3d6dae011000fe42edb7a82ece80965b42abd61f26"}, - {file = "watchdog-6.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc64ab3bdb6a04d69d4023b29422170b74681784ffb9463ed4870cf2f3e66112"}, - {file = "watchdog-6.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c897ac1b55c5a1461e16dae288d22bb2e412ba9807df8397a635d88f671d36c3"}, - {file = "watchdog-6.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6eb11feb5a0d452ee41f824e271ca311a09e250441c262ca2fd7ebcf2461a06c"}, - {file = "watchdog-6.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ef810fbf7b781a5a593894e4f439773830bdecb885e6880d957d5b9382a960d2"}, - {file = "watchdog-6.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:afd0fe1b2270917c5e23c2a65ce50c2a4abb63daafb0d419fde368e272a76b7c"}, - {file = "watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948"}, - {file = "watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860"}, - {file = "watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0"}, - {file = "watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c"}, - {file = "watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134"}, - {file = "watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b"}, - {file = "watchdog-6.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e6f0e77c9417e7cd62af82529b10563db3423625c5fce018430b249bf977f9e8"}, - {file = "watchdog-6.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:90c8e78f3b94014f7aaae121e6b909674df5b46ec24d6bebc45c44c56729af2a"}, - {file = "watchdog-6.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e7631a77ffb1f7d2eefa4445ebbee491c720a5661ddf6df3498ebecae5ed375c"}, - {file = "watchdog-6.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c7ac31a19f4545dd92fc25d200694098f42c9a8e391bc00bdd362c5736dbf881"}, - {file = "watchdog-6.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9513f27a1a582d9808cf21a07dae516f0fab1cf2d7683a742c498b93eedabb11"}, - {file = "watchdog-6.0.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7a0e56874cfbc4b9b05c60c8a1926fedf56324bb08cfbc188969777940aef3aa"}, - {file = "watchdog-6.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:e6439e374fc012255b4ec786ae3c4bc838cd7309a540e5fe0952d03687d8804e"}, - {file = "watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13"}, - {file = "watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379"}, - {file = "watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e"}, - {file = "watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f"}, - {file = "watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26"}, - {file = "watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c"}, - {file = "watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2"}, - {file = "watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a"}, - {file = "watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680"}, - {file = "watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f"}, - {file = "watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282"}, -] - -[package.extras] -watchmedo = ["PyYAML (>=3.10)"] - -[[package]] -name = "wcwidth" -version = "0.2.13" -description = "Measures the displayed width of unicode strings in a terminal" -optional = false -python-versions = "*" -groups = ["main"] -files = [ - {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, - {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, -] - -[[package]] -name = "wrapt" -version = "1.17.0" -description = "Module for decorators, wrappers and monkey patching." -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "wrapt-1.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a0c23b8319848426f305f9cb0c98a6e32ee68a36264f45948ccf8e7d2b941f8"}, - {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1ca5f060e205f72bec57faae5bd817a1560fcfc4af03f414b08fa29106b7e2d"}, - {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e185ec6060e301a7e5f8461c86fb3640a7beb1a0f0208ffde7a65ec4074931df"}, - {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb90765dd91aed05b53cd7a87bd7f5c188fcd95960914bae0d32c5e7f899719d"}, - {file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:879591c2b5ab0a7184258274c42a126b74a2c3d5a329df16d69f9cee07bba6ea"}, - {file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fce6fee67c318fdfb7f285c29a82d84782ae2579c0e1b385b7f36c6e8074fffb"}, - {file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0698d3a86f68abc894d537887b9bbf84d29bcfbc759e23f4644be27acf6da301"}, - {file = "wrapt-1.17.0-cp310-cp310-win32.whl", hash = "sha256:69d093792dc34a9c4c8a70e4973a3361c7a7578e9cd86961b2bbf38ca71e4e22"}, - {file = "wrapt-1.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:f28b29dc158ca5d6ac396c8e0a2ef45c4e97bb7e65522bfc04c989e6fe814575"}, - {file = "wrapt-1.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:74bf625b1b4caaa7bad51d9003f8b07a468a704e0644a700e936c357c17dd45a"}, - {file = "wrapt-1.17.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f2a28eb35cf99d5f5bd12f5dd44a0f41d206db226535b37b0c60e9da162c3ed"}, - {file = "wrapt-1.17.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:81b1289e99cf4bad07c23393ab447e5e96db0ab50974a280f7954b071d41b489"}, - {file = "wrapt-1.17.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f2939cd4a2a52ca32bc0b359015718472d7f6de870760342e7ba295be9ebaf9"}, - {file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6a9653131bda68a1f029c52157fd81e11f07d485df55410401f745007bd6d339"}, - {file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4e4b4385363de9052dac1a67bfb535c376f3d19c238b5f36bddc95efae15e12d"}, - {file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bdf62d25234290db1837875d4dceb2151e4ea7f9fff2ed41c0fde23ed542eb5b"}, - {file = "wrapt-1.17.0-cp311-cp311-win32.whl", hash = "sha256:5d8fd17635b262448ab8f99230fe4dac991af1dabdbb92f7a70a6afac8a7e346"}, - {file = "wrapt-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:92a3d214d5e53cb1db8b015f30d544bc9d3f7179a05feb8f16df713cecc2620a"}, - {file = "wrapt-1.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:89fc28495896097622c3fc238915c79365dd0ede02f9a82ce436b13bd0ab7569"}, - {file = "wrapt-1.17.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:875d240fdbdbe9e11f9831901fb8719da0bd4e6131f83aa9f69b96d18fae7504"}, - {file = "wrapt-1.17.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5ed16d95fd142e9c72b6c10b06514ad30e846a0d0917ab406186541fe68b451"}, - {file = "wrapt-1.17.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18b956061b8db634120b58f668592a772e87e2e78bc1f6a906cfcaa0cc7991c1"}, - {file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:daba396199399ccabafbfc509037ac635a6bc18510ad1add8fd16d4739cdd106"}, - {file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4d63f4d446e10ad19ed01188d6c1e1bb134cde8c18b0aa2acfd973d41fcc5ada"}, - {file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8a5e7cc39a45fc430af1aefc4d77ee6bad72c5bcdb1322cfde852c15192b8bd4"}, - {file = "wrapt-1.17.0-cp312-cp312-win32.whl", hash = "sha256:0a0a1a1ec28b641f2a3a2c35cbe86c00051c04fffcfcc577ffcdd707df3f8635"}, - {file = "wrapt-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:3c34f6896a01b84bab196f7119770fd8466c8ae3dfa73c59c0bb281e7b588ce7"}, - {file = "wrapt-1.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:714c12485aa52efbc0fc0ade1e9ab3a70343db82627f90f2ecbc898fdf0bb181"}, - {file = "wrapt-1.17.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da427d311782324a376cacb47c1a4adc43f99fd9d996ffc1b3e8529c4074d393"}, - {file = "wrapt-1.17.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba1739fb38441a27a676f4de4123d3e858e494fac05868b7a281c0a383c098f4"}, - {file = "wrapt-1.17.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e711fc1acc7468463bc084d1b68561e40d1eaa135d8c509a65dd534403d83d7b"}, - {file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:140ea00c87fafc42739bd74a94a5a9003f8e72c27c47cd4f61d8e05e6dec8721"}, - {file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:73a96fd11d2b2e77d623a7f26e004cc31f131a365add1ce1ce9a19e55a1eef90"}, - {file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0b48554952f0f387984da81ccfa73b62e52817a4386d070c75e4db7d43a28c4a"}, - {file = "wrapt-1.17.0-cp313-cp313-win32.whl", hash = "sha256:498fec8da10e3e62edd1e7368f4b24aa362ac0ad931e678332d1b209aec93045"}, - {file = "wrapt-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:fd136bb85f4568fffca995bd3c8d52080b1e5b225dbf1c2b17b66b4c5fa02838"}, - {file = "wrapt-1.17.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:17fcf043d0b4724858f25b8826c36e08f9fb2e475410bece0ec44a22d533da9b"}, - {file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4a557d97f12813dc5e18dad9fa765ae44ddd56a672bb5de4825527c847d6379"}, - {file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0229b247b0fc7dee0d36176cbb79dbaf2a9eb7ecc50ec3121f40ef443155fb1d"}, - {file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8425cfce27b8b20c9b89d77fb50e368d8306a90bf2b6eef2cdf5cd5083adf83f"}, - {file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9c900108df470060174108012de06d45f514aa4ec21a191e7ab42988ff42a86c"}, - {file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:4e547b447073fc0dbfcbff15154c1be8823d10dab4ad401bdb1575e3fdedff1b"}, - {file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:914f66f3b6fc7b915d46c1cc424bc2441841083de01b90f9e81109c9759e43ab"}, - {file = "wrapt-1.17.0-cp313-cp313t-win32.whl", hash = "sha256:a4192b45dff127c7d69b3bdfb4d3e47b64179a0b9900b6351859f3001397dabf"}, - {file = "wrapt-1.17.0-cp313-cp313t-win_amd64.whl", hash = "sha256:4f643df3d4419ea3f856c5c3f40fec1d65ea2e89ec812c83f7767c8730f9827a"}, - {file = "wrapt-1.17.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:69c40d4655e078ede067a7095544bcec5a963566e17503e75a3a3e0fe2803b13"}, - {file = "wrapt-1.17.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f495b6754358979379f84534f8dd7a43ff8cff2558dcdea4a148a6e713a758f"}, - {file = "wrapt-1.17.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:baa7ef4e0886a6f482e00d1d5bcd37c201b383f1d314643dfb0367169f94f04c"}, - {file = "wrapt-1.17.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fc931382e56627ec4acb01e09ce66e5c03c384ca52606111cee50d931a342d"}, - {file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8f8909cdb9f1b237786c09a810e24ee5e15ef17019f7cecb207ce205b9b5fcce"}, - {file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ad47b095f0bdc5585bced35bd088cbfe4177236c7df9984b3cc46b391cc60627"}, - {file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:948a9bd0fb2c5120457b07e59c8d7210cbc8703243225dbd78f4dfc13c8d2d1f"}, - {file = "wrapt-1.17.0-cp38-cp38-win32.whl", hash = "sha256:5ae271862b2142f4bc687bdbfcc942e2473a89999a54231aa1c2c676e28f29ea"}, - {file = "wrapt-1.17.0-cp38-cp38-win_amd64.whl", hash = "sha256:f335579a1b485c834849e9075191c9898e0731af45705c2ebf70e0cd5d58beed"}, - {file = "wrapt-1.17.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d751300b94e35b6016d4b1e7d0e7bbc3b5e1751e2405ef908316c2a9024008a1"}, - {file = "wrapt-1.17.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7264cbb4a18dc4acfd73b63e4bcfec9c9802614572025bdd44d0721983fc1d9c"}, - {file = "wrapt-1.17.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33539c6f5b96cf0b1105a0ff4cf5db9332e773bb521cc804a90e58dc49b10578"}, - {file = "wrapt-1.17.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c30970bdee1cad6a8da2044febd824ef6dc4cc0b19e39af3085c763fdec7de33"}, - {file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:bc7f729a72b16ee21795a943f85c6244971724819819a41ddbaeb691b2dd85ad"}, - {file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:6ff02a91c4fc9b6a94e1c9c20f62ea06a7e375f42fe57587f004d1078ac86ca9"}, - {file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2dfb7cff84e72e7bf975b06b4989477873dcf160b2fd89959c629535df53d4e0"}, - {file = "wrapt-1.17.0-cp39-cp39-win32.whl", hash = "sha256:2399408ac33ffd5b200480ee858baa58d77dd30e0dd0cab6a8a9547135f30a88"}, - {file = "wrapt-1.17.0-cp39-cp39-win_amd64.whl", hash = "sha256:4f763a29ee6a20c529496a20a7bcb16a73de27f5da6a843249c7047daf135977"}, - {file = "wrapt-1.17.0-py3-none-any.whl", hash = "sha256:d2c63b93548eda58abf5188e505ffed0229bf675f7c3090f8e36ad55b8cbc371"}, - {file = "wrapt-1.17.0.tar.gz", hash = "sha256:16187aa2317c731170a88ef35e8937ae0f533c402872c1ee5e6d079fcf320801"}, -] - -[metadata] -lock-version = "2.1" -python-versions = "^3.11" -content-hash = "0037818706617fa5806ad3e5dbddd4045fa4835e8a5c4a927faa08640b5447f8" diff --git a/operators/nova-flavors/pyproject.toml b/operators/nova-flavors/pyproject.toml deleted file mode 100644 index a6223c427..000000000 --- a/operators/nova-flavors/pyproject.toml +++ /dev/null @@ -1,57 +0,0 @@ -[tool.poetry] -name = "nova-flavors" -version = "0.0.1" -description = "Monitors FlavorSpec repository and reconciles it with Nova" -authors = ["Marek Skrobacki "] -license = "Apache License 2.0" -readme = "README.md" -packages = [ - { include = "nova_flavors" } -] - -[tool.poetry.dependencies] -python = "^3.11" -understack-flavor-matcher = {path = "../../python/understack-flavor-matcher"} -python-novaclient = "^18.8.0" -watchdog = "^6.0.0" - -[tool.poetry.group.dev.dependencies] - - -[tool.poetry.group.test.dependencies] -pytest = "^8.3.3" -pytest-cov = "^7.0.0" -pytest-github-actions-annotate-failures = "*" -pytest-mock = "^3.14.0" - -[build-system] -requires = ["poetry-core"] -build-backend = "poetry.core.masonry.api" - -[tool.pytest.ini_options] -minversion = "6.0" -addopts = "-ra --cov=nova_flavors" -testpaths = [ - "tests", -] - -[tool.ruff] -target-version = "py310" -fix = true - -[tool.ruff.lint] -select = [ - "S", # flake8-bandit -] - -[tool.ruff.lint.per-file-ignores] -"tests/**/*.py" = [ - "S101", # allow 'assert' for pytest - "S105", # allow hardcoded passwords for testing - "S106", # allow hardcoded passwords for testing -] -[tool.isort] -profile = "open_stack" - -[tool.poetry.scripts] -nova-flavors-sync = "nova_flavors.reconcile:main" diff --git a/operators/nova-flavors/tests/test_flavor_synchronizer.py b/operators/nova-flavors/tests/test_flavor_synchronizer.py deleted file mode 100644 index 3d322f87a..000000000 --- a/operators/nova-flavors/tests/test_flavor_synchronizer.py +++ /dev/null @@ -1,106 +0,0 @@ -from unittest.mock import MagicMock - -from flavor_matcher.flavor_spec import FlavorSpec -from novaclient import client as novaclient -import pytest -from nova_flavors.flavor_synchronizer import FlavorSynchronizer - - -@pytest.fixture -def flavor_synchronizer(): - return FlavorSynchronizer( - username="test_username", - password="test_password", - auth_url="test_auth_url", - ) - - -@pytest.fixture -def mock_nova_client(mocker): - mock_nova_client = mocker.patch.object(novaclient, "Client") - mock_nova_client.return_value = MagicMock() - return mock_nova_client - - -@pytest.fixture -def mock_flavor(mocker): - return mocker.patch.object(FlavorSpec, "__init__", return_value=None) - - -@pytest.fixture -def flavor(): - return FlavorSpec( - name="maybeprod.test_flavor", - memory_gb=1, - cpu_cores=2, - drives=[10, 10], - model="xyz", - manufacturer="EvilCorp", - cpu_model="Pentium 60", - pci=[], - ) - - -def test_flavor_synchronizer_init(flavor_synchronizer): - assert flavor_synchronizer.username == "test_username" - assert flavor_synchronizer.password == "test_password" - assert flavor_synchronizer.user_domain_name == "service" - assert flavor_synchronizer.auth_url == "test_auth_url" - - -def test_flavor_synchronizer_reconcile_new_flavor( - flavor_synchronizer, mock_nova_client, flavor -): - mock_nova_client.return_value.flavors.list.return_value = [] - flavor_synchronizer.reconcile([flavor]) - mock_nova_client.return_value.flavors.create.assert_called_once_with( - flavor.name, flavor.memory_mib, flavor.cpu_cores, min(flavor.drives) - ) - - -def test_flavor_synchronizer_reconcile_existing_flavor( - flavor_synchronizer, mock_nova_client, flavor -): - existing_flavor = MagicMock() - existing_flavor.name = flavor.name - existing_flavor.ram = flavor.memory_mib - existing_flavor.disk = max(flavor.drives) - existing_flavor.vcpus = flavor.cpu_cores - mock_nova_client.return_value.flavors.list.return_value = [existing_flavor] - flavor_synchronizer.reconcile([flavor]) - mock_nova_client.return_value.flavors.create.assert_not_called() - - -def test_flavor_synchronizer_reconcile_existing_flavor_update_needed( - flavor_synchronizer, mock_nova_client, flavor -): - existing_flavor = MagicMock() - existing_flavor.name = flavor.name - existing_flavor.ram = flavor.memory_mib + 1 - existing_flavor.disk = max(flavor.drives) - existing_flavor.vcpus = flavor.cpu_cores - existing_flavor.delete = MagicMock() - mock_nova_client.return_value.flavors.list.return_value = [existing_flavor] - flavor_synchronizer.reconcile([flavor]) - existing_flavor.delete.assert_called_once() - mock_nova_client.return_value.flavors.create.assert_called_once_with( - flavor.name, flavor.memory_mib, flavor.cpu_cores, min(flavor.drives) - ) - - -def test_flavor_synchronizer_create_flavor( - mock_nova_client, flavor_synchronizer, flavor -): - mock_create_flavor = mock_nova_client.return_value.flavors.create.return_value - flavor_synchronizer._create(flavor) - mock_nova_client.return_value.flavors.create.assert_called_once_with( - flavor.name, flavor.memory_mib, flavor.cpu_cores, min(flavor.drives) - ) - mock_create_flavor.set_keys.assert_called_once_with( - { - "resources:DISK_GB": 0, - "resources:MEMORY_MB": 0, - "resources:VCPU": 0, - flavor.baremetal_nova_resource_class: 1, - } - ) diff --git a/operators/nova-flavors/tests/test_reconcile.py b/operators/nova-flavors/tests/test_reconcile.py deleted file mode 100644 index 7734ee77a..000000000 --- a/operators/nova-flavors/tests/test_reconcile.py +++ /dev/null @@ -1,64 +0,0 @@ -import logging -import os -import pytest -from unittest.mock import patch - -from nova_flavors.reconcile import ( - FlavorSynchronizer, - SpecChangedHandler, - main, -) -from watchdog.observers import Observer - - -@pytest.fixture -def mock_logger(mocker): - return mocker.Mock(spec=logging.Logger) - - -@pytest.mark.parametrize("return_value", [None, "/non/existent/directory"]) -def test_flavors_dir_env_var_not_set(mocker, return_value): - # Set up - mocker.patch("os.getenv", return_value=return_value) - if return_value == "/non/existent/directory": - mocker.patch("os.path.isdir", return_value=False) - - # Execute and Verify - with pytest.raises(Exception): - main() - - -@patch.dict( - "os.environ", - { - "FLAVORS_ENV": "testenv", - "NOVA_FLAVOR_MONITOR_LOGLEVEL": "info", - "FLAVORS_DIR": "/", - }, -) -def test_main_exception(mocker, mock_logger): - # Set up - mocker.patch("nova_flavors.reconcile.setup_logger", return_value=mock_logger) - mock_flavor_synchronizer = mocker.Mock(spec=FlavorSynchronizer) - mocker.patch( - "nova_flavors.reconcile.FlavorSynchronizer", - return_value=mock_flavor_synchronizer, - ) - mock_spec_changed_handler = mocker.Mock(spec=SpecChangedHandler) - mocker.patch( - "nova_flavors.reconcile.SpecChangedHandler", - return_value=mock_spec_changed_handler, - ) - mock_observer = mocker.Mock(spec=Observer) - mocker.patch("nova_flavors.reconcile.Observer", return_value=mock_observer) - mocker.patch("time.sleep", side_effect=Exception("Mock exception")) - - # Execute - with pytest.raises(Exception): - main() - - # Verify - mock_observer.schedule.assert_called_once() - mock_observer.start.assert_called_once() - mock_observer.stop.assert_called_once() - mock_observer.join.assert_called_once() diff --git a/operators/nova-flavors/tests/test_spec_changed_handler.py b/operators/nova-flavors/tests/test_spec_changed_handler.py deleted file mode 100644 index 2a85dfeca..000000000 --- a/operators/nova-flavors/tests/test_spec_changed_handler.py +++ /dev/null @@ -1,60 +0,0 @@ -import pytest -import time -from unittest.mock import MagicMock -from nova_flavors.flavor_synchronizer import FlavorSynchronizer -from nova_flavors.spec_changed_handler import SpecChangedHandler -from watchdog.events import DirModifiedEvent, FileModifiedEvent - - -@pytest.fixture -def handler(): - synchronizer = MagicMock(spec=FlavorSynchronizer) - flavors_cback = MagicMock() - return SpecChangedHandler(synchronizer, flavors_cback) - - -def test_init(handler): - assert handler.last_call is None - assert handler.synchronizer is not None - assert handler.flavors_cback is not None - - -def test_on_modified_dir(handler): - event = DirModifiedEvent("/path/to/dir") - handler.on_modified(event) - handler.synchronizer.reconcile.assert_called_once_with(handler.flavors_cback()) - - -def test_on_modified_file(handler): - event = MagicMock(spec=FileModifiedEvent) - handler.on_modified(event) - handler.synchronizer.reconcile.assert_not_called() - - -def test_run_cool_down(handler): - event = DirModifiedEvent("/path/to/dir") - handler.last_call = time.time() - 29 - handler._run(event) - handler.synchronizer.reconcile.assert_not_called() - - -def test_run_no_cool_down(handler): - event = DirModifiedEvent("/path/to/dir") - handler.last_call = time.time() - 31 - handler._run(event) - handler.synchronizer.reconcile.assert_called_once_with(handler.flavors_cback()) - - -def test_run_first_call(handler): - event = DirModifiedEvent("/path/to/dir") - handler._run(event) - handler.synchronizer.reconcile.assert_called_once_with(handler.flavors_cback()) - - -@pytest.mark.parametrize("last_call", [None, time.time() - 100]) -def test_run_logging(handler, last_call, caplog): - event = DirModifiedEvent("/path/to/dir") - handler.last_call = last_call - with caplog.at_level("INFO"): - handler._run(event) - assert "Flavors directory /path/to/dir has changed." in caplog.text diff --git a/python/ironic-understack/ironic_understack/conf.py b/python/ironic-understack/ironic_understack/conf.py index 6ea1bba64..d41bbb234 100644 --- a/python/ironic-understack/ironic_understack/conf.py +++ b/python/ironic-understack/ironic_understack/conf.py @@ -7,9 +7,9 @@ def setup_conf(): grp = cfg.OptGroup("ironic_understack") opts = [ cfg.StrOpt( - "flavors_dir", - help="directory storing Flavor description YAML files", - default="/var/lib/understack/flavors/undercloud-nautobot-device-types.git/flavors", + "device_types_dir", + help="directory storing Device Type description YAML files", + default="/var/lib/understack/device-types", ) ] cfg.CONF.register_group(grp) diff --git a/python/ironic-understack/ironic_understack/redfish_inspect_understack.py b/python/ironic-understack/ironic_understack/redfish_inspect_understack.py index d6483c37c..b45fbc883 100644 --- a/python/ironic-understack/ironic_understack/redfish_inspect_understack.py +++ b/python/ironic-understack/ironic_understack/redfish_inspect_understack.py @@ -12,8 +12,9 @@ """Redfish Inspect Interface modified for Understack.""" import re +from pathlib import Path -from flavor_matcher.flavor_spec import FlavorSpec +from flavor_matcher.device_type import DeviceType from flavor_matcher.machine import Machine from flavor_matcher.matcher import Matcher from ironic.drivers.drac import IDRACHardware @@ -27,8 +28,8 @@ from ironic_understack.conf import CONF LOG = log.getLogger(__name__) -FLAVORS = FlavorSpec.from_directory(CONF.ironic_understack.flavors_dir) -LOG.info("Loaded %d flavor specifications.", len(FLAVORS)) +DEVICE_TYPES = DeviceType.from_directory(Path(CONF.ironic_understack.device_types_dir)) +LOG.info("Loaded %d device types.", len(DEVICE_TYPES)) class FlavorInspectMixin: @@ -85,21 +86,29 @@ def inspect_hardware(self, task): else: model_name = model_name_match.group(1) + # Extract additional fields for new Machine API + cpu_cores = inventory.get("cpu", {}).get("count", 0) + manufacturer = inventory.get("system_vendor", {}).get("manufacturer", "") + machine = Machine( memory_mb=inventory["memory"]["physical_mb"], disk_gb=smallest_disk_gb, cpu=inventory["cpu"]["model_name"], + cpu_cores=cpu_cores, + manufacturer=manufacturer, model=model_name, ) - matcher = Matcher(FLAVORS) - best_flavor = matcher.pick_best_flavor(machine) - if not best_flavor: - LOG.warning("No flavor matched for %s", task.node.uuid) + matcher = Matcher(device_types=DEVICE_TYPES) + match_result = matcher.match(machine) + if not match_result: + LOG.warning("No resource class matched for %s", task.node.uuid) return upstream_state - LOG.info("Matched %s to flavor %s", task.node.uuid, best_flavor) - task.node.resource_class = f"baremetal.{best_flavor.name}" + device_type, resource_class = match_result + LOG.info("Matched %s to resource class %s", task.node.uuid, resource_class.name) + + task.node.resource_class = resource_class.name task.node.save() return upstream_state diff --git a/python/ironic-understack/ironic_understack/resource_class.py b/python/ironic-understack/ironic_understack/resource_class.py index dce2435b1..001b7a748 100644 --- a/python/ironic-understack/ironic_understack/resource_class.py +++ b/python/ironic-understack/ironic_understack/resource_class.py @@ -1,7 +1,8 @@ # from ironic.drivers.modules.inspector.hooks import base import re +from pathlib import Path -from flavor_matcher.flavor_spec import FlavorSpec +from flavor_matcher.device_type import DeviceType from flavor_matcher.machine import Machine from flavor_matcher.matcher import Matcher from ironic.common import exception @@ -12,19 +13,19 @@ LOG = logging.getLogger(__name__) -FLAVORS = FlavorSpec.from_directory(CONF.ironic_understack.flavors_dir) -LOG.info("Loaded %d flavor specifications.", len(FLAVORS)) +DEVICE_TYPES = DeviceType.from_directory(Path(CONF.ironic_understack.device_types_dir)) +LOG.info("Loaded %d device types.", len(DEVICE_TYPES)) class NoMatchError(Exception): pass -class UndercloudResourceClassHook(base.InspectionHook): +class ResourceClassHook(base.InspectionHook): """Hook to set the node's resource_class based on the inventory.""" def __call__(self, task, inventory, plugin_data): - """Update node resource_class with deducted flavor.""" + """Update node resource_class with matched resource class.""" try: memory_mb = inventory["memory"]["physical_mb"] disk_size_gb = int(int(inventory["disks"][0]["size"]) / 10**9) @@ -35,15 +36,24 @@ def __call__(self, task, inventory, plugin_data): ) if not model_name: - LOG.warning("No model_name detected. skipping flavor setting.") + LOG.warning("No model_name detected. skipping resource class setting.") raise NoMatchError("mode_name not matched") else: model_name = model_name.group(1) + # Extract additional fields for new Machine API + cpu_cores = inventory.get("cpu", {}).get("count", 0) + manufacturer = inventory.get("system_vendor", {}).get("manufacturer", "") + + # trim ' Inc.' + manufacturer = manufacturer.replace(" Inc.", "") + machine = Machine( memory_mb=memory_mb, cpu=cpu_model_name, + cpu_cores=cpu_cores, disk_gb=disk_size_gb, + manufacturer=manufacturer, model=model_name, ) @@ -65,13 +75,14 @@ def __call__(self, task, inventory, plugin_data): node=task.node.uuid, reason=msg ) from None except NoMatchError: - msg = f"No matching flavor found for {task.node.uuid}" + msg = f"No matching resource class found for {task.node.uuid}" LOG.error(msg) def classify(self, machine): - matcher = Matcher(FLAVORS) - flavor = matcher.pick_best_flavor(machine) - if not flavor: - raise NoMatchError(f"No flavor found for {machine}") + matcher = Matcher(device_types=DEVICE_TYPES) + match_result = matcher.match(machine) + if not match_result: + raise NoMatchError(f"No resource class found for {machine}") else: - return flavor + device_type, resource_class = match_result + return resource_class.name diff --git a/python/ironic-understack/pyproject.toml b/python/ironic-understack/pyproject.toml index c4fab7bd2..cb3c5f52d 100644 --- a/python/ironic-understack/pyproject.toml +++ b/python/ironic-understack/pyproject.toml @@ -17,7 +17,7 @@ dependencies = [ ] [project.entry-points."ironic.inspection.hooks"] -resource-class = "ironic_understack.resource_class:UndercloudResourceClassHook" +resource-class = "ironic_understack.resource_class:ResourceClassHook" [project.entry-points."ironic.hardware.interfaces.inspect"] redfish-understack = "ironic_understack.redfish_inspect_understack:UnderstackRedfishInspect" diff --git a/python/understack-flavor-matcher/flavor_matcher/device_type.py b/python/understack-flavor-matcher/flavor_matcher/device_type.py new file mode 100644 index 000000000..5c031f046 --- /dev/null +++ b/python/understack-flavor-matcher/flavor_matcher/device_type.py @@ -0,0 +1,139 @@ +import logging +from dataclasses import dataclass +from pathlib import Path + +import yaml + +logger = logging.getLogger(__name__) + + +@dataclass +class CpuSpec: + cores: int + model: str + + +@dataclass +class MemorySpec: + size: int # MB + + +@dataclass +class DriveSpec: + size: int # GB + + +@dataclass +class InterfaceSpec: + name: str + type: str + mgmt_only: bool = False + + +@dataclass +class PowerPortSpec: + name: str + type: str + maximum_draw: int | None = None + + +@dataclass +class ResourceClass: + name: str + cpu: CpuSpec + memory: MemorySpec + drives: list[DriveSpec] + nic_count: int + + +@dataclass +class DeviceType: + class_: str # Using class_ since class is a Python keyword + manufacturer: str + model: str + u_height: float + is_full_depth: bool + resource_class: list[ResourceClass] + interfaces: list[InterfaceSpec] | None = None + power_ports: list[PowerPortSpec] | None = None + + @staticmethod + def from_yaml(yaml_str: str) -> "DeviceType": + data = yaml.safe_load(yaml_str) + + # Parse resource classes + resource_classes = [] + for rc_data in data.get("resource_class", []): + cpu = CpuSpec(cores=rc_data["cpu"]["cores"], model=rc_data["cpu"]["model"]) + memory = MemorySpec(size=rc_data["memory"]["size"]) + drives = [DriveSpec(size=d["size"]) for d in rc_data["drives"]] + resource_classes.append( + ResourceClass( + name=rc_data["name"], + cpu=cpu, + memory=memory, + drives=drives, + nic_count=rc_data["nic_count"], + ) + ) + + # Parse interfaces + interfaces = None + if "interfaces" in data: + interfaces = [ + InterfaceSpec( + name=i["name"], + type=i["type"], + mgmt_only=i.get("mgmt_only", False), + ) + for i in data["interfaces"] + ] + + # Parse power ports + power_ports = None + if "power-ports" in data: + power_ports = [ + PowerPortSpec( + name=p["name"], + type=p["type"], + maximum_draw=p.get("maximum_draw"), + ) + for p in data["power-ports"] + ] + + return DeviceType( + class_=data["class"], + manufacturer=data["manufacturer"], + model=data["model"], + u_height=data["u_height"], + is_full_depth=data["is_full_depth"], + resource_class=resource_classes, + interfaces=interfaces, + power_ports=power_ports, + ) + + @staticmethod + def from_directory(data_dir: Path) -> list["DeviceType"]: + """Load all device type definitions from a directory.""" + device_types = [] + if not data_dir.exists(): + return device_types + + for pattern in ("*.yaml", "*.yml"): + for filepath in data_dir.rglob(pattern): + try: + yaml_content = filepath.read_text() + device_type = DeviceType.from_yaml(yaml_content) + device_types.append(device_type) + except yaml.YAMLError as e: + logger.error("Error parsing YAML file %s: %s", filepath.name, e) + except Exception as e: + logger.error("Error processing file %s: %s", filepath.name, e) + return device_types + + def get_resource_class(self, name: str) -> ResourceClass | None: + """Get a specific resource class by name.""" + for rc in self.resource_class: + if rc.name == name: + return rc + return None diff --git a/python/understack-flavor-matcher/flavor_matcher/flavor_spec.py b/python/understack-flavor-matcher/flavor_matcher/flavor_spec.py deleted file mode 100644 index cf16c3452..000000000 --- a/python/understack-flavor-matcher/flavor_matcher/flavor_spec.py +++ /dev/null @@ -1,134 +0,0 @@ -import os -import re -from dataclasses import dataclass - -import yaml - -from flavor_matcher.machine import Machine - - -@dataclass -class PciSpec: - vendor_id: str - device_id: str - sub_vendor_id: str - sub_device_id: str - - -@dataclass -class FlavorSpec: - name: str - manufacturer: str - model: str - memory_gb: int - cpu_cores: int - cpu_model: str - drives: list[int] - pci: list[PciSpec] - - @staticmethod - def from_yaml(yaml_str: str) -> "FlavorSpec": - data = yaml.safe_load(yaml_str) - return FlavorSpec( - name=data["name"], - manufacturer=data["manufacturer"], - model=data["model"], - memory_gb=data["memory_gb"], - cpu_cores=data["cpu_cores"], - cpu_model=data["cpu_model"], - drives=data["drives"], - pci=data.get("pci", []), - ) - - @property - def baremetal_nova_resource_class(self): - """Returns flavor name converted to be used with Nova flavor resources. - - https://docs.openstack.org/ironic/latest/install/configure-nova-flavors.html - """ - converted_name = re.sub(r"[^\w]", "_", self.name).upper() - return f"resources:CUSTOM_BAREMETAL_{converted_name}" - - @property - def memory_mib(self): - """Returns memory size in MiB.""" - return self.memory_gb * 1024 - - @staticmethod - def from_directory(directory: str = "/etc/flavors/") -> list["FlavorSpec"]: - flavor_specs = [] - for root, _, files in os.walk(directory): - for filename in files: - if filename.endswith(".yaml") or filename.endswith(".yml"): - filepath = os.path.join(root, filename) - try: - with open(filepath) as file: - yaml_content = file.read() - flavor_spec = FlavorSpec.from_yaml(yaml_content) - flavor_specs.append(flavor_spec) - except yaml.YAMLError as e: - print(f"Error parsing YAML file {filename}: {e}") - except Exception as e: - print(f"Error processing file {filename}: {e}") - return flavor_specs - - def score_machine(self, machine: Machine): - # Scoring Rules: - # - # 1. 100% match gets highest priority, no further evaluation needed - # 2. If the machine has less memory size than specified in the flavor, - # it cannot be used - the score should be 0. - # 3. If the machine has smaller disk size than specified in the flavor, - # it cannot be used - the score should be 0. - # 4. If the machine's model does not match exactly, score should be 0 - # 5. Machine must match the flavor on one of the CPU models exactly. - # 6. If the machine has exact amount memory as specified in flavor, but - # more disk space it is less desirable than the machine that matches - # exactly on both disk and memory. - # 7. If the machine has exact amount of disk as specified in flavor, - # but more memory space it is less desirable than the machine that - # matches exactly on both disk and memory. - - # Rule 1: 100% match gets the highest priority - if ( - machine.memory_gb == self.memory_gb - and machine.disk_gb in self.drives - and machine.cpu == self.cpu_model - and machine.model == self.model - ): - return 100 - - # Rule 2: If machine has less memory than specified in the - # flavor, it cannot be used - if machine.memory_gb < self.memory_gb: - return 0 - - # Rule 3: If machine has smaller disk than specified in the - # flavor, it cannot be used - if any(machine.disk_gb < drive for drive in self.drives): - return 0 - - # Rule 4: Machine's model must match exactly - if machine.model != self.model: - return 0 - - # Rule 5: Machine must match the flavor on one of the CPU models exactly - if machine.cpu != self.cpu_model: - return 0 - - # Rule 6 and 7: Rank based on exact matches or excess capacity - score = 0 - - # Exact memory match gives preference - if machine.memory_gb == self.memory_gb: - score += 10 - elif machine.memory_gb > self.memory_gb: - score += 5 # Less desirable but still usable - - # Exact disk match gives preference - if machine.disk_gb in self.drives: - score += 10 - elif all(machine.disk_gb > drive for drive in self.drives): - score += 5 # Less desirable but still usable - - return score diff --git a/python/understack-flavor-matcher/flavor_matcher/machine.py b/python/understack-flavor-matcher/flavor_matcher/machine.py index cef753190..55597624f 100644 --- a/python/understack-flavor-matcher/flavor_matcher/machine.py +++ b/python/understack-flavor-matcher/flavor_matcher/machine.py @@ -5,7 +5,9 @@ class Machine: memory_mb: int cpu: str + cpu_cores: int disk_gb: int + manufacturer: str model: str @property diff --git a/python/understack-flavor-matcher/flavor_matcher/matcher.py b/python/understack-flavor-matcher/flavor_matcher/matcher.py index fac5be3be..a1d78594e 100644 --- a/python/understack-flavor-matcher/flavor_matcher/matcher.py +++ b/python/understack-flavor-matcher/flavor_matcher/matcher.py @@ -1,28 +1,51 @@ -from flavor_matcher.flavor_spec import FlavorSpec +from flavor_matcher.device_type import DeviceType +from flavor_matcher.device_type import ResourceClass from flavor_matcher.machine import Machine class Matcher: - def __init__(self, flavors: list[FlavorSpec]): - self.flavors = flavors - - def match(self, machine: Machine) -> list[FlavorSpec]: - """Find list of all flavors that the machine is eligible for.""" - results = [] - for flavor in self.flavors: - score = flavor.score_machine(machine) - if score > 0: - results.append(flavor) - return results - - def pick_best_flavor(self, machine: Machine) -> FlavorSpec | None: - """Selects the best patching flavor. - - Obtains list of all flavors that particular machine can be classified as, - then tries to select "the best" one. + def __init__(self, device_types: list[DeviceType]): + self.device_types = device_types + + def match(self, machine: Machine) -> tuple[DeviceType, ResourceClass] | None: + """Find the resource class that matches the machine's hardware specs. + + Returns a tuple of (DeviceType, ResourceClass) that matches the machine, + or None if no match is found. + + Matching rules: + 1. Manufacturer and model must match exactly + 2. CPU model must match exactly + 4. Memory must match exactly (in MB) + 5. Must have at least as many drives as specified in resource class + 6. Each drive must be at least as large as the smallest drive in resource class """ - possible = self.match(machine) + for device_type in self.device_types: + # Check manufacturer and model + if ( + device_type.manufacturer != machine.manufacturer + or device_type.model != machine.model + ): + continue + + # Check each resource class in this device type + for resource_class in device_type.resource_class: + # Check CPU model + if resource_class.cpu.model != machine.cpu: + continue + + # Check memory (in MB) + if resource_class.memory.size != machine.memory_mb: + continue + + # Check drives - machine must have enough storage + # For simplicity, we check if machine's total disk meets the minimum + # drive size specified in resource class + min_drive_size = min(d.size for d in resource_class.drives) + if machine.disk_gb < min_drive_size: + continue + + # Found a match + return (device_type, resource_class) - if len(possible) == 0: - return None - return max(possible, key=lambda flv: flv.memory_gb) + return None diff --git a/python/understack-flavor-matcher/tests/test_device_type.py b/python/understack-flavor-matcher/tests/test_device_type.py new file mode 100644 index 000000000..d2a2c9282 --- /dev/null +++ b/python/understack-flavor-matcher/tests/test_device_type.py @@ -0,0 +1,143 @@ +import pytest + +from flavor_matcher.device_type import DeviceType + + +@pytest.fixture +def valid_device_type_yaml(): + return """ +class: server +manufacturer: Dell +model: PowerEdge R7615 +u_height: 2 +is_full_depth: true + +interfaces: + - name: iDRAC + type: 1000base-t + mgmt_only: true + +resource_class: + - name: m1.small + cpu: + cores: 16 + model: AMD EPYC 9124 + memory: + size: 128 + drives: + - size: 480 + - size: 480 + nic_count: 2 + + - name: m1.medium + cpu: + cores: 32 + model: AMD EPYC 9334 + memory: + size: 256 + drives: + - size: 960 + - size: 960 + nic_count: 2 +""" + + +def test_device_type_from_yaml(valid_device_type_yaml): + device_type = DeviceType.from_yaml(valid_device_type_yaml) + + assert device_type.class_ == "server" + assert device_type.manufacturer == "Dell" + assert device_type.model == "PowerEdge R7615" + assert device_type.u_height == 2 + assert device_type.is_full_depth is True + + # Check interfaces + assert device_type.interfaces is not None + assert len(device_type.interfaces) == 1 + assert device_type.interfaces[0].name == "iDRAC" + assert device_type.interfaces[0].type == "1000base-t" + assert device_type.interfaces[0].mgmt_only is True + + # Check resource classes + assert len(device_type.resource_class) == 2 + + # Check first resource class + rc1 = device_type.resource_class[0] + assert rc1.name == "m1.small" + assert rc1.cpu.cores == 16 + assert rc1.cpu.model == "AMD EPYC 9124" + assert rc1.memory.size == 128 + assert len(rc1.drives) == 2 + assert rc1.drives[0].size == 480 + assert rc1.drives[1].size == 480 + assert rc1.nic_count == 2 + + # Check second resource class + rc2 = device_type.resource_class[1] + assert rc2.name == "m1.medium" + assert rc2.cpu.cores == 32 + assert rc2.cpu.model == "AMD EPYC 9334" + assert rc2.memory.size == 256 + + +def test_get_resource_class(valid_device_type_yaml): + device_type = DeviceType.from_yaml(valid_device_type_yaml) + + rc = device_type.get_resource_class("m1.small") + assert rc is not None + assert rc.name == "m1.small" + assert rc.cpu.cores == 16 + + rc = device_type.get_resource_class("m1.medium") + assert rc is not None + assert rc.name == "m1.medium" + assert rc.cpu.cores == 32 + + rc = device_type.get_resource_class("nonexistent") + assert rc is None + + +def test_device_type_from_directory(tmp_path): + # Create test files + yaml_content = """ +class: server +manufacturer: Dell +model: TestModel +u_height: 1 +is_full_depth: false +resource_class: + - name: test.small + cpu: + cores: 4 + model: Test CPU + memory: + size: 8 + drives: + - size: 100 + nic_count: 1 +""" + test_file = tmp_path / "test-device.yaml" + test_file.write_text(yaml_content) + + device_types = DeviceType.from_directory(tmp_path) + assert len(device_types) == 1 + assert device_types[0].model == "TestModel" + assert device_types[0].resource_class[0].name == "test.small" + + +def test_device_type_minimal_yaml(): + """Test with minimal required fields only.""" + minimal_yaml = """ +class: server +manufacturer: Dell +model: TestModel +u_height: 1 +is_full_depth: true +""" + device_type = DeviceType.from_yaml(minimal_yaml) + assert device_type.class_ == "server" + assert device_type.manufacturer == "Dell" + assert device_type.model == "TestModel" + assert device_type.resource_class == [] + assert device_type.interfaces is None + assert device_type.power_ports is None diff --git a/python/understack-flavor-matcher/tests/test_flavor_spec.py b/python/understack-flavor-matcher/tests/test_flavor_spec.py deleted file mode 100644 index f158f6bd8..000000000 --- a/python/understack-flavor-matcher/tests/test_flavor_spec.py +++ /dev/null @@ -1,335 +0,0 @@ -import os -from unittest.mock import mock_open -from unittest.mock import patch - -import pytest - -from flavor_matcher.flavor_spec import FlavorSpec -from flavor_matcher.machine import Machine - - -@pytest.fixture -def valid_yaml(): - return """ ---- -name: gp2.ultramedium -manufacturer: Dell -model: PowerEdge R7615 -memory_gb: 7777 -cpu_cores: 245 -cpu_model: AMD EPYC 9254 245-Core Processor -drives: - - 960 - - 960 -""" - - -@pytest.fixture -def invalid_yaml(): - return """ ---- -name: gp2.ultramedium -x: abcd -malformed_field: 123: invalid -""" - - -@pytest.fixture -def yaml_directory(tmp_path, valid_yaml, invalid_yaml): - valid_file = tmp_path / "valid.yaml" - invalid_file = tmp_path / "invalid.yaml" - - valid_file.write_text(valid_yaml) - invalid_file.write_text(invalid_yaml) - - return tmp_path - - -def test_from_yaml(valid_yaml): - spec = FlavorSpec.from_yaml(valid_yaml) - assert spec.name == "gp2.ultramedium" - assert spec.manufacturer == "Dell" - assert spec.model == "PowerEdge R7615" - assert spec.memory_gb == 7777 - assert spec.cpu_cores == 245 - assert spec.cpu_model == "AMD EPYC 9254 245-Core Processor" - assert spec.drives == [960, 960] - - -def test_from_yaml_invalid(invalid_yaml): - with pytest.raises(Exception): # noqa:B017 - FlavorSpec.from_yaml(invalid_yaml) - - -@patch("os.walk") -@patch("builtins.open", new_callable=mock_open) -@patch.dict(os.environ, {"FLAVORS_ENV": "nonprod"}) -def test_from_directory(mocked_open, mock_walk, valid_yaml, invalid_yaml): - mock_walk.return_value = [ - ("/etc/flavors", [], ["valid.yaml", "invalid.yaml"]), - ] - mock_file_handles = [ - mock_open(read_data=valid_yaml).return_value, - mock_open(read_data=invalid_yaml).return_value, - ] - mocked_open.side_effect = mock_file_handles - specs = FlavorSpec.from_directory("/etc/flavors/") - - assert len(specs) == 1 - assert specs[0].name == "gp2.ultramedium" - assert specs[0].memory_gb == 7777 - assert specs[0].cpu_cores == 245 - - -@patch.dict(os.environ, {"FLAVORS_ENV": "nonprod"}) -def test_from_directory_with_real_files(yaml_directory): - specs = FlavorSpec.from_directory(str(yaml_directory)) - - assert len(specs) == 1 - assert specs[0].name == "gp2.ultramedium" - assert specs[0].memory_gb == 7777 - assert specs[0].cpu_cores == 245 - - -def test_empty_directory(tmp_path): - specs = FlavorSpec.from_directory(str(tmp_path)) - assert len(specs) == 0 - - -@pytest.fixture -def machines(): - return [ - # 1024 GB, exact CPU, medium - Machine( - memory_mb=102400, - cpu="AMD EPYC 9254 245-Core Processor", - disk_gb=1000, - model="Dell XPS1319", - ), - # 800 GB, non-matching CPU - Machine( - memory_mb=800000, - cpu="Intel Xeon E5-2676 v3", - disk_gb=500, - model="Dell XPS1319", - ), - # 200 GB, exact CPU, medium - Machine( - memory_mb=200000, - cpu="AMD EPYC 9254 245-Core Processor", - disk_gb=150, - model="Dell XPS1319", - ), - # 300 GB, non-matching CPU - Machine( - memory_mb=300000, - cpu="Intel Xeon E5-2676 v3", - disk_gb=500, - model="Dell XPS1319", - ), - # 409 GB, exact CPU, large - Machine( - memory_mb=409600, - cpu="AMD EPYC 9254 245-Core Processor", - disk_gb=2000, - model="Dell XPS1319", - ), - ] - - -@pytest.fixture -def flavors(): - return [ - FlavorSpec( - name="small", - manufacturer="Dell", - model="Dell XPS1319", - memory_gb=100, - cpu_cores=13, - cpu_model="AMD EPYC 9254 245-Core Processor", - drives=[500, 500], - pci=[], - ), - FlavorSpec( - name="medium", - manufacturer="Dell", - model="Fake Machine", - memory_gb=200, - cpu_cores=15, - cpu_model="AMD EPYC 9254 245-Core Processor", - drives=[1500, 1500], - pci=[], - ), - FlavorSpec( - name="large", - manufacturer="Dell", - model="Dell XPS1319", - memory_gb=400, - cpu_cores=27, - cpu_model="AMD EPYC 9254 245-Core Processor", - drives=[1800, 1800], - pci=[], - ), - ] - - -def test_exact_match(flavors): - machine = Machine( - memory_mb=102400, - cpu="AMD EPYC 9254 245-Core Processor", - disk_gb=500, - model="Dell XPS1319", - ) - assert flavors[0].score_machine(machine) == 100 - assert flavors[1].score_machine(machine) == 0 - - -def test_wrong_model_non_match(flavors): - machine = Machine( - memory_mb=102400, - cpu="AMD EPYC 9254 245-Core Processor", - disk_gb=500, - model="Some other model", - ) - for flavor in flavors: - assert flavor.score_machine(machine) == 0 - - -def test_memory_too_small(flavors): - machine = Machine( - memory_mb=51200, - cpu="AMD EPYC 9254 245-Core Processor", - disk_gb=500, - model="Dell XPS1319", - ) - for flavor in flavors: - assert flavor.score_machine(machine) == 0 - - -def test_disk_too_small(flavors): - machine = Machine( - memory_mb=204800, - cpu="AMD EPYC 9254 245-Core Processor", - disk_gb=100, - model="Dell XPS1319", - ) - assert all(flavor.score_machine(machine) == 0 for flavor in flavors) - - -def test_cpu_model_not_matching(flavors): - machine = Machine( - memory_mb=102400, - cpu="Non-Existent CPU Model", - disk_gb=500, - model="Dell XPS1319", - ) - assert all(flavor.score_machine(machine) == 0 for flavor in flavors) - - -def test_memory_match_but_more_disk(flavors): - machine = Machine( - memory_mb=102400, - cpu="AMD EPYC 9254 245-Core Processor", - disk_gb=1000, - model="Dell XPS1319", - ) - assert flavors[0].score_machine(machine) > 0 - - -def test_disk_match_but_more_memory(flavors): - machine = Machine( - memory_mb=204800, - cpu="AMD EPYC 9254 245-Core Processor", - disk_gb=500, - model="Dell XPS1319", - ) - - assert flavors[0].score_machine(machine) > 0 - assert flavors[1].score_machine(machine) == 0 - assert flavors[2].score_machine(machine) == 0 - - -# Edge cases -def test_memory_slightly_less(flavors): - # Machine with slightly less memory than required by the smallest flavor - machine = Machine( - memory_mb=102300, - cpu="AMD EPYC 9254 245-Core Processor", - disk_gb=500, - model="Dell XPS1319", - ) - # Should not match because memory is slightly less - assert all(flavor.score_machine(machine) == 0 for flavor in flavors) - - -def test_disk_slightly_less(flavors): - # Machine with slightly less disk space than required by the smallest flavor - machine = Machine( - memory_mb=102400, - cpu="AMD EPYC 9254 245-Core Processor", - disk_gb=499, - model="Dell XPS1319", - ) - # Should not match because disk space is slightly less - assert all(flavor.score_machine(machine) == 0 for flavor in flavors) - - -def test_memory_exact_disk_slightly_more(flavors): - # Machine with exact memory but slightly more disk space than required - machine = Machine( - memory_mb=102400, - cpu="AMD EPYC 9254 245-Core Processor", - disk_gb=501, - model="Dell XPS1319", - ) - assert flavors[0].score_machine(machine) > 0 - assert flavors[1].score_machine(machine) == 0 - assert flavors[2].score_machine(machine) == 0 - - -def test_disk_exact_memory_slightly_more(flavors): - # Machine with exact disk space but slightly more memory than required - machine = Machine( - memory_mb=102500, - cpu="AMD EPYC 9254 245-Core Processor", - disk_gb=500, - model="Dell XPS1319", - ) - assert flavors[0].score_machine(machine) > 0 - assert flavors[1].score_machine(machine) == 0 - assert flavors[2].score_machine(machine) == 0 - - -def test_cpu_model_not_exact_but_memory_and_disk_match(flavors): - # Machine with exact memory and disk space but CPU model is close but not exact - machine = Machine( - memory_mb=102400, - cpu="AMD EPYC 9254 245-Core Processor v2", - disk_gb=500, - model="Dell XPS1319", - ) - # Should not match because CPU model is not exactly listed - assert all(flavor.score_machine(machine) == 0 for flavor in flavors) - - -def test_large_flavor_memory_slightly_less_disk_exact(flavors): - # Machine with slightly less memory than required for the medium - # flavor, exact disk space - machine = Machine( - memory_mb=204600, cpu="Intel 80386DX", disk_gb=1800, model="Dell XPS1319" - ) - # Should not match because memory is slightly less than required - assert all(flavor.score_machine(machine) == 0 for flavor in flavors) - - -def test_memory_gib(valid_yaml): - flv = FlavorSpec.from_yaml(valid_yaml) - assert flv.memory_mib == 7963648 - - -def test_baremetal_nova_resource_class(valid_yaml): - flv = FlavorSpec.from_yaml(valid_yaml) - assert ( - flv.baremetal_nova_resource_class - == "resources:CUSTOM_BAREMETAL_GP2_ULTRAMEDIUM" - ) diff --git a/python/understack-flavor-matcher/tests/test_machine.py b/python/understack-flavor-matcher/tests/test_machine.py index f9bba7af9..099a04828 100644 --- a/python/understack-flavor-matcher/tests/test_machine.py +++ b/python/understack-flavor-matcher/tests/test_machine.py @@ -3,17 +3,45 @@ def test_memory_gb_property(): # Test a machine with exactly 1 GB of memory - machine = Machine(memory_mb=1024, cpu="x86", disk_gb=50, model="SomeModel") + machine = Machine( + memory_mb=1024, + cpu="x86", + cpu_cores=4, + disk_gb=50, + manufacturer="Dell", + model="SomeModel", + ) assert machine.memory_gb == 1 # Test a machine with 2 GB of memory - machine = Machine(memory_mb=2048, cpu="x86", disk_gb=50, model="SomeModel") + machine = Machine( + memory_mb=2048, + cpu="x86", + cpu_cores=4, + disk_gb=50, + manufacturer="Dell", + model="SomeModel", + ) assert machine.memory_gb == 2 # Test a machine with non-exact GB memory (should floor the value) - machine = Machine(memory_mb=3072, cpu="x86", disk_gb=50, model="SomeModel") + machine = Machine( + memory_mb=3072, + cpu="x86", + cpu_cores=4, + disk_gb=50, + manufacturer="Dell", + model="SomeModel", + ) assert machine.memory_gb == 3 # Test a machine with less than 1 GB of memory - machine = Machine(memory_mb=512, cpu="x86", disk_gb=50, model="SomeModel") + machine = Machine( + memory_mb=512, + cpu="x86", + cpu_cores=4, + disk_gb=50, + manufacturer="Dell", + model="SomeModel", + ) assert machine.memory_gb == 0 diff --git a/python/understack-flavor-matcher/tests/test_matcher.py b/python/understack-flavor-matcher/tests/test_matcher.py index fd0189e7e..ae3e87428 100644 --- a/python/understack-flavor-matcher/tests/test_matcher.py +++ b/python/understack-flavor-matcher/tests/test_matcher.py @@ -1,80 +1,235 @@ import pytest -from flavor_matcher.flavor_spec import FlavorSpec +from flavor_matcher.device_type import CpuSpec +from flavor_matcher.device_type import DeviceType +from flavor_matcher.device_type import DriveSpec +from flavor_matcher.device_type import MemorySpec +from flavor_matcher.device_type import ResourceClass from flavor_matcher.machine import Machine from flavor_matcher.matcher import Matcher @pytest.fixture -def sample_flavors(): - return [ - FlavorSpec( - name="small", - manufacturer="Dell", - model="Fake Machine", - memory_gb=4, - cpu_cores=2, - cpu_model="x86", - drives=[20], - pci=[], - ), - FlavorSpec( - name="medium", - manufacturer="Dell", - model="Fake Machine", - memory_gb=8, - cpu_cores=4, - cpu_model="x86", - drives=[40], - pci=[], - ), - FlavorSpec( - name="large", - manufacturer="Dell", - model="Fake Machine", - memory_gb=16, - cpu_cores=8, - cpu_model="x86", - drives=[80], - pci=[], - ), - ] +def device_types(): + """Create sample device types for testing.""" + # Dell PowerEdge R7615 with multiple resource classes + dell_r7615 = DeviceType( + class_="server", + manufacturer="Dell", + model="PowerEdge R7615", + u_height=2, + is_full_depth=True, + resource_class=[ + ResourceClass( + name="m1.small", + cpu=CpuSpec(cores=16, model="AMD EPYC 9124"), + memory=MemorySpec(size=131072), + drives=[DriveSpec(size=480), DriveSpec(size=480)], + nic_count=2, + ), + ResourceClass( + name="m1.medium", + cpu=CpuSpec(cores=32, model="AMD EPYC 9334"), + memory=MemorySpec(size=262144), + drives=[DriveSpec(size=960), DriveSpec(size=960)], + nic_count=2, + ), + ResourceClass( + name="m1.large", + cpu=CpuSpec(cores=64, model="AMD EPYC 9554"), + memory=MemorySpec(size=524288), + drives=[DriveSpec(size=1920), DriveSpec(size=1920)], + nic_count=4, + ), + ], + ) + + return [dell_r7615] @pytest.fixture -def matcher(sample_flavors): - return Matcher(flavors=sample_flavors) +def matcher(device_types): + """Create a matcher with test data.""" + return Matcher(device_types=device_types) -@pytest.fixture -def machine(): - return Machine(memory_mb=8192, cpu="x86", disk_gb=50, model="Fake Machine") +def test_match_exact_machine(matcher): + """Test matching a machine that exactly matches m1.small resource class.""" + machine = Machine( + memory_mb=131072, # 128 GB + cpu="AMD EPYC 9124", + cpu_cores=16, + disk_gb=480, + manufacturer="Dell", + model="PowerEdge R7615", + ) + + result = matcher.match(machine) + assert result is not None + device_type, resource_class = result + assert resource_class.name == "m1.small" + assert device_type.manufacturer == "Dell" + assert device_type.model == "PowerEdge R7615" + + +def test_match_machine_small(matcher): + """Test that machine matches the m1.small resource class.""" + machine = Machine( + memory_mb=131072, # 128 GB + cpu="AMD EPYC 9124", + cpu_cores=16, + disk_gb=480, + manufacturer="Dell", + model="PowerEdge R7615", + ) + + result = matcher.match(machine) + assert result is not None + _, resource_class = result + assert resource_class.name == "m1.small" + + +def test_match_machine_verifies_specs(matcher): + """Test that machine matches based on hardware specs.""" + machine = Machine( + memory_mb=131072, # 128 GB + cpu="AMD EPYC 9124", + cpu_cores=16, + disk_gb=480, + manufacturer="Dell", + model="PowerEdge R7615", + ) + + result = matcher.match(machine) + assert result is not None + _, resource_class = result + assert resource_class.name == "m1.small" + assert resource_class.cpu.cores == 16 + assert resource_class.memory.size == 131072 + + +def test_match_medium_machine(matcher): + """Test matching a machine to m1.medium resource class.""" + machine = Machine( + memory_mb=262144, # 256 GB + cpu="AMD EPYC 9334", + cpu_cores=32, + disk_gb=960, + manufacturer="Dell", + model="PowerEdge R7615", + ) + + result = matcher.match(machine) + assert result is not None + _, resource_class = result + assert resource_class.name == "m1.medium" + + +def test_match_medium_machine_with_specs(matcher): + """Test matching a medium machine and verify its specs.""" + machine = Machine( + memory_mb=262144, # 256 GB + cpu="AMD EPYC 9334", + cpu_cores=32, + disk_gb=960, + manufacturer="Dell", + model="PowerEdge R7615", + ) + + result = matcher.match(machine) + assert result is not None + _, resource_class = result + assert resource_class.name == "m1.medium" + assert resource_class.cpu.cores == 32 + assert resource_class.memory.size == 262144 + + +def test_no_match_wrong_manufacturer(matcher): + """Test that wrong manufacturer doesn't match.""" + machine = Machine( + memory_mb=131072, # 128 GB + cpu="AMD EPYC 9124", + cpu_cores=16, + disk_gb=480, + manufacturer="HP", # Wrong manufacturer + model="PowerEdge R7615", + ) + + result = matcher.match(machine) + assert result is None + + +def test_no_match_wrong_model(matcher): + """Test that wrong model doesn't match.""" + machine = Machine( + memory_mb=131072, # 128 GB + cpu="AMD EPYC 9124", + cpu_cores=16, + disk_gb=480, + manufacturer="Dell", + model="PowerEdge R7525", # Wrong model + ) + + result = matcher.match(machine) + assert result is None + + +def test_no_match_wrong_cpu_model(matcher): + """Test that wrong CPU model doesn't match.""" + machine = Machine( + memory_mb=131072, # 128 GB + cpu="Intel Xeon", # Wrong CPU + cpu_cores=16, + disk_gb=480, + manufacturer="Dell", + model="PowerEdge R7615", + ) + + result = matcher.match(machine) + assert result is None + +def test_no_match_wrong_memory(matcher): + """Test that wrong memory size doesn't match.""" + machine = Machine( + memory_mb=65536, # 64 GB - wrong size + cpu="AMD EPYC 9124", + cpu_cores=16, + disk_gb=480, + manufacturer="Dell", + model="PowerEdge R7615", + ) -def test_match(matcher, machine): - # This machine should match the small and medium flavors - results = matcher.match(machine) - assert len(results) == 2 - assert results[0].name == "small" - assert results[1].name == "medium" + result = matcher.match(machine) + assert result is None -def test_match_no_flavor(matcher): - # A machine that does not meet any flavor specs - machine = Machine(memory_mb=2048, cpu="x86", disk_gb=10, model="SomeModel") - results = matcher.match(machine) - assert len(results) == 0 +def test_no_match_insufficient_disk(matcher): + """Test that insufficient disk space doesn't match.""" + machine = Machine( + memory_mb=131072, # 128 GB + cpu="AMD EPYC 9124", + cpu_cores=16, + disk_gb=200, # Too small + manufacturer="Dell", + model="PowerEdge R7615", + ) + result = matcher.match(machine) + assert result is None -def test_pick_best_flavor2(matcher, machine): - # This machine should pick the medium flavor as the best - best_flavor = matcher.pick_best_flavor(machine) - assert best_flavor is not None - assert best_flavor.name == "medium" +def test_match_empty_device_types(): + """Test matcher with no device types.""" + matcher = Matcher(device_types=[]) + machine = Machine( + memory_mb=131072, + cpu="AMD EPYC 9124", + cpu_cores=16, + disk_gb=480, + manufacturer="Dell", + model="PowerEdge R7615", + ) -def test_pick_best_flavor_no_match(matcher): - # A machine that does not meet any flavor specs - machine = Machine(memory_mb=1024, cpu="ARM", disk_gb=10, model="SomeModel") - best_flavor = matcher.pick_best_flavor(machine) - assert best_flavor is None + result = matcher.match(machine) + assert result is None diff --git a/python/understack-workflows/pyproject.toml b/python/understack-workflows/pyproject.toml index 07b6e6c93..38cdf3968 100644 --- a/python/understack-workflows/pyproject.toml +++ b/python/understack-workflows/pyproject.toml @@ -25,7 +25,6 @@ dependencies = [ "python-ironicclient>=5,<6", "sushy>=5.3.0,<6", "kubernetes==33.1.0", - "understack-flavor-matcher", "netapp-ontap>=9.17.1.0", ] @@ -55,9 +54,6 @@ test = [ [tool.uv] default-groups = ["test"] -[tool.uv.sources] -understack-flavor-matcher = { path = "../understack-flavor-matcher" } - [tool.hatch.build.targets.sdist] include = ["understack_workflows"] diff --git a/python/understack-workflows/understack_workflows/flavor_detect.py b/python/understack-workflows/understack_workflows/flavor_detect.py deleted file mode 100644 index b12397be8..000000000 --- a/python/understack-workflows/understack_workflows/flavor_detect.py +++ /dev/null @@ -1,35 +0,0 @@ -import os - -from flavor_matcher.machine import Machine -from flavor_matcher.matcher import FlavorSpec -from flavor_matcher.matcher import Matcher - -from understack_workflows import bmc_disk -from understack_workflows.bmc import Bmc -from understack_workflows.bmc_chassis_info import ChassisInfo -from understack_workflows.helpers import setup_logger - -logger = setup_logger(__name__) -FLAVORS_DIR = os.getenv("FLAVORS_DIR", "/etc/understack_flavors/") -FLAVORS = FlavorSpec.from_directory(FLAVORS_DIR) -logger.info("Loaded %d flavor specifications.", len(FLAVORS)) - - -def guess_machine_flavor(device_info: ChassisInfo, bmc: Bmc) -> str: - memory_mb = (device_info.memory_gib * 1024**3) // 10**6 - - machine = Machine( - memory_mb=memory_mb, - cpu=device_info.cpu, - disk_gb=bmc_disk.smallest_disk_size(bmc), - model=device_info.model_number, - ) - - flavor_name = Matcher(FLAVORS).pick_best_flavor(machine) - if not flavor_name: - raise Exception( - f"Machine: {machine} could not be classified into any flavor {FLAVORS=}" - ) - logger.info("Device has been classified as flavor: %s", flavor_name.name) - - return flavor_name.name diff --git a/python/understack-workflows/understack_workflows/ironic_node.py b/python/understack-workflows/understack_workflows/ironic_node.py index 070def6e7..577e0cf2c 100644 --- a/python/understack-workflows/understack_workflows/ironic_node.py +++ b/python/understack-workflows/understack_workflows/ironic_node.py @@ -1,5 +1,6 @@ +from dataclasses import dataclass + import ironicclient.common.apiclient.exceptions -from flavor_matcher.flavor_spec import dataclass from ironicclient.common.utils import args_array_to_patch from understack_workflows.bmc import Bmc @@ -17,7 +18,6 @@ class NodeMetadata: uuid: str hostname: str manufacturer: str - resource_class: str @property def driver(self): @@ -58,8 +58,8 @@ def update_ironic_node(client, node_meta, bmc): "driver_info/redfish_verify_ca=false", f"driver_info/redfish_username={bmc.username}", f"driver_info/redfish_password={bmc.password}", - f"resource_class={node_meta.resource_class}", "boot_interface=http-ipxe", + "inspect_interface=agent", ] patches = args_array_to_patch("add", updates) @@ -85,7 +85,7 @@ def create_ironic_node( "redfish_username": bmc.username, "redfish_password": bmc.password, }, - "resource_class": node_meta.resource_class, "boot_interface": "http-ipxe", + "inspect_interface": "agent", } ) diff --git a/python/understack-workflows/understack_workflows/main/enroll_server.py b/python/understack-workflows/understack_workflows/main/enroll_server.py index a5d1c8c30..11e7cb6d6 100644 --- a/python/understack-workflows/understack_workflows/main/enroll_server.py +++ b/python/understack-workflows/understack_workflows/main/enroll_server.py @@ -16,7 +16,6 @@ from understack_workflows.bmc_network_config import bmc_set_permanent_ip_addr from understack_workflows.bmc_settings import update_dell_drac_settings from understack_workflows.discover import discover_chassis_info -from understack_workflows.flavor_detect import guess_machine_flavor from understack_workflows.helpers import credential from understack_workflows.helpers import parser_nautobot_args from understack_workflows.helpers import setup_logger @@ -137,15 +136,11 @@ def enroll_server(bmc: Bmc, nautobot, old_password: str | None) -> NautobotDevic # any pending BIOS jobs, so do BIOS settings after the DRAC settings. update_dell_bios_settings(bmc, pxe_interface=pxe_interface) - flavor = guess_machine_flavor(device_info, bmc) - resource_class = f"baremetal.{flavor}" - _ironic_provision_state = ironic_node.create_or_update( ironic_node.NodeMetadata( uuid=nb_device.id, hostname=nb_device.name, manufacturer=device_info.manufacturer, - resource_class=resource_class, ), bmc, ) diff --git a/python/understack-workflows/uv.lock b/python/understack-workflows/uv.lock index bff373649..8b8f3b49a 100644 --- a/python/understack-workflows/uv.lock +++ b/python/understack-workflows/uv.lock @@ -990,24 +990,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a6/ab/7e5f53c3b9d14972843a647d8d7a853969a58aecc7559cb3267302c94774/tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd", size = 346586, upload-time = "2024-09-23T18:56:45.478Z" }, ] -[[package]] -name = "understack-flavor-matcher" -version = "0.0.0" -source = { directory = "../understack-flavor-matcher" } -dependencies = [ - { name = "pyyaml" }, -] - -[package.metadata] -requires-dist = [{ name = "pyyaml", specifier = "~=6.0" }] - -[package.metadata.requires-dev] -test = [ - { name = "pytest", specifier = ">=8.3.2,<9" }, - { name = "pytest-cov", specifier = ">=7,<8" }, - { name = "pytest-github-actions-annotate-failures" }, -] - [[package]] name = "understack-workflows" version = "0.0.0" @@ -1019,7 +1001,6 @@ dependencies = [ { name = "pynautobot" }, { name = "python-ironicclient" }, { name = "sushy" }, - { name = "understack-flavor-matcher" }, ] [package.dev-dependencies] @@ -1040,7 +1021,6 @@ requires-dist = [ { name = "pynautobot", specifier = ">=2.2.1,<3" }, { name = "python-ironicclient", specifier = ">=5,<6" }, { name = "sushy", specifier = ">=5.3.0,<6" }, - { name = "understack-flavor-matcher", directory = "../understack-flavor-matcher" }, ] [package.metadata.requires-dev] diff --git a/workflows/argo-events/kustomization.yaml b/workflows/argo-events/kustomization.yaml index 9adb42311..91f8f204d 100644 --- a/workflows/argo-events/kustomization.yaml +++ b/workflows/argo-events/kustomization.yaml @@ -6,6 +6,7 @@ resources: - eventbus/poddisruptionbudget-eventbus-default-pdb.yaml - secrets/openstack-svc-acct.yaml - secrets/operate-workflow-sa.token.yaml + - secrets/baremetal-manage.yaml - eventsources/nautobot-webhook.yaml - serviceaccounts/serviceaccount-sensor-submit-workflow.yaml - serviceaccounts/openstack-sensor-submit-workflow.yaml diff --git a/workflows/argo-events/secrets/baremetal-manage.yaml b/workflows/argo-events/secrets/baremetal-manage.yaml new file mode 100644 index 000000000..f09adfb3b --- /dev/null +++ b/workflows/argo-events/secrets/baremetal-manage.yaml @@ -0,0 +1,16 @@ +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: baremetal-manage +spec: + refreshInterval: 1h + secretStoreRef: + kind: ClusterSecretStore + name: openstack + target: + name: baremetal-manage + data: + - secretKey: clouds.yaml + remoteRef: + key: baremetal-manage + property: clouds.yaml diff --git a/workflows/argo-events/workflowtemplates/enroll-server.yaml b/workflows/argo-events/workflowtemplates/enroll-server.yaml index 912f18f03..6cc145ffb 100644 --- a/workflows/argo-events/workflowtemplates/enroll-server.yaml +++ b/workflows/argo-events/workflowtemplates/enroll-server.yaml @@ -8,6 +8,7 @@ metadata: Defined in `workflows/argo-events/workflowtemplates/enroll-server.yaml` kind: WorkflowTemplate spec: + serviceAccountName: workflow entrypoint: main arguments: parameters: @@ -58,6 +59,15 @@ spec: parameters: - name: device_id value: "{{steps.enroll-server.outputs.result}}" + - - name: inspect-server + template: openstack-wait-cmd + arguments: + parameters: + - name: operation + value: "inspect" + - name: device_id + value: "{{steps.enroll-server.outputs.result}}" + when: "{{steps.server-manage-state.outputs.result}} == manageable" - - name: avail-server template: openstack-wait-cmd arguments: @@ -77,7 +87,7 @@ spec: - "{{workflow.parameters.ip_address}}" volumeMounts: - mountPath: /etc/openstack - name: openstack-svc-acct + name: baremetal-manage readOnly: true - mountPath: /etc/nb-token/ name: nb-token @@ -85,9 +95,6 @@ spec: - mountPath: /etc/bmc_master/ name: bmc-master readOnly: true - - mountPath: /etc/understack_flavors/ - name: understack-flavors - readOnly: true env: - name: WF_NS value: "{{workflow.namespace}}" @@ -95,12 +102,6 @@ spec: value: "{{workflow.name}}" - name: WF_UID value: "{{workflow.uid}}" - - name: FLAVORS_DIR - valueFrom: - configMapKeyRef: - name: understack-flavors - key: FLAVORS_DIR - optional: true volumes: - name: bmc-master secret: @@ -108,13 +109,12 @@ spec: - name: nb-token secret: secretName: nautobot-token - - name: openstack-svc-acct + - name: baremetal-manage secret: - secretName: openstack-svc-acct - - name: understack-flavors - persistentVolumeClaim: - claimName: understack-flavors - readOnly: true + secretName: baremetal-manage + items: + - key: clouds.yaml + path: clouds.yaml - name: openstack-wait-cmd inputs: parameters: @@ -136,12 +136,14 @@ spec: value: understack volumeMounts: - mountPath: /etc/openstack - name: openstack-svc-acct - readOnly: true + name: baremetal-manage volumes: - - name: openstack-svc-acct + - name: baremetal-manage secret: - secretName: openstack-svc-acct + secretName: baremetal-manage + items: + - key: clouds.yaml + path: clouds.yaml - name: openstack-set-baremetal-node-raid-config inputs: parameters: @@ -190,12 +192,14 @@ spec: value: understack volumeMounts: - mountPath: /etc/openstack - name: openstack-svc-acct - readOnly: true + name: baremetal-manage volumes: - - name: openstack-svc-acct + - name: baremetal-manage secret: - secretName: openstack-svc-acct + secretName: baremetal-manage + items: + - key: clouds.yaml + path: clouds.yaml - name: openstack-state-cmd inputs: parameters: @@ -218,9 +222,11 @@ spec: value: understack volumeMounts: - mountPath: /etc/openstack - name: openstack-svc-acct - readOnly: true + name: baremetal-manage volumes: - - name: openstack-svc-acct + - name: baremetal-manage secret: - secretName: openstack-svc-acct + secretName: baremetal-manage + items: + - key: clouds.yaml + path: clouds.yaml