-
Notifications
You must be signed in to change notification settings - Fork 169
/
admin.py
441 lines (394 loc) · 14.4 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
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
from __future__ import print_function
import tuned.admin
from tuned.utils.commands import commands
from tuned.profiles import Locator as profiles_locator
from .exceptions import TunedAdminDBusException
from tuned.exceptions import TunedException
import tuned.consts as consts
from tuned.utils.profile_recommender import ProfileRecommender
import os
import sys
import errno
import time
import threading
import logging
class Admin(object):
def __init__(self, dbus = True, debug = False, asynco = False,
timeout = consts.ADMIN_TIMEOUT,
log_level = logging.ERROR):
self._dbus = dbus
self._debug = debug
self._async = asynco
self._timeout = timeout
self._cmd = commands(debug)
self._profiles_locator = profiles_locator(consts.LOAD_DIRECTORIES)
self._daemon_action_finished = threading.Event()
self._daemon_action_profile = ""
self._daemon_action_result = True
self._daemon_action_errstr = ""
self._controller = None
self._log_token = None
self._log_level = log_level
self._profile_recommender = ProfileRecommender()
if self._dbus:
self._controller = tuned.admin.DBusController(consts.DBUS_BUS, consts.DBUS_INTERFACE, consts.DBUS_OBJECT, debug)
try:
self._controller.set_signal_handler(consts.SIGNAL_PROFILE_CHANGED, self._signal_profile_changed_cb)
except TunedAdminDBusException as e:
self._error(e)
self._dbus = False
def _error(self, message):
print(message, file=sys.stderr)
def _signal_profile_changed_cb(self, profile_name, result, errstr):
# ignore successive signals if the signal is not yet processed
if not self._daemon_action_finished.is_set():
self._daemon_action_profile = profile_name
self._daemon_action_result = result
self._daemon_action_errstr = errstr
self._daemon_action_finished.set()
def _tuned_is_running(self):
try:
os.kill(int(self._cmd.read_file(consts.PID_FILE)), 0)
except OSError as e:
return e.errno == errno.EPERM
except (ValueError, IOError) as e:
return False
return True
# run the action specified by the action_name with args
def action(self, action_name, *args, **kwargs):
if action_name is None or action_name == "":
return False
action = None
action_dbus = None
res = False
try:
action_dbus = getattr(self, "_action_dbus_" + action_name)
except AttributeError as e:
self._dbus = False
try:
action = getattr(self, "_action_" + action_name)
except AttributeError as e:
if not self._dbus:
self._error(str(e) + ", action '%s' is not implemented" % action_name)
return False
if self._dbus:
try:
self._controller.set_on_exit_action(
self._log_capture_finish)
self._controller.set_action(action_dbus, *args, **kwargs)
res = self._controller.run()
except TunedAdminDBusException as e:
self._error(e)
self._dbus = False
if not self._dbus:
res = action(*args, **kwargs)
return res
def _print_profiles(self, profile_names):
print("Available profiles:")
for profile in profile_names:
if profile[1] is not None and profile[1] != "":
print(self._cmd.align_str("- %s" % profile[0], 30, "- %s" % profile[1]))
else:
print("- %s" % profile[0])
def _action_dbus_list_profiles(self):
try:
profile_names = self._controller.profiles2()
except TunedAdminDBusException as e:
# fallback to older API
profile_names = [(profile, "") for profile in self._controller.profiles()]
self._print_profiles(profile_names)
self._action_dbus_active()
return self._controller.exit(True)
def _action_list_profiles(self):
self._print_profiles(self._profiles_locator.get_known_names_summary())
self._action_active()
return True
def _dbus_get_active_profile(self):
profile_name = self._controller.active_profile()
if profile_name == "":
profile_name = None
self._controller.exit(True)
return profile_name
def _get_active_profile(self):
profile_name, manual = self._cmd.get_active_profile()
return profile_name
def _get_profile_mode(self):
(profile, manual) = self._cmd.get_active_profile()
if manual is None:
manual = profile is not None
return consts.ACTIVE_PROFILE_MANUAL if manual else consts.ACTIVE_PROFILE_AUTO
def _dbus_get_post_loaded_profile(self):
profile_name = self._controller.post_loaded_profile()
if profile_name == "":
profile_name = None
return profile_name
def _get_post_loaded_profile(self):
profile_name = self._cmd.get_post_loaded_profile()
return profile_name
def _print_profile_info(self, profile, profile_info):
if profile_info[0] == True:
print("Profile name:")
print(profile_info[1])
print()
print("Profile summary:")
print(profile_info[2])
print()
print("Profile description:")
print(profile_info[3])
return True
else:
print("Unable to get information about profile '%s'" % profile)
return False
def _action_dbus_profile_info(self, profile = ""):
if profile == "":
profile = self._dbus_get_active_profile()
if profile:
res = self._print_profile_info(profile, self._controller.profile_info(profile))
else:
print("No current active profile.")
res = False
return self._controller.exit(res)
def _action_profile_info(self, profile = ""):
if profile == "":
try:
profile = self._get_active_profile()
if profile is None:
print("No current active profile.")
return False
except TunedException as e:
self._error(str(e))
return False
return self._print_profile_info(profile, self._profiles_locator.get_profile_attrs(profile, [consts.PROFILE_ATTR_SUMMARY, consts.PROFILE_ATTR_DESCRIPTION], ["", ""]))
def _print_profile_name(self, profile_name):
if profile_name is None:
print("No current active profile.")
return False
else:
print("Current active profile: %s" % profile_name)
return True
def _print_post_loaded_profile(self, profile_name):
if profile_name:
print("Current post-loaded profile: %s" % profile_name)
def _action_dbus_active(self):
active_profile = self._dbus_get_active_profile()
res = self._print_profile_name(active_profile)
if res:
post_loaded_profile = self._dbus_get_post_loaded_profile()
self._print_post_loaded_profile(post_loaded_profile)
return self._controller.exit(res)
def _action_active(self):
try:
profile_name = self._get_active_profile()
post_loaded_profile = self._get_post_loaded_profile()
# The result of the DBus call active_profile includes
# the post-loaded profile, so add it here as well
if post_loaded_profile:
if profile_name:
profile_name += " "
else:
profile_name = ""
profile_name += post_loaded_profile
except TunedException as e:
self._error(str(e))
return False
if profile_name is not None and not self._tuned_is_running():
print("It seems that tuned daemon is not running, preset profile is not activated.")
print("Preset profile: %s" % profile_name)
if post_loaded_profile:
print("Preset post-loaded profile: %s" % post_loaded_profile)
return True
res = self._print_profile_name(profile_name)
self._print_post_loaded_profile(post_loaded_profile)
return res
def _print_profile_mode(self, mode):
print("Profile selection mode: " + mode)
def _action_dbus_profile_mode(self):
mode, error = self._controller.profile_mode()
self._print_profile_mode(mode)
if error != "":
self._error(error)
return self._controller.exit(False)
return self._controller.exit(True)
def _action_profile_mode(self):
try:
mode = self._get_profile_mode()
self._print_profile_mode(mode)
return True
except TunedException as e:
self._error(str(e))
return False
def _profile_print_status(self, ret, msg):
if ret:
if not self._controller.is_running() and not self._controller.start():
self._error("Cannot enable the tuning.")
ret = False
else:
self._error(msg)
return ret
def _action_dbus_wait_profile(self, profile_name):
if time.time() >= self._timestamp + self._timeout:
print("Operation timed out after waiting %d seconds(s), you may try to increase timeout by using --timeout command line option or using --async." % self._timeout)
return self._controller.exit(False)
if self._daemon_action_finished.isSet():
if self._daemon_action_profile == profile_name:
if not self._daemon_action_result:
print("Error changing profile: %s" % self._daemon_action_errstr)
return self._controller.exit(False)
return self._controller.exit(True)
return False
def _log_capture_finish(self):
if self._log_token is None or self._log_token == "":
return
try:
log_msgs = self._controller.log_capture_finish(
self._log_token)
self._log_token = None
print(log_msgs, end = "", file = sys.stderr)
sys.stderr.flush()
except TunedAdminDBusException as e:
self._error("Error: Failed to stop log capture. Restart the TuneD daemon to prevent a memory leak.")
def _action_dbus_profile(self, profiles):
if len(profiles) == 0:
return self._action_dbus_list()
profile_name = " ".join(profiles)
if profile_name == "":
return self._controller.exit(False)
self._daemon_action_finished.clear()
if not self._async and self._log_level is not None:
# 25 seconds default DBus timeout + 5 secs safety margin
timeout = self._timeout + 25 + 5
self._log_token = self._controller.log_capture_start(
self._log_level, timeout)
(ret, msg) = self._controller.switch_profile(profile_name)
if self._async or not ret:
return self._controller.exit(self._profile_print_status(ret, msg))
else:
self._timestamp = time.time()
self._controller.set_action(self._action_dbus_wait_profile, profile_name)
return self._profile_print_status(ret, msg)
def _restart_tuned(self):
print("Trying to (re)start tuned...")
(ret, msg) = self._cmd.execute(["service", "tuned", "restart"])
if ret == 0:
print("TuneD (re)started, changes applied.")
else:
print("TuneD (re)start failed, you need to (re)start TuneD by hand for changes to apply.")
def _set_profile(self, profile_name, manual):
if profile_name in self._profiles_locator.get_known_names():
try:
self._cmd.save_active_profile(profile_name, manual)
self._restart_tuned()
return True
except TunedException as e:
self._error(str(e))
self._error("Unable to switch profile.")
return False
else:
self._error("Requested profile '%s' doesn't exist." % profile_name)
return False
def _action_profile(self, profiles):
if len(profiles) == 0:
return self._action_list_profiles()
profile_name = " ".join(profiles)
if profile_name == "":
return False
return self._set_profile(profile_name, True)
def _action_dbus_auto_profile(self):
profile_name = self._controller.recommend_profile()
self._daemon_action_finished.clear()
if not self._async and self._log_level is not None:
# 25 seconds default DBus timeout + 5 secs safety margin
timeout = self._timeout + 25 + 5
self._log_token = self._controller.log_capture_start(
self._log_level, timeout)
(ret, msg) = self._controller.auto_profile()
if self._async or not ret:
return self._controller.exit(self._profile_print_status(ret, msg))
else:
self._timestamp = time.time()
self._controller.set_action(self._action_dbus_wait_profile, profile_name)
return self._profile_print_status(ret, msg)
def _action_auto_profile(self):
profile_name = self._profile_recommender.recommend()
return self._set_profile(profile_name, False)
def _action_dbus_recommend_profile(self):
print(self._controller.recommend_profile())
return self._controller.exit(True)
def _action_recommend_profile(self):
print(self._profile_recommender.recommend())
return True
def _action_dbus_verify_profile(self, ignore_missing):
if ignore_missing:
ret = self._controller.verify_profile_ignore_missing()
else:
ret = self._controller.verify_profile()
if ret:
print("Verfication succeeded, current system settings match the preset profile.")
else:
print("Verification failed, current system settings differ from the preset profile.")
print("You can mostly fix this by restarting the TuneD daemon, e.g.:")
print(" systemctl restart tuned")
print("or")
print(" service tuned restart")
print("Sometimes (if some plugins like bootloader are used) a reboot may be required.")
print("See TuneD log file ('%s') for details." % consts.LOG_FILE)
return self._controller.exit(ret)
def _action_verify_profile(self, ignore_missing):
print("Not supported in no_daemon mode.")
return False
def _action_dbus_off(self):
# 25 seconds default DBus timeout + 5 secs safety margin
timeout = 25 + 5
self._log_token = self._controller.log_capture_start(
self._log_level, timeout)
ret = self._controller.off()
if not ret:
self._error("Cannot disable active profile.")
return self._controller.exit(ret)
def _action_off(self):
print("Not supported in no_daemon mode.")
return False
def _action_dbus_list(self, list_choice="profiles", verbose=False):
"""Print accessible profiles or plugins got from TuneD dbus api
Keyword arguments:
list_choice -- argument from command line deciding what will be listed
verbose -- if True then list plugin's config options and their hints
if possible. Functional only with plugin listing, with profiles
this argument is omitted
"""
if list_choice == "profiles":
return self._action_dbus_list_profiles()
elif list_choice == "plugins":
return self._action_dbus_list_plugins(verbose=verbose)
def _action_list(self, list_choice="profiles", verbose=False):
"""Print accessible profiles or plugins with no daemon mode
Keyword arguments:
list_choice -- argument from command line deciding what will be listed
verbose -- Plugins cannot be listed in this mode, so verbose argument
is here only because argparse module always supplies verbose
option and if verbose was not here it would result in error
"""
if list_choice == "profiles":
return self._action_list_profiles()
elif list_choice == "plugins":
return self._action_list_plugins(verbose=verbose)
def _action_dbus_list_plugins(self, verbose=False):
"""Print accessible plugins
Keyword arguments:
verbose -- if is set to True then parameters and hints are printed
"""
plugins = self._controller.get_plugins()
for plugin in plugins.keys():
print(plugin)
if not verbose or len(plugins[plugin]) == 0:
continue
hints = self._controller.get_plugin_hints(plugin)
for parameter in plugins[plugin]:
print("\t%s" %(parameter))
hint = hints.get(parameter, None)
if hint:
print("\t\t%s" %(hint))
return self._controller.exit(True)
def _action_list_plugins(self, verbose=False):
print("Not supported in no_daemon mode.")
return False