One the joys (or miseries) of working on OpenStack client tooling is that you get exposed to all the nuances of the OpenStack API tooling. The microservice-like architecture, with projects being managed by often wholly separate teams, means there’s precious little consistency across APIs, beyond that enforced by the long-dormant API SIG. I’ve touched on this before, covering things like the disparity in API versioning, and its been particularly notable during work on the [OpenAPI effort]. OpenStack is also pretty old (at least in software terms) and most service APIs come with at least a decade of baggage: there is an abundance of APIs that still exist in services but are no longer relevant or useful in modern deployments.
The Glance Tasks API is an example of one such API. It’s a positively ancient API, dating back to the Havana release (2013), and has long-since been made redundant by the image import API introduced in the Mitaka release (2016). Nonetheless, python-openstackclient (OSC) and openstacksdk (SDK) aim to support all OpenStack APIs, and the tasks API, while deprecated, is still present in Glance in the Flamingo release, meaning OSC and SDK should support them.
As always, when adding support for a new API to SDK and OSC, its important that you figure out how that API actually works, ideally by playing around with the API using a local DevStack: the OpenStack API docs are good, but they’re not flawless, and if I had a euro for every time the API docs had said one thing only for the API to behave a different way, I wouldn’t be stuck doing my own painting. Anywho, adding support for the tasks API is no different and I conducted a few tests to prove things out while reviewing some recent patches against both SDK and OSC. Below are my test notes, which I’m sharing in case anyone else ends up having to do something similar.
Notes
We start by creating a task. Because the tasks API is deprecated and replaced by the image import process, OSC has
indicated that it won’t add support for task creation. Thus, for this one we need to use either glanceclient or curl
.
I used the former, creating a sample task for an existing image. I provided syntactically valid-but-nonsense input since
I only wanted to test the behavior of the API; I didn’t actually need the task to complete:
❯ glance task-create --type "api_image_import" --input '{
"image_id": "8bd3bebc-8e1b-40fe-a292-1a63a53dcfd9",
"import_req": {
"method": {
"name": "copy-image"
},
"all_stores": true,
"all_stores_must_succeed": false
},
"backend": [
"fast",
"cheap",
"slow",
"reliable",
"common"
]
}'
Once created, I was able to view this task in a few ways. Firstly, by listing all tasks. This is supported by OSC, so I used that:
❯ openstack image task list
+--------------------------------------+------------------+------------+----------------------------------+
| ID | Type | Status | Owner |
+--------------------------------------+------------------+------------+----------------------------------+
| 546641fe-6a35-4b0e-90d2-b45367fc94b2 | api_image_import | processing | d8dea803f1fe44e2bdf9d9d017750b4e |
+--------------------------------------+------------------+------------+----------------------------------+
or, via curl
:
❯ token=$(openstack token issue -f value -c id)
❯ curl -s -X GET "http://10.0.111.148/image/v2/tasks" \
-H "Accept: application/json"-H "Content-Type: application/json" -H "X-Auth-Token: ${token}" | jq
{
"tasks": [
{
"id": "546641fe-6a35-4b0e-90d2-b45367fc94b2",
"type": "api_image_import",
"status": "processing",
"owner": "d8dea803f1fe44e2bdf9d9d017750b4e",
"created_at": "2025-08-15T11:32:10Z",
"updated_at": "2025-08-15T11:32:10Z",
"self": "/v2/tasks/546641fe-6a35-4b0e-90d2-b45367fc94b2",
"schema": "/v2/schemas/task"
}
],
"first": "/v2/tasks",
"schema": "/v2/schemas/tasks"
}
I could also inspect the individual task:
❯ openstack image task show 546641fe-6a35-4b0e-90d2-b45367fc94b2
+------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Field | Value |
+------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| created_at | 2025-08-15T11:32:10Z |
| expires_at | None |
| id | 546641fe-6a35-4b0e-90d2-b45367fc94b2 |
| input | {'image_id': '8bd3bebc-8e1b-40fe-a292-1a63a53dcfd9', 'import_req': {'method': {'name': 'copy-image'}, 'all_stores': True, 'all_stores_must_succeed': False}, 'backend': ['fast', 'cheap', 'slow', 'reliable', 'common']} |
| message | |
| owner_id | d8dea803f1fe44e2bdf9d9d017750b4e |
| properties | |
| result | None |
| status | processing |
| type | api_image_import |
| updated_at | 2025-08-15T11:32:10Z |
+------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
again, via curl
:
❯ curl -s -X GET "http://10.0.111.148/image/v2/tasks/546641fe-6a35-4b0e-90d2-b45367fc94b2"
-H "Accept: application/json"-H "Content-Type: application/json" -H "X-Auth-Token: ${token}" | jq
{
"id": "546641fe-6a35-4b0e-90d2-b45367fc94b2",
"input": {
"image_id": "8bd3bebc-8e1b-40fe-a292-1a63a53dcfd9",
"import_req": {
"method": {
"name": "copy-image"
},
"all_stores": true,
"all_stores_must_succeed": false
},
"backend": [
"fast",
"cheap",
"slow",
"reliable",
"common"
]
},
"type": "api_image_import",
"status": "processing",
"owner": "d8dea803f1fe44e2bdf9d9d017750b4e",
"message": "",
"result": null,
"created_at": "2025-08-15T11:32:10Z",
"updated_at": "2025-08-15T11:32:10Z",
"self": "/v2/tasks/546641fe-6a35-4b0e-90d2-b45367fc94b2",
"schema": "/v2/schemas/task",
"image_id": "8bd3bebc-8e1b-40fe-a292-1a63a53dcfd9",
"request_id": "req-1918a068-0ce3-4eff-81f5-625e2d0037ae",
"user_id": "105a84e11f0a421ea814833a5d23f37b"
}
Finally, I could list tasks associated with the image. For reasons unbeknownst to me, glance opted to implement this
via a separate, nested API (/images/{imageID}/tasks
) rather than a new filter on the existing API
(/tasks?image_id={imageID}
). This is supposedly supported by glanceclient, but attempting to use this command with the
latest glanceclient version yielded the following error:
❯ glance image-tasks 8bd3bebc-8e1b-40fe-a292-1a63a53dcfd9
Server does not support image tasks API (v2.12)
This seemed odd since the API should be supported. Sure enough, when I tried with curl
, things worked as expected:
❯ curl -s -X GET "http://10.0.111.148/image/v2/tasks/546641fe-6a35-4b0e-90d2-b45367fc94b2" \
-H "Accept: application/json"-H "Content-Type: application/json" -H "X-Auth-Token: ${token}" | jq
{
"id": "546641fe-6a35-4b0e-90d2-b45367fc94b2",
"input": {
"image_id": "8bd3bebc-8e1b-40fe-a292-1a63a53dcfd9",
"import_req": {
"method": {
"name": "copy-image"
},
"all_stores": true,
"all_stores_must_succeed": false
},
"backend": [
"fast",
"cheap",
"slow",
"reliable",
"common"
]
},
"type": "api_image_import",
"status": "processing",
"owner": "d8dea803f1fe44e2bdf9d9d017750b4e",
"message": "",
"result": null,
"created_at": "2025-08-15T11:32:10Z",
"updated_at": "2025-08-15T11:32:10Z",
"self": "/v2/tasks/546641fe-6a35-4b0e-90d2-b45367fc94b2",
"schema": "/v2/schemas/task",
"image_id": "8bd3bebc-8e1b-40fe-a292-1a63a53dcfd9",
"request_id": "req-1918a068-0ce3-4eff-81f5-625e2d0037ae",
"user_id": "105a84e11f0a421ea814833a5d23f37b"
}
Clearly a bug report is needed here.
Finally, task deletion. There isn’t a task deletion API. Rather, tasks have an expiry date, after which glance will reap
the task. You can likely force this with glance-manage
but I didn’t care enough to do so.