# 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_urlopen.assert_called_with(fake_request)
        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_urlopen.assert_called_with(fake_request)
        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'
        expected_url = "%(glance_endpoint)s/v2/images/%(image_id)s/file" % {
            '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_v2(
            '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')

    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'
        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'

        self.glance._upload_tarball_by_url_v2(
            '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,
                                               expected_wsgi_path)
        self.assertTrue(mock_update_image_meta.called)
        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'
        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'

        self.glance._upload_tarball_by_url_v2(
            'fake_staging_path', 'fake_image_id', fake_endpoint,
            fake_extra_headers, fake_properties)

        self.assertTrue(mock_HTTPSConn.called)
        self.assertTrue(mock_update_image_meta.called)
        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'
        expected_url = "%(glance_endpoint)s/v2/images/%(image_id)s/file" % {
            'glance_endpoint': fake_endpoint,
            'image_id': 'fake_image_id'}
        expected_api_path = '/v2/images/%s' % 'fake_image_id'

        self.glance._upload_tarball_by_url_v2(
            'fake_staging_path', 'fake_image_id', fake_endpoint,
            fake_extra_headers, fake_properties)

        self.assertTrue(mock_Conn.called)
        self.assertTrue(mock_update_image_meta.called)
        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'
        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'

        self.glance._upload_tarball_by_url_v2(
            'fake_staging_path', 'fake_image_id', fake_endpoint,
            fake_extra_headers, fake_properties)

        self.assertTrue(mock_Conn.called)
        self.assertTrue(mock_update_image_meta.called)
        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'
        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'

        self.glance._upload_tarball_by_url_v2(
            'fake_staging_path', 'fake_image_id', fake_endpoint,
            fake_extra_headers, fake_properties)

        self.assertTrue(mock_HTTPSConn.called)
        self.assertTrue(mock_update_image_meta.called)
        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')