print_stats()   A
last analyzed

Complexity

Conditions 3

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
dl 0
loc 19
rs 9.4285
c 0
b 0
f 0
1
#!/usr/bin/env python
2
# -*- coding: utf8 -*-
3
#
4
#  flexlm_analysis.py : Script to analyse flexlm log files
5
#
6
#  (C) Copyright 2013i - 2017 Olivier Delhomme
7
#  e-mail : [email protected]
8
#
9
#  This program is free software; you can redistribute it and/or modify
10
#  it under the terms of the GNU General Public License as published by
11
#  the Free Software Foundation; either version 2, or (at your option)
12
#  any later version.
13
#
14
#  This program is distributed in the hope that it will be useful,
15
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
#  GNU General Public License for more details.
18
#
19
#  You should have received a copy of the GNU General Public License
20
#  along with this program; if not, write to the Free Software Foundation,
21
#  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22
#
23
24
import argparse
25
import re
26
import gzip
27
28
29
class Options:
30
    """
31
    A class to manage command line options
32
    """
33
34
    files = []           # file list that we will read looking for entries
35
    out = 'None'         # Character string to choose which output we want:
36
                         # 'stat' or 'gnuplot'
37
    image = 'image.png'  # Image name to be included in the gnuplot script
38
    description = {}
39
    options = None
40
41
    def __init__(self):
42
        """
43
        Inits the class
44
        """
45
        self.files = []
46
        self.out = 'None'
47
        self.image = 'image.png'
48
        self.description = {}
49
        self.options = None
50
51
        self._get_command_line_arguments()
52
53
    # End of init() function
54
55
56
    def _get_command_line_arguments(self):
57
        """
58
        Defines and gets all the arguments for the command line using
59
        argparse module. This function is called in the __init__ function
60
        of this class.
61
        """
62
        str_version = 'flexlm_analysis'
63
64
        parser = argparse.ArgumentParser(description='Script to analyse flexlm log files.', version=str_version)
65
66
        parser.add_argument('-i', '--image', action='store', dest='image', help='Tells the image name the gnuplot script may generate', default='image.png')
67
        parser.add_argument('-g', '--gnuplot', action='store_true', dest='gnuplot', help='Outputs a gnuplot script that can be executed later to generate an image about the usage', default=False)
68
        parser.add_argument('-s', '--stats', action='store_true', dest='stats', help='Outputs some stats about the use of the modules as stated in the log file', default=False)
69
        parser.add_argument('files', metavar='Files', type=str, nargs='+', help='Log files to be parsed')
70
71
        self.options = parser.parse_args()
72
73
        if self.options.gnuplot:
74
            self.out = 'gnuplot'
75
76
        if self.options.stats:
77
            self.out = 'stat'
78
79
        self.image = self.options.image
80
        self.files = self.options.files
81
82
    # End of get_command_line_arguments() function
83
# End of Options class
84
85
86
def read_files(files):
87
    """Matches lines in the files and returns the number of days in use and a
88
    list of the following tuple : (date, time, state, module, user, machine).
89
    """
90
91
# 8:21:20 (lmgrd) FLEXnet Licensing (v10.8.0 build 18869) started on MACHINE (IBM PC) (7/30/2012)
92
93
    result_list = []
94
    nb_days = 0
95
    date_matched = False
96
97
    # Reading each file here, one after the other.
98
    for a_file in files:
99
100
        # Openning the files with gzip if they end with .gz, in normal mode
101
        # if not. Do we need bz2 ? May be we should do this with a magic number
102
        if '.gz' in a_file:
103
            inode = gzip.open(a_file, 'r')
104
        else:
105
            inode = open(a_file, 'r')
106
107
        date = '??/??/????'  # Some unknown date
108
        old_date = ''
109
110
        for line in inode:
111
            # Looking for some patterns in the file
112
113
            if date_matched is False:
114
                # Look for the line below to guess the starting date (here : 2012/7/30)
115
                # 8:21:20 (lmgrd) FLEXnet Licensing (v10.8.0 build 18869) started on MACHINE (IBM PC) (7/30/2012)
116
                res = re.match(r'\ ?\d+:\d+:\d+ .+ \((\d+)/(\d+)/(\d+)\)', line)
117
                if res:
118
                    # Formating like this : year/month/day
119
                    date = '%s/%s/%s' % (res.group(3), res.group(1), res.group(2))
120
                    date_matched = True
121
122
            # 11:50:22 (orglab) OUT: "Origin7" user@MACHINE-NAME
123
            res = re.match(r'\ ?(\d+:\d+:\d+) .+ (\w+): "(.+)" (\w+)@(.+)', line)
124
            if res:
125
                # I put the results in variables for clarity
126
                time = res.group(1)
127
                state = res.group(2)
128
                module = res.group(3)
