| # Licensed to the Apache Software Foundation (ASF) under one |
| # or more contributor license agreements. See the NOTICE file |
| # distributed with this work for additional information |
| # regarding copyright ownership. The ASF licenses this file |
| # to you under the Apache License, Version 2.0 (the |
| # "License"); you may not use this file except in compliance |
| # with the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, |
| # software distributed under the License is distributed on an |
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| # KIND, either express or implied. See the License for the |
| # specific language governing permissions and limitations |
| # under the License. |
| # |
| import logging |
| |
| from backoff import expo, on_exception |
| from requests import get |
| from requests.exceptions import RequestException |
| |
| # The maximum amount of tries to attempt when making API calls. |
| MAX_TRIES = 3 |
| |
| class IpUtil: |
| _machine_ip = None |
| |
| def __init__(self): |
| self.IPIFY_API_URI = 'https://api.ipify.org' |
| self.IP_INFO_API_URI = 'https://ipinfo.io/ip' |
| self.IDENT_ME_API_URI = 'https://4.ident.me/' |
| self.USER_AGENT = 'openserverless-ip-resolver' |
| # The maximum amount of tries to attempt when making API calls. |
| self.MAX_TRIES = 3 |
| |
| @on_exception(expo, RequestException, max_tries=MAX_TRIES) |
| def _get_ip_resp(self,api_uri: str): |
| """ |
| Internal function which attempts to retrieve this machine's public IP |
| address from a service running at the given api_uri parameter. |
| :rtype: obj |
| :returns: The response object from the HTTP request. |
| :raises: RequestException if something bad happened and the request wasn't |
| completed. |
| .. note:: |
| If an error occurs when making the HTTP request, it will be retried |
| using an exponential backoff algorithm. This is a safe way to retry |
| failed requests without giving up. |
| """ |
| logging.debug(f"querying ip from {api_uri}") |
| return get(api_uri, headers={'user-agent': self.USER_AGENT}) |
| |
| def handle_ip_request(self,url, extra=None): |
| """ |
| Query a public api returning the machine's public ip address |
| >>> from openserverless.ip_util import IpUtil |
| >>> ip_util = IpUtil() |
| >>> ipify_ip = ip_util.handle_ip_request('https://api.ipify.org') |
| >>> ifinfo_ip = ip_util.handle_ip_request('https://ipinfo.io/ip') |
| >>> identme_ip = ip_util.handle_ip_request('https://4.ident.me/') |
| >>> ipify_ip == ifinfo_ip |
| True |
| >>> ipify_ip == identme_ip |
| True |
| """ |
| try: |
| resp = self._get_ip_resp(url) |
| except RequestException as e: |
| logging.error(e) |
| return None |
| |
| if resp.status_code != 200: |
| logging.error('Received an invalid status code from ip service:' + str(resp.status_code) + '. The service might be experiencing issues.') |
| return None |
| |
| return resp.text |
| |
| def get_ip_chain(self, url): |
| if not self._machine_ip: |
| self._machine_ip = self.handle_ip_request(url) |
| else : |
| logging.info(f"current ip address {self._machine_ip}") |
| |
| def get_public_ip(self): |
| """ |
| Attempt to get the machine public ip |
| >>> from openserverless.ip_util import IpUtil |
| >>> ip_util = IpUtil() |
| >>> ip = ip_util.get_public_ip() |
| >>> ip != None |
| True |
| >>> ipify_ip = ip_util.handle_ip_request('https://api.ipify.org') |
| >>> ip == ipify_ip |
| True |
| """ |
| self.get_ip_chain(self.IPIFY_API_URI) |
| self.get_ip_chain(self.IP_INFO_API_URI) |
| self.get_ip_chain(self.IDENT_ME_API_URI) |
| |
| return self._machine_ip |
| |