-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathadmin.py
247 lines (192 loc) · 9.8 KB
/
admin.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
"""
The admin module holds the CherryPy server side implementation of the admin interface.
Note that most of its initialisation happens in the broker init script, ../broker.py and this is added by
/hooks.py
Created on Jun 22, 2016
@author: Nicklas Boerjesson
"""
import copy
import os
import threading
import cherrypy
import json
from of.broker.cherrypy_api.node import aop_check_session
from of.schemas.constants import id_right_admin_everything
from of.common.security.groups import aop_has_right
from of.common.logging import write_to_log, EC_SERVICE, SEV_DEBUG, EC_NOTIFICATION, SEV_ERROR
from of.common.messaging.utils import get_environment_data
class CherryPyAdmin(object):
"""
The CherryPyAdmin class is a plugin for CherryPy that shows the admin UI for the Optimal Framework
"""
#: Plugin management
plugins = None
#: A reference to the stop broker function in the main thread
stop_broker = None
#: A reference to root object (broker)
root = None
#: A init string for the client
admin_ui_init = None
#: A init string for SystemJS
admin_systemjs_init = None
#: The name of the application
application_name = None
def __init__(self, _process_id, _address, _stop_broker, _root_object,
_plugins, _web_config):
write_to_log(_category=EC_SERVICE, _severity=SEV_DEBUG, _process_id=_process_id,
_data="Initializing administrative REST API.")
self.stop_broker = _stop_broker
self.process_id = _process_id
self.address = _address
self.root = _root_object
self.plugins = _plugins
# Mount the static content at a mount point /admin
_web_config.update({
"/admin" : {
"tools.staticdir.on": True,
"tools.staticdir.dir": os.path.join(os.path.dirname(__file__), "../ui"),
"tools.trailing_slash.on": True
}
})
self.refresh_static(_web_config=_web_config)
def broker_ctrl(self, _command, _reason, _user):
"""
Controls the broker's running state
:param _command: Can be "stop" or "restart".
:param _user: A user instance
"""
write_to_log("broker.broker_control: Got the command " + str(_command), _category=EC_SERVICE,
_process_id=self.process_id)
# TODO: There should be a log item written with reason and userid.(PROD-32)
# TODO: UserId should also be appended to reason below.(PROD-32)
def _command_local(_local_command):
if _local_command == "restart":
self.stop_broker(_reason=_reason, _restart=True)
if _local_command == "stop":
self.stop_broker(_reason=_reason, _restart=False)
_restart_thread = threading.Thread(target=_command_local, args=[_command])
_restart_thread.start()
return {}
@cherrypy.expose
@cherrypy.tools.json_in()
@cherrypy.tools.json_out(content_type='application/json')
@aop_check_session
@aop_has_right([id_right_admin_everything])
def broker_control(self, **kwargs):
return self.broker_ctrl(cherrypy.request.json["command"],
cherrypy.request.json["reason"],
kwargs["_user"])
@cherrypy.expose
@cherrypy.tools.json_out(content_type='application/json')
@aop_check_session
@aop_has_right([id_right_admin_everything])
def get_peers(self, **kwargs):
"""
Returns a list of all logged in peers
:param kwargs: Unused here so far, but injected by get session
:return: A list of all logged in peers
"""
# TODO: This should probably not be in admin. However listing peers does seems slightly administrative.
_result = []
# Filter out the unserializable web socket
for _session in self.root.peers.values():
_new_session = copy.copy(_session)
_new_session["web_socket"] = "removed for serialization"
_new_session["queue"] = "removed for serialization"
_result.append(_new_session)
write_to_log(_process_id=self.process_id, _category=EC_NOTIFICATION, _severity=SEV_DEBUG,
_data="Returning a list of peers:" + str(_result))
return _result
def refresh_hooks(self):
# Gather all hooks
_hooks = []
for _curr_plugin_key, _curr_plugin in self.plugins.items():
if "admin-ui" in _curr_plugin:
_curr_admin_info = _curr_plugin["admin-ui"]
if "ui-hooks" in _curr_admin_info:
_hooks+=_curr_plugin["admin-ui"]["ui-hooks"]
# TODO: Add check to see what hooks are actually implemented in each plugin by parsing hooks.ts? Simple search check or AST?
# TODO: At least add check to see if a plugin actually have a hooks.ts? No reason to get errors in the UI.
_presentation = "// This file is generated by the OF backend and presented as /admin/hooks\n// Defined hooks:\n"
for _hook in _hooks:
_presentation+="// " + _hook["name"] + ": " + _hook["description"] + "\n"
# Generate the imports
_imports = ""
_hook_defaults = ["pluginStructure", "pluginRoutes", "pluginMenus"]
for _curr_plugin_key, _curr_plugin in self.plugins.items():
for _hook in _hook_defaults:
_hook_alias = _curr_plugin_key + "_" + _hook
_imports+="import {" + _hook +" as " + _hook_alias + "} from '" + _curr_plugin["admin-ui"]["mountpoint"] + "/hooks';\n"
# Join the arrays
_hook_defs=""
for _hook in _hook_defaults:
_curr_hook_def = "export const " + _hook + ": any[] = [];\n"
for _curr_plugin_key, _curr_plugin in self.plugins.items():
_hook_alias = _curr_plugin_key + "_" + _hook
_curr_hook_def += _hook +".push(..." + _hook_alias + ");\n"
_hook_defs+=_curr_hook_def + "\n\n"
for _curr_plugin_key, _curr_plugin in self.plugins.items():
for _hook in _hooks:
_hook_alias = _curr_plugin_key + "_" + _hook["name"]
_imports+="import {" + _hook["name"] +" as " + _hook_alias + "} from '" + _curr_plugin["admin-ui"]["mountpoint"] + "/hooks';\n"
# Generate the hook calls
for _hook in _hooks:
_param_list = ", ".join(_hook["parameters"])
_curr_hook_def = "export function hook_" + _hook["name"] + "(" + _param_list + "){\n"
for _curr_plugin_key, _curr_plugin in self.plugins.items():
_hook_alias = _curr_plugin_key + "_" + _hook["name"]
_curr_hook_def += " " + _hook_alias + "(" + _param_list + ");\n"
_hook_defs+=_curr_hook_def + "\n}\n"
return _presentation + str(_imports) + "\n" + _hook_defs
def refresh_static(self, _web_config):
"""
This function regenerates all the static content that is used to initialize the user interface
plugins.
:param _web_config: An instance of the CherryPy web configuration
"""
self.admin_ui_hooks = self.refresh_hooks()
_imports = ""
_systemjs = ""
# has_right(id_right_admin_everything, kwargs["user"])
for _curr_plugin_key, _curr_plugin_info in self.plugins.items():
# Add any plugin configuration for the Admin user interface
if "admin-ui" in _curr_plugin_info:
_curr_ui_def = _curr_plugin_info["admin-ui"]
if "mountpoint" not in _curr_ui_def:
write_to_log("Error loading admin-ui for " + _curr_plugin_key + " no mount point.",
_category=EC_SERVICE, _severity=SEV_ERROR)
continue
_mount_point = _curr_ui_def["mountpoint"]
if _mount_point[0] == "/":
write_to_log(
"Not mounting " + _mount_point + ", cannot mount admin-specific ui under root(root can "
"never depend on admin), use root-ui instead.",
_category=EC_SERVICE, _severity=SEV_ERROR)
continue
# Mount the static content at a mount point somewhere under /admin
_web_config.update({
"/admin/" + _mount_point: {
"tools.staticdir.on": True,
"tools.staticdir.dir": os.path.join(_curr_plugin_info["baseDirectoryName"], _mount_point),
"tools.trailing_slash.on": True
}
})
_systemjs += "System.config({\"packages\": {\"" + _mount_point + "\": {\"defaultExtension\": \"ts\", \"meta\": {\"*.ts\": {\"loader\": \"ts\"}}}}});\n"
self.admin_systemjs_init = _systemjs
@cherrypy.expose(alias="hook_wrapper.ts")
def hook_wrapper(self, **kwargs):
return self.admin_ui_hooks
@cherrypy.expose(alias="admin_jspm_config.js")
def systemjs(self, **kwargs):
return self.admin_systemjs_init
@cherrypy.expose
@cherrypy.tools.json_out(content_type='application/json')
@aop_check_session
@aop_has_right([id_right_admin_everything])
def get_broker_environment(self, **kwargs):
write_to_log(_process_id=self.process_id, _category=EC_NOTIFICATION, _severity=SEV_DEBUG, _data="Request for broker information")
return get_environment_data()
@cherrypy.expose
@cherrypy.tools.json_out(content_type='application/json')
def get_application_name(self):
return {"applicationName": self.application_name}