source: admin/Sources/Clients/ogagent/src/OGAgentUser.py @ f2e6a2e

918-git-images-111dconfigfileconfigure-oglivegit-imageslgromero-new-oglivemainmaint-cronmount-efivarfsmultivmmultivm-ogboot-installerogClonningEngineogboot-installer-jenkinsoglive-ipv6test-python-scriptsticket-301ticket-50ticket-50-oldticket-577ticket-585ticket-611ticket-612ticket-693ticket-700ubu24tplunification2use-local-agent-oglivevarios-instalacionwebconsole3
Last change on this file since f2e6a2e was 30492e9, checked in by ramon <ramongomez@…>, 7 years ago

#708: OGAgent notifica el idioma al iniciar sesión de usuario.

git-svn-id: https://opengnsys.es/svn/branches/version1.1@5528 a21b9725-9963-47de-94b9-378ad31fedc9

  • Property mode set to 100644
File size: 11.8 KB
RevLine 
[c3e7c06]1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#
4# Copyright (c) 2014 Virtual Cable S.L.
5# All rights reserved.
6#
7# Redistribution and use in source and binary forms, with or without modification,
8# are permitted provided that the following conditions are met:
9#
10#    * Redistributions of source code must retain the above copyright notice,
11#      this list of conditions and the following disclaimer.
12#    * Redistributions in binary form must reproduce the above copyright notice,
13#      this list of conditions and the following disclaimer in the documentation
14#      and/or other materials provided with the distribution.
15#    * Neither the name of Virtual Cable S.L. nor the names of its contributors
16#      may be used to endorse or promote products derived from this software
17#      without specific prior written permission.
18#
19# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29'''
30@author: Adolfo Gómez, dkmaster at dkmon dot com
31'''
32from __future__ import unicode_literals
33
34import sys
35import time
36import signal
37import json
38import six
[2489637]39import atexit
[82bc070]40from PyQt4 import QtGui
41from PyQt4 import QtCore
[c3e7c06]42
43from opengnsys import ipc
44from opengnsys import utils
45from opengnsys.log import logger
46from opengnsys.service import IPC_PORT
47from opengnsys import operations
48from about_dialog_ui import Ui_OGAAboutDialog
49from message_dialog_ui import Ui_OGAMessageDialog
50from opengnsys.scriptThread import ScriptExecutorThread
51from opengnsys import VERSION
52from opengnsys.config import readConfig
53from opengnsys.loader import loadModules
54
[82bc070]55# Set default characters encoding to UTF-8
56reload(sys)
57if hasattr(sys, 'setdefaultencoding'):
58    sys.setdefaultencoding('utf-8')
59
[c3e7c06]60trayIcon = None
61
[2489637]62def sigAtExit():
[c3e7c06]63    if trayIcon:
64        trayIcon.quit()
65
[2489637]66#def sigTerm(sigNo, stackFrame):
67#    logger.debug("Exec sigTerm")
68#    if trayIcon:
69#        trayIcon.quit()
[c3e7c06]70
71# About dialog
72class OGAAboutDialog(QtGui.QDialog):
73    def __init__(self, parent=None):
74        QtGui.QDialog.__init__(self, parent)
75        self.ui = Ui_OGAAboutDialog()
76        self.ui.setupUi(self)
77        self.ui.VersionLabel.setText("Version " + VERSION)
78
79    def closeDialog(self):
80        self.hide()
81
82
83class OGAMessageDialog(QtGui.QDialog):
84    def __init__(self, parent=None):
85        QtGui.QDialog.__init__(self, parent)
86        self.ui = Ui_OGAMessageDialog()
87        self.ui.setupUi(self)
88
89    def message(self, message):
90        self.ui.message.setText(message)
91        self.show()
92
93    def closeDialog(self):
94        self.hide()
95
96
97class MessagesProcessor(QtCore.QThread):
98
99    logoff = QtCore.pyqtSignal(name='logoff')
100    message = QtCore.pyqtSignal(tuple, name='message')
101    script = QtCore.pyqtSignal(QtCore.QString, name='script')
102    exit = QtCore.pyqtSignal(name='exit')
103
104    def __init__(self, port):
105        super(self.__class__, self).__init__()
106        # Retries connection for a while
107        for _ in range(10):
108            try:
109                self.ipc = ipc.ClientIPC(port)
110                self.ipc.start()
111                break
112            except Exception:
113                logger.debug('IPC Server is not reachable')
114                self.ipc = None
115                time.sleep(2)
116
117        self.running = False
118
119    def stop(self):
120        self.running = False
121        if self.ipc:
122            self.ipc.stop()
123
124    def isAlive(self):
125        return self.ipc is not None
126
[30492e9]127    def sendLogin(self, userName, language):
[c3e7c06]128        if self.ipc:
[30492e9]129            self.ipc.sendLogin(userName, language)
[c3e7c06]130
131    def sendLogout(self, userName):
132        if self.ipc:
133            self.ipc.sendLogout(userName)
134
135    def run(self):
136        if self.ipc is None:
137            return
138        self.running = True
139
140        # Wait a bit so we ensure IPC thread is running...
141        time.sleep(2)
142
143        while self.running and self.ipc.running:
144            try:
145                msg = self.ipc.getMessage()
146                if msg is None:
147                    break
148                msgId, data = msg
149                logger.debug('Got Message on User Space: {}:{}'.format(msgId, data))
150                if msgId == ipc.MSG_MESSAGE:
151                    module, message, data = data.split('\0')
152                    self.message.emit((module, message, data))
153                elif msgId == ipc.MSG_LOGOFF:
154                    self.logoff.emit()
155                elif msgId == ipc.MSG_SCRIPT:
156                    self.script.emit(QtCore.QString.fromUtf8(data))
157            except Exception as e:
158                try:
159                    logger.error('Got error on IPC thread {}'.format(utils.exceptionToMessage(e)))
160                except:
161                    logger.error('Got error on IPC thread (an unicode error??)')
162
163        if self.ipc.running is False and self.running is True:
164            logger.warn('Lost connection with Service, closing program')
165
166        self.exit.emit()
167
168
169class OGASystemTray(QtGui.QSystemTrayIcon):
170    def __init__(self, app_, parent=None):
171        self.app = app_
172        self.config = readConfig(client=True)
173
[2489637]174        # Get opengnsys section as dict
[c3e7c06]175        cfg = dict(self.config.items('opengnsys'))
[2489637]176
[c3e7c06]177        # Set up log level
178        logger.setLevel(cfg.get('log', 'INFO'))
[2489637]179
[c3e7c06]180        self.ipcport = int(cfg.get('ipc_port', IPC_PORT))
[2489637]181
[c3e7c06]182        # style = app.style()
183        # icon = QtGui.QIcon(style.standardPixmap(QtGui.QStyle.SP_ComputerIcon))
184        icon = QtGui.QIcon(':/images/img/oga.png')
185
186        QtGui.QSystemTrayIcon.__init__(self, icon, parent)
187        self.menu = QtGui.QMenu(parent)
188        exitAction = self.menu.addAction("About")
189        exitAction.triggered.connect(self.about)
190        self.setContextMenu(self.menu)
191        self.ipc = MessagesProcessor(self.ipcport)
[2489637]192
[c3e7c06]193        if self.ipc.isAlive() is False:
194            raise Exception('No connection to service, exiting.')
[2489637]195
[c3e7c06]196        self.timer = QtCore.QTimer()
197        self.timer.timeout.connect(self.timerFnc)
198
199
200        self.stopped = False
201
202        self.ipc.message.connect(self.message)
203        self.ipc.exit.connect(self.quit)
204        self.ipc.script.connect(self.executeScript)
205        self.ipc.logoff.connect(self.logoff)
206
207        self.aboutDlg = OGAAboutDialog()
208        self.msgDlg = OGAMessageDialog()
209
210        self.timer.start(1000)  # Launch idle checking every 1 seconds
211
212        self.ipc.start()
[2489637]213
[c3e7c06]214    def initialize(self):
215        # Load modules and activate them
216        # Also, sends "login" event to service
217        self.modules = loadModules(self, client=True)
218        logger.debug('Modules: {}'.format(list(v.name for v in self.modules)))
[2489637]219
[c3e7c06]220        # Send init to all modules
221        validMods = []
222        for mod in self.modules:
223            try:
224                logger.debug('Activating module {}'.format(mod.name))
225                mod.activate()
226                validMods.append(mod)
227            except Exception as e:
228                logger.exception()
229                logger.error("Activation of {} failed: {}".format(mod.name, utils.exceptionToMessage(e)))
[2489637]230
[c3e7c06]231        self.modules[:] = validMods  # copy instead of assignment
232
233        # If this is running, it's because he have logged in, inform service of this fact
[30492e9]234        self.ipc.sendLogin(operations.getCurrentUser(), operations.getSessionLanguage())
[c3e7c06]235
236    def deinitialize(self):
237        for mod in reversed(self.modules):  # Deinitialize reversed of initialization
238            try:
239                logger.debug('Deactivating module {}'.format(mod.name))
240                mod.deactivate()
241            except Exception as e:
242                logger.exception()
243                logger.error("Deactivation of {} failed: {}".format(mod.name, utils.exceptionToMessage(e)))
244
245    def timerFnc(self):
246        pass
247
248    def message(self, msg):
249        '''
250        Processes the message sent asynchronously, msg is an QString
251        '''
252        try:
253            logger.debug('msg: {}, {}'.format(type(msg), msg))
254            module, message, data = msg
255        except Exception as e:
256            logger.error('Got exception {} processing message {}'.format(e, msg))
257            return
258
259        for v in self.modules:
260            if v.name == module:  # Case Sensitive!!!!
261                try:
262                    logger.debug('Notifying message {} to module {} with json data {}'.format(message, v.name, data))
263                    v.processMessage(message, json.loads(data))
264                    return
265                except Exception as e:
266                    logger.error('Got exception {} processing generic message on {}'.format(e, v.name))
[2489637]267
[c3e7c06]268        logger.error('Module {} not found, messsage {} not sent'.format(module, message))
269
270    def executeScript(self, script):
271        logger.debug('Executing script')
[2489637]272        script = six.text_type(script.toUtf8()).decode('base64')
[c3e7c06]273        th = ScriptExecutorThread(script)
274        th.start()
275
276    def logoff(self):
277        logger.debug('Logoff invoked')
278        operations.logoff()  # Invoke log off
279
280    def about(self):
281        self.aboutDlg.exec_()
282
[2489637]283    def cleanup(self):
[c3e7c06]284        logger.debug('Quit invoked')
285        if self.stopped is False:
286            self.stopped = True
287            try:
288                self.deinitialize()
289            except Exception:
290                logger.exception()
291                logger.error('Got exception deinitializing modules')
[2489637]292
[c3e7c06]293            try:
294                # If we close Client, send Logoff to Broker
295                self.ipc.sendLogout(operations.getCurrentUser())
[83de7cb]296                time.sleep(1)
[c3e7c06]297                self.timer.stop()
298                self.ipc.stop()
299            except Exception:
[83de7cb]300                # May we have lost connection with server, simply log and exit in that case
[ea40e6b]301                logger.exception()
302                logger.exception("Got an exception, processing quit")
[c3e7c06]303
[2489637]304            try:
305                # operations.logoff()  # Uncomment this after testing to logoff user
306                pass
307            except Exception:
308                pass
309
310    def quit(self):
311        #logger.debug("Exec quit {}".format(self.stopped))
312        if self.stopped is False:
313            self.cleanup()
314            self.app.quit()
[c3e7c06]315
[82bc070]316    def closeEvent(self, event):
[2489637]317        logger.debug("Exec closeEvent")
318        event.accept()
319        self.quit()
[c3e7c06]320
321if __name__ == '__main__':
322    app = QtGui.QApplication(sys.argv)
323
324    if not QtGui.QSystemTrayIcon.isSystemTrayAvailable():
325        # QtGui.QMessageBox.critical(None, "Systray", "I couldn't detect any system tray on this system.")
326        sys.exit(1)
327
328    # This is important so our app won't close on messages windows (alerts, etc...)
329    QtGui.QApplication.setQuitOnLastWindowClosed(False)
330
331    try:
332        trayIcon = OGASystemTray(app)
333    except Exception as e:
334        logger.exception()
335        logger.error('OGA Service is not running, or it can\'t contact with OGA Server. User Tools stopped: {}'.format(utils.exceptionToMessage(e)))
336        sys.exit(1)
337
338    try:
339        trayIcon.initialize()  # Initialize modules, etc..
340    except Exception as e:
341        logger.exception()
342        logger.error('Exception initializing OpenGnsys User Agent {}'.format(utils.exceptionToMessage(e)))
343        trayIcon.quit()
344        sys.exit(1)
345
[2489637]346    app.aboutToQuit.connect(trayIcon.cleanup)
[c3e7c06]347    trayIcon.show()
348
349    # Catch kill and logout user :)
[2489637]350    #signal.signal(signal.SIGTERM, sigTerm)
351    atexit.register(sigAtExit)
[c3e7c06]352
353    res = app.exec_()
354
355    logger.debug('Exiting')
356    trayIcon.quit()
357
358    sys.exit(res)
Note: See TracBrowser for help on using the repository browser.