# Copyright (c) 2015 Clinton Knight.  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.
"""
Unified driver for NetApp storage systems.

Supports multiple storage systems of different families and driver modes.
"""

from oslo_log import log
from oslo_utils import importutils

from manila import exception
from manila.i18n import _, _LI
from manila.share import driver
from manila.share.drivers.netapp import options
from manila.share.drivers.netapp import utils as na_utils


LOG = log.getLogger(__name__)

MULTI_SVM = 'multi_svm'
SINGLE_SVM = 'single_svm'
DATAONTAP_CMODE_PATH = 'manila.share.drivers.netapp.dataontap.cluster_mode'

# Add new drivers here, no other code changes required.
NETAPP_UNIFIED_DRIVER_REGISTRY = {
    'ontap_cluster':
    {
        MULTI_SVM: DATAONTAP_CMODE_PATH +
        '.drv_multi_svm.NetAppCmodeMultiSvmShareDriver',
        SINGLE_SVM: DATAONTAP_CMODE_PATH +
        '.drv_single_svm.NetAppCmodeSingleSvmShareDriver',
    },
}
NETAPP_UNIFIED_DRIVER_DEFAULT_MODE = {
    'ontap_cluster': MULTI_SVM,
}


class NetAppDriver(object):
    """"NetApp unified share storage driver.

    Acts as a factory to create NetApp storage drivers based on the
    storage family and driver mode configured.
    """

    REQUIRED_FLAGS = ['netapp_storage_family', 'driver_handles_share_servers']

    def __new__(cls, *args, **kwargs):

        config = kwargs.get('configuration', None)
        if not config:
            raise exception.InvalidInput(
                reason=_('Required configuration not found.'))

        config.append_config_values(driver.share_opts)
        config.append_config_values(options.netapp_proxy_opts)
        na_utils.check_flags(NetAppDriver.REQUIRED_FLAGS, config)

        app_version = na_utils.OpenStackInfo().info()
        LOG.info(_LI('OpenStack OS Version Info: %s'), app_version)
        kwargs['app_version'] = app_version

        driver_mode = NetAppDriver._get_driver_mode(
            config.netapp_storage_family, config.driver_handles_share_servers)

        return NetAppDriver._create_driver(config.netapp_storage_family,
                                           driver_mode,
                                           *args, **kwargs)

    @staticmethod
    def _get_driver_mode(storage_family, driver_handles_share_servers):

        if driver_handles_share_servers is None:
            driver_mode = NETAPP_UNIFIED_DRIVER_DEFAULT_MODE.get(
                storage_family.lower())

            if driver_mode:
                LOG.debug('Default driver mode %s selected.', driver_mode)
            else:
                raise exception.InvalidInput(
                    reason=_('Driver mode was not specified and a default '
                             'value could not be determined from the '
                             'specified storage family.'))
        elif driver_handles_share_servers:
            driver_mode = MULTI_SVM
        else:
            driver_mode = SINGLE_SVM

        return driver_mode

    @staticmethod
    def _create_driver(storage_family, driver_mode, *args, **kwargs):
        """"Creates an appropriate driver based on family and mode."""

        storage_family = storage_family.lower()

        fmt = {'storage_family': storage_family, 'driver_mode': driver_mode}
        LOG.info(_LI('Requested unified config: %(storage_family)s and '
                     '%(driver_mode)s.') % fmt)

        family_meta = NETAPP_UNIFIED_DRIVER_REGISTRY.get(storage_family)
        if family_meta is None:
            raise exception.InvalidInput(
                reason=_('Storage family %s is not supported.')
                % storage_family)

        driver_loc = family_meta.get(driver_mode)
        if driver_loc is None:
            raise exception.InvalidInput(
                reason=_('Driver mode %(driver_mode)s is not supported '
                         'for storage family %(storage_family)s.') % fmt)

        kwargs['netapp_mode'] = 'proxy'
        driver = importutils.import_object(driver_loc, *args, **kwargs)
        LOG.info(_LI('NetApp driver of family %(storage_family)s and mode '
                     '%(driver_mode)s loaded.') % fmt)
        return driver
