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

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 53c03ca 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
Line 
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
39import atexit
40from PyQt4 import QtGui
41from PyQt4 import QtCore
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
55# Set default characters encoding to UTF-8
56reload(sys)
57if hasattr(sys, 'setdefaultencoding'):
58    sys.setdefaultencoding('utf-8')
59
60trayIcon = None
61
62def sigAtExit():
63    if trayIcon:
64        trayIcon.quit()
65
66#def sigTerm(sigNo, stackFrame):
67#    logger.debug("Exec sigTerm")
68#    if trayIcon:
69#        trayIcon.quit()
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
127    def sendLogin(self, userName, language):
128        if self.ipc:
129            self.ipc.sendLogin(userName, language)
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
174        # Get opengnsys section as dict
175        cfg = dict(self.config.items('opengnsys'))
176
177        # Set up log level
178        logger.setLevel(cfg.get('log', 'INFO'))
179
180        self.ipcport = int(cfg.get('ipc_port', IPC_PORT))
181
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)
192
193        if self.ipc.isAlive() is False:
194            raise Exception('No connection to service, exiting.')
195
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()
213
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)))
219
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)))
230
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
234        self.ipc.sendLogin(operations.getCurrentUser(), operations.getSessionLanguage())
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))
267
268        logger.error('Module {} not found, messsage {} not sent'.format(module, message))
269
270    def executeScript(self, script):
271        logger.debug('Executing script')
272        script = six.text_type(script.toUtf8()).decode('base64')
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
283    def cleanup(self):
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')
292
293            try:
294                # If we close Client, send Logoff to Broker
295                self.ipc.sendLogout(operations.getCurrentUser())
296                time.sleep(1)
297                self.timer.stop()
298                self.ipc.stop()
299            except Exception:
300                # May we have lost connection with server, simply log and exit in that case
301                logger.exception()
302                logger.exception("Got an exception, processing quit")
303
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()
315
316    def closeEvent(self, event):
317        logger.debug("Exec closeEvent")
318        event.accept()
319        self.quit()
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
346    app.aboutToQuit.connect(trayIcon.cleanup)
347    trayIcon.show()
348
349    # Catch kill and logout user :)
350    #signal.signal(signal.SIGTERM, sigTerm)
351    atexit.register(sigAtExit)
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.