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
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    if trayIcon:
60        trayIcon.quit()
61
62#def sigTerm(sigNo, stackFrame):
63#    logger.debug("Exec sigTerm")
64#    if trayIcon:
65#        trayIcon.quit()
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
170        # Get opengnsys section as dict
171        cfg = dict(self.config.items('opengnsys'))
172
173        # Set up log level
174        logger.setLevel(cfg.get('log', 'INFO'))
175
176        self.ipcport = int(cfg.get('ipc_port', IPC_PORT))
177
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)
188
189        if self.ipc.isAlive() is False:
190            raise Exception('No connection to service, exiting.')
191
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()
209
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)))
215
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)))
226
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))
263
264        logger.error('Module {} not found, messsage {} not sent'.format(module, message))
265
266    def executeScript(self, script):
267        logger.debug('Executing script')
268        script = six.text_type(script.toUtf8()).decode('base64')
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
279    def cleanup(self):
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')
288
289            try:
290                # If we close Client, send Logoff to Broker
291                self.ipc.sendLogout(operations.getCurrentUser())
292                time.sleep(1)
293                self.timer.stop()
294                self.ipc.stop()
295            except Exception:
296                # May we have lost connection with server, simply log and exit in that case
297                logger.exception("Got an exception processing quit")
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.