# vim: tabstop=4 shiftwidth=4 softtabstop=4

#    Copyright 2012 Michael Still and Canonical Inc
#
#    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 copy
import imp
import json
import os
import StringIO
import sys
import UserDict

from glance.tests import utils as test_utils


TOPDIR = os.path.normpath(os.path.join(
                            os.path.dirname(os.path.abspath(__file__)),
                            os.pardir,
                            os.pardir,
                            os.pardir))
GLANCE_REPLICATOR_PATH = os.path.join(TOPDIR, 'bin', 'glance-replicator')

sys.dont_write_bytecode = True
glance_replicator = imp.load_source('glance_replicator',
                                    GLANCE_REPLICATOR_PATH)
sys.dont_write_bytecode = False


IMG_RESPONSE_ACTIVE = {
    'content-length': '0',
    'property-image_state': 'available',
    'min_ram': '0',
    'disk_format': 'aki',
    'updated_at': '2012-06-25T02:10:36',
    'date': 'Thu, 28 Jun 2012 07:20:05 GMT',
    'owner': '8aef75b5c0074a59aa99188fdb4b9e90',
    'id': '6d55dd55-053a-4765-b7bc-b30df0ea3861',
    'size': '4660272',
    'property-image_location': 'ubuntu-bucket/oneiric-server-cloudimg-amd64-'
                               'vmlinuz-generic.manifest.xml',
    'property-architecture': 'x86_64',
    'etag': 'f46cfe7fb3acaff49a3567031b9b53bb',
    'location': 'http://127.0.0.1:9292/v1/images/'
                '6d55dd55-053a-4765-b7bc-b30df0ea3861',
    'container_format': 'aki',
    'status': 'active',
    'deleted': 'False',
    'min_disk': '0',
    'is_public': 'False',
    'name': 'ubuntu-bucket/oneiric-server-cloudimg-amd64-vmlinuz-generic',
    'checksum': 'f46cfe7fb3acaff49a3567031b9b53bb',
    'created_at': '2012-06-25T02:10:32',
    'protected': 'False',
    'content-type': 'text/html; charset=UTF-8'
}

IMG_RESPONSE_QUEUED = copy.copy(IMG_RESPONSE_ACTIVE)
IMG_RESPONSE_QUEUED['status'] = 'queued'
IMG_RESPONSE_QUEUED['id'] = '49b2c782-ee10-4692-84f8-3942e9432c4b'
IMG_RESPONSE_QUEUED['location'] = ('http://127.0.0.1:9292/v1/images/'
                                   + IMG_RESPONSE_QUEUED['id'])


class FakeHTTPConnection(object):
    def __init__(self):
        self.count = 0
        self.reqs = {}
        self.last_req = None
        self.host = 'localhost'
        self.port = 9292

    def prime_request(self, method, url, in_body, in_headers,
                      out_code, out_body, out_headers):
        if not url.startswith('/'):
            url = '/' + url

        hkeys = in_headers.keys()
        hkeys.sort()
        hashable = (method, url, in_body, ' '.join(hkeys))

        flat_headers = []
        for key in out_headers:
            flat_headers.append((key, out_headers[key]))

        self.reqs[hashable] = (out_code, out_body, flat_headers)

    def request(self, method, url, body, headers):
        self.count += 1

        hkeys = headers.keys()
        hkeys.sort()
        hashable = (method, url, body, ' '.join(hkeys))

        if not hashable in self.reqs:
            options = []
            for h in self.reqs:
                options.append(repr(h))

            raise Exception('No such primed request: %s "%s"\n'
                            '%s\n\n'
                            'Available:\n'
                            '%s'
                            % (method, url, hashable, '\n\n'.join(options)))
        self.last_req = hashable

    def getresponse(self):
        class FakeResponse(object):
            def __init__(self, (code, body, headers)):
                self.body = StringIO.StringIO(body)
                self.headers = headers
                self.status = code

            def read(self, count=1000000):
                return self.body.read(count)

            def getheaders(self):
                return self.headers

        return FakeResponse(self.reqs[self.last_req])


