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

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 1e8645b was 83de7cb, checked in by ramon <ramongomez@…>, 9 years ago

#718: Incluir espera para que OGAgent de Windows pueda enviar mensaje de logout.

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

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