XenAPI: add unit test for the plugin - glance: the last part

Add unit test for the xenapi plugin -glance and also add
a boolean return value to _download_tarball_and_verify
so that it's eaiser to check the result for this function.
This commit is for upload image and vhd
At the same time, fix the keyword error in _update_image_meta_v2

Change-Id: Idad364a4e10f6e19a3d2344baa00272da12de316
This commit is contained in:
naichuans 2017-05-10 08:42:42 +00:00
parent 783cfbd93b
commit c77de9ce83
2 changed files with 326 additions and 2 deletions
os_xenapi
dom0/etc/xapi.d/plugins
tests/plugins

@ -317,7 +317,7 @@ def _update_image_meta_v2(conn, image_id, extra_headers, properties):
for key, value in properties.items(): for key, value in properties.items():
prop = {"path": "/%s" % key.replace('_', '-'), prop = {"path": "/%s" % key.replace('_', '-'),
"value": key, "value": str(value),
"op": "add"} "op": "add"}
body.append(prop) body.append(prop)
body = json.dumps(body) body = json.dumps(body)

@ -20,17 +20,23 @@ try:
import urllib2 import urllib2
from urllib2 import HTTPError from urllib2 import HTTPError
from urllib2 import URLError from urllib2 import URLError
from urlparse import urlparse
except ImportError: except ImportError:
# make py3.x happy: it's needed for script parsing, although this test # make py3.x happy: it's needed for script parsing, although this test
# is excluded from py3.x testing # is excluded from py3.x testing
import http.client as httplib import http.client as httplib
from urllib.error import HTTPError from urllib.error import HTTPError
from urllib.error import URLError from urllib.error import URLError
from urllib.parse import urlparse
import urllib.request as urllib2 import urllib.request as urllib2
import json
from os_xenapi.tests.plugins import plugin_test from os_xenapi.tests.plugins import plugin_test
class Fake_HTTP_Request_Error(Exception):
pass
class GlanceTestCase(plugin_test.PluginTestBase): class GlanceTestCase(plugin_test.PluginTestBase):
def setUp(self): def setUp(self):
super(GlanceTestCase, self).setUp() super(GlanceTestCase, self).setUp()
@ -409,3 +415,321 @@ class GlanceTestCase(plugin_test.PluginTestBase):
self.assertTrue( self.assertTrue(
mock_HTTPSConn.return_value.getresponse.called) mock_HTTPSConn.return_value.getresponse.called)
self.assertTrue(mock_check_resp_status.called) self.assertTrue(mock_check_resp_status.called)
def test_update_image_meta_ok_v2(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
self.glance._update_image_meta_v2(fake_conn, 'fake_image_id',
fake_extra_headers, fake_properties)
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_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_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(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
self.glance.validate_image_status_before_upload_v2(
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)
mock_conn.request.assert_called_once()
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
self.assertRaises(self.glance.RetryableError,
self.glance.validate_image_status_before_upload_v2,
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_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
self.glance.validate_image_status_before_upload_v2(
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()
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
self.assertRaises(self.glance.PluginError,
self.glance.validate_image_status_before_upload_v2,
mock_conn, fake_url, extra_headers=mock.Mock())
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')