Пример #1
0
def available_api_versions(base_url, timeout=10, verify=True):
    """
    Get all available API versions for this SMC

    :return version numbers
    :rtype: list
    """
    try:
        r = requests.get('%s/api' % base_url, timeout=timeout,
                         verify=verify)  # no session required

        if r.status_code == 200:
            j = json.loads(r.text)
            versions = []
            for version in j['version']:
                versions.append(version['rel'])
            #versions = [float(i) for i in versions]
            return versions

        raise SMCConnectionError(
            'Invalid status received while getting entry points from SMC. '
            'Status code received %s. Reason: %s' % (r.status_code, r.reason))

    except requests.exceptions.RequestException as e:
        raise SMCConnectionError(e)
Пример #2
0
    def get_api_entry(self, url, api_version=None, timeout=10, verify=True):
        """
        Called internally after login to get cache of SMC entry points
        
        :param: str url: URL for SMC 
        :param str api_version: if specified, use this version, or use latest
        """
        try:
            # Get api versions
            r = requests.get('%s/api' % url, timeout=timeout,
                             verify=verify)  #no session required
            j = json.loads(r.text)
            versions = []
            for version in j['version']:
                versions.append(version['rel'])
            versions = [float(i) for i in versions]

            if api_version is None:  # Use latest
                api_version = max(versions)
            else:
                try:
                    specified_version = float(api_version)
                    if specified_version in versions:
                        api_version = specified_version
                    else:
                        api_version = max(versions)
                except ValueError:
                    api_version = max(versions)

            #else api_version was defined
            logger.info("Using SMC API version: %s", api_version)
            smc_url = '{}/{}'.format(url, str(api_version))

            r = requests.get('%s/api' % (smc_url),
                             timeout=timeout,
                             verify=verify)

            if r.status_code == 200:
                j = json.loads(r.text)
                self.api_version = api_version
                logger.debug(
                    "Successfully retrieved API entry points from SMC")
            else:
                raise SMCConnectionError("Error occurred during initial api "
                                         "request, json was not returned. "
                                         "Return data was: %s" % r.text)
            self.api_entry = j['entry_point']

        except requests.exceptions.RequestException as e:
            raise SMCConnectionError(e)
Пример #3
0
 def get_entry_href(self, verb):
     """
     Get entry point from entry point cache
     Call get_all_entry_points to find all available entry points. 
     
     :param str verb: top level entry point into SMC api
     :return dict: meta data for specified entry point
     :raises: :py:class:`smc.api.exceptions.UnsupportedEntryPoint`
     """
     if self.api_entry:
         href = None
         for entry in self.api_entry:
             if entry.get('rel') == verb:
                 href = entry.get('href', None)
         if not 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. Current api version "
                 "is {}".format(verb, self.api_version))
         else:
             return href
     else:
         raise SMCConnectionError("No entry points found, it is likely "
                                  "there is no valid login session.")
Пример #4
0
def get_entry_points(base_url, timeout=10, verify=True):
    """
    Return the entry points in iterable class
    """
    try:
        r = requests.get('%s/api' % (base_url), timeout=timeout, verify=verify)

        if r.status_code == 200:
            entry_point_list = json.loads(r.text)
            logger.debug('Successfully retrieved API entry points from SMC')

            return entry_point_list['entry_point']

        else:
            raise SMCConnectionError(
                'Invalid status received while getting entry points from SMC. '
                'Status code received %s. Reason: %s' %
                (r.status_code, r.reason))

    except requests.exceptions.RequestException as e:
        raise SMCConnectionError(e)
Пример #5
0
def load_entry_points(session):
    try:
        r = session.session.get('{url}/{api_version}/api'.format(
                url=session.url, api_version=session.api_version))
        
        if r.status_code == 200:
            result_list = json.loads(r.text)
            
            if session._resource:
                session.entry_points.clear()
            
            session._resource.add(result_list['entry_point'])
            logger.debug("Loaded entry points with obtained session.")
        
        else:
            raise SMCConnectionError(
                'Invalid status received while getting entry points from SMC. '
                'Status code received %s. Reason: %s' % (r.status_code, r.reason))
    
    except requests.exceptions.RequestException as e:
        raise SMCConnectionError(e)
Пример #6
0
    def refresh(self):
        """
        Refresh session on 401. Wrap this in a loop with retries.

        :raises SMCConnectionError: Problem re-authenticating using existing
            api credentials
        """
        # Did we already have a session that just timed out
        if self.session and self.credential.has_credentials and self.url:
            # Try relogging in to refresh, otherwise fail
            logger.info('Session timed out, will try obtaining a new session using '
                'previously saved credential information.')
            self.login(**self._get_login_params())
            return
        raise SMCConnectionError('Session expired and attempted refresh failed.')        
