diff options
Diffstat (limited to 'src/services')
-rw-r--r-- | src/services/api/graphql/bindings.py | 3 | ||||
-rw-r--r-- | src/services/api/graphql/graphql/errors.py | 8 | ||||
-rw-r--r-- | src/services/api/graphql/graphql/mutations.py | 17 | ||||
-rw-r--r-- | src/services/api/graphql/graphql/queries.py | 17 | ||||
-rw-r--r-- | src/services/api/graphql/session/__init__.py (renamed from src/services/api/graphql/recipes/__init__.py) | 0 | ||||
-rwxr-xr-x | src/services/api/graphql/session/composite/system_status.py (renamed from src/services/api/graphql/recipes/queries/system_status.py) | 0 | ||||
-rw-r--r-- | src/services/api/graphql/session/errors/op_mode_errors.py | 13 | ||||
-rw-r--r-- | src/services/api/graphql/session/override/remove_firewall_address_group_members.py (renamed from src/services/api/graphql/recipes/remove_firewall_address_group_members.py) | 0 | ||||
-rw-r--r-- | src/services/api/graphql/session/session.py (renamed from src/services/api/graphql/recipes/session.py) | 15 | ||||
-rw-r--r-- | src/services/api/graphql/session/templates/create_dhcp_server.tmpl (renamed from src/services/api/graphql/recipes/templates/create_dhcp_server.tmpl) | 0 | ||||
-rw-r--r-- | src/services/api/graphql/session/templates/create_firewall_address_group.tmpl (renamed from src/services/api/graphql/recipes/templates/create_firewall_address_group.tmpl) | 0 | ||||
-rw-r--r-- | src/services/api/graphql/session/templates/create_firewall_address_ipv_6_group.tmpl (renamed from src/services/api/graphql/recipes/templates/create_firewall_address_ipv_6_group.tmpl) | 0 | ||||
-rw-r--r-- | src/services/api/graphql/session/templates/create_interface_ethernet.tmpl (renamed from src/services/api/graphql/recipes/templates/create_interface_ethernet.tmpl) | 0 | ||||
-rw-r--r-- | src/services/api/graphql/session/templates/remove_firewall_address_group_members.tmpl (renamed from src/services/api/graphql/recipes/templates/remove_firewall_address_group_members.tmpl) | 0 | ||||
-rw-r--r-- | src/services/api/graphql/session/templates/remove_firewall_address_ipv_6_group_members.tmpl (renamed from src/services/api/graphql/recipes/templates/remove_firewall_address_ipv_6_group_members.tmpl) | 0 | ||||
-rw-r--r-- | src/services/api/graphql/session/templates/update_firewall_address_group_members.tmpl (renamed from src/services/api/graphql/recipes/templates/update_firewall_address_group_members.tmpl) | 0 | ||||
-rw-r--r-- | src/services/api/graphql/session/templates/update_firewall_address_ipv_6_group_members.tmpl (renamed from src/services/api/graphql/recipes/templates/update_firewall_address_ipv_6_group_members.tmpl) | 0 | ||||
-rwxr-xr-x | src/services/api/graphql/utils/schema_from_op_mode.py | 48 | ||||
-rwxr-xr-x | src/services/vyos-http-api-server | 1 |
19 files changed, 106 insertions, 16 deletions
diff --git a/src/services/api/graphql/bindings.py b/src/services/api/graphql/bindings.py index 049d59de7..0b1260912 100644 --- a/src/services/api/graphql/bindings.py +++ b/src/services/api/graphql/bindings.py @@ -17,6 +17,7 @@ import vyos.defaults from . graphql.queries import query from . graphql.mutations import mutation from . graphql.directives import directives_dict +from . graphql.errors import op_mode_error from . utils.schema_from_op_mode import generate_op_mode_definitions from ariadne import make_executable_schema, load_schema_from_path, snake_case_fallback_resolvers @@ -27,6 +28,6 @@ def generate_schema(): type_defs = load_schema_from_path(api_schema_dir) - schema = make_executable_schema(type_defs, query, mutation, snake_case_fallback_resolvers, directives=directives_dict) + schema = make_executable_schema(type_defs, query, op_mode_error, mutation, snake_case_fallback_resolvers, directives=directives_dict) return schema diff --git a/src/services/api/graphql/graphql/errors.py b/src/services/api/graphql/graphql/errors.py new file mode 100644 index 000000000..1066300e0 --- /dev/null +++ b/src/services/api/graphql/graphql/errors.py @@ -0,0 +1,8 @@ + +from ariadne import InterfaceType + +op_mode_error = InterfaceType("OpModeError") + +@op_mode_error.type_resolver +def resolve_op_mode_error(obj, *_): + return obj['name'] diff --git a/src/services/api/graphql/graphql/mutations.py b/src/services/api/graphql/graphql/mutations.py index 3e89fb239..1b77cff87 100644 --- a/src/services/api/graphql/graphql/mutations.py +++ b/src/services/api/graphql/graphql/mutations.py @@ -21,7 +21,9 @@ from makefun import with_signature from .. import state from .. import key_auth -from api.graphql.recipes.session import Session +from api.graphql.session.session import Session +from api.graphql.session.errors.op_mode_errors import op_mode_err_msg, op_mode_err_code +from vyos.opmode import Error as OpModeError mutation = ObjectType("Mutation") @@ -71,7 +73,7 @@ def make_mutation_resolver(mutation_name, class_name, session_func): # one may override the session functions with a local subclass try: - mod = import_module(f'api.graphql.recipes.{func_base_name}') + mod = import_module(f'api.graphql.session.override.{func_base_name}') klass = getattr(mod, class_name) except ImportError: # otherwise, dynamically generate subclass to invoke subclass @@ -86,10 +88,19 @@ def make_mutation_resolver(mutation_name, class_name, session_func): "success": True, "data": data } + except OpModeError as e: + typename = type(e).__name__ + return { + "success": False, + "errore": ['op_mode_error'], + "op_mode_error": {"name": f"{typename}", + "message": op_mode_err_msg.get(typename, "Unknown"), + "vyos_code": op_mode_err_code.get(typename, 9999)} + } except Exception as error: return { "success": False, - "errors": [str(error)] + "errors": [repr(error)] } return func_impl diff --git a/src/services/api/graphql/graphql/queries.py b/src/services/api/graphql/graphql/queries.py index f6544709e..8ae61b704 100644 --- a/src/services/api/graphql/graphql/queries.py +++ b/src/services/api/graphql/graphql/queries.py @@ -21,7 +21,9 @@ from makefun import with_signature from .. import state from .. import key_auth -from api.graphql.recipes.session import Session +from api.graphql.session.session import Session +from api.graphql.session.errors.op_mode_errors import op_mode_err_msg, op_mode_err_code +from vyos.opmode import Error as OpModeError query = ObjectType("Query") @@ -71,7 +73,7 @@ def make_query_resolver(query_name, class_name, session_func): # one may override the session functions with a local subclass try: - mod = import_module(f'api.graphql.recipes.{func_base_name}') + mod = import_module(f'api.graphql.session.override.{func_base_name}') klass = getattr(mod, class_name) except ImportError: # otherwise, dynamically generate subclass to invoke subclass @@ -86,10 +88,19 @@ def make_query_resolver(query_name, class_name, session_func): "success": True, "data": data } + except OpModeError as e: + typename = type(e).__name__ + return { + "success": False, + "errors": ['op_mode_error'], + "op_mode_error": {"name": f"{typename}", + "message": op_mode_err_msg.get(typename, "Unknown"), + "vyos_code": op_mode_err_code.get(typename, 9999)} + } except Exception as error: return { "success": False, - "errors": [str(error)] + "errors": [repr(error)] } return func_impl diff --git a/src/services/api/graphql/recipes/__init__.py b/src/services/api/graphql/session/__init__.py index e69de29bb..e69de29bb 100644 --- a/src/services/api/graphql/recipes/__init__.py +++ b/src/services/api/graphql/session/__init__.py diff --git a/src/services/api/graphql/recipes/queries/system_status.py b/src/services/api/graphql/session/composite/system_status.py index 8dadcc9f3..8dadcc9f3 100755 --- a/src/services/api/graphql/recipes/queries/system_status.py +++ b/src/services/api/graphql/session/composite/system_status.py diff --git a/src/services/api/graphql/session/errors/op_mode_errors.py b/src/services/api/graphql/session/errors/op_mode_errors.py new file mode 100644 index 000000000..7ba75455d --- /dev/null +++ b/src/services/api/graphql/session/errors/op_mode_errors.py @@ -0,0 +1,13 @@ + + +op_mode_err_msg = { + "UnconfiguredSubsystem": "subsystem is not configured or not running", + "DataUnavailable": "data currently unavailable", + "PermissionDenied": "client does not have permission" +} + +op_mode_err_code = { + "UnconfiguredSubsystem": 2000, + "DataUnavailable": 2001, + "PermissionDenied": 1003 +} diff --git a/src/services/api/graphql/recipes/remove_firewall_address_group_members.py b/src/services/api/graphql/session/override/remove_firewall_address_group_members.py index b91932e14..b91932e14 100644 --- a/src/services/api/graphql/recipes/remove_firewall_address_group_members.py +++ b/src/services/api/graphql/session/override/remove_firewall_address_group_members.py diff --git a/src/services/api/graphql/recipes/session.py b/src/services/api/graphql/session/session.py index ac185beb7..93e1c328e 100644 --- a/src/services/api/graphql/recipes/session.py +++ b/src/services/api/graphql/session/session.py @@ -22,6 +22,7 @@ from vyos.config import Config from vyos.configtree import ConfigTree from vyos.defaults import directories from vyos.template import render +from vyos.opmode import Error as OpModeError from api.graphql.utils.util import load_op_mode_as_module, split_compound_op_mode_name @@ -149,7 +150,7 @@ class Session: return res def system_status(self): - import api.graphql.recipes.queries.system_status as system_status + import api.graphql.session.composite.system_status as system_status session = self._session data = self._data @@ -177,10 +178,10 @@ class Session: mod = load_op_mode_as_module(f'{scriptname}') func = getattr(mod, func_name) - if len(list(data)) > 0: + try: res = func(True, **data) - else: - res = func(True) + except OpModeError as e: + raise e return res @@ -199,9 +200,9 @@ class Session: mod = load_op_mode_as_module(f'{scriptname}') func = getattr(mod, func_name) - if len(list(data)) > 0: + try: res = func(**data) - else: - res = func() + except OpModeError as e: + raise e return res diff --git a/src/services/api/graphql/recipes/templates/create_dhcp_server.tmpl b/src/services/api/graphql/session/templates/create_dhcp_server.tmpl index 70de43183..70de43183 100644 --- a/src/services/api/graphql/recipes/templates/create_dhcp_server.tmpl +++ b/src/services/api/graphql/session/templates/create_dhcp_server.tmpl diff --git a/src/services/api/graphql/recipes/templates/create_firewall_address_group.tmpl b/src/services/api/graphql/session/templates/create_firewall_address_group.tmpl index a890d0086..a890d0086 100644 --- a/src/services/api/graphql/recipes/templates/create_firewall_address_group.tmpl +++ b/src/services/api/graphql/session/templates/create_firewall_address_group.tmpl diff --git a/src/services/api/graphql/recipes/templates/create_firewall_address_ipv_6_group.tmpl b/src/services/api/graphql/session/templates/create_firewall_address_ipv_6_group.tmpl index e9b660722..e9b660722 100644 --- a/src/services/api/graphql/recipes/templates/create_firewall_address_ipv_6_group.tmpl +++ b/src/services/api/graphql/session/templates/create_firewall_address_ipv_6_group.tmpl diff --git a/src/services/api/graphql/recipes/templates/create_interface_ethernet.tmpl b/src/services/api/graphql/session/templates/create_interface_ethernet.tmpl index d9d7ed691..d9d7ed691 100644 --- a/src/services/api/graphql/recipes/templates/create_interface_ethernet.tmpl +++ b/src/services/api/graphql/session/templates/create_interface_ethernet.tmpl diff --git a/src/services/api/graphql/recipes/templates/remove_firewall_address_group_members.tmpl b/src/services/api/graphql/session/templates/remove_firewall_address_group_members.tmpl index 458f3e5fc..458f3e5fc 100644 --- a/src/services/api/graphql/recipes/templates/remove_firewall_address_group_members.tmpl +++ b/src/services/api/graphql/session/templates/remove_firewall_address_group_members.tmpl diff --git a/src/services/api/graphql/recipes/templates/remove_firewall_address_ipv_6_group_members.tmpl b/src/services/api/graphql/session/templates/remove_firewall_address_ipv_6_group_members.tmpl index 0efa0b226..0efa0b226 100644 --- a/src/services/api/graphql/recipes/templates/remove_firewall_address_ipv_6_group_members.tmpl +++ b/src/services/api/graphql/session/templates/remove_firewall_address_ipv_6_group_members.tmpl diff --git a/src/services/api/graphql/recipes/templates/update_firewall_address_group_members.tmpl b/src/services/api/graphql/session/templates/update_firewall_address_group_members.tmpl index f56c61231..f56c61231 100644 --- a/src/services/api/graphql/recipes/templates/update_firewall_address_group_members.tmpl +++ b/src/services/api/graphql/session/templates/update_firewall_address_group_members.tmpl diff --git a/src/services/api/graphql/recipes/templates/update_firewall_address_ipv_6_group_members.tmpl b/src/services/api/graphql/session/templates/update_firewall_address_ipv_6_group_members.tmpl index f98a5517c..f98a5517c 100644 --- a/src/services/api/graphql/recipes/templates/update_firewall_address_ipv_6_group_members.tmpl +++ b/src/services/api/graphql/session/templates/update_firewall_address_ipv_6_group_members.tmpl diff --git a/src/services/api/graphql/utils/schema_from_op_mode.py b/src/services/api/graphql/utils/schema_from_op_mode.py index d27586747..379d15250 100755 --- a/src/services/api/graphql/utils/schema_from_op_mode.py +++ b/src/services/api/graphql/utils/schema_from_op_mode.py @@ -21,17 +21,21 @@ import os import json import typing -from inspect import signature, getmembers, isfunction +from inspect import signature, getmembers, isfunction, isclass, getmro from jinja2 import Template from vyos.defaults import directories -from . util import load_as_module, is_op_mode_function_name, is_show_function_name +if __package__ is None or __package__ == '': + from util import load_as_module, is_op_mode_function_name, is_show_function_name +else: + from . util import load_as_module, is_op_mode_function_name, is_show_function_name OP_MODE_PATH = directories['op_mode'] SCHEMA_PATH = directories['api_schema'] DATA_DIR = directories['data'] op_mode_include_file = os.path.join(DATA_DIR, 'op-mode-standardized.json') +op_mode_error_schema = 'op_mode_error.graphql' schema_data: dict = {'schema_name': '', 'schema_fields': []} @@ -50,6 +54,7 @@ type {{ schema_name }} { type {{ schema_name }}Result { data: {{ schema_name }} + op_mode_error: OpModeError success: Boolean! errors: [String] } @@ -73,6 +78,7 @@ type {{ schema_name }} { type {{ schema_name }}Result { data: {{ schema_name }} + op_mode_error: OpModeError success: Boolean! errors: [String] } @@ -82,6 +88,21 @@ extend type Mutation { } """ +error_template = """ +interface OpModeError { + name: String! + message: String! + vyos_code: Int! +} +{% for name in error_names %} +type {{ name }} implements OpModeError { + name: String! + message: String! + vyos_code: Int! +} +{%- endfor %} +""" + def _snake_to_pascal_case(name: str) -> str: res = ''.join(map(str.title, name.split('_'))) return res @@ -133,7 +154,30 @@ def create_schema(func_name: str, base_name: str, func: callable) -> str: return res +def create_error_schema(): + from vyos import opmode + + e = Exception + err_types = getmembers(opmode, isclass) + err_types = [k for k in err_types if issubclass(k[1], e)] + # drop base class, to be replaced by interface type. Find the class + # programmatically, in case the base class name changes. + for i in range(len(err_types)): + if err_types[i][1] in getmro(err_types[i-1][1]): + del err_types[i] + break + err_names = [k[0] for k in err_types] + error_data = {'error_names': err_names} + j2_template = Template(error_template) + res = j2_template.render(error_data) + + return res + def generate_op_mode_definitions(): + out = create_error_schema() + with open(f'{SCHEMA_PATH}/{op_mode_error_schema}', 'w') as f: + f.write(out) + with open(op_mode_include_file) as f: op_mode_files = json.load(f) diff --git a/src/services/vyos-http-api-server b/src/services/vyos-http-api-server index af8837e1e..190f3409d 100755 --- a/src/services/vyos-http-api-server +++ b/src/services/vyos-http-api-server @@ -678,6 +678,7 @@ if __name__ == '__main__': server_config = load_server_config() except Exception as err: logger.critical(f"Failed to load the HTTP API server config: {err}") + sys.exit(1) config_session = ConfigSession(os.getpid()) |