# Copyright 2012 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.
#

import httplib

from neutron.openstack.common import log as logging
from neutron.plugins.vmware.api_client import base
from neutron.plugins.vmware.api_client import eventlet_client
from neutron.plugins.vmware.api_client import eventlet_request
from neutron.plugins.vmware.api_client import exception
from neutron.plugins.vmware.api_client import version

LOG = logging.getLogger(__name__)


class NsxApiClient(eventlet_client.EventletApiClient):
    """The Nsx API Client."""

    def __init__(self, api_providers, user, password,
                 concurrent_connections=base.DEFAULT_CONCURRENT_CONNECTIONS,
                 gen_timeout=base.GENERATION_ID_TIMEOUT,
                 use_https=True,
                 connect_timeout=base.DEFAULT_CONNECT_TIMEOUT,
                 request_timeout=30, http_timeout=10, retries=2, redirects=2):
        '''Constructor. Adds the following:

        :param request_timeout: all operations (including retries, redirects
            from unresponsive controllers, etc) should finish within this
            timeout.
        :param http_timeout: how long to wait before aborting an
            unresponsive controller (and allow for retries to another
            controller in the cluster)
        :param retries: the number of concurrent connections.
        :param redirects: the number of concurrent connections.
        '''
        super(NsxApiClient, self).__init__(
            api_providers, user, password,
            concurrent_connections=concurrent_connections,
            gen_timeout=gen_timeout, use_https=use_https,
            connect_timeout=connect_timeout)

        self._request_timeout = request_timeout
        self._http_timeout = http_timeout
        self._retries = retries
        self._redirects = redirects
        self._version = None

    # NOTE(salvatore-orlando): This method is not used anymore. Login is now
    # performed automatically inside the request eventlet if necessary.
    def login(self, user=None, password=None):
        '''Login to NSX controller.

        Assumes same password is used for all controllers.

        :param user: controller user (usually admin). Provided for
                backwards compatibility. In the  normal mode of operation
                this should be None.
        :param password: controller password. Provided for backwards
                compatibility. In the normal mode of operation this should
                be None.
        '''
        if user:
            self._user = user
        if password:
            self._password = password

        return self._login()

    def request(self, method, url, body="", content_type="application/json"):
        '''Issues request to controller.'''

        g = eventlet_request.GenericRequestEventlet(
            self, method, url, body, content_type, auto_login=True,
            request_timeout=self._request_timeout,
            http_timeout=self._http_timeout,
            retries=self._retries, redirects=self._redirects)
        g.start()
        response = g.join()
        LOG.debug(_('Request returns "%s"'), response)

        # response is a modified HTTPResponse object or None.
        # response.read() will not work on response as the underlying library
        # request_eventlet.ApiRequestEventlet has already called this
        # method in order to extract the body and headers for processing.
        # ApiRequestEventlet derived classes call .read() and
        # .getheaders() on the HTTPResponse objects and store the results in
        # the response object's .body and .headers data members for future
        # access.

        if response is None:
            # Timeout.
            LOG.error(_('Request timed out: %(method)s to %(url)s'),
                      {'method': method, 'url': url})
            raise exception.RequestTimeout()

        status = response.status
        if status == httplib.UNAUTHORIZED:
            raise exception.UnAuthorizedRequest()

        # Fail-fast: Check for exception conditions and raise the
        # appropriate exceptions for known error codes.
        if status in exception.ERROR_MAPPINGS:
            LOG.error(_("Received error code: %s"), status)
            LOG.error(_("Server Error Message: %s"), response.body)
            exception.ERROR_MAPPINGS[status](response)

        # Continue processing for non-error condition.
        if (status != httplib.OK and status != httplib.CREATED
                and status != httplib.NO_CONTENT):
            LOG.error(_("%(method)s to %(url)s, unexpected response code: "
                        "%(status)d (content = '%(body)s')"),
                      {'method': method, 'url': url,
                       'status': response.status, 'body': response.body})
            return None

        if not self._version:
            self._version = version.find_version(response.headers)
        return response.body

    def get_version(self):
        if not self._version:
            # Determine the controller version by querying the
            # cluster nodes. Currently, the version will be the
            # one of the server that responds.
            self.request('GET', '/ws.v1/control-cluster/node')
            if not self._version:
                LOG.error(_('Unable to determine NSX version. '
                          'Plugin might not work as expected.'))
        return self._version
