source: ogAgent-Git/src/opengnsys/service.py @ e777421

configure-ptt-chedecorare-oglive-methodsejecutarscript-b64fix-cfg2objfixes-winlgromero-filebeatmainmodulesnew-browserno-ptt-paramogadmcliogadmclient-statusogagent-jobsogagent-macosogcore1oglogoglog2override-moduleping1ping2ping3ping4py3-winpython3report-progresstlsunification2unification3versionswindows-fixes
Last change on this file since e777421 was e274dc0, checked in by Ramón M. Gómez <ramongomez@…>, 5 years ago

#940: Fix string and byte conversions

  • Property mode set to 100644
File size: 8.8 KB
RevLine 
[11f7a07]1# -*- coding: utf-8 -*-
2#
3# Copyright (c) 2014 Virtual Cable S.L.
4# All rights reserved.
5#
6# Redistribution and use in source and binary forms, with or without modification,
7# are permitted provided that the following conditions are met:
8#
9#    * Redistributions of source code must retain the above copyright notice,
10#      this list of conditions and the following disclaimer.
11#    * Redistributions in binary form must reproduce the above copyright notice,
12#      this list of conditions and the following disclaimer in the documentation
13#      and/or other materials provided with the distribution.
14#    * Neither the name of Virtual Cable S.L. nor the names of its contributors
15#      may be used to endorse or promote products derived from this software
16#      without specific prior written permission.
17#
18# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
[53e7d45]29"""
[11f7a07]30@author: Adolfo Gómez, dkmaster at dkmon dot com
[53e7d45]31"""
32
[e274dc0]33import json
34import socket
35import time
[11f7a07]36
37from . import ipc
38from . import httpserver
[e274dc0]39from .config import readConfig
[11f7a07]40from .loader import loadModules
[e274dc0]41from .log import logger
42from .utils import exceptionToMessage
[11f7a07]43
44
45IPC_PORT = 10398
46
47
48class CommonService(object):
49    isAlive = True
50    ipc = None
51    httpServer = None
52    modules = None
53   
54    def __init__(self):
55        logger.info('----------------------------------------')
56        logger.info('Initializing OpenGnsys Agent')
57       
58        # Read configuration file before proceding & ensures minimal config is there
59        self.config = readConfig()
[e274dc0]60        # Get opengnsys section as dict
[11f7a07]61        cfg = dict(self.config.items('opengnsys'))
62   
63        # Set up log level
64        logger.setLevel(cfg.get('log', 'INFO'))
65
66        logger.debug('Loaded configuration from opengnsys.cfg:')
67        for section in self.config.sections():
68            logger.debug('Section {} = {}'.format(section, self.config.items(section)))
[e274dc0]69
[11f7a07]70        if logger.logger.isWindows():
71            # Logs will also go to windows event log for services
72            logger.logger.serviceLogger = True
73           
74        self.address = (cfg.get('address', '0.0.0.0'), int(cfg.get('port', '10997')))
75        self.ipcport = int(cfg.get('ipc_port', IPC_PORT))
76       
77        self.timeout = int(cfg.get('timeout', '20'))
78       
79        logger.debug('Socket timeout: {}'.format(self.timeout))
80        socket.setdefaulttimeout(self.timeout)
81       
82        # Now load modules
83        self.modules = loadModules(self)
84        logger.debug('Modules: {}'.format(list(v.name for v in self.modules)))
85       
86    def stop(self):
[e274dc0]87        """
[11f7a07]88        Requests service termination
[e274dc0]89        """
[11f7a07]90        self.isAlive = False
91       
92    # ********************************
93    # * Internal messages processors *
94    # ********************************
[e274dc0]95    def notifyLogin(self, data):
96        username = data.decode('utf-8')
[11f7a07]97        for v in self.modules:
98            try:
99                logger.debug('Notifying login of user {} to module {}'.format(username, v.name))
100                v.onLogin(username)
101            except Exception as e:
102                logger.error('Got exception {} processing login message on {}'.format(e, v.name))
103   
[e274dc0]104    def notifyLogout(self, data):
105        username = data.decode('utf-8')
[11f7a07]106        for v in self.modules:
107            try:
108                logger.debug('Notifying logout of user {} to module {}'.format(username, v.name))
109                v.onLogout(username)
110            except Exception as e:
111                logger.error('Got exception {} processing logout message on {}'.format(e, v.name))
112               
113    def notifyMessage(self, data):
[e274dc0]114        module, message, data = data.decode('utf-8').split('\0')
[11f7a07]115        for v in self.modules:
116            if v.name == module:  # Case Sensitive!!!!
117                try:
118                    logger.debug('Notifying message {} to module {} with json data {}'.format(message, v.name, data))
119                    v.processClientMessage(message, json.loads(data))
120                    return
121                except Exception as e:
122                    logger.error('Got exception {} processing generic message on {}'.format(e, v.name))
123
124        logger.error('Module {} not found, messsage {} not sent'.format(module, message))
125
126    def clientMessageProcessor(self, msg, data):
[e274dc0]127        """
[11f7a07]128        Callback, invoked from IPC, on its own thread (not the main thread).
129        This thread will "block" communication with agent untill finished, but this should be no problem
[e274dc0]130        """
[11f7a07]131        logger.debug('Got message {}'.format(msg))
132       
133        if msg == ipc.REQ_LOGIN:
134            self.notifyLogin(data)
135        elif msg == ipc.REQ_LOGOUT:
136            self.notifyLogout(data)
137        elif msg == ipc.REQ_MESSAGE:
138            self.notifyMessage(data)
139
140    def initialize(self):
[e274dc0]141        """
142        Initialize listeners, modules, etc...
143        """
[11f7a07]144        logger.debug('Starting IPC listener at {}'.format(IPC_PORT))
145        self.ipc = ipc.ServerIPC(self.ipcport, clientMessageProcessor=self.clientMessageProcessor)
146        self.ipc.start()
147
148        # And http threaded server
149        self.httpServer = httpserver.HTTPServerThread(self.address, self)
150        self.httpServer.start()
151       
152        # And lastly invoke modules activation
153        validMods = []
154        for mod in self.modules:
155            try:
156                logger.debug('Activating module {}'.format(mod.name))
157                mod.activate()
158                validMods.append(mod)
159            except Exception as e:
160                logger.exception()
161                logger.error("Activation of {} failed: {}".format(mod.name, exceptionToMessage(e)))
162       
163        self.modules[:] = validMods  # copy instead of assignment
164       
165        logger.debug('Modules after activation: {}'.format(list(v.name for v in self.modules)))
166
167    def terminate(self):
168        # First invoke deactivate on modules
169        for mod in reversed(self.modules):
170            try:
171                logger.debug('Deactivating module {}'.format(mod.name))
172                mod.deactivate()
173            except Exception as e:
174                logger.exception()
175                logger.error("Deactivation of {} failed: {}".format(mod.name, exceptionToMessage(e)))
176       
177        # Remove IPC threads
178        if self.ipc is not None:
179            try:
180                self.ipc.stop()
181            except Exception:
182                logger.error('Couln\'t stop ipc server')
183               
184        if self.httpServer is not None:
185            try:
186                self.httpServer.stop()
187            except Exception:
188                logger.error('Couln\'t stop RESTApi server')
189
190        self.notifyStop()
191
192    # ****************************************
193    # Methods that CAN BE overridden by agents
194    # ****************************************
195    def doWait(self, miliseconds):
[e274dc0]196        """
[11f7a07]197        Invoked to wait a bit
198        CAN be OVERRIDDEN
[e274dc0]199        """
[11f7a07]200        time.sleep(float(miliseconds) / 1000)
201
202    def notifyStop(self):
[e274dc0]203        """
[11f7a07]204        Overridden to log stop
[e274dc0]205        """
[11f7a07]206        logger.info('Service is being stopped')
207       
208    # ***************************************************
209    # * Helpers, convenient methods to facilitate comms *
210    # ***************************************************
211    def sendClientMessage(self, toModule, message, data):
[e274dc0]212        """
[11f7a07]213        Sends a message to the clients using IPC
214        The data is converted to json, so ensure that it is serializable.
215        All IPC is asynchronous, so if you expect a response, this will be sent by client using another message
[e274dc0]216
[11f7a07]217        @param toModule: Module that will receive this message
218        @param message: Message to send
[e274dc0]219        @param data: data to send
220        """
[11f7a07]221        self.ipc.sendMessageMessage('\0'.join((toModule, message, json.dumps(data))))
222       
223    def sendScriptMessage(self, script):
[e274dc0]224        """
[11f7a07]225        Sends an script to be executed by client
[e274dc0]226        """
[11f7a07]227        self.ipc.sendScriptMessage(script)
228       
229    def sendLogoffMessage(self):
[e274dc0]230        """
[11f7a07]231        Sends a logoff message to client
[e274dc0]232        """
[11f7a07]233        self.ipc.sendLoggofMessage()
[1deb0d1]234       
235    def sendPopupMessage(self, title, message):
[e274dc0]236        """
237        Sends a popup box to be displayed by client
238        """
[1deb0d1]239        self.ipc.sendPopupMessage(title, message)
Note: See TracBrowser for help on using the repository browser.