2111 lines
73 KiB
Python
2111 lines
73 KiB
Python
|
# Copyright 2016 Hewlett-Packard Development Company, L.P.
|
||
|
#
|
||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||
|
# not use this file except in compliance with the License. You may obtain
|
||
|
# a copy of the License at
|
||
|
#
|
||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||
|
#
|
||
|
# Unless required by applicable law or agreed to in writing, software
|
||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||
|
# License for the specific language governing permissions and limitations
|
||
|
# under the License.
|
||
|
|
||
|
import io
|
||
|
import operator
|
||
|
import tempfile
|
||
|
import uuid
|
||
|
|
||
|
from openstack.cloud import exc
|
||
|
from openstack.cloud import meta
|
||
|
from openstack import exceptions
|
||
|
from openstack.image.v1 import image as image_v1
|
||
|
from openstack.image.v2 import image
|
||
|
from openstack.tests import fakes
|
||
|
from openstack.tests.unit import base
|
||
|
|
||
|
|
||
|
IMPORT_METHODS = 'glance-direct,web-download'
|
||
|
|
||
|
|
||
|
class BaseTestImage(base.TestCase):
|
||
|
def setUp(self):
|
||
|
super(BaseTestImage, self).setUp()
|
||
|
self.image_id = str(uuid.uuid4())
|
||
|
self.image_name = self.getUniqueString('image')
|
||
|
self.object_name = 'images/{name}'.format(name=self.image_name)
|
||
|
self.imagefile = tempfile.NamedTemporaryFile(delete=False)
|
||
|
data = b'\2\0'
|
||
|
self.imagefile.write(data)
|
||
|
self.imagefile.close()
|
||
|
self.output = data
|
||
|
self.fake_image_dict = fakes.make_fake_image(
|
||
|
image_id=self.image_id,
|
||
|
image_name=self.image_name,
|
||
|
data=self.imagefile.name,
|
||
|
)
|
||
|
self.fake_search_return = {'images': [self.fake_image_dict]}
|
||
|
self.container_name = self.getUniqueString('container')
|
||
|
|
||
|
def _compare_images(self, exp, real):
|
||
|
self.assertDictEqual(
|
||
|
image.Image(**exp).to_dict(computed=False),
|
||
|
real.to_dict(computed=False),
|
||
|
)
|
||
|
|
||
|
def _compare_images_v1(self, exp, real):
|
||
|
self.assertDictEqual(
|
||
|
image_v1.Image(**exp).to_dict(computed=False),
|
||
|
real.to_dict(computed=False),
|
||
|
)
|
||
|
|
||
|
|
||
|
class TestImage(BaseTestImage):
|
||
|
def setUp(self):
|
||
|
super(TestImage, self).setUp()
|
||
|
self.use_glance()
|
||
|
|
||
|
def test_config_v1(self):
|
||
|
self.cloud.config.config['image_api_version'] = '1'
|
||
|
# We override the scheme of the endpoint with the scheme of the service
|
||
|
# because glance has a bug where it doesn't return https properly.
|
||
|
self.assertEqual(
|
||
|
'https://image.example.com/v1/',
|
||
|
self.cloud._image_client.get_endpoint(),
|
||
|
)
|
||
|
self.assertEqual('1', self.cloud_config.get_api_version('image'))
|
||
|
|
||
|
def test_config_v2(self):
|
||
|
self.cloud.config.config['image_api_version'] = '2'
|
||
|
# We override the scheme of the endpoint with the scheme of the service
|
||
|
# because glance has a bug where it doesn't return https properly.
|
||
|
self.assertEqual(
|
||
|
'https://image.example.com/v2/',
|
||
|
self.cloud._image_client.get_endpoint(),
|
||
|
)
|
||
|
self.assertEqual('2', self.cloud_config.get_api_version('image'))
|
||
|
|
||
|
def test_download_image_no_output(self):
|
||
|
self.assertRaises(
|
||
|
exc.OpenStackCloudException,
|
||
|
self.cloud.download_image,
|
||
|
self.image_name,
|
||
|
)
|
||
|
|
||
|
def test_download_image_two_outputs(self):
|
||
|
fake_fd = io.BytesIO()
|
||
|
self.assertRaises(
|
||
|
exc.OpenStackCloudException,
|
||
|
self.cloud.download_image,
|
||
|
self.image_name,
|
||
|
output_path='fake_path',
|
||
|
output_file=fake_fd,
|
||
|
)
|
||
|
|
||
|
def test_download_image_no_images_found(self):
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri='https://image.example.com/v2/images/{name}'.format(
|
||
|
name=self.image_name
|
||
|
),
|
||
|
status_code=404,
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri='https://image.example.com/v2/images?name={name}'.format( # noqa: E501
|
||
|
name=self.image_name
|
||
|
),
|
||
|
json=dict(images=[]),
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri='https://image.example.com/v2/images?os_hidden=True',
|
||
|
json=dict(images=[]),
|
||
|
),
|
||
|
]
|
||
|
)
|
||
|
self.assertRaises(
|
||
|
exc.OpenStackCloudResourceNotFound,
|
||
|
self.cloud.download_image,
|
||
|
self.image_name,
|
||
|
output_path='fake_path',
|
||
|
)
|
||
|
self.assert_calls()
|
||
|
|
||
|
def _register_image_mocks(self):
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri='https://image.example.com/v2/images/{name}'.format(
|
||
|
name=self.image_name
|
||
|
),
|
||
|
status_code=404,
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri='https://image.example.com/v2/images?name={name}'.format( # noqa: E501
|
||
|
name=self.image_name
|
||
|
),
|
||
|
json=self.fake_search_return,
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri='https://image.example.com/v2/images/{id}/file'.format(
|
||
|
id=self.image_id
|
||
|
),
|
||
|
content=self.output,
|
||
|
headers={
|
||
|
'Content-Type': 'application/octet-stream',
|
||
|
'Content-MD5': self.fake_image_dict['checksum'],
|
||
|
},
|
||
|
),
|
||
|
]
|
||
|
)
|
||
|
|
||
|
def test_download_image_with_fd(self):
|
||
|
self._register_image_mocks()
|
||
|
output_file = io.BytesIO()
|
||
|
self.cloud.download_image(self.image_name, output_file=output_file)
|
||
|
output_file.seek(0)
|
||
|
self.assertEqual(output_file.read(), self.output)
|
||
|
self.assert_calls()
|
||
|
|
||
|
def test_download_image_with_path(self):
|
||
|
self._register_image_mocks()
|
||
|
output_file = tempfile.NamedTemporaryFile()
|
||
|
self.cloud.download_image(
|
||
|
self.image_name, output_path=output_file.name
|
||
|
)
|
||
|
output_file.seek(0)
|
||
|
self.assertEqual(output_file.read(), self.output)
|
||
|
self.assert_calls()
|
||
|
|
||
|
def test_get_image_name(self, cloud=None):
|
||
|
cloud = cloud or self.cloud
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image', append=['images'], base_url_append='v2'
|
||
|
),
|
||
|
json=self.fake_search_return,
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image', append=['images'], base_url_append='v2'
|
||
|
),
|
||
|
json=self.fake_search_return,
|
||
|
),
|
||
|
]
|
||
|
)
|
||
|
|
||
|
self.assertEqual(self.image_name, cloud.get_image_name(self.image_id))
|
||
|
self.assertEqual(
|
||
|
self.image_name, cloud.get_image_name(self.image_name)
|
||
|
)
|
||
|
|
||
|
self.assert_calls()
|
||
|
|
||
|
def test_get_image_by_id(self):
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image',
|
||
|
append=['images', self.image_id],
|
||
|
base_url_append='v2',
|
||
|
),
|
||
|
json=self.fake_image_dict,
|
||
|
)
|
||
|
]
|
||
|
)
|
||
|
self._compare_images(
|
||
|
self.fake_image_dict, self.cloud.get_image_by_id(self.image_id)
|
||
|
)
|
||
|
self.assert_calls()
|
||
|
|
||
|
def test_get_image_id(self, cloud=None):
|
||
|
cloud = cloud or self.cloud
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image', append=['images'], base_url_append='v2'
|
||
|
),
|
||
|
json=self.fake_search_return,
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image', append=['images'], base_url_append='v2'
|
||
|
),
|
||
|
json=self.fake_search_return,
|
||
|
),
|
||
|
]
|
||
|
)
|
||
|
|
||
|
self.assertEqual(self.image_id, cloud.get_image_id(self.image_id))
|
||
|
self.assertEqual(self.image_id, cloud.get_image_id(self.image_name))
|
||
|
|
||
|
self.assert_calls()
|
||
|
|
||
|
def test_get_image_name_operator(self):
|
||
|
# This should work the same as non-operator, just verifying it does.
|
||
|
self.test_get_image_name(cloud=self.cloud)
|
||
|
|
||
|
def test_get_image_id_operator(self):
|
||
|
# This should work the same as the other test, just verifying it does.
|
||
|
self.test_get_image_id(cloud=self.cloud)
|
||
|
|
||
|
def test_empty_list_images(self):
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image', append=['images'], base_url_append='v2'
|
||
|
),
|
||
|
json={'images': []},
|
||
|
)
|
||
|
]
|
||
|
)
|
||
|
self.assertEqual([], self.cloud.list_images())
|
||
|
self.assert_calls()
|
||
|
|
||
|
def test_list_images(self):
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image', append=['images'], base_url_append='v2'
|
||
|
),
|
||
|
json=self.fake_search_return,
|
||
|
)
|
||
|
]
|
||
|
)
|
||
|
[
|
||
|
self._compare_images(a, b)
|
||
|
for a, b in zip([self.fake_image_dict], self.cloud.list_images())
|
||
|
]
|
||
|
self.assert_calls()
|
||
|
|
||
|
def test_list_images_show_all(self):
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image',
|
||
|
append=['images'],
|
||
|
base_url_append='v2',
|
||
|
qs_elements=['member_status=all'],
|
||
|
),
|
||
|
json=self.fake_search_return,
|
||
|
)
|
||
|
]
|
||
|
)
|
||
|
[
|
||
|
self._compare_images(a, b)
|
||
|
for a, b in zip(
|
||
|
[self.fake_image_dict], self.cloud.list_images(show_all=True)
|
||
|
)
|
||
|
]
|
||
|
self.assert_calls()
|
||
|
|
||
|
def test_list_images_show_all_deleted(self):
|
||
|
deleted_image = self.fake_image_dict.copy()
|
||
|
deleted_image['status'] = 'deleted'
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image',
|
||
|
append=['images'],
|
||
|
base_url_append='v2',
|
||
|
qs_elements=['member_status=all'],
|
||
|
),
|
||
|
json={'images': [self.fake_image_dict, deleted_image]},
|
||
|
)
|
||
|
]
|
||
|
)
|
||
|
[
|
||
|
self._compare_images(a, b)
|
||
|
for a, b in zip(
|
||
|
[self.fake_image_dict], self.cloud.list_images(show_all=True)
|
||
|
)
|
||
|
]
|
||
|
self.assert_calls()
|
||
|
|
||
|
def test_list_images_no_filter_deleted(self):
|
||
|
deleted_image = self.fake_image_dict.copy()
|
||
|
deleted_image['status'] = 'deleted'
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image', append=['images'], base_url_append='v2'
|
||
|
),
|
||
|
json={'images': [self.fake_image_dict, deleted_image]},
|
||
|
)
|
||
|
]
|
||
|
)
|
||
|
[
|
||
|
self._compare_images(a, b)
|
||
|
for a, b in zip(
|
||
|
[self.fake_image_dict],
|
||
|
self.cloud.list_images(filter_deleted=False),
|
||
|
)
|
||
|
]
|
||
|
self.assert_calls()
|
||
|
|
||
|
def test_list_images_filter_deleted(self):
|
||
|
deleted_image = self.fake_image_dict.copy()
|
||
|
deleted_image['status'] = 'deleted'
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image', append=['images'], base_url_append='v2'
|
||
|
),
|
||
|
json={'images': [self.fake_image_dict, deleted_image]},
|
||
|
)
|
||
|
]
|
||
|
)
|
||
|
[
|
||
|
self._compare_images(a, b)
|
||
|
for a, b in zip([self.fake_image_dict], self.cloud.list_images())
|
||
|
]
|
||
|
self.assert_calls()
|
||
|
|
||
|
def test_list_images_string_properties(self):
|
||
|
image_dict = self.fake_image_dict.copy()
|
||
|
image_dict['properties'] = 'list,of,properties'
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image', append=['images'], base_url_append='v2'
|
||
|
),
|
||
|
json={'images': [image_dict]},
|
||
|
),
|
||
|
]
|
||
|
)
|
||
|
images = self.cloud.list_images()
|
||
|
[self._compare_images(a, b) for a, b in zip([image_dict], images)]
|
||
|
|
||
|
self.assertEqual(
|
||
|
images[0]['properties']['properties'], 'list,of,properties'
|
||
|
)
|
||
|
self.assert_calls()
|
||
|
|
||
|
def test_list_images_paginated(self):
|
||
|
marker = str(uuid.uuid4())
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image', append=['images'], base_url_append='v2'
|
||
|
),
|
||
|
json={
|
||
|
'images': [self.fake_image_dict],
|
||
|
'next': '/v2/images?marker={marker}'.format(
|
||
|
marker=marker
|
||
|
),
|
||
|
},
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image',
|
||
|
append=['images'],
|
||
|
base_url_append='v2',
|
||
|
qs_elements=['marker={marker}'.format(marker=marker)],
|
||
|
),
|
||
|
json=self.fake_search_return,
|
||
|
),
|
||
|
]
|
||
|
)
|
||
|
[
|
||
|
self._compare_images(a, b)
|
||
|
for a, b in zip([self.fake_image_dict], self.cloud.list_images())
|
||
|
]
|
||
|
self.assert_calls()
|
||
|
|
||
|
def test_create_image_put_v2_no_import(self):
|
||
|
self.cloud.image_api_use_tasks = False
|
||
|
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image',
|
||
|
append=['images', self.image_name],
|
||
|
base_url_append='v2',
|
||
|
),
|
||
|
status_code=404,
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image',
|
||
|
append=['images'],
|
||
|
base_url_append='v2',
|
||
|
qs_elements=['name=' + self.image_name],
|
||
|
),
|
||
|
validate=dict(),
|
||
|
json={'images': []},
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image',
|
||
|
append=['images'],
|
||
|
base_url_append='v2',
|
||
|
qs_elements=['os_hidden=True'],
|
||
|
),
|
||
|
json={'images': []},
|
||
|
),
|
||
|
dict(
|
||
|
method='POST',
|
||
|
uri=self.get_mock_url(
|
||
|
'image', append=['images'], base_url_append='v2'
|
||
|
),
|
||
|
json=self.fake_image_dict,
|
||
|
validate=dict(
|
||
|
json={
|
||
|
'container_format': 'bare',
|
||
|
'disk_format': 'qcow2',
|
||
|
'name': self.image_name,
|
||
|
'owner_specified.openstack.md5': self.fake_image_dict[ # noqa: E501
|
||
|
'owner_specified.openstack.md5'
|
||
|
],
|
||
|
'owner_specified.openstack.object': self.object_name, # noqa: E501
|
||
|
'owner_specified.openstack.sha256': self.fake_image_dict[ # noqa: E501
|
||
|
'owner_specified.openstack.sha256'
|
||
|
],
|
||
|
'visibility': 'private',
|
||
|
'tags': ['tag1', 'tag2'],
|
||
|
}
|
||
|
),
|
||
|
),
|
||
|
dict(
|
||
|
method='PUT',
|
||
|
uri=self.get_mock_url(
|
||
|
'image',
|
||
|
append=['images', self.image_id, 'file'],
|
||
|
base_url_append='v2',
|
||
|
),
|
||
|
request_headers={
|
||
|
'Content-Type': 'application/octet-stream'
|
||
|
},
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image',
|
||
|
append=['images', self.fake_image_dict['id']],
|
||
|
base_url_append='v2',
|
||
|
),
|
||
|
json=self.fake_image_dict,
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image', append=['images'], base_url_append='v2'
|
||
|
),
|
||
|
complete_qs=True,
|
||
|
json=self.fake_search_return,
|
||
|
),
|
||
|
]
|
||
|
)
|
||
|
|
||
|
self.cloud.create_image(
|
||
|
self.image_name,
|
||
|
self.imagefile.name,
|
||
|
wait=True,
|
||
|
timeout=1,
|
||
|
tags=['tag1', 'tag2'],
|
||
|
is_public=False,
|
||
|
validate_checksum=True,
|
||
|
)
|
||
|
|
||
|
self.assert_calls()
|
||
|
self.assertEqual(
|
||
|
self.adapter.request_history[7].text.read(), self.output
|
||
|
)
|
||
|
|
||
|
def test_create_image_put_v2_import_supported(self):
|
||
|
self.cloud.image_api_use_tasks = False
|
||
|
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image',
|
||
|
append=['images', self.image_name],
|
||
|
base_url_append='v2',
|
||
|
),
|
||
|
status_code=404,
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image',
|
||
|
append=['images'],
|
||
|
base_url_append='v2',
|
||
|
qs_elements=['name=' + self.image_name],
|
||
|
),
|
||
|
validate=dict(),
|
||
|
json={'images': []},
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image',
|
||
|
append=['images'],
|
||
|
base_url_append='v2',
|
||
|
qs_elements=['os_hidden=True'],
|
||
|
),
|
||
|
json={'images': []},
|
||
|
),
|
||
|
dict(
|
||
|
method='POST',
|
||
|
uri=self.get_mock_url(
|
||
|
'image', append=['images'], base_url_append='v2'
|
||
|
),
|
||
|
json=self.fake_image_dict,
|
||
|
headers={
|
||
|
'OpenStack-image-import-methods': IMPORT_METHODS,
|
||
|
},
|
||
|
validate=dict(
|
||
|
json={
|
||
|
'container_format': 'bare',
|
||
|
'disk_format': 'qcow2',
|
||
|
'name': self.image_name,
|
||
|
'owner_specified.openstack.md5': self.fake_image_dict[ # noqa: E501
|
||
|
'owner_specified.openstack.md5'
|
||
|
],
|
||
|
'owner_specified.openstack.object': self.object_name, # noqa: E501
|
||
|
'owner_specified.openstack.sha256': self.fake_image_dict[ # noqa: E501
|
||
|
'owner_specified.openstack.sha256'
|
||
|
],
|
||
|
'visibility': 'private',
|
||
|
'tags': ['tag1', 'tag2'],
|
||
|
}
|
||
|
),
|
||
|
),
|
||
|
dict(
|
||
|
method='PUT',
|
||
|
uri=self.get_mock_url(
|
||
|
'image',
|
||
|
append=['images', self.image_id, 'file'],
|
||
|
base_url_append='v2',
|
||
|
),
|
||
|
request_headers={
|
||
|
'Content-Type': 'application/octet-stream'
|
||
|
},
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image',
|
||
|
append=['images', self.fake_image_dict['id']],
|
||
|
base_url_append='v2',
|
||
|
),
|
||
|
json=self.fake_image_dict,
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image', append=['images'], base_url_append='v2'
|
||
|
),
|
||
|
complete_qs=True,
|
||
|
json=self.fake_search_return,
|
||
|
),
|
||
|
]
|
||
|
)
|
||
|
|
||
|
self.cloud.create_image(
|
||
|
self.image_name,
|
||
|
self.imagefile.name,
|
||
|
wait=True,
|
||
|
timeout=1,
|
||
|
tags=['tag1', 'tag2'],
|
||
|
is_public=False,
|
||
|
validate_checksum=True,
|
||
|
)
|
||
|
|
||
|
self.assert_calls()
|
||
|
self.assertEqual(
|
||
|
self.adapter.request_history[7].text.read(), self.output
|
||
|
)
|
||
|
|
||
|
def test_create_image_use_import(self):
|
||
|
self.cloud.image_api_use_tasks = False
|
||
|
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image',
|
||
|
append=['images', self.image_name],
|
||
|
base_url_append='v2',
|
||
|
),
|
||
|
status_code=404,
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image',
|
||
|
append=['images'],
|
||
|
base_url_append='v2',
|
||
|
qs_elements=['name=' + self.image_name],
|
||
|
),
|
||
|
validate=dict(),
|
||
|
json={'images': []},
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image',
|
||
|
append=['images'],
|
||
|
base_url_append='v2',
|
||
|
qs_elements=['os_hidden=True'],
|
||
|
),
|
||
|
json={'images': []},
|
||
|
),
|
||
|
dict(
|
||
|
method='POST',
|
||
|
uri=self.get_mock_url(
|
||
|
'image', append=['images'], base_url_append='v2'
|
||
|
),
|
||
|
json=self.fake_image_dict,
|
||
|
headers={
|
||
|
'OpenStack-image-import-methods': IMPORT_METHODS,
|
||
|
},
|
||
|
validate=dict(
|
||
|
json={
|
||
|
'container_format': 'bare',
|
||
|
'disk_format': 'qcow2',
|
||
|
'name': self.image_name,
|
||
|
'owner_specified.openstack.md5': self.fake_image_dict[ # noqa: E501
|
||
|
'owner_specified.openstack.md5'
|
||
|
],
|
||
|
'owner_specified.openstack.object': self.object_name, # noqa: E501
|
||
|
'owner_specified.openstack.sha256': self.fake_image_dict[ # noqa: E501
|
||
|
'owner_specified.openstack.sha256'
|
||
|
],
|
||
|
'visibility': 'private',
|
||
|
'tags': ['tag1', 'tag2'],
|
||
|
}
|
||
|
),
|
||
|
),
|
||
|
dict(
|
||
|
method='PUT',
|
||
|
uri=self.get_mock_url(
|
||
|
'image',
|
||
|
append=['images', self.image_id, 'stage'],
|
||
|
base_url_append='v2',
|
||
|
),
|
||
|
request_headers={
|
||
|
'Content-Type': 'application/octet-stream'
|
||
|
},
|
||
|
),
|
||
|
dict(
|
||
|
method='POST',
|
||
|
uri=self.get_mock_url(
|
||
|
'image',
|
||
|
append=['images', self.image_id, 'import'],
|
||
|
base_url_append='v2',
|
||
|
),
|
||
|
json={'method': {'name': 'glance-direct'}},
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image',
|
||
|
append=['images', self.fake_image_dict['id']],
|
||
|
base_url_append='v2',
|
||
|
),
|
||
|
json=self.fake_image_dict,
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image', append=['images'], base_url_append='v2'
|
||
|
),
|
||
|
complete_qs=True,
|
||
|
json=self.fake_search_return,
|
||
|
),
|
||
|
]
|
||
|
)
|
||
|
|
||
|
self.cloud.create_image(
|
||
|
self.image_name,
|
||
|
self.imagefile.name,
|
||
|
wait=True,
|
||
|
timeout=1,
|
||
|
tags=['tag1', 'tag2'],
|
||
|
is_public=False,
|
||
|
validate_checksum=True,
|
||
|
use_import=True,
|
||
|
)
|
||
|
|
||
|
self.assert_calls()
|
||
|
self.assertEqual(
|
||
|
self.adapter.request_history[7].text.read(), self.output
|
||
|
)
|
||
|
|
||
|
def test_create_image_task(self):
|
||
|
self.cloud.image_api_use_tasks = True
|
||
|
endpoint = self.cloud._object_store_client.get_endpoint()
|
||
|
|
||
|
task_id = str(uuid.uuid4())
|
||
|
args = dict(
|
||
|
id=task_id,
|
||
|
status='success',
|
||
|
type='import',
|
||
|
result={
|
||
|
'image_id': self.image_id,
|
||
|
},
|
||
|
)
|
||
|
|
||
|
image_no_checksums = self.fake_image_dict.copy()
|
||
|
del image_no_checksums['owner_specified.openstack.md5']
|
||
|
del image_no_checksums['owner_specified.openstack.sha256']
|
||
|
del image_no_checksums['owner_specified.openstack.object']
|
||
|
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image',
|
||
|
append=['images', self.image_name],
|
||
|
base_url_append='v2',
|
||
|
),
|
||
|
status_code=404,
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image',
|
||
|
append=['images'],
|
||
|
base_url_append='v2',
|
||
|
qs_elements=['name=' + self.image_name],
|
||
|
),
|
||
|
validate=dict(),
|
||
|
json={'images': []},
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image',
|
||
|
append=['images'],
|
||
|
base_url_append='v2',
|
||
|
qs_elements=['os_hidden=True'],
|
||
|
),
|
||
|
json={'images': []},
|
||
|
),
|
||
|
dict(
|
||
|
method='HEAD',
|
||
|
uri='{endpoint}/{container}'.format(
|
||
|
endpoint=endpoint, container=self.container_name
|
||
|
),
|
||
|
status_code=404,
|
||
|
),
|
||
|
dict(
|
||
|
method='PUT',
|
||
|
uri='{endpoint}/{container}'.format(
|
||
|
endpoint=endpoint, container=self.container_name
|
||
|
),
|
||
|
status_code=201,
|
||
|
headers={
|
||
|
'Date': 'Fri, 16 Dec 2016 18:21:20 GMT',
|
||
|
'Content-Length': '0',
|
||
|
'Content-Type': 'text/html; charset=UTF-8',
|
||
|
},
|
||
|
),
|
||
|
dict(
|
||
|
method='HEAD',
|
||
|
uri='{endpoint}/{container}'.format(
|
||
|
endpoint=endpoint, container=self.container_name
|
||
|
),
|
||
|
headers={
|
||
|
'Content-Length': '0',
|
||
|
'X-Container-Object-Count': '0',
|
||
|
'Accept-Ranges': 'bytes',
|
||
|
'X-Storage-Policy': 'Policy-0',
|
||
|
'Date': 'Fri, 16 Dec 2016 18:29:05 GMT',
|
||
|
'X-Timestamp': '1481912480.41664',
|
||
|
'X-Trans-Id': 'tx60ec128d9dbf44b9add68-0058543271dfw1',
|
||
|
'X-Container-Bytes-Used': '0',
|
||
|
'Content-Type': 'text/plain; charset=utf-8',
|
||
|
},
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
# This is explicitly not using get_mock_url because that
|
||
|
# gets us a project-id oriented URL.
|
||
|
uri='https://object-store.example.com/info',
|
||
|
json=dict(
|
||
|
swift={'max_file_size': 1000},
|
||
|
slo={'min_segment_size': 500},
|
||
|
),
|
||
|
),
|
||
|
dict(
|
||
|
method='HEAD',
|
||
|
uri='{endpoint}/{container}/{object}'.format(
|
||
|
endpoint=endpoint,
|
||
|
container=self.container_name,
|
||
|
object=self.image_name,
|
||
|
),
|
||
|
status_code=404,
|
||
|
),
|
||
|
dict(
|
||
|
method='PUT',
|
||
|
uri='{endpoint}/{container}/{object}'.format(
|
||
|
endpoint=endpoint,
|
||
|
container=self.container_name,
|
||
|
object=self.image_name,
|
||
|
),
|
||
|
status_code=201,
|
||
|
validate=dict(
|
||
|
headers={
|
||
|
'X-Object-Meta-x-sdk-md5': self.fake_image_dict[
|
||
|
'owner_specified.openstack.md5'
|
||
|
],
|
||
|
'X-Object-Meta-x-sdk-sha256': self.fake_image_dict[
|
||
|
'owner_specified.openstack.sha256'
|
||
|
],
|
||
|
}
|
||
|
),
|
||
|
),
|
||
|
dict(
|
||
|
method='POST',
|
||
|
uri=self.get_mock_url(
|
||
|
'image', append=['tasks'], base_url_append='v2'
|
||
|
),
|
||
|
json={'id': task_id, 'status': 'processing'},
|
||
|
validate=dict(
|
||
|
json=dict(
|
||
|
type='import',
|
||
|
input={
|
||
|
'import_from': '{container}/{object}'.format(
|
||
|
container=self.container_name,
|
||
|
object=self.image_name,
|
||
|
),
|
||
|
'image_properties': {'name': self.image_name},
|
||
|
},
|
||
|
)
|
||
|
),
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image',
|
||
|
append=['tasks', task_id],
|
||
|
base_url_append='v2',
|
||
|
),
|
||
|
status_code=503,
|
||
|
text='Random error',
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image',
|
||
|
append=['tasks', task_id],
|
||
|
base_url_append='v2',
|
||
|
),
|
||
|
json=args,
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image',
|
||
|
append=['images', self.image_id],
|
||
|
base_url_append='v2',
|
||
|
),
|
||
|
json=image_no_checksums,
|
||
|
),
|
||
|
dict(
|
||
|
method='PATCH',
|
||
|
uri=self.get_mock_url(
|
||
|
'image',
|
||
|
append=['images', self.image_id],
|
||
|
base_url_append='v2',
|
||
|
),
|
||
|
validate=dict(
|
||
|
json=sorted(
|
||
|
[
|
||
|
{
|
||
|
'op': 'add',
|
||
|
'value': '{container}/{object}'.format(
|
||
|
container=self.container_name,
|
||
|
object=self.image_name,
|
||
|
),
|
||
|
'path': '/owner_specified.openstack.object', # noqa: E501
|
||
|
},
|
||
|
{
|
||
|
'op': 'add',
|
||
|
'value': self.fake_image_dict[
|
||
|
'owner_specified.openstack.md5'
|
||
|
],
|
||
|
'path': '/owner_specified.openstack.md5',
|
||
|
},
|
||
|
{
|
||
|
'op': 'add',
|
||
|
'value': self.fake_image_dict[
|
||
|
'owner_specified.openstack.sha256'
|
||
|
],
|
||
|
'path': '/owner_specified.openstack.sha256', # noqa: E501
|
||
|
},
|
||
|
],
|
||
|
key=operator.itemgetter('path'),
|
||
|
),
|
||
|
headers={
|
||
|
'Content-Type': 'application/openstack-images-v2.1-json-patch' # noqa: E501
|
||
|
},
|
||
|
),
|
||
|
json=self.fake_search_return,
|
||
|
),
|
||
|
dict(
|
||
|
method='HEAD',
|
||
|
uri='{endpoint}/{container}/{object}'.format(
|
||
|
endpoint=endpoint,
|
||
|
container=self.container_name,
|
||
|
object=self.image_name,
|
||
|
),
|
||
|
headers={
|
||
|
'X-Timestamp': '1429036140.50253',
|
||
|
'X-Trans-Id': 'txbbb825960a3243b49a36f-005a0dadaedfw1',
|
||
|
'Content-Length': '1290170880',
|
||
|
'Last-Modified': 'Tue, 14 Apr 2015 18:29:01 GMT',
|
||
|
'X-Object-Meta-X-Sdk-Sha256': self.fake_image_dict[
|
||
|
'owner_specified.openstack.sha256'
|
||
|
],
|
||
|
'X-Object-Meta-X-Sdk-Md5': self.fake_image_dict[
|
||
|
'owner_specified.openstack.md5'
|
||
|
],
|
||
|
'Date': 'Thu, 16 Nov 2017 15:24:30 GMT',
|
||
|
'Accept-Ranges': 'bytes',
|
||
|
'Content-Type': 'application/octet-stream',
|
||
|
'Etag': fakes.NO_MD5,
|
||
|
},
|
||
|
),
|
||
|
dict(
|
||
|
method='DELETE',
|
||
|
uri='{endpoint}/{container}/{object}'.format(
|
||
|
endpoint=endpoint,
|
||
|
container=self.container_name,
|
||
|
object=self.image_name,
|
||
|
),
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image', append=['images'], base_url_append='v2'
|
||
|
),
|
||
|
complete_qs=True,
|
||
|
json=self.fake_search_return,
|
||
|
),
|
||
|
]
|
||
|
)
|
||
|
|
||
|
self.cloud.create_image(
|
||
|
self.image_name,
|
||
|
self.imagefile.name,
|
||
|
wait=True,
|
||
|
timeout=1,
|
||
|
disk_format='vhd',
|
||
|
container_format='ovf',
|
||
|
is_public=False,
|
||
|
validate_checksum=True,
|
||
|
container=self.container_name,
|
||
|
)
|
||
|
|
||
|
self.assert_calls()
|
||
|
|
||
|
def test_delete_autocreated_no_tasks(self):
|
||
|
self.use_keystone_v3()
|
||
|
self.cloud.image_api_use_tasks = False
|
||
|
deleted = self.cloud.delete_autocreated_image_objects(
|
||
|
container=self.container_name
|
||
|
)
|
||
|
self.assertFalse(deleted)
|
||
|
self.assert_calls([])
|
||
|
|
||
|
def test_delete_image_task(self):
|
||
|
self.cloud.image_api_use_tasks = True
|
||
|
endpoint = self.cloud._object_store_client.get_endpoint()
|
||
|
|
||
|
object_path = self.fake_image_dict['owner_specified.openstack.object']
|
||
|
|
||
|
image_no_checksums = self.fake_image_dict.copy()
|
||
|
del image_no_checksums['owner_specified.openstack.md5']
|
||
|
del image_no_checksums['owner_specified.openstack.sha256']
|
||
|
del image_no_checksums['owner_specified.openstack.object']
|
||
|
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image', append=['images'], base_url_append='v2'
|
||
|
),
|
||
|
json=self.fake_search_return,
|
||
|
),
|
||
|
dict(
|
||
|
method='DELETE',
|
||
|
uri='https://image.example.com/v2/images/{id}'.format(
|
||
|
id=self.image_id
|
||
|
),
|
||
|
),
|
||
|
dict(
|
||
|
method='HEAD',
|
||
|
uri='{endpoint}/{object}'.format(
|
||
|
endpoint=endpoint, object=object_path
|
||
|
),
|
||
|
headers={
|
||
|
'X-Timestamp': '1429036140.50253',
|
||
|
'X-Trans-Id': 'txbbb825960a3243b49a36f-005a0dadaedfw1',
|
||
|
'Content-Length': '1290170880',
|
||
|
'Last-Modified': 'Tue, 14 Apr 2015 18:29:01 GMT',
|
||
|
'X-Object-Meta-X-Sdk-Sha256': self.fake_image_dict[
|
||
|
'owner_specified.openstack.sha256'
|
||
|
],
|
||
|
'X-Object-Meta-X-Sdk-Md5': self.fake_image_dict[
|
||
|
'owner_specified.openstack.md5'
|
||
|
],
|
||
|
'Date': 'Thu, 16 Nov 2017 15:24:30 GMT',
|
||
|
'Accept-Ranges': 'bytes',
|
||
|
'Content-Type': 'application/octet-stream',
|
||
|
'Etag': fakes.NO_MD5,
|
||
|
},
|
||
|
),
|
||
|
dict(
|
||
|
method='DELETE',
|
||
|
uri='{endpoint}/{object}'.format(
|
||
|
endpoint=endpoint, object=object_path
|
||
|
),
|
||
|
),
|
||
|
]
|
||
|
)
|
||
|
|
||
|
self.cloud.delete_image(self.image_id)
|
||
|
|
||
|
self.assert_calls()
|
||
|
|
||
|
def test_delete_autocreated_image_objects(self):
|
||
|
self.use_keystone_v3()
|
||
|
self.cloud.image_api_use_tasks = True
|
||
|
endpoint = self.cloud._object_store_client.get_endpoint()
|
||
|
other_image = self.getUniqueString('no-delete')
|
||
|
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
service_type='object-store',
|
||
|
resource=self.container_name,
|
||
|
qs_elements=['format=json'],
|
||
|
),
|
||
|
json=[
|
||
|
{
|
||
|
'content_type': 'application/octet-stream',
|
||
|
'bytes': 1437258240,
|
||
|
'hash': '249219347276c331b87bf1ac2152d9af',
|
||
|
'last_modified': '2015-02-16T17:50:05.289600',
|
||
|
'name': other_image,
|
||
|
},
|
||
|
{
|
||
|
'content_type': 'application/octet-stream',
|
||
|
'bytes': 1290170880,
|
||
|
'hash': fakes.NO_MD5,
|
||
|
'last_modified': '2015-04-14T18:29:00.502530',
|
||
|
'name': self.image_name,
|
||
|
},
|
||
|
],
|
||
|
),
|
||
|
dict(
|
||
|
method='HEAD',
|
||
|
uri=self.get_mock_url(
|
||
|
service_type='object-store',
|
||
|
resource=self.container_name,
|
||
|
append=[other_image],
|
||
|
),
|
||
|
headers={
|
||
|
'X-Timestamp': '1429036140.50253',
|
||
|
'X-Trans-Id': 'txbbb825960a3243b49a36f-005a0dadaedfw1',
|
||
|
'Content-Length': '1290170880',
|
||
|
'Last-Modified': 'Tue, 14 Apr 2015 18:29:01 GMT',
|
||
|
'X-Object-Meta-X-Shade-Sha256': 'does not matter',
|
||
|
'X-Object-Meta-X-Shade-Md5': 'does not matter',
|
||
|
'Date': 'Thu, 16 Nov 2017 15:24:30 GMT',
|
||
|
'Accept-Ranges': 'bytes',
|
||
|
'Content-Type': 'application/octet-stream',
|
||
|
'Etag': '249219347276c331b87bf1ac2152d9af',
|
||
|
},
|
||
|
),
|
||
|
dict(
|
||
|
method='HEAD',
|
||
|
uri=self.get_mock_url(
|
||
|
service_type='object-store',
|
||
|
resource=self.container_name,
|
||
|
append=[self.image_name],
|
||
|
),
|
||
|
headers={
|
||
|
'X-Timestamp': '1429036140.50253',
|
||
|
'X-Trans-Id': 'txbbb825960a3243b49a36f-005a0dadaedfw1',
|
||
|
'Content-Length': '1290170880',
|
||
|
'Last-Modified': 'Tue, 14 Apr 2015 18:29:01 GMT',
|
||
|
'X-Object-Meta-X-Shade-Sha256': fakes.NO_SHA256,
|
||
|
'X-Object-Meta-X-Shade-Md5': fakes.NO_MD5,
|
||
|
'Date': 'Thu, 16 Nov 2017 15:24:30 GMT',
|
||
|
'Accept-Ranges': 'bytes',
|
||
|
'Content-Type': 'application/octet-stream',
|
||
|
(
|
||
|
'X-Object-Meta-'
|
||
|
+ self.cloud._OBJECT_AUTOCREATE_KEY
|
||
|
): 'true',
|
||
|
'Etag': fakes.NO_MD5,
|
||
|
'X-Static-Large-Object': 'false',
|
||
|
},
|
||
|
),
|
||
|
dict(
|
||
|
method='DELETE',
|
||
|
uri='{endpoint}/{container}/{object}'.format(
|
||
|
endpoint=endpoint,
|
||
|
container=self.container_name,
|
||
|
object=self.image_name,
|
||
|
),
|
||
|
),
|
||
|
]
|
||
|
)
|
||
|
|
||
|
deleted = self.cloud.delete_autocreated_image_objects(
|
||
|
container=self.container_name
|
||
|
)
|
||
|
self.assertTrue(deleted)
|
||
|
|
||
|
self.assert_calls()
|
||
|
|
||
|
def _image_dict(self, fake_image):
|
||
|
return self.cloud._normalize_image(meta.obj_to_munch(fake_image))
|
||
|
|
||
|
def _call_create_image(self, name, **kwargs):
|
||
|
imagefile = tempfile.NamedTemporaryFile(delete=False)
|
||
|
imagefile.write(b'\0')
|
||
|
imagefile.close()
|
||
|
self.cloud.create_image(
|
||
|
name,
|
||
|
imagefile.name,
|
||
|
wait=True,
|
||
|
timeout=1,
|
||
|
is_public=False,
|
||
|
validate_checksum=True,
|
||
|
**kwargs
|
||
|
)
|
||
|
|
||
|
def test_create_image_put_v1(self):
|
||
|
self.cloud.config.config['image_api_version'] = '1'
|
||
|
|
||
|
args = {
|
||
|
'name': self.image_name,
|
||
|
'container_format': 'bare',
|
||
|
'disk_format': 'qcow2',
|
||
|
'properties': {
|
||
|
'owner_specified.openstack.md5': fakes.NO_MD5,
|
||
|
'owner_specified.openstack.sha256': fakes.NO_SHA256,
|
||
|
'owner_specified.openstack.object': 'images/{name}'.format(
|
||
|
name=self.image_name
|
||
|
),
|
||
|
'is_public': False,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
ret = args.copy()
|
||
|
ret['id'] = self.image_id
|
||
|
ret['status'] = 'success'
|
||
|
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri='https://image.example.com/v1/images/'
|
||
|
+ self.image_name,
|
||
|
status_code=404,
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri='https://image.example.com/v1/images/detail?name='
|
||
|
+ self.image_name,
|
||
|
json={'images': []},
|
||
|
),
|
||
|
dict(
|
||
|
method='POST',
|
||
|
uri='https://image.example.com/v1/images',
|
||
|
json={'image': ret},
|
||
|
validate=dict(json=args),
|
||
|
),
|
||
|
dict(
|
||
|
method='PUT',
|
||
|
uri='https://image.example.com/v1/images/{id}'.format(
|
||
|
id=self.image_id
|
||
|
),
|
||
|
json={'image': ret},
|
||
|
validate=dict(
|
||
|
headers={
|
||
|
'x-image-meta-checksum': fakes.NO_MD5,
|
||
|
'x-glance-registry-purge-props': 'false',
|
||
|
}
|
||
|
),
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri='https://image.example.com/v1/images/detail',
|
||
|
json={'images': [ret]},
|
||
|
),
|
||
|
]
|
||
|
)
|
||
|
self._call_create_image(self.image_name)
|
||
|
[
|
||
|
self._compare_images_v1(b, a)
|
||
|
for a, b in zip(self.cloud.list_images(), [ret])
|
||
|
]
|
||
|
|
||
|
def test_create_image_put_v1_bad_delete(self):
|
||
|
self.cloud.config.config['image_api_version'] = '1'
|
||
|
|
||
|
args = {
|
||
|
'name': self.image_name,
|
||
|
'container_format': 'bare',
|
||
|
'disk_format': 'qcow2',
|
||
|
'properties': {
|
||
|
'owner_specified.openstack.md5': fakes.NO_MD5,
|
||
|
'owner_specified.openstack.sha256': fakes.NO_SHA256,
|
||
|
'owner_specified.openstack.object': 'images/{name}'.format(
|
||
|
name=self.image_name
|
||
|
),
|
||
|
'is_public': False,
|
||
|
},
|
||
|
'validate_checksum': True,
|
||
|
}
|
||
|
|
||
|
ret = args.copy()
|
||
|
ret['id'] = self.image_id
|
||
|
ret['status'] = 'success'
|
||
|
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri='https://image.example.com/v1/images/'
|
||
|
+ self.image_name,
|
||
|
status_code=404,
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri='https://image.example.com/v1/images/detail?name='
|
||
|
+ self.image_name,
|
||
|
json={'images': []},
|
||
|
),
|
||
|
dict(
|
||
|
method='POST',
|
||
|
uri='https://image.example.com/v1/images',
|
||
|
json={'image': ret},
|
||
|
validate=dict(json=args),
|
||
|
),
|
||
|
dict(
|
||
|
method='PUT',
|
||
|
uri='https://image.example.com/v1/images/{id}'.format(
|
||
|
id=self.image_id
|
||
|
),
|
||
|
status_code=400,
|
||
|
validate=dict(
|
||
|
headers={
|
||
|
'x-image-meta-checksum': fakes.NO_MD5,
|
||
|
'x-glance-registry-purge-props': 'false',
|
||
|
}
|
||
|
),
|
||
|
),
|
||
|
dict(
|
||
|
method='DELETE',
|
||
|
uri='https://image.example.com/v1/images/{id}'.format(
|
||
|
id=self.image_id
|
||
|
),
|
||
|
json={'images': [ret]},
|
||
|
),
|
||
|
]
|
||
|
)
|
||
|
|
||
|
self.assertRaises(
|
||
|
exc.OpenStackCloudHTTPError,
|
||
|
self._call_create_image,
|
||
|
self.image_name,
|
||
|
)
|
||
|
|
||
|
self.assert_calls()
|
||
|
|
||
|
def test_update_image_no_patch(self):
|
||
|
self.cloud.image_api_use_tasks = False
|
||
|
|
||
|
args = {
|
||
|
'name': self.image_name,
|
||
|
'container_format': 'bare',
|
||
|
'disk_format': 'qcow2',
|
||
|
'owner_specified.openstack.md5': fakes.NO_MD5,
|
||
|
'owner_specified.openstack.sha256': fakes.NO_SHA256,
|
||
|
'owner_specified.openstack.object': 'images/{name}'.format(
|
||
|
name=self.image_name
|
||
|
),
|
||
|
'visibility': 'private',
|
||
|
}
|
||
|
|
||
|
ret = args.copy()
|
||
|
ret['id'] = self.image_id
|
||
|
ret['status'] = 'success'
|
||
|
|
||
|
self.cloud.update_image_properties(
|
||
|
image=image.Image.existing(**ret),
|
||
|
**{
|
||
|
'owner_specified.openstack.object': 'images/{name}'.format(
|
||
|
name=self.image_name
|
||
|
)
|
||
|
}
|
||
|
)
|
||
|
|
||
|
self.assert_calls()
|
||
|
|
||
|
def test_create_image_put_v2_bad_delete(self):
|
||
|
self.cloud.image_api_use_tasks = False
|
||
|
|
||
|
args = {
|
||
|
'name': self.image_name,
|
||
|
'container_format': 'bare',
|
||
|
'disk_format': 'qcow2',
|
||
|
'owner_specified.openstack.md5': fakes.NO_MD5,
|
||
|
'owner_specified.openstack.sha256': fakes.NO_SHA256,
|
||
|
'owner_specified.openstack.object': 'images/{name}'.format(
|
||
|
name=self.image_name
|
||
|
),
|
||
|
'visibility': 'private',
|
||
|
}
|
||
|
|
||
|
ret = args.copy()
|
||
|
ret['id'] = self.image_id
|
||
|
ret['status'] = 'success'
|
||
|
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image',
|
||
|
append=['images', self.image_name],
|
||
|
base_url_append='v2',
|
||
|
),
|
||
|
status_code=404,
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image',
|
||
|
append=['images'],
|
||
|
base_url_append='v2',
|
||
|
qs_elements=['name=' + self.image_name],
|
||
|
),
|
||
|
validate=dict(),
|
||
|
json={'images': []},
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image',
|
||
|
append=['images'],
|
||
|
base_url_append='v2',
|
||
|
qs_elements=['os_hidden=True'],
|
||
|
),
|
||
|
json={'images': []},
|
||
|
),
|
||
|
dict(
|
||
|
method='POST',
|
||
|
uri='https://image.example.com/v2/images',
|
||
|
json=ret,
|
||
|
validate=dict(json=args),
|
||
|
),
|
||
|
dict(
|
||
|
method='PUT',
|
||
|
uri='https://image.example.com/v2/images/{id}/file'.format(
|
||
|
id=self.image_id
|
||
|
),
|
||
|
status_code=400,
|
||
|
validate=dict(
|
||
|
headers={
|
||
|
'Content-Type': 'application/octet-stream',
|
||
|
},
|
||
|
),
|
||
|
),
|
||
|
dict(
|
||
|
method='DELETE',
|
||
|
uri='https://image.example.com/v2/images/{id}'.format(
|
||
|
id=self.image_id
|
||
|
),
|
||
|
),
|
||
|
]
|
||
|
)
|
||
|
|
||
|
self.assertRaises(
|
||
|
exc.OpenStackCloudHTTPError,
|
||
|
self._call_create_image,
|
||
|
self.image_name,
|
||
|
)
|
||
|
|
||
|
self.assert_calls()
|
||
|
|
||
|
def test_create_image_put_v2_wrong_checksum_delete(self):
|
||
|
self.cloud.image_api_use_tasks = False
|
||
|
|
||
|
fake_image = self.fake_image_dict
|
||
|
|
||
|
fake_image['owner_specified.openstack.md5'] = 'a'
|
||
|
fake_image['owner_specified.openstack.sha256'] = 'b'
|
||
|
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='POST',
|
||
|
uri=self.get_mock_url(
|
||
|
'image', append=['images'], base_url_append='v2'
|
||
|
),
|
||
|
json=self.fake_image_dict,
|
||
|
validate=dict(
|
||
|
json={
|
||
|
'container_format': 'bare',
|
||
|
'disk_format': 'qcow2',
|
||
|
'name': self.image_name,
|
||
|
'owner_specified.openstack.md5': fake_image[
|
||
|
'owner_specified.openstack.md5'
|
||
|
],
|
||
|
'owner_specified.openstack.object': self.object_name, # noqa: E501
|
||
|
'owner_specified.openstack.sha256': fake_image[
|
||
|
'owner_specified.openstack.sha256'
|
||
|
],
|
||
|
'visibility': 'private',
|
||
|
}
|
||
|
),
|
||
|
),
|
||
|
dict(
|
||
|
method='PUT',
|
||
|
uri=self.get_mock_url(
|
||
|
'image',
|
||
|
append=['images', self.image_id, 'file'],
|
||
|
base_url_append='v2',
|
||
|
),
|
||
|
request_headers={
|
||
|
'Content-Type': 'application/octet-stream'
|
||
|
},
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image',
|
||
|
append=['images', self.fake_image_dict['id']],
|
||
|
base_url_append='v2',
|
||
|
),
|
||
|
json=fake_image,
|
||
|
),
|
||
|
dict(
|
||
|
method='DELETE',
|
||
|
uri='https://image.example.com/v2/images/{id}'.format(
|
||
|
id=self.image_id
|
||
|
),
|
||
|
),
|
||
|
]
|
||
|
)
|
||
|
|
||
|
self.assertRaises(
|
||
|
exceptions.SDKException,
|
||
|
self.cloud.create_image,
|
||
|
self.image_name,
|
||
|
self.imagefile.name,
|
||
|
is_public=False,
|
||
|
md5='a',
|
||
|
sha256='b',
|
||
|
allow_duplicates=True,
|
||
|
validate_checksum=True,
|
||
|
)
|
||
|
|
||
|
self.assert_calls()
|
||
|
|
||
|
def test_create_image_put_bad_int(self):
|
||
|
self.cloud.image_api_use_tasks = False
|
||
|
|
||
|
self.assertRaises(
|
||
|
exc.OpenStackCloudException,
|
||
|
self._call_create_image,
|
||
|
self.image_name,
|
||
|
allow_duplicates=True,
|
||
|
min_disk='fish',
|
||
|
min_ram=0,
|
||
|
)
|
||
|
|
||
|
self.assert_calls()
|
||
|
|
||
|
def test_create_image_put_user_int(self):
|
||
|
self.cloud.image_api_use_tasks = False
|
||
|
|
||
|
args = {
|
||
|
'name': self.image_name,
|
||
|
'container_format': 'bare',
|
||
|
'disk_format': 'qcow2',
|
||
|
'owner_specified.openstack.md5': fakes.NO_MD5,
|
||
|
'owner_specified.openstack.sha256': fakes.NO_SHA256,
|
||
|
'owner_specified.openstack.object': 'images/{name}'.format(
|
||
|
name=self.image_name
|
||
|
),
|
||
|
'int_v': '12345',
|
||
|
'visibility': 'private',
|
||
|
'min_disk': 0,
|
||
|
'min_ram': 0,
|
||
|
}
|
||
|
|
||
|
ret = args.copy()
|
||
|
ret['id'] = self.image_id
|
||
|
ret['status'] = 'success'
|
||
|
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image',
|
||
|
append=['images', self.image_name],
|
||
|
base_url_append='v2',
|
||
|
),
|
||
|
status_code=404,
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image',
|
||
|
append=['images'],
|
||
|
base_url_append='v2',
|
||
|
qs_elements=['name=' + self.image_name],
|
||
|
),
|
||
|
validate=dict(),
|
||
|
json={'images': []},
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image',
|
||
|
append=['images'],
|
||
|
base_url_append='v2',
|
||
|
qs_elements=['os_hidden=True'],
|
||
|
),
|
||
|
json={'images': []},
|
||
|
),
|
||
|
dict(
|
||
|
method='POST',
|
||
|
uri='https://image.example.com/v2/images',
|
||
|
json=ret,
|
||
|
validate=dict(json=args),
|
||
|
),
|
||
|
dict(
|
||
|
method='PUT',
|
||
|
uri='https://image.example.com/v2/images/{id}/file'.format(
|
||
|
id=self.image_id
|
||
|
),
|
||
|
validate=dict(
|
||
|
headers={
|
||
|
'Content-Type': 'application/octet-stream',
|
||
|
},
|
||
|
),
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri='https://image.example.com/v2/images/{id}'.format(
|
||
|
id=self.image_id
|
||
|
),
|
||
|
json=ret,
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri='https://image.example.com/v2/images',
|
||
|
complete_qs=True,
|
||
|
json={'images': [ret]},
|
||
|
),
|
||
|
]
|
||
|
)
|
||
|
|
||
|
self._call_create_image(
|
||
|
self.image_name, min_disk='0', min_ram=0, int_v=12345
|
||
|
)
|
||
|
|
||
|
self.assert_calls()
|
||
|
|
||
|
def test_create_image_put_meta_int(self):
|
||
|
self.cloud.image_api_use_tasks = False
|
||
|
|
||
|
args = {
|
||
|
'name': self.image_name,
|
||
|
'container_format': 'bare',
|
||
|
'disk_format': 'qcow2',
|
||
|
'owner_specified.openstack.md5': fakes.NO_MD5,
|
||
|
'owner_specified.openstack.sha256': fakes.NO_SHA256,
|
||
|
'owner_specified.openstack.object': 'images/{name}'.format(
|
||
|
name=self.image_name
|
||
|
),
|
||
|
'int_v': 12345,
|
||
|
'visibility': 'private',
|
||
|
'min_disk': 0,
|
||
|
'min_ram': 0,
|
||
|
}
|
||
|
|
||
|
ret = args.copy()
|
||
|
ret['id'] = self.image_id
|
||
|
ret['status'] = 'success'
|
||
|
ret['checksum'] = fakes.NO_MD5
|
||
|
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image',
|
||
|
append=['images', self.image_name],
|
||
|
base_url_append='v2',
|
||
|
),
|
||
|
status_code=404,
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image',
|
||
|
append=['images'],
|
||
|
base_url_append='v2',
|
||
|
qs_elements=['name=' + self.image_name],
|
||
|
),
|
||
|
validate=dict(),
|
||
|
json={'images': []},
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image',
|
||
|
append=['images'],
|
||
|
base_url_append='v2',
|
||
|
qs_elements=['os_hidden=True'],
|
||
|
),
|
||
|
json={'images': []},
|
||
|
),
|
||
|
dict(
|
||
|
method='POST',
|
||
|
uri='https://image.example.com/v2/images',
|
||
|
json=ret,
|
||
|
validate=dict(json=args),
|
||
|
),
|
||
|
dict(
|
||
|
method='PUT',
|
||
|
uri='https://image.example.com/v2/images/{id}/file'.format(
|
||
|
id=self.image_id
|
||
|
),
|
||
|
validate=dict(
|
||
|
headers={
|
||
|
'Content-Type': 'application/octet-stream',
|
||
|
},
|
||
|
),
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri='https://image.example.com/v2/images/{id}'.format(
|
||
|
id=self.image_id
|
||
|
),
|
||
|
json=ret,
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri='https://image.example.com/v2/images',
|
||
|
complete_qs=True,
|
||
|
json={'images': [ret]},
|
||
|
),
|
||
|
]
|
||
|
)
|
||
|
|
||
|
self._call_create_image(
|
||
|
self.image_name, min_disk='0', min_ram=0, meta={'int_v': 12345}
|
||
|
)
|
||
|
|
||
|
self.assert_calls()
|
||
|
|
||
|
def test_create_image_put_protected(self):
|
||
|
self.cloud.image_api_use_tasks = False
|
||
|
|
||
|
args = {
|
||
|
'name': self.image_name,
|
||
|
'container_format': 'bare',
|
||
|
'disk_format': 'qcow2',
|
||
|
'owner_specified.openstack.md5': fakes.NO_MD5,
|
||
|
'owner_specified.openstack.sha256': fakes.NO_SHA256,
|
||
|
'owner_specified.openstack.object': 'images/{name}'.format(
|
||
|
name=self.image_name
|
||
|
),
|
||
|
'int_v': '12345',
|
||
|
'protected': False,
|
||
|
'visibility': 'private',
|
||
|
'min_disk': 0,
|
||
|
'min_ram': 0,
|
||
|
}
|
||
|
|
||
|
ret = args.copy()
|
||
|
ret['id'] = self.image_id
|
||
|
ret['status'] = 'success'
|
||
|
ret['checksum'] = fakes.NO_MD5
|
||
|
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image',
|
||
|
append=['images', self.image_name],
|
||
|
base_url_append='v2',
|
||
|
),
|
||
|
status_code=404,
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image',
|
||
|
append=['images'],
|
||
|
base_url_append='v2',
|
||
|
qs_elements=['name=' + self.image_name],
|
||
|
),
|
||
|
validate=dict(),
|
||
|
json={'images': []},
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image',
|
||
|
append=['images'],
|
||
|
base_url_append='v2',
|
||
|
qs_elements=['os_hidden=True'],
|
||
|
),
|
||
|
json={'images': []},
|
||
|
),
|
||
|
dict(
|
||
|
method='POST',
|
||
|
uri='https://image.example.com/v2/images',
|
||
|
json=ret,
|
||
|
validate=dict(json=args),
|
||
|
),
|
||
|
dict(
|
||
|
method='PUT',
|
||
|
uri='https://image.example.com/v2/images/{id}/file'.format(
|
||
|
id=self.image_id
|
||
|
),
|
||
|
validate=dict(
|
||
|
headers={
|
||
|
'Content-Type': 'application/octet-stream',
|
||
|
},
|
||
|
),
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri='https://image.example.com/v2/images/{id}'.format(
|
||
|
id=self.image_id
|
||
|
),
|
||
|
json=ret,
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri='https://image.example.com/v2/images',
|
||
|
complete_qs=True,
|
||
|
json={'images': [ret]},
|
||
|
),
|
||
|
]
|
||
|
)
|
||
|
|
||
|
self._call_create_image(
|
||
|
self.image_name,
|
||
|
min_disk='0',
|
||
|
min_ram=0,
|
||
|
properties={'int_v': 12345},
|
||
|
is_protected=False,
|
||
|
)
|
||
|
|
||
|
self.assert_calls()
|
||
|
|
||
|
|
||
|
class TestImageSuburl(BaseTestImage):
|
||
|
def setUp(self):
|
||
|
super(TestImageSuburl, self).setUp()
|
||
|
self.os_fixture.use_suburl()
|
||
|
self.os_fixture.build_tokens()
|
||
|
self.use_keystone_v3()
|
||
|
self.use_glance(
|
||
|
image_version_json='image-version-suburl.json',
|
||
|
image_discovery_url='https://example.com/image',
|
||
|
)
|
||
|
|
||
|
def test_list_images(self):
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image', append=['images'], base_url_append='v2'
|
||
|
),
|
||
|
json=self.fake_search_return,
|
||
|
)
|
||
|
]
|
||
|
)
|
||
|
[
|
||
|
self._compare_images(b, a)
|
||
|
for a, b in zip(self.cloud.list_images(), [self.fake_image_dict])
|
||
|
]
|
||
|
self.assert_calls()
|
||
|
|
||
|
def test_list_images_paginated(self):
|
||
|
marker = str(uuid.uuid4())
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image', append=['images'], base_url_append='v2'
|
||
|
),
|
||
|
json={
|
||
|
'images': [self.fake_image_dict],
|
||
|
'next': '/v2/images?marker={marker}'.format(
|
||
|
marker=marker
|
||
|
),
|
||
|
},
|
||
|
),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image',
|
||
|
append=['images'],
|
||
|
base_url_append='v2',
|
||
|
qs_elements=['marker={marker}'.format(marker=marker)],
|
||
|
),
|
||
|
json=self.fake_search_return,
|
||
|
),
|
||
|
]
|
||
|
)
|
||
|
[
|
||
|
self._compare_images(b, a)
|
||
|
for a, b in zip(
|
||
|
self.cloud.list_images(),
|
||
|
[self.fake_image_dict, self.fake_image_dict],
|
||
|
)
|
||
|
]
|
||
|
self.assert_calls()
|
||
|
|
||
|
|
||
|
class TestImageV1Only(base.TestCase):
|
||
|
def setUp(self):
|
||
|
super(TestImageV1Only, self).setUp()
|
||
|
self.use_glance(image_version_json='image-version-v1.json')
|
||
|
|
||
|
def test_config_v1(self):
|
||
|
self.cloud.config.config['image_api_version'] = '1'
|
||
|
# We override the scheme of the endpoint with the scheme of the service
|
||
|
# because glance has a bug where it doesn't return https properly.
|
||
|
self.assertEqual(
|
||
|
'https://image.example.com/v1/',
|
||
|
self.cloud._image_client.get_endpoint(),
|
||
|
)
|
||
|
self.assertTrue(self.cloud._is_client_version('image', 1))
|
||
|
|
||
|
def test_config_v2(self):
|
||
|
self.cloud.config.config['image_api_version'] = '2'
|
||
|
# We override the scheme of the endpoint with the scheme of the service
|
||
|
# because glance has a bug where it doesn't return https properly.
|
||
|
self.assertEqual(
|
||
|
'https://image.example.com/v1/',
|
||
|
self.cloud._image_client.get_endpoint(),
|
||
|
)
|
||
|
self.assertFalse(self.cloud._is_client_version('image', 2))
|
||
|
|
||
|
|
||
|
class TestImageV2Only(base.TestCase):
|
||
|
def setUp(self):
|
||
|
super(TestImageV2Only, self).setUp()
|
||
|
self.use_glance(image_version_json='image-version-v2.json')
|
||
|
|
||
|
def test_config_v1(self):
|
||
|
self.cloud.config.config['image_api_version'] = '1'
|
||
|
# We override the scheme of the endpoint with the scheme of the service
|
||
|
# because glance has a bug where it doesn't return https properly.
|
||
|
self.assertEqual(
|
||
|
'https://image.example.com/v2/',
|
||
|
self.cloud._image_client.get_endpoint(),
|
||
|
)
|
||
|
self.assertTrue(self.cloud._is_client_version('image', 2))
|
||
|
|
||
|
def test_config_v2(self):
|
||
|
self.cloud.config.config['image_api_version'] = '2'
|
||
|
# We override the scheme of the endpoint with the scheme of the service
|
||
|
# because glance has a bug where it doesn't return https properly.
|
||
|
self.assertEqual(
|
||
|
'https://image.example.com/v2/',
|
||
|
self.cloud._image_client.get_endpoint(),
|
||
|
)
|
||
|
self.assertTrue(self.cloud._is_client_version('image', 2))
|
||
|
|
||
|
|
||
|
class TestImageVolume(BaseTestImage):
|
||
|
def setUp(self):
|
||
|
super(TestImageVolume, self).setUp()
|
||
|
self.volume_id = str(uuid.uuid4())
|
||
|
|
||
|
def test_create_image_volume(self):
|
||
|
self.register_uris(
|
||
|
[
|
||
|
self.get_cinder_discovery_mock_dict(),
|
||
|
dict(
|
||
|
method='POST',
|
||
|
uri=self.get_mock_url(
|
||
|
'volumev3',
|
||
|
append=['volumes', self.volume_id, 'action'],
|
||
|
),
|
||
|
json={
|
||
|
'os-volume_upload_image': {'image_id': self.image_id}
|
||
|
},
|
||
|
validate=dict(
|
||
|
json={
|
||
|
'os-volume_upload_image': {
|
||
|
'container_format': 'bare',
|
||
|
'disk_format': 'qcow2',
|
||
|
'force': False,
|
||
|
'image_name': 'fake_image',
|
||
|
}
|
||
|
}
|
||
|
),
|
||
|
),
|
||
|
# NOTE(notmorgan): Glance discovery happens here, insert the
|
||
|
# glance discovery mock at this point, DO NOT use the
|
||
|
# .use_glance() method, that is intended only for use in
|
||
|
# .setUp
|
||
|
self.get_glance_discovery_mock_dict(),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image', append=['images'], base_url_append='v2'
|
||
|
),
|
||
|
json=self.fake_search_return,
|
||
|
),
|
||
|
]
|
||
|
)
|
||
|
|
||
|
self.cloud.create_image(
|
||
|
'fake_image',
|
||
|
self.imagefile.name,
|
||
|
wait=True,
|
||
|
timeout=1,
|
||
|
volume={'id': self.volume_id},
|
||
|
)
|
||
|
|
||
|
self.assert_calls()
|
||
|
|
||
|
def test_create_image_volume_duplicate(self):
|
||
|
self.register_uris(
|
||
|
[
|
||
|
self.get_cinder_discovery_mock_dict(),
|
||
|
dict(
|
||
|
method='POST',
|
||
|
uri=self.get_mock_url(
|
||
|
'volumev3',
|
||
|
append=['volumes', self.volume_id, 'action'],
|
||
|
),
|
||
|
json={
|
||
|
'os-volume_upload_image': {'image_id': self.image_id}
|
||
|
},
|
||
|
validate=dict(
|
||
|
json={
|
||
|
'os-volume_upload_image': {
|
||
|
'container_format': 'bare',
|
||
|
'disk_format': 'qcow2',
|
||
|
'force': True,
|
||
|
'image_name': 'fake_image',
|
||
|
}
|
||
|
}
|
||
|
),
|
||
|
),
|
||
|
# NOTE(notmorgan): Glance discovery happens here, insert the
|
||
|
# glance discovery mock at this point, DO NOT use the
|
||
|
# .use_glance() method, that is intended only for use in
|
||
|
# .setUp
|
||
|
self.get_glance_discovery_mock_dict(),
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image', append=['images'], base_url_append='v2'
|
||
|
),
|
||
|
json=self.fake_search_return,
|
||
|
),
|
||
|
]
|
||
|
)
|
||
|
|
||
|
self.cloud.create_image(
|
||
|
'fake_image',
|
||
|
self.imagefile.name,
|
||
|
wait=True,
|
||
|
timeout=1,
|
||
|
volume={'id': self.volume_id},
|
||
|
allow_duplicates=True,
|
||
|
)
|
||
|
|
||
|
self.assert_calls()
|
||
|
|
||
|
|
||
|
class TestImageBrokenDiscovery(base.TestCase):
|
||
|
def setUp(self):
|
||
|
super(TestImageBrokenDiscovery, self).setUp()
|
||
|
self.use_glance(image_version_json='image-version-broken.json')
|
||
|
|
||
|
def test_url_fix(self):
|
||
|
# image-version-broken.json has both http urls and localhost as the
|
||
|
# host. This is testing that what is discovered is https, because
|
||
|
# that's what's in the catalog, and image.example.com for the same
|
||
|
# reason.
|
||
|
self.register_uris(
|
||
|
[
|
||
|
dict(
|
||
|
method='GET',
|
||
|
uri=self.get_mock_url(
|
||
|
'image', append=['images'], base_url_append='v2'
|
||
|
),
|
||
|
json={'images': []},
|
||
|
)
|
||
|
]
|
||
|
)
|
||
|
self.assertEqual([], self.cloud.list_images())
|
||
|
self.assertEqual(
|
||
|
self.cloud._image_client.get_endpoint(),
|
||
|
'https://image.example.com/v2/',
|
||
|
)
|
||
|
self.assert_calls()
|