129
                user = res.group(4)
130
                machine = res.group(5)
131
                result_list.append((date, time, state, module, user, machine))
132
            else:
133
                # Trying this patern instead :
134
                # 20:52:29 (lmgrd) TIMESTAMP 1/23/2012
135
                res = re.match(r'\ ?\d+:\d+:\d+ .+ TIMESTAMP (\d+)\/(\d+)\/(\d+)', line)
136
                if res:
137
                    # Formating like this : year/month/day
138
                    date = '%s/%s/%s' % (res.group(3), res.group(1), res.group(2))
139
                    if date != old_date:
140
                        nb_days = nb_days + 1
141
                        old_date = date
142
143
        inode.close()
144
145
    # Returning the total number of days seen in the log file and the
146
    # list containing all tuples
147
    return (nb_days, result_list)
148
149
# End of read_files() function
150
151
152
def get_stats_from_module(stats, module_list, module):
153
    """Returns the tuple corresponding to the module
154
    from stats dictionary and takes care to create one
155
    if its a new module.
156
    """
157
158
    if module not in module_list:
159
        module_list.append(module)
160
        stats[module] = (0, 999999999999, '', -1, 0, 0)
161
162
    return stats[module]
163
164
# End of get_stats_from_module() function
165
166
167
def count_users_upon_state(state, nb_users, total_use):
168
    """Counts the maximum number of users and the number
169
    of usage made upon the state of the license IN or OUT
170
    Returns updated nb_users and total_use in a tuple
171
    """
172
173
    if state.lower() == 'out':
174
        nb_users = nb_users + 1
175
        total_use = total_use + 1
176
177
    elif state.lower() == 'in':
178
        nb_users = nb_users - 1
179
180
    return (nb_users, total_use)
181
182
# End of count_users_upon_state() function
183
184
185
def get_min_max_users(nb_users, min_users, max_users, date, max_day):
186
    """Gets the maximum and the minimum users usage
187
    Returns updated (or not) min_users, max_users and
188
    max_day in a tuple
189
    """
190
191
    if nb_users > 0 and nb_users > max_users:
192
        max_users = nb_users
193
        max_day = date
194
195
    elif nb_users > 0 and nb_users < min_users:
196
        min_users = nb_users
197
198
    return (min_users, max_users, max_day)
199
200
# End of get_min_max_users() function
201
202
203
def do_some_stats(result_list):
204
    """Here we do some stats and fill a dictionnary of stats that will
205
    contain all stats per module ie one tuple containing the following :
206
    (max_users, min_users, max_day, nb_users, total_use, nb_days).
207
208
    Returns a dictionnary of statistics and a list of module that has stats.
209
    """
210
211
    old_date = 'XXXXXXX'  # A value that does not exists
212
    # nb_days = 0           # Total number of days of use
213
214
    module_list = []
215
    stats = dict()  # Tuple dictionnary : (max_users, min_users, max_day, nb_users, total_use, nb_days)
216
217
    for data in result_list:
218
        (date, time, state, module, user, machine) = data
219
220
        # Retrieving usage values for a specific module
221
        (max_users, min_users, max_day, nb_users, total_use, nb_days) = get_stats_from_module(stats, module_list, module)
222
223
        # Calculating statistics
224
        if date != old_date:
225
226
            if nb_users < 0:
227
                nb_users = 1
228
229
            nb_days = nb_days + 1
230
            old_date = date
231
232
        (nb_users, total_use) = count_users_upon_state(state, nb_users, total_use)
233
        (min_users, max_users, max_day) = get_min_max_users(nb_users, min_users, max_users, date, max_day)
234
235
        # Saving the new values into the corresponding module
236
        stats[module] = (max_users, min_users, max_day, nb_users, total_use, nb_days)
237
238
    return (stats, module_list)
239
240
# End of do_some_stats function
241
242
243
def print_stats(nb_days, stats, name_list):
244
    """Prints the stats module per module to the screen.
245
    """
246
247
    for name in name_list:
248
        (max_users, min_users, max_day, nb_users, total_use, nb_use_days) = stats[name]
249
250
        print('Module %s :' % name)
251
        print(' Number of users per day :')
252
        print('  max : %d (%s)' % (max_users, max_day))
253
254
        if (nb_use_days > 0):
255
            print('  avg : %d' % (total_use/nb_use_days))
256
257
        print('  min : %d' % min_users)
258
259
        print(' Total number of use : %d' % total_use)
260
        print(' Number of days used : %d / %d' % (nb_use_days, nb_days))
261
        print('')  # Fancier things
262
263
# End of print_stats function
264
265
266
def output_stats(nb_days, result_list):
267
    """Does some stats on the result list and prints them on the screen.
268
    """
269
270
    (stats, name_list) = do_some_stats(result_list)
