class CacheEncoder(json.JSONEncoder):
class SMCResult(object):
    def default(self, o):
def send_request(user_session, method, request):
def file_download(user_session, request):
def file_upload(user_session, method, request):
    def __init__(self, respobj=None, msg=None, user_session=None):
    def _unpack_response(self, response):
    def __str__(self):
def debug(response):
GET = 'GET'
PUT = 'PUT'
POST = 'POST'
DELETE = 'DELETE' class Resource(object):
    def __init__(self, entry_point_list):
        self._entry_points = entry_point_list
        
    def __iter__(self):
        for entry in self._entry_points:
            yield EntryPoint(**entry)
    
    def __len__(self):
        return len(self._entry_points)
    
    def clear(self):
        self._entry_points[:] = []
    
    def all(self):
        """
        Return all resources
        
        :rtype: EntryPoint
        """
        for resource in self:
            yield resource
    
    def all_by_name(self):
        """     
        Return all resources by rel name
        
        :rtype: str
        """
        for resource in self:
            yield resource.rel
    
    def get(self, rel):
        """
        Get the resource by rel name
        
        :param str rel_name: name of rel
        :raises UnsupportedEntryPoint: entry point not found in this version
            of the API
        """
        for link in self._entry_points:
            if link.get('rel') == rel:
                return link.get('href')
        raise UnsupportedEntryPoint(
            "The specified entry point '{}' was not found in this "
            "version of the SMC API. Check the element documentation "
            "to determine the correct version and specify the api_version "
            "parameter during session.login() if necessary.".format(rel)) def load_from_file(alt_filepath=None):
    """ Attempt to read the SMC configuration from a
    dot(.) file in the users home directory. The order in
    which credentials are parsed is:

    - Passing credentials as parameters to the session login
    - Shared credential file (~/.smcrc)
    - Environment variables

    :param alt_filepath: Specify a different file name for the
           configuration file. This should be fully qualified and include
           the name of the configuration file to read.

    Configuration file should look like::

        [smc]
        smc_address=172.18.1.150
        smc_apikey=xxxxxxxxxxxxxxxxxxx
        api_version=6.1
        smc_port=8082
        smc_ssl=True
        verify_ssl=True
        retry_on_busy=True
        ssl_cert_file='/Users/davidlepage/home/mycacert.pem'

    :param str smc_address: IP of the SMC Server
    :param str smc_apikey: obtained from creating an API Client in SMC
    :param str api_version: Which version to use (default: latest)
    :param int smc_port: port to use for SMC, (default: 8082)
    :param bool smc_ssl: Whether to use SSL (default: False)
    :param bool verify_ssl: Verify client cert (default: False)
    :param bool retry_on_busy: Retry CRUD operation if service is unavailable (default: False)
    :param str ssl_cert_file: Full path to client pem (default: None)

    The only settings that are required are smc_address and smc_apikey.

    Setting verify_ssl to True (default) validates the client cert for
    SSL connections and requires the ssl_cert_file path to the cacert.pem.
    If not given, verify will default back to False.

    FQDN will be constructed from the information above.

    """
    required = ['smc_address', 'smc_apikey']
    bool_type = ['smc_ssl', 'verify_ssl', 'retry_on_busy']  # boolean option flag
    option_names = ['smc_port',
                    'api_version',
                    'smc_ssl',
                    'verify_ssl',
                    'ssl_cert_file',
                    'retry_on_busy',
                    'timeout',
                    'domain']

    parser = configparser.SafeConfigParser(defaults={
        'smc_port': '8082',
        'api_version': None,
        'smc_ssl': 'false',
        'verify_ssl': 'false',
        'ssl_cert_file': None,
        'timeout': None,
        'domain': None},
        allow_no_value=True)

    path = '~/.smcrc'

    if alt_filepath is not None:
        full_path = alt_filepath
    else:
        ex_path = os.path.expandvars(path)
        full_path = os.path.expanduser(ex_path)

    section = 'smc'
    config_dict = {}
    try:
        with io.open(full_path, 'rt', encoding='UTF-8') as f:
            parser.readfp(f)

        # Get required settings, if not found it will raise err
        for name in required:
            config_dict[name] = parser.get(section, name)

        for name in option_names:
            if parser.has_option(section, name):
                if name in bool_type:
                    config_dict[name] = parser.getboolean(section, name)
                else:  # str
                    config_dict[name] = parser.get(section, name)

    except configparser.NoOptionError as e:
        raise ConfigLoadError('Failed loading credentials from configuration '
                              'file: {}; {}'.format(path, e))
    except (configparser.NoSectionError, configparser.MissingSectionHeaderError) as e:
        raise ConfigLoadError('Failed loading credential file from: {}, check the '
                              'path and verify contents are correct.'.format(path, e))
    except IOError as e:
        raise ConfigLoadError(
            'Failed loading configuration file: {}'.format(e))

    return transform_login(config_dict) def load_from_environ():
    """
    Load the SMC URL, API KEY and optional SSL certificate from
    the environment.
    
    Fields are::
    
        SMC_ADDRESS=http://1.1.1.1:8082
        SMC_API_KEY=123abc
        SMC_CLIENT_CERT=path/to/cert
        SMC_TIMEOUT = 30 (seconds)
        SMC_API_VERSION = 6.1 (optional - uses latest by default)
        SMC_DOMAIN = name of domain, Shared is default
        SMC_EXTRA_ARGS = string in dict format of extra args needed
    
    SMC_CLIENT CERT is only checked IF the SMC_URL is an HTTPS url.
    
    Example of forcing retry on service unavailable::
    
        os.environ['SMC_EXTRA_ARGS'] = '{"retry_on_busy": "True"}'
    
    """
    try:
        from urllib.parse import urlparse
    except ImportError:
        from urlparse import urlparse
    
    smc_address = os.environ.get('SMC_ADDRESS', '')
    smc_apikey = os.environ.get('SMC_API_KEY', '')
    smc_timeout = os.environ.get('SMC_TIMEOUT', None)
    api_version = os.environ.get('SMC_API_VERSION', None)
    domain = os.environ.get('SMC_DOMAIN', None)
    smc_extra_args = os.environ.get('SMC_EXTRA_ARGS', '')
    
    if not smc_apikey or not smc_address:
        raise ConfigLoadError(
            'If loading from environment variables, you must provide values '
            'SMC_ADDRESS and SMC_API_KEY.')
    
    config_dict = {}
    
    if smc_extra_args:
        try:
            args_as_dict = json.loads(smc_extra_args)
            config_dict.update(args_as_dict)
        except ValueError:
            pass
   
    config_dict.update(smc_apikey=smc_apikey)
    config_dict.update(timeout=smc_timeout)
    config_dict.update(api_version=api_version)
    config_dict.update(domain=domain)
        
    url = urlparse(smc_address)
    
    config_dict.update(smc_address=url.hostname)
        
    port = url.port
    if not port:
        port = '8082'
    
    config_dict.update(smc_port=port)
        
    if url.scheme == 'https':
        config_dict.update(smc_ssl=True)
        ssl_cert = os.environ.get('SMC_CLIENT_CERT', None)
        if ssl_cert: # Enable cert validation
            config_dict.update(verify_ssl=True)
            config_dict.update(ssl_cert_file=ssl_cert)
        # Else http
    
    return transform_login(config_dict) class SMCRequest(object):
    """
    SMCRequest represents the data structure that will be submitted to the web
    layer for submission to the SMC API.

    :param str href: href for request, required by all methods
    :param dict json: json to submit, required by create, update
    :param dict params: query string parameters
    :param str filename: name of file for download, optional for create
    :param str etag: etag of element, required for update
    """
    _session_manager = None
    
    def __init__(self, href=None, json=None, params=None, filename=None,
                 etag=None, user_session=None, **kwargs):
        
        #: Filename if a file download is requested
        self.filename = filename
        #: dictionary of query parameters
        self.params = params
        #: href for this request
        self.href = href
        #: ETag for PUT or DELETE request modifications
        self.etag = etag
        #: JSON data to send in request
        self.json = {} if json is None else json
        
        # Only used in the case of streaming file download/upload
        self.files = None
        #: Default headers
        self.headers = {'Content-Type': 'application/json'}
        
        # Optional user session for this request
        #self.user_session = user_session # smc.api.session.Session
        
        for k, v in kwargs.items():
            setattr(self, k, v)
    
    def __call__(self, session):
        pass
    
    def create(self):
        return self._make_request(method='POST')

    def delete(self):
        return self._make_request(method='DELETE')

    def update(self):
        return self._make_request(method='PUT')

    def read(self):
        return self._make_request(method='GET')
    
    def _make_request(self, method):
        err = None
        result = None
        try:
            # Obtain the session
            session = _get_session(getattr(self, '_session_manager', None))
            
            if method == 'GET':
                if not self.href:
                    self.href = session.entry_points.get('elements')
            
            result = send_request(session, method, self)
            
        except SMCOperationFailure as e:
            result = e.smcresult
            try:
                err = self.exception(result.msg)  # Exception set
            except AttributeError:
                pass
        except (SessionManagerNotFound, SMCConnectionError,
                IOError, TypeError) as e:
            err = e
        finally:
            if err:
                raise err
            return result

    def __str__(self):
        sb = []
        for key in self.__dict__:
            sb.append(
                "{key}='{value}'".format(
                    key=key,
                    value=self.__dict__[key]))
        return 'SMCRequest({})'.format(','.join(sb))     class cached_property(object):
    """
    Use for caching a property value on the instance. If the
    attribute is deleted, it will be recreated when called.
    """

    def __init__(self, func):
        self.func = func
        #self.__doc__ = func.__doc__

    def __get__(self, obj, cls):
        if obj is None:
            return self
        value = obj.__dict__[self.func.__name__] = self.func(obj)
        return value class ConfigLoadError(SMCException):
    """
    Thrown when there was a problem reading credential information from
    file. Typically caused by missing settings.
    """ class SMCConnectionError(SMCException):
    """
    Thrown when there are connection related issues with the SMC.
    This could be that the underlying http requests library could not connect
    due to wrong IP address, wrong port, or time out
    """ class UnsupportedEntryPoint(SMCException):
    """
    An entry point was specified that was not found in this API
    version. This is likely due to using an older version of the
    SMC API that does not support that feature. The exception is
    thrown specifying the entry point specified.
    """ class SessionManagerNotFound(Exception):
    def __init__(self, message=''):
        msg = 'A session manager was not found for this session. This generally means '\
        'the session was not obtained through the standard session factory or the session '\
        'manager may have been replaced. '
        _msg = message or msg
        super(SessionManagerNotFound, self).__init__(_msg) class SessionNotFound(SMCException):
    """
    Retrieving a session by name did not succeed because the session did
    not already exist
    """ def ElementFactory(href, smcresult=None, raise_exc=None):
    """
    Factory returns an object of type Element when only
    the href is provided.
    
    :param str href: string href to fetch
    :param SMCResult smcresult: optional SMCResult. If provided,
        the request fetch will be skipped
    :param Exception raise_exc: exception to raise if fetch
        failed
    """
    if smcresult is None:
        smcresult = SMCRequest(href=href).read()
    if smcresult.json:
        cache = ElementCache(smcresult.json, etag=smcresult.etag)
        typeof = lookup_class(cache.type)
        instance = typeof(
            name=cache.get('name'),
            href=href,
            type=cache.type)
        instance.data = cache
        return instance
    if raise_exc and smcresult.msg:
        raise raise_exc(smcresult.msg)
