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)
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)
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.")
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)
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)
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.')
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
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")
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
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
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")
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)
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')