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

# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# 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.

"""Unit tests for the DB API."""

import copy
import datetime
import iso8601
import types
import u
uid as stdlib_uuid

import mox
import netaddr
from oslo.config import cfg
import six
from sqlalchemy.dialects import sqlite
from sqlalchemy import exc
from sqlalchemy.exc import IntegrityError
from sqlalchemy import MetaData
from sqlalchemy.orm import exc as 
sqlalchemy_orm_exc
from sqlalchemy.orm import query
from sqlalchemy.sql.expression import select

from nova import block_device
from nova.compute import vm_states
from nova import context
from nova import db
from nova.db.sqlalchemy import api as sqlalchemy_api
from nova.db.sqlalchemy import models
from nova.db.sqlalchemy import utils as db_utils
from nova import exception
from nova.openstack.common.db import exception as db_exc
from nova.openstack.common.db.sqlalchemy import session as db_session
from nova.openstack.common import timeutils
from nova.openstack.common import uuidutils
from nova import quota
from nova import test
from nova.tests import matchers
from nova import utils


CONF = cfg.CONF
CONF.import_opt('reserved_host_memory_mb', 'nova.compute.resource_tracker')
CONF.import_opt('reserved_host_disk_mb', 'nova.compute.resource_tracker')

get_engine = db_session.get_engine
get_session = db_session.get_session


def _reservation_get(context, uuid):
    result = sqlalchemy_api.model_query(context, models.Reservation,
            read_deleted="no").filter_by(uuid=uuid).first()

    if not result:
        raise exception.ReservationNotFound(uuid=uuid)

    return result


def _quota_reserve(context, project_id, user_id):
    """Create sample Quota, QuotaUsage and Reservation objects.

    There is no method db.quota_usage_create(), so we have to use
    db.quota_reserve() for creating QuotaUsage objects.

    Returns reservations uuids.

    """
    def get_sync(resource, usage):
        def sync(elevated, project_id, user_id, session):
            return {resource: usage}
        return sync
    quotas = {}
    user_quotas = {}
    resources = {}
    deltas = {}
    for i in range(3):
        resource = 'resource%d' % i
        if i == 2:
            # test for project level resources
            resource = 'fixed_ips'
            quotas[resource] = db.quota_create(context,
                                               project_id, resource, i)
            user_quotas[resource] = quotas[resource]
        else:
            quotas[resource] = db.quota_create(context,
                                               project_id, resource, i)
            user_quotas[resource] = db.quota_create(context, project_id,
                                                    resource, i,
                                                    user_id=user_id)
        sync_name = '_sync_%s' % resource
        resources[resource] = quota.ReservableResource(
            resource, sync_name, 'quota_res_%d' % i)
        deltas[resource] = i
        setattr(sqlalchemy_api, sync_name, get_sync(resource, i))
        sqlalchemy_api.QUOTA_SYNC_FUNCTIONS[sync_name] = getattr(
            sqlalchemy_api, sync_name)
    return db.quota_reserve(context, resources, quotas, user_quotas, deltas,
                    timeutils.utcnow(), CONF.until_refresh,
                    datetime.timedelta(days=1), project_id, user_id)


class DbTestCase(test.TestCase):
    def setUp(self):
        super(DbTestCase, self).setUp()
        self.user_id = 'fake'
        self.project_id = 'fake'
        self.context = context.RequestContext(self.user_id, self.project_id)

    def create_instance_with_args(self, **kwargs):
        args = {'reservation_id': 'a', 'image_ref': 1, 'host': 'host1',
                'node': 'node1', 'project_id': self.project_id,
                'vm_state': 'fake'}
        if 'context' in kwargs:
            ctxt = kwargs.pop('context')
            args['project_id'] = ctxt.project_id
        else:
            ctxt = self.context
        args.update(kwargs)
        return db.instance_create(ctxt, args)

    def fake_metadata(self, content):
        meta = {}
        for i in range(0, 10):
            meta["foo%i" % i] = "this is %s item %i" % (content, i)
        return meta

    def create_metadata_for_instance(self, instance_uuid):
        meta = self.fake_metadata('metadata')
        db.instance_metadata_update(self.context, instance_uuid, meta, False)
        sys_meta = self.fake_metadata('system_metadata')
        db.instance_system_metadata_update(self.context, instance_uuid,
                                           sys_meta, False)
        return meta, sys_meta


class DecoratorTestCase(test.TestCase):
    def _test_decorator_wraps_helper(self, decorator):
        def test_func():
            """Test docstring."""

        decorated_func = decorator(test_func)

        self.assertEqual(test_func.func_name, decorated_func.func_name)
        self.assertEqual(test_func.__doc__, decorated_func.__doc__)
        self.assertEqual(test_func.__module__, decorated_func.__module__)

    def test_require_context_decorator_wraps_functions_properly(self):
        self._test_decorator_wraps_helper(sqlalchemy_api.require_context)

    def test_require_admin_context_decorator_wraps_functions_properly(self):
        self._test_decorator_wraps_helper(sqlalchemy_api.require_admin_context)


def _get_fake_aggr_values():
    return {'name': 'fake_aggregate'}


def _get_fake_aggr_metadata():
    return {'fake_key1': 'fake_value1',
            'fake_key2': 'fake_value2',
            'availability_zone': 'fake_avail_zone'}


def _get_fake_aggr_hosts():
    return ['foo.openstack.org']


def _create_aggregate(context=context.get_admin_context(),
                      values=_get_fake_aggr_values(),
                      metadata=_get_fake_aggr_metadata()):
    return db.aggregate_create(context, values, metadata)


def _create_aggregate_with_hosts(context=context.get_admin_context(),
                      values=_get_fake_aggr_values(),
                      metadata=_get_fake_aggr_metadata(),
                      hosts=_get_fake_aggr_hosts()):
    result = _create_aggregate(context=context,
                               values=values, metadata=metadata)
    for host in hosts:
        db.aggregate_host_add(context, result['id'], host)
    return result


class NotDbApiTestCase(DbTestCase):
    def setUp(self):
        super(NotDbApiTestCase, self).setUp()
        self.flags(connection='notdb://', group='database')

    def test_instance_get_all_by_filters_regex_unsupported_db(self):
        # Ensure that the 'LIKE' operator is used for unsupported dbs.
        self.create_instance_with_args(display_name='test1')
        self.create_instance_with_args(display_name='test2')
        self.create_instance_with_args(display_name='diff')
        result = db.instance_get_all_by_filters(self.context,
                                                {'display_name': 'test'})
        self.assertEqual(2, len(result))
        result = db.instance_get_all_by_filters(self.context,
                                                {'display_name': 'di'})
        self.assertEqual(1, len(result))

    def test_instance_get_all_by_filters_paginate(self):
        test1 = self.create_instance_with_args(display_name='test1')
        test2 = self.create_instance_with_args(display_name='test2')
        test3 = self.create_instance_with_args(display_name='test3')
