api: Add response body schemas for images APIs
This is mostly uneventful save for us needing to fix our API ref, which indicated that the 'OS-EXT-IMG-SIZE:size' field shown in the 'show' and 'detail' views was a string rather than an int. You can confirm this is *not* the case like so: >>> import openstack >>> conn = openstack.connect() >>> conn.conn.compute.get('https://example.com/compute/v2.1/images/detail').json() (obviously replace 'https://example.com/' with a compute API host) Change-Id: Ia318478dfdb50f8d57a74958b3555f6ad97351ec Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"image": {
|
"image": {
|
||||||
"OS-DCF:diskConfig": "AUTO",
|
"OS-DCF:diskConfig": "AUTO",
|
||||||
"OS-EXT-IMG-SIZE:size": "74185822",
|
"OS-EXT-IMG-SIZE:size": 74185822,
|
||||||
"created": "2011-01-01T01:02:03Z",
|
"created": "2011-01-01T01:02:03Z",
|
||||||
"id": "70a599e0-31e7-49b7-b260-868f441e862b",
|
"id": "70a599e0-31e7-49b7-b260-868f441e862b",
|
||||||
"links": [
|
"links": [
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"images": [
|
"images": [
|
||||||
{
|
{
|
||||||
"OS-EXT-IMG-SIZE:size": "25165824",
|
"OS-EXT-IMG-SIZE:size": 25165824,
|
||||||
"created": "2011-01-01T01:02:03Z",
|
"created": "2011-01-01T01:02:03Z",
|
||||||
"id": "155d900f-4e14-4e4c-a73d-069cbf4541e6",
|
"id": "155d900f-4e14-4e4c-a73d-069cbf4541e6",
|
||||||
"links": [
|
"links": [
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
"updated": "2011-01-01T01:02:03Z"
|
"updated": "2011-01-01T01:02:03Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"OS-EXT-IMG-SIZE:size": "58145823",
|
"OS-EXT-IMG-SIZE:size": 58145823,
|
||||||
"created": "2011-01-01T01:02:03Z",
|
"created": "2011-01-01T01:02:03Z",
|
||||||
"id": "a2459075-d96c-40d5-893e-577ff92e721c",
|
"id": "a2459075-d96c-40d5-893e-577ff92e721c",
|
||||||
"links": [
|
"links": [
|
||||||
@@ -62,7 +62,7 @@
|
|||||||
"updated": "2011-01-01T01:02:03Z"
|
"updated": "2011-01-01T01:02:03Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"OS-EXT-IMG-SIZE:size": "83594576",
|
"OS-EXT-IMG-SIZE:size": 83594576,
|
||||||
"created": "2011-01-01T01:02:03Z",
|
"created": "2011-01-01T01:02:03Z",
|
||||||
"id": "76fa36fc-c930-4bf3-8c8a-ea2a2420deb6",
|
"id": "76fa36fc-c930-4bf3-8c8a-ea2a2420deb6",
|
||||||
"links": [
|
"links": [
|
||||||
@@ -93,7 +93,7 @@
|
|||||||
"updated": "2011-01-01T01:02:03Z"
|
"updated": "2011-01-01T01:02:03Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"OS-EXT-IMG-SIZE:size": "84035174",
|
"OS-EXT-IMG-SIZE:size": 84035174,
|
||||||
"created": "2011-01-01T01:02:03Z",
|
"created": "2011-01-01T01:02:03Z",
|
||||||
"id": "cedef40a-ed67-4d10-800e-17455edce175",
|
"id": "cedef40a-ed67-4d10-800e-17455edce175",
|
||||||
"links": [
|
"links": [
|
||||||
@@ -123,7 +123,7 @@
|
|||||||
"updated": "2011-01-01T01:02:03Z"
|
"updated": "2011-01-01T01:02:03Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"OS-EXT-IMG-SIZE:size": "26360814",
|
"OS-EXT-IMG-SIZE:size": 26360814,
|
||||||
"created": "2011-01-01T01:02:03Z",
|
"created": "2011-01-01T01:02:03Z",
|
||||||
"id": "c905cedb-7281-47e4-8a62-f26bc5fc4c77",
|
"id": "c905cedb-7281-47e4-8a62-f26bc5fc4c77",
|
||||||
"links": [
|
"links": [
|
||||||
@@ -154,7 +154,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"OS-DCF:diskConfig": "MANUAL",
|
"OS-DCF:diskConfig": "MANUAL",
|
||||||
"OS-EXT-IMG-SIZE:size": "49163826",
|
"OS-EXT-IMG-SIZE:size": 49163826,
|
||||||
"created": "2011-01-01T01:02:03Z",
|
"created": "2011-01-01T01:02:03Z",
|
||||||
"id": "a440c04b-79fa-479c-bed1-0b816eaec379",
|
"id": "a440c04b-79fa-479c-bed1-0b816eaec379",
|
||||||
"links": [
|
"links": [
|
||||||
@@ -187,7 +187,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"OS-DCF:diskConfig": "AUTO",
|
"OS-DCF:diskConfig": "AUTO",
|
||||||
"OS-EXT-IMG-SIZE:size": "74185822",
|
"OS-EXT-IMG-SIZE:size": 74185822,
|
||||||
"created": "2011-01-01T01:02:03Z",
|
"created": "2011-01-01T01:02:03Z",
|
||||||
"id": "70a599e0-31e7-49b7-b260-868f441e862b",
|
"id": "70a599e0-31e7-49b7-b260-868f441e862b",
|
||||||
"links": [
|
"links": [
|
||||||
@@ -219,7 +219,7 @@
|
|||||||
"updated": "2011-01-01T01:02:03Z"
|
"updated": "2011-01-01T01:02:03Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"OS-EXT-IMG-SIZE:size": "25165824",
|
"OS-EXT-IMG-SIZE:size": 25165824,
|
||||||
"created": "2011-01-01T01:02:03Z",
|
"created": "2011-01-01T01:02:03Z",
|
||||||
"id": "95fad737-9325-4855-b37e-20a62268ec88",
|
"id": "95fad737-9325-4855-b37e-20a62268ec88",
|
||||||
"links": [
|
"links": [
|
||||||
@@ -248,7 +248,7 @@
|
|||||||
"updated": "2011-01-01T01:02:03Z"
|
"updated": "2011-01-01T01:02:03Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"OS-EXT-IMG-SIZE:size": "25165824",
|
"OS-EXT-IMG-SIZE:size": 25165824,
|
||||||
"created": "2011-01-01T01:02:03Z",
|
"created": "2011-01-01T01:02:03Z",
|
||||||
"id": "535426d4-5d75-44f4-9591-a2123d23c33f",
|
"id": "535426d4-5d75-44f4-9591-a2123d23c33f",
|
||||||
"links": [
|
"links": [
|
||||||
@@ -277,7 +277,7 @@
|
|||||||
"updated": "2011-01-01T01:02:03Z"
|
"updated": "2011-01-01T01:02:03Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"OS-EXT-IMG-SIZE:size": "25165824",
|
"OS-EXT-IMG-SIZE:size": 25165824,
|
||||||
"created": "2011-01-01T01:02:03Z",
|
"created": "2011-01-01T01:02:03Z",
|
||||||
"id": "5f7d4f5b-3781-4a4e-9046-a2a800e807e5",
|
"id": "5f7d4f5b-3781-4a4e-9046-a2a800e807e5",
|
||||||
"links": [
|
"links": [
|
||||||
@@ -307,7 +307,7 @@
|
|||||||
"updated": "2011-01-01T01:02:03Z"
|
"updated": "2011-01-01T01:02:03Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"OS-EXT-IMG-SIZE:size": "25165824",
|
"OS-EXT-IMG-SIZE:size": 25165824,
|
||||||
"created": "2011-01-01T01:02:03Z",
|
"created": "2011-01-01T01:02:03Z",
|
||||||
"id": "261b52ed-f693-4147-8f3b-d25df5efd968",
|
"id": "261b52ed-f693-4147-8f3b-d25df5efd968",
|
||||||
"links": [
|
"links": [
|
||||||
@@ -337,4 +337,4 @@
|
|||||||
"updated": "2011-01-01T01:02:03Z"
|
"updated": "2011-01-01T01:02:03Z"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@@ -38,20 +38,21 @@ SUPPORTED_FILTERS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@validation.validated
|
||||||
class ImagesController(wsgi.Controller):
|
class ImagesController(wsgi.Controller):
|
||||||
"""Base controller for retrieving/displaying images."""
|
"""Base controller for retrieving/displaying images."""
|
||||||
|
|
||||||
_view_builder_class = views_images.ViewBuilder
|
_view_builder_class = views_images.ViewBuilder
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(ImagesController, self).__init__()
|
super().__init__()
|
||||||
self._image_api = glance.API()
|
self._image_api = glance.API()
|
||||||
|
|
||||||
def _get_filters(self, req):
|
def _get_filters(self, req):
|
||||||
"""Return a dictionary of query param filters from the request.
|
"""Return a dictionary of query param filters from the request.
|
||||||
|
|
||||||
:param req: the Request object coming from the wsgi layer
|
:param req: the Request object coming from the wsgi layer
|
||||||
:retval a dict of key/value filters
|
:returns: a dict of key/value filters
|
||||||
"""
|
"""
|
||||||
filters = {}
|
filters = {}
|
||||||
for param in req.params:
|
for param in req.params:
|
||||||
@@ -77,6 +78,7 @@ class ImagesController(wsgi.Controller):
|
|||||||
@wsgi.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION)
|
@wsgi.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION)
|
||||||
@wsgi.expected_errors(404)
|
@wsgi.expected_errors(404)
|
||||||
@validation.query_schema(schema.show_query)
|
@validation.query_schema(schema.show_query)
|
||||||
|
@validation.response_body_schema(schema.show_response)
|
||||||
def show(self, req, id):
|
def show(self, req, id):
|
||||||
"""Return detailed information about a specific image.
|
"""Return detailed information about a specific image.
|
||||||
|
|
||||||
@@ -96,6 +98,7 @@ class ImagesController(wsgi.Controller):
|
|||||||
@wsgi.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION)
|
@wsgi.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION)
|
||||||
@wsgi.expected_errors((403, 404))
|
@wsgi.expected_errors((403, 404))
|
||||||
@wsgi.response(204)
|
@wsgi.response(204)
|
||||||
|
@validation.response_body_schema(schema.delete_response)
|
||||||
def delete(self, req, id):
|
def delete(self, req, id):
|
||||||
"""Delete an image, if allowed.
|
"""Delete an image, if allowed.
|
||||||
|
|
||||||
@@ -117,11 +120,11 @@ class ImagesController(wsgi.Controller):
|
|||||||
@wsgi.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION)
|
@wsgi.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION)
|
||||||
@wsgi.expected_errors(400)
|
@wsgi.expected_errors(400)
|
||||||
@validation.query_schema(schema.index_query)
|
@validation.query_schema(schema.index_query)
|
||||||
|
@validation.response_body_schema(schema.index_response)
|
||||||
def index(self, req):
|
def index(self, req):
|
||||||
"""Return an index listing of images available to the request.
|
"""Return an index listing of images available to the request.
|
||||||
|
|
||||||
:param req: `wsgi.Request` object
|
:param req: `wsgi.Request` object
|
||||||
|
|
||||||
"""
|
"""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
filters = self._get_filters(req)
|
filters = self._get_filters(req)
|
||||||
@@ -137,11 +140,11 @@ class ImagesController(wsgi.Controller):
|
|||||||
@wsgi.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION)
|
@wsgi.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION)
|
||||||
@wsgi.expected_errors(400)
|
@wsgi.expected_errors(400)
|
||||||
@validation.query_schema(schema.detail_query)
|
@validation.query_schema(schema.detail_query)
|
||||||
|
@validation.response_body_schema(schema.detail_response)
|
||||||
def detail(self, req):
|
def detail(self, req):
|
||||||
"""Return a detailed index listing of images available to the request.
|
"""Return a detailed index listing of images available to the request.
|
||||||
|
|
||||||
:param req: `wsgi.Request` object.
|
:param req: `wsgi.Request` object.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
filters = self._get_filters(req)
|
filters = self._get_filters(req)
|
||||||
|
@@ -10,7 +10,10 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import copy
|
||||||
|
|
||||||
from nova.api.validation import parameter_types
|
from nova.api.validation import parameter_types
|
||||||
|
from nova.api.validation import response_types
|
||||||
|
|
||||||
# NOTE(stephenfin): These schemas are incomplete but won't be enhanced further
|
# NOTE(stephenfin): These schemas are incomplete but won't be enhanced further
|
||||||
# since these APIs have been removed
|
# since these APIs have been removed
|
||||||
@@ -50,3 +53,179 @@ index_query = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
detail_query = index_query
|
detail_query = index_query
|
||||||
|
|
||||||
|
_links_response = {
|
||||||
|
'type': 'array',
|
||||||
|
'prefixItems': [
|
||||||
|
{
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'href': {'type': 'string', 'format': 'uri'},
|
||||||
|
'rel': {'const': 'self'},
|
||||||
|
},
|
||||||
|
'required': ['href', 'rel'],
|
||||||
|
'additionalProperties': False,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'href': {'type': 'string', 'format': 'uri'},
|
||||||
|
'rel': {'const': 'bookmark'},
|
||||||
|
},
|
||||||
|
'required': ['href', 'rel'],
|
||||||
|
'additionalProperties': False,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'href': {'type': 'string', 'format': 'uri'},
|
||||||
|
'rel': {'const': 'alternate'},
|
||||||
|
'type': {'const': 'application/vnd.openstack.image'},
|
||||||
|
},
|
||||||
|
'required': ['href', 'rel', 'type'],
|
||||||
|
'additionalProperties': False,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'minItems': 3,
|
||||||
|
'maxItems': 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
_image_response = {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'created': {'type': 'string', 'format': 'date-time'},
|
||||||
|
'id': {'type': 'string', 'format': 'uuid'},
|
||||||
|
'links': _links_response,
|
||||||
|
'metadata': {
|
||||||
|
'type': 'object',
|
||||||
|
'patternProperties': {
|
||||||
|
# unlike nova's metadata, glance doesn't have a maximum length
|
||||||
|
# on property values. Also, while glance serializes all
|
||||||
|
# non-null values as strings, nova's image API deserializes
|
||||||
|
# these again, so we can expected practically any primitive
|
||||||
|
# type here. Listing all these is effectively the same as
|
||||||
|
# providing an empty schema so we're mainly doing it for the
|
||||||
|
# benefit of tooling.
|
||||||
|
'^[a-zA-Z0-9-_:. ]{1,255}$': {
|
||||||
|
'type': [
|
||||||
|
'array',
|
||||||
|
'boolean',
|
||||||
|
'integer',
|
||||||
|
'number',
|
||||||
|
'object',
|
||||||
|
'string',
|
||||||
|
'null',
|
||||||
|
]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'additionalProperties': False,
|
||||||
|
},
|
||||||
|
'minDisk': {'type': 'integer', 'minimum': 0},
|
||||||
|
'minRam': {'type': 'integer', 'minimum': 0},
|
||||||
|
'name': {'type': ['string', 'null']},
|
||||||
|
'progress': {
|
||||||
|
'type': 'integer',
|
||||||
|
'enum': [0, 25, 50, 100],
|
||||||
|
},
|
||||||
|
'server': {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'id': {'type': 'string', 'format': 'uuid'},
|
||||||
|
'links': {
|
||||||
|
'type': 'array',
|
||||||
|
'prefixItems': [
|
||||||
|
{
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'href': {'type': 'string', 'format': 'uri'},
|
||||||
|
'rel': {'const': 'self'},
|
||||||
|
},
|
||||||
|
'required': ['href', 'rel'],
|
||||||
|
'additionalProperties': False,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'href': {'type': 'string', 'format': 'uri'},
|
||||||
|
'rel': {'const': 'bookmark'},
|
||||||
|
},
|
||||||
|
'required': ['href', 'rel'],
|
||||||
|
'additionalProperties': False,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'minItems': 2,
|
||||||
|
'maxItems': 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'required': ['id', 'links'],
|
||||||
|
'additionalProperties': False,
|
||||||
|
},
|
||||||
|
'status': {
|
||||||
|
'type': 'string',
|
||||||
|
'enum': ['ACTIVE', 'SAVING', 'DELETED', 'ERROR', 'UNKNOWN'],
|
||||||
|
},
|
||||||
|
'updated': {'type': ['string', 'null'], 'format': 'date-time'},
|
||||||
|
'OS-DCF:diskConfig': {'type': 'string', 'enum': ['AUTO', 'MANUAL']},
|
||||||
|
'OS-EXT-IMG-SIZE:size': {'type': 'integer'},
|
||||||
|
},
|
||||||
|
'required': [
|
||||||
|
'created',
|
||||||
|
'id',
|
||||||
|
'links',
|
||||||
|
'metadata',
|
||||||
|
'minDisk',
|
||||||
|
'minRam',
|
||||||
|
'name',
|
||||||
|
'progress',
|
||||||
|
'status',
|
||||||
|
'updated',
|
||||||
|
'OS-EXT-IMG-SIZE:size',
|
||||||
|
],
|
||||||
|
'additionalProperties': False,
|
||||||
|
}
|
||||||
|
|
||||||
|
show_response = {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'image': copy.deepcopy(_image_response),
|
||||||
|
},
|
||||||
|
'required': [],
|
||||||
|
'additionalProperties': False,
|
||||||
|
}
|
||||||
|
|
||||||
|
delete_response = {'type': 'null'}
|
||||||
|
|
||||||
|
index_response = {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'images': {
|
||||||
|
'type': 'array',
|
||||||
|
'items': {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'id': {'type': 'string', 'format': 'uuid'},
|
||||||
|
'links': _links_response,
|
||||||
|
'name': {'type': ['string', 'null']},
|
||||||
|
},
|
||||||
|
'required': ['id', 'links', 'name'],
|
||||||
|
'additionalProperties': False,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'images_links': response_types.collection_links,
|
||||||
|
},
|
||||||
|
'required': [],
|
||||||
|
'additionalProperties': False,
|
||||||
|
}
|
||||||
|
|
||||||
|
detail_response = {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'images': {
|
||||||
|
'type': 'array',
|
||||||
|
'items': copy.deepcopy(_image_response),
|
||||||
|
},
|
||||||
|
'images_links': response_types.collection_links,
|
||||||
|
},
|
||||||
|
'required': ['images'],
|
||||||
|
'additionalProperties': False,
|
||||||
|
}
|
||||||
|
@@ -19,7 +19,7 @@ metadata = {
|
|||||||
'type': 'object',
|
'type': 'object',
|
||||||
'patternProperties': {
|
'patternProperties': {
|
||||||
'^[a-zA-Z0-9-_:. ]{1,255}$': {
|
'^[a-zA-Z0-9-_:. ]{1,255}$': {
|
||||||
'type': 'string', 'maxLength': 255,
|
'type': ['string', 'null'], 'maxLength': 255,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'additionalProperties': False,
|
'additionalProperties': False,
|
||||||
|
27
nova/tests/fixtures/glance.py
vendored
27
nova/tests/fixtures/glance.py
vendored
@@ -30,7 +30,9 @@ class GlanceFixture(fixtures.Fixture):
|
|||||||
|
|
||||||
# NOTE(justinsb): The OpenStack API can't upload an image?
|
# NOTE(justinsb): The OpenStack API can't upload an image?
|
||||||
# So, make sure we've got one..
|
# So, make sure we've got one..
|
||||||
timestamp = datetime.datetime(2011, 1, 1, 1, 2, 3)
|
timestamp = datetime.datetime(
|
||||||
|
2011, 1, 1, 1, 2, 3, tzinfo=datetime.timezone.utc
|
||||||
|
)
|
||||||
|
|
||||||
image1 = {
|
image1 = {
|
||||||
'id': '155d900f-4e14-4e4c-a73d-069cbf4541e6',
|
'id': '155d900f-4e14-4e4c-a73d-069cbf4541e6',
|
||||||
@@ -43,7 +45,7 @@ class GlanceFixture(fixtures.Fixture):
|
|||||||
'is_public': False,
|
'is_public': False,
|
||||||
'container_format': 'raw',
|
'container_format': 'raw',
|
||||||
'disk_format': 'raw',
|
'disk_format': 'raw',
|
||||||
'size': '25165824',
|
'size': 25165824,
|
||||||
'min_ram': 0,
|
'min_ram': 0,
|
||||||
'min_disk': 0,
|
'min_disk': 0,
|
||||||
'protected': False,
|
'protected': False,
|
||||||
@@ -67,7 +69,7 @@ class GlanceFixture(fixtures.Fixture):
|
|||||||
'is_public': True,
|
'is_public': True,
|
||||||
'container_format': 'ami',
|
'container_format': 'ami',
|
||||||
'disk_format': 'ami',
|
'disk_format': 'ami',
|
||||||
'size': '58145823',
|
'size': 58145823,
|
||||||
'min_ram': 0,
|
'min_ram': 0,
|
||||||
'min_disk': 0,
|
'min_disk': 0,
|
||||||
'protected': False,
|
'protected': False,
|
||||||
@@ -90,7 +92,7 @@ class GlanceFixture(fixtures.Fixture):
|
|||||||
'is_public': True,
|
'is_public': True,
|
||||||
'container_format': 'bare',
|
'container_format': 'bare',
|
||||||
'disk_format': 'raw',
|
'disk_format': 'raw',
|
||||||
'size': '83594576',
|
'size': 83594576,
|
||||||
'min_ram': 0,
|
'min_ram': 0,
|
||||||
'min_disk': 0,
|
'min_disk': 0,
|
||||||
'protected': False,
|
'protected': False,
|
||||||
@@ -114,7 +116,7 @@ class GlanceFixture(fixtures.Fixture):
|
|||||||
'is_public': True,
|
'is_public': True,
|
||||||
'container_format': 'ami',
|
'container_format': 'ami',
|
||||||
'disk_format': 'ami',
|
'disk_format': 'ami',
|
||||||
'size': '84035174',
|
'size': 84035174,
|
||||||
'min_ram': 0,
|
'min_ram': 0,
|
||||||
'min_disk': 0,
|
'min_disk': 0,
|
||||||
'protected': False,
|
'protected': False,
|
||||||
@@ -137,7 +139,7 @@ class GlanceFixture(fixtures.Fixture):
|
|||||||
'is_public': True,
|
'is_public': True,
|
||||||
'container_format': 'ami',
|
'container_format': 'ami',
|
||||||
'disk_format': 'ami',
|
'disk_format': 'ami',
|
||||||
'size': '26360814',
|
'size': 26360814,
|
||||||
'min_ram': 0,
|
'min_ram': 0,
|
||||||
'min_disk': 0,
|
'min_disk': 0,
|
||||||
'protected': False,
|
'protected': False,
|
||||||
@@ -160,7 +162,7 @@ class GlanceFixture(fixtures.Fixture):
|
|||||||
'is_public': False,
|
'is_public': False,
|
||||||
'container_format': 'ova',
|
'container_format': 'ova',
|
||||||
'disk_format': 'vhd',
|
'disk_format': 'vhd',
|
||||||
'size': '49163826',
|
'size': 49163826,
|
||||||
'min_ram': 0,
|
'min_ram': 0,
|
||||||
'min_disk': 0,
|
'min_disk': 0,
|
||||||
'protected': False,
|
'protected': False,
|
||||||
@@ -185,7 +187,7 @@ class GlanceFixture(fixtures.Fixture):
|
|||||||
'is_public': False,
|
'is_public': False,
|
||||||
'container_format': 'ova',
|
'container_format': 'ova',
|
||||||
'disk_format': 'vhd',
|
'disk_format': 'vhd',
|
||||||
'size': '74185822',
|
'size': 74185822,
|
||||||
'min_ram': 0,
|
'min_ram': 0,
|
||||||
'min_disk': 0,
|
'min_disk': 0,
|
||||||
'protected': False,
|
'protected': False,
|
||||||
@@ -309,7 +311,7 @@ class GlanceFixture(fixtures.Fixture):
|
|||||||
# by the caller. This is needed to avoid a KeyError in the
|
# by the caller. This is needed to avoid a KeyError in the
|
||||||
# image-size API.
|
# image-size API.
|
||||||
if 'size' not in image_meta:
|
if 'size' not in image_meta:
|
||||||
image_meta['size'] = None
|
image_meta['size'] = 74185822
|
||||||
|
|
||||||
# Similarly, Glance provides the status on the image once it's created
|
# Similarly, Glance provides the status on the image once it's created
|
||||||
# and this is checked in the compute API when booting a server from
|
# and this is checked in the compute API when booting a server from
|
||||||
@@ -325,6 +327,13 @@ class GlanceFixture(fixtures.Fixture):
|
|||||||
# proxy API by throwing it into the generic "properties" dict.
|
# proxy API by throwing it into the generic "properties" dict.
|
||||||
image_meta.get('properties', {})['owner'] = context.project_id
|
image_meta.get('properties', {})['owner'] = context.project_id
|
||||||
|
|
||||||
|
# Glance would always populate these fields, so we need to ensure we do
|
||||||
|
# the same
|
||||||
|
if not image_meta.get('created_at'):
|
||||||
|
image_meta['created_at'] = self.timestamp
|
||||||
|
if not image_meta.get('updated_at'):
|
||||||
|
image_meta['updated_at'] = self.timestamp
|
||||||
|
|
||||||
self.images[image_id] = image_meta
|
self.images[image_id] = image_meta
|
||||||
|
|
||||||
if data:
|
if data:
|
||||||
|
@@ -411,7 +411,7 @@ class InstanceHelperMixin:
|
|||||||
'is_public': False,
|
'is_public': False,
|
||||||
'container_format': 'raw',
|
'container_format': 'raw',
|
||||||
'disk_format': 'raw',
|
'disk_format': 'raw',
|
||||||
'size': '25165824',
|
'size': 25165824,
|
||||||
'min_ram': 0,
|
'min_ram': 0,
|
||||||
'min_disk': 0,
|
'min_disk': 0,
|
||||||
'protected': False,
|
'protected': False,
|
||||||
@@ -740,8 +740,9 @@ class InstanceHelperMixin:
|
|||||||
def _create_server_boot_from_volume(self, image_args=None,
|
def _create_server_boot_from_volume(self, image_args=None,
|
||||||
flavor_id=None, networks=None):
|
flavor_id=None, networks=None):
|
||||||
bfv_image_id = uuids.bfv_image_uuid
|
bfv_image_id = uuids.bfv_image_uuid
|
||||||
timestamp = datetime.datetime(2011, 1, 1, 1, 2, 3)
|
timestamp = datetime.datetime(
|
||||||
|
2011, 1, 1, 1, 2, 3, tzinfo=datetime.timezone.utc
|
||||||
|
)
|
||||||
image = {
|
image = {
|
||||||
'id': bfv_image_id,
|
'id': bfv_image_id,
|
||||||
'name': 'fake_image_name',
|
'name': 'fake_image_name',
|
||||||
@@ -752,7 +753,8 @@ class InstanceHelperMixin:
|
|||||||
'status': 'active',
|
'status': 'active',
|
||||||
'container_format': 'raw',
|
'container_format': 'raw',
|
||||||
'disk_format': 'raw',
|
'disk_format': 'raw',
|
||||||
'min_disk': 0
|
'min_disk': 0,
|
||||||
|
'size': 74185822,
|
||||||
}
|
}
|
||||||
if image_args:
|
if image_args:
|
||||||
image.update(image_args)
|
image.update(image_args)
|
||||||
|
@@ -111,20 +111,23 @@ class LibvirtDeviceBusMigration(base.ServersTestBase):
|
|||||||
'hw_video_model': 'qxl',
|
'hw_video_model': 'qxl',
|
||||||
'hw_vif_model': 'e1000',
|
'hw_vif_model': 'e1000',
|
||||||
}
|
}
|
||||||
|
timestamp = datetime.datetime(
|
||||||
|
2011, 1, 1, 1, 2, 3, tzinfo=datetime.timezone.utc
|
||||||
|
)
|
||||||
self.glance.create(
|
self.glance.create(
|
||||||
None,
|
None,
|
||||||
{
|
{
|
||||||
'id': uuids.hw_bus_model_image_uuid,
|
'id': uuids.hw_bus_model_image_uuid,
|
||||||
'name': 'hw_bus_model_image',
|
'name': 'hw_bus_model_image',
|
||||||
'created_at': datetime.datetime(2011, 1, 1, 1, 2, 3),
|
'created_at': timestamp,
|
||||||
'updated_at': datetime.datetime(2011, 1, 1, 1, 2, 3),
|
'updated_at': timestamp,
|
||||||
'deleted_at': None,
|
'deleted_at': None,
|
||||||
'deleted': False,
|
'deleted': False,
|
||||||
'status': 'active',
|
'status': 'active',
|
||||||
'is_public': False,
|
'is_public': False,
|
||||||
'container_format': 'bare',
|
'container_format': 'bare',
|
||||||
'disk_format': 'qcow2',
|
'disk_format': 'qcow2',
|
||||||
'size': '74185822',
|
'size': 74185822,
|
||||||
'min_ram': 0,
|
'min_ram': 0,
|
||||||
'min_disk': 0,
|
'min_disk': 0,
|
||||||
'protected': False,
|
'protected': False,
|
||||||
@@ -330,20 +333,23 @@ class LibvirtDeviceBusMigration(base.ServersTestBase):
|
|||||||
'hw_video_model': 'cirrus',
|
'hw_video_model': 'cirrus',
|
||||||
'hw_vif_model': 'e1000',
|
'hw_vif_model': 'e1000',
|
||||||
}
|
}
|
||||||
|
timestamp = datetime.datetime(
|
||||||
|
2011, 1, 1, 1, 2, 3, tzinfo=datetime.timezone.utc
|
||||||
|
)
|
||||||
self.glance.create(
|
self.glance.create(
|
||||||
None,
|
None,
|
||||||
{
|
{
|
||||||
'id': uuids.pc_image_uuid,
|
'id': uuids.pc_image_uuid,
|
||||||
'name': 'pc_image',
|
'name': 'pc_image',
|
||||||
'created_at': datetime.datetime(2011, 1, 1, 1, 2, 3),
|
'created_at': timestamp,
|
||||||
'updated_at': datetime.datetime(2011, 1, 1, 1, 2, 3),
|
'updated_at': timestamp,
|
||||||
'deleted_at': None,
|
'deleted_at': None,
|
||||||
'deleted': False,
|
'deleted': False,
|
||||||
'status': 'active',
|
'status': 'active',
|
||||||
'is_public': False,
|
'is_public': False,
|
||||||
'container_format': 'bare',
|
'container_format': 'bare',
|
||||||
'disk_format': 'qcow2',
|
'disk_format': 'qcow2',
|
||||||
'size': '74185822',
|
'size': 74185822,
|
||||||
'min_ram': 0,
|
'min_ram': 0,
|
||||||
'min_disk': 0,
|
'min_disk': 0,
|
||||||
'protected': False,
|
'protected': False,
|
||||||
|
@@ -57,7 +57,9 @@ class RescueServerTestWithDeletedBaseImage(
|
|||||||
'nova.virt.libvirt.utils.get_instance_path', fake_path))
|
'nova.virt.libvirt.utils.get_instance_path', fake_path))
|
||||||
|
|
||||||
def _create_test_images(self):
|
def _create_test_images(self):
|
||||||
timestamp = datetime.datetime(2021, 1, 2, 3, 4, 5)
|
timestamp = datetime.datetime(
|
||||||
|
2021, 1, 2, 3, 4, 5, tzinfo=datetime.timezone.utc
|
||||||
|
)
|
||||||
base_image = {
|
base_image = {
|
||||||
'id': uuids.base_image,
|
'id': uuids.base_image,
|
||||||
'name': 'base_image',
|
'name': 'base_image',
|
||||||
@@ -69,7 +71,7 @@ class RescueServerTestWithDeletedBaseImage(
|
|||||||
'is_public': False,
|
'is_public': False,
|
||||||
'container_format': 'ova',
|
'container_format': 'ova',
|
||||||
'disk_format': 'vhd',
|
'disk_format': 'vhd',
|
||||||
'size': '74185822',
|
'size': 74185822,
|
||||||
'min_ram': 0,
|
'min_ram': 0,
|
||||||
'min_disk': 0,
|
'min_disk': 0,
|
||||||
'protected': False,
|
'protected': False,
|
||||||
@@ -88,7 +90,7 @@ class RescueServerTestWithDeletedBaseImage(
|
|||||||
'is_public': False,
|
'is_public': False,
|
||||||
'container_format': 'ova',
|
'container_format': 'ova',
|
||||||
'disk_format': 'vhd',
|
'disk_format': 'vhd',
|
||||||
'size': '74185822',
|
'size': 74185822,
|
||||||
'min_ram': 0,
|
'min_ram': 0,
|
||||||
'min_disk': 0,
|
'min_disk': 0,
|
||||||
'protected': False,
|
'protected': False,
|
||||||
|
@@ -75,7 +75,9 @@ class UEFIServersTest(base.ServersTestBase):
|
|||||||
self.assertIn('COMPUTE_SECURITY_UEFI_SECURE_BOOT', traits)
|
self.assertIn('COMPUTE_SECURITY_UEFI_SECURE_BOOT', traits)
|
||||||
|
|
||||||
# create a server with UEFI and secure boot
|
# create a server with UEFI and secure boot
|
||||||
timestamp = datetime.datetime(2021, 1, 2, 3, 4, 5)
|
timestamp = datetime.datetime(
|
||||||
|
2021, 1, 2, 3, 4, 5, tzinfo=datetime.timezone.utc
|
||||||
|
)
|
||||||
uefi_image = {
|
uefi_image = {
|
||||||
'id': uuids.uefi_image,
|
'id': uuids.uefi_image,
|
||||||
'name': 'uefi_image',
|
'name': 'uefi_image',
|
||||||
@@ -87,7 +89,7 @@ class UEFIServersTest(base.ServersTestBase):
|
|||||||
'is_public': False,
|
'is_public': False,
|
||||||
'container_format': 'ova',
|
'container_format': 'ova',
|
||||||
'disk_format': 'vhd',
|
'disk_format': 'vhd',
|
||||||
'size': '74185822',
|
'size': 74185822,
|
||||||
'min_ram': 0,
|
'min_ram': 0,
|
||||||
'min_disk': 0,
|
'min_disk': 0,
|
||||||
'protected': False,
|
'protected': False,
|
||||||
|
@@ -30,20 +30,23 @@ class LibvirtVifModelTest(base.ServersTestBase):
|
|||||||
CONF.set_default("image_metadata_prefilter", True, group='scheduler')
|
CONF.set_default("image_metadata_prefilter", True, group='scheduler')
|
||||||
super().setUp()
|
super().setUp()
|
||||||
|
|
||||||
|
timestamp = datetime.datetime(
|
||||||
|
2011, 1, 1, 1, 2, 3, tzinfo=datetime.timezone.utc
|
||||||
|
)
|
||||||
self.glance.create(
|
self.glance.create(
|
||||||
None,
|
None,
|
||||||
{
|
{
|
||||||
'id': uuids.image_vif_model_igb,
|
'id': uuids.image_vif_model_igb,
|
||||||
'name': 'image-with-igb',
|
'name': 'image-with-igb',
|
||||||
'created_at': datetime.datetime(2011, 1, 1, 1, 2, 3),
|
'created_at': timestamp,
|
||||||
'updated_at': datetime.datetime(2011, 1, 1, 1, 2, 3),
|
'updated_at': timestamp,
|
||||||
'deleted_at': None,
|
'deleted_at': None,
|
||||||
'deleted': False,
|
'deleted': False,
|
||||||
'status': 'active',
|
'status': 'active',
|
||||||
'is_public': False,
|
'is_public': False,
|
||||||
'container_format': 'bare',
|
'container_format': 'bare',
|
||||||
'disk_format': 'qcow2',
|
'disk_format': 'qcow2',
|
||||||
'size': '74185822',
|
'size': 74185822,
|
||||||
'min_ram': 0,
|
'min_ram': 0,
|
||||||
'min_disk': 0,
|
'min_disk': 0,
|
||||||
'protected': False,
|
'protected': False,
|
||||||
|
@@ -44,7 +44,7 @@ class TestServerGet(test.TestCase):
|
|||||||
'is_public': False,
|
'is_public': False,
|
||||||
'container_format': 'raw',
|
'container_format': 'raw',
|
||||||
'disk_format': 'raw',
|
'disk_format': 'raw',
|
||||||
'size': '25165824',
|
'size': 25165824,
|
||||||
'properties': {'kernel_id': 'nokernel',
|
'properties': {'kernel_id': 'nokernel',
|
||||||
'ramdisk_id': 'nokernel',
|
'ramdisk_id': 'nokernel',
|
||||||
'architecture': 'x64'}}
|
'architecture': 'x64'}}
|
||||||
|
@@ -34,7 +34,9 @@ class TestNonBootableImageMeta(integrated_helpers._IntegratedTestBase):
|
|||||||
super().setUp()
|
super().setUp()
|
||||||
|
|
||||||
# Add an image to the Glance fixture with cinder_encryption_key set
|
# Add an image to the Glance fixture with cinder_encryption_key set
|
||||||
timestamp = datetime.datetime(2011, 1, 1, 1, 2, 3)
|
timestamp = datetime.datetime(
|
||||||
|
2011, 1, 1, 1, 2, 3, tzinfo=datetime.timezone.utc
|
||||||
|
)
|
||||||
cinder_encrypted_image = {
|
cinder_encrypted_image = {
|
||||||
'id': uuids.cinder_encrypted_image_uuid,
|
'id': uuids.cinder_encrypted_image_uuid,
|
||||||
'name': 'cinder_encryption_key_image',
|
'name': 'cinder_encryption_key_image',
|
||||||
@@ -46,7 +48,7 @@ class TestNonBootableImageMeta(integrated_helpers._IntegratedTestBase):
|
|||||||
'is_public': False,
|
'is_public': False,
|
||||||
'container_format': 'ova',
|
'container_format': 'ova',
|
||||||
'disk_format': 'vhd',
|
'disk_format': 'vhd',
|
||||||
'size': '74185822',
|
'size': 74185822,
|
||||||
'min_ram': 0,
|
'min_ram': 0,
|
||||||
'min_disk': 0,
|
'min_disk': 0,
|
||||||
'protected': False,
|
'protected': False,
|
||||||
|
@@ -58,7 +58,7 @@ class AggregateImagePropertiesIsolationTestCase(_AggregateTestCase):
|
|||||||
'is_public': False,
|
'is_public': False,
|
||||||
'container_format': 'raw',
|
'container_format': 'raw',
|
||||||
'disk_format': 'raw',
|
'disk_format': 'raw',
|
||||||
'size': '25165824',
|
'size': 25165824,
|
||||||
'min_ram': 0,
|
'min_ram': 0,
|
||||||
'min_disk': 0,
|
'min_disk': 0,
|
||||||
'protected': False,
|
'protected': False,
|
||||||
|
@@ -39,7 +39,7 @@ class BFVRescue(integrated_helpers.ProviderUsageBaseTestCase):
|
|||||||
'is_public': False,
|
'is_public': False,
|
||||||
'container_format': 'raw',
|
'container_format': 'raw',
|
||||||
'disk_format': 'raw',
|
'disk_format': 'raw',
|
||||||
'size': '25165824',
|
'size': 25165824,
|
||||||
'min_ram': 0,
|
'min_ram': 0,
|
||||||
'min_disk': 0,
|
'min_disk': 0,
|
||||||
'protected': False,
|
'protected': False,
|
||||||
|
@@ -111,7 +111,7 @@ class DiskConfigTestCaseV21(test.TestCase):
|
|||||||
'is_public': False,
|
'is_public': False,
|
||||||
'container_format': 'ova',
|
'container_format': 'ova',
|
||||||
'disk_format': 'vhd',
|
'disk_format': 'vhd',
|
||||||
'size': '74185822',
|
'size': 74185822,
|
||||||
'properties': {'auto_disk_config': 'Disabled'}}
|
'properties': {'auto_disk_config': 'Disabled'}}
|
||||||
self.image_service.create(None, image)
|
self.image_service.create(None, image)
|
||||||
|
|
||||||
|
@@ -68,104 +68,116 @@ class ImagesControllerTestV21(test.NoDBTestCase):
|
|||||||
self.server_uuid))
|
self.server_uuid))
|
||||||
self.alternate = "%s/images/%s"
|
self.alternate = "%s/images/%s"
|
||||||
|
|
||||||
self.expected_image_123 = {
|
self.image_a_uuid = IMAGE_FIXTURES[0]['id']
|
||||||
"image": {'id': '123',
|
self.expected_image_a = {
|
||||||
'name': 'public image',
|
"image": {
|
||||||
'metadata': {'key1': 'value1'},
|
'id': self.image_a_uuid,
|
||||||
'updated': NOW_API_FORMAT,
|
'name': 'public image',
|
||||||
'created': NOW_API_FORMAT,
|
'metadata': {'key1': 'value1'},
|
||||||
'status': 'ACTIVE',
|
'updated': NOW_API_FORMAT,
|
||||||
'minDisk': 10,
|
'created': NOW_API_FORMAT,
|
||||||
'progress': 100,
|
'status': 'ACTIVE',
|
||||||
'minRam': 128,
|
'minDisk': 10,
|
||||||
'OS-EXT-IMG-SIZE:size': 25165824,
|
'progress': 100,
|
||||||
"links": [{
|
'minRam': 128,
|
||||||
"rel": "self",
|
'OS-EXT-IMG-SIZE:size': 25165824,
|
||||||
"href": "%s/123" % self.url_prefix
|
"links": [
|
||||||
},
|
{
|
||||||
{
|
"rel": "self",
|
||||||
"rel": "bookmark",
|
"href": f"{self.url_prefix}/{self.image_a_uuid}"
|
||||||
"href":
|
},
|
||||||
"%s/123" % self.bookmark_prefix
|
{
|
||||||
},
|
"rel": "bookmark",
|
||||||
{
|
"href": f"{self.bookmark_prefix}/{self.image_a_uuid}"
|
||||||
"rel": "alternate",
|
},
|
||||||
"type": "application/vnd.openstack.image",
|
{
|
||||||
"href": self.alternate %
|
"rel": "alternate",
|
||||||
(glance.generate_glance_url('ctx'),
|
"type": "application/vnd.openstack.image",
|
||||||
123),
|
"href": self.alternate % (
|
||||||
}],
|
glance.generate_glance_url('ctx'),
|
||||||
|
self.image_a_uuid,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
self.expected_image_124 = {
|
self.image_b_uuid = IMAGE_FIXTURES[1]['id']
|
||||||
"image": {'id': '124',
|
self.expected_image_b = {
|
||||||
'name': 'queued snapshot',
|
"image": {
|
||||||
'metadata': {
|
'id': self.image_b_uuid,
|
||||||
u'instance_uuid': self.server_uuid,
|
'name': 'queued snapshot',
|
||||||
u'user_id': u'fake',
|
'metadata': {
|
||||||
},
|
'instance_uuid': self.server_uuid,
|
||||||
'updated': NOW_API_FORMAT,
|
'user_id': 'fake',
|
||||||
'created': NOW_API_FORMAT,
|
},
|
||||||
'status': 'SAVING',
|
'updated': NOW_API_FORMAT,
|
||||||
'progress': 25,
|
'created': NOW_API_FORMAT,
|
||||||
'minDisk': 0,
|
'status': 'SAVING',
|
||||||
'minRam': 0,
|
'progress': 25,
|
||||||
'OS-EXT-IMG-SIZE:size': 25165824,
|
'minDisk': 0,
|
||||||
'server': {
|
'minRam': 0,
|
||||||
'id': self.server_uuid,
|
'OS-EXT-IMG-SIZE:size': 25165824,
|
||||||
"links": [{
|
'server': {
|
||||||
"rel": "self",
|
'id': self.server_uuid,
|
||||||
"href": self.server_href,
|
"links": [
|
||||||
},
|
{
|
||||||
{
|
"rel": "self",
|
||||||
"rel": "bookmark",
|
"href": self.server_href,
|
||||||
"href": self.server_bookmark,
|
},
|
||||||
}],
|
{
|
||||||
},
|
"rel": "bookmark",
|
||||||
"links": [{
|
"href": self.server_bookmark,
|
||||||
"rel": "self",
|
}
|
||||||
"href": "%s/124" % self.url_prefix
|
],
|
||||||
},
|
},
|
||||||
{
|
"links": [
|
||||||
"rel": "bookmark",
|
{
|
||||||
"href":
|
"rel": "self",
|
||||||
"%s/124" % self.bookmark_prefix
|
"href": f"{self.url_prefix}/{self.image_b_uuid}"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"rel": "alternate",
|
"rel": "bookmark",
|
||||||
"type":
|
"href": f"{self.bookmark_prefix}/{self.image_b_uuid}"
|
||||||
"application/vnd.openstack.image",
|
},
|
||||||
"href": self.alternate %
|
{
|
||||||
(glance.generate_glance_url('ctx'),
|
"rel": "alternate",
|
||||||
124),
|
"type": "application/vnd.openstack.image",
|
||||||
}],
|
"href": self.alternate % (
|
||||||
|
glance.generate_glance_url('ctx'),
|
||||||
|
self.image_b_uuid,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@mock.patch('nova.image.glance.API.get', return_value=IMAGE_FIXTURES[0])
|
@mock.patch('nova.image.glance.API.get', return_value=IMAGE_FIXTURES[0])
|
||||||
def test_get_image(self, get_mocked):
|
def test_get_image(self, get_mocked):
|
||||||
request = self.http_request.blank(self.url_base + 'images/123')
|
request = self.http_request.blank(
|
||||||
actual_image = self.controller.show(request, '123')
|
self.url_base + f'images/{self.image_a_uuid}')
|
||||||
|
actual_image = self.controller.show(request, self.image_a_uuid)
|
||||||
self.assertThat(actual_image,
|
self.assertThat(actual_image,
|
||||||
matchers.DictMatches(self.expected_image_123))
|
matchers.DictMatches(self.expected_image_a))
|
||||||
get_mocked.assert_called_once_with(mock.ANY, '123')
|
get_mocked.assert_called_once_with(mock.ANY, self.image_a_uuid)
|
||||||
|
|
||||||
@mock.patch('nova.image.glance.API.get', return_value=IMAGE_FIXTURES[1])
|
@mock.patch('nova.image.glance.API.get', return_value=IMAGE_FIXTURES[1])
|
||||||
def test_get_image_with_custom_prefix(self, _get_mocked):
|
def test_get_image_with_custom_prefix(self, _get_mocked):
|
||||||
self.flags(compute_link_prefix='https://zoo.com:42',
|
self.flags(compute_link_prefix='https://zoo.com:42',
|
||||||
glance_link_prefix='http://circus.com:34',
|
glance_link_prefix='http://circus.com:34',
|
||||||
group='api')
|
group='api')
|
||||||
fake_req = self.http_request.blank(self.url_base + 'images/124')
|
fake_req = self.http_request.blank(
|
||||||
actual_image = self.controller.show(fake_req, '124')
|
self.url_base + f'images/{self.image_b_uuid}')
|
||||||
|
actual_image = self.controller.show(fake_req, self.image_b_uuid)
|
||||||
|
|
||||||
expected_image = self.expected_image_124
|
expected_image = self.expected_image_b
|
||||||
expected_image["image"]["links"][0]["href"] = (
|
expected_image["image"]["links"][0]["href"] = (
|
||||||
"https://zoo.com:42%s/images/124" % self.url_base)
|
f"https://zoo.com:42{self.url_base}/images/{self.image_b_uuid}")
|
||||||
expected_image["image"]["links"][1]["href"] = (
|
expected_image["image"]["links"][1]["href"] = (
|
||||||
"https://zoo.com:42%s/images/124" % self.bookmark_base)
|
f"https://zoo.com:42{self.bookmark_base}/images/"
|
||||||
|
f"{self.image_b_uuid}")
|
||||||
expected_image["image"]["links"][2]["href"] = (
|
expected_image["image"]["links"][2]["href"] = (
|
||||||
"http://circus.com:34/images/124")
|
f"http://circus.com:34/images/{self.image_b_uuid}")
|
||||||
expected_image["image"]["server"]["links"][0]["href"] = (
|
expected_image["image"]["server"]["links"][0]["href"] = (
|
||||||
"https://zoo.com:42%s/servers/%s" % (self.url_base,
|
"https://zoo.com:42%s/servers/%s" % (self.url_base,
|
||||||
self.server_uuid))
|
self.server_uuid))
|
||||||
@@ -190,82 +202,96 @@ class ImagesControllerTestV21(test.NoDBTestCase):
|
|||||||
get_all_mocked.assert_called_once_with(mock.ANY, filters={})
|
get_all_mocked.assert_called_once_with(mock.ANY, filters={})
|
||||||
response_list = response["images"]
|
response_list = response["images"]
|
||||||
|
|
||||||
image_125 = copy.deepcopy(self.expected_image_124["image"])
|
image_c = copy.deepcopy(self.expected_image_b["image"])
|
||||||
image_125['id'] = '125'
|
image_c['id'] = IMAGE_FIXTURES[2]['id']
|
||||||
image_125['name'] = 'saving snapshot'
|
image_c['name'] = 'saving snapshot'
|
||||||
image_125['progress'] = 50
|
image_c['progress'] = 50
|
||||||
image_125["links"][0]["href"] = "%s/125" % self.url_prefix
|
image_c["links"][0]["href"] = "%s/%s" % (
|
||||||
image_125["links"][1]["href"] = "%s/125" % self.bookmark_prefix
|
self.url_prefix, IMAGE_FIXTURES[2]['id'])
|
||||||
image_125["links"][2]["href"] = (
|
image_c["links"][1]["href"] = "%s/%s" % (
|
||||||
"%s/images/125" % glance.generate_glance_url('ctx'))
|
self.bookmark_prefix, IMAGE_FIXTURES[2]['id'])
|
||||||
|
image_c["links"][2]["href"] = "%s/images/%s" % (
|
||||||
|
glance.generate_glance_url('ctx'), IMAGE_FIXTURES[2]['id'])
|
||||||
|
|
||||||
image_126 = copy.deepcopy(self.expected_image_124["image"])
|
image_d = copy.deepcopy(self.expected_image_b["image"])
|
||||||
image_126['id'] = '126'
|
image_d['id'] = IMAGE_FIXTURES[3]['id']
|
||||||
image_126['name'] = 'active snapshot'
|
image_d['name'] = 'active snapshot'
|
||||||
image_126['status'] = 'ACTIVE'
|
image_d['status'] = 'ACTIVE'
|
||||||
image_126['progress'] = 100
|
image_d['progress'] = 100
|
||||||
image_126["links"][0]["href"] = "%s/126" % self.url_prefix
|
image_d["links"][0]["href"] = "%s/%s" % (
|
||||||
image_126["links"][1]["href"] = "%s/126" % self.bookmark_prefix
|
self.url_prefix, IMAGE_FIXTURES[3]['id'])
|
||||||
image_126["links"][2]["href"] = (
|
image_d["links"][1]["href"] = "%s/%s" % (
|
||||||
"%s/images/126" % glance.generate_glance_url('ctx'))
|
self.bookmark_prefix, IMAGE_FIXTURES[3]['id'])
|
||||||
|
image_d["links"][2]["href"] = "%s/images/%s" % (
|
||||||
|
glance.generate_glance_url('ctx'), IMAGE_FIXTURES[3]['id'])
|
||||||
|
|
||||||
image_127 = copy.deepcopy(self.expected_image_124["image"])
|
image_e = copy.deepcopy(self.expected_image_b["image"])
|
||||||
image_127['id'] = '127'
|
image_e['id'] = IMAGE_FIXTURES[4]['id']
|
||||||
image_127['name'] = 'killed snapshot'
|
image_e['name'] = 'killed snapshot'
|
||||||
image_127['status'] = 'ERROR'
|
image_e['status'] = 'ERROR'
|
||||||
image_127['progress'] = 0
|
image_e['progress'] = 0
|
||||||
image_127["links"][0]["href"] = "%s/127" % self.url_prefix
|
image_e["links"][0]["href"] = "%s/%s" % (
|
||||||
image_127["links"][1]["href"] = "%s/127" % self.bookmark_prefix
|
self.url_prefix, IMAGE_FIXTURES[4]['id'])
|
||||||
image_127["links"][2]["href"] = (
|
image_e["links"][1]["href"] = "%s/%s" % (
|
||||||
"%s/images/127" % glance.generate_glance_url('ctx'))
|
self.bookmark_prefix, IMAGE_FIXTURES[4]['id'])
|
||||||
|
image_e["links"][2]["href"] = "%s/images/%s" % (
|
||||||
|
glance.generate_glance_url('ctx'), IMAGE_FIXTURES[4]['id'])
|
||||||
|
|
||||||
image_128 = copy.deepcopy(self.expected_image_124["image"])
|
image_f = copy.deepcopy(self.expected_image_b["image"])
|
||||||
image_128['id'] = '128'
|
image_f['id'] = IMAGE_FIXTURES[5]['id']
|
||||||
image_128['name'] = 'deleted snapshot'
|
image_f['name'] = 'deleted snapshot'
|
||||||
image_128['status'] = 'DELETED'
|
image_f['status'] = 'DELETED'
|
||||||
image_128['progress'] = 0
|
image_f['progress'] = 0
|
||||||
image_128["links"][0]["href"] = "%s/128" % self.url_prefix
|
image_f["links"][0]["href"] = "%s/%s" % (
|
||||||
image_128["links"][1]["href"] = "%s/128" % self.bookmark_prefix
|
self.url_prefix, IMAGE_FIXTURES[5]['id'])
|
||||||
image_128["links"][2]["href"] = (
|
image_f["links"][1]["href"] = "%s/%s" % (
|
||||||
"%s/images/128" % glance.generate_glance_url('ctx'))
|
self.bookmark_prefix, IMAGE_FIXTURES[5]['id'])
|
||||||
|
image_f["links"][2]["href"] = "%s/images/%s" % (
|
||||||
|
glance.generate_glance_url('ctx'), IMAGE_FIXTURES[5]['id'])
|
||||||
|
|
||||||
image_129 = copy.deepcopy(self.expected_image_124["image"])
|
image_g = copy.deepcopy(self.expected_image_b["image"])
|
||||||
image_129['id'] = '129'
|
image_g['id'] = IMAGE_FIXTURES[6]['id']
|
||||||
image_129['name'] = 'pending_delete snapshot'
|
image_g['name'] = 'pending_delete snapshot'
|
||||||
image_129['status'] = 'DELETED'
|
image_g['status'] = 'DELETED'
|
||||||
image_129['progress'] = 0
|
image_g['progress'] = 0
|
||||||
image_129["links"][0]["href"] = "%s/129" % self.url_prefix
|
image_g["links"][0]["href"] = "%s/%s" % (
|
||||||
image_129["links"][1]["href"] = "%s/129" % self.bookmark_prefix
|
self.url_prefix, IMAGE_FIXTURES[6]['id'])
|
||||||
image_129["links"][2]["href"] = (
|
image_g["links"][1]["href"] = "%s/%s" % (
|
||||||
"%s/images/129" % glance.generate_glance_url('ctx'))
|
self.bookmark_prefix, IMAGE_FIXTURES[6]['id'])
|
||||||
|
image_g["links"][2]["href"] = "%s/images/%s" % (
|
||||||
|
glance.generate_glance_url('ctx'), IMAGE_FIXTURES[6]['id'])
|
||||||
|
|
||||||
image_130 = copy.deepcopy(self.expected_image_123["image"])
|
image_h = copy.deepcopy(self.expected_image_a["image"])
|
||||||
image_130['id'] = '130'
|
image_h['id'] = IMAGE_FIXTURES[7]['id']
|
||||||
image_130['name'] = None
|
image_h['name'] = None
|
||||||
image_130['metadata'] = {}
|
image_h['metadata'] = {}
|
||||||
image_130['minDisk'] = 0
|
image_h['minDisk'] = 0
|
||||||
image_130['minRam'] = 0
|
image_h['minRam'] = 0
|
||||||
image_130["links"][0]["href"] = "%s/130" % self.url_prefix
|
image_h["links"][0]["href"] = "%s/%s" % (
|
||||||
image_130["links"][1]["href"] = "%s/130" % self.bookmark_prefix
|
self.url_prefix, IMAGE_FIXTURES[7]['id'])
|
||||||
image_130["links"][2]["href"] = (
|
image_h["links"][1]["href"] = "%s/%s" % (
|
||||||
"%s/images/130" % glance.generate_glance_url('ctx'))
|
self.bookmark_prefix, IMAGE_FIXTURES[7]['id'])
|
||||||
|
image_h["links"][2]["href"] = "%s/images/%s" % (
|
||||||
|
glance.generate_glance_url('ctx'), IMAGE_FIXTURES[7]['id'])
|
||||||
|
|
||||||
image_131 = copy.deepcopy(self.expected_image_123["image"])
|
image_i = copy.deepcopy(self.expected_image_a["image"])
|
||||||
image_131['id'] = '131'
|
image_i['id'] = IMAGE_FIXTURES[8]['id']
|
||||||
image_131['name'] = None
|
image_i['name'] = None
|
||||||
image_131['metadata'] = {}
|
image_i['metadata'] = {}
|
||||||
image_131['minDisk'] = 0
|
image_i['minDisk'] = 0
|
||||||
image_131['minRam'] = 0
|
image_i['minRam'] = 0
|
||||||
image_131["links"][0]["href"] = "%s/131" % self.url_prefix
|
image_i["links"][0]["href"] = "%s/%s" % (
|
||||||
image_131["links"][1]["href"] = "%s/131" % self.bookmark_prefix
|
self.url_prefix, IMAGE_FIXTURES[8]['id'])
|
||||||
image_131["links"][2]["href"] = (
|
image_i["links"][1]["href"] = "%s/%s" % (
|
||||||
"%s/images/131" % glance.generate_glance_url('ctx'))
|
self.bookmark_prefix, IMAGE_FIXTURES[8]['id'])
|
||||||
|
image_i["links"][2]["href"] = "%s/images/%s" % (
|
||||||
|
glance.generate_glance_url('ctx'), IMAGE_FIXTURES[8]['id'])
|
||||||
|
|
||||||
expected = [self.expected_image_123["image"],
|
expected = [self.expected_image_a["image"],
|
||||||
self.expected_image_124["image"],
|
self.expected_image_b["image"],
|
||||||
image_125, image_126, image_127,
|
image_c, image_d, image_e,
|
||||||
image_128, image_129, image_130,
|
image_f, image_g, image_h,
|
||||||
image_131]
|
image_i]
|
||||||
|
|
||||||
self.assertThat(expected, matchers.DictListMatches(response_list))
|
self.assertThat(expected, matchers.DictListMatches(response_list))
|
||||||
|
|
||||||
@@ -358,29 +384,37 @@ class ImagesControllerTestV21(test.NoDBTestCase):
|
|||||||
|
|
||||||
@mock.patch('nova.image.glance.API.delete')
|
@mock.patch('nova.image.glance.API.delete')
|
||||||
def test_delete_image(self, delete_mocked):
|
def test_delete_image(self, delete_mocked):
|
||||||
request = self.http_request.blank(self.url_base + 'images/124')
|
request = self.http_request.blank(
|
||||||
|
self.url_base + f'images/{self.image_a_uuid}')
|
||||||
request.method = 'DELETE'
|
request.method = 'DELETE'
|
||||||
delete_method = self.controller.delete
|
delete_method = self.controller.delete
|
||||||
delete_method(request, '124')
|
delete_method(request, self.image_a_uuid)
|
||||||
self.assertEqual(204, delete_method.wsgi_codes(request))
|
self.assertEqual(204, delete_method.wsgi_codes(request))
|
||||||
delete_mocked.assert_called_once_with(mock.ANY, '124')
|
delete_mocked.assert_called_once_with(mock.ANY, self.image_a_uuid)
|
||||||
|
|
||||||
@mock.patch('nova.image.glance.API.delete',
|
def test_delete_deleted_image(self):
|
||||||
side_effect=exception.ImageNotAuthorized(image_id='123'))
|
|
||||||
def test_delete_deleted_image(self, _delete_mocked):
|
|
||||||
# If you try to delete a deleted image, you get back 403 Forbidden.
|
# If you try to delete a deleted image, you get back 403 Forbidden.
|
||||||
request = self.http_request.blank(self.url_base + 'images/123')
|
request = self.http_request.blank(
|
||||||
|
self.url_base + f'images/{self.image_a_uuid}')
|
||||||
request.method = 'DELETE'
|
request.method = 'DELETE'
|
||||||
self.assertRaises(webob.exc.HTTPForbidden, self.controller.delete,
|
with mock.patch(
|
||||||
request, '123')
|
'nova.image.glance.API.delete',
|
||||||
|
side_effect=exception.ImageNotAuthorized(
|
||||||
|
image_id=self.image_a_uuid
|
||||||
|
)
|
||||||
|
):
|
||||||
|
self.assertRaises(webob.exc.HTTPForbidden, self.controller.delete,
|
||||||
|
request, self.image_a_uuid)
|
||||||
|
|
||||||
@mock.patch('nova.image.glance.API.delete',
|
def test_delete_image_not_found(self):
|
||||||
side_effect=exception.ImageNotFound(image_id='123'))
|
|
||||||
def test_delete_image_not_found(self, _delete_mocked):
|
|
||||||
request = self.http_request.blank(self.url_base + 'images/300')
|
request = self.http_request.blank(self.url_base + 'images/300')
|
||||||
request.method = 'DELETE'
|
request.method = 'DELETE'
|
||||||
self.assertRaises(webob.exc.HTTPNotFound,
|
with mock.patch(
|
||||||
self.controller.delete, request, '300')
|
'nova.image.glance.API.delete',
|
||||||
|
side_effect=exception.ImageNotFound(image_id='300')
|
||||||
|
):
|
||||||
|
self.assertRaises(webob.exc.HTTPNotFound,
|
||||||
|
self.controller.delete, request, '300')
|
||||||
|
|
||||||
@mock.patch('nova.image.glance.API.get_all',
|
@mock.patch('nova.image.glance.API.get_all',
|
||||||
return_value=[IMAGE_FIXTURES[0]])
|
return_value=[IMAGE_FIXTURES[0]])
|
||||||
|
@@ -12,6 +12,8 @@
|
|||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
|
from oslo_utils import uuidutils
|
||||||
|
|
||||||
# nova.image.glance._translate_from_glance() returns datetime
|
# nova.image.glance._translate_from_glance() returns datetime
|
||||||
# objects, not strings.
|
# objects, not strings.
|
||||||
NOW_DATE = datetime.datetime(2010, 10, 11, 10, 30, 22)
|
NOW_DATE = datetime.datetime(2010, 10, 11, 10, 30, 22)
|
||||||
@@ -22,25 +24,22 @@ def get_image_fixtures():
|
|||||||
|
|
||||||
Returns a set of dicts representing images/snapshots of varying statuses
|
Returns a set of dicts representing images/snapshots of varying statuses
|
||||||
that would be returned from a call to
|
that would be returned from a call to
|
||||||
`glanceclient.client.Client.images.list`. The IDs of the images returned
|
`glanceclient.client.Client.images.list`. The IDs of the images are random,
|
||||||
start at 123 and go to 131, with the following brief summary of image
|
with the following brief summary of image attributes:
|
||||||
attributes:
|
|
||||||
|
|
||||||
| ID Type Status Notes
|
| # Type Status Notes
|
||||||
| ----------------------------------------------------------
|
| ----------------------------------------------------------
|
||||||
| 123 Public image active
|
| 0 Public image active
|
||||||
| 124 Snapshot queued
|
| 1 Snapshot queued
|
||||||
| 125 Snapshot saving
|
| 2 Snapshot saving
|
||||||
| 126 Snapshot active
|
| 3 Snapshot active
|
||||||
| 127 Snapshot killed
|
| 4 Snapshot killed
|
||||||
| 128 Snapshot deleted
|
| 5 Snapshot deleted
|
||||||
| 129 Snapshot pending_delete
|
| 6 Snapshot pending_delete
|
||||||
| 130 Public image active Has no name
|
| 7 Public image active Has no name
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
image_id = 123
|
|
||||||
|
|
||||||
fixtures = []
|
fixtures = []
|
||||||
|
|
||||||
def add_fixture(**kwargs):
|
def add_fixture(**kwargs):
|
||||||
@@ -49,10 +48,10 @@ def get_image_fixtures():
|
|||||||
fixtures.append(kwargs)
|
fixtures.append(kwargs)
|
||||||
|
|
||||||
# Public image
|
# Public image
|
||||||
|
image_id = uuidutils.generate_uuid()
|
||||||
add_fixture(id=str(image_id), name='public image', is_public=True,
|
add_fixture(id=str(image_id), name='public image', is_public=True,
|
||||||
status='active', properties={'key1': 'value1'},
|
status='active', properties={'key1': 'value1'},
|
||||||
min_ram="128", min_disk="10", size=25165824)
|
min_ram="128", min_disk="10", size=25165824)
|
||||||
image_id += 1
|
|
||||||
|
|
||||||
# Snapshot for User 1
|
# Snapshot for User 1
|
||||||
uuid = 'aa640691-d1a7-4a67-9d3c-d35ee6b3cc74'
|
uuid = 'aa640691-d1a7-4a67-9d3c-d35ee6b3cc74'
|
||||||
@@ -62,17 +61,18 @@ def get_image_fixtures():
|
|||||||
deleted = False if status != 'deleted' else True
|
deleted = False if status != 'deleted' else True
|
||||||
deleted_at = NOW_DATE if deleted else None
|
deleted_at = NOW_DATE if deleted else None
|
||||||
|
|
||||||
|
image_id = uuidutils.generate_uuid()
|
||||||
add_fixture(id=str(image_id), name='%s snapshot' % status,
|
add_fixture(id=str(image_id), name='%s snapshot' % status,
|
||||||
is_public=False, status=status,
|
is_public=False, status=status,
|
||||||
properties=snapshot_properties, size=25165824,
|
properties=snapshot_properties, size=25165824,
|
||||||
deleted=deleted, deleted_at=deleted_at)
|
deleted=deleted, deleted_at=deleted_at)
|
||||||
image_id += 1
|
|
||||||
|
|
||||||
# Image without a name
|
# Image without a name
|
||||||
|
image_id = uuidutils.generate_uuid()
|
||||||
add_fixture(id=str(image_id), is_public=True, status='active',
|
add_fixture(id=str(image_id), is_public=True, status='active',
|
||||||
properties={}, size=25165824)
|
properties={}, size=25165824)
|
||||||
# Image for permission tests
|
# Image for permission tests
|
||||||
image_id += 1
|
image_id = uuidutils.generate_uuid()
|
||||||
add_fixture(id=str(image_id), is_public=True, status='active',
|
add_fixture(id=str(image_id), is_public=True, status='active',
|
||||||
properties={}, owner='authorized_fake', size=25165824)
|
properties={}, owner='authorized_fake', size=25165824)
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user