From: Frank Brehm Date: Tue, 7 Nov 2017 17:13:07 +0000 (+0100) Subject: First successful request X-Git-Tag: 0.1.2~115 X-Git-Url: https://git.uhu-banane.de/?a=commitdiff_plain;h=b3654b4d376efada1923b07b2075bec63a8f1b15;p=pixelpark%2Fadmin-tools.git First successful request --- diff --git a/pp_lib/pdns_app.py b/pp_lib/pdns_app.py index a6a9a8e..0b75cd1 100644 --- a/pp_lib/pdns_app.py +++ b/pp_lib/pdns_app.py @@ -24,7 +24,7 @@ from .common import pp, to_bool from .cfg_app import PpCfgAppError, PpConfigApplication -__version__ = '0.1.1' +__version__ = '0.2.0' LOG = logging.getLogger(__name__) _LIBRARY_NAME = "pp-pdns-api-client" @@ -36,6 +36,45 @@ class PpPDNSAppError(PpCfgAppError): pass +# ============================================================================= +class PDNSApiError(PpPDNSAppError): + """Base class for more complex exceptions""" + def __init__(self, resp, content, uri=None): + self.resp = resp + self.content = content + self.uri = uri + + +# ============================================================================= +class PDNSApiNotAuthorizedError(PDNSApiError): + """The authorization information provided is not correct""" + + +# ============================================================================= +class PDNSApiNotFoundError(PDNSApiError): + """The ProfitBricks entity was not found""" + + +# ============================================================================= +class PDNSApiValidationError(PDNSApiError): + """The HTTP data provided is not valid""" + + +# ============================================================================= +class PDNSApiRateLimitExceededError(PDNSApiError): + """The number of requests sent have exceeded the allowed API rate limit""" + + +# ============================================================================= +class PDNSApiRequestError(PDNSApiError): + """Base error for request failures""" + + +# ============================================================================= +class PDNSApiTimeoutError(PDNSApiRequestError): + """Raised when a request does not finish in the given time span.""" + + # ============================================================================= class PpPDNSApplication(PpConfigApplication): @@ -57,6 +96,7 @@ class PpPDNSApplication(PpConfigApplication): default_api_port = 8081 default_api_servername = "localhost" + default_timeout = 20 # ------------------------------------------------------------------------- def __init__( @@ -69,8 +109,9 @@ class PpPDNSApplication(PpConfigApplication): self._api_host = self.api_hosts['global'] self._api_port = self.default_api_port self._api_servername = self.default_api_servername - self._user_agent = '{}/{}'.format(_LIBRARY_NAME,self.version) + self._user_agent = '{}/{}'.format(_LIBRARY_NAME, self.version) self._environment = 'global' + self._timeout = self.default_timeout stems = [] if cfg_stems: @@ -100,6 +141,8 @@ class PpPDNSApplication(PpConfigApplication): cfg_encoding=cfg_encoding, need_config_file=need_config_file, ) + self._user_agent = '{}/{}'.format(_LIBRARY_NAME, self.version) + # ----------------------------------------------------------- @property def api_key(self): @@ -161,6 +204,19 @@ class PpPDNSApplication(PpConfigApplication): raise PpPDNSAppError("Invalid user agent {!r} given.".format(value)) self._user_agent = str(value).strip() + # ----------------------------------------------------------- + @property + def timeout(self): + "The timeout in seconds on requesting the PowerDNS API." + return self._timeout + + @timeout.setter + def timeout(self, value): + v = int(value) + if v < 1: + raise PpPDNSAppError("Invalid timeout {!r} given.".format(value)) + self._timeout = v + # ----------------------------------------------------------- @property def environment(self): @@ -197,9 +253,11 @@ class PpPDNSApplication(PpConfigApplication): res['api_keys'] = copy.copy(self.api_keys) res['api_port'] = self.api_port res['api_servername'] = self.api_servername - res['default_api_servername'] = self.default_api_servername res['default_api_port'] = self.default_api_port + res['default_api_servername'] = self.default_api_servername + res['default_timeout'] = self.default_timeout res['environment'] = self.environment + res['timeout'] = self.timeout res['user_agent'] = self.user_agent return res @@ -257,6 +315,13 @@ class PpPDNSApplication(PpConfigApplication): help=("Which port to connect to PowerDNS API, default: {}.".format(self.default_api_port)), ) + pdns_group.add_argument( + '-t', '--timeout', + metavar="SECS", type=int, dest='timeout', default=self.default_timeout, + help=("The timeout in seconds to request the PowerDNS API, default: {}.".format( + self.default_timeout)), + ) + # ------------------------------------------------------------------------- def perform_arg_parser(self): """ @@ -276,6 +341,9 @@ class PpPDNSApplication(PpConfigApplication): if self.args.api_port: self.api_port = self.args.api_port + if self.args.timeout: + self.timeout = self.args.timeout + # ------------------------------------------------------------------------- def pre_run(self): """ @@ -310,6 +378,73 @@ class PpPDNSApplication(PpConfigApplication): if self.verbose > 1: LOG.debug("executing post_run() ...") + # ------------------------------------------------------------------------- + def _build_url(self, path): + + url = 'http://{}'.format(self.api_host) + if self.api_port != 80: + url += ':{}'.format(self.api_port) + + url += '/api/v1' + path + LOG.debug("Used URL: {!r}".format(url)) + return url + + # ------------------------------------------------------------------------- + def perform_request(self, path, method='GET', data=None, headers=None): + """Performing the underlying API request.""" + + if headers is None: + headers = dict() + headers['X-API-Key'] = self.api_key + + url = self._build_url(path) + if self.verbose > 1: + LOG.debug("Request method: {!r}".format(method)) + if data and self.verbose > 1: + data_out = "{!r}".format(data) + try: + data_out = json.loads(data) + except ValueError: + pass + else: + data_out = pp(data_out) + LOG.debug("Data:\n%s", data_out) + + headers.update({'User-Agent': self.user_agent}) + headers.update({'Content-Type': 'application/json'}) + if self.verbose > 1: + LOG.debug("Headers:\n%s", pp(headers)) + + session = requests.Session() + response = session.request(method, url, data=data, headers=headers, timeout=self.timeout) + + try: + if not response.ok: + err = response.json() + code = err['httpStatus'] + msg = err['messages'] + if response.status_code == 401: + raise PDNSApiNotAuthorizedError(code, msg, url) + if response.status_code == 404: + raise PDNSApiNotFoundError(code, msg, url) + if response.status_code == 422: + raise PDNSApiValidationError(code, msg, url) + if response.status_code == 429: + raise PDNSApiRateLimitExceededError(code, msg, url) + else: + raise PDNSApiError(code, msg, url) + + except ValueError: + raise PpPDNSAppError('Failed to parse the response', response.text) + + json_response = response.json() + + if 'location' in response.headers: + json_response['requestId'] = self._request_id(response.headers) + + return json_response + + # ============================================================================= diff --git a/pp_lib/pdns_list_zones.py b/pp_lib/pdns_list_zones.py index 2bf1921..ddbd77c 100644 --- a/pp_lib/pdns_list_zones.py +++ b/pp_lib/pdns_list_zones.py @@ -19,7 +19,7 @@ from .common import pp from .pdns_app import PpPDNSAppError, PpPDNSApplication -__version__ = '0.1.0' +__version__ = '0.2.0' LOG = logging.getLogger(__name__) @@ -53,6 +53,10 @@ class PpPDNSListZonesApp(PpPDNSApplication): LOG.info("Listing all available zones from PowerrDNS environment {!r}.".format( self.environment)) + path = "/servers/{}/zones".format(self.api_servername) + json_response = self.perform_request(path) + if self.verbose > 2: + LOG.debug("Got a response:\n{}".format(pp(json_response))) # =============================================================================