summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDaniil Baturin <daniil@vyos.io>2022-07-25 10:34:17 +0100
committerGitHub <noreply@github.com>2022-07-25 10:34:17 +0100
commit3337aedd5f7ff48bfad051d760023a188fdace70 (patch)
tree3dc5b5c22444430a74f8d0f8a8ed54415a8ccd47 /src
parente1e9f690d3eb4cd03aba118558fffd5b6b2920c8 (diff)
parentf9d6f089014007193996e51757f72a8bf7ec78b9 (diff)
downloadvyos-1x-3337aedd5f7ff48bfad051d760023a188fdace70.tar.gz
vyos-1x-3337aedd5f7ff48bfad051d760023a188fdace70.zip
Merge pull request #1431 from jestabro/gql-dev
graphql: T4567: Merge experimental branch of GraphQL development
Diffstat (limited to 'src')
-rwxr-xr-xsrc/conf_mode/http-api.py8
-rw-r--r--src/services/api/graphql/graphql/directives.py9
-rw-r--r--src/services/api/graphql/graphql/mutations.py10
-rw-r--r--src/services/api/graphql/graphql/queries.py14
-rw-r--r--src/services/api/graphql/graphql/schema/config_file.graphql2
-rw-r--r--src/services/api/graphql/graphql/schema/dhcp_server.graphql1
-rw-r--r--src/services/api/graphql/graphql/schema/firewall_group.graphql6
-rw-r--r--src/services/api/graphql/graphql/schema/image.graphql2
-rw-r--r--src/services/api/graphql/graphql/schema/interface_ethernet.graphql1
-rw-r--r--src/services/api/graphql/graphql/schema/schema.graphql4
-rw-r--r--src/services/api/graphql/graphql/schema/show.graphql1
-rw-r--r--src/services/api/graphql/graphql/schema/show_config.graphql2
-rw-r--r--src/services/api/graphql/graphql/schema/system_status.graphql18
-rw-r--r--src/services/api/graphql/key_auth.py18
-rwxr-xr-xsrc/services/api/graphql/recipes/queries/system_status.py45
-rw-r--r--src/services/api/graphql/recipes/session.py14
-rwxr-xr-xsrc/services/vyos-http-api-server7
17 files changed, 153 insertions, 9 deletions
diff --git a/src/conf_mode/http-api.py b/src/conf_mode/http-api.py
index 4a7906c17..04113fc09 100755
--- a/src/conf_mode/http-api.py
+++ b/src/conf_mode/http-api.py
@@ -66,14 +66,10 @@ def get_config(config=None):
if conf.exists('debug'):
http_api['debug'] = True
- # this node is not available by CLI by default, and is reserved for
- # the graphql tools. One can enable it for testing, with the warning
- # that this will open an unauthenticated server. To do so
- # mkdir /opt/vyatta/share/vyatta-cfg/templates/service/https/api/gql
- # touch /opt/vyatta/share/vyatta-cfg/templates/service/https/api/gql/node.def
- # and configure; editing the config alone is insufficient.
if conf.exists('gql'):
http_api['gql'] = True
+ if conf.exists('gql introspection'):
+ http_api['introspection'] = True
if conf.exists('socket'):
http_api['socket'] = True
diff --git a/src/services/api/graphql/graphql/directives.py b/src/services/api/graphql/graphql/directives.py
index 0a9298f55..551d28831 100644
--- a/src/services/api/graphql/graphql/directives.py
+++ b/src/services/api/graphql/graphql/directives.py
@@ -48,6 +48,14 @@ class ShowConfigDirective(VyosDirective):
super().visit_field_definition(field, object_type,
make_resolver=make_show_config_resolver)
+class SystemStatusDirective(VyosDirective):
+ """
+ Class providing implementation of 'system_status' directive in schema.
+ """
+ def visit_field_definition(self, field, object_type):
+ super().visit_field_definition(field, object_type,
+ make_resolver=make_system_status_resolver)
+
class ConfigFileDirective(VyosDirective):
"""
Class providing implementation of 'configfile' directive in schema.
@@ -74,6 +82,7 @@ class ImageDirective(VyosDirective):
directives_dict = {"configure": ConfigureDirective,
"showconfig": ShowConfigDirective,
+ "systemstatus": SystemStatusDirective,
"configfile": ConfigFileDirective,
"show": ShowDirective,
"image": ImageDirective}
diff --git a/src/services/api/graphql/graphql/mutations.py b/src/services/api/graphql/graphql/mutations.py
index 0c3eb702a..93e046319 100644
--- a/src/services/api/graphql/graphql/mutations.py
+++ b/src/services/api/graphql/graphql/mutations.py
@@ -20,6 +20,7 @@ from graphql import GraphQLResolveInfo
from makefun import with_signature
from .. import state
+from .. import key_auth
from api.graphql.recipes.session import Session
mutation = ObjectType("Mutation")
@@ -53,6 +54,15 @@ def make_mutation_resolver(mutation_name, class_name, session_func):
}
data = kwargs['data']
+ key = data['key']
+
+ auth = key_auth.auth_required(key)
+ if auth is None:
+ return {
+ "success": False,
+ "errors": ['invalid API key']
+ }
+
session = state.settings['app'].state.vyos_session
# one may override the session functions with a local subclass
diff --git a/src/services/api/graphql/graphql/queries.py b/src/services/api/graphql/graphql/queries.py
index e1868091e..eeaa9e19c 100644
--- a/src/services/api/graphql/graphql/queries.py
+++ b/src/services/api/graphql/graphql/queries.py
@@ -20,6 +20,7 @@ from graphql import GraphQLResolveInfo
from makefun import with_signature
from .. import state
+from .. import key_auth
from api.graphql.recipes.session import Session
query = ObjectType("Query")
@@ -53,6 +54,15 @@ def make_query_resolver(query_name, class_name, session_func):
}
data = kwargs['data']
+ key = data['key']
+
+ auth = key_auth.auth_required(key)
+ if auth is None:
+ return {
+ "success": False,
+ "errors": ['invalid API key']
+ }
+
session = state.settings['app'].state.vyos_session
# one may override the session functions with a local subclass
@@ -84,6 +94,10 @@ def make_show_config_resolver(query_name):
class_name = query_name
return make_query_resolver(query_name, class_name, 'show_config')
+def make_system_status_resolver(query_name):
+ class_name = query_name
+ return make_query_resolver(query_name, class_name, 'system_status')
+
def make_show_resolver(query_name):
class_name = query_name
return make_query_resolver(query_name, class_name, 'show')
diff --git a/src/services/api/graphql/graphql/schema/config_file.graphql b/src/services/api/graphql/graphql/schema/config_file.graphql
index 31ab26b9e..a7263114b 100644
--- a/src/services/api/graphql/graphql/schema/config_file.graphql
+++ b/src/services/api/graphql/graphql/schema/config_file.graphql
@@ -1,4 +1,5 @@
input SaveConfigFileInput {
+ key: String!
fileName: String
}
@@ -13,6 +14,7 @@ type SaveConfigFileResult {
}
input LoadConfigFileInput {
+ key: String!
fileName: String!
}
diff --git a/src/services/api/graphql/graphql/schema/dhcp_server.graphql b/src/services/api/graphql/graphql/schema/dhcp_server.graphql
index 25f091bfa..345c349ac 100644
--- a/src/services/api/graphql/graphql/schema/dhcp_server.graphql
+++ b/src/services/api/graphql/graphql/schema/dhcp_server.graphql
@@ -1,4 +1,5 @@
input DhcpServerConfigInput {
+ key: String!
sharedNetworkName: String
subnet: String
defaultRouter: String
diff --git a/src/services/api/graphql/graphql/schema/firewall_group.graphql b/src/services/api/graphql/graphql/schema/firewall_group.graphql
index d89904b9e..9454d2997 100644
--- a/src/services/api/graphql/graphql/schema/firewall_group.graphql
+++ b/src/services/api/graphql/graphql/schema/firewall_group.graphql
@@ -1,4 +1,5 @@
input CreateFirewallAddressGroupInput {
+ key: String!
name: String!
address: [String]
}
@@ -15,6 +16,7 @@ type CreateFirewallAddressGroupResult {
}
input UpdateFirewallAddressGroupMembersInput {
+ key: String!
name: String!
address: [String!]!
}
@@ -31,6 +33,7 @@ type UpdateFirewallAddressGroupMembersResult {
}
input RemoveFirewallAddressGroupMembersInput {
+ key: String!
name: String!
address: [String!]!
}
@@ -47,6 +50,7 @@ type RemoveFirewallAddressGroupMembersResult {
}
input CreateFirewallAddressIpv6GroupInput {
+ key: String!
name: String!
address: [String]
}
@@ -63,6 +67,7 @@ type CreateFirewallAddressIpv6GroupResult {
}
input UpdateFirewallAddressIpv6GroupMembersInput {
+ key: String!
name: String!
address: [String!]!
}
@@ -79,6 +84,7 @@ type UpdateFirewallAddressIpv6GroupMembersResult {
}
input RemoveFirewallAddressIpv6GroupMembersInput {
+ key: String!
name: String!
address: [String!]!
}
diff --git a/src/services/api/graphql/graphql/schema/image.graphql b/src/services/api/graphql/graphql/schema/image.graphql
index 7d1b4f9d0..485033875 100644
--- a/src/services/api/graphql/graphql/schema/image.graphql
+++ b/src/services/api/graphql/graphql/schema/image.graphql
@@ -1,4 +1,5 @@
input AddSystemImageInput {
+ key: String!
location: String!
}
@@ -14,6 +15,7 @@ type AddSystemImageResult {
}
input DeleteSystemImageInput {
+ key: String!
name: String!
}
diff --git a/src/services/api/graphql/graphql/schema/interface_ethernet.graphql b/src/services/api/graphql/graphql/schema/interface_ethernet.graphql
index 32438b315..8a17d919f 100644
--- a/src/services/api/graphql/graphql/schema/interface_ethernet.graphql
+++ b/src/services/api/graphql/graphql/schema/interface_ethernet.graphql
@@ -1,4 +1,5 @@
input InterfaceEthernetConfigInput {
+ key: String!
interface: String
address: String
replace: Boolean = true
diff --git a/src/services/api/graphql/graphql/schema/schema.graphql b/src/services/api/graphql/graphql/schema/schema.graphql
index 952e46f34..8ae71f632 100644
--- a/src/services/api/graphql/graphql/schema/schema.graphql
+++ b/src/services/api/graphql/graphql/schema/schema.graphql
@@ -7,11 +7,15 @@ directive @configure on FIELD_DEFINITION
directive @configfile on FIELD_DEFINITION
directive @show on FIELD_DEFINITION
directive @showconfig on FIELD_DEFINITION
+directive @systemstatus on FIELD_DEFINITION
directive @image on FIELD_DEFINITION
+scalar Generic
+
type Query {
Show(data: ShowInput) : ShowResult @show
ShowConfig(data: ShowConfigInput) : ShowConfigResult @showconfig
+ SystemStatus(data: SystemStatusInput) : SystemStatusResult @systemstatus
}
type Mutation {
diff --git a/src/services/api/graphql/graphql/schema/show.graphql b/src/services/api/graphql/graphql/schema/show.graphql
index c7709e48b..278ed536b 100644
--- a/src/services/api/graphql/graphql/schema/show.graphql
+++ b/src/services/api/graphql/graphql/schema/show.graphql
@@ -1,4 +1,5 @@
input ShowInput {
+ key: String!
path: [String!]!
}
diff --git a/src/services/api/graphql/graphql/schema/show_config.graphql b/src/services/api/graphql/graphql/schema/show_config.graphql
index 34afd2aa9..5a1fe43da 100644
--- a/src/services/api/graphql/graphql/schema/show_config.graphql
+++ b/src/services/api/graphql/graphql/schema/show_config.graphql
@@ -2,9 +2,9 @@
Use 'scalar Generic' for show config output, to avoid attempts to
JSON-serialize in case of JSON output.
"""
-scalar Generic
input ShowConfigInput {
+ key: String!
path: [String!]!
configFormat: String
}
diff --git a/src/services/api/graphql/graphql/schema/system_status.graphql b/src/services/api/graphql/graphql/schema/system_status.graphql
new file mode 100644
index 000000000..be8d87535
--- /dev/null
+++ b/src/services/api/graphql/graphql/schema/system_status.graphql
@@ -0,0 +1,18 @@
+"""
+Use 'scalar Generic' for system status output, to avoid attempts to
+JSON-serialize in case of JSON output.
+"""
+
+input SystemStatusInput {
+ key: String!
+}
+
+type SystemStatus {
+ result: Generic
+}
+
+type SystemStatusResult {
+ data: SystemStatus
+ success: Boolean!
+ errors: [String]
+}
diff --git a/src/services/api/graphql/key_auth.py b/src/services/api/graphql/key_auth.py
new file mode 100644
index 000000000..f756ed6d8
--- /dev/null
+++ b/src/services/api/graphql/key_auth.py
@@ -0,0 +1,18 @@
+
+from . import state
+
+def check_auth(key_list, key):
+ if not key_list:
+ return None
+ key_id = None
+ for k in key_list:
+ if k['key'] == key:
+ key_id = k['id']
+ return key_id
+
+def auth_required(key):
+ api_keys = None
+ api_keys = state.settings['app'].state.vyos_keys
+ key_id = check_auth(api_keys, key)
+ state.settings['app'].state.vyos_id = key_id
+ return key_id
diff --git a/src/services/api/graphql/recipes/queries/system_status.py b/src/services/api/graphql/recipes/queries/system_status.py
new file mode 100755
index 000000000..00c137443
--- /dev/null
+++ b/src/services/api/graphql/recipes/queries/system_status.py
@@ -0,0 +1,45 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+import os
+import sys
+import json
+import importlib.util
+
+from vyos.defaults import directories
+
+OP_PATH = directories['op_mode']
+
+def load_as_module(name: str):
+ path = os.path.join(OP_PATH, name)
+ spec = importlib.util.spec_from_file_location(name, path)
+ mod = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(mod)
+ return mod
+
+def get_system_version() -> dict:
+ show_version = load_as_module('version.py')
+ return show_version.show(raw=True, funny=False)
+
+def get_system_uptime() -> dict:
+ show_uptime = load_as_module('show_uptime.py')
+ return show_uptime.get_raw_data()
+
+def get_system_ram_usage() -> dict:
+ show_ram = load_as_module('memory.py')
+ return show_ram.show(raw=True)
diff --git a/src/services/api/graphql/recipes/session.py b/src/services/api/graphql/recipes/session.py
index 1f844ff70..c436de08a 100644
--- a/src/services/api/graphql/recipes/session.py
+++ b/src/services/api/graphql/recipes/session.py
@@ -136,3 +136,17 @@ class Session:
raise error
return res
+
+ def system_status(self):
+ import api.graphql.recipes.queries.system_status as system_status
+
+ session = self._session
+ data = self._data
+
+ status = {}
+ status['host_name'] = session.show(['host', 'name']).strip()
+ status['version'] = system_status.get_system_version()
+ status['uptime'] = system_status.get_system_uptime()
+ status['ram'] = system_status.get_system_ram_usage()
+
+ return status
diff --git a/src/services/vyos-http-api-server b/src/services/vyos-http-api-server
index e9b904ba8..af8837e1e 100755
--- a/src/services/vyos-http-api-server
+++ b/src/services/vyos-http-api-server
@@ -654,11 +654,13 @@ def graphql_init(fast_api_app):
schema = generate_schema()
+ in_spec = app.state.vyos_introspection
+
if app.state.vyos_origins:
origins = app.state.vyos_origins
- app.add_route('/graphql', CORSMiddleware(GraphQL(schema, debug=True), allow_origins=origins, allow_methods=("GET", "POST", "OPTIONS")))
+ app.add_route('/graphql', CORSMiddleware(GraphQL(schema, debug=True, introspection=in_spec), allow_origins=origins, allow_methods=("GET", "POST", "OPTIONS")))
else:
- app.add_route('/graphql', GraphQL(schema, debug=True))
+ app.add_route('/graphql', GraphQL(schema, debug=True, introspection=in_spec))
###
@@ -684,6 +686,7 @@ if __name__ == '__main__':
app.state.vyos_debug = server_config['debug']
app.state.vyos_gql = server_config['gql']
+ app.state.vyos_introspection = server_config['introspection']
app.state.vyos_strict = server_config['strict']
app.state.vyos_origins = server_config.get('cors', {}).get('origins', [])