1 | # -*- coding: utf-8 -*- |
---|
2 | # |
---|
3 | # Copyright (c) 2014 Virtual Cable S.L. |
---|
4 | # All rights reserved. |
---|
5 | # |
---|
6 | # Redistribution and use in source and binary forms, with or without modification, |
---|
7 | # are permitted provided that the following conditions are met: |
---|
8 | # |
---|
9 | # * Redistributions of source code must retain the above copyright notice, |
---|
10 | # this list of conditions and the following disclaimer. |
---|
11 | # * Redistributions in binary form must reproduce the above copyright notice, |
---|
12 | # this list of conditions and the following disclaimer in the documentation |
---|
13 | # and/or other materials provided with the distribution. |
---|
14 | # * Neither the name of Virtual Cable S.L. nor the names of its contributors |
---|
15 | # may be used to endorse or promote products derived from this software |
---|
16 | # without specific prior written permission. |
---|
17 | # |
---|
18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
---|
19 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
---|
20 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
---|
21 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
---|
22 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
---|
23 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
---|
24 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
---|
25 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
---|
26 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
---|
27 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
---|
28 | """ |
---|
29 | @author: Ramón M. Gómez, ramongomez at us dot es |
---|
30 | """ |
---|
31 | from __future__ import unicode_literals |
---|
32 | |
---|
33 | import os |
---|
34 | import random |
---|
35 | import shutil |
---|
36 | import signal |
---|
37 | import string |
---|
38 | import subprocess |
---|
39 | import threading |
---|
40 | import time |
---|
41 | import urllib |
---|
42 | |
---|
43 | from opengnsys import REST |
---|
44 | from opengnsys import operations |
---|
45 | from opengnsys.log import logger |
---|
46 | from opengnsys.workers import ServerWorker |
---|
47 | from six.moves.urllib import parse |
---|
48 | |
---|
49 | |
---|
50 | # Check authorization header decorator |
---|
51 | def check_secret(fnc): |
---|
52 | """ |
---|
53 | Decorator to check for received secret key and raise exception if it isn't valid. |
---|
54 | """ |
---|
55 | |
---|
56 | def wrapper(*args, **kwargs): |
---|
57 | try: |
---|
58 | this, path, get_params, post_params, server = args # @UnusedVariable |
---|
59 | if this.random == server.headers['Authorization']: |
---|
60 | fnc(*args, **kwargs) |
---|
61 | else: |
---|
62 | raise Exception('Unauthorized operation') |
---|
63 | except Exception as e: |
---|
64 | logger.error(e) |
---|
65 | raise Exception(e) |
---|
66 | |
---|
67 | return wrapper |
---|
68 | |
---|
69 | |
---|
70 | # Error handler decorator. |
---|
71 | def catch_background_error(fnc): |
---|
72 | def wrapper(*args, **kwargs): |
---|
73 | this = args[0] |
---|
74 | try: |
---|
75 | fnc(*args, **kwargs) |
---|
76 | except Exception as e: |
---|
77 | this.REST.sendMessage('error?id={}'.format(kwargs.get('requestId', 'error')), {'error': '{}'.format(e)}) |
---|
78 | |
---|
79 | return wrapper |
---|
80 | |
---|
81 | |
---|
82 | def check_locked_partition(sync=False): |
---|
83 | """ |
---|
84 | Decorator to check if a partition is locked |
---|
85 | """ |
---|
86 | |
---|
87 | def outer(fnc): |
---|
88 | def wrapper(*args, **kwargs): |
---|
89 | part_id = 'None' |
---|
90 | try: |
---|
91 | this, path, get_params, post_params, server = args # @UnusedVariable |
---|
92 | part_id = post_params['disk'] + post_params['part'] |
---|
93 | if this.locked.get(part_id, False): |
---|
94 | this.locked[part_id] = True |
---|
95 | fnc(*args, **kwargs) |
---|
96 | else: |
---|
97 | return 'partition locked' |
---|
98 | except Exception as e: |
---|
99 | this.locked[part_id] = False |
---|
100 | return 'error {}'.format(e) |
---|
101 | finally: |
---|
102 | if sync is True: |
---|
103 | this.locked[part_id] = False |
---|
104 | logger.debug('Lock status: {} {}'.format(fnc, this.locked)) |
---|
105 | |
---|
106 | return wrapper |
---|
107 | |
---|
108 | return outer |
---|
109 | |
---|
110 | |
---|
111 | class OpenGnSysWorker(ServerWorker): |
---|
112 | name = 'opengnsys' |
---|
113 | interface = None # Bound interface for OpenGnsys |
---|
114 | REST = None # REST object |
---|
115 | logged_in = False # User session flag |
---|
116 | browser = {} # Browser info |
---|
117 | commands = [] # Running commands |
---|
118 | random = None # Random string for secure connections |
---|
119 | length = 32 # Random string length |
---|
120 | |
---|
121 | def _launch_browser(self, url): |
---|
122 | """ |
---|
123 | Launches the Browser with specified URL |
---|
124 | :param url: URL to show |
---|
125 | """ |
---|
126 | logger.debug('Launching browser with URL: {}'.format(url)) |
---|
127 | # Trying to kill an old browser |
---|
128 | try: |
---|
129 | os.kill(self.browser['process'].pid, signal.SIGKILL) |
---|
130 | except OSError: |
---|
131 | logger.warn('Cannot kill the old browser process') |
---|
132 | except KeyError: |
---|
133 | # There is no previous browser |
---|
134 | pass |
---|
135 | self.browser['url'] = url |
---|
136 | self.browser['process'] = subprocess.Popen(['browser', '-qws', url]) |
---|
137 | |
---|
138 | def _task_command(self, route, code, op_id, send_config=False): |
---|
139 | """ |
---|
140 | Task to execute a command and return results to a server URI |
---|
141 | :param route: server callback REST route to return results |
---|
142 | :param code: code to execute |
---|
143 | :param op_id: operation id. |
---|
144 | """ |
---|
145 | menu_url = '' |
---|
146 | # Show execution tacking log, if OGAgent runs on ogLive |
---|
147 | os_type = operations.os_type.lower() |
---|
148 | if os_type == 'oglive': |
---|
149 | menu_url = self.browser['url'] |
---|
150 | self._launch_browser('http://localhost/cgi-bin/httpd-log.sh') |
---|
151 | # Execute the code |
---|
152 | (stat, out, err) = operations.exec_command(code) |
---|
153 | # Remove command from the list |
---|
154 | for c in self.commands: |
---|
155 | if c.getName() == op_id: |
---|
156 | self.commands.remove(c) |
---|
157 | # Remove the REST API prefix, if needed |
---|
158 | if route.startswith(self.REST.endpoint): |
---|
159 | route = route[len(self.REST.endpoint):] |
---|
160 | # Send back exit status and outputs (base64-encoded) |
---|
161 | self.REST.sendMessage(route, {'mac': self.interface.mac, 'ip': self.interface.ip, 'trace': op_id, |
---|
162 | 'status': stat, 'output': out.encode('base64'), 'error': err.encode('base64')}) |
---|
163 | # Show latest menu, if OGAgent runs on ogLive |
---|
164 | if os_type == 'oglive': |
---|
165 | # Send configuration data, if needed |
---|
166 | if send_config: |
---|
167 | self.REST.sendMessage('client/configs', {'mac': self.interface.mac, 'ip': self.interface.ip, |
---|
168 | 'config': operations.get_configuration()}) |
---|
169 | self._launch_browser(menu_url) |
---|
170 | |
---|
171 | def onActivation(self): |
---|
172 | """ |
---|
173 | Sends OGAgent activation notification to OpenGnsys server |
---|
174 | """ |
---|
175 | t = 0 |
---|
176 | # Generate random secret to send on activation |
---|
177 | self.random = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(self.length)) |
---|
178 | # Ensure cfg has required configuration variables or an exception will be thrown |
---|
179 | url = self.service.config.get('opengnsys', 'remote') |
---|
180 | if operations.os_type == 'ogLive' and 'oglive' in os.environ: |
---|
181 | # Replacing server IP if its running on ogLive clinet |
---|
182 | logger.debug('Activating on ogLive client, new server is {}'.format(os.environ['oglive'])) |
---|
183 | url = parse.urlsplit(url)._replace(netloc=os.environ['oglive']).geturl() |
---|
184 | if not url.endswith(os.path.sep): |
---|
185 | url += os.path.sep |
---|
186 | self.REST = REST(url) |
---|
187 | # Get network interfaces until they are active or timeout (5 minutes) |
---|
188 | for t in range(0, 300): |
---|
189 | try: |
---|
190 | self.interface = list(operations.getNetworkInfo())[0] # Get first network interface |
---|
191 | except Exception as e: |
---|
192 | # Wait 1 sec. and retry |
---|
193 | time.sleep(1) |
---|
194 | finally: |
---|
195 | # Exit loop if interface is active |
---|
196 | if self.interface: |
---|
197 | if t > 0: |
---|
198 | logger.debug("Fetch connection data after {} tries".format(t)) |
---|
199 | break |
---|
200 | # Raise error after timeout |
---|
201 | if not self.interface: |
---|
202 | raise e |
---|
203 | # Loop to send initialization message |
---|
204 | for t in range(0, 100): |
---|
205 | try: |
---|
206 | try: |
---|
207 | self.REST.sendMessage('ogagent/started', {'mac': self.interface.mac, 'ip': self.interface.ip, |
---|
208 | 'secret': self.random, 'ostype': operations.os_type, |
---|
209 | 'osversion': operations.os_version}) |
---|
210 | break |
---|
211 | except: |
---|
212 | # Trying to initialize on alternative server, if defined |
---|
213 | # (used in "exam mode" from the University of Seville) |
---|
214 | self.REST = REST(self.service.config.get('opengnsys', 'altremote')) |
---|
215 | self.REST.sendMessage('ogagent/started', {'mac': self.interface.mac, 'ip': self.interface.ip, |
---|
216 | 'secret': self.random, 'ostype': operations.os_type, |
---|
217 | 'osversion': operations.os_version, 'alt_url': True}) |
---|
218 | break |
---|
219 | except: |
---|
220 | time.sleep(3) |
---|
221 | # Raise error after timeout |
---|
222 | if 0 < t < 100: |
---|
223 | logger.debug('Successful connection after {} tries'.format(t)) |
---|
224 | elif t == 100: |
---|
225 | raise Exception('Initialization error: Cannot connect to remote server') |
---|
226 | # Completing OGAgent initialization process |
---|
227 | os_type = operations.os_type.lower() |
---|
228 | if os_type == 'oglive': |
---|
229 | # # Following code may be separated into a different function to launch browser while getting the disk configuration |
---|
230 | message = """ |
---|
231 | <html> |
---|
232 | <head></head> |
---|
233 | <style> |
---|
234 | #bar { width: 20px; height: 10px; position: relative; background: darkslategrey; } |
---|
235 | </style> |
---|
236 | <body> |
---|
237 | <h1 style="margin: 5em 0 0 5em; font-size: 250%; color: darkslategrey;"> |
---|
238 | <span id="opengnsys"><span style="font-weight: lighter;">Open</span>Gnsys 3</div> |
---|
239 | <div id="bar"></span> |
---|
240 | </h1> |
---|
241 | <script> |
---|
242 | var elem = document.getElementById("bar"); |
---|
243 | var max = document.getElementById("opengnsys").offsetWidth; |
---|
244 | var pos = 0; |
---|
245 | var inc = true; |
---|
246 | var id = setInterval(frame, 5); |
---|
247 | function frame() { |
---|
248 | if (inc) { |
---|
249 | if (pos == max - 20) { inc = false; } else { pos++; } |
---|
250 | } else { |
---|
251 | if (pos == 0) { inc = true; } else { pos--; } |
---|
252 | } |
---|
253 | elem.style.left = pos + 'px'; |
---|
254 | } |
---|
255 | </script> |
---|
256 | </body> |
---|
257 | </html> |
---|
258 | """ |
---|
259 | f = open('/tmp/init.html', 'w') |
---|
260 | f.write(message) |
---|
261 | f.close() |
---|
262 | # Launch the Browser |
---|
263 | self._launch_browser('/tmp/init.html') |
---|
264 | config = operations.get_configuration() |
---|
265 | self.REST.sendMessage('ogagent/config', {'mac': self.interface.mac, 'ip': self.interface.ip, |
---|
266 | 'config': config}) |
---|
267 | else: |
---|
268 | # Delete marking files |
---|
269 | for f in ['ogboot.me', 'ogboot.firstboot', 'ogboot.secondboot']: |
---|
270 | try: |
---|
271 | os.remove(os.sep + f) |
---|
272 | except OSError: |
---|
273 | pass |
---|
274 | # Copy file "HostsFile.FirstOctetOfIPAddress" to "HostsFile", if it exists |
---|
275 | # (used in "exam mode" from the University of Seville) |
---|
276 | hosts_file = os.path.join(operations.get_etc_path(), 'hosts') |
---|
277 | new_hosts_file = hosts_file + '.' + self.interface.ip.split('.')[0] |
---|
278 | if os.path.isfile(new_hosts_file): |
---|
279 | shutil.copyfile(new_hosts_file, hosts_file) |
---|
280 | |
---|
281 | def onDeactivation(self): |
---|
282 | """ |
---|
283 | Sends OGAgent stopping notification to OpenGnsys server |
---|
284 | """ |
---|
285 | logger.debug('onDeactivation') |
---|
286 | self.REST.sendMessage('ogagent/stopped', {'mac': self.interface.mac, 'ip': self.interface.ip, |
---|
287 | 'ostype': operations.os_type, 'osversion': operations.os_version}) |
---|
288 | |
---|
289 | def processClientMessage(self, message, data): |
---|
290 | logger.debug('Got OpenGnsys message from client: {}, data {}'.format(message, data)) |
---|
291 | |
---|
292 | def onLogin(self, data): |
---|
293 | """ |
---|
294 | Sends session login notification to OpenGnsys server |
---|
295 | """ |
---|
296 | user, sep, language = data.partition(',') |
---|
297 | logger.debug('Received login for {} with language {}'.format(user, language)) |
---|
298 | self.logged_in = True |
---|
299 | self.REST.sendMessage('ogagent/loggedin', {'ip': self.interface.ip, 'user': user, 'language': language, |
---|
300 | 'ostype': operations.os_type, 'osversion': operations.os_version}) |
---|
301 | |
---|
302 | def onLogout(self, user): |
---|
303 | """ |
---|
304 | Sends session logout notification to OpenGnsys server |
---|
305 | """ |
---|
306 | logger.debug('Received logout for {}'.format(user)) |
---|
307 | self.logged_in = False |
---|
308 | self.REST.sendMessage('ogagent/loggedout', {'ip': self.interface.ip, 'user': user}) |
---|
309 | |
---|
310 | def process_ogclient(self, path, get_params, post_params, server): |
---|
311 | """ |
---|
312 | This method can be overridden to provide your own message processor, or better you can |
---|
313 | implement a method that is called exactly as "process_" + path[0] (module name has been removed from path |
---|
314 | array) and this default processMessage will invoke it |
---|
315 | * Example: |
---|
316 | Imagine this invocation url (no matter if GET or POST): http://example.com:9999/Sample/mazinger/Z |
---|
317 | The HTTP Server will remove "Sample" from path, parse arguments and invoke this method as this: |
---|
318 | module.processMessage(["mazinger","Z"], get_params, post_params) |
---|
319 | |
---|
320 | This method will process "mazinger", and look for a "self" method that is called "process_mazinger", |
---|
321 | and invoke it this way: |
---|
322 | return self.process_mazinger(["Z"], get_params, post_params) |
---|
323 | |
---|
324 | In the case path is empty (that is, the path is composed only by the module name, like in |
---|
325 | "http://example.com/Sample", the "process" method will be invoked directly |
---|
326 | |
---|
327 | The methods must return data that can be serialized to json (i.e. Objects are not serializable to json, |
---|
328 | basic type are) |
---|
329 | """ |
---|
330 | if not path: |
---|
331 | return "ok" |
---|
332 | try: |
---|
333 | operation = getattr(self, 'ogclient_' + path[0]) |
---|
334 | except Exception: |
---|
335 | raise Exception('Message processor for "{}" not found'.format(path[0])) |
---|
336 | return operation(path[1:], get_params, post_params) |
---|
337 | |
---|
338 | def process_status(self, path, get_params, post_params, server): |
---|
339 | """ |
---|
340 | Returns client status (OS type or execution status) and login status |
---|
341 | :param path: |
---|
342 | :param get_params: |
---|
343 | :param post_params: |
---|
344 | :param server: |
---|
345 | :return: JSON object {"status": "status_code", "loggedin": boolean} |
---|
346 | """ |
---|
347 | res = {'loggedin': self.logged_in} |
---|
348 | try: |
---|
349 | res['status'] = operations.os_type.lower() |
---|
350 | except KeyError: |
---|
351 | res['status'] = '' |
---|
352 | # Check if OpenGnsys Client is busy |
---|
353 | if res['status'] == 'oglive' and len(self.commands) > 0: |
---|
354 | res['status'] = 'busy' |
---|
355 | return res |
---|
356 | |
---|
357 | @check_secret |
---|
358 | def process_reboot(self, path, get_params, post_params, server): |
---|
359 | """ |
---|
360 | Launches a system reboot operation |
---|
361 | :param path: |
---|
362 | :param get_params: |
---|
363 | :param post_params: |
---|
364 | :param server: authorization header |
---|
365 | :return: JSON object {"op": "launched"} |
---|
366 | """ |
---|
367 | logger.debug('Received reboot operation') |
---|
368 | |
---|
369 | # Rebooting thread |
---|
370 | def rebt(): |
---|
371 | operations.reboot() |
---|
372 | |
---|
373 | threading.Thread(target=rebt).start() |
---|
374 | return {'op': 'launched'} |
---|
375 | |
---|
376 | @check_secret |
---|
377 | def process_poweroff(self, path, get_params, post_params, server): |
---|
378 | """ |
---|
379 | Launches a system power off operation |
---|
380 | :param path: |
---|
381 | :param get_params: |
---|
382 | :param post_params: |
---|
383 | :param server: authorization header |
---|
384 | :return: JSON object {"op": "launched"} |
---|
385 | """ |
---|
386 | logger.debug('Received poweroff operation') |
---|
387 | |
---|
388 | # Powering off thread |
---|
389 | def pwoff(): |
---|
390 | time.sleep(2) |
---|
391 | operations.poweroff() |
---|
392 | |
---|
393 | threading.Thread(target=pwoff).start() |
---|
394 | return {'op': 'launched'} |
---|
395 | |
---|
396 | @check_secret |
---|
397 | def process_script(self, path, get_params, post_params, server): |
---|
398 | """ |
---|
399 | Processes an script execution (script should be encoded in base64) |
---|
400 | :param path: |
---|
401 | :param get_params: |
---|
402 | :param post_params: object with format: |
---|
403 | id: operation id. |
---|
404 | script: command code |
---|
405 | redirect_url: callback REST route |
---|
406 | :param server: headers data |
---|
407 | :rtype: JSON object with launching status |
---|
408 | """ |
---|
409 | logger.debug('Processing script operation with params: {}'.format(post_params)) |
---|
410 | # Processing data |
---|
411 | try: |
---|
412 | script = urllib.unquote(post_params.get('script').decode('base64')).decode('utf8') |
---|
413 | op_id = post_params.get('id') |
---|
414 | route = post_params.get('redirect_uri') |
---|
415 | # Checking if the thread id. exists |
---|
416 | for c in self.commands: |
---|
417 | if c.getName() == str(op_id): |
---|
418 | raise Exception('Task id. already exists: {}'.format(op_id)) |
---|
419 | if post_params.get('client', 'false') == 'false': |
---|
420 | # Launching a new thread |
---|
421 | thr = threading.Thread(name=op_id, target=self._task_command, args=(route, script, op_id)) |
---|
422 | thr.start() |
---|
423 | self.commands.append(thr) |
---|
424 | else: |
---|
425 | # Executing as normal user |
---|
426 | self.sendClientMessage('script', {'code': script}) |
---|
427 | except Exception as e: |
---|
428 | logger.error('Got exception {}'.format(e)) |
---|
429 | return {'error': e} |
---|
430 | return {'op': 'launched'} |
---|
431 | |
---|
432 | @check_secret |
---|
433 | def process_logoff(self, path, get_params, post_params, server): |
---|
434 | """ |
---|
435 | Closes user session |
---|
436 | """ |
---|
437 | logger.debug('Received logoff operation') |
---|
438 | # Send log off message to OGAgent client |
---|
439 | self.sendClientMessage('logoff', {}) |
---|
440 | return {'op': 'sent to client'} |
---|
441 | |
---|
442 | @check_secret |
---|
443 | def process_popup(self, path, get_params, post_params, server): |
---|
444 | """ |
---|
445 | Shows a message popup on the user's session |
---|
446 | """ |
---|
447 | logger.debug('Received message operation') |
---|
448 | # Send popup message to OGAgent client |
---|
449 | self.sendClientMessage('popup', post_params) |
---|
450 | return {'op': 'launched'} |
---|
451 | |
---|
452 | def process_client_popup(self, params): |
---|
453 | self.REST.sendMessage('popup_done', params) |
---|
454 | |
---|
455 | @check_secret |
---|
456 | def process_config(self, path, get_params, post_params, server): |
---|
457 | """ |
---|
458 | Returns client configuration |
---|
459 | :param path: |
---|
460 | :param get_params: |
---|
461 | :param post_params: |
---|
462 | :param server: |
---|
463 | :return: object |
---|
464 | """ |
---|
465 | serial_no = '' # Serial number |
---|
466 | storage = [] # Storage configuration |
---|
467 | warnings = 0 # Number of warnings |
---|
468 | logger.debug('Received getconfig operation') |
---|
469 | # Processing data |
---|
470 | for row in operations.get_configuration().split(';'): |
---|
471 | cols = row.split(':') |
---|
472 | if len(cols) == 1: |
---|
473 | if cols[0] != '': |
---|
474 | # Serial number |
---|
475 | serial_no = cols[0] |
---|
476 | else: |
---|
477 | # Skip blank rows |
---|
478 | pass |
---|
479 | elif len(cols) == 7: |
---|
480 | disk, part_no, part_type, fs, op_sys, size, usage = cols |
---|
481 | try: |
---|
482 | if int(part_no) == 0: |
---|
483 | # Disk information |
---|
484 | storage.append({'disk': int(disk), 'parttable': int(part_type), 'size': int(size)}) |
---|
485 | else: |
---|
486 | # Partition information |
---|
487 | storage.append({'disk': int(disk), 'partition': int(part_no), 'parttype': part_type, |
---|
488 | 'filesystem': fs, 'operatingsystem': op_sys, 'size': int(size), |
---|
489 | 'usage': int(usage)}) |
---|
490 | except ValueError: |
---|
491 | logger.warn('Configuration parameter error: {}'.format(cols)) |
---|
492 | warnings += 1 |
---|
493 | else: |
---|
494 | # Logging warnings |
---|
495 | logger.warn('Configuration data error: {}'.format(cols)) |
---|
496 | warnings += 1 |
---|
497 | # Returning configuration data and count of warnings |
---|
498 | return {'serial': serial_no, 'storage': storage, 'warnings': warnings} |
---|
499 | |
---|
500 | @check_secret |
---|
501 | def process_execinfo(self, path, get_params, post_params, server): |
---|
502 | """ |
---|
503 | Returns running commands information |
---|
504 | :param path: |
---|
505 | :param get_params: |
---|
506 | :param post_params: |
---|
507 | :param server: |
---|
508 | :return: object |
---|
509 | """ |
---|
510 | data = [] |
---|
511 | logger.debug('Received execinfo operation') |
---|
512 | # Returning the arguments of all running threads |
---|
513 | for c in self.commands: |
---|
514 | if c.is_alive(): |
---|
515 | data.append(c.__dict__['_Thread__args']) |
---|
516 | return data |
---|
517 | |
---|
518 | @check_secret |
---|
519 | def process_stopcmd(self, path, get_params, post_params, server): |
---|
520 | logger.debug('Received stopcmd operation with params {}:'.format(post_params)) |
---|
521 | op_id = post_params.get('trace') |
---|
522 | for c in self.commands: |
---|
523 | if c.is_alive() and c.getName() == str(op_id): |
---|
524 | c._Thread__stop() |
---|
525 | return {"stopped": op_id} |
---|
526 | return {} |
---|
527 | |
---|
528 | @check_secret |
---|
529 | def process_hardware(self, path, get_params, post_params, server): |
---|
530 | """ |
---|
531 | Returns client's hardware profile |
---|
532 | :param path: |
---|
533 | :param get_params: |
---|
534 | :param post_params: |
---|
535 | :param server: |
---|
536 | :return: array of component data objects |
---|
537 | """ |
---|
538 | data = [] |
---|
539 | logger.debug('Received hardware operation') |
---|
540 | self.checkSecret(server) |
---|
541 | # Processing data |
---|
542 | try: |
---|
543 | for comp in operations.get_hardware(): |
---|
544 | data.append({'component': comp.split('=')[0], 'value': comp.split('=')[1]}) |
---|
545 | except: |
---|
546 | pass |
---|
547 | # Return list of hardware components |
---|
548 | return data |
---|
549 | |
---|
550 | @check_secret |
---|
551 | def process_software(self, path, get_params, post_params, server): |
---|
552 | """ |
---|
553 | Returns software profile installed on an operating system |
---|
554 | :param path: |
---|
555 | :param get_params: |
---|
556 | :param post_params: |
---|
557 | :param server: |
---|
558 | :return: |
---|
559 | """ |
---|
560 | logger.debug('Received software operation with params: {}'.format(post_params)) |
---|
561 | return operations.get_software(post_params.get('disk'), post_params.get('part')) |
---|