Skip to content

Commit

Permalink
Display error message when .edgerc file is not found
Browse files Browse the repository at this point in the history
Connector list in tail mode improvments
Command return codes are documented in libeaa/error module
Improved error tracking on connector health with performance details
Additional coverage unit/system tests
Bump version to 0.4.2
  • Loading branch information
bitonio committed Jul 26, 2021
1 parent 481f209 commit 4106720
Show file tree
Hide file tree
Showing 10 changed files with 177 additions and 73 deletions.
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.4.0
0.4.2
6 changes: 5 additions & 1 deletion bin/akamai-eaa
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import time
import platform
import logging
import fnmatch
import signal
import http.client as http_client
from json import dumps

Expand Down Expand Up @@ -149,6 +150,8 @@ if __name__ == "__main__":
setup_logging()
logging.debug("Python %s" % platform.python_version())

signal.signal(signal.SIGTERM, cli.exit_gracefully)

try:
if config.command == "version":
print(__version__)
Expand Down Expand Up @@ -194,7 +197,8 @@ if __name__ == "__main__":
# the ArgumentParser won't have the attribute set
json = hasattr(config, 'json') and config.json
tail = hasattr(config, 'tail') and config.tail
c.list(perf, json, tail)
interval = hasattr(config, 'interval') and config.interval
c.list(perf, json, tail, interval, cli.stop_event)
elif config.command in ("certificate", "cert"):
c = CertificateAPI(config)
if config.action is None or config.action == "list" or config.certificate_id is None:
Expand Down
13 changes: 9 additions & 4 deletions bin/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

from configparser import ConfigParser

import _paths
from error import rc_error, cli_exit_with_error

class EdgeGridConfig():

Expand Down Expand Up @@ -140,6 +142,7 @@ def __init__(self, config_values, configuration, flags=None):
list_parser.add_argument('--perf', default=False, action="store_true", help='Show performance metrics')
list_parser.add_argument('--json', '-j', default=False, action="store_true", help='View as JSON')
list_parser.add_argument('--tail', '-f', default=False, action="store_true", help='Keep watching, do not exit until Control+C/SIGTERM')
list_parser.add_argument('--interval', '-i', default=300, type=float, help='Interval between update (works with --tail only)')
# subparsers.required = False
swap_parser = subsub.add_parser("swap", help="Swap connector with another one")
swap_parser.add_argument(dest="new_connector_id", help='New connector ID')
Expand Down Expand Up @@ -179,7 +182,7 @@ def __init__(self, config_values, configuration, flags=None):
try:
args = parser.parse_args()
except Exception:
sys.exit(1)
cli_exit_with_error(rc_error.GENERAL_ERROR)

arguments = vars(args)

Expand All @@ -194,9 +197,8 @@ def __init__(self, config_values, configuration, flags=None):
if not config.has_section(configuration):
err_msg = "ERROR: No section named %s was found in your %s file\n" % \
(configuration, arguments["edgerc"])
err_msg += "ERROR: Please generate credentials for the script functionality\n"
err_msg += "ERROR: and run 'python gen_edgerc.py %s' to generate the credential file\n" % configuration
sys.exit(err_msg)
cli_exit_with_error(rc_error.EDGERC_SECTION_NOT_FOUND, err_msg)

for key, value in config.items(configuration):
# ConfigParser lowercases magically
if key not in arguments or arguments[key] is None:
Expand All @@ -206,6 +208,9 @@ def __init__(self, config_values, configuration, flags=None):
print("Run python gen_edgerc.py to get your credentials file set up "
"once you've provisioned credentials in Akamai Control Center.")
return None
else:
err_msg = f"ERROR: EdgeRc configuration {arguments['edgerc']} not found.\n"
cli_exit_with_error(rc_error.EDGERC_MISSING.value, err_msg)

for option in arguments:
setattr(self, option, arguments[option])
Expand Down
2 changes: 1 addition & 1 deletion cli.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"commands": [
{
"name": "eaa",
"version": "0.4.1",
"version": "0.4.2",
"description": "Akamai CLI for Enterprise Application Access (EAA)"
}
]
Expand Down
Empty file added libeaa/__init__.py
Empty file.
4 changes: 2 additions & 2 deletions libeaa/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
config = EdgeGridConfig({'verbose': False}, 'default')

#: cli-eaa version
__version__ = '0.4.1'
__version__ = '0.4.2'

