32_SYSSTAT.pm 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987
  1. # $Id: 32_SYSSTAT.pm 10567 2016-01-18 21:34:09Z justme1968 $
  2. package main;
  3. use strict;
  4. use warnings;
  5. use Data::Dumper;
  6. my $SYSSTAT_hasSysStatistics = 1;
  7. my $SYSSTAT_hasSNMP = 1;
  8. my %SYSSTAT_diskTypes = (
  9. ".1.3.6.1.2.1.25.2.1.1" => 'Other',
  10. ".1.3.6.1.2.1.25.2.1.2" => 'Ram',
  11. ".1.3.6.1.2.1.25.2.1.3" => 'VirtualMemory',
  12. ".1.3.6.1.2.1.25.2.1.4" => 'FixedDisk',
  13. ".1.3.6.1.2.1.25.2.1.5" => 'RemovableDisk',
  14. ".1.3.6.1.2.1.25.2.1.6" => 'FloppyDisk',
  15. ".1.3.6.1.2.1.25.2.1.7" => 'CompactDisk',
  16. ".1.3.6.1.2.1.25.2.1.8" => 'RamDisk',
  17. ".1.3.6.1.2.1.25.2.1.9" => 'FlashMemory',
  18. ".1.3.6.1.2.1.25.2.1.10" => 'NetworkDisk',
  19. );
  20. sub
  21. SYSSTAT_Initialize($)
  22. {
  23. my ($hash) = @_;
  24. eval "use Sys::Statistics::Linux::LoadAVG";
  25. $SYSSTAT_hasSysStatistics = 0 if($@);
  26. eval "use Sys::Statistics::Linux::DiskUsage";
  27. $SYSSTAT_hasSysStatistics = 0 if($@);
  28. eval "use Net::SNMP";
  29. $SYSSTAT_hasSNMP = 0 if($@);
  30. $hash->{DefFn} = "SYSSTAT_Define";
  31. $hash->{UndefFn} = "SYSSTAT_Undefine";
  32. $hash->{GetFn} = "SYSSTAT_Get";
  33. $hash->{AttrFn} = "SYSSTAT_Attr";
  34. $hash->{AttrList} = "disable:1 disabledForIntervals raspberrycpufreq:1 raspberrytemperature:0,1,2 synologytemperature:0,1,2 stat:1 uptime:1,2 ssh_user ";
  35. $hash->{AttrList} .= " snmp:1 mibs:textField-long snmpVersion:1,2 snmpCommunity" if( $SYSSTAT_hasSNMP );
  36. $hash->{AttrList} .= " filesystems showpercent";
  37. $hash->{AttrList} .= " useregex:1" if( $SYSSTAT_hasSysStatistics );
  38. $hash->{AttrList} .= " $readingFnAttributes";
  39. }
  40. #####################################
  41. sub
  42. SYSSTAT_Define($$)
  43. {
  44. my ($hash, $def) = @_;
  45. my @a = split("[ \t][ \t]*", $def);
  46. return "Usage: define <name> SYSSTAT [interval [interval_fs [host]]]" if(@a < 2);
  47. my $interval = 60;
  48. if(int(@a)>=3) { $interval = $a[2]; }
  49. if( $interval < 60 ) { $interval = 60; }
  50. my $interval_fs = $interval * 60;
  51. if(int(@a)>=4) { $interval_fs = $a[3]; }
  52. if( $interval_fs < $interval ) { $interval_fs = $interval; }
  53. if( $interval_fs == $interval ) { $interval_fs = undef; }
  54. my $host = $a[4] if(int(@a)>=5);;
  55. delete( $hash->{INTERVAL_FS} );
  56. delete( $hash->{HOST} );
  57. $hash->{"HAS_Sys::Statistics"} = $SYSSTAT_hasSysStatistics;
  58. $hash->{"HAS_Net::SNMP"} = $SYSSTAT_hasSNMP;
  59. $hash->{STATE} = "Initialized";
  60. $hash->{INTERVAL} = $interval;
  61. $hash->{INTERVAL_FS} = $interval_fs if( defined( $interval_fs ) );
  62. $hash->{HOST} = $host if( defined( $host ) );
  63. $hash->{interval_fs} = $interval_fs;
  64. SYSSTAT_InitSys( $hash ) if( $SYSSTAT_hasSysStatistics );
  65. SYSSTAT_InitSNMP( $hash ) if( $SYSSTAT_hasSNMP );
  66. RemoveInternalTimer($hash);
  67. InternalTimer(gettimeofday()+$hash->{INTERVAL}, "SYSSTAT_GetUpdate", $hash, 0);
  68. return undef;
  69. }
  70. sub
  71. SYSSTAT_InitSys($)
  72. {
  73. my ($hash) = @_;
  74. return if( !$SYSSTAT_hasSysStatistics );
  75. if( defined($hash->{HOST}) ) {
  76. my $cmd = qx(which ssh);
  77. chomp( $cmd );
  78. my $user = AttrVal($hash->{NAME}, "ssh_user", undef );
  79. $cmd .= ' ';
  80. $cmd .= $user."\@" if( defined($user) );
  81. $cmd .= $hash->{HOST}." df -kP 2>/dev/null";
  82. $hash->{loadavg} = Sys::Statistics::Linux::LoadAVG->new;
  83. $hash->{diskusage} = Sys::Statistics::Linux::DiskUsage->new( cmd => { path => '',
  84. df => $cmd } );
  85. } else {
  86. $hash->{loadavg} = Sys::Statistics::Linux::LoadAVG->new;
  87. $hash->{diskusage} = Sys::Statistics::Linux::DiskUsage->new;
  88. }
  89. }
  90. sub
  91. SYSSTAT_InitSNMP($)
  92. {
  93. my ($hash) = @_;
  94. my $name = $hash->{NAME};
  95. delete( $hash->{session} );
  96. return if( !$SYSSTAT_hasSNMP );
  97. my $host = "localhost";
  98. my $community = "public";
  99. $host = $hash->{HOST} if( defined($hash->{HOST} ) );
  100. my ( $session, $error ) = Net::SNMP->session(
  101. -hostname => $host,
  102. -community => AttrVal($name,"snmpCommunity","public"),
  103. -port => 161,
  104. -version => AttrVal($name,"snmpVersion",1),
  105. );
  106. if( $error ) {
  107. Log3 $name, 2, "$name: $error";
  108. } elsif ( !defined($session) ) {
  109. Log3 $name, 2, "$name: can't connect to host $host";
  110. } else {
  111. $session->timeout(3);
  112. $hash->{session} = $session;
  113. }
  114. }
  115. sub
  116. SYSSTAT_Undefine($$)
  117. {
  118. my ($hash, $arg) = @_;
  119. RemoveInternalTimer($hash);
  120. return undef;
  121. }
  122. sub
  123. SYSSTAT_Get($@)
  124. {
  125. my ($hash, @a) = @_;
  126. my $name = $a[0];
  127. return "$name: get needs at least one parameter" if(@a < 2);
  128. my $cmd= $a[1];
  129. if($cmd eq "filesystems") {
  130. my $ret;
  131. if( $hash->{USE_SNMP} && defined($hash->{session}) ) {
  132. my $types = SYSSTAT_readOIDs($hash,".1.3.6.1.2.1.25.2.3.1.2");
  133. my $response = SYSSTAT_readOIDs($hash,".1.3.6.1.2.1.25.2.3.1.3");
  134. foreach my $oid ( sort { ($a =~/\.(\d+)$/)[0] <=> ($b =~/\.(\d+)$/)[0]} keys %$response ) {
  135. $ret .= "\n" if( $ret );
  136. my $id = ($oid =~/\.(\d+)$/)[0];
  137. $ret .= $id ." <= ". $response->{$oid} ." (". $SYSSTAT_diskTypes{$types->{".1.3.6.1.2.1.25.2.3.1.2.$id"}} .")";
  138. }
  139. $ret = "<id> => <filesystem>\n$ret" if( $ret );
  140. } elsif(defined($hash->{diskusage})) {
  141. my $filesystems = $hash->{diskusage}->get;
  142. $ret = "<filesystem> <= <mountpoint>";
  143. foreach my $filesystem (keys %$filesystems ) {
  144. $ret .= "\n" if( $ret );
  145. $ret .= $filesystem ." <= ". $filesystems->{$filesystem}->{mountpoint};
  146. }
  147. }
  148. return $ret;
  149. } elsif( $cmd eq "update" ) {
  150. $hash->{LOCAL} = 1;
  151. SYSSTAT_GetUpdate( $hash );
  152. delete $hash->{LOCAL};
  153. return;
  154. }
  155. return "Unknown argument $cmd, choose one of update:noArg filesystems:noArg";
  156. }
  157. sub
  158. SYSSTAT_Attr($$$)
  159. {
  160. my ($cmd, $name, $attrName, $attrVal) = @_;
  161. $attrVal= "" unless defined($attrVal);
  162. my $orig = $attrVal;
  163. $attrVal = "1" if($attrName eq "snmp");
  164. $attrVal = "1" if($attrName eq "useregex");
  165. $attrVal = "1" if($attrName eq "showpercent");
  166. $attrVal = "1" if($attrName eq "raspberrycpufreq");
  167. my $hash = $defs{$name};
  168. if( $attrName eq "filesystems") {
  169. my @filesystems = split(",",$attrVal);
  170. @{$hash->{filesystems}} = @filesystems;
  171. } elsif( $attrName eq "ssh_user") {
  172. $attr{$name}{$attrName} = $attrVal;
  173. SYSSTAT_InitSys( $hash ) if( $SYSSTAT_hasSysStatistics );
  174. } elsif( $attrName eq "snmpVersion" && $SYSSTAT_hasSNMP ) {
  175. $hash->{$attrName} = $attrVal;
  176. SYSSTAT_InitSNMP( $hash );
  177. } elsif( $attrName eq "snmpCommunity" && $SYSSTAT_hasSNMP ) {
  178. $hash->{$attrName} = $attrVal;
  179. SYSSTAT_InitSNMP( $hash );
  180. } elsif ($attrName eq "snmp" && $SYSSTAT_hasSNMP ) {
  181. if( $cmd eq "set" && $attrVal ne "0" ) {
  182. $hash->{USE_SNMP} = $attrVal;
  183. SYSSTAT_InitSNMP( $hash );
  184. } else {
  185. delete $hash->{USE_SNMP};
  186. }
  187. }
  188. if( $cmd eq "set" ) {
  189. if( $orig ne $attrVal ) {
  190. $attr{$name}{$attrName} = $attrVal;
  191. return $attrName ." set to ". $attrVal;
  192. }
  193. }
  194. return;
  195. }
  196. sub SYSSTAT_getLoadAVG($);
  197. sub SYSSTAT_getPiTemp($);
  198. sub SYSSTAT_getUptime($);
  199. sub
  200. SYSSTAT_GetUpdate($)
  201. {
  202. my ($hash) = @_;
  203. my $name = $hash->{NAME};
  204. if(!$hash->{LOCAL}) {
  205. RemoveInternalTimer($hash);
  206. InternalTimer(gettimeofday()+$hash->{INTERVAL}, "SYSSTAT_GetUpdate", $hash, 0);
  207. return if( IsDisabled($name) > 0 );
  208. }
  209. my $load = SYSSTAT_getLoadAVG( $hash );
  210. readingsBeginUpdate($hash);
  211. my $state = undef;
  212. $state = $load->{state} if( defined($load->{state}) );
  213. $state = $load->{avg_1} . " " . $load->{avg_5} . " " . $load->{avg_15} if( !$state && defined($load->{avg_1}) );
  214. readingsBulkUpdate($hash,"state",$state);
  215. readingsBulkUpdate($hash,"load",$load->{avg_1});
  216. my $do_diskusage = 1;
  217. if( defined($hash->{INTERVAL_FS} ) ) {
  218. $do_diskusage = 0;
  219. $hash->{interval_fs} -= $hash->{INTERVAL};
  220. if( $hash->{interval_fs} <= 0 ) {
  221. $do_diskusage = 1;
  222. $hash->{interval_fs} += $hash->{INTERVAL_FS};
  223. }
  224. }
  225. if( $do_diskusage
  226. && $#{$hash->{filesystems}} >= 0 ) {
  227. if( $hash->{USE_SNMP} && defined($hash->{session}) ) {
  228. my $showpercent = AttrVal($name, "showpercent", "") ne "";
  229. my @snmpoids = ();
  230. for my $id (@{$hash->{filesystems}}) {
  231. push @snmpoids, sprintf( ".1.3.6.1.2.1.25.2.3.1.3.%i", $id );
  232. push @snmpoids, sprintf( ".1.3.6.1.2.1.25.2.3.1.4.%i", $id ) if( !$showpercent );
  233. push @snmpoids, sprintf( ".1.3.6.1.2.1.25.2.3.1.5.%i", $id );
  234. push @snmpoids, sprintf( ".1.3.6.1.2.1.25.2.3.1.6.%i", $id );
  235. }
  236. my $response = SYSSTAT_readOIDs($hash,\@snmpoids);
  237. if( $response ) {
  238. for my $id (@{$hash->{filesystems}}) {
  239. my $unit = $response->{sprintf( ".1.3.6.1.2.1.25.2.3.1.4.%i", $id )};
  240. my $free = $response->{sprintf( ".1.3.6.1.2.1.25.2.3.1.5.%i", $id )} - $response->{sprintf( ".1.3.6.1.2.1.25.2.3.1.6.%i", $id )};
  241. if( $showpercent ) {
  242. $free = 100 * $response->{sprintf( ".1.3.6.1.2.1.25.2.3.1.6.%i", $id )} / $response->{sprintf( ".1.3.6.1.2.1.25.2.3.1.5.%i", $id )};
  243. $free = sprintf( "%.1f", $free );
  244. } else {
  245. $free *= $unit;
  246. }
  247. my $name = $response->{sprintf( ".1.3.6.1.2.1.25.2.3.1.3.%i", $id )};
  248. if( $name =~ m/^([[:alpha:]]:\\)/ ) {
  249. $name = $1;
  250. $name =~ s.\\./.g;
  251. } else {
  252. $name =~ s/ //g;
  253. }
  254. readingsBulkUpdate($hash,$name,$free);
  255. }
  256. }
  257. } elsif( defined($hash->{diskusage} ) ) {
  258. my $usage = $hash->{diskusage}->get;
  259. my $type = 'free';
  260. if( AttrVal($name, "showpercent", "") ne "" ) {
  261. $type = 'usageper';
  262. }
  263. if( AttrVal($name, "useregex", "") eq "" ) {
  264. for my $filesystem (@{$hash->{filesystems}}) {
  265. my $fs = $usage->{$filesystem};
  266. readingsBulkUpdate($hash,$fs->{mountpoint},$fs->{$type});
  267. }
  268. } else {
  269. for my $filesystem (@{$hash->{filesystems}}) {
  270. foreach my $key (keys %$usage) {
  271. if( $key =~ /$filesystem/ ) {
  272. my $fs = $usage->{$key};
  273. readingsBulkUpdate($hash,$fs->{mountpoint},$fs->{$type});
  274. }
  275. }
  276. }
  277. }
  278. }
  279. }
  280. if( AttrVal($name, "raspberrytemperature", 0) > 0 ) {
  281. my $temp = SYSSTAT_getPiTemp($hash);
  282. if( $temp > 0 && $temp < 200 ) {
  283. if( AttrVal($name, "raspberrytemperature", 0) eq 2 ) {
  284. $temp = sprintf( "%.1f", (3 * ReadingsVal($name,"temperature",$temp) + $temp ) / 4 );
  285. }
  286. readingsBulkUpdate($hash,"temperature",$temp);
  287. }
  288. } elsif( AttrVal($name, "synologytemperature", 0) > 0 ) {
  289. my $temp = SYSSTAT_getSynoTemp($hash);
  290. if( $temp && $temp > 0 && $temp < 200 ) {
  291. if( AttrVal($name, "synologytemperature", 0) eq 2 ) {
  292. $temp = sprintf( "%.1f", (3 * ReadingsVal($name,"temperature",$temp) + $temp ) / 4 );
  293. }
  294. readingsBulkUpdate($hash,"temperature",$temp);
  295. }
  296. }
  297. if( $hash->{USE_SNMP} && defined($hash->{session}) ) {
  298. if( my $mibs = AttrVal($name, "mibs", undef) ) {
  299. my @snmpoids;
  300. foreach my $entry (split(/[ ,\n]/, $mibs)) {
  301. next if( !$entry );
  302. my($mib,undef) = split(':', $entry );
  303. next if( !$mib );
  304. push @snmpoids, $mib;
  305. }
  306. my $response = SYSSTAT_readOIDs($hash,\@snmpoids);
  307. foreach my $entry (split(/[ ,\n]/, $mibs)) {
  308. next if( !$entry );
  309. my($mib,$reading) = split(':', $entry );
  310. next if( !$mib );
  311. next if( !$reading );
  312. my $result = $response->{$mib};
  313. readingsBulkUpdate($hash,$reading,$result);
  314. }
  315. }
  316. }
  317. if( AttrVal($name, "raspberrycpufreq", 0) > 0 ) {
  318. my $freq = SYSSTAT_getPiFreq($hash);
  319. readingsBulkUpdate($hash,"cpufreq",$freq);
  320. }
  321. if( AttrVal($name, "stat", 0) > 0 ) {
  322. my @percent = SYSSTAT_getStat($hash);
  323. if( @percent ) {
  324. #my($user,$nice,$system,$idle,$iowait,$irq,$softirq,$steal,$guest,$guest_nice) = @percent;
  325. readingsBulkUpdate($hash,"user", $percent[0]);
  326. readingsBulkUpdate($hash,"system", $percent[2]);
  327. readingsBulkUpdate($hash,"idle", $percent[3]);
  328. readingsBulkUpdate($hash,"iowait", $percent[4]);
  329. }
  330. }
  331. if( AttrVal($name, "uptime", 0) > 0 ) {
  332. my $uptime = SYSSTAT_getUptime($hash);
  333. readingsBulkUpdate($hash,"uptime",$uptime);
  334. }
  335. readingsEndUpdate($hash,defined($hash->{LOCAL} ? 0 : 1));
  336. }
  337. sub
  338. SYSSTAT_getLoadAVG($ )
  339. {
  340. my ($hash) = @_;
  341. my $name = $hash->{NAME};
  342. if( $hash->{USE_SNMP} && defined($hash->{session}) ) {
  343. my @snmpoids = ( ".1.3.6.1.4.1.2021.10.1.3.1", ".1.3.6.1.4.1.2021.10.1.3.2", ".1.3.6.1.4.1.2021.10.1.3.3" );
  344. my $response = SYSSTAT_readOIDs($hash,\@snmpoids);
  345. if( !$response ) {
  346. my $response = SYSSTAT_readOIDs($hash,".1.3.6.1.2.1.25.3.3.1.2");
  347. my $avg = "";
  348. my %lavg = ();
  349. my $load = 0;
  350. foreach my $key (keys %$response) {
  351. $avg .= "," if( $avg ne "" );
  352. $avg .= $response->{$key};
  353. $load += $response->{$key} / 100;
  354. }
  355. $lavg{avg_1} = $load if( $avg ne "" );
  356. $lavg{state} = $avg if( $avg ne "" );
  357. return \%lavg;
  358. }
  359. my %lavg = ();
  360. $lavg{avg_1} = $response->{".1.3.6.1.4.1.2021.10.1.3.1"};
  361. $lavg{avg_5} = $response->{".1.3.6.1.4.1.2021.10.1.3.2"};
  362. $lavg{avg_15} = $response->{".1.3.6.1.4.1.2021.10.1.3.3"};
  363. return \%lavg;
  364. }
  365. return undef if( !defined($hash->{loadavg}) );
  366. if( defined($hash->{HOST}) ) {
  367. no strict;
  368. no warnings 'redefine';
  369. local *Sys::Statistics::Linux::LoadAVG::get = sub {
  370. my $self = shift;
  371. my $class = ref $self;
  372. my $file = $self->{files};
  373. my %lavg = ();
  374. my $cmd = qx(which ssh);
  375. chomp( $cmd );
  376. my $user = AttrVal($hash->{NAME}, "ssh_user", undef );
  377. $cmd .= ' ';
  378. $cmd .= $user."\@" if( defined($user) );
  379. $cmd .= $hash->{HOST}." cat /proc/loadavg 2>/dev/null";
  380. my $fh;
  381. if( open($fh, "$cmd|" ) ) {
  382. ( $lavg{avg_1}
  383. , $lavg{avg_5}
  384. , $lavg{avg_15}
  385. ) = (split /\s+/, <$fh>)[0..2];
  386. close($fh);
  387. }
  388. return \%lavg;
  389. };
  390. return $hash->{loadavg}->get;
  391. }
  392. return $hash->{loadavg}->get;
  393. }
  394. sub
  395. SYSSTAT_readOIDs($$)
  396. {
  397. my ($hash,$snmpoids) = @_;
  398. my $name = $hash->{NAME};
  399. return undef if( !defined($hash->{session}) );
  400. my $response;
  401. if( ref($snmpoids) eq "ARRAY" ) {
  402. $response = $hash->{session}->get_request( @{$snmpoids} );
  403. Log3 $name, 4, "$name: got empty result from snmp query ".$hash->{session}->error() if( !$response );
  404. } else {
  405. $response = $hash->{session}->get_next_request($snmpoids);
  406. my @snmpoids = ();
  407. my @nextid = keys %$response;
  408. while ( @nextid && $nextid[0] && $nextid[0] =~ m/^$snmpoids/ ) {
  409. push( @snmpoids, $nextid[0] );
  410. $response = $hash->{session}->get_next_request( $nextid[0] );
  411. @nextid = keys %$response;
  412. }
  413. $response = $hash->{session}->get_request( @snmpoids );
  414. #Log3 $name, 4, "$name: got empty result from snmp query ".$hash->{session}->error() if( !$response );
  415. }
  416. return $response;
  417. }
  418. sub
  419. SYSSTAT_readCmd($$$)
  420. {
  421. my ($hash,$command,$default) = @_;
  422. my $value = $default;
  423. if( defined($hash->{HOST}) ) {
  424. my $cmd = qx(which ssh);
  425. chomp( $cmd );
  426. my $user = AttrVal($hash->{NAME}, "ssh_user", undef );
  427. $cmd .= ' ';
  428. $cmd .= $user."\@" if( defined($user) );
  429. $cmd .= $hash->{HOST}." $command 2>/dev/null";
  430. if( open(my $fh, "$cmd|" ) ) {
  431. $value = <$fh>;
  432. close($fh);
  433. }
  434. } else {
  435. if( open( my $fh, "$command|" ) )
  436. {
  437. $value = <$fh>;
  438. close($fh);
  439. }
  440. }
  441. return $value;
  442. }
  443. sub
  444. SYSSTAT_readFile($$$)
  445. {
  446. my ($hash,$filename,$default) = @_;
  447. my $value = $default;
  448. if( defined($hash->{HOST}) ) {
  449. my $cmd = qx(which ssh);
  450. chomp( $cmd );
  451. my $user = AttrVal($hash->{NAME}, "ssh_user", undef );
  452. $cmd .= ' ';
  453. $cmd .= $user."\@" if( defined($user) );
  454. $cmd .= $hash->{HOST}." cat ". $filename ." 2>/dev/null";
  455. if( open(my $fh, "$cmd|" ) ) {
  456. $value = <$fh>;
  457. close($fh);
  458. }
  459. } else {
  460. if( open( my $fh, '<', $filename ) )
  461. {
  462. $value = <$fh>;
  463. close($fh);
  464. }
  465. }
  466. return $value;
  467. }
  468. sub
  469. SYSSTAT_getPiTemp($)
  470. {
  471. my ($hash) = @_;
  472. my $temp = SYSSTAT_readFile($hash,"/sys/class/thermal/thermal_zone0/temp",-1);
  473. return $temp / 1000;
  474. }
  475. sub
  476. SYSSTAT_getSynoTemp($)
  477. {
  478. my ($hash) = @_;
  479. my $temp = -1;
  480. if( $hash->{USE_SNMP} && defined($hash->{session}) ) {
  481. my @snmpoids = ( ".1.3.6.1.4.1.6574.1.2.0" );
  482. my $response = SYSSTAT_readOIDs($hash,\@snmpoids);
  483. $temp = $response->{".1.3.6.1.4.1.6574.1.2.0"};
  484. }
  485. return $temp;
  486. }
  487. sub
  488. SYSSTAT_getPiFreq($)
  489. {
  490. my ($hash) = @_;
  491. my $freq = SYSSTAT_readFile($hash,"/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq",0);
  492. return $freq / 1000;
  493. }
  494. sub
  495. SYSSTAT_getUptime($)
  496. {
  497. my ($hash) = @_;
  498. my $name = $hash->{NAME};
  499. if( $hash->{USE_SNMP} && defined($hash->{session}) ) {
  500. my @snmpoids = ( ".1.3.6.1.2.1.25.1.1.0" );
  501. my $response = SYSSTAT_readOIDs($hash,\@snmpoids);
  502. my $uptime = $response->{".1.3.6.1.2.1.25.1.1.0"};
  503. if( AttrVal($name, "uptime", 0) == 2 ) {
  504. if( $uptime && $uptime =~ m/(\d+)\s\D+,\s(\d+):(\d+):(\d+)/ ) {
  505. my $days = $1?$1:0;
  506. my $hours = $2;
  507. my $minutes = $3;
  508. my $seconds = $4;
  509. $uptime = $days * 24;
  510. $uptime += $hours;
  511. $uptime *= 60;
  512. $uptime += $minutes;
  513. $uptime *= 60;
  514. $uptime += $seconds;
  515. }
  516. }
  517. return $uptime;
  518. }
  519. my $uptime = SYSSTAT_readFile($hash,"/proc/uptime","");
  520. if($uptime) {
  521. $uptime = $1 if ( $uptime && $uptime =~ /^\s*([0-9.]+)\s+([0-9.]+)/ );
  522. if( AttrVal($name, "uptime", 0) != 2 ) {
  523. # cut off partial seconds
  524. $uptime = int( $uptime );
  525. my $seconds = $uptime % 60;
  526. $uptime = int($uptime / 60);
  527. my $minutes = $uptime % 60;
  528. $uptime = int($uptime / 60);
  529. my $hours = $uptime % 24;
  530. my $days = int($uptime / 24);
  531. $uptime = sprintf( "%d days, %d:%.2d", $days, $hours, $minutes);
  532. Log3 $name, 4, "$name: uptime returned :$uptime: via proc-uptime file "; # JVI
  533. }
  534. # fallback if by any reason parsing /proc/uptime does not work
  535. } else {
  536. my $uptime = SYSSTAT_readCmd($hash,"uptime",0);
  537. ############# match uptime time statement with the different formats seen on linux
  538. # examples
  539. # 18:52:21 up 26 days, 21:08, 2 users, load average: 0.04, 0.03, 0.05
  540. # 18:52:21 up 26 days, 55 min, 1 user, load average: 0.05, 0.05, 0.05
  541. # 18:52:21 up 55 min, 1 user, load average: 0.05, 0.05, 0.05
  542. # 18:52:21 up 21:08, 1 user, load average: 0.05, 0.05, 0.05
  543. #
  544. # complex expression to match only the time parts of the uptime result
  545. # $1 is complete up time information of uptime result
  546. # $2 is # days part of the uptime
  547. # $3 just the # from the "# days"" part or nothing if no days are given
  548. # $4 is complete hour/minutes or # min information
  549. # $5 is hours part if hours:min are given
  550. # $6 is minutes part if hours:min are given
  551. # $7 is minutes if # min is given
  552. $uptime = $1 if ( $uptime && $uptime =~ m/[[:alpha:]]{2}\s*(((\d*)\s*[[:alnum:]]*,?)?\s+((\d+):(\d+)|(\d+)\s+[[:alpha:]]+in[[:alpha:]]*)),?/ );
  553. $uptime = "0 days, $uptime" if( $uptime && !$2);
  554. if( AttrVal($name, "uptime", 0) == 2 ) {
  555. my $days = $3?$3:0;
  556. my $hours = $5?$5:0;
  557. my $minutes = $6?$6:$7;
  558. $uptime = $days * 24;
  559. $uptime += $hours;
  560. $uptime *= 60;
  561. $uptime += $minutes;
  562. $uptime *= 60;
  563. }
  564. Log3 $name, 4, "$name: uptime returned :$uptime: via cmdline "; # JVI
  565. }
  566. return $uptime;
  567. }
  568. sub
  569. SYSSTAT_getStat($)
  570. {
  571. my ($hash) = @_;
  572. my $name = $hash->{NAME};
  573. if( $hash->{USE_SNMP} && defined($hash->{session}) ) {
  574. my @snmpoids = ( ".1.3.6.1.4.1.2021.11.9.0", ".1.3.6.1.4.1.2021.11.10.0", ".1.3.6.1.4.1.2021.11.11.0" );
  575. my $response = SYSSTAT_readOIDs($hash,\@snmpoids);
  576. my @percent = ( $response->{".1.3.6.1.4.1.2021.11.9.0"}, # user
  577. undef,
  578. $response->{".1.3.6.1.4.1.2021.11.10.0"}, # system
  579. $response->{".1.3.6.1.4.1.2021.11.11.0"} ); # idle
  580. return @percent;
  581. }
  582. my $line = SYSSTAT_readFile($hash,"/proc/stat","");
  583. #my($user,$nice,$system,$idle,$iowait,$irq,$softirq,$steal,$guest,$guest_nice) = split( " ", $Line );
  584. my($dummy,@values) = split( " ", $line );
  585. if( !defined($hash->{values}) ) {
  586. $hash->{values} = \@values;
  587. return undef;
  588. } else {
  589. my @diff = map { $values[$_] - $hash->{values}->[$_] } 0 .. $#values;
  590. $hash->{values} = \@values;
  591. my $sum = 0;
  592. $sum += $_ for @diff;
  593. my @percent = map { int($diff[$_]*1000 / $sum)/10 } 0 .. $#values;
  594. return @percent;
  595. }
  596. }
  597. 1;
  598. =pod
  599. =item device
  600. =begin html
  601. <a name="SYSSTAT"></a>
  602. <h3>SYSSTAT</h3>
  603. <ul>
  604. Provides system statistics for the host FHEM runs on or a remote Linux system that is reachable by preconfigured passwordless ssh access.<br><br>
  605. Notes:
  606. <ul>
  607. <li>This module needs <code>Sys::Statistics::Linux</code> on Linux.<br>
  608. It can be installed with '<code>cpan install Sys::Statistics::Linux</code>'<br>
  609. or on debian with '<code>apt-get install libsys-statistics-linux-perl</code>'</li>
  610. <li>To monitor a target by snmp <code>Net::SNMP</code> hast to be installed.<br></li>
  611. <li>To plot the load values the following code can be used:
  612. <PRE>
  613. define sysstatlog FileLog /usr/local/FHEM/var/log/sysstat-%Y-%m.log sysstat
  614. attr sysstatlog nrarchive 1
  615. define svg_sysstat SVG sysstatlog:sysstat:CURRENT
  616. attr wl_sysstat label "Load Min: $data{min1}, Max: $data{max1}, Aktuell: $data{currval1}"
  617. attr wl_sysstat room System
  618. </PRE></li>
  619. <li>to match the root filesystem (mount point '/') in diskusage plots use
  620. '<code>#FileLog 4:/\x3a:0:</code>' or '<code>#FileLog 4:\s..\s:0:</code>'
  621. and <b>not</b> '<code>#FileLog 4:/:0:</code>' as the later will match all mount points</li>.
  622. </ul>
  623. <a name="SYSSTAT_Define"></a>
  624. <b>Define</b>
  625. <ul>
  626. <code>define &lt;name&gt; SYSSTAT [&lt;interval&gt; [&lt;interval_fs&gt;] [&lt;host&gt;]]</code><br>
  627. <br>
  628. Defines a SYSSTAT device.<br><br>
  629. The load is updated every &lt;interval&gt; seconds. The default and minimum is 60.<br><br>
  630. The diskusage is updated every &lt;interval_fs&gt; seconds. The default is &lt;interval&gt;*60 and the minimum is 60.
  631. &lt;interval_fs&gt; is only aproximated and works best if &lt;interval_fs&gt; is an integral multiple of &lt;interval&gt;.<br><br>
  632. If &lt;host&gt; is given it has to be accessible by ssh without the need for a password.
  633. Examples:
  634. <ul>
  635. <code>define sysstat SYSSTAT</code><br>
  636. <code>define sysstat SYSSTAT 300</code><br>
  637. <code>define sysstat SYSSTAT 60 600</code><br>
  638. </ul>
  639. </ul><br>
  640. <a name="SYSSTAT_Readings"></a>
  641. <b>Readings</b>
  642. <ul>
  643. <li>load<br>
  644. the 1 minute load average (for windows targets monitored by snmp aproximated value</li>
  645. <li>state<br>
  646. the 1, 5 and 15 minute load averages (or windows targets monitored by snmp the per cpu utilization)</li>
  647. <li>user,system,idle,iowait<br>
  648. respective percentage of systemutilization (linux targets only)</li>
  649. <li>&lt;mountpoint&gt;<br>
  650. free bytes for &lt;mountpoint&gt;</li>
  651. </ul><br>
  652. <a name="SYSSTAT_Get"></a>
  653. <b>Get</b>
  654. <ul>
  655. <code>get &lt;name&gt; &lt;value&gt;</code>
  656. <br><br>
  657. where <code>value</code> is one of<br><br>
  658. <li>filesystems<br>
  659. Lists the filesystems that can be monitored.</li>
  660. </ul><br>
  661. <a name="SYSSTAT_Attr"></a>
  662. <b>Attributes</b>
  663. <ul>
  664. <li>disable<br>
  665. keep timers running but disable collection of statistics.</li>
  666. <li>filesystems<br>
  667. List of comma separated filesystems (not mountpoints) that should be monitored.<br>
  668. Examples:
  669. <ul>
  670. <code>attr sysstat filesystems /dev/md0,/dev/md2</code><br>
  671. <code>attr sysstat filesystems /dev/.*</code><br>
  672. <code>attr sysstat filesystems 1,3,5</code><br>
  673. </ul></li></lu>
  674. <li>disabledForIntervals HH:MM-HH:MM HH:MM-HH-MM...</li>
  675. <li>mibs<br>
  676. space separated list of &lt;mib&gt;:&lt;reding&gt; pairs that sould be polled.</li>
  677. <li>showpercent<br>
  678. If set the usage is shown in percent. If not set the remaining free space in bytes is shown.</li>
  679. <li>snmp<br>
  680. 1 -> use snmp to monitor load, uptime and filesystems (including physical and virtual memory)</li>
  681. <li>stat<br>
  682. 1 -> monitor user,system,idle and iowait percentage of system utilization (available only for linux targets)</li>
  683. <li>raspberrytemperature<br>
  684. If set and > 0 the raspberry pi on chip termal sensor is read.<br>
  685. If set to 2 a geometric average over the last 4 values is created.</li>
  686. <li>synologytemperature<br>
  687. If set and > 0 the main temperaure of a synology diskstation is read. requires snmp.<br>
  688. If set to 2 a geometric average over the last 4 values is created.</li>
  689. <li>raspberrycpufreq<br>
  690. If set and > 0 the raspberry pi on chip termal sensor is read.</li>
  691. <li>uptime<br>
  692. If set and > 0 the system uptime is read.<br>
  693. If set to 2 the uptime is displayed in seconds.</li>
  694. <li>useregex<br>
  695. If set the entries of the filesystems list are treated as regex.</li>
  696. <li>ssh_user<br>
  697. The username for ssh remote access.</li>
  698. <li>snmpVersion</li>
  699. <li>snmpCommunity</li>
  700. <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
  701. </ul>
  702. </ul>
  703. =end html
  704. =begin html_DE
  705. <a name="SYSSTAT"></a>
  706. <h3>SYSSTAT</h3>
  707. <ul>
  708. Das Modul stellt Systemstatistiken f&uuml;r den Rechner, auf dem FHEM l&auml;uft bzw.
  709. f&uuml;r ein entferntes Linux System, das per vorkonfiguriertem ssh Zugang ohne Passwort
  710. erreichbar ist, zur Vef&uuml;gung.<br><br>
  711. Notes:
  712. <ul>
  713. <li>Dieses Modul ben&ouml;tigt <code>Sys::Statistics::Linux</code> f&uuml;r Linux.<br>
  714. Es kann mit '<code>cpan install Sys::Statistics::Linux</code>'<br>
  715. bzw. auf Debian mit '<code>apt-get install libsys-statistics-linux-perl</code>'
  716. installiert werden.</li>
  717. <li>Um einen Zielrechner mit snmp zu &uuml;berwachen, muss
  718. <code>Net::SNMP</code> installiert sein.<br></li>
  719. <li>Um die Lastwerte zu plotten, kann der folgende Code verwendet werden:
  720. <pre>
  721. define sysstatlog FileLog /usr/local/FHEM/var/log/sysstat-%Y-%m.log sysstat
  722. attr sysstatlog nrarchive 1
  723. define svg_sysstat SVG sysstatlog:sysstat:CURRENT
  724. attr wl_sysstat label "Load Min: $data{min1}, Max: $data{max1}, Aktuell: $data{currval1}"
  725. attr wl_sysstat room System
  726. </pre></li>
  727. <li>Um das Wurzel-Dateisystem (Mountpunkt '/') bei Plots der Plattennutzung zu erhalten,
  728. sollte dieser Code '<code>#FileLog 4:/\x3a:0:</code>' bzw. '<code>#FileLog 4:\s..\s:0:</code>'
  729. und <b>nicht</b> dieser Code '<code>#FileLog 4:/:0:</code>' verwendet werden, da der letztere
  730. alle Mountpunkte darstellt.</li>.
  731. </ul>
  732. <a name="SYSSTAT_Define"></a>
  733. <b>Define</b>
  734. <ul>
  735. <code>define &lt;name&gt; SYSSTAT [&lt;interval&gt; [&lt;interval_fs&gt;] [&lt;host&gt;]]</code><br>
  736. <br>
  737. definiert ein SYSSTAT Device.<br><br>
  738. Die (Prozessor)last wird alle &lt;interval&gt; Sekunden aktualisiert. Standard bzw. Minimum ist 60.<br><br>
  739. Die Plattennutzung wird alle &lt;interval_fs&gt; Sekunden aktualisiert. Standardwert ist &lt;interval&gt;*60
  740. und Minimum ist 60.
  741. &lt;interval_fs&gt; wird nur angen&auml;hert und funktioniert am Besten, wenn &lt;interval_fs&gt;
  742. ein ganzzahliges Vielfaches von &lt;interval&gt; ist.<br><br>
  743. Wenn &lt;host&gt; angegeben wird, muss der Zugang per ssh ohne Passwort m&ouml;glich sein.<br><br>
  744. Beispiele:
  745. <ul>
  746. <code>define sysstat SYSSTAT</code><br>
  747. <code>define sysstat SYSSTAT 300</code><br>
  748. <code>define sysstat SYSSTAT 60 600</code>
  749. </ul>
  750. </ul><br>
  751. <a name="SYSSTAT_Readings"></a>
  752. <b>Readings</b>
  753. <ul>
  754. <li>load<br>
  755. die durchschnittliche (Prozessor)last der letzten 1 Minute (f&uuml;r Windows Rechner mit
  756. snmp angen&auml;hertem Wert)</li>
  757. <li>state<br>
  758. die durchschnittliche (Prozessor)last der letzten 1, 5 und 15 Minuten (f&uuml;r Windows
  759. Rechner die Nutzung pro CPU via snmp ermittelt)</li>
  760. <li>user, system, idle, iowait<br>
  761. den Prozentsatz der entsprechenden Systemlast (nur f&uuml;r Linux Systeme)</li>
  762. <li>&lt;mountpoint&gt;<br>
  763. Anzahl der freien Bytes f&uuml;r &lt;mountpoint&gt;</li>
  764. </ul><br>
  765. <a name="SYSSTAT_Get"></a>
  766. <b>Get</b>
  767. <ul>
  768. <code>get &lt;name&gt; &lt;value&gt;</code>
  769. <br><br>
  770. Werte f&uuml;r <code>value</code> sind<br><br>
  771. <li>filesystems<br>
  772. zeigt die Dateisysteme an, die &uuml;berwacht werden k&ouml;nnen.</li>
  773. </ul><br>
  774. <a name="SYSSTAT_Attr"></a>
  775. <b>Attributes</b>
  776. <ul>
  777. <li>disable<br>
  778. l&auml;sst die Timer weiterlaufen, aber stoppt die Speicherung der Daten.</li>
  779. <li>filesystems<br>
  780. Liste mit Komma getrennten Dateisystemen (nicht Mountpunkten) die &uuml;berwacht
  781. werden sollen.<br>
  782. Beispiele:
  783. <ul>
  784. <code>attr sysstat filesystems /dev/md0,/dev/md2</code><br>
  785. <code>attr sysstat filesystems /dev/.*</code><br>
  786. <code>attr sysstat filesystems 1,3,5</code><br>
  787. </ul></li>
  788. <li>mibs<br>
  789. Leerzeichen getrennte Liste aus &lt;mib&gt;:&lt;reding&gt; Paaren die abgefragt werden sollen.</li>
  790. <li>showpercent<br>
  791. Wenn gesetzt, wird die Nutzung in Prozent angegeben. Wenn nicht gesetzt, wird der verf&uuml;bare
  792. Platz in Bytes angezeigt.</li>
  793. <li>snmp<br>
  794. 1 -> snmp wird verwendet, um Last, Einschaltzeit und Dateisysteme (inkl. physikalischem und
  795. virtuellem Speicher) zu &uuml;berwachen</li>
  796. <li>stat<br>
  797. 1 -> &uuml;berwacht Prozentsatz der user, system, idle und iowait Last
  798. (nur auf Linux Systemen verf&uuml;gbar)</li>
  799. <li>raspberrytemperature<br>
  800. Wenn gesetzt und > 0 wird der Temperatursensor auf dem Raspberry Pi ausgelesen.<br>
  801. Wenn Wert 2 ist, wird ein geometrischer Durchschnitt der letzten 4 Werte dargestellt.</li>
  802. <li>synologytemperature<br>
  803. Wenn gesetzt und > 0 wird die Temperatur einer Synology Diskstation ausgelesen (erfordert snmp).<br>
  804. Wenn Wert 2 ist, wird ein geometrischer Durchschnitt der letzten 4 Werte dargestellt.</li>
  805. <li>raspberrycpufreq<br>
  806. Wenn gesetzt und > 0 wird die Raspberry Pi CPU Frequenz ausgelesen.</li>
  807. <li>uptime<br>
  808. Wenn gesetzt und > 0 wird die Betriebszeit (uptime) des Systems ausgelesen.<br>
  809. Wenn Wert 2 ist, wird die Betriebszeit (uptime) in Sekunden angezeigt.</li>
  810. <li>useregex<br>
  811. Wenn Wert gesetzt, werden die Eintr&auml;ge der Dateisysteme als regex behandelt.</li>
  812. <li>ssh_user<br>
  813. Der Username f&uuml;r den ssh Zugang auf dem entfernten Rechner.</li>
  814. <li>snmpVersion</li>
  815. <li>snmpCommunity</li>
  816. <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
  817. </ul>
  818. </ul>
  819. =end html_DE
  820. =cut