root/src/modules-lua/noit/module/haproxy.lua

Revision 7a6be888d4962ef932ca3bd3e2fa529fe08cc698, 5.9 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 4 years ago)

Graham Barr offered this up a long time back.. how did it not make it into the repo?

  • Property mode set to 100644
Line 
1 -- Copyright (c) 2010, Graham Barr
2 --
3 -- Permission is hereby granted, free of charge, to any person
4 -- obtaining a copy of this software and associated documentation
5 -- files (the "Software"), to deal in the Software without
6 -- restriction, including without limitation the rights to use,
7 -- copy, modify, merge, publish, distribute, sublicense, and/or sell
8 -- copies of the Software, and to permit persons to whom the
9 -- Software is furnished to do so, subject to the following
10 -- conditions:
11 --
12 -- The above copyright notice and this permission notice shall be
13 -- included in all copies or substantial portions of the Software.
14 --
15 -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 -- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17 -- OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 -- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19 -- HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 -- WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 -- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 -- OTHER DEALINGS IN THE SOFTWARE.
23
24 -- This connects to a haproxy instance
25 -- It issues a stat commands and translates the output into metrics
26
27 module(..., package.seeall)
28
29 function onload(image)
30   image.xml_description([=[
31 <module>
32   <name>haproxy</name>
33   <description><para>Monitor management metrics of a haproxy instance.</para></description>
34   <loader>lua</loader>
35   <object>noit.module.haproxy</object>
36   <moduleconfig />
37   <checkconfig>
38     <parameter name="uri" required="required" default="/admin?stats;csv" allowed="^/.+">
39       The URL excluding schema and hostname for the haproxy stats CSV export.
40       eg /admin?stats;csv
41     </parameter>
42     <parameter name="host" required="optional" allowed=".+">
43       Host name to include in HTTP request, defaults to target IP
44     </parameter>
45     <parameter name="port" required="optional" default="80" allowed="^[0-9]+$">
46       Port to connect to
47     </parameter>
48     <parameter name="select" required="optional" default=".*" allowed=".+">
49       Specifies a regular expression to pick which metrics to report. Will be matched
50       against the pxname and svname columns concatenated by a ","
51     </parameter>
52     <parameter name="auth_user" required="optional" allowed="[^:]*">
53       The user to authenticate as.
54     </parameter>
55     <parameter name="auth_password" required="optional" allowed=".*">
56       The password to use during authentication.
57     </parameter>
58   </checkconfig>
59   <examples>
60     <example>
61       <title>Monitor two haproxy instances</title>
62       <para>The following example pulls all metrics available from haproxy running on 10.1.2.3 and 10.1.2.4</para>
63       <programlisting><![CDATA[
64       <noit>
65         <modules>
66           <loader image="lua" name="lua">
67             <config><directory>/opt/reconnoiter/libexec/modules-lua/?.lua</directory></config>
68           </loader>
69           <module loader="lua" name="haproxy" object="noit.module.haproxy"/>
70         </modules>
71         <checks>
72           <check uuid="2d42adbc-7c7a-11dd-a48f-4f59e0b654d3" module="haproxy" target="10.1.2.3" />
73           <check uuid="324c2234-7c7a-11dd-8585-cbb783f8267f" module="haproxy" target="10.1.2.4" />
74         </checks>
75       </noit>
76       ]]></programlisting>
77     </example>
78   </examples>
79 </module>
80 ]=]);
81   return 0
82 end
83
84 function init(module)
85   return 0
86 end
87
88 function config(module, options)
89   return 0
90 end
91
92 local HttpClient = require 'noit.HttpClient'
93
94
95 function initiate(module, check)
96   local host = check.config.host or check.target
97   local port = check.config.port or 80
98   local uri  = check.config.uri or "/admin?stats;csv"
99
100   -- expect the worst
101   check.bad()
102   check.unavailable()
103
104   -- build request headers
105   local headers = {}
106   headers.Host = host
107   for header, value in pairs(check.config) do
108     hdr = string.match(header, '^header_(.+)$')
109     if hdr ~= nil then
110       headers[hdr] = value
111     end
112   end
113
114   if check.config.auth_user ~= nil then
115     local user = check.config.auth_user;
116     local password = check.config.auth_password or ''
117     local encoded = noit.base64_encode(user .. ':' .. password)
118     headers["Authorization"] = "Basic " .. encoded
119   end
120
121   -- gather output from HttpClient
122   local output = ''
123   local callbacks = { }
124   callbacks.consume = function (str)
125     output = output .. (str or '')
126   end
127
128   local client = HttpClient:new(callbacks)
129   local rv, err = client:connect(check.target, port, false)
130  
131   if rv ~= 0 then error(err or "unknown error") end
132
133   client:do_request('GET', uri, headers, nil)
134   client:get_response();
135
136   check.available()
137
138   if client.code == nil or client.code ~= 200 then error("HTTP response " .. tostring(code)) end
139   if not output:find('^# .*\n.*\n') then error("Invalid CSV '" .. string.sub(output,1,10) .. "'...") end
140
141   local hdr      = {}
142   local state    = 0
143   local pos      = 3
144   local column   = 0
145   local selectre = noit.pcre(check.config.select or '.*')
146   local rowname  = ''
147   local count    = 0
148  
149   local is_string        = {}
150   is_string.status       = 1
151   is_string.check_status = 1
152   is_string.qlimit       = 1
153   is_string.throttle     = 1
154
155   while 1 do
156     local nextpos, char = output:match('()([,\n])', pos)
157     if nextpos then
158       local field = output:sub(pos, nextpos - 1)
159       if column == 0 then -- pxname
160         rowname = field
161       elseif column == 1 then -- svname
162         rowname = rowname .. "`" .. field
163       elseif state == 0 then -- collecting header line
164         hdr[column] = field
165       elseif selectre == nil or selectre(rowname) then
166         local cname = rowname .. "`" .. hdr[column]
167         if is_string[hdr[column]] then
168           check.metric(cname,field)
169         elseif field ~= '' then
170           check.metric_uint64(cname,field)
171         end
172       end
173       if char == '\n' then
174         state  = 1
175         column = 0
176         count  = count + 1
177       else
178         column = column + 1
179       end
180       pos = nextpos + 1
181     else
182       break     
183     end
184   end
185
186   check.status(string.format("%d stats", count))
187   check.good()
188 end
Note: See TracBrowser for help on using the browser.