code: def _make_request(self, url, headers, params=None):
          
          self.logger.debug("Request URL, Headers and Params: %s, %s, %s", url, headers, params)
  
          # Checking if request is in cache
          cache_key = "|".join([url, json.dumps(headers), json.dumps(params), str(self.timeout)])
          if cache_key in self.cache:
              self.logger.debug("Request found in cache. cache key %s", cache_key)
              return self.cache.get(cache_key)
  
          try:
              resp = requests.get(
                  url, headers=headers, verify=self.ssl_verify, params=params, timeout=self.timeout, proxies=self.proxies
              )
              resp.raise_for_status()
          except requests.exceptions.HTTPError as e:
              self.logger.debug("Error contacting openstack endpoint: %s", e)
              if resp.status_code == 401:
                  self.logger.info('Need to reauthenticate before next check')
                  raise AuthenticationNeeded()
              elif resp.status_code == 409:
                  raise InstancePowerOffFailure()
              else:
                  raise e
          except Exception:
              self.logger.exception("Unexpected error contacting openstack endpoint {}".format(url))
              raise
          jresp = resp.json()
          self.logger.debug("url: %s || response: %s", url, jresp)
  
          # Adding response to the cache
          self.cache[cache_key] = jresp
          return jresp
docs: Generic request handler for OpenStack API requests
          Raises specialized Exceptions for commonly encountered error codes