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 3 3 100.0
total 116 165 70.3


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