Completed
Branch master (9e7cac)
by dup
01:18 queued 20s
created

get_stats_from_module()   A

Complexity

Conditions 2

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

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