diff --git a/api.py b/api.py new file mode 100644 index 0000000..0d3c0c0 --- /dev/null +++ b/api.py @@ -0,0 +1,99 @@ +import requests +from requests.exceptions import HTTPError +import endpoints +import datetime +import exceptions + +TIME_FOR_RENEW = 60 + + +class Re2oAPI: + + def __init__(self, hostname, username, password, use_tls=True): + self.use_tls = use_tls + self.hostname = hostname + self._username = username + self._password = password + self.token = self._obtain_token() + + def _need_renew_token(self): + return self.token['expiration'] > datetime.datetime.utcnow() + \ + datetime.timedelta(seconds=TIME_FOR_RENEW) + + def _obtain_token(self): + response = requests.post( + self.get_url_for('token', forma=None), + data={'username': self._username, 'password': self._password} + ) + if response.status_code == requests.codes.bad_request: + raise exceptions.InvalidCredentials() + response.raise_for_status() + response = response.json() + return { + 'token': response['token'], + 'expiration': datetime.datetime.strptime( + response['expiration'], + '%Y-%m-%dT%H:%M:%S.%fZ' + ) + } + + def get_token(self): + if self._need_renew_token(): + self.token = self._obtain_token() + return self.token['token'] + + def get(self, url, *args, **kwargs): + headers = kwargs.pop('headers', {}) + headers.update({ + 'Authorization': 'Token {}'.format(self.get_token()) + }) + response = requests.get(url, headers=headers, *args, **kwargs) + response.raise_for_status() + return response.json() + + def post(self, url, *args, **kwargs): + headers = kwargs.pop('headers', {}) + headers.update({ + 'Authorization': 'Token {}'.format(self.get_token()) + }) + response = requests.post(url, headers=headers, *args, **kwargs) + response.raise_for_status() + return response.json() + + def get_url_for(self, name, forma='json', **kwargs): + url = '{proto}://{host}{endpoint}'.format( + proto=('https' if self.use_tls else 'http'), + host=self.hostname, + endpoint=endpoints.get_endpoint_for(name, **kwargs) + ) + if forma: + url = '{url}.{forma}/'.format(url=url[:-1], forma=forma) + return url + + def __getattr__(self, item): + if item.startswith('list_'): + def f(max_results=None, **kwargs): + response = self.get( + self.get_url_for('%s-list' % item[len('list_'):], **kwargs), + ) + results = response['results'] + while response['next'] is not None and \ + (max_results is None or len(results) < max_results): + response = self.get(response['next']) + results += response['results'] + return results[:max_results] if max_results else results + return f + elif item.startswith('count_'): + def f(**kwargs): + return self.get( + self.get_url_for('%s-list' % item[len('count_'):], **kwargs), + )['count'] + return f + elif item.startswith('view_'): + def f(**kwargs): + return self.get( + self.get_url_for('%s-details' % item[len('view_'):], **kwargs), + ) + return f + else: + raise AttributeError(item) diff --git a/endpoints.py b/endpoints.py new file mode 100644 index 0000000..7c739ee --- /dev/null +++ b/endpoints.py @@ -0,0 +1,108 @@ +import exceptions + +urls = { + 'root': '/api/', + 'articles-list': '/api/cotisations/articles/', + 'articles-details': '/api/cotisations/articles/{pk}/', + 'banques-list': '/api/cotisations/banques/', + 'banques-details': '/api/cotisations/banques/{pk}/', + 'cotisations-list': '/api/cotisations/cotisations/', + 'cotisations-details': '/api/cotisations/cotisations/{pk}/', + 'factures-list': '/api/cotisations/factures/', + 'factures-details': '/api/cotisations/factures/{pk}/', + 'paiments-list': '/api/cotisations/paiements/', + 'paiements-details': '/api/cotisations/paiements/{pk}/', + 'ventes-list': '/api/cotisations/ventes/', + 'ventes-details': '/api/cotisations/ventes/{pk}/', + 'domains-list': '/api/machines/domains/', + 'domains-details': '/api/machines/domains/{pk}/', + 'extensions-list': '/api/machines/extensions/', + 'extensions-details': '/api/machines/extensions/{pk}/', + 'interfaces-list': '/api/machines/interfaces/', + 'interfaces-details': '/api/machines/interfaces/{pk}/', + 'iplists-list': '/api/machines/iplists/', + 'iplists-details': '/api/machines/iplists/{pk}/', + 'iptypes-list': '/api/machines/iptypes/', + 'iptypes-details': '/api/machines/iptypes/{pk}/', + 'ipv6lists-list': '/api/machines/ipv6lists/', + 'ipv6lists-details': '/api/machines/ipv6lists/{pk}/', + 'machines-list': '/api/machines/machines/', + 'machines-details': '/api/machines/machines/{pk}/', + 'machinetypes-list': '/api/machines/machinetypes/', + 'machinetypes-details': '/api/machines/machinetypes/{pk}/', + 'mx-list': '/api/machines/mx/', + 'mx-details': '/api/machines/mx/{pk}/', + 'nas-list': '/api/machines/nas/', + 'nas-details': '/api/machines/nas/{pk}/', + 'ns-list': '/api/machines/ns/', + 'ns-details': '/api/machines/ns/{pk}/', + 'ouvertureportlists-list': '/api/machines/ouvertureportlists/', + 'ouvertureportlists-details': '/api/machines/ouvertureportlists/{pk}/', + 'ouvertureports-list': '/api/machines/ouvertureports/', + 'ouvertureports-details': '/api/machines/ouvertureports/{pk}/', + 'servicelinks-list': '/api/machines/servicelinks/', + 'servicelinks-details': '/api/machines/servicelinks/{pk}/', + 'services-list': '/api/machines/services/', + 'services-details': '/api/machines/services/{pk}/', + 'soa-list': '/api/machines/soa/', + 'soa-details': '/api/machines/soa/{pk}/', + 'srv-list': '/api/machines/srv/', + 'srv-details': '/api/machines/srv/{pk}/', + 'txt-list': '/api/machines/txt/', + 'txt-details': '/api/machines/txt/{pk}/', + 'vlans-list': '/api/machines/vlans/', + 'vlans-details': '/api/machines/vlans/{pk}', + 'accesspoint-list': '/api/topologie/acesspoint/', + 'accesspoint-details': '/api/topologie/acesspoint/{pk}/', + 'building-list': '/api/topologie/building/', + 'building-details': '/api/topologie/building/{pk}/', + 'constructorswitch-list': '/api/topologie/constructorswitch/', + 'contructorswitch-details': '/api/topologie/constructorswitch/{pk}/', + 'modelswitch-list': '/api/topologie/modelswitch/', + 'modelswitch-details': '/api/topologie/modelswitch/{pk}/', + 'room-list': '/api/topologie/room/', + 'room-details': '/api/topologie/room/{pk}/', + 'stack-list': '/api/topologie/stack/', + 'stack-details': '/api/topologie/stack/{pk}/', + 'switch-list': '/api/topologie/switch/', + 'switch-details': '/api/topologie/switch/{pk}/', + 'switchbay-list': '/api/topologie/switchbay/', + 'switchbay-details': '/api/topologie/switchbay/{pk}/', + 'switchport-list': '/api/topologie/switchport/', + 'switchport-details': '/api/topologie/switchport/{pk}/', + 'adherents-list': '/api/users/adherents/', + 'adherents-details': '/api/users/adherents/{pk}/', + 'bans-list': '/api/users/bans/', + 'bans-details': '/api/users/bans/{pk}/', + 'clubs-list': '/api/users/clubs/', + 'clubs-details': '/api/users/clubs/{pk}/', + 'listrights-list': '/api/users/listrights/', + 'listrights-details': '/api/users/listrights/{pk}/', + 'schools-list': '/api/users/schools/', + 'schools-details': '/api/users/schools/{pk}/', + 'serviceusers-list': '/api/users/serviceusers/', + 'serviceusers-details': '/api/users/serviceusers/{pk}/', + 'shells-list': '/api/users/shells/', + 'shells-details': '/api/users/shells/{pk}/', + 'users-list': '/api/users/users/', + 'users-details': '/api/users/users/{pk}/', + 'whitelists-list': '/api/users/whitelists/', + 'whitelists-details': '/api/users/whitelists/{pk}/', + 'token': '/api/token-auth/', +} + + +def get_names(): + return urls.keys() + + +def get_endpoint_for(name, **kwargs): + try: + url=urls[name] + except KeyError as e: + raise exceptions.URLNameNotExists(name) + else: + try: + return url.format_map(kwargs) + except KeyError as e: + raise exceptions.URLParameterMissing(e) diff --git a/exceptions.py b/exceptions.py new file mode 100644 index 0000000..6ff579b --- /dev/null +++ b/exceptions.py @@ -0,0 +1,8 @@ +class URLNameNotExists(ValueError): + pass + +class URLParameterMissing(ValueError): + pass + +class InvalidCredentials(ValueError): + pass