# 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 functools
import itertools
import operator

from oslo_log import log as logging
from oslo_serialization import jsonutils
from oslo_utils import excutils
import six

from nova import block_device
from nova import exception
from nova.i18n import _LE
from nova.i18n import _LI
from nova.i18n import _LW
from nova import objects
from nova.objects import base as obj_base
from nova.volume import encryptors

LOG = logging.getLogger(__name__)


class _NotTransformable(Exception):
    pass


class _InvalidType(_NotTransformable):
    pass


class _NoLegacy(Exception):
    pass


def update_db(method):
    @functools.wraps(method)
    def wrapped(obj, context, *args, **kwargs):
        try:
            ret_val = method(obj, context, *args, **kwargs)
        finally:
            obj.save()
        return ret_val
    return wrapped


class DriverBlockDevice(dict):
    """A dict subclass that represents block devices used by the virt layer.

    Uses block device objects internally to do the database access.

    _fields and _legacy_fields class attributes present a set of fields that
    are expected on a certain DriverBlockDevice type. We may have more legacy
    versions in the future.

    If an attribute access is attempted for a name that is found in the
    _proxy_as_attr set, it will be proxied to the underlying object. This
    allows us to access stuff that is not part of the data model that all
    drivers understand.

    The save() method allows us to update the database using the underlying
    object. _update_on_save class attribute dictionary keeps the following
    mapping: