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

# Copyright (c) 2013 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.

"""
Session and API call management for VMware ESX/VC server.
Provides abstraction over cinder.volume.drivers.vmware.vim.Vim SOAP calls.
"""

from eventlet import event

from cinder.openstack.common import log as logging
from cinder.openstack.common import loopingcall
from cinder.volume.drivers.vmware import error_util
from cinder.volume.drivers.vmware import vim
from cinder.volume.drivers.vmware import vim_util

LOG = logging.getLogger(__name__)


class Retry(object):
    """Decorator for retrying a function upon suggested exceptions.

    The method retries for given number of times and the sleep
    time increments till the max sleep time is reached.
    If max retries is set to -1, then the decorated function is
    invoked indefinitely till no exception is thrown or if
    the caught exception is not in the list of suggested exceptions.
    """

    def __init__(self, max_retry_count=-1, inc_sleep_time=10,
                 max_sleep_time=60, exceptions=()):
        """Initialize retry object based on input params.

        :param max_retry_count: Max number of times, a function must be
                                retried when one of input 'exceptions'
                                is caught. The default -1 will always
                                retry the function till a non-exception
                                case, or an un-wanted error case arises.
        :param inc_sleep_time: Incremental time in seconds for sleep time
                               between retrial
        :param max_sleep_time: Max sleep time beyond which the sleep time will
                               not be incremented using param inc_sleep_time
                               and max_sleep_time will be used as sleep time
        :param exceptions: Suggested exceptions for which the function must be
                           retried
        """
        self._max_retry_count = max_retry_count
        self._inc_sleep_time = inc_sleep_time
        self._max_sleep_time = max_sleep_time
        self._exceptions = exceptions
        self._retry_count = 0
        self._sleep_time = 0

    def __call__(self, f):

        def _func(done, *args, **kwargs):
            try:
                result = f(*args, **kwargs)
                done.send(result)
            except self._exceptions as excep:
                LOG.exception(_("Failure while invoking function: "
                                "%(func)s. Error: %(excep)s.") %
                              {'func': f.__name__, 'excep': excep})
                if (self._max_retry_count != -1 and
                        self._retry_count >= self._max_retry_count):
                    done.send_exception(excep)
                else:
                    self._retry_count += 1
                    self._sleep_time += self._inc_sleep_time
                    return self._sleep_time
            except Exception as excep:
                done.send_exception(excep)
            return 0

        def func(*args, **kwargs):
            done = event.Event()
            loop = loopingcall.DynamicLoopingCall(_func, done, *args, **kwargs)
            loop.start(periodic_interval_max=self._max_sleep_time)
            result = done.wait()
            loop.stop()
            return result

        return func


class VMwareAPISession(object):
    """Sets up a session with the server and handles all calls made to it."""

    @Retry(exceptions=(Exception))
    def __init__(self, server_ip, server_username, server_password,
                 api_retry_count, task_poll_interval, scheme='https',
                 create_session=True, wsdl_loc=None):
        """Constructs session object.

        :param server_ip: IP address of ESX/VC server
        :param server_username: Username of ESX/VC server admin user
        :param server_password: Password for param server_username
        :param api_retry_count: Number of times an API must be retried upon
                                session/connection related errors