import copy
import json
import logging
import requests
import collections
from smc.api.web import send_request, counters
from smc.api.entry_point import Resource
from smc.api.configloader import load_from_file, load_from_environ
from smc.api.common import SMCRequest
from smc.base.decorators import cached_property
from smc.api.exceptions import ConfigLoadError, SMCConnectionError,\
    UnsupportedEntryPoint, SessionManagerNotFound, SessionNotFound
from smc.base.model import ElementFactory
            from requests.adapters import HTTPAdapter
            from requests.packages.urllib3.util.retry import Retry
        
        :rtype: bool
        """
        return self.url.startswith('https') if self.session else False

    @property
    def timeout(self):
        """
        Session timeout in seconds
        
        :rtype: int
        """
        return self._params.get('timeout', 30)

    @property
    def domain(self):
        """
        Logged in SMC domain
        
        :rtype: str
        """
        return 'Shared Domain' if not self._params.get('domain') else \
            self._params.get('domain')
        
    @property
    def name(self):
        """
        Return the administrator name for this session. Can be None if
        the session has not yet been established.
        
        .. note:: The administrator name was introduced in SMC version
            6.4. Previous versions will show the unique session
            identifier for this session.
        
        :rtype: str
        """
        if self.session: # protect cached property from being set before session
            try:
                return self.current_user.name
            except AttributeError: # TODO: Catch ConnectionError? No session
                pass
        return hash(self)
    
    @cached_property
    def current_user(self):
        """
        .. versionadded:: 0.6.0
            Requires SMC version >= 6.4
        
        Return the currently logged on API Client user element.
        
        :raises UnsupportedEntryPoint: Current user is only supported with SMC
            version >= 6.4
        :rtype: Element
        """
        if self.session:
            try:
                response = self.session.get(self.entry_points.get('current_user'))
                if response.status_code in (200, 201):
                    admin_href=response.json().get('value')
                    request = SMCRequest(href=admin_href)
                    smcresult = send_request(self, 'get', request)
                    return ElementFactory(admin_href, smcresult)
            except UnsupportedEntryPoint:
                pass
    
    def login(self, url=None, api_key=None, login=None, pwd=None,
            api_version=None, timeout=None, verify=True, alt_filepath=None,
            domain=None, **kwargs):
        """
        Login to SMC API and retrieve a valid session.
        Sessions use a pool connection manager to provide dynamic scalability
        during times of increased load. Each session is managed by a global
        session manager making it possible to have more than one session per
        interpreter.

        An example login and logout session::

            from smc import session
            session.login(url='http://1.1.1.1:8082', api_key='SomeSMCG3ener@t3dPwd')
            .....do stuff.....
            session.logout()

        :param str url: ip of SMC management server
        :param str api_key: API key created for api client in SMC
        :param str login: Administrator user in SMC that has privilege to SMC API.
        :param str pwd: Password for user login.
        :param api_version (optional): specify api version
        :param int timeout: (optional): specify a timeout for initial connect; (default 10)
        :param str|boolean verify: verify SSL connections using cert (default: verify=True)
            You can pass verify the path to a CA_BUNDLE file or directory with certificates
            of trusted CAs
        :param str alt_filepath: If using .smcrc, alternate path+filename
        :param str domain: domain to log in to. If domains are not configured, this
            field will be ignored and api client logged in to 'Shared Domain'.
        :param bool retry_on_busy: pass as kwarg with boolean if you want to add retries
            if the SMC returns HTTP 503 error during operation. You can also optionally customize
            this behavior and call :meth:`.set_retry_on_busy`
        :raises ConfigLoadError: loading cfg from ~.smcrc fails

        For SSL connections, you can disable validation of the SMC SSL certificate by setting
        verify=False, however this is not a recommended practice.

        If you want to use the SSL certificate generated and used by the SMC API server
        for validation, set verify='path_to_my_dot_pem'. It is also recommended that your
        certificate has subjectAltName defined per RFC 2818
        
        If SSL warnings are thrown in debug output, see:
        https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings

        Logout should be called to remove the session immediately from the
        SMC server.
        
        .. note:: As of SMC 6.4 it is possible to give a standard Administrative user
            access to the SMC API. It is still possible to use an API Client by
            providing the api_key in the login call.
        """
        params = {}
        if not url or (not api_key and not (login and pwd)):
            try: # First try load from file

                params = load_from_file(alt_filepath) if alt_filepath\