# Copyright (c) 2014 VMware, Inc.
# 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.

"""
Classes and utility methods for datastore selection.
"""

from oslo_log import log as logging
from oslo_utils import excutils
from oslo_vmware import exceptions
from oslo_vmware import pbm

from cinder.i18n import _LE, _LW
from cinder.volume.drivers.vmware import exceptions as vmdk_exceptions


LOG = logging.getLogger(__name__)


class DatastoreType(object):
    """Supported datastore types."""

    NFS = "nfs"
    VMFS = "vmfs"
    VSAN = "vsan"
    VVOL = "vvol"

    _ALL_TYPES = {NFS, VMFS, VSAN, VVOL}

    @staticmethod
    def get_all_types():
        return DatastoreType._ALL_TYPES


class DatastoreSelector(object):
    """Class for selecting datastores which satisfy input requirements."""

    HARD_AFFINITY_DS_TYPE = "hardAffinityDatastoreTypes"
    HARD_ANTI_AFFINITY_DS = "hardAntiAffinityDatastores"
    PREF_UTIL_THRESH = "preferredUtilizationThreshold"
    SIZE_BYTES = "sizeBytes"
    PROFILE_NAME = "storageProfileName"

    # TODO(vbala) Remove dependency on volumeops.
    def __init__(self, vops, session):
        self._vops = vops
        self._session = session

    def get_profile_id(self, profile_name):
        """Get vCenter profile ID for the given profile name.

        :param profile_name: profile name
        :return: vCenter profile ID
        :raises: ProfileNotFoundException
        """
        profile_id = pbm.get_profile_id_by_name(self._session, profile_name)
        if profile_id is None:
            LOG.error(_LE("Storage profile: %s cannot be found in vCenter."),
                      profile_name)
            raise vmdk_exceptions.ProfileNotFoundException(
                storage_profile=profile_name)
        LOG.debug("Storage profile: %(name)s resolved to vCenter profile ID: "
                  "%(id)s.",
                  {'name': profile_name,
                   'id': profile_id})
        return profile_id

    def _filter_by_profile(self, datastores, profile_id):
        """Filter out input datastores that do not match the given profile."""
        cf = self._session.pbm.client.factory
        hubs = pbm.convert_datastores_to_hubs(cf, datastores)
        filtered_hubs = pbm.filter_hubs_by_profile(self._session, hubs,
                                                   profile_id)
        return pbm.filter_datastores_by_hubs(filtered_hubs, datastores)

    def _filter_datastores(self, datastores, size_bytes, profile_id,
                           hard_anti_affinity_datastores,
                           hard_affinity_ds_types):
        """Filter datastores based on profile, size and affinity."""
        LOG.debug(
            "Filtering datastores: %(datastores)s based on size (bytes): "
            "%(size)d, profile: %(profile)s, hard-anti-affinity-datastores: "
            "%(hard_anti_affinity_datastores)s, hard-affinity-datastore-types:"
            " %(hard_affinity_ds_types)s.",
            {'datastores': datastores,
             'size': size_bytes,
             'profile': profile_id,
             'hard_anti_affinity_datastores': hard_anti_affinity_datastores,
             'hard_affinity_ds_types': hard_affinity_ds_types})
        if hard_anti_affinity_datastores is None:
            hard_anti_affinity_datastores = []
        filtered_datastores = [ds for ds in datastores if ds.value not in
                               hard_anti_affinity_datastores]

        if filtered_datastores and profile_id is not None: