Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 58 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,61 @@ GET /api/resources/get-dependent-workloads?id=x86-ubuntu-18.04-img
]
```

### 5. List All Resources by gem5 Version

**Endpoint**: `GET /api/resources/list-all-resources`

Retrieve all resources that are compatible with a specific gem5 version. The endpoint performs prefix matching, so a version like `25.0.0.1` will match resources that have `25.0` or `25.0.0` in their `gem5_versions` field.

**Parameters**:

- `gem5-version` (required): The gem5 version to match against (e.g., "23.0", "25.0.0.1")
- `latest-version` (optional): If set to `"true"`, returns only the latest compatible version of each resource instead of all compatible versions. Default is `"false"` (returns all versions).

**Examples**:

```bash
# Get all resources compatible with gem5 version 23.0
GET /api/resources/list-all-resources?gem5-version=23.0

# Get all resources compatible with gem5 version 25.0.0.1
# This will match resources with gem5_versions containing "25", "25.0", "25.0.0", or "25.0.0.1"
GET /api/resources/list-all-resources?gem5-version=25.0.0.1

# Get only the latest version of each resource compatible with gem5 version 23.0
GET /api/resources/list-all-resources?gem5-version=23.0&latest-version=true
```

**Response Format**:

```json
[
{
"id": "riscv-ubuntu-20.04-boot",
"resource_version": "3.0.0",
"category": "workload",
"architecture": "RISCV",
"gem5_versions": ["23.0", "22.1", "22.0"],
// ... other resource fields
},
{
"id": "arm-hello64-static",
"resource_version": "1.0.0",
"category": "binary",
"architecture": "ARM",
"gem5_versions": ["23.0", "22.0"],
// ... other resource fields
}
]
```

**Notes**:

- Uses prefix matching: a parameter like `25.0.0.1` matches resources with `25.0`, `25.0.0`, or `25.0.0.1` in their `gem5_versions` field
- Version parameter must have at least `major` version (e.g., `23.0`), gem5 major versions follow the format `release-year.release_num` (eg, "25.1" would be the second release of year 2025).
- Returns all versions of resources that match
- Returns an empty list if no resources match the specified gem5 version

## Development Setup

### Prerequisites
Expand Down Expand Up @@ -216,7 +271,8 @@ gem5-resources-api/
│ ├── get_resources_by_batch.py
│ ├── search_resources.py
│ ├── get_filters.py
│ └── get_dependent_workloads.py
│ ├── get_dependent_workloads.py
│ └── list_all_resources.py
├── shared/ # Shared utilities
│ ├── database.py # Database connection & config
│ └── utils.py # Common utilities & validation
Expand Down Expand Up @@ -244,6 +300,7 @@ Functions:
search_resources: [GET] http://localhost:7071/api/resources/search
get_filters: [GET] http://localhost:7071/api/resources/filters
get_dependent_workloads: [GET] http://localhost:7071/api/resources/get-dependent-workloads
list_all_resources: [GET] http://localhost:7071/api/resources/list-all-resources
```

### Testing
Expand Down
2 changes: 2 additions & 0 deletions function_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
get_dependent_workloads,
get_filters,
get_resources_by_batch,
list_all_resources,
search_resources,
)
from shared.azure_search_client import get_search_client
Expand All @@ -27,3 +28,4 @@
search_resources.register_function(app, search_client)
get_filters.register_function(app, collection, db["filter_values"])
get_dependent_workloads.register_function(app, collection)
list_all_resources.register_function(app, collection)
96 changes: 96 additions & 0 deletions functions/list_all_resources.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Copyright (c) 2025 The Regents of the University of California
# SPDX-License-Identifier: BSD-3-Clause

import json
import logging

import azure.functions as func

from shared.database import RESOURCE_FIELDS
from shared.utils import (
create_error_response,
get_latest_versions,
sanitize_version,
)


def register_function(app, collection):
"""Register the function with the app."""

@app.function_name(name="list_all_resources")
@app.route(
route="resources/list-all-resources",
auth_level=func.AuthLevel.ANONYMOUS,
)
def list_all_resources(req: func.HttpRequest) -> func.HttpResponse:
"""
Get all resources where a gem5 version in the resource is a prefix
of the specified version parameter.

Route: /resources/list-all-resources

Query Parameters:
- gem5-version: Required, the gem5 version to match against
(e.g., "25.0.0.1" will match resources with "25.0")
- latest-version: Optional, if set to "true", returns only the latest
compatible version of each resource instead of all
compatible versions. Default is false (return all).
"""
logging.info(
"Processing request to list all resources by gem5 version"
)
try:
gem5_version = req.params.get("gem5-version")
latest_version_only = (
req.params.get("latest-version", "false").lower() == "true"
)

if not gem5_version:
return create_error_response(
400, "'gem5-version' parameter is required"
)

gem5_version = sanitize_version(gem5_version)

if not gem5_version:
return create_error_response(
400, "Invalid 'gem5-version' parameter format"
)

# Build a list of all possible prefixes from the version
# Starting from major.minor since gem5 versions always have at
# least one dot (e.g., "25.0", "23.1")
# e.g., "25.0.0.1" -> ["25.0", "25.0.0", "25.0.0.1"]
version_parts = gem5_version.split(".")
prefixes = []
for i in range(2, len(version_parts) + 1):
prefixes.append(".".join(version_parts[:i]))

# If only one part provided (e.g., "25"), return error
if not prefixes:
return create_error_response(
400,
"Invalid 'gem5-version' parameter: must have at least "
"major version format (e.g., '23.0', '25.1')",
)

# Query for resources where any gem5_version matches one of
# the prefixes
query = {"gem5_versions": {"$in": prefixes}}

resources = list(collection.find(query, RESOURCE_FIELDS))

# If latest-version is set, return only the latest version of
# each resource
if latest_version_only:
resources = get_latest_versions(resources)

return func.HttpResponse(
body=json.dumps(resources),
status_code=200,
headers={"Content-Type": "application/json"},
)

except Exception as e:
logging.error(f"Error listing resources by gem5 version: {str(e)}")
return create_error_response(500, "Internal server error")
34 changes: 34 additions & 0 deletions shared/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,37 @@ def create_json_response(data, status_code=200):
headers={"Content-Type": "application/json"},
status_code=status_code,
)


def parse_version(version_str):
"""Parse a version string into a tuple of integers for comparison."""
try:
return tuple(int(x) for x in version_str.split("."))
except (ValueError, AttributeError):
return (0,)


def get_latest_versions(resources):
"""
Filter resources to return only the latest version of each resource.
Groups resources by 'id' and returns the one with the highest
'resource_version'.
"""
latest_by_id = {}
for resource in resources:
resource_id = resource.get("id")
if not resource_id:
continue

current_version = parse_version(resource.get("resource_version", "0"))

if resource_id not in latest_by_id:
latest_by_id[resource_id] = resource
else:
existing_version = parse_version(
latest_by_id[resource_id].get("resource_version", "0")
)
if current_version > existing_version:
latest_by_id[resource_id] = resource

return list(latest_by_id.values())
Loading