source: ogClient-Git/src/ogClient.py

Last change on this file was 30fdcce, checked in by Jose M. Guisado <jguisado@…>, 2 years ago

src: improve logging

Adds new logging handler redirecting messages to the log file
located in the Samba shared directory (applies to live mode
clients, i.e: ogLive)

Parses log level configuration from ogclient.json. See:

{

"opengnsys": {

...
"log": "INFO",
...

}
...

}

Adds --debug option to set root logger level to DEBUG when starting
ogClient. Overrides log level from config file.

In addition:

  • Replaces any occurence of print with a corresponding logging function.
  • Unsets log level for handlers, use root logger level instead.
  • Default level for root logger is INFO.
  • Replaces level from response log messages to debug (ogRest)
  • Property mode set to 100644
File size: 5.2 KB
Line 
1#
2# Copyright (C) 2020-2021 Soleta Networks <info@soleta.eu>
3#
4# This program is free software: you can redistribute it and/or modify it under
5# the terms of the GNU Affero General Public License as published by the
6# Free Software Foundation; either version 3 of the License, or
7# (at your option) any later version.
8
9import errno
10import select
11import socket
12import time
13import email
14import logging
15from io import StringIO
16
17from src.restRequest import *
18from src.ogRest import *
19from enum import Enum
20
21class State(Enum):
22        CONNECTING = 0
23        RECEIVING = 1
24        FORCE_DISCONNECTED = 2
25
26class ogClient:
27        OG_PATH = '/opt/opengnsys/'
28
29        def __init__(self, config):
30                self.CONFIG = config
31
32                self.mode = self.CONFIG['opengnsys']['mode']
33                if self.mode not in {'virtual', 'live', 'linux', 'windows'}:
34                        logging.critical('Invalid ogClient mode')
35                        raise ValueError('Mode not supported.')
36                if self.mode in {'linux', 'windows'}:
37                        self.event_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
38                        self.event_sock.setblocking(0)
39                        self.event_sock.bind(('127.0.0.1', 55885))
40                else:
41                        self.event_sock = None
42
43                if self.CONFIG['samba']['activate']:
44                        assert('user' in self.CONFIG['samba'])
45                        assert('pass' in self.CONFIG['samba'])
46
47                self.ip = self.CONFIG['opengnsys']['ip']
48                self.port = self.CONFIG['opengnsys']['port']
49                self.ogrest = ogRest(self.CONFIG)
50
51        def get_socket(self):
52                return self.sock
53
54        def get_event_socket(self):
55                return self.event_sock
56
57        def get_state(self):
58                return self.state
59
60        def send_event_hint(self, message):
61                try:
62                        event, action, user = message.split(" ")
63                        logging.debug('Sending event: %s, %s, %s', event, action, user)
64                except:
65                        logging.warning('Error parsing session datagram')
66                        return
67
68                if (event != "session" or
69                    action not in ['start', 'stop'] or
70                    not user):
71                        logging.warning('Invalid value in session datagram: %s', message)
72
73                payload = jsonBody({'event': event, 'action': action, 'user': user})
74                response = restResponse(ogResponses.EARLY_HINTS, payload)
75                self.send(response.get())
76                logging.debug('Sending event OK')
77
78        def cleanup(self):
79                self.data = ""
80                self.content_len = 0
81                self.header_len = 0
82                self.trailer = False
83
84        def connect(self):
85                logging.debug('Connecting...')
86                self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
87                self.sock.setblocking(0)
88                self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
89                self.sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 60)
90                self.sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 30)
91                self.sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 4)
92
93                self.state = State.CONNECTING
94                self.cleanup()
95
96                try:
97                        self.sock.connect((self.ip, self.port))
98                except socket.error as err:
99                        if err.errno == errno.EINPROGRESS:
100                                return
101                        elif err.errno == errno.ECONNREFUSED:
102                                return
103
104        def send(self, msg):
105                self.sock.send(bytes(msg, 'utf-8'))
106                return len(msg)
107
108        def connect2(self):
109                try:
110                        self.sock.connect((self.ip, self.port))
111                except socket.error as err:
112                        if err.errno == errno.EISCONN:
113                                logging.debug('Connected')
114                                self.state = State.RECEIVING
115                        else:
116                                time.sleep(1)
117                                logging.warning('Connection refused, retrying...')
118                                self.state = State.CONNECTING
119                                self.sock.close()
120                                self.connect()
121
122        def receive(self):
123                try:
124                        data = self.sock.recv(1024).decode('utf-8')
125                except socket.error as err:
126                        data = ''
127                        logging.warning('Receive failed: %s', str(err))
128
129                if len(data) == 0:
130                        self.sock.close()
131                        self.ogrest.kill_process()
132                        self.connect()
133                        return
134
135                self.data = self.data + data
136                request = restRequest()
137
138                if not self.trailer:
139                        header_len = self.data.find("\r\n\r\n")
140                        if header_len > 0:
141                                # https://stackoverflow.com/questions/4685217/parse-raw-http-headers
142                                request_line, headers_alone = self.data.split('\n', 1)
143                                headers = email.message_from_file(StringIO(headers_alone))
144
145                                if 'Content-Length' in headers.keys():
146                                        self.content_len = int(headers['Content-Length'])
147
148                                self.trailer = True
149                                # Add 4 because self.data.find("\r\n\r\n") does not count
150                                # "\r\n\r\n" for the length
151                                self.header_len = header_len + 4
152
153                if self.trailer and (len(self.data) >= self.content_len + self.header_len):
154                        request.parser(self.data)
155                        self.ogrest.process_request(request, self)
156                        self.cleanup()
157
158        def disconnect(self):
159                self.state = State.FORCE_DISCONNECTED
160                self.sock.shutdown(socket.SHUT_RDWR)
161                self.sock.close()
162
163        def run(self):
164                while 1:
165                        sock = self.get_socket()
166                        event_sock = self.get_event_socket()
167                        state = self.get_state()
168
169                        if state == State.CONNECTING:
170                                readset = [ sock ]
171                                writeset = [ sock ]
172                                exceptset = [ sock ]
173                        elif state == State.FORCE_DISCONNECTED:
174                                return 0
175                        else:
176                                readset = [ sock, event_sock ] if event_sock else [ sock ]
177                                writeset = [ ]
178                                exceptset = [ ]
179
180                        readable, writable, exception = select.select(readset, writeset, exceptset)
181                        if state == State.CONNECTING and sock in writable:
182                                self.connect2()
183                        elif state == State.RECEIVING and sock in readable:
184                                self.receive()
185                        elif state == State.CONNECTING and sock in exception:
186                                self.connect2()
187                        elif state == State.RECEIVING and event_sock in readable:
188                                message = event_sock.recv(4096).decode('utf-8').rstrip()
189                                self.send_event_hint(message)
190                        else:
191                                logging.critical('Invalid state: %s', str(state))
Note: See TracBrowser for help on using the repository browser.