summaryrefslogtreecommitdiff
path: root/python/vyos/opmode.py
diff options
context:
space:
mode:
Diffstat (limited to 'python/vyos/opmode.py')
-rw-r--r--python/vyos/opmode.py71
1 files changed, 60 insertions, 11 deletions
diff --git a/python/vyos/opmode.py b/python/vyos/opmode.py
index 5ff768859..d7172a0b5 100644
--- a/python/vyos/opmode.py
+++ b/python/vyos/opmode.py
@@ -1,4 +1,4 @@
-# Copyright 2022 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2022-2023 VyOS maintainers and contributors <maintainers@vyos.io>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -22,6 +22,10 @@ from humps import decamelize
class Error(Exception):
""" Any error that makes requested operation impossible to complete
for reasons unrelated to the user input or script logic.
+
+ This is the base class, scripts should not use it directly
+ and should raise more specific errors instead,
+ whenever possible.
"""
pass
@@ -45,6 +49,17 @@ class PermissionDenied(Error):
"""
pass
+class InsufficientResources(Error):
+ """ Requested operation and its arguments are valid but the system
+ does not have enough resources (such as drive space or memory)
+ to complete it.
+ """
+ pass
+
+class UnsupportedOperation(Error):
+ """ Requested operation is technically valid but is not implemented yet. """
+ pass
+
class IncorrectValue(Error):
""" Requested operation is valid, but an argument provided has an
incorrect value, preventing successful completion.
@@ -66,13 +81,13 @@ class InternalError(Error):
def _is_op_mode_function_name(name):
- if re.match(r"^(show|clear|reset|restart)", name):
+ if re.match(r"^(show|clear|reset|restart|add|delete|generate|set)", name):
return True
else:
return False
-def _is_show(name):
- if re.match(r"^show", name):
+def _capture_output(name):
+ if re.match(r"^(show|generate)", name):
return True
else:
return False
@@ -113,6 +128,25 @@ def _get_arg_type(t):
else:
return t
+def _is_literal_type(t):
+ if _is_optional_type(t):
+ t = _get_arg_type(t)
+
+ if typing.get_origin(t) == typing.Literal:
+ return True
+
+ return False
+
+def _get_literal_values(t):
+ """ Returns the tuple of allowed values for a Literal type
+ """
+ if not _is_literal_type(t):
+ return tuple()
+ if _is_optional_type(t):
+ t = _get_arg_type(t)
+
+ return typing.get_args(t)
+
def _normalize_field_name(name):
# Convert the name to string if it is not
# (in some cases they may be numbers)
@@ -179,9 +213,21 @@ def run(module):
subparser.add_argument(f"--{opt}", action='store_true')
else:
if _is_optional_type(th):
- subparser.add_argument(f"--{opt}", type=_get_arg_type(th), default=None)
+ if _is_literal_type(th):
+ subparser.add_argument(f"--{opt}",
+ choices=list(_get_literal_values(th)),
+ default=None)
+ else:
+ subparser.add_argument(f"--{opt}",
+ type=_get_arg_type(th), default=None)
else:
- subparser.add_argument(f"--{opt}", type=_get_arg_type(th), required=True)
+ if _is_literal_type(th):
+ subparser.add_argument(f"--{opt}",
+ choices=list(_get_literal_values(th)),
+ required=True)
+ else:
+ subparser.add_argument(f"--{opt}",
+ type=_get_arg_type(th), required=True)
# Get options as a dict rather than a namespace,
# so that we can modify it and pack for passing to functions
@@ -199,20 +245,23 @@ def run(module):
# it would cause an extra argument error when we pass the dict to a function
del args["subcommand"]
- # Show commands must always get the "raw" argument,
- # but other commands (clear/reset/restart) should not,
+ # Show and generate commands must always get the "raw" argument,
+ # but other commands (clear/reset/restart/add/delete) should not,
# because they produce no output and it makes no sense for them.
- if ("raw" not in args) and _is_show(function_name):
+ if ("raw" not in args) and _capture_output(function_name):
args["raw"] = False
- if re.match(r"^show", function_name):
- # Show commands are slightly special:
+ if _capture_output(function_name):
+ # Show and generate commands are slightly special:
# they may return human-formatted output
# or a raw dict that we need to serialize in JSON for printing
res = func(**args)
if not args["raw"]:
return res
else:
+ if not isinstance(res, dict) and not isinstance(res, list):
+ raise InternalError(f"Bare literal is not an acceptable raw output, must be a list or an object.\
+ The output was:{res}")
res = decamelize(res)
res = _normalize_field_names(res)
from json import dumps