Пример #7
0
 def _get_session(self, request):
     """
     Authenticate the request dict
     
     :param dict request: request dict built from user input
     :raises SMCConnectionError: failure to connect
     :return: python requests session
     :rtype: requests.Session
     """
     _session = requests.session()  # empty session
     
     response = _session.post(**request)
     logger.info('Using SMC API version: %s', self.api_version)
     
     if response.status_code != 200:
         raise SMCConnectionError(
             'Login failed, HTTP status code: %s and reason: %s' % (
                 response.status_code, response.reason))
     return _session
Пример #8
0
    def send_request(self, method, request):
        """
        Send request to SMC
        """
        if self.session:
            try:
                method = method.upper() if method else ''

                if method == SMCAPIConnection.GET:
                    if request.filename:  #File download request
                        return self.file_download(request)

                    response = self.session.get(request.href,
                                                params=request.params,
                                                headers=request.headers,
                                                timeout=self.timeout)
                    response.encoding = 'utf-8'

                    logger.debug(vars(response))
                    counters.update(read=1)

                    if response.status_code not in (200, 304):
                        raise SMCOperationFailure(response)

                elif method == SMCAPIConnection.POST:
                    if request.files:  #File upload request
                        return self.file_upload(request)

                    response = self.session.post(
                        request.href,
                        #data=json.dumps(request.json),
                        json=request.json,
                        headers=request.headers,
                        params=request.params)
                    response.encoding = 'utf-8'

                    logger.debug(vars(response))
                    counters.update(create=1)

                    if response.status_code not in (200, 201, 202):
                        # 202 is asynchronous response with follower link
                        raise SMCOperationFailure(response)

                elif method == SMCAPIConnection.PUT:
                    #Etag should be set in request object
                    request.headers.update(Etag=request.etag)

                    response = self.session.put(
                        request.href,
                        #data=json.dumps(request.json),
                        json=request.json,
                        params=request.params,
                        headers=request.headers)

                    logger.debug(vars(response))
                    counters.update(update=1)

                    if response.status_code != 200:
                        raise SMCOperationFailure(response)

                elif method == SMCAPIConnection.DELETE:
                    response = self.session.delete(request.href)
                    response.encoding = 'utf-8'

                    counters.update(delete=1)

                    if response.status_code not in (200, 204):
                        raise SMCOperationFailure(response)

                else:  #Unsupported method
                    return SMCResult(msg='Unsupported method: %s' % method)

            except SMCOperationFailure:
                raise
            except requests.exceptions.RequestException as e:
                raise SMCConnectionError(
                    "Connection problem to SMC, ensure the "
                    "API service is running and host is correct: %s, "
                    "exiting." % e)
            else:
                return SMCResult(response)
        else:
            raise SMCConnectionError(
                "No session found. Please login to continue")
Пример #9
0
 def entry_points(self):
     if not len(self._resource):
         raise SMCConnectionError(
             "No entry points found, it is likely there is no valid "
             "login session.")
     return self._resource