class ImageServiceTestCase(test_utils.BaseTestCase):
    def test_rest_errors(self):
        c = glance_replicator.ImageService(FakeHTTPConnection(), 'noauth')

        for code, exc in [(400, glance_replicator.ServerErrorException),
                          (401, glance_replicator.AuthenticationException),
                          (403, glance_replicator.AuthenticationException),
                          (409,
                           glance_replicator.ImageAlreadyPresentException),
                          (500, glance_replicator.ServerErrorException)]:
            c.conn.prime_request('GET', 'v1/images/123', '',
                                 {'x-auth-token': 'noauth'}, code, '', {})
            self.assertRaises(exc, c.get_image, '123')

    def test_rest_get_images(self):
        c = glance_replicator.ImageService(FakeHTTPConnection(), 'noauth')

        # Two images, one of which is queued
        resp = {'images': [IMG_RESPONSE_ACTIVE, IMG_RESPONSE_QUEUED]}
        c.conn.prime_request('GET', 'v1/images/detail?is_public=None', '',
                             {'x-auth-token': 'noauth'},
                             200, json.dumps(resp), {})
        c.conn.prime_request('GET',
                             ('v1/images/detail?marker=%s&is_public=None'
                              % IMG_RESPONSE_QUEUED['id']),
                             '', {'x-auth-token': 'noauth'},
                             200, json.dumps({'images': []}), {})

        imgs = list(c.get_images())
        self.assertEquals(len(imgs), 2)
        self.assertEquals(c.conn.count, 2)

    def test_rest_get_image(self):
        c = glance_replicator.ImageService(FakeHTTPConnection(), 'noauth')

        image_contents = 'THISISTHEIMAGEBODY'
        c.conn.prime_request('GET',
                             'v1/images/%s' % IMG_RESPONSE_ACTIVE['id'],
                             '', {'x-auth-token': 'noauth'},
                             200, image_contents, IMG_RESPONSE_ACTIVE)

        body = c.get_image(IMG_RESPONSE_ACTIVE['id'])
        self.assertEquals(body.read(), image_contents)

    def test_rest_header_list_to_dict(self):
        i = [('x-image-meta-banana', 42),
             ('gerkin', 12),
             ('x-image-meta-property-frog', 11),
             ('x-image-meta-property-duck', 12)]
        o = glance_replicator.ImageService._header_list_to_dict(i)
        self.assertTrue('banana' in o)
        self.assertTrue('gerkin' in o)
        self.assertTrue('properties' in o)
        self.assertTrue('frog' in o['properties'])
        self.assertTrue('duck' in o['properties'])
        self.assertFalse('x-image-meta-banana' in o)

    def test_rest_get_image_meta(self):
        c = glance_replicator.ImageService(FakeHTTPConnection(), 'noauth')

        c.conn.prime_request('HEAD',
                             'v1/images/%s' % IMG_RESPONSE_ACTIVE['id'],
                             '', {'x-auth-token': 'noauth'},
                             200, '', IMG_RESPONSE_ACTIVE)

        header = c.get_image_meta(IMG_RESPONSE_ACTIVE['id'])
        self.assertTrue('id' in header)

    def test_rest_dict_to_headers(self):
        i = {'banana': 42,
             'gerkin': 12,
             'properties': {'frog': 1}
             }
        o = glance_replicator.ImageService._dict_to_headers(i)
        self.assertTrue('x-image-meta-banana' in o)
        self.assertTrue('x-image-meta-gerkin' in o)
        self.assertTrue('x-image-meta-property-frog' in o)
        self.assertFalse('properties' in o)

    def test_rest_add_image(self):
        c = glance_replicator.ImageService(FakeHTTPConnection(), 'noauth')

        image_body = 'THISISANIMAGEBODYFORSURE!'
        image_meta_with_proto = {}
        image_meta_with_proto['x-auth-token'] = 'noauth'
        image_meta_with_proto['Content-Type'] = 'application/octet-stream'
        image_meta_with_proto['Content-Length'] = len(image_body)

        for key in IMG_RESPONSE_ACTIVE:
            image_meta_with_proto['x-image-meta-%s' % key] = \
                IMG_RESPONSE_ACTIVE[key]

        c.conn.prime_request('POST', 'v1/images',
                             image_body, image_meta_with_proto,
                             200, '', IMG_RESPONSE_ACTIVE)

        headers, body = c.add_image(IMG_RESPONSE_ACTIVE, image_body)
        self.assertEquals(headers, IMG_RESPONSE_ACTIVE)
        self.assertEquals(c.conn.count, 1)

    def test_rest_add_image_meta(self):
        c = glance_replicator.ImageService(FakeHTTPConnection(), 'noauth')

        image_meta = {'id': 123}
        image_meta_headers = \
            glance_replicator.ImageService._dict_to_headers(image_meta)
        image_meta_headers['x-auth-token'] = 'noauth'
        image_meta_headers['Content-Type'] = 'application/octet-stream'
        c.conn.prime_request('PUT', 'v1/images/%s' % image_meta['id'],
                             '', image_meta_headers, 200, '', '')
        headers, body = c.add_image_meta(image_meta)


class FakeImageService(object):
    def __init__(self, http_conn, authtoken):
        pass

    def get_images(self):
        return [{'status': 'active', 'size': 100, 'id': 123},
                {'status': 'deleted', 'size': 200, 'id': 456},
                {'status': 'active', 'size': 300, 'id': 789}]


class ReplicationCommandsTestCase(test_utils.BaseTestCase):
    def test_replication_size(self):
        options = UserDict.UserDict()
        options.slavetoken = 'slavetoken'
        args = ['localhost:9292']

        stdout = sys.stdout
        sys.stdout = StringIO.StringIO()
        try:
            glance_replicator.replication_size(options, args,
                                               imageservice=FakeImageService)
            sys.stdout.seek(0)
            output = sys.stdout.read()
        finally:
            sys.stdout = stdout

        output = output.rstrip()
        self.assertEqual(output, 'Total size is 400 bytes across 2 images')
