def do_update(request): _logger.info("{} plugin update started...".format(request._name)) code, link = update_repo_sources_and_plugin(request._type, request._name) if code != 0: _logger.error("{} plugin update failed. Logs available at {}".format( request._name, link)) else: _logger.info("{} plugin update completed. Logs available at {}".format( request._name, link)) # PKGUP audit log entry storage_client = connect.get_storage_async() audit = AuditLogger(storage_client) audit_detail = { 'packageName': "fledge-{}-{}".format(request._type, request._name.replace("_", "-")) } asyncio.ensure_future(audit.information('PKGUP', audit_detail)) # Restart the services which were disabled before plugin update for s in request._sch_list: asyncio.ensure_future( server.Server.scheduler.enable_schedule(uuid.UUID(s))) # Below case is applicable for the notification plugins ONLY # Enabled back configuration categories which were disabled during update process if request._type in ['notify', 'rule']: storage_client = connect.get_storage_async() config_mgr = ConfigurationManager(storage_client) for n in request._notification_list: asyncio.ensure_future( config_mgr.set_category_item_value_entry(n, "enable", "true"))
def test_exception_when_non_fledge_storage(self, mock_logger): with patch.object(ServiceRegistry._logger, 'info') as log_info: ServiceRegistry.register("foo", "Storage", "127.0.0.1", 1, 2) assert 1 == log_info.call_count args, kwargs = log_info.call_args assert args[0].startswith('Registered service instance id=') assert args[0].endswith( ': <foo, type=Storage, protocol=http, address=127.0.0.1, service port=1, ' 'management port=2, status=1>') with pytest.raises(DoesNotExist) as excinfo: connect.get_storage_async() assert str(excinfo).endswith('DoesNotExist') mock_logger.exception.assert_called_once_with('')
async def get_notifications(request): """ GET list of notifications :Example: curl -X GET http://localhost:8081/fledge/notification """ try: storage = connect.get_storage_async() config_mgr = ConfigurationManager(storage) all_notifications = await config_mgr._read_all_child_category_names( "Notifications") notifications = [] for notification in all_notifications: notification_config = await config_mgr._read_category_val( notification['child']) notification = { "name": notification_config['name']['value'], "rule": notification_config['rule']['value'], "channel": notification_config['channel']['value'], "notificationType": notification_config['notification_type']['value'], "enable": notification_config['enable']['value'], } notifications.append(notification) except Exception as ex: raise web.HTTPInternalServerError(reason=ex) else: return web.json_response({'notifications': notifications})
async def get_category(request): """ Args: request: category_name is required Returns: the configuration items in the given category. :Example: curl -X GET http://localhost:8081/fledge/category/PURGE_READ """ category_name = request.match_info.get('category_name', None) category_name = urllib.parse.unquote( category_name) if category_name is not None else None cf_mgr = ConfigurationManager(connect.get_storage_async()) category = await cf_mgr.get_category_all_items(category_name) if category is None: raise web.HTTPNotFound( reason="No such Category found for {}".format(category_name)) try: request.is_core_mgt except AttributeError: category = hide_password(category) return web.json_response(category)
async def delete_parent_category(request): """ Args: request: category_name Returns: remove the link b/w parent-child category for the parent :Example: curl -X DELETE http://localhost:8081/fledge/category/{category_name}/parent """ category_name = request.match_info.get('category_name', None) category_name = urllib.parse.unquote( category_name) if category_name is not None else None cf_mgr = ConfigurationManager(connect.get_storage_async()) try: await cf_mgr.delete_parent_category(category_name) except TypeError as ex: raise web.HTTPBadRequest(reason=str(ex)) except ValueError as ex: raise web.HTTPNotFound(reason=str(ex)) return web.json_response({ "message": "Parent-child relationship for the parent-{} is deleted".format( category_name) })
async def delete_child_category(request): """ Args: request: category_name, child_category are required Returns: remove the link b/w child category and its parent :Example: curl -X DELETE http://localhost:8081/fledge/category/{category_name}/children/{child_category} """ category_name = request.match_info.get('category_name', None) child_category = request.match_info.get('child_category', None) category_name = urllib.parse.unquote( category_name) if category_name is not None else None cf_mgr = ConfigurationManager(connect.get_storage_async()) try: result = await cf_mgr.delete_child_category(category_name, child_category) except TypeError as ex: raise web.HTTPBadRequest(reason=str(ex)) except ValueError as ex: raise web.HTTPNotFound(reason=str(ex)) return web.json_response({"children": result})
async def get_backup_download(request): """ Download back up file by id :Example: wget -O fledge-backup-1.tar.gz http://localhost:8081/fledge/backup/1/download """ backup_id = request.match_info.get('backup_id', None) try: backup_id = int(backup_id) backup = Backup(connect.get_storage_async()) backup_json = await backup.get_backup_details(backup_id) # Strip filename from backup path file_name_path = str(backup_json["file_name"]).split('data/backup/') file_name = str(file_name_path[1]) dir_name = _FLEDGE_DATA + '/backup/' if _FLEDGE_DATA else _FLEDGE_ROOT + "/data/backup/" source = dir_name + file_name # Create tar file t = tarfile.open(source + ".tar.gz", "w:gz") t.add(source, arcname=os.path.basename(source)) t.close() # Path of tar.gz file gz_path = Path(source + ".tar.gz") except ValueError: raise web.HTTPBadRequest(reason='Invalid backup id') except exceptions.DoesNotExist: raise web.HTTPNotFound(reason='Backup id {} does not exist'.format(backup_id)) except Exception as ex: raise web.HTTPInternalServerError(reason=(str(ex))) return web.FileResponse(path=gz_path)
async def get_filter_pipeline(request: web.Request) -> web.Response: """ GET filter pipeline :Example: curl -X GET http://localhost:8081/fledge/filter/<user_name>/pipeline """ user_name = request.match_info.get('user_name', None) try: storage = connect.get_storage_async() cf_mgr = ConfigurationManager(storage) # Fetch the filter items: get category items category_info = await cf_mgr.get_category_all_items( category_name=user_name) if category_info is None: raise ValueError("No such '{}' category found.".format(user_name)) filter_value_from_storage = json.loads( category_info['filter']['value']) except KeyError: msg = "No filter pipeline exists for {}".format(user_name) _LOGGER.info(msg) raise web.HTTPNotFound(reason=msg) except StorageServerError as ex: _LOGGER.exception("Get pipeline: %s, caught exception: %s", user_name, str(ex.error)) raise web.HTTPInternalServerError(reason=str(ex.error)) except ValueError as ex: raise web.HTTPNotFound(reason=ex) except Exception as ex: raise web.HTTPInternalServerError(reason=ex) else: return web.json_response({'result': filter_value_from_storage})
async def get_south_services(request): """ Args: request: Returns: list of all south services with tracked assets and readings count :Example: curl -X GET http://localhost:8081/fledge/south """ if 'cached' in request.query and request.query['cached'].lower( ) == 'false': _get_installed_plugins.cache_clear() storage_client = connect.get_storage_async() cf_mgr = ConfigurationManager(storage_client) try: south_cat = await cf_mgr.get_category_child("South") south_categories = [nc["key"] for nc in south_cat] except: return web.json_response({'services': []}) response = await _services_with_assets(storage_client, south_categories) return web.json_response({'services': response})
async def certificate_login(cls, username, host): """ Args: username: username host: IP address Returns: uid: User id token: jwt token is_admin: boolean flag """ storage_client = connect.get_storage_async() # get user info on the basis of username payload = PayloadBuilder().SELECT("id", "role_id").WHERE(['uname', '=', username])\ .AND_WHERE(['enabled', '=', 't']).payload() result = await storage_client.query_tbl_with_payload( 'users', payload) if len(result['rows']) == 0: raise User.DoesNotExist('User does not exist') found_user = result['rows'][0] uid, jwt_token, is_admin = await cls._get_new_token( storage_client, found_user, host) return uid, jwt_token, is_admin
async def get_scheduled_process(request): """ Returns: a list of all the defined scheduled_processes from scheduled_processes table :Example: curl -X GET http://localhost:8081/fledge/schedule/process/purge curl -X GET http://localhost:8081/fledge/schedule/process/purge%2Cbackup%2Crestore curl -X GET http://localhost:8081/fledge/schedule/process/purge%2Cbackup%2Cstats%20collector """ scheduled_process_names = request.match_info.get('scheduled_process_name', None) scheduled_process_name = scheduled_process_names.split(',') payload = PayloadBuilder().SELECT("name").WHERE( ["name", "in", scheduled_process_name]).payload() _storage = connect.get_storage_async() scheduled_process = await _storage.query_tbl_with_payload( 'scheduled_processes', payload) if len(scheduled_process['rows']) == 0: raise web.HTTPNotFound(reason='No such Scheduled Process: {}.'.format( scheduled_process_name)) if len(scheduled_process['rows']) == 1: retval = scheduled_process['rows'][0].get("name") else: retval = scheduled_process['rows'] return web.json_response(retval)
async def all(cls): storage_client = connect.get_storage_async() payload = PayloadBuilder().SELECT("id", "uname", "role_id").WHERE( ['enabled', '=', 't']).payload() result = await storage_client.query_tbl_with_payload( 'users', payload) return result['rows']
async def delete(cls, user_id): """ Args: user_id: user id to delete Returns: json response """ # either keep 1 admin user or just reserve id:1 for superuser if int(user_id) == 1: raise ValueError("Super admin user can not be deleted") storage_client = connect.get_storage_async() try: # first delete the active login references await cls.delete_user_tokens(user_id) payload = PayloadBuilder().SET(enabled="f").WHERE( ['id', '=', user_id]).AND_WHERE(['enabled', '=', 't']).payload() result = await storage_client.update_tbl("users", payload) except StorageServerError as ex: if ex.error["retryable"]: pass # retry INSERT raise ValueError(ERROR_MSG) return result
async def create_child_category(request): """ Args: request: category_name is required and JSON object that defines the child category Returns: parent of the children being added :Example: curl -d '{"children": ["coap", "http", "sinusoid"]}' -X POST http://localhost:8081/fledge/category/south/children """ cf_mgr = ConfigurationManager(connect.get_storage_async()) data = await request.json() if not isinstance(data, dict): raise ValueError('Data payload must be a dictionary') category_name = request.match_info.get('category_name', None) category_name = urllib.parse.unquote( category_name) if category_name is not None else None children = data.get('children') try: r = await cf_mgr.create_child_category(category_name, children) except TypeError as ex: raise web.HTTPBadRequest(reason=str(ex)) except ValueError as ex: raise web.HTTPNotFound(reason=str(ex)) return web.json_response(r)
async def get_role_id_by_name(cls, name): storage_client = connect.get_storage_async() payload = PayloadBuilder().SELECT("id").WHERE(['name', '=', name]).payload() result = await storage_client.query_tbl_with_payload( 'roles', payload) return result["rows"]
async def delete_category(request): """ Args: request: category_name required Returns: Success message on successful deletion Raises: TypeError/ValueError/Exception on error :Example: curl -X DELETE http://localhost:8081/fledge/category/{category_name} """ category_name = request.match_info.get('category_name', None) category_name = urllib.parse.unquote( category_name) if category_name is not None else None try: cf_mgr = ConfigurationManager(connect.get_storage_async()) await cf_mgr.delete_category_and_children_recursively(category_name) except (ValueError, TypeError) as ex: raise web.HTTPBadRequest(reason=ex) except Exception as ex: raise web.HTTPInternalServerError(reason=ex) else: return web.json_response({ 'result': 'Category {} deleted successfully.'.format(category_name) })
async def remove_plugin(request): """ Remove installed plugin from fledge type: installed plugin type name: installed plugin name Example: curl -X DELETE http://localhost:8081/fledge/plugins/south/sinusoid curl -X DELETE http://localhost:8081/fledge/plugins/north/http_north curl -X DELETE http://localhost:8081/fledge/plugins/filter/expression curl -X DELETE http://localhost:8081/fledge/plugins/notificationDelivery/alexa curl -X DELETE http://localhost:8081/fledge/plugins/notificationRule/Average """ plugin_type = request.match_info.get('type', None) name = request.match_info.get('name', None) try: plugin_type = str(plugin_type).lower() if not str(plugin_type).startswith('notification') else plugin_type if plugin_type not in valid_plugin_types: raise ValueError("Invalid plugin type. Please provide valid type: {}".format(valid_plugin_types)) installed_plugin = PluginDiscovery.get_plugins_installed(plugin_type, False) if name not in [plugin['name'] for plugin in installed_plugin]: raise KeyError("Invalid plugin name {} or plugin is not installed".format(name)) if plugin_type in ['notificationDelivery', 'notificationRule']: notification_instances_plugin_used_in = await check_plugin_usage_in_notification_instances(name) if notification_instances_plugin_used_in: raise RuntimeError("{} cannot be removed. This is being used by {} instances". format(name, notification_instances_plugin_used_in)) plugin_type = 'notify' if plugin_type == 'notificationDelivery' else 'rule' else: get_tracked_plugins = await check_plugin_usage(plugin_type, name) if get_tracked_plugins: e = "{} cannot be removed. This is being used by {} instances".\ format(name, get_tracked_plugins[0]['service_list']) _logger.error(e) raise RuntimeError(e) else: _logger.info("No entry found for {name} plugin in asset tracker; or " "{name} plugin may have been added in disabled state & never used".format(name=name)) res, log_path, is_package = purge_plugin(plugin_type, name) if res != 0: e_msg = "Something went wrong. Please check log {}".format(log_path) _logger.error(e_msg) raise RuntimeError(e_msg) else: if is_package: storage_client = connect.get_storage_async() audit_log = AuditLogger(storage_client) audit_detail = {'package_name': "fledge-{}-{}".format(plugin_type, name)} await audit_log.information('PKGRM', audit_detail) except (ValueError, RuntimeError) as ex: raise web.HTTPBadRequest(reason=str(ex)) except KeyError as ex: raise web.HTTPNotFound(reason=str(ex)) except PackageError as e: msg = "Failed to remove package for plugin {}".format(name) raise web.HTTPBadRequest(body=json.dumps({"message": msg, "link": str(e)}), reason=msg) else: _logger.info('{} plugin removed successfully'.format(name)) return web.json_response({'message': '{} plugin removed successfully'.format(name)}, status=200)
async def _get_plugin_and_sch_name_from_asset_tracker(_type: str) -> list: event_name = "Ingest" if _type == "south" else "Egress" storage_client = connect.get_storage_async() payload = PayloadBuilder().SELECT("plugin", "service").WHERE( ['event', '=', event_name]).payload() result = await storage_client.query_tbl_with_payload( 'asset_tracker', payload) return result['rows']
async def update_service(request: web.Request) -> web.Response: """ update service :Example: curl -sX PUT http://localhost:8081/fledge/service/notification/notification/update """ _type = request.match_info.get('type', None) name = request.match_info.get('name', None) try: _type = _type.lower() if _type != 'notification': raise ValueError("Invalid service type. Must be 'notification'") # Check requested service name is installed or not installed_services = get_service_installed() if name not in installed_services: raise KeyError( "{} service is not installed yet. Hence update is not possible." .format(name)) storage_client = connect.get_storage_async() # TODO: process_name ends with "_c" suffix payload = PayloadBuilder().SELECT( "id", "enabled", "schedule_name").WHERE(['process_name', '=', '{}_c'.format(_type)]).payload() result = await storage_client.query_tbl_with_payload( 'schedules', payload) sch_info = result['rows'] sch_list = [] if sch_info and sch_info[0]['enabled'] == 't': status, reason = await server.Server.scheduler.disable_schedule( uuid.UUID(sch_info[0]['id'])) if status: _logger.warning( "Schedule is disabled for {}, as {} service of type {} is being updated..." .format(sch_info[0]['schedule_name'], name, _type)) # TODO: SCHCH Audit log entry sch_list.append(sch_info[0]['id']) # service update is running as a background task loop = request.loop request._type = _type request._name = name request._sch_list = sch_list loop.call_later(1, do_update, request) except KeyError as ex: raise web.HTTPNotFound(reason=str(ex)) except ValueError as ex: raise web.HTTPBadRequest(reason=str(ex)) except Exception as ex: raise web.HTTPInternalServerError(reason=str(ex)) return web.json_response({ "message": "{} service update in process. Wait for few minutes to complete.". format(name) })
async def update(cls, user_id, user_data): """ Args: user_id: logged user id user_data: user dict Returns: updated user info dict """ kwargs = dict() if 'role_id' in user_data: kwargs.update({"role_id": user_data['role_id']}) storage_client = connect.get_storage_async() hashed_pwd = None pwd_history_list = [] if 'password' in user_data: if len(user_data['password']): hashed_pwd = cls.hash_password(user_data['password']) current_datetime = datetime.now() kwargs.update({ "pwd": hashed_pwd, "pwd_last_changed": str(current_datetime) }) # get password history list pwd_history_list = await cls._get_password_history( storage_client, user_id, user_data) try: payload = PayloadBuilder().SET(**kwargs).WHERE( ['id', '=', user_id]).AND_WHERE(['enabled', '=', 't']).payload() result = await storage_client.update_tbl("users", payload) if result['rows_affected']: # FIXME: FOGL-1226 active session delete only in case of role_id and password updation # delete all active sessions await cls.delete_user_tokens(user_id) if 'password' in user_data: # insert pwd history and delete oldest pwd if USED_PASSWORD_HISTORY_COUNT exceeds await cls._insert_pwd_history_with_oldest_pwd_deletion_if_count_exceeds( storage_client, user_id, hashed_pwd, pwd_history_list) return True except StorageServerError as ex: if ex.error["retryable"]: pass # retry UPDATE raise ValueError(ERROR_MSG) except Exception: raise
async def delete_service(request): """ Delete an existing service :Example: curl -X DELETE http://localhost:8081/fledge/service/<svc name> """ try: svc = request.match_info.get('service_name', None) storage = connect.get_storage_async() result = await get_schedule(storage, svc) if result['count'] == 0: return web.HTTPNotFound( reason='{} service does not exist.'.format(svc)) config_mgr = ConfigurationManager(storage) # In case of notification service, if notifications exists, then deletion is not allowed if 'notification' in result['rows'][0]['process_name']: notf_children = await config_mgr.get_category_child( category_name="Notifications") children = [x['key'] for x in notf_children] if len(notf_children) > 0: return web.HTTPBadRequest( reason= 'Notification service `{}` can not be deleted, as {} notification instances exist.' .format(svc, children)) # First disable the schedule svc_schedule = result['rows'][0] sch_id = uuid.UUID(svc_schedule['id']) if svc_schedule['enabled'].lower() == 't': await server.Server.scheduler.disable_schedule(sch_id) # return control to event loop await asyncio.sleep(1) # Delete all configuration for the service name await config_mgr.delete_category_and_children_recursively(svc) # Remove from registry as it has been already shutdown via disable_schedule() and since # we intend to delete the schedule also, there is no use of its Service registry entry try: services = ServiceRegistry.get(name=svc) ServiceRegistry.remove_from_registry(services[0]._id) except service_registry_exceptions.DoesNotExist: pass # Delete schedule await server.Server.scheduler.delete_schedule(sch_id) except Exception as ex: raise web.HTTPInternalServerError(reason=str(ex)) else: return web.json_response( {'result': 'Service {} deleted successfully.'.format(svc)})
async def delete_token(cls, token): storage_client = connect.get_storage_async() payload = PayloadBuilder().WHERE(['token', '=', token]).payload() try: res = await storage_client.delete_from_tbl( "user_logins", payload) except StorageServerError as ex: if not ex.error["retryable"]: pass raise ValueError(ERROR_MSG) return res
def test_get_storage(self): with patch.object(ServiceRegistry._logger, 'info') as log_info: ServiceRegistry.register("Fledge Storage", "Storage", "127.0.0.1", 37449, 37843) storage_client = connect.get_storage_async() assert isinstance(storage_client, StorageClientAsync) assert 1 == log_info.call_count args, kwargs = log_info.call_args assert args[0].startswith('Registered service instance id=') assert args[0].endswith( ': <Fledge Storage, type=Storage, protocol=http, address=127.0.0.1, service port=37449,' ' management port=37843, status=1>')
async def create_backup(request): """ Creates a backup :Example: curl -X POST http://localhost:8081/fledge/backup """ try: backup = Backup(connect.get_storage_async()) status = await backup.create_backup() except Exception as ex: raise web.HTTPInternalServerError(reason=str(ex)) return web.json_response({"status": status})
async def is_user_exists(cls, username, password): payload = PayloadBuilder().SELECT("id", "pwd").WHERE( ['uname', '=', username]).AND_WHERE(['enabled', '=', 't']).payload() storage_client = connect.get_storage_async() result = await storage_client.query_tbl_with_payload( 'users', payload) if len(result['rows']) == 0: return None found_user = result['rows'][0] is_valid_pwd = cls.check_password(found_user['pwd'], str(password)) return result['rows'][0]['id'] if is_valid_pwd else None
async def _read_config(self): """Reads configuration""" default_config = { "sleep_interval": { "description": "Time in seconds to sleep between health checks. (must be greater than 5)", "type": "integer", "default": str(self._DEFAULT_SLEEP_INTERVAL), "displayName": "Health Check Interval (In seconds)", "minimum": "5" }, "ping_timeout": { "description": "Timeout for a response from any given micro-service. (must be greater than 0)", "type": "integer", "default": str(self._DEFAULT_PING_TIMEOUT), "displayName": "Ping Timeout", "minimum": "1", "maximum": "5" }, "max_attempts": { "description": "Maximum number of attempts for finding a heartbeat of service", "type": "integer", "default": str(self._DEFAULT_MAX_ATTEMPTS), "displayName": "Max Attempts To Check Heartbeat", "minimum": "1" }, "restart_failed": { "description": "Restart failed microservice - manual/auto", "type": "enumeration", 'options': ['auto', 'manual'], "default": self._DEFAULT_RESTART_FAILED, "displayName": "Restart Failed" } } storage_client = connect.get_storage_async() cfg_manager = ConfigurationManager(storage_client) await cfg_manager.create_category('SMNTR', default_config, 'Service Monitor', display_name='Service Monitor') config = await cfg_manager.get_category_all_items('SMNTR') self._sleep_interval = int(config['sleep_interval']['value']) self._ping_timeout = int(config['ping_timeout']['value']) self._max_attempts = int(config['max_attempts']['value']) self._restart_failed = config['restart_failed']['value']
async def login(cls, username, password, host): """ Args: username: username password: password host: IP address Returns: return token """ # check password change configuration storage_client = connect.get_storage_async() cfg_mgr = ConfigurationManager(storage_client) category_item = await cfg_mgr.get_category_item( 'rest_api', 'passwordChange') age = int(category_item['value']) # get user info on the basis of username payload = PayloadBuilder().SELECT("pwd", "id", "role_id", "pwd_last_changed").WHERE(['uname', '=', username])\ .ALIAS("return", ("pwd_last_changed", 'pwd_last_changed'))\ .FORMAT("return", ("pwd_last_changed", "YYYY-MM-DD HH24:MI:SS.MS"))\ .AND_WHERE(['enabled', '=', 't']).payload() result = await storage_client.query_tbl_with_payload( 'users', payload) if len(result['rows']) == 0: raise User.DoesNotExist('User does not exist') found_user = result['rows'][0] # check age of password t1 = datetime.now() t2 = datetime.strptime(found_user['pwd_last_changed'], "%Y-%m-%d %H:%M:%S.%f") delta = t1 - t2 if age == 0: # user will not be forced to change their password. pass elif age <= delta.days: # user will be forced to change their password. raise User.PasswordExpired(found_user['id']) # validate password is_valid_pwd = cls.check_password(found_user['pwd'], str(password)) if not is_valid_pwd: raise User.PasswordDoesNotMatch( 'Username or Password do not match') uid, jwt_token, is_admin = await cls._get_new_token( storage_client, found_user, host) return uid, jwt_token, is_admin
async def verify_certificate(cls, cert): certs_dir = _FLEDGE_DATA + '/etc/certs' if _FLEDGE_DATA else _FLEDGE_ROOT + "/data/etc/certs" storage_client = connect.get_storage_async() cfg_mgr = ConfigurationManager(storage_client) ca_cert_item = await cfg_mgr.get_category_item( 'rest_api', 'authCertificateName') ca_cert_file = "{}/{}.cert".format(certs_dir, ca_cert_item['value']) SSLVerifier.set_ca_cert(ca_cert_file) SSLVerifier.set_user_cert(cert) SSLVerifier.verify( ) # raises OSError, SSLVerifier.VerificationError
async def get_statistics(request): """ Args: request: Returns: a general set of statistics :Example: curl -X GET http://localhost:8081/fledge/statistics """ payload = PayloadBuilder().SELECT( ("key", "description", "value")).ORDER_BY(["key"]).payload() storage_client = connect.get_storage_async() result = await storage_client.query_tbl_with_payload('statistics', payload) return web.json_response(result['rows'])
async def get_audit_log_codes(request): """ Args: request: Returns: an array of log codes with description :Example: curl -X GET http://localhost:8081/fledge/audit/logcode """ storage_client = connect.get_storage_async() result = await storage_client.query_tbl('log_codes') return web.json_response({'logCode': result['rows']})