Пример #10
0
    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.
        Session will be re-used when multiple queries are required.

        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 file+path
        :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'.
        :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.
        """
        if not url or (not api_key and not (login and pwd)):
            # First try load from file
            try:
                cfg = load_from_file(alt_filepath) if alt_filepath\
                    is not None else load_from_file()
                logger.debug('Read config data from file: %s', cfg)
            except ConfigLoadError:
                # Last ditch effort, try to load from environment
                cfg = load_from_environ()
                logger.debug('Read config data from environ: %s', cfg)

            url = cfg.get('url')
            api_key = cfg.get('api_key')
            api_version = cfg.get('api_version')
            verify = cfg.get('verify')
            timeout = cfg.get('timeout')
            domain = cfg.get('domain')
            kwargs = cfg.get('kwargs')

        if timeout:
            self._timeout = timeout

        self._api_version = get_api_version(url, api_version, timeout, verify)

        base = get_api_base(url, self.api_version, verify=verify)

        self._resource.add(get_entry_points(base, timeout, verify))

        s = requests.session()  # empty session

        json = {'domain': domain}

        if api_key:
            json.update(authenticationkey=api_key)

        if kwargs:
            json.update(**kwargs)
            self._extra_args.update(**kwargs)

        params = dict(login=login, pwd=pwd) if login and pwd else None

        req = dict(
            url=self.entry_points.get('login') if api_key else \
                '{}/{}/lms_login'.format(url, self._api_version),
            json=json,
            params=params,
            headers={'content-type': 'application/json'},
            verify=verify)

        r = s.post(**req)
        logger.info('Using SMC API version: %s', self.api_version)

        if r.status_code == 200:
            self._session = s  # session creation was successful
            self._session.verify = verify  # make verify setting persistent
            self._url = url
            self.credential.set_credentials(api_key, login, pwd)

            if domain:
                self._domain = domain

            self._sessions[self.domain] = self.session
            if self.connection is None:
                self._connection = smc.api.web.SMCAPIConnection(self)

            logger.debug(
                'Login succeeded and session retrieved: %s, domain: %s',
                self.session_id, self.domain)

            # Reload entry points
            self.entry_points.clear()
            self._resource.add(reload_entry_points(self))

        else:
            raise SMCConnectionError(
                'Login failed, HTTP status code: %s and reason: %s' %
                (r.status_code, r.reason))

        if not self._MODS_LOADED:
            logger.debug('Registering class mappings.')
            # Load the modules to register needed classes
            for pkg in ('smc.policy', 'smc.elements', 'smc.routing', 'smc.vpn',
                        'smc.administration', 'smc.core',
                        'smc.administration.user_auth'):
                import_submodules(pkg, recursive=False)

            self._MODS_LOADED = True
Пример #11
0
    def send_request(self, method, request):
        """
        Send request to SMC
        """
        if self.session:
            try:
                method = method.upper() if method else ''

                if method == SMCAPIConnection.GET:
                    if request.filename:  # File download request
                        return self.file_download(request)

                    response = self.session.get(request.href,
                                                params=request.params,
                                                headers=request.headers,
                                                timeout=self.timeout)

                    response.encoding = 'utf-8'

                    logger.debug('GET %s: %s', request.href, vars(response))
                    counters.update(read=1)

                    if response.status_code not in (200, 204, 304):
                        raise SMCOperationFailure(response)

                elif method == SMCAPIConnection.POST:
                    if request.files:  # File upload request
                        return self.file_upload(method, request)

                    response = self.session.post(
                        request.href,
                        json=request.json if request.json else None,
                        headers=request.headers,
                        params=request.params)

                    response.encoding = 'utf-8'

                    logger.debug('POST %s: %s', request.href, vars(response))
                    counters.update(create=1)

                    if response.status_code not in (200, 201, 202):
                        # 202 is asynchronous response with follower link
                        raise SMCOperationFailure(response)

                elif method == SMCAPIConnection.PUT:
                    if request.files:  # File upload request
                        return self.file_upload(method, request)

                    # Etag should be set in request object
                    request.headers.update(Etag=request.etag)
                    logger.debug('PUT: %s', request)

                    response = self.session.put(request.href,
                                                json=request.json,
                                                params=request.params,
                                                headers=request.headers)

                    logger.debug(vars(response))
                    counters.update(update=1)

                    if response.status_code != 200:
                        raise SMCOperationFailure(response)

                elif method == SMCAPIConnection.DELETE:
                    response = self.session.delete(request.href,
                                                   headers=request.headers)

                    counters.update(delete=1)

                    # Conflict (409) if ETag is not current
                    if response.status_code in (409, ):
                        req = self.session.get(request.href)
                        etag = req.headers.get('ETag')
                        response = self.session.delete(
                            request.href, headers={'if-match': etag})

                    response.encoding = 'utf-8'

                    logger.debug('DELETE %s: %s', request.href, vars(response))
                    if response.status_code not in (200, 204):
                        raise SMCOperationFailure(response)

                else:  # Unsupported method
                    return SMCResult(msg='Unsupported method: %s' % method)

            except SMCOperationFailure as error:
                if error.code in (401, ):
                    self._session.refresh()
                    return self.send_request(method, request)
                raise error
            except requests.exceptions.RequestException as e:
                raise SMCConnectionError(
                    'Connection problem to SMC, ensure the '
                    'API service is running and host is correct: %s, '
                    'exiting.' % e)
            else:
                return SMCResult(response, domain=self.session_domain)
        else:
            raise SMCConnectionError(
                "No session found. Please login to continue")
Пример #12
0
    def login(self,
              url=None,
              api_key=None,
              api_version=None,
              timeout=None,
              verify=True,
              alt_filepath=None,
              **kwargs):
        """
        Login to SMC API and retrieve a valid session.
        Session will be re-used when multiple queries are required.
        
        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 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)
        :param str alt_filepath: If using .smcrc, alternate file+path

        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.
        """
        if url and api_key:
            self._url = url
            self._api_key = api_key
            if timeout:
                self._timeout = timeout
        else:
            try:
                cfg = load_from_file(alt_filepath) if alt_filepath\
                    is not None else load_from_file()
                logger.debug("Config read has data: %s", cfg)
                self._url = cfg.get('url')
                self._api_key = cfg.get('api_key')
                api_version = cfg.get('api_version')
                verify = cfg.get('verify')
                timeout = cfg.get('timeout')
                if timeout:
                    self._timeout = timeout
            except ConfigLoadError:
                raise

        self.cache.get_api_entry(self.url,
                                 api_version,
                                 timeout=self.timeout,
                                 verify=verify)
        s = requests.session()  #no session yet

        r = s.post(self.cache.get_entry_href('login'),
                   json={'authenticationkey': self.api_key},
                   headers={'content-type': 'application/json'},
                   verify=verify)

        if r.status_code == 200:
            self._session = s  #session creation was successful
            self._session.verify = verify  #make verify setting persistent
            logger.debug("Login succeeded and session retrieved: %s", \
                         self.session_id)
            self._connection = smc.api.web.SMCAPIConnection(self)
        else:
            raise SMCConnectionError("Login failed, HTTP status code: %s" \
                                     % r.status_code)
Пример #13
0
def send_request(user_session, method, request):
    """
    Send request to SMC
    
    :param Session user_session: session object
    :param str method: method for request
    :param SMCRequest request: request object
    :raises SMCOperationFailure: failure with reason
    :rtype: SMCResult
    """
    if user_session.session:
        session = user_session.session  # requests session
        try:
            method = method.upper() if method else ''

            if method == GET:
                if request.filename:  # File download request
                    return file_download(user_session, request)

                response = session.get(request.href,
                                       params=request.params,
                                       headers=request.headers,
                                       timeout=user_session.timeout)

                response.encoding = 'utf-8'

                counters.update(read=1)

                if logger.isEnabledFor(logging.DEBUG):
                    debug(response)

                if response.status_code not in (200, 204, 304):
                    raise SMCOperationFailure(response)

            elif method == POST:
                if request.files:  # File upload request
                    return file_upload(user_session, method, request)

                response = session.post(request.href,
                                        data=json.dumps(request.json,
                                                        cls=CacheEncoder),
                                        headers=request.headers,
                                        params=request.params)

                response.encoding = 'utf-8'

                counters.update(create=1)
                if logger.isEnabledFor(logging.DEBUG):
                    debug(response)

                if response.status_code not in (200, 201, 202):
                    # 202 is asynchronous response with follower link
                    raise SMCOperationFailure(response)

            elif method == PUT:
                if request.files:  # File upload request
                    return file_upload(user_session, method, request)

                # Etag should be set in request object
                request.headers.update(Etag=request.etag)

                response = session.put(request.href,
                                       data=json.dumps(request.json,
                                                       cls=CacheEncoder),
                                       params=request.params,
                                       headers=request.headers)

                counters.update(update=1)

                if logger.isEnabledFor(logging.DEBUG):
                    debug(response)

                if response.status_code != 200:
                    raise SMCOperationFailure(response)

            elif method == DELETE:
                response = session.delete(request.href,
                                          headers=request.headers)

                counters.update(delete=1)

                # Conflict (409) if ETag is not current
                if response.status_code in (409, ):
                    req = session.get(request.href)
                    etag = req.headers.get('ETag')
                    response = session.delete(request.href,
                                              headers={'if-match': etag})

                response.encoding = 'utf-8'

                if logger.isEnabledFor(logging.DEBUG):
                    debug(response)

                if response.status_code not in (200, 204):
                    raise SMCOperationFailure(response)

            else:  # Unsupported method
                return SMCResult(msg='Unsupported method: %s' % method,
                                 user_session=user_session)

        except SMCOperationFailure as error:
            if error.code in (401, ):
                user_session.refresh()
                return send_request(user_session, method, request)
            raise error
        except requests.exceptions.RequestException as e:
            raise SMCConnectionError(
                'Connection problem to SMC, ensure the API '
                'service is running and host is correct: %s, exiting.' % e)
        else:
            return SMCResult(response, user_session=user_session)
    else:
        raise SMCConnectionError('No session found. Please login to continue')