SymuxClient.pm 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. #
  2. # Copyright (c) 2001-2010 Willem Dijkstra
  3. # All rights reserved.
  4. #
  5. # Redistribution and use in source and binary forms, with or without
  6. # modification, are permitted provided that the following conditions
  7. # are met:
  8. #
  9. # - Redistributions of source code must retain the above copyright
  10. # notice, this list of conditions and the following disclaimer.
  11. # - Redistributions in binary form must reproduce the above
  12. # copyright notice, this list of conditions and the following
  13. # disclaimer in the documentation and/or other materials provided
  14. # with the distribution.
  15. #
  16. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  17. # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  18. # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  19. # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  20. # COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  21. # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  22. # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  23. # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  24. # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  25. # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  26. # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  27. # POSSIBILITY OF SUCH DAMAGE.
  28. #
  29. package SymuxClient;
  30. use Carp;
  31. use IO::Socket;
  32. my $streamitem =
  33. {cpu => {user => 1, nice => 2, system => 3, interrupt => 4, idle => 5},
  34. cpuiow => {user => 1, nice => 2, system => 3, interrupt => 4, idle => 5, iowait => 6},
  35. mem => {real_active => 1, real_total => 2, free => 3, swap_used => 4,
  36. swap_total =>5},
  37. if => {packets_in => 1, packets_out => 2, bytes_in => 3, bytes_out => 4,
  38. multicasts_in => 5, multicasts_out => 6, errors_in => 7,
  39. errors_out => 8, collisions => 9, drops => 10},
  40. io1 => {total_transfers => 1, total_seeks => 2, total_bytes => 3},
  41. pf => {bytes_v4_in => 1, bytes_v4_out => 2, bytes_v6_in => 3,
  42. bytes_v6_out => 4, packets_v4_in_pass => 5,
  43. packets_v4_in_drop => 6, packets_v4_out_pass => 7,
  44. packets_v4_out_drop => 8, packets_v6_in_pass => 9,
  45. packets_v6_in_drop => 10, packets_v6_out_pass => 11,
  46. packets_v6_out_drop => 12, states_entries => 13,
  47. states_searches => 14, states_inserts => 15,
  48. states_removals => 16, counters_match => 17,
  49. counters_badoffset => 18, counters_fragment => 19,
  50. counters_short => 20, counters_normalize => 21,
  51. counters_memory => 22},
  52. debug => {debug0 => 1, debug1 => 2, debug3 => 3, debug4 => 4, debug5 => 5,
  53. debug6 => 6, debug7 => 7, debug8 => 8, debug9 => 9,
  54. debug10 => 10, debug11 => 11, debug12 => 12, debug13 => 13,
  55. debug14 => 14, debug15 => 15, debug16 => 16, debug17 => 17,
  56. debug18 => 18, debug19 => 19},
  57. proc => {number => 1, uticks => 2, sticks => 3, iticks => 4, cpusec => 5,
  58. cpupct => 6, procsz => 7, rsssz => 8},
  59. mbuf => {totmbufs => 1, mt_data => 2, mt_oobdata => 3, mt_control => 4,
  60. mt_header => 5, mt_ftable => 6, mt_soname => 7, mt_soopts => 8,
  61. pgused => 9, pgtotal => 10, totmem => 11, totpct => 12,
  62. m_drops => 13, m_wait => 14, m_drain => 15 },
  63. sensor => {value => 1},
  64. io => {total_rxfers => 1, total_wxfers => 2, total_seeks => 3,
  65. total_rbytes => 4, total_wbytes => 5 },
  66. pfq => {sent_bytes => 1, sent_packets => 2, drop_bytes => 3,
  67. drop_packets => 4},
  68. df => {blocks => 1, bfree => 2, bavail => 3, files => 4, ffree => 5,
  69. syncwrites => 6, asyncwrites => 7},
  70. smart => {read_error_rate => 1, reallocated_sectors => 2, spin_retries => 3,
  71. air_flow_temp => 4, temperature => 5, reallocations => 6,
  72. current_pending => 7, uncorrectables => 8,
  73. soft_read_error_rate => 9, g_sense_error_rate => 10,
  74. temperature2 => 10, free_fall_protection => 11},
  75. load => {load1 => 1, load5 => 2, load15 => 3}
  76. };
  77. sub new {
  78. my ($class, %arg) = @_;
  79. my $self;
  80. (defined $arg{host} && defined $arg{port}) or
  81. croak "error: need a host and a port to connect to.";
  82. ($self->{host}, $self->{port}) = ($arg{host}, $arg{port});
  83. $self->{retry} = (defined $arg{retry}? $arg{retry} : 10);
  84. bless($self, $class);
  85. $self->connect();
  86. return $self;
  87. }
  88. sub DESTROY {
  89. my $self = shift;
  90. if (defined $self->{sock}) {
  91. close($self->{sock});
  92. }
  93. }
  94. sub connect {
  95. my $self = shift;
  96. if (defined $self->{sock} && $self->{sock}->connected() ne '') {
  97. return;
  98. } else {
  99. close($self->{sock});
  100. }
  101. $self->{sock} = new
  102. IO::Socket::INET(PeerAddr => $self->{host},
  103. PeerPort => $self->{port},
  104. Proto => "tcp",
  105. Type => SOCK_STREAM)
  106. or croak "error: could not connect to $self->{host}:$self->{port}";
  107. }
  108. sub getdata {
  109. my $self = shift;
  110. my $sock;
  111. my $data;
  112. my $tries;
  113. $tries = 0;
  114. while (1) {
  115. $self->connect();
  116. $sock = $self->{sock};
  117. $data = <$sock>;
  118. if ((index($data, "\012") != -1) && (index($data, ';') != -1)) {
  119. $self->{rawdata} = $data;
  120. return $data;
  121. } else {
  122. croak "error: tried to get data $tries times and failed"
  123. if (++$tries == $self->{retry});
  124. }
  125. }
  126. }
  127. sub parse {
  128. my $self = shift;
  129. my ($stream, @streams, $name, $arg, @data, $number);
  130. $self->getdata() if ! defined $self->{rawdata};
  131. $number = 0;
  132. undef $self->{data};
  133. undef $self->{datasource};
  134. chop $self->{rawdata}; # remove ';\n'
  135. chop $self->{rawdata};
  136. @streams = split(/;/, $self->{rawdata});
  137. croak "error: expected a symux dataline with ';' delimited streams"
  138. if ($#streams < 1);
  139. $self->{datasource} = shift @streams;
  140. foreach $stream (@streams) {
  141. ($name, $arg, @data) = split(':', $stream);
  142. croak "error: expected a symux stream with ':' delimited values"
  143. if ($#data < 1);
  144. $name .= '('.$arg.')' if ($arg ne '');
  145. @{$self->{data}{$name}} = @data;
  146. $number++;
  147. }
  148. $self->{rawdata} = '';
  149. return $number;
  150. }
  151. sub getcacheditem {
  152. my ($self, $host, $streamname, $item) = @_;
  153. my ($streamtype, @addr, $element);
  154. return undef if not defined $self->{datasource};
  155. return undef if (($host ne $self->{datasource}) &&
  156. ($host ne "*"));
  157. croak "error: source $host does not contain stream $streamname"
  158. if not defined $self->{data}{$streamname};
  159. ($streamtype = $streamname) =~ s/^([a-z]+).*/\1/;
  160. if ($item eq 'timestamp') {
  161. $element = 0;
  162. } elsif (not defined $$streamitem{$streamtype}{$item}) {
  163. croak "error: unknown stream item '$item' - check symux(8) for names";
  164. } else {
  165. $element = $$streamitem{$streamtype}{$item};
  166. }
  167. return $self->{data}{$streamname}[$element];
  168. }
  169. sub getitem {
  170. my ($self, $host, $streamname, $item) = @_;
  171. my $data;
  172. my %hosts = ();
  173. undef $data;
  174. while (! defined $data) {
  175. $self->getdata();
  176. $self->parse();
  177. $hosts{$self->{datasource}} += 1;
  178. if ($hosts{$self->{datasource}} > $self->{retry}) {
  179. croak "error: seen a lot of data ($tries strings), but none that matches $host";
  180. }
  181. $data = $self->getcacheditem($host, $streamname, $item);
  182. return $data if defined $data;
  183. $tries++;
  184. }
  185. }
  186. sub source {
  187. my $self = shift;
  188. return $self->{datasource};
  189. }
  190. 1;
  191. __END__
  192. =head1 NAME
  193. SymuxClient - Object client interface for symux
  194. =head1 SYNOPSIS
  195. use SymuxClient;
  196. =head1 DESCRIPTION
  197. C<SymuxClient> provides an object client interface for listening to, and
  198. parsing of, symux data.
  199. =head1 CONSTRUCTOR
  200. =over 4
  201. =item new ( ARGS )
  202. Creates a new C<SymuxClient> object that holds both the connection to a symux
  203. and data it receives from that connection. Arguments are:
  204. host dotted quad ipv4 hostaddress
  205. port tcp port that symux is on
  206. retry* maximum number of retries; used to limit number
  207. of connection attempts and number of successive
  208. read attempts
  209. Arguments marked with * are optional.
  210. Example:
  211. $sc = new SymuxClient(host => '127.0.0.1',
  212. port => 2100);
  213. =back
  214. =head2 METHODS
  215. =item getitem (host, stream, item)
  216. Refresh the measured data and get an item from a stream for a particular
  217. host. Note that successive calls for this function deal with successive
  218. measurements of B<symon>. Set C<host> to '*' if data about any host is of
  219. interest. Any errors are sent out on STDOUT prepended with 'error: '.
  220. =item getcacheditem (host, stream, item)
  221. Get an item from a stream for a particular host. Returns C<undef> if no data is
  222. cached, or if the data cached does not match the B<host>. Can be called
  223. multiple times to obtain items from the same measurement. Set C<host> to '*' if
  224. data about any host is of interest. Any errors are sent out on STDOUT prepended
  225. with 'error: '.
  226. =item getsource ()
  227. Get the symon source host of the currently cached information. Usefull to see
  228. what host's data getcacheditem is working on.
  229. Example:
  230. $sc = new SymuxClient(host => 127.0.0.1,
  231. port => 2100);
  232. print $sc->getitem("127.0.0.1", "if(de0)",
  233. "packets_out");
  234. # get more data from that measurement
  235. print $sc->getcacheditem("127.0.0.1","if(de0)",
  236. "packets_in");
  237. # start a new measurement
  238. print $sc->getitem("*", "if(de0)",
  239. "packets_out");
  240. # which hosts packets_out was that?
  241. print $sc->getsource();
  242. =cut