]> Frank Brehm's Git Trees - pixelpark/admin-tools.git/commitdiff
First successful request
authorFrank Brehm <frank.brehm@pixelpark.com>
Tue, 7 Nov 2017 17:13:07 +0000 (18:13 +0100)
committerFrank Brehm <frank.brehm@pixelpark.com>
Tue, 7 Nov 2017 17:13:07 +0000 (18:13 +0100)
pp_lib/pdns_app.py
pp_lib/pdns_list_zones.py

index a6a9a8ea7df989386d4e0e9de8fa193b8aed904d..0b75cd197535fc9c88889e50297ba56004378802 100644 (file)
@@ -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
+
+
 
 # =============================================================================
 
index 2bf1921e1245457bdc037788d75a6e48e9e6d070..ddbd77cee20527fa233eb628b3e675d9bb6ead53 100644 (file)
@@ -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)))
 
 
 # =============================================================================