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

# Copyright 2012 Nicira, 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.
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#

from oslo.config import cfg

from neutron.openstack.common import log as logging
from neutron.plugins.nicira.common import exceptions

LOG = logging.getLogger(__name__)
DEFAULT_PORT = 443
# Raise if one of those attributes is not specified
REQUIRED_ATTRIBUTES = ['default_tz_uuid', 'nvp_user',
                       'nvp_password', 'nvp_controllers']
# Emit a INFO log if one of those attributes is not specified
IMPORTANT_ATTRIBUTES = ['default_l3_gw_service_uuid']
# Deprecated attributes
DEPRECATED_ATTRIBUTES = ['metadata_dhcp_host_route',
                         'nvp_controller_connection']


class NVPCluster(object):
    """NVP cluster class.

    Encapsulates controller connections and the API client for a NVP cluster.
    Accessed within the NvpPluginV2 class.

    Controller-specific parameters, such as timeouts are stored in the
    elements of the controllers attribute, which are dicts.
    """

    def __init__(self, **kwargs):
        self._required_attributes = REQUIRED_ATTRIBUTES[:]
        self._important_attributes = IMPORTANT_ATTRIBUTES[:]
        self._deprecated_attributes = {}
        self._sanity_check(kwargs)

        for opt, val in self._deprecated_attributes.iteritems():
            LOG.deprecated(_("Attribute '%s' has been deprecated or moved "
                             "to a new section. See new configuration file "
                             "for details."), opt)
            depr_func = getattr(self, '_process_%s' % opt, None)
            if depr_func:
                depr_func(val)

        # If everything went according to plan these two lists should be empty
        if self._required_attributes:
            raise exceptions.NvpInvalidClusterConfiguration(
                invalid_attrs=self._required_attributes)
        if self._important_attributes:
            LOG.info(_("The following cluster attributes were "
                       "not specified: %s'"), self._important_attributes)
        # The API client will be explicitly created by users of this class
        self.api_client = None

    def _sanity_check(self, options):
        # Iterating this way ensures the conf parameters also
        # define the structure of this class
        for arg in cfg.CONF:
            if arg not in DEPRECATED_ATTRIBUTES:
                setattr(self, arg, options.get(arg, cfg.CONF.get(arg)))
                self._process_attribute(arg)
            elif options.get(arg) is not None:
                # Process deprecated attributes only if specified
                self._deprecated_attributes[arg] = options.get(arg)
            if arg.startswith("CLUSTER:"):
                cluster_section = cfg.CONF.get(arg)
                for option in cluster_section:
                    v = cluster_section.get(option)
                    if option not in DEPRECATED_ATTRIBUTES:
                        # option may be in dict, but with None value
                        setattr(self, option, options.get(option) or v)
                        self._process_attribute(option)
                    else:
                        self._deprecated_attributes[option] = v

    def _process_attribute(self, attribute):
        # Process the attribute only if it's not empty!
        if getattr(self, attribute, None):
            if attribute in self._required_attributes:
                self._required_attributes.remove(attribute)
            if attribute in self._important_attributes:
                self._important_attributes.remove(attribute)
            handler_func = getattr(self, '_process_%s' % attribute, None)
            if handler_func:
                handler_func()
        else:
            LOG.debug(_("Attribute:%s is empty or null"), attribute)

    def _process_nvp_controllers(self):
        # If this raises something is not right, so let it bubble up
        # TODO(salvatore-orlando): Also validate attribute here
        for i, ctrl in enumerate(self.nvp_controllers or []):
            if len(ctrl.split(':')) == 1:
                self.nvp_controllers[i] = '%s:%s' % (ctrl, DEFAULT_PORT)

    def _process_nvp_controller_connection(self, connections):

        def parse_conn_str(ip, port, user, password, req_timeout,
                           http_timeout, retries, redirects):
            # TODO(salvatore-orlando): Set the attributes only
            # if correspondent non-deprecated options have been
            # explicitly specified in the ini file
            # TODO(salvatore-orlando): Validate data to avoid ugly ValueError
            self.nvp_user = user
            self._process_attribute('nvp_user')
            self.nvp_password = password
            self._process_attribute('nvp_password')
            self.req_timeout = int(req_timeout)
            self._process_attribute('req_timeout')
            self.http_timeout = int(http_timeout)
            self._process_attribute('http_timeout')
            self.retries = int(retries)
            self._process_attribute('retries')
            self.redirects = int(redirects)
            self._process_attribute('redirects')
            try:
                nvp_controllers = getattr(self, 'nvp_controllers')
                nvp_controllers.append('%s:%s' % (ip, port))
            except AttributeError:
                self.nvp_controllers = ['%s:%s' % (ip, port)]
                self._process_attribute('nvp_controllers')
        for conn in connections:
            parse_conn_str(*conn.split(':'))
