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

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 d98bc86 was 2489637, checked in by ramon <ramongomez@…>, 9 years ago

#718: Mejorar la comprobación de salida de la sesión de usuario.

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

  • Property mode set to 100644
File size: 11.5 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
35from PyQt4 import QtGui
36from PyQt4 import QtCore
37
38import time
39import signal
40import json
41import six
42import atexit
43
44from opengnsys import ipc
45from opengnsys import utils
46from opengnsys.log import logger
47from opengnsys.service import IPC_PORT
48from opengnsys import operations
49from about_dialog_ui import Ui_OGAAboutDialog
50from message_dialog_ui import Ui_OGAMessageDialog
51from opengnsys.scriptThread import ScriptExecutorThread
52from opengnsys import VERSION
53from opengnsys.config import readConfig
54from opengnsys.loader import loadModules
55
56trayIcon = None
57
58def sigAtExit():
59    #logger.debug("Exec sigAtExit")
60    if trayIcon:
61        trayIcon.quit()
62
63#def sigTerm(sigNo, stackFrame):
64#    logger.debug("Exec sigTerm")
65#    if trayIcon:
66#        trayIcon.quit()
67
68# About dialog
69class OGAAboutDialog(QtGui.QDialog):
70    def __init__(self, parent=None):
71        QtGui.QDialog.__init__(self, parent)
72        self.ui = Ui_OGAAboutDialog()
73        self.ui.setupUi(self)
74        self.ui.VersionLabel.setText("Version " + VERSION)
75
76    def closeDialog(self):
77        self.hide()
78
79
80class OGAMessageDialog(QtGui.QDialog):
81    def __init__(self, parent=None):
82        QtGui.QDialog.__init__(self, parent)
83        self.ui = Ui_OGAMessageDialog()
84        self.ui.setupUi(self)
85
86    def message(self, message):
87        self.ui.message.setText(message)
88        self.show()
89
90    def closeDialog(self):
91        self.hide()
92
93
94class MessagesProcessor(QtCore.QThread):
95
96    logoff = QtCore.pyqtSignal(name='logoff')
97    message = QtCore.pyqtSignal(tuple, name='message')
98    script = QtCore.pyqtSignal(QtCore.QString, name='script')
99    exit = QtCore.pyqtSignal(name='exit')
100
101    def __init__(self, port):
102        super(self.__class__, self).__init__()
103        # Retries connection for a while
104        for _ in range(10):
105            try:
106                self.ipc = ipc.ClientIPC(port)
107                self.ipc.start()
108                break
109            except Exception:
110                logger.debug('IPC Server is not reachable')
111                self.ipc = None
112                time.sleep(2)
113
114        self.running = False
115
116    def stop(self):
117        self.running = False
118        if self.ipc:
119            self.ipc.stop()
120
121    def isAlive(self):
122        return self.ipc is not None
123
124    def sendLogin(self, userName):
125        if self.ipc:
126            self.ipc.sendLogin(userName)
127
128    def sendLogout(self, userName):
129        if self.ipc:
130            self.ipc.sendLogout(userName)
131
132    def run(self):
133        if self.ipc is None:
134            return
135        self.running = True
136
137        # Wait a bit so we ensure IPC thread is running...
138        time.sleep(2)
139
140        while self.running and self.ipc.running:
141            try:
142                msg = self.ipc.getMessage()
143                if msg is None:
144                    break
145                msgId, data = msg
146                logger.debug('Got Message on User Space: {}:{}'.format(msgId, data))
147                if msgId == ipc.MSG_MESSAGE:
148                    module, message, data = data.split('\0')
149                    self.message.emit((module, message, data))
150                elif msgId == ipc.MSG_LOGOFF:
151                    self.logoff.emit()
152                elif msgId == ipc.MSG_SCRIPT:
153                    self.script.emit(QtCore.QString.fromUtf8(data))
154            except Exception as e:
155                try:
156                    logger.error('Got error on IPC thread {}'.format(utils.exceptionToMessage(e)))
157                except:
158                    logger.error('Got error on IPC thread (an unicode error??)')
159
160        if self.ipc.running is False and self.running is True:
161            logger.warn('Lost connection with Service, closing program')
162
163        self.exit.emit()
164
165
166class OGASystemTray(QtGui.QSystemTrayIcon):
167    def __init__(self, app_, parent=None):
168        self.app = app_
169        self.config = readConfig(client=True)
170
171        # Get opengnsys section as dict
172        cfg = dict(self.config.items('opengnsys'))
173
174        # Set up log level
175        logger.setLevel(cfg.get('log', 'INFO'))
176
177        self.ipcport = int(cfg.get('ipc_port', IPC_PORT))
178
179        # style = app.style()
180        # icon = QtGui.QIcon(style.standardPixmap(QtGui.QStyle.SP_ComputerIcon))
181        icon = QtGui.QIcon(':/images/img/oga.png')
182
183        QtGui.QSystemTrayIcon.__init__(self, icon, parent)
184        self.menu = QtGui.QMenu(parent)
185        exitAction = self.menu.addAction("About")
186        exitAction.triggered.connect(self.about)
187        self.setContextMenu(self.menu)
188        self.ipc = MessagesProcessor(self.ipcport)
189
190        if self.ipc.isAlive() is False:
191            raise Exception('No connection to service, exiting.')
192
193        self.timer = QtCore.QTimer()
194        self.timer.timeout.connect(self.timerFnc)
195
196
197        self.stopped = False
198
199        self.ipc.message.connect(self.message)
200        self.ipc.exit.connect(self.quit)
201        self.ipc.script.connect(self.executeScript)
202        self.ipc.logoff.connect(self.logoff)
203
204        self.aboutDlg = OGAAboutDialog()
205        self.msgDlg = OGAMessageDialog()
206
207        self.timer.start(1000)  # Launch idle checking every 1 seconds
208
209        self.ipc.start()
210
211    def initialize(self):
212        # Load modules and activate them
213        # Also, sends "login" event to service
214        self.modules = loadModules(self, client=True)
215        logger.debug('Modules: {}'.format(list(v.name for v in self.modules)))
216
217        # Send init to all modules
218        validMods = []
219        for mod in self.modules:
220            try:
221                logger.debug('Activating module {}'.format(mod.name))
222                mod.activate()
223                validMods.append(mod)
224            except Exception as e:
225                logger.exception()
226                logger.error("Activation of {} failed: {}".format(mod.name, utils.exceptionToMessage(e)))
227
228        self.modules[:] = validMods  # copy instead of assignment
229
230        # If this is running, it's because he have logged in, inform service of this fact
231        self.ipc.sendLogin(operations.getCurrentUser())
232
233    def deinitialize(self):
234        for mod in reversed(self.modules):  # Deinitialize reversed of initialization
235            try:
236                logger.debug('Deactivating module {}'.format(mod.name))
237                mod.deactivate()
238            except Exception as e:
239                logger.exception()
240                logger.error("Deactivation of {} failed: {}".format(mod.name, utils.exceptionToMessage(e)))
241
242    def timerFnc(self):
243        pass
244
245    def message(self, msg):
246        '''
247        Processes the message sent asynchronously, msg is an QString
248        '''
249        try:
250            logger.debug('msg: {}, {}'.format(type(msg), msg))
251            module, message, data = msg
252        except Exception as e:
253            logger.error('Got exception {} processing message {}'.format(e, msg))
254            return
255
256        for v in self.modules:
257            if v.name == module:  # Case Sensitive!!!!
258                try:
259                    logger.debug('Notifying message {} to module {} with json data {}'.format(message, v.name, data))
260                    v.processMessage(message, json.loads(data))
261                    return
262                except Exception as e:
263                    logger.error('Got exception {} processing generic message on {}'.format(e, v.name))
264
265        logger.error('Module {} not found, messsage {} not sent'.format(module, message))
266
267    def executeScript(self, script):
268        logger.debug('Executing script')
269        script = six.text_type(script.toUtf8()).decode('base64')
270        th = ScriptExecutorThread(script)
271        th.start()
272
273    def logoff(self):
274        logger.debug('Logoff invoked')
275        operations.logoff()  # Invoke log off
276
277    def about(self):
278        self.aboutDlg.exec_()
279
280    def cleanup(self):
281        logger.debug('Quit invoked')
282        if self.stopped is False:
283            self.stopped = True
284            try:
285                self.deinitialize()
286            except Exception:
287                logger.exception()
288                logger.error('Got exception deinitializing modules')
289
290            try:
291                # If we close Client, send Logoff to Broker
292                self.ipc.sendLogout(operations.getCurrentUser())
293                self.timer.stop()
294                self.ipc.stop()
295            except Exception:
296                # May we have lost connection with server, simply exit in that case
297                pass
298
299            try:
300                # operations.logoff()  # Uncomment this after testing to logoff user
301                pass
302            except Exception:
303                pass
304
305    def quit(self):
306        #logger.debug("Exec quit {}".format(self.stopped))
307        if self.stopped is False:
308            self.cleanup()
309            self.app.quit()
310
311    def closeEvent(self,event):
312        logger.debug("Exec closeEvent")
313        event.accept()
314        self.quit()
315
316if __name__ == '__main__':
317    app = QtGui.QApplication(sys.argv)
318
319    if not QtGui.QSystemTrayIcon.isSystemTrayAvailable():
320        # QtGui.QMessageBox.critical(None, "Systray", "I couldn't detect any system tray on this system.")
321        sys.exit(1)
322
323    # This is important so our app won't close on messages windows (alerts, etc...)
324    QtGui.QApplication.setQuitOnLastWindowClosed(False)
325
326    try:
327        trayIcon = OGASystemTray(app)
328    except Exception as e:
329        logger.exception()
330        logger.error('OGA Service is not running, or it can\'t contact with OGA Server. User Tools stopped: {}'.format(utils.exceptionToMessage(e)))
331        sys.exit(1)
332
333    try:
334        trayIcon.initialize()  # Initialize modules, etc..
335    except Exception as e:
336        logger.exception()
337        logger.error('Exception initializing OpenGnsys User Agent {}'.format(utils.exceptionToMessage(e)))
338        trayIcon.quit()
339        sys.exit(1)
340
341    app.aboutToQuit.connect(trayIcon.cleanup)
342    trayIcon.show()
343
344    # Catch kill and logout user :)
345    #signal.signal(signal.SIGTERM, sigTerm)
346    atexit.register(sigAtExit)
347
348    res = app.exec_()
349
350    logger.debug('Exiting')
351    trayIcon.quit()
352
353    sys.exit(res)
Note: See TracBrowser for help on using the repository browser.