File Coverage

blib/lib/Flexnet/lmutil.pm
Criterion Covered Total %
statement 77 98 78.5
branch 28 52 53.8
condition 2 5 40.0
subroutine 6 7 85.7
pod 0 3 0.0
total 113 165 68.4


line stmt bran cond sub pod time code
1             package Flexnet::lmutil;
2              
3 2     2   41237 use 5.006;
  2         8  
4 2     2   12 use strict;
  2         3  
  2         42  
5 2     2   9 use warnings;
  2         7  
  2         63  
6 2     2   1434 use File::Which;
  2         1893  
  2         4308  
7              
8             =head1 NAME
9              
10             Flexnet::lmutil - Convenient OO-interface for Flexnet license server utility lmutil
11              
12             =head1 VERSION
13              
14             Version 1.3
15              
16             =cut
17              
18             our $VERSION = '1.3';
19              
20             =head1 DESCRIPTION
21              
22             Flexnet::lmutil is a small wrapper around the Flexnet license server utility lmutil,
23             currently implementing the sub-functions lmstat and lmremove. The module parses the
24             output of lmstat and returns an easy-to-use data structure. This makes it easy to
25             work further with lmstat output for e.g. web pages, monitoring plugins etc.
26              
27              
28             =head1 SYNOPSIS
29              
30             use Flexnet::lmutil;
31              
32             my $lmutil = new Flexnet::lmutil (
33             lm_license_path => 'port@host',
34             ...
35            
36             );
37              
38             $status = $lmutil->lmstat (
39             feature => 'feature',
40            
41             OR
42            
43             daemon => 'daemon',
44            
45             OR
46            
47             'all'
48             );
49              
50             $lmutil->lmremove (
51             feature => 'feature',
52             serverhost => 'host',
53             port => 'port',
54             handle => 'handle'
55             );
56              
57             =head1 DETAILS
58              
59             Possible arguments for the constructor are:
60              
61             =over 4
62              
63             =item C
64              
65             either the full pathname of the license file or the string C
66             or even C...
67              
68             =item C
69              
70             show command line call
71              
72             =item C
73              
74             textfile containing lmstat output (for testing), does not run lmstat
75              
76             =back
77              
78             Possible arguments for C are:
79              
80             =over 4
81              
82             =item C
83              
84             get info about feature usage
85              
86             =item C
87              
88             get info about daemon usage
89              
90             =item C
91              
92             get info about usage of all daemons and features
93              
94             =back
95              
96             C returns a hash reference with the following keys:
97              
98             =over 4
99              
100             =item * C
101              
102             =item * C
103              
104             =item * C
105              
106             =back
107              
108             B points to another structure like
109              
110             'server' => {
111             'elba.uni-paderborn.de' => {
112             'ok' => 1,
113             'status' => 'UP'
114             }
115             },
116              
117             B points to a structure like
118              
119             'vendor' => {
120             'cdslmd' => {
121             'ok' => 1,
122             'status' => 'UP v11.11',
123             'version' => '11.11'
124             }
125             }
126              
127             B points to a structure like
128              
129             'feature' => {
130             'MATLAB' => {
131             'reservations' => [
132             {
133             'reservations' => '1',
134             'group' => 'etechnik-labor',
135             'type' => 'HOST_GROUP'
136             }
137             ],
138             'issued' => '115',
139             'used' => '36',
140             'users' => [
141             {
142             'serverhost' => 'dabu.uni-paderborn.de',
143             'startdate' => 'Wed 8/12 17:18',
144             'port' => '27000',
145             'licenses' => 1,
146             'display' => 'bessel',
147             'host' => 'bessel',
148             'handle' => '4401',
149             'user' => 'hangmann'
150             },
151             ]
152             },
153             },
154             ...
155              
156             =head1 AUTHOR
157              
158             Christopher Odenbach, C<< >>
159              
160             =head1 BUGS
161              
162             Please report any bugs or feature requests to C, or through
163             the web interface at L. I will be notified, and then you'll
164             automatically be notified of progress on your bug as I make changes.
165              
166              
167              
168              
169             =head1 SUPPORT
170              
171             You can find documentation for this module with the perldoc command.
172              
173             perldoc Flexnet::lmutil
174              
175              
176             You can also look for information at:
177              
178             =over 4
179              
180             =item * RT: CPAN's request tracker (report bugs here)
181              
182             L
183              
184             =item * AnnoCPAN: Annotated CPAN documentation
185              
186             L
187              
188             =item * CPAN Ratings
189              
190             L
191              
192             =item * Search CPAN
193              
194             L
195              
196             =back
197              
198              
199             =head1 ACKNOWLEDGEMENTS
200              
201              
202             =head1 LICENSE AND COPYRIGHT
203              
204             Copyright 2015 Christopher Odenbach.
205              
206             This program is free software; you can redistribute it and/or modify it
207             under the terms of either: the GNU General Public License as published
208             by the Free Software Foundation; or the Artistic License.
209              
210             See http://dev.perl.org/licenses/ for more information.
211              
212              
213             =cut
214              
215             sub new {
216 1     1 0 12 my $pkg = shift;
217 1         5 my %args = @_;
218              
219 1   50     9 my $lmutil = ($args{lmutil} or which ('lmutil') or '');
220              
221 1 0 33     399 if (not defined $args{testfile} and not -x $lmutil ) {
222 0         0 die "lmutil not executable\n";
223             }
224              
225 1         6 my $self = {
226             lmutil => $lmutil,
227             %args
228             };
229 1         5 return bless ($self, $pkg);
230             }
231              
232             sub lmstat {
233 1     1 0 6 my $self = shift;
234            
235 1         4 my @args = @_;
236            
237 1 50       7 if (@args == 1) {
238 1         3 push @args, 1;
239             }
240 1         3 my %args = @args;
241              
242 1         2 my ($feature, $status);
243              
244 1         7 my $cmd = "$self->{lmutil} lmstat";
245              
246 1 50       5 if ( defined ($self->{lm_license_path})) {
247 0         0 $cmd .= " -c $self->{lm_license_path}";
248             }
249              
250 1 50       4 if ( defined ($args{all}) ) {
    0          
    0          
251 1         2 $cmd .= " -a";
252             } elsif ( defined ($args{feature}) ) {
253 0         0 $cmd .= " -f $args{feature}";
254             } elsif ( defined ($args{daemon}) ) {
255 0         0 $cmd .= " -S $args{daemon}";
256             }
257            
258             # for testing purpose we can provide a text file with the output of lmstat
259 1         2 my $fh;
260 1 50       4 if ( defined ($self->{testfile}) ) {
261 1 50       36 open ($fh, $self->{testfile}) or die "Could not open $self->{testfile}: $!";
262             } else {
263 0 0       0 print "Running command: $cmd\n" if $self->{verbose};
264 0         0 open ($fh, "$cmd |");
265             }
266            
267 1         23 while (<$fh>) {
268 98 50       271 print "lmstat: $_" if $self->{verbose};
269            
270             # lmgrd status
271 98 100       1032 if ( my ($server, $server_status) = /^\s*([\w.-]+): license server (\S+)/ ) {
    100          
    100          
    100          
    100          
272 1         5 $status->{server}->{$server}->{status} = $server_status;
273 1 50       5 if ( $server_status eq "UP" ) {
274 1         11 $status->{server}->{$server}->{ok} = 1;
275             } else {
276 0         0 $status->{server}->{$server}->{ok} = 0;
277             }
278            
279             # vendor daemon status
280             } elsif ( my ($vendor, $state) = /^\s*(\S+?): (.*)/ ) {
281 3         11 $status->{vendor}->{$vendor}->{status} = $state;
282 3 50       12 if ( $state =~ /^UP v([\d.]+)/ ) {
283 3         11 $status->{vendor}->{$vendor}->{ok} = 1;
284 3         23 $status->{vendor}->{$vendor}->{version} = $1;
285             } else {
286 0         0 $status->{vendor}->{$vendor}->{ok} = 0;
287             }
288            
289             # feature usage info
290             } elsif ( /^Users of (\S+):\s*\(Total of (\d+) licenses? issued;\s*Total of (\d+) lic/ ) {
291 11         27 $feature = $1;
292 11         18 my $issued = $2;
293 11         19 my $used = $3;
294 11         37 $status->{feature}->{$feature}->{issued} = $issued;
295 11         73 $status->{feature}->{$feature}->{used} = $used;
296            
297             # user info
298             } elsif ( my ($clientinfo, $version, $serverhost, $port, $handle, $rest) =
299             m{^\s+(.+) \(v([\d\.]+)\) \(([^/]+)/(\d+) (\d+)\), start (.*)} ) {
300            
301 36         47 my ($user, $host);
302 36         51 my $display = '';
303            
304             # split clientinfo
305            
306 36         96 my @parts = split / /, $clientinfo;
307 36 100       118 if (@parts == 2) {
    100          
308 4         8 ($user, $host) = @parts;
309             } elsif (@parts == 3) {
310 28         53 ($user, $host, $display) = @parts;
311             } else {
312 4         7 my $max = @parts;
313            
314             # host = display?
315 4 100       13 if ($parts[$max - 2] eq $parts[$max - 1]) {
316 1         3 $display = pop @parts;
317 1         2 $host = pop @parts;
318 1         3 $user = join (' ', @parts);
319             } else {
320             # display contains / or : ?
321 3         4 my $i = 2;
322 3         8 while ($i <= @parts) {
323 3 50       13 if ($parts[$i] =~ m{^[:/]}) {
324 3         5 $display = $parts[$i];
325 3         5 $host = $parts[$i - 1];
326 3         7 $user = join (' ', map { $parts[$_] } 0..$i-2);
  3         10  
327 3         22 last;
328             }
329 0         0 $i++;
330             }
331            
332             # if still no luck, just guess
333 3 50       11 unless (defined $user) {
334 0         0 ($user, $host, $display) = @parts;
335             }
336             }
337             }
338            
339             # starttime and optional number of licenses
340 36         55 my $startdate;
341 36         45 my $licenses = 1;
342 36 100       73 if ($rest =~ /^([^,]+), (\d+) license/) {
343 1         4 $startdate = $1;
344 1         2 $licenses = $2;
345             } else {
346 35         59 $startdate = $rest;
347             }
348            
349 36         44 push @{$status->{feature}->{$feature}->{users}}, {
  36         483  
350             user=>$user,
351             host=>$host,
352             display=>$display,
353             licenses=>$licenses,
354             serverhost=>$serverhost,
355             port=>$port,
356             handle=>$handle,
357             startdate=>$startdate
358             };
359            
360             # reservation info
361             } elsif ( my ($reservations, $type, $group) = /^\s+(\d+)\s+RESERVATIONs? for ([\w_]+) ([\w_-]+) / ) {
362 4         5 push @{$status->{feature}->{$feature}->{reservations}},
  4         40  
363             {type=>$type, group=>$group, reservations=>$reservations};
364             }
365            
366             }
367 1         8 close ($fh);
368            
369 1         7 return $status;
370             }
371              
372             sub lmremove {
373 0     0 0   my $self = shift;
374 0           my %args = @_;
375 0           my $cmd;
376            
377 0           foreach my $arg (qw (feature serverhost port handle)) {
378 0 0         die "Parameter '$arg' missing\n" unless $args{$arg};
379             }
380            
381 0           $cmd = "$self->{lmutil} lmremove";
382 0 0         if ( defined ($self->{lm_license_path})) {
383 0           $cmd .= " -c $self->{lm_license_path}";
384             }
385 0           $cmd .= " -h $args{feature} $args{serverhost} $args{port} $args{handle}";
386              
387 0 0         print "Running command: $cmd\n" if $self->{verbose};
388 0           system($cmd);
389             }
390              
391              
392              
393              
394             1; # End of Flexnet::lmutil