
Fixes os_xenapi/dom0/etc/xapi.d/plugins/*.py wrt Python 3 compatibility, also remove some tests in os_xenapi/tests/plugins/test_glance.py that are too Py2 centric. Change-Id: Iab721f6a47dc023f01670076ef7d1de107a913ae
946 lines
44 KiB
Python
946 lines
44 KiB
Python
# Copyright (c) 2017 Citrix Systems
|
|
# All Rights Reserved.
|
|
#
|
|
# 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 mock
|
|
import sys
|
|
try:
|
|
import httplib
|
|
import urllib2
|
|
from urllib2 import HTTPError
|
|
from urllib2 import URLError
|
|
from urlparse import urlparse
|
|
except ImportError:
|
|
# make py3.x happy: it's needed for script parsing, although this test
|
|
# is excluded from py3.x testing
|
|
import http.client as httplib
|
|
from urllib.error import HTTPError
|
|
from urllib.error import URLError
|
|
from urllib.parse import urlparse
|
|
import urllib.request as urllib2
|
|
import json
|
|
from os_xenapi.tests.plugins import plugin_test
|
|
|
|
|
|
class FakeXenAPIException(Exception):
|
|
pass
|
|
|
|
|
|
class Fake_HTTP_Request_Error(Exception):
|
|
pass
|
|
|
|
|
|
class GlanceTestCase(plugin_test.PluginTestBase):
|
|
def setUp(self):
|
|
super(GlanceTestCase, self).setUp()
|
|
# md5 is deprecated in py2.7 and forward;
|
|
sys.modules['md5'] = mock.Mock()
|
|
self.glance = self.load_plugin("glance.py")
|
|
|
|
@mock.patch.object(httplib, 'HTTPSConnection')
|
|
def test_create_connection_https(self, mock_HTTPConn):
|
|
fake_scheme = 'https'
|
|
fake_netloc = 'fake_netloc'
|
|
fake_https_return = mock.Mock()
|
|
mock_HTTPConn.return_value = fake_https_return
|
|
|
|
fake_create_Conn_return = self.glance._create_connection(
|
|
fake_scheme, fake_netloc)
|
|
mock_HTTPConn.assert_called_with(fake_netloc)
|
|
mock_HTTPConn.return_value.connect.assert_called_once()
|
|
self.assertEqual(fake_https_return, fake_create_Conn_return)
|
|
|
|
@mock.patch.object(httplib, 'HTTPConnection')
|
|
def test_create_connection_http(self, mock_HTTPConn):
|
|
fake_scheme = 'http'
|
|
fake_netloc = 'fake_netloc'
|
|
fake_https_return = mock.Mock()
|
|
mock_HTTPConn.return_value = fake_https_return
|
|
|
|
fake_create_Conn_return = self.glance._create_connection(
|
|
fake_scheme, fake_netloc)
|
|
mock_HTTPConn.assert_called_with(fake_netloc)
|
|
mock_HTTPConn.return_value.connect.assert_called_once()
|
|
self.assertEqual(fake_https_return, fake_create_Conn_return)
|
|
|
|
@mock.patch.object(urllib2, 'urlopen')
|
|
def test_download_and_verify_ok(self, mock_urlopen):
|
|
mock_extract_tarball = self.mock_patch_object(
|
|
self.glance.utils, 'extract_tarball')
|
|
mock_md5 = mock.Mock()
|
|
mock_md5.hexdigest.return_value = 'expect_cksum'
|
|
mock_md5_new = self.mock_patch_object(
|
|
self.glance.md5, 'new', mock_md5)
|
|
mock_info = mock.Mock()
|
|
mock_info.getheader.return_value = 'expect_cksum'
|
|
mock_urlopen.return_value.info.return_value = mock_info
|
|
fake_request = urllib2.Request('http://fakeurl.com')
|
|
|
|
self.glance._download_tarball_and_verify(
|
|
fake_request, 'fake_staging_path')
|
|
mock_urlopen.assert_called_with(fake_request)
|
|
mock_extract_tarball.assert_called_once()
|
|
mock_md5_new.assert_called_once()
|
|
mock_info.getheader.assert_called_once()
|
|
mock_md5_new.return_value.hexdigest.assert_called_once()
|
|
|
|
@mock.patch.object(urllib2, 'urlopen')
|
|
def test_download_ok_extract_failed(self, mock_urlopen):
|
|
mock_extract_tarball = self.mock_patch_object(
|
|
self.glance.utils, 'extract_tarball')
|
|
fake_retcode = 0
|
|
mock_extract_tarball.side_effect = \
|
|
self.glance.utils.SubprocessException('fake_cmd',
|
|
fake_retcode,
|
|
'fake_out',
|
|
'fake_stderr')
|
|
mock_md5 = mock.Mock()
|
|
mock_md5.hexdigest.return_value = 'unexpect_cksum'
|
|
mock_md5_new = self.mock_patch_object(
|
|
self.glance.md5, 'new', mock_md5)
|
|
mock_info = mock.Mock()
|
|
mock_info.getheader.return_value = 'expect_cksum'
|
|
mock_urlopen.return_value.info.return_value = mock_info
|
|
fake_request = urllib2.Request('http://fakeurl.com')
|
|
|
|
self.assertRaises(self.glance.RetryableError,
|
|
self.glance._download_tarball_and_verify,
|
|
fake_request, 'fake_staging_path'
|
|
)
|
|
mock_extract_tarball.assert_called_once()
|
|
mock_md5_new.assert_called_once()
|
|
mock_info.getheader.assert_not_called()
|
|
mock_md5_new.hexdigest.assert_not_called()
|
|
|
|
@mock.patch.object(urllib2, 'urlopen')
|
|
def test_download_ok_verify_failed(self, mock_urlopen):
|
|
mock_extract_tarball = self.mock_patch_object(
|
|
self.glance.utils, 'extract_tarball')
|
|
mock_md5 = mock.Mock()
|
|
mock_md5.hexdigest.return_value = 'unexpect_cksum'
|
|
mock_md5_new = self.mock_patch_object(
|
|
self.glance.md5, 'new', mock_md5)
|
|
mock_info = mock.Mock()
|
|
mock_info.getheader.return_value = 'expect_cksum'
|
|
mock_urlopen.return_value.info.return_value = mock_info
|
|
fake_request = urllib2.Request('http://fakeurl.com')
|
|
|
|
self.assertRaises(self.glance.RetryableError,
|
|
self.glance._download_tarball_and_verify,
|
|
fake_request, 'fake_staging_path'
|
|
)
|
|
mock_extract_tarball.assert_called_once()
|
|
mock_md5_new.assert_called_once()
|
|
mock_md5_new.return_value.hexdigest.assert_called_once()
|
|
|
|
@mock.patch.object(urllib2, 'urlopen')
|
|
def test_download_failed_HTTPError(self, mock_urlopen):
|
|
mock_urlopen.side_effect = HTTPError(
|
|
None, None, None, None, None)
|
|
fake_request = urllib2.Request('http://fakeurl.com')
|
|
|
|
self.assertRaises(
|
|
self.glance.RetryableError,
|
|
self.glance._download_tarball_and_verify,
|
|
fake_request, 'fake_staging_path')
|
|
|
|
@mock.patch.object(urllib2, 'urlopen')
|
|
def test_download_failed_URLError(self, mock_urlopen):
|
|
mock_urlopen.side_effect = URLError(None)
|
|
fake_request = urllib2.Request('http://fakeurl.com')
|
|
|
|
self.assertRaises(
|
|
self.glance.RetryableError,
|
|
self.glance._download_tarball_and_verify,
|
|
fake_request, 'fake_staging_path')
|
|
|
|
@mock.patch.object(urllib2, 'urlopen')
|
|
def test_download_failed_HTTPException(self, mock_urlopen):
|
|
mock_urlopen.side_effect = httplib.HTTPException()
|
|
fake_request = urllib2.Request('http://fakeurl.com')
|
|
|
|
self.assertRaises(
|
|
self.glance.RetryableError,
|
|
self.glance._download_tarball_and_verify,
|
|
fake_request, 'fake_staging_path')
|
|
|
|
@mock.patch.object(urllib2, 'Request')
|
|
def test_download_tarball_by_url_v1(self, mock_request):
|
|
fake_glance_endpoint = 'fake_glance_endpoint'
|
|
fake_image_id = 'fake_extra_headers'
|
|
expected_url = "%(glance_endpoint)s/v1/images/%(image_id)s" % {
|
|
'glance_endpoint': fake_glance_endpoint,
|
|
'image_id': fake_image_id}
|
|
mock_download_tarball_and_verify = self.mock_patch_object(
|
|
self.glance, '_download_tarball_and_verify')
|
|
mock_request.return_value = 'fake_request'
|
|
|
|
self.glance._download_tarball_by_url_v1(
|
|
'fake_sr_path', 'fake_staging_path', fake_image_id,
|
|
fake_glance_endpoint, 'fake_extra_headers')
|
|
mock_request.assert_called_with(expected_url,
|
|
headers='fake_extra_headers')
|
|
mock_download_tarball_and_verify.assert_called_with(
|
|
'fake_request', 'fake_staging_path')
|
|
|
|
@mock.patch.object(urllib2, 'Request')
|
|
def test_download_tarball_by_url_v2(self, mock_request):
|
|
fake_glance_endpoint = 'fake_glance_endpoint'
|
|
fake_image_id = 'fake_extra_headers'
|
|
mock_download_tarball_and_verify = self.mock_patch_object(
|
|
self.glance, '_download_tarball_and_verify')
|
|
mock_request.return_value = 'fake_request'
|
|
|
|
self.glance._download_tarball_by_url_v2(
|
|
'fake_sr_path', 'fake_staging_path', fake_image_id,
|
|
fake_glance_endpoint, 'fake_extra_headers')
|
|
mock_download_tarball_and_verify.assert_called_with(
|
|
'fake_request', 'fake_staging_path')
|
|
|
|
def test_upload_tarball_by_url_http_v1(self):
|
|
fake_conn = mock.Mock()
|
|
mock_HTTPConn = self.mock_patch_object(
|
|
self.glance, '_create_connection', fake_conn)
|
|
mock_validate_image = self.mock_patch_object(
|
|
self.glance, 'validate_image_status_before_upload_v1')
|
|
mock_create_tarball = self.mock_patch_object(
|
|
self.glance.utils, 'create_tarball')
|
|
mock_check_resp_status = self.mock_patch_object(
|
|
self.glance, 'check_resp_status_and_retry')
|
|
self.glance._create_connection().getresponse = mock.Mock()
|
|
self.glance._create_connection().getresponse().status = httplib.OK
|
|
fake_extra_headers = {}
|
|
fake_properties = {}
|
|
fake_endpoint = 'http://fake_netloc/fake_path'
|
|
expected_url = "%(glance_endpoint)s/v1/images/%(image_id)s" % {
|
|
'glance_endpoint': fake_endpoint,
|
|
'image_id': 'fake_image_id'}
|
|
|
|
self.glance._upload_tarball_by_url_v1(
|
|
'fake_staging_path', 'fake_image_id', fake_endpoint,
|
|
fake_extra_headers, fake_properties)
|
|
|
|
self.assertTrue(mock_HTTPConn.called)
|
|
mock_validate_image.assert_called_with(fake_conn,
|
|
expected_url,
|
|
fake_extra_headers)
|
|
self.assertTrue(mock_create_tarball.called)
|
|
self.assertTrue(
|
|
mock_HTTPConn.return_value.getresponse.called)
|
|
self.assertFalse(mock_check_resp_status.called)
|
|
|
|
def test_upload_tarball_by_url_https_v1(self):
|
|
fake_conn = mock.Mock()
|
|
mock_HTTPSConn = self.mock_patch_object(
|
|
self.glance, '_create_connection', fake_conn)
|
|
mock_validate_image = self.mock_patch_object(
|
|
self.glance, 'validate_image_status_before_upload_v1')
|
|
mock_create_tarball = self.mock_patch_object(
|
|
self.glance.utils, 'create_tarball')
|
|
mock_check_resp_status = self.mock_patch_object(
|
|
self.glance, 'check_resp_status_and_retry')
|
|
self.glance._create_connection().getresponse = mock.Mock()
|
|
self.glance._create_connection().getresponse().status = httplib.OK
|
|
fake_extra_headers = {}
|
|
fake_properties = {}
|
|
fake_endpoint = 'https://fake_netloc/fake_path'
|
|
expected_url = "%(glance_endpoint)s/v1/images/%(image_id)s" % {
|
|
'glance_endpoint': fake_endpoint,
|
|
'image_id': 'fake_image_id'}
|
|
|
|
self.glance._upload_tarball_by_url_v1(
|
|
'fake_staging_path', 'fake_image_id', fake_endpoint,
|
|
fake_extra_headers, fake_properties)
|
|
|
|
self.assertTrue(mock_HTTPSConn.called)
|
|
mock_validate_image.assert_called_with(fake_conn,
|
|
expected_url,
|
|
fake_extra_headers)
|
|
self.assertTrue(mock_create_tarball.called)
|
|
self.assertTrue(
|
|
mock_HTTPSConn.return_value.getresponse.called)
|
|
self.assertFalse(mock_check_resp_status.called)
|
|
|
|
def test_upload_tarball_by_url_https_failed_retry_v1(self):
|
|
fake_conn = mock.Mock()
|
|
mock_HTTPSConn = self.mock_patch_object(
|
|
self.glance, '_create_connection', fake_conn)
|
|
mock_validate_image = self.mock_patch_object(
|
|
self.glance, 'validate_image_status_before_upload_v1')
|
|
mock_create_tarball = self.mock_patch_object(
|
|
self.glance.utils, 'create_tarball')
|
|
mock_check_resp_status = self.mock_patch_object(
|
|
self.glance, 'check_resp_status_and_retry')
|
|
self.glance._create_connection().getresponse = mock.Mock()
|
|
self.glance._create_connection().getresponse().status = \
|
|
httplib.REQUEST_TIMEOUT
|
|
fake_extra_headers = {}
|
|
fake_properties = {}
|
|
fake_endpoint = 'https://fake_netloc/fake_path'
|
|
expected_url = "%(glance_endpoint)s/v1/images/%(image_id)s" % {
|
|
'glance_endpoint': fake_endpoint,
|
|
'image_id': 'fake_image_id'}
|
|
|
|
self.glance._upload_tarball_by_url_v1(
|
|
'fake_staging_path', 'fake_image_id', fake_endpoint,
|
|
fake_extra_headers, fake_properties)
|
|
|
|
self.assertTrue(mock_HTTPSConn.called)
|
|
mock_validate_image.assert_called_with(fake_conn,
|
|
expected_url,
|
|
fake_extra_headers)
|
|
self.assertTrue(mock_create_tarball.called)
|
|
self.assertTrue(
|
|
mock_HTTPSConn.return_value.getresponse.called)
|
|
self.assertTrue(mock_check_resp_status.called)
|
|
|
|
def test_upload_tarball_by_url_http_v2(self):
|
|
fake_conn = mock.Mock()
|
|
mock_HTTPConn = self.mock_patch_object(
|
|
self.glance, '_create_connection', fake_conn)
|
|
mock_validate_image = self.mock_patch_object(
|
|
self.glance, 'validate_image_status_before_upload_v2')
|
|
mock_create_tarball = self.mock_patch_object(
|
|
self.glance.utils, 'create_tarball')
|
|
mock_check_resp_status = self.mock_patch_object(
|
|
self.glance, 'check_resp_status_and_retry')
|
|
mock_update_image_meta = self.mock_patch_object(
|
|
self.glance, '_update_image_meta_v2')
|
|
self.glance._create_connection().getresponse = mock.Mock()
|
|
self.glance._create_connection().getresponse().status = \
|
|
httplib.NO_CONTENT
|
|
fake_extra_headers = {}
|
|
fake_properties = {}
|
|
fake_endpoint = 'http://fake_netloc/fake_path'
|
|
fake_image_id = 'fake_image_id'
|
|
expected_url = "%(glance_endpoint)s/v2/images/%(image_id)s/file" % {
|
|
'glance_endpoint': fake_endpoint,
|
|
'image_id': fake_image_id}
|
|
expected_wsgi_path = '/fake_path/v2/images/%s' % fake_image_id
|
|
expect_url_parts = urlparse(expected_url)
|
|
|
|
expected_mgt_url = "%(glance_endpoint)s/v2/images/%(image_id)s" % {
|
|
'glance_endpoint': fake_endpoint,
|
|
'image_id': fake_image_id}
|
|
fake_mgt_parts = urlparse(expected_mgt_url)
|
|
fake_mgt_path = fake_mgt_parts[2]
|
|
|
|
self.glance._upload_tarball_by_url_v2(
|
|
'fake_staging_path', fake_image_id, fake_endpoint,
|
|
fake_extra_headers, fake_properties)
|
|
|
|
mock_HTTPConn.assert_called_with(expect_url_parts[0],
|
|
expect_url_parts[1])
|
|
mock_validate_image.assert_called_with(fake_conn,
|
|
expected_url,
|
|
fake_extra_headers,
|
|
expected_wsgi_path)
|
|
mock_update_image_meta.assert_called_with(fake_conn,
|
|
fake_extra_headers,
|
|
fake_properties,
|
|
fake_mgt_path)
|
|
|
|
self.assertTrue(mock_create_tarball.called)
|
|
self.assertTrue(
|
|
mock_HTTPConn.return_value.getresponse.called)
|
|
self.assertFalse(mock_check_resp_status.called)
|
|
|
|
def test_upload_tarball_by_url_https_v2(self):
|
|
fake_conn = mock.Mock()
|
|
mock_HTTPSConn = self.mock_patch_object(
|
|
self.glance, '_create_connection', fake_conn)
|
|
mock_validate_image = self.mock_patch_object(
|
|
self.glance, 'validate_image_status_before_upload_v2')
|
|
mock_create_tarball = self.mock_patch_object(
|
|
self.glance.utils, 'create_tarball')
|
|
mock_check_resp_status = self.mock_patch_object(
|
|
self.glance, 'check_resp_status_and_retry')
|
|
mock_update_image_meta = self.mock_patch_object(
|
|
self.glance, '_update_image_meta_v2')
|
|
self.glance._create_connection().getresponse = mock.Mock()
|
|
self.glance._create_connection().getresponse().status = \
|
|
httplib.NO_CONTENT
|
|
fake_extra_headers = {}
|
|
fake_properties = {}
|
|
fake_endpoint = 'https://fake_netloc/fake_path'
|
|
fake_image_id = 'fake_image_id'
|
|
expected_url = "%(glance_endpoint)s/v2/images/%(image_id)s/file" % {
|
|
'glance_endpoint': fake_endpoint,
|
|
'image_id': fake_image_id}
|
|
expect_url_parts = urlparse(expected_url)
|
|
expected_wsgi_path = '/fake_path/v2/images/%s' % fake_image_id
|
|
|
|
expected_mgt_url = "%(glance_endpoint)s/v2/images/%(image_id)s" % {
|
|
'glance_endpoint': fake_endpoint,
|
|
'image_id': fake_image_id}
|
|
fake_mgt_parts = urlparse(expected_mgt_url)
|
|
fake_mgt_path = fake_mgt_parts[2]
|
|
|
|
self.glance._upload_tarball_by_url_v2(
|
|
'fake_staging_path', fake_image_id, fake_endpoint,
|
|
fake_extra_headers, fake_properties)
|
|
|
|
mock_update_image_meta.assert_called_with(fake_conn,
|
|
fake_extra_headers,
|
|
fake_properties,
|
|
fake_mgt_path)
|
|
mock_HTTPSConn.assert_called_with(expect_url_parts[0],
|
|
expect_url_parts[1])
|
|
mock_validate_image.assert_called_with(fake_conn,
|
|
expected_url,
|
|
fake_extra_headers,
|
|
expected_wsgi_path)
|
|
self.assertTrue(mock_create_tarball.called)
|
|
self.assertTrue(
|
|
mock_HTTPSConn.return_value.getresponse.called)
|
|
self.assertFalse(mock_check_resp_status.called)
|
|
|
|
def test_upload_tarball_by_url_v2_with_api_endpoint(self):
|
|
fake_conn = mock.Mock()
|
|
mock_Conn = self.mock_patch_object(
|
|
self.glance, '_create_connection', fake_conn)
|
|
mock_validate_image = self.mock_patch_object(
|
|
self.glance, 'validate_image_status_before_upload_v2')
|
|
mock_create_tarball = self.mock_patch_object(
|
|
self.glance.utils, 'create_tarball')
|
|
mock_check_resp_status = self.mock_patch_object(
|
|
self.glance, 'check_resp_status_and_retry')
|
|
mock_update_image_meta = self.mock_patch_object(
|
|
self.glance, '_update_image_meta_v2')
|
|
self.glance._create_connection().getresponse = mock.Mock()
|
|
self.glance._create_connection().getresponse().status = \
|
|
httplib.NO_CONTENT
|
|
fake_extra_headers = {}
|
|
fake_properties = {}
|
|
fake_endpoint = 'https://fake_netloc:fake_port'
|
|
fake_image_id = 'fake_image_id'
|
|
expected_url = "%(glance_endpoint)s/v2/images/%(image_id)s/file" % {
|
|
'glance_endpoint': fake_endpoint,
|
|
'image_id': fake_image_id}
|
|
expect_url_parts = urlparse(expected_url)
|
|
expected_api_path = '/v2/images/%s' % fake_image_id
|
|
|
|
expected_mgt_url = "%(glance_endpoint)s/v2/images/%(image_id)s" % {
|
|
'glance_endpoint': fake_endpoint,
|
|
'image_id': fake_image_id}
|
|
fake_mgt_parts = urlparse(expected_mgt_url)
|
|
fake_mgt_path = fake_mgt_parts[2]
|
|
|
|
self.glance._upload_tarball_by_url_v2(
|
|
'fake_staging_path', fake_image_id, fake_endpoint,
|
|
fake_extra_headers, fake_properties)
|
|
|
|
mock_update_image_meta.assert_called_with(fake_conn,
|
|
fake_extra_headers,
|
|
fake_properties,
|
|
fake_mgt_path)
|
|
mock_Conn.assert_called_with(expect_url_parts[0], expect_url_parts[1])
|
|
mock_validate_image.assert_called_with(fake_conn,
|
|
expected_url,
|
|
fake_extra_headers,
|
|
expected_api_path)
|
|
self.assertTrue(mock_create_tarball.called)
|
|
self.assertTrue(
|
|
mock_Conn.return_value.getresponse.called)
|
|
self.assertFalse(mock_check_resp_status.called)
|
|
|
|
def test_upload_tarball_by_url_v2_with_wsgi_endpoint(self):
|
|
fake_conn = mock.Mock()
|
|
mock_Conn = self.mock_patch_object(
|
|
self.glance, '_create_connection', fake_conn)
|
|
mock_validate_image = self.mock_patch_object(
|
|
self.glance, 'validate_image_status_before_upload_v2')
|
|
mock_create_tarball = self.mock_patch_object(
|
|
self.glance.utils, 'create_tarball')
|
|
mock_check_resp_status = self.mock_patch_object(
|
|
self.glance, 'check_resp_status_and_retry')
|
|
mock_update_image_meta = self.mock_patch_object(
|
|
self.glance, '_update_image_meta_v2')
|
|
self.glance._create_connection().getresponse = mock.Mock()
|
|
self.glance._create_connection().getresponse().status = \
|
|
httplib.NO_CONTENT
|
|
fake_extra_headers = {}
|
|
fake_properties = {}
|
|
fake_endpoint = 'https://fake_netloc/fake_path'
|
|
fake_image_id = 'fake_image_id'
|
|
expected_url = "%(glance_endpoint)s/v2/images/%(image_id)s/file" % {
|
|
'glance_endpoint': fake_endpoint,
|
|
'image_id': fake_image_id}
|
|
expect_url_parts = urlparse(expected_url)
|
|
expected_wsgi_path = '/fake_path/v2/images/%s' % fake_image_id
|
|
|
|
expected_mgt_url = "%(glance_endpoint)s/v2/images/%(image_id)s" % {
|
|
'glance_endpoint': fake_endpoint,
|
|
'image_id': fake_image_id}
|
|
fake_mgt_parts = urlparse(expected_mgt_url)
|
|
fake_mgt_path = fake_mgt_parts[2]
|
|
|
|
self.glance._upload_tarball_by_url_v2(
|
|
'fake_staging_path', fake_image_id, fake_endpoint,
|
|
fake_extra_headers, fake_properties)
|
|
|
|
mock_update_image_meta.assert_called_with(fake_conn,
|
|
fake_extra_headers,
|
|
fake_properties,
|
|
fake_mgt_path)
|
|
mock_Conn.assert_called_with(expect_url_parts[0], expect_url_parts[1])
|
|
mock_validate_image.assert_called_with(fake_conn,
|
|
expected_url,
|
|
fake_extra_headers,
|
|
expected_wsgi_path)
|
|
self.assertTrue(mock_create_tarball.called)
|
|
self.assertTrue(
|
|
mock_Conn.return_value.getresponse.called)
|
|
self.assertFalse(mock_check_resp_status.called)
|
|
|
|
def test_upload_tarball_by_url_https_failed_retry_v2(self):
|
|
fake_conn = mock.Mock()
|
|
mock_HTTPSConn = self.mock_patch_object(
|
|
self.glance, '_create_connection', fake_conn)
|
|
mock_validate_image = self.mock_patch_object(
|
|
self.glance, 'validate_image_status_before_upload_v2')
|
|
mock_create_tarball = self.mock_patch_object(
|
|
self.glance.utils, 'create_tarball')
|
|
mock_check_resp_status = self.mock_patch_object(
|
|
self.glance, 'check_resp_status_and_retry')
|
|
mock_update_image_meta = self.mock_patch_object(
|
|
self.glance, '_update_image_meta_v2')
|
|
self.glance._create_connection().getresponse = mock.Mock()
|
|
self.glance._create_connection().getresponse().status = \
|
|
httplib.REQUEST_TIMEOUT
|
|
fake_extra_headers = {}
|
|
fake_properties = {}
|
|
fake_endpoint = 'https://fake_netloc/fake_path'
|
|
fake_image_id = 'fake_image_id'
|
|
expected_url = "%(glance_endpoint)s/v2/images/%(image_id)s/file" % {
|
|
'glance_endpoint': fake_endpoint,
|
|
'image_id': fake_image_id}
|
|
expected_wsgi_path = '/fake_path/v2/images/%s' % fake_image_id
|
|
|
|
expected_mgt_url = "%(glance_endpoint)s/v2/images/%(image_id)s" % {
|
|
'glance_endpoint': fake_endpoint,
|
|
'image_id': fake_image_id}
|
|
expect_url_parts = urlparse(expected_url)
|
|
fake_mgt_parts = urlparse(expected_mgt_url)
|
|
fake_mgt_path = fake_mgt_parts[2]
|
|
|
|
self.glance._upload_tarball_by_url_v2(
|
|
'fake_staging_path', fake_image_id, fake_endpoint,
|
|
fake_extra_headers, fake_properties)
|
|
|
|
mock_update_image_meta.assert_called_with(fake_conn,
|
|
fake_extra_headers,
|
|
fake_properties,
|
|
fake_mgt_path)
|
|
mock_HTTPSConn.assert_called_with(expect_url_parts[0],
|
|
expect_url_parts[1])
|
|
mock_validate_image.assert_called_with(fake_conn,
|
|
expected_url,
|
|
fake_extra_headers,
|
|
expected_wsgi_path)
|
|
self.assertTrue(mock_create_tarball.called)
|
|
self.assertTrue(
|
|
mock_HTTPSConn.return_value.getresponse.called)
|
|
self.assertTrue(mock_check_resp_status.called)
|
|
|
|
def test_update_image_meta_ok_v2_using_api_service(self):
|
|
fake_conn = mock.Mock()
|
|
fake_extra_headers = {'fake_type': 'fake_content'}
|
|
fake_properties = {'fake_path': True}
|
|
new_fake_properties = {'path': '/fake-path',
|
|
'value': "True",
|
|
'op': 'add'}
|
|
fake_body = [
|
|
{"path": "/container_format", "value": "ovf", "op": "add"},
|
|
{"path": "/disk_format", "value": "vhd", "op": "add"},
|
|
{"path": "/visibility", "value": "private", "op": "add"}]
|
|
fake_body.append(new_fake_properties)
|
|
fake_body_json = json.dumps(fake_body)
|
|
fake_headers = {
|
|
'Content-Type': 'application/openstack-images-v2.1-json-patch'}
|
|
fake_headers.update(**fake_extra_headers)
|
|
fake_conn.getresponse.return_value = mock.Mock()
|
|
fake_conn.getresponse().status = httplib.OK
|
|
expected_api_path = '/v2/images/%s' % 'fake_image_id'
|
|
|
|
self.glance._update_image_meta_v2(fake_conn, fake_extra_headers,
|
|
fake_properties, expected_api_path)
|
|
fake_conn.request.assert_called_with('PATCH',
|
|
'/v2/images/%s' % 'fake_image_id',
|
|
body=fake_body_json,
|
|
headers=fake_headers)
|
|
fake_conn.getresponse.assert_called()
|
|
|
|
def test_update_image_meta_ok_v2_using_uwsgi_service(self):
|
|
fake_conn = mock.Mock()
|
|
fake_extra_headers = {'fake_type': 'fake_content'}
|
|
fake_properties = {'fake_path': True}
|
|
new_fake_properties = {'path': '/fake-path',
|
|
'value': "True",
|
|
'op': 'add'}
|
|
fake_body = [
|
|
{"path": "/container_format", "value": "ovf", "op": "add"},
|
|
{"path": "/disk_format", "value": "vhd", "op": "add"},
|
|
{"path": "/visibility", "value": "private", "op": "add"}]
|
|
fake_body.append(new_fake_properties)
|
|
fake_body_json = json.dumps(fake_body)
|
|
fake_headers = {
|
|
'Content-Type': 'application/openstack-images-v2.1-json-patch'}
|
|
fake_headers.update(**fake_extra_headers)
|
|
fake_conn.getresponse.return_value = mock.Mock()
|
|
fake_conn.getresponse().status = httplib.OK
|
|
expected_wsgi_path = '/fake_path/v2/images/%s' % 'fake_image_id'
|
|
|
|
self.glance._update_image_meta_v2(fake_conn, fake_extra_headers,
|
|
fake_properties, expected_wsgi_path)
|
|
fake_conn.request.assert_called_with('PATCH',
|
|
'/fake_path/v2/images/%s' %
|
|
'fake_image_id',
|
|
body=fake_body_json,
|
|
headers=fake_headers)
|
|
fake_conn.getresponse.assert_called()
|
|
|
|
def test_check_resp_status_and_retry_plugin_error(self):
|
|
mock_resp_badrequest = mock.Mock()
|
|
mock_resp_badrequest.status = httplib.BAD_REQUEST
|
|
|
|
self.assertRaises(
|
|
self.glance.PluginError,
|
|
self.glance.check_resp_status_and_retry,
|
|
mock_resp_badrequest,
|
|
'fake_image_id',
|
|
'fake_url')
|
|
|
|
def test_check_resp_status_and_retry_retry_error(self):
|
|
mock_resp_badgateway = mock.Mock()
|
|
mock_resp_badgateway.status = httplib.BAD_GATEWAY
|
|
|
|
self.assertRaises(
|
|
self.glance.RetryableError,
|
|
self.glance.check_resp_status_and_retry,
|
|
mock_resp_badgateway,
|
|
'fake_image_id',
|
|
'fake_url')
|
|
|
|
def test_check_resp_status_and_retry_image_not_found(self):
|
|
mock_resp_badgateway = mock.Mock()
|
|
mock_resp_badgateway.status = httplib.NOT_FOUND
|
|
self.glance.XenAPI.Failure = FakeXenAPIException
|
|
self.assertRaises(
|
|
self.glance.XenAPI.Failure,
|
|
self.glance.check_resp_status_and_retry,
|
|
mock_resp_badgateway,
|
|
'fake_image_id',
|
|
'fake_url')
|
|
|
|
def test_check_resp_status_and_retry_unknown_status(self):
|
|
fake_unknown_http_status = -1
|
|
mock_resp_other = mock.Mock()
|
|
mock_resp_other.status = fake_unknown_http_status
|
|
|
|
self.assertRaises(
|
|
self.glance.RetryableError,
|
|
self.glance.check_resp_status_and_retry,
|
|
mock_resp_other,
|
|
'fake_image_id',
|
|
'fake_url')
|
|
|
|
def test_validate_image_status_before_upload_ok_v1(self):
|
|
mock_conn = mock.Mock()
|
|
fake_url = 'http://fake_host/fake_path/fake_image_id'
|
|
mock_check_resp_status_and_retry = self.mock_patch_object(
|
|
self.glance, 'check_resp_status_and_retry')
|
|
mock_head_resp = mock.Mock()
|
|
mock_head_resp.status = httplib.OK
|
|
mock_head_resp.read.return_value = 'fakeData'
|
|
mock_head_resp.getheader.return_value = 'queued'
|
|
mock_conn.getresponse.return_value = mock_head_resp
|
|
|
|
self.glance.validate_image_status_before_upload_v1(
|
|
mock_conn, fake_url, extra_headers=mock.Mock())
|
|
|
|
self.assertTrue(mock_conn.getresponse.called)
|
|
self.assertEqual(mock_head_resp.read.call_count, 2)
|
|
self.assertFalse(mock_check_resp_status_and_retry.called)
|
|
|
|
def test_validate_image_status_before_upload_image_status_error_v1(self):
|
|
mock_conn = mock.Mock()
|
|
fake_url = 'http://fake_host/fake_path/fake_image_id'
|
|
mock_head_resp = mock.Mock()
|
|
mock_head_resp.status = httplib.OK
|
|
mock_head_resp.read.return_value = 'fakeData'
|
|
mock_head_resp.getheader.return_value = 'not-queued'
|
|
mock_conn.getresponse.return_value = mock_head_resp
|
|
|
|
self.assertRaises(self.glance.PluginError,
|
|
self.glance.validate_image_status_before_upload_v1,
|
|
mock_conn, fake_url, extra_headers=mock.Mock())
|
|
mock_conn.request.assert_called_once()
|
|
mock_conn.getresponse.assert_called_once()
|
|
self.assertEqual(mock_head_resp.read.call_count, 2)
|
|
|
|
def test_validate_image_status_before_upload_rep_body_too_long_v1(self):
|
|
mock_conn = mock.Mock()
|
|
fake_url = 'http://fake_host/fake_path/fake_image_id'
|
|
mock_head_resp = mock.Mock()
|
|
mock_head_resp.status = httplib.OK
|
|
mock_head_resp.read.return_value = 'fakeData longer than 8'
|
|
mock_head_resp.getheader.return_value = 'queued'
|
|
mock_conn.getresponse.return_value = mock_head_resp
|
|
|
|
self.assertRaises(self.glance.RetryableError,
|
|
self.glance.validate_image_status_before_upload_v1,
|
|
mock_conn, fake_url, extra_headers=mock.Mock())
|
|
mock_conn.request.assert_called_once()
|
|
mock_conn.getresponse.assert_called_once()
|
|
mock_head_resp.read.assert_called_once()
|
|
|
|
def test_validate_image_status_before_upload_req_head_exception_v1(self):
|
|
mock_conn = mock.Mock()
|
|
mock_conn.request.side_effect = Fake_HTTP_Request_Error()
|
|
fake_url = 'http://fake_host/fake_path/fake_image_id'
|
|
mock_head_resp = mock.Mock()
|
|
mock_head_resp.status = httplib.OK
|
|
mock_head_resp.read.return_value = 'fakeData'
|
|
mock_head_resp.getheader.return_value = 'queued'
|
|
mock_conn.getresponse.return_value = mock_head_resp
|
|
|
|
self.assertRaises(self.glance.RetryableError,
|
|
self.glance.validate_image_status_before_upload_v1,
|
|
mock_conn, fake_url, extra_headers=mock.Mock())
|
|
mock_conn.request.assert_called_once()
|
|
mock_head_resp.read.assert_not_called()
|
|
mock_conn.getresponse.assert_not_called()
|
|
|
|
def test_validate_image_status_before_upload_unexpected_resp_v1(self):
|
|
mock_conn = mock.Mock()
|
|
fake_url = 'http://fake_host/fake_path/fake_image_id'
|
|
parts = urlparse(fake_url)
|
|
path = parts[2]
|
|
fake_image_id = path.split('/')[-1]
|
|
mock_head_resp = mock.Mock()
|
|
mock_head_resp.status = httplib.BAD_REQUEST
|
|
mock_head_resp.read.return_value = 'fakeData'
|
|
mock_head_resp.getheader.return_value = 'queued'
|
|
mock_conn.getresponse.return_value = mock_head_resp
|
|
self.mock_patch_object(self.glance, 'check_resp_status_and_retry')
|
|
|
|
self.glance.validate_image_status_before_upload_v1(
|
|
mock_conn, fake_url, extra_headers=mock.Mock())
|
|
self.assertEqual(mock_head_resp.read.call_count, 2)
|
|
self.glance.check_resp_status_and_retry.assert_called_with(
|
|
mock_head_resp, fake_image_id, fake_url)
|
|
mock_conn.request.assert_called_once()
|
|
|
|
def test_validate_image_status_before_upload_ok_v2_using_api_service(self):
|
|
mock_conn = mock.Mock()
|
|
fake_url = 'http://fake_host:fake_port/fake_path/fake_image_id'
|
|
mock_check_resp_status_and_retry = self.mock_patch_object(
|
|
self.glance, 'check_resp_status_and_retry')
|
|
mock_head_resp = mock.Mock()
|
|
mock_head_resp.status = httplib.OK
|
|
mock_head_resp.read.return_value = '{"status": "queued"}'
|
|
mock_conn.getresponse.return_value = mock_head_resp
|
|
fake_extra_headers = mock.Mock()
|
|
expected_api_path = '/v2/images/%s' % 'fake_image_id'
|
|
|
|
self.glance.validate_image_status_before_upload_v2(
|
|
mock_conn, fake_url, fake_extra_headers, expected_api_path)
|
|
|
|
self.assertTrue(mock_conn.getresponse.called)
|
|
self.assertEqual(
|
|
mock_head_resp.read.call_count, 2)
|
|
self.assertFalse(mock_check_resp_status_and_retry.called)
|
|
mock_conn.request.assert_called_with('GET',
|
|
'/v2/images/fake_image_id',
|
|
headers=fake_extra_headers)
|
|
|
|
def test_validate_image_status_before_upload_ok_v2_using_uwsgi(self):
|
|
mock_conn = mock.Mock()
|
|
fake_url = 'http://fake_host/fake_path/fake_image_id'
|
|
mock_check_resp_status_and_retry = self.mock_patch_object(
|
|
self.glance, 'check_resp_status_and_retry')
|
|
mock_head_resp = mock.Mock()
|
|
mock_head_resp.status = httplib.OK
|
|
mock_head_resp.read.return_value = '{"status": "queued"}'
|
|
mock_conn.getresponse.return_value = mock_head_resp
|
|
|
|
fake_extra_headers = mock.Mock()
|
|
fake_patch_path = 'fake_patch_path'
|
|
|
|
self.glance.validate_image_status_before_upload_v2(
|
|
mock_conn, fake_url, fake_extra_headers, fake_patch_path)
|
|
|
|
self.assertTrue(mock_conn.getresponse.called)
|
|
self.assertEqual(
|
|
mock_head_resp.read.call_count, 2)
|
|
self.assertFalse(mock_check_resp_status_and_retry.called)
|
|
mock_conn.request.assert_called_with('GET',
|
|
'fake_patch_path',
|
|
headers=fake_extra_headers)
|
|
|
|
def test_validate_image_status_before_upload_get_image_failed_v2(self):
|
|
mock_conn = mock.Mock()
|
|
mock_conn.request.side_effect = Fake_HTTP_Request_Error()
|
|
fake_url = 'http://fake_host/fake_path/fake_image_id'
|
|
mock_head_resp = mock.Mock()
|
|
mock_head_resp.status = httplib.OK
|
|
mock_conn.getresponse.return_value = mock_head_resp
|
|
expected_wsgi_path = '/fake_path/v2/images/%s' % 'fake_image_id'
|
|
|
|
self.assertRaises(self.glance.RetryableError,
|
|
self.glance.validate_image_status_before_upload_v2,
|
|
mock_conn, fake_url, mock.Mock(), expected_wsgi_path)
|
|
mock_conn.request.assert_called_once()
|
|
mock_head_resp.read.assert_not_called()
|
|
mock_conn.getresponse.assert_not_called()
|
|
|
|
def test_validate_image_status_before_upload_unexpected_resp_v2(self):
|
|
mock_conn = mock.Mock()
|
|
fake_url = 'http://fake_host/fake_path/fake_image_id'
|
|
self.mock_patch_object(self.glance, 'check_resp_status_and_retry')
|
|
mock_head_resp = mock.Mock()
|
|
mock_head_resp.status = httplib.BAD_REQUEST
|
|
mock_conn.getresponse.return_value = mock_head_resp
|
|
expected_wsgi_path = '/fake_path/v2/images/%s' % 'fake_image_id'
|
|
|
|
self.glance.validate_image_status_before_upload_v2(
|
|
mock_conn, fake_url, mock.Mock(), expected_wsgi_path)
|
|
mock_conn.request.assert_called_once()
|
|
mock_conn.getresponse.assert_called_once()
|
|
mock_head_resp.read.assert_called_once()
|
|
self.glance.check_resp_status_and_retry.assert_called_once()
|
|
|
|
def test_validate_image_status_before_upload_failed_v2(self):
|
|
mock_conn = mock.Mock()
|
|
fake_url = 'http://fake_host/fake_path/fake_image_id'
|
|
mock_head_resp = mock.Mock()
|
|
mock_head_resp.status = httplib.OK
|
|
mock_head_resp.read.return_value = '{"status": "not-queued"}'
|
|
mock_conn.getresponse.return_value = mock_head_resp
|
|
expected_wsgi_path = '/fake_path/v2/images/%s' % 'fake_image_id'
|
|
|
|
self.assertRaises(self.glance.PluginError,
|
|
self.glance.validate_image_status_before_upload_v2,
|
|
mock_conn, fake_url, mock.Mock(), expected_wsgi_path)
|
|
mock_conn.request.assert_called_once()
|
|
mock_head_resp.read.assert_called_once()
|
|
|
|
def test_download_vhd2_v1(self):
|
|
fake_api_version = 1
|
|
mock_make_staging_area = self.mock_patch_object(
|
|
self.glance.utils, 'make_staging_area', 'fake_staging_path')
|
|
mock_download_tarball_by_url = self.mock_patch_object(
|
|
self.glance, '_download_tarball_by_url_v1')
|
|
mock_import_vhds = self.mock_patch_object(
|
|
self.glance.utils, 'import_vhds')
|
|
mock_cleanup_staging_area = self.mock_patch_object(
|
|
self.glance.utils, 'cleanup_staging_area')
|
|
|
|
self.glance.download_vhd2(
|
|
'fake_session', 'fake_image_id', 'fake_endpoint',
|
|
'fake_uuid_stack', 'fake_sr_path', 'fake_extra_headers',
|
|
fake_api_version)
|
|
|
|
mock_make_staging_area.assert_called_with('fake_sr_path')
|
|
mock_download_tarball_by_url.assert_called_with('fake_sr_path',
|
|
'fake_staging_path',
|
|
'fake_image_id',
|
|
'fake_endpoint',
|
|
'fake_extra_headers')
|
|
mock_import_vhds.assert_called_with('fake_sr_path',
|
|
'fake_staging_path',
|
|
'fake_uuid_stack')
|
|
mock_cleanup_staging_area.assert_called_with('fake_staging_path')
|
|
|
|
def test_download_vhd2_v2(self):
|
|
fake_api_version = 2
|
|
mock_make_staging_area = self.mock_patch_object(
|
|
self.glance.utils, 'make_staging_area', 'fake_staging_path')
|
|
mock_download_tarball_by_url = self.mock_patch_object(
|
|
self.glance, '_download_tarball_by_url_v2')
|
|
mock_import_vhds = self.mock_patch_object(
|
|
self.glance.utils, 'import_vhds')
|
|
mock_cleanup_staging_area = self.mock_patch_object(
|
|
self.glance.utils, 'cleanup_staging_area')
|
|
|
|
self.glance.download_vhd2(
|
|
'fake_session', 'fake_image_id', 'fake_endpoint',
|
|
'fake_uuid_stack', 'fake_sr_path', 'fake_extra_headers',
|
|
fake_api_version)
|
|
|
|
mock_make_staging_area.assert_called_with('fake_sr_path')
|
|
mock_download_tarball_by_url.assert_called_with('fake_sr_path',
|
|
'fake_staging_path',
|
|
'fake_image_id',
|
|
'fake_endpoint',
|
|
'fake_extra_headers')
|
|
mock_import_vhds.assert_called_with('fake_sr_path',
|
|
'fake_staging_path',
|
|
'fake_uuid_stack')
|
|
mock_cleanup_staging_area.assert_called_with('fake_staging_path')
|
|
|
|
def test_upload_vhd2_v1(self):
|
|
fake_api_version = 1
|
|
mock_make_staging_area = self.mock_patch_object(
|
|
self.glance.utils, 'make_staging_area', 'fake_staging_path')
|
|
mock_prepare_staging_area = self.mock_patch_object(
|
|
self.glance.utils, 'prepare_staging_area')
|
|
mock_upload_tarball_by_url = self.mock_patch_object(
|
|
self.glance, '_upload_tarball_by_url_v1')
|
|
mock_cleanup_staging_area = self.mock_patch_object(
|
|
self.glance.utils, 'cleanup_staging_area')
|
|
|
|
self.glance.upload_vhd2(
|
|
'fake_session', 'fake_vid_uuids', 'fake_image_id',
|
|
'fake_endpoint', 'fake_sr_path', 'fake_extra_headers',
|
|
'fake_properties', fake_api_version)
|
|
mock_make_staging_area.assert_called_with('fake_sr_path')
|
|
mock_upload_tarball_by_url.assert_called_with('fake_staging_path',
|
|
'fake_image_id',
|
|
'fake_endpoint',
|
|
'fake_extra_headers',
|
|
'fake_properties')
|
|
mock_prepare_staging_area.assert_called_with('fake_sr_path',
|
|
'fake_staging_path',
|
|
'fake_vid_uuids')
|
|
mock_cleanup_staging_area.assert_called_with('fake_staging_path')
|
|
|
|
def test_upload_vhd2_v2(self):
|
|
fake_api_version = 2
|
|
mock_make_staging_area = self.mock_patch_object(
|
|
self.glance.utils, 'make_staging_area', 'fake_staging_path')
|
|
mock_prepare_staging_area = self.mock_patch_object(
|
|
self.glance.utils, 'prepare_staging_area')
|
|
mock_upload_tarball_by_url = self.mock_patch_object(
|
|
self.glance, '_upload_tarball_by_url_v2')
|
|
mock_cleanup_staging_area = self.mock_patch_object(
|
|
self.glance.utils, 'cleanup_staging_area')
|
|
|
|
self.glance.upload_vhd2(
|
|
'fake_session', 'fake_vid_uuids', 'fake_image_id',
|
|
'fake_endpoint', 'fake_sr_path', 'fake_extra_headers',
|
|
'fake_properties', fake_api_version)
|
|
|
|
mock_make_staging_area.assert_called_with('fake_sr_path')
|
|
mock_upload_tarball_by_url.assert_called_with('fake_staging_path',
|
|
'fake_image_id',
|
|
'fake_endpoint',
|
|
'fake_extra_headers',
|
|
'fake_properties')
|
|
mock_prepare_staging_area.assert_called_with('fake_sr_path',
|
|
'fake_staging_path',
|
|
'fake_vid_uuids')
|
|
mock_cleanup_staging_area.assert_called_with('fake_staging_path')
|