271
    print_stats(nb_days, stats, name_list)
272
273
# End of output_stats
274
275
276
def init_use_upon_state(state):
277
    """Inits use variable upon the first state we
278
    encounter. We normally should only encounter
279
    a OUT state but it might not be the case in
280
    real life so a IN state fixes use at 0.
281
    """
282
283
    if state.lower() == 'out':
284
        use = 1
285
    elif state.lower == 'in':
286
        use = 0
287
288
    return use
289
290
# End of init_use_upon_state() function
291
292
293
def update_use_value_upon_state(state, use):
294
    """updates use variable upon state content:
295
    OUT adds a usage an IN removes a usage.
296
    """
297
298
    if state.lower() == 'out':
299
        use = use + 1
300
    elif state.lower() == 'in':
301
        use = use - 1
302
303
    return use
304
305
# End of update_use_value_upon_state() function
306
307
308
def do_gnuplot_stats(result_list):
309
    """Here we do some gnuplot style stats in order to draw an image of the
310
    evolution of the use of the modules.
311
312
    event_list contains the date, time and number of used licenses in reverse
313
    chronological order.
314
    """
315
316
    module_list = []
317
    stats = dict()
318
319
    for data in result_list:
320
        (date, time, state, module, user, machine) = data
321
322
        if module not in module_list:
323
            module_list.append(module)
324
            stats[module] = []   # Creating an empty list of tuples for this new module.
325
326
        event_list = stats[module]
327
328
        if event_list == []:
329
            use = init_use_upon_state(state)
330
331
        else:
332
            (some_date, some_time, use) = event_list[0]  # retrieving the last 'use' value to update it
333
            use = update_use_value_upon_state(state, use)
334
335
        event_list.insert(0, (date, time, use))  # Prepending to the list
336
        stats[module] = event_list
337
338
    return (stats, module_list)
339
340
# End of do_some_stats function
341
342
343
def print_gnuplot(image_name, nb_days, stats, module_list):
344
    """Writing the data files and the gnuplot script.
345
    """
346
347
    # Generating the gnuplot script
348
    gnuplot_file = open('gnuplot.script', 'w')
349
350
    gnuplot_file.write('set key right\n')
351
    gnuplot_file.write('set grid\n')
352
    gnuplot_file.write('set title "FlexLm"\n')
353
    gnuplot_file.write('set xdata time\n')
354
    gnuplot_file.write('set timefmt "%Y/%m/%d %H:%M:%S"\n')
355
    gnuplot_file.write('set format x "%Y/%m/%d %H:%M:%S"\n')
356
    gnuplot_file.write('set xlabel "Date"\n')
357
    gnuplot_file.write('set ylabel "Nombre d\'executions"\n')
358
    gnuplot_file.write('set output "%s"\n' % image_name)
359
    gnuplot_file.write('set style line 1 lw 1\n')
360
    gnuplot_file.write('set terminal png size %d,1024\n' % (24*nb_days))
361
    gnuplot_file.write('plot ')
362
363
    first_line = True
364
365
    # Generating data files. Their names are based upon the module name being analysed
366
    for m in module_list:
367
        dat_filename = '%s.dat' % m
368
        dat_file = open(dat_filename, 'w')
369
370
        if first_line:
371
            gnuplot_file.write('"%s" using 1:3 title "%s" with lines' % (dat_filename, m))
372
            first_line = False
373
        else:
374
            gnuplot_file.write(', \\\n"%s" using 1:3 title "%s" with lines' % (dat_filename, m))
375
376
        event_list = stats[m]
377
        event_list.reverse()  # We have to reverse the list as we prepended elements
378
379
        for event in event_list:
380
            (date, time, use) = event
381
            dat_file.write('%s %s %d\n' % (date, time, use))
382
383
        dat_file.close()
384
385
# End of print_gnuplot() function
386
387
388
def output_gnuplot(image_name, nb_days, result_list):
389
    """Does some stats and outputs them into some data files and a gnuplot script
390
    that one might run later.
391
    """
392
393
    (stats, module_list) = do_gnuplot_stats(result_list)
394
    print_gnuplot(image_name, nb_days, stats, module_list)
395
396
# End of output_gnuplot() function
397
398
399
def main():
400
    """Here we choose what to do upon the command line's options.
401
    """
402
403
    # Parsing options
404
    my_opts = Options()
405
406
    (nb_days, result_list) = read_files(my_opts.files)
407
408
    if len(result_list) > 1:
409
410
        if my_opts.out == 'stat':
411
            output_stats(nb_days, result_list)
412
413
        # We do not want to generate an image if the number of day usage is less than one !
414
        elif my_opts.out == 'gnuplot' and nb_days > 0:
415
            output_gnuplot(my_opts.image, nb_days, result_list)
416
417
if __name__ == "__main__":
418
    main()
419