diff --git a/ara/clients/http.py b/ara/clients/http.py new file mode 100644 index 0000000..30c8dd0 --- /dev/null +++ b/ara/clients/http.py @@ -0,0 +1,127 @@ +# Copyright (c) 2018 Red Hat, Inc. +# +# This file is part of ARA: Ansible Run Analysis. +# +# ARA is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# ARA is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with ARA. If not, see . + +# This is an "offline" API client that does not require standing up +# an API server and does not execute actual HTTP calls. + +import json +import logging +import requests + + +class HttpClient(object): + def __init__(self, endpoint='http://127.0.0.1:8000', timeout=30, **params): + self.endpoint = endpoint + self.timeout = timeout + self.params = params + + self.log = logging.getLogger(__name__) + self.user_agent = 'ara-http-client' + self.log.debug("%s: %s" % (self.user_agent, str(self.params))) + + self.http = requests.Session() + + def _request(self, method, url, **kwargs): + # Override timeout and headers only if user supplied + kwargs.setdefault('timeout', self.timeout) + kwargs.setdefault('headers', kwargs.get('headers', {})) + + # Headers we're enforcing (kind of) + kwargs['headers']['User-Agent'] = self.user_agent + kwargs['headers']['Accept'] = 'application/json' + kwargs['headers']['Content-Type'] = 'application/json' + + self.log.debug("%s on %s" % (method, url)) + + # Use requests.Session to do the query + # The actual endpoint is: + # + # http://127.0.0.1:8000 / api/v1/playbooks + return self.http.request(method, self.endpoint + url, **kwargs) + + def get(self, url, **kwargs): + return self._request('get', url, **kwargs) + + def patch(self, url, **kwargs): + return self._request('patch', url, **kwargs) + + def post(self, url, **kwargs): + return self._request('post', url, **kwargs) + + def put(self, url, **kwargs): + return self._request('put', url, **kwargs) + + def delete(self, url, **kwargs): + return self._request('delete', url, **kwargs) + + +class AraHttpClient(object): + def __init__(self): + self.log = logging.getLogger(__name__) + self.client = HttpClient() + + def _request(self, method, url, **kwargs): + func = getattr(self.client, method) + # TODO: Is there a better way than doing this if/else ? + if kwargs: + response = func(url, json.dumps(kwargs)) + else: + response = func(url) + + if response.status_code >= 500: + self.log.error( + 'Failed to {method} on {url}: {content}'.format( + method=method, + url=url, + content=kwargs + ) + ) + + self.log.debug('HTTP {status}: {method} on {url}'.format( + status=response.status_code, + method=method, + url=url + )) + + if response.status_code not in [200, 201, 204]: + self.log.error( + 'Failed to {method} on {url}: {content}'.format( + method=method, + url=url, + content=kwargs + ) + ) + + if response.status_code == 204: + return response + + return response.json() + + def get(self, endpoint, **kwargs): + return self._request('get', endpoint, **kwargs) + + def patch(self, endpoint, **kwargs): + return self._request('patch', endpoint, **kwargs) + + def post(self, endpoint, **kwargs): + return self._request('post', endpoint, **kwargs) + + def put(self, endpoint, **kwargs): + return self._request('put', endpoint, **kwargs) + + def delete(self, endpoint, **kwargs): + return self._request('delete', endpoint, **kwargs) diff --git a/ara/clients/offline.py b/ara/clients/offline.py index 2d25a5b..f2876ed 100644 --- a/ara/clients/offline.py +++ b/ara/clients/offline.py @@ -22,16 +22,21 @@ import json import logging import os -from django import setup as django_setup -from django.core.management import execute_from_command_line -from django.test import Client +try: + from django import setup as django_setup + from django.core.management import execute_from_command_line + from django.test import Client -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'ara.server.settings') -# Automatically create the database and run migrations (is there a better way?) -execute_from_command_line(['django', 'migrate']) + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'ara.server.settings') -# Set up the things Django needs -django_setup() + # Automatically create the database and run migrations (is there a better way?) + execute_from_command_line(['django', 'migrate']) + + # Set up the things Django needs + django_setup() +except ImportError as e: + print('ERROR: The offline client requires ara-server to be installed') + raise e class AraOfflineClient(object): diff --git a/ara/clients/utils.py b/ara/clients/utils.py index 99006a8..e1ce500 100644 --- a/ara/clients/utils.py +++ b/ara/clients/utils.py @@ -16,10 +16,13 @@ # along with ARA. If not, see . from ara.clients.offline import AraOfflineClient +from ara.clients.http import AraHttpClient def get_client(client=None, **kwargs): if client is None or client == 'offline': return AraOfflineClient(**kwargs) + elif client == 'http': + return AraHttpClient(**kwargs) else: raise ValueError('Unsupported client: %s' % client)