#: HTTP Request Timeout in seconds
HTTP_REQ_TIMEOUT = 300
Expand Down Expand Up @@ -227,7 +227,7 @@ def get(self, url_path, params=None):
Send a GET reques to the API.
"""
url = urljoin(self._baseurl, url_path)
response = self._session.get(url, params=self.build_params(params), timeout=HTTP_REQ_TIMEOUT)
response = self._session.get(url, params=self.build_params(params), timeout=HTTP_REQ_TIMEOUT)
logging.info("BaseAPI: GET response is HTTP %s" % response.status_code)
if response.status_code != requests.status_codes.codes.ok:
logging.info("BaseAPI: GET response body: %s" % response.text)
Expand Down
67 changes: 39 additions & 28 deletions libeaa/connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,18 +62,21 @@ def perf_system(self, connector_id):
Returns:
[tuple]: (connector_id, dictionnary with metric name as key)
"""
systemres_api_url = 'mgmt-pop/agents/{agentid}/system_resource/metrics'.format(agentid=connector_id)
perf_data_resp = self.get(systemres_api_url, params={'period': '1h'})
perf_data = perf_data_resp.json()
perf_latest = {
'timestamp': None,
'mem_pct': None, 'disk_pct': None, 'cpu_pct': None,
'network_traffic_mbps': None,
'dialout_total': None, 'dialout_idle': None, 'active_dialout_count': None
}
if len(perf_data.get('data', [])) >= 1:
perf_latest = perf_data.get('data', [])[-1]
return (connector_id, perf_latest)
try: # This method is executed as separate thread, we need the able to troubleshoot
systemres_api_url = 'mgmt-pop/agents/{agentid}/system_resource/metrics'.format(agentid=connector_id)
perf_data_resp = self.get(systemres_api_url, params={'period': '1h'})
perf_data = perf_data_resp.json()
perf_latest = {
'timestamp': None,
'mem_pct': None, 'disk_pct': None, 'cpu_pct': None,
'network_traffic_mbps': None,
'dialout_total': None, 'dialout_idle': None, 'active_dialout_count': None
}
if len(perf_data.get('data', [])) >= 1:
perf_latest = perf_data.get('data', [])[-1]
return (connector_id, perf_latest)
except:
logging.exception("Error during fetching connector performance health.")

def perf_apps(self, connector_id):
"""
Expand Down Expand Up @@ -170,29 +173,37 @@ def list_once(self, perf=False, json_fmt=False):
if not json_fmt:
cli.footer("Total %s connector(s)" % total_con)

def list(self, perf, json_fmt, follow=False, stopEvent=None):
def list(self, perf, json_fmt, follow=False, interval=300, stop_event=None):
"""
List the connector and their attributes and status
The default output is CSV
Args:
perf (bool): Add performance data (cpu, mem, disk, dialout)
json_fmt (bool): Output as JSON instead of CSV
follow (bool): Never stop until Control+C or SIGTERM is received
perf (bool): Add performance data (cpu, mem, disk, dialout)
json_fmt (bool): Output as JSON instead of CSV
follow (bool): Never stop until Control+C or SIGTERM is received
interval (float): Interval in seconds between pulling the API, default is 5 minutes (300s)
stop_event (Event): Main program stop event allowing the function
to stop at the earliest possible
"""
interval_sec = 5 * 60 # Interval in seconds between pulling the API, default is 5 minutes
while True or (stopEvent and not stopEvent.is_set()):
start = time.time()
self.list_once(perf, json_fmt)
if follow:
sleep_time = interval_sec - (time.time() - start)
if sleep_time > 0:
time.sleep(sleep_time)
while True or (stop_event and not stop_event.is_set()):
try:
start = time.time()
self.list_once(perf, json_fmt)
if follow:
sleep_time = interval - (time.time() - start)
if sleep_time > 0:
time.sleep(sleep_time)
else:
logging.error(f"The EAA Connector API is slow to respond (could be also a proxy in the middle), holding for {interval} sec.")
time.sleep(interval)
else:
logging.error(f"The EAA Connector API is slow to respond (could be also a proxy in the middle), holding for {interval_sec} sec.")
time.sleep(interval_sec)
else:
break
break
except Exception as e:
if follow:
logging.error(f"General exception {e}, since we are in follow mode (--tail), we keep going.")
else:
raise

def findappbyconnector(self, connector_moniker):
"""
Expand Down
41 changes: 41 additions & 0 deletions libeaa/error.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Copyright 2021 Akamai Technologies, Inc. All Rights Reserved
#
# Licensed 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 sys
from enum import Enum
from unittest.signals import installHandler

class rc_error(Enum):
"""
Command line return codes from 0 (all good) to 255
"""
OK = 0
GENERAL_ERROR = 2
EDGERC_MISSING = 30
EDGERC_SECTION_NOT_FOUND = 31
ERROR_NOT_SPECIFIED = 255


def cli_exit_with_error(error_code, message=None):
"""
Exit the command line with error_code (integer or rc_error) with an optional message.
"""
if message:
sys.stderr.write(message)
if isinstance(error_code, rc_error):
sys.exit(error_code.value)
elif isinstance(error_code, int):
sys.exit(error_code)
else:
sys.exit(rc_error.ERROR_NOT_SPECIFIED.value)
Empty file added test/__init__.py
Empty file.
Loading

0 comments on commit 4106720

Please sign in to comment.