Commit 15ce4ad9 authored by Pavel Vainerman's avatar Pavel Vainerman

(netdata-plugin): Вариант на основе запуска uniset2-admin

parent 527661d2
...@@ -52,6 +52,7 @@ static struct option longopts[] = ...@@ -52,6 +52,7 @@ static struct option longopts[] =
{ "oinfo", required_argument, 0, 'p' }, { "oinfo", required_argument, 0, 'p' },
{ "verbose", no_argument, 0, 'v' }, { "verbose", no_argument, 0, 'v' },
{ "quiet", no_argument, 0, 'q' }, { "quiet", no_argument, 0, 'q' },
{ "csv", required_argument, 0, 'z' },
{ NULL, 0, 0, 0 } { NULL, 0, 0, 0 }
}; };
...@@ -117,6 +118,7 @@ static void usage() ...@@ -117,6 +118,7 @@ static void usage()
print_help(36, "-t|--getChangedTime id1@node1,id2@node2,id3,.. ", "Получить время последнего изменения.\n"); print_help(36, "-t|--getChangedTime id1@node1,id2@node2,id3,.. ", "Получить время последнего изменения.\n");
print_help(36, "-v|--verbose", "Подробный вывод логов.\n"); print_help(36, "-v|--verbose", "Подробный вывод логов.\n");
print_help(36, "-q|--quiet", "Выводит только результат.\n"); print_help(36, "-q|--quiet", "Выводит только результат.\n");
print_help(36, "-z|--csv", "Вывести результат (getValue) в виде val1,val2,val3...\n");
cout << endl; cout << endl;
} }
...@@ -129,6 +131,7 @@ static void usage() ...@@ -129,6 +131,7 @@ static void usage()
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
static bool verb = false; static bool verb = false;
static bool quiet = false; static bool quiet = false;
static bool csv = false;
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
...@@ -141,7 +144,7 @@ int main(int argc, char** argv) ...@@ -141,7 +144,7 @@ int main(int argc, char** argv)
while(1) while(1)
{ {
opt = getopt_long(argc, argv, "hc:beosfur:l:i::x:g:w:y:p:vq", longopts, &optindex); opt = getopt_long(argc, argv, "hc:beosfur:l:i::x:g:w:y:p:vqz:", longopts, &optindex);
if( opt == -1 ) if( opt == -1 )
break; break;
...@@ -190,7 +193,10 @@ int main(int argc, char** argv) ...@@ -190,7 +193,10 @@ int main(int argc, char** argv)
break; break;
case 'g': //--getValue case 'g': //--getValue
case 'z': //--csv
{ {
if( opt == 'z' )
csv = true;
// cout<<"(main):received option --getValue='"<<optarg<<"'"<<endl; // cout<<"(main):received option --getValue='"<<optarg<<"'"<<endl;
auto conf = uniset_init(argc, argv, conffile); auto conf = uniset_init(argc, argv, conffile);
UInterface ui(conf); UInterface ui(conf);
...@@ -666,9 +672,14 @@ int getValue( const string& args, UInterface& ui ) ...@@ -666,9 +672,14 @@ int getValue( const string& args, UInterface& ui )
auto conf = ui.getConf(); auto conf = ui.getConf();
auto sl = UniSetTypes::getSInfoList( args, conf ); auto sl = UniSetTypes::getSInfoList( args, conf );
if( csv )
quiet = true;
if( !quiet ) if( !quiet )
cout << "====== getValue ======" << endl; cout << "====== getValue ======" << endl;
size_t num = 0;
for( auto && it : sl ) for( auto && it : sl )
{ {
try try
...@@ -694,8 +705,19 @@ int getValue( const string& args, UInterface& ui ) ...@@ -694,8 +705,19 @@ int getValue( const string& args, UInterface& ui )
if( !quiet ) if( !quiet )
cout << " value: " << ui.getValue(it.si.id, it.si.node) << endl; cout << " value: " << ui.getValue(it.si.id, it.si.node) << endl;
else else
cout << ui.getValue(it.si.id, it.si.node); {
if( csv )
{
// т.к. может сработать исключение, а нам надо вывести ','
// до числа, то сперва получаем val
long val = ui.getValue(it.si.id, it.si.node);
if( csv && num++ > 0 )
cout << ",";
cout << val;
}
else
cout << ui.getValue(it.si.id, it.si.node);
}
break; break;
default: default:
......
...@@ -11,6 +11,7 @@ ln -s -f admin.sh omap ...@@ -11,6 +11,7 @@ ln -s -f admin.sh omap
ln -s -f admin.sh msgmap ln -s -f admin.sh msgmap
ln -s -f admin.sh setValue ln -s -f admin.sh setValue
ln -s -f admin.sh getValue ln -s -f admin.sh getValue
ln -s -f admin.sh csv
ln -s -f admin.sh getRawValue ln -s -f admin.sh getRawValue
ln -s -f admin.sh getChangedTime ln -s -f admin.sh getChangedTime
ln -s -f admin.sh getCalibrate ln -s -f admin.sh getCalibrate
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#%def_enable modbus #%def_enable modbus
%def_disable tests %def_disable tests
%def_disable mqtt %def_disable mqtt
%def_enable netdata
%define oname uniset2 %define oname uniset2
...@@ -62,6 +63,10 @@ BuildRequires: librrd-devel ...@@ -62,6 +63,10 @@ BuildRequires: librrd-devel
BuildRequires: libmosquitto-devel BuildRequires: libmosquitto-devel
%endif %endif
%if_enabled netdata
BuildRequires: netdata
%endif
%if_enabled python %if_enabled python
BuildRequires: python-devel python-module-distribute BuildRequires: python-devel python-module-distribute
BuildRequires(pre): rpm-build-python BuildRequires(pre): rpm-build-python
...@@ -118,6 +123,16 @@ Requires: %name = %version-%release ...@@ -118,6 +123,16 @@ Requires: %name = %version-%release
Python interface for %name Python interface for %name
%endif %endif
%if_enabled netdata
%package netdata-plugin
Group: Development/Tools
Summary: python plugin for netdata
Requires: python-module-%oname
%description netdata-plugin
python plugin for netdata
%endif
%package utils %package utils
Summary: UniSet utilities Summary: UniSet utilities
Group: Development/Tools Group: Development/Tools
...@@ -385,6 +400,12 @@ mv -f %buildroot%python_sitelibdir_noarch/* %buildroot%python_sitelibdir/%oname ...@@ -385,6 +400,12 @@ mv -f %buildroot%python_sitelibdir_noarch/* %buildroot%python_sitelibdir/%oname
%python_sitelibdir/%oname/ %python_sitelibdir/%oname/
%endif %endif
%if_enabled netdata
%files netdata-plugin
%_libdir/netdata/python.d/*.*
%config(noreplace) %_sysconfdir/netdata/python.d/*.conf
%endif
%if_enabled docs %if_enabled docs
%files docs %files docs
%_docdir/%oname/ %_docdir/%oname/
......
...@@ -5653,7 +5653,7 @@ ...@@ -5653,7 +5653,7 @@
<!-- https://github.com/firehol/netdata/wiki/External-Plugins --> <!-- https://github.com/firehol/netdata/wiki/External-Plugins -->
<!-- CHART type.id name title units [family [context [charttype [priority [update_every]]]]] --> <!-- CHART type.id name title units [family [context [charttype [priority [update_every]]]]] -->
<chart id='unet' name='NAME' title="unet" units='count' family='UNET' context='' charttype='stacked' priority='' update_every=''> <chart id='unet' name='NAME' title="unet" units='count' family='UNET' context='' charttype='line' priority='' update_every=''>
<!-- Чтобы иметь возможность выводить на одном чарте, разные группы можно указывать много 'line' --> <!-- Чтобы иметь возможность выводить на одном чарте, разные группы можно указывать много 'line' -->
......
...@@ -451,6 +451,7 @@ AC_CONFIG_FILES([Makefile ...@@ -451,6 +451,7 @@ AC_CONFIG_FILES([Makefile
testsuite/Makefile testsuite/Makefile
python/lib/Makefile python/lib/Makefile
python/lib/pyUniSet/Makefile python/lib/pyUniSet/Makefile
python/netdata-plugin/Makefile
python/Makefile]) python/Makefile])
......
if DISABLE_PYTHON if DISABLE_PYTHON
else else
SUBDIRS=lib SUBDIRS=lib netdata-plugin
include $(top_builddir)/include.mk include $(top_builddir)/include.mk
......
if DISABLE_PYTHON
else
netdataconfdir = $(sysconfdir)/netdata/python.d
netdataconf_DATA = *.conf
plugdir = $(libdir)/netdata/python.d
plug_DATA = *.py
include $(top_builddir)/include.mk
endif
/usr/lib/netdata/python.d/python_modules
\ No newline at end of file
/home/pv/Projects/uniset-2.0/conf/test.xml
\ No newline at end of file
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# ----------------------------------------------------------------------------------------------------------
# Description: uniset netdata python.d module # Description: uniset netdata python.d module
# Author: Pavel Vainerman (pv) # Author: Pavel Vainerman (pv)
# ----------------------------------------------------------------------------------------------------------
# Вариант на основе запуска uniset2-admin --сsv id1,id2,id3...
# ----------------------------------------------------------------------------------------------------------
# В configure.xml проекта ождается секция <netdata> описывающая
# charts и параметры.
# <netdata>
# <!-- https://github.com/firehol/netdata/wiki/External-Plugins -->
# <!-- CHART type.id name title units [family [context [charttype [priority [update_every]]]]] -->
# <chart id='unet' name='Temperature' title="Temperature" units='°С' context='' charttype='area' priority='' update_every=''>
#
# <!-- Чтобы иметь возможность выводить на одном чарте, разные группы можно указывать много 'line' -->
#
# <!-- Параметры берутся из <sensors> в виду netdata_xxx (необязательные помечены []
# В качестве id - берётся name.
# В качестве name - берётся name, если не указан netdata_name=''
# -->
# <!-- DIMENSION id [name [algorithm [multiplier [divisor [hidden]]]]] -->
# <lines filter_field='ndata' filter_value='temp'/>
# </chart>
# </netdata>
#
# TODO: можно сделать более оптимальный (по ресурсам) вариант со специальным UniSetNetDataServer (экономия на запуске admin каждые X секунд)
# ------------------------------------------------------------------------------------------------------------------
import os import os
import sys import sys
import random
import uniset2 import uniset2
from uniset2 import UniXML from uniset2 import UniXML
from uniset2 import UProxyObject from subprocess import Popen, PIPE
from uniset2 import UConnector
sys.path.append("./python_modules") sys.path.append("./python_modules")
from base import SimpleService from base import ExecutableService
NAME = os.path.basename(__file__).replace(".chart.py", "") NAME = os.path.basename(__file__).replace(".chart.py", "")
# default module values # default module values
update_every = 5 update_every = 5
priority = 90000 priority = 90000
retries = 60 retries = 10000
class Service(SimpleService): class Service(ExecutableService):
sensors = []
uproxy = None
uconnector = None
smOK = False
def __init__(self, configuration=None, name=None): def __init__(self, configuration=None, name=None):
super(self.__class__, self).__init__(configuration=configuration, name=name) super(self.__class__, self).__init__(configuration=configuration, name=name)
conf = self.configuration.pop conf = self.configuration.pop
confile = None confile = None
try: try:
confile = conf('confile') confile = conf('confile')
except KeyError: except KeyError:
...@@ -43,36 +61,32 @@ class Service(SimpleService): ...@@ -43,36 +61,32 @@ class Service(SimpleService):
self.error("uniset plugin: Not found confile '%s'"%confile) self.error("uniset plugin: Not found confile '%s'"%confile)
raise RuntimeError raise RuntimeError
#self.name = self.get_conf_param('name', name) self.initOK = False
self.sensors = []
self.confile = confile
self.info("%s: read from %s"%(name,confile)) self.info("%s: read from %s"%(name,confile))
self.create_charts(confile) self.create_charts(confile)
self.init_uniset(confile)
# добавляем датчики в опрос..
for s in self.sensors:
self.uproxy.addToAsk(s[1])
def init_uniset(self, confile): uniset_port = self.get_conf_param('port', '')
arglist= uniset2.Params_inst() idlist = ''
for i in range(0, len(sys.argv)): num = 0
if i >= uniset2.Params.max: for nid,sid in self.sensors:
break; if num == 0:
arglist.add(sys.argv[i]) idlist="%s"%str(sid)
num += 1
else:
idlist += ",%s"%str(sid)
port = self.get_conf_param('port', '') uniset_command = self.get_conf_param('uniset_command',"/usr/bin/uniset2-admin --confile %s"%(self.confile))
if port != '':
p = '--uniset-port'
arglist.add_str(p)
arglist.add_str( str(port) )
uname = self.get_conf_param('uname', 'TestProc') command = "%s --csv %s"%(uniset_command,idlist)
if uniset_port!=None and uniset_port!='':
command += " --uniset-port %s"%uniset_port
try: self.configuration['command'] = command
self.uconnector = UConnector(arglist,confile) self.command = command
self.uproxy = UProxyObject(uname) self.info("%s: Check %d sensors. Update command: %s"%(name,len(self.sensors),command))
except uniset2.UException, e:
self.error("uniset plugin: error: %s"% e.getError())
raise RuntimeError
def get_conf_param(self, propname, defval): def get_conf_param(self, propname, defval):
try: try:
...@@ -86,7 +100,7 @@ class Service(SimpleService): ...@@ -86,7 +100,7 @@ class Service(SimpleService):
def find_section(self, xml, secname): def find_section(self, xml, secname):
node = xml.findNode(xml.getDoc(), secname)[0] node = xml.findNode(xml.getDoc(), secname)[0]
if node == None: if node == None:
self.error("not found '%s' section in %s" % (secname,xml.getFileName())) self.error("not found section <%s> in confile '%s'" % (secname,xml.getFileName()))
raise RuntimeError raise RuntimeError
return node.children return node.children
...@@ -107,7 +121,7 @@ class Service(SimpleService): ...@@ -107,7 +121,7 @@ class Service(SimpleService):
# CHART type.id name title units [family [context [charttype [priority [update_every]]]]] # CHART type.id name title units [family [context [charttype [priority [update_every]]]]]
id = node.prop('id') id = node.prop('id')
if id == '' or id == None: if id == '' or id == None:
self.error("IGNORE CHART.. Unknown id=''.") self.error("IGNORE CHART.. Unknown id=''")
node = xml.nextNode(node) node = xml.nextNode(node)
continue continue
...@@ -204,7 +218,7 @@ class Service(SimpleService): ...@@ -204,7 +218,7 @@ class Service(SimpleService):
return False return False
if ff == '': if ff == '':
return True; return True
if fv == '' and node.prop(ff) == '': if fv == '' and node.prop(ff) == '':
return False return False
...@@ -214,62 +228,87 @@ class Service(SimpleService): ...@@ -214,62 +228,87 @@ class Service(SimpleService):
return True return True
def check(self): def _get_data(self):
data = {}
self.info("**** uniset_activate_objects")
# ret = super(self.__class__, self).check()
try: try:
self.uconnector.activate_objects() raw = self._get_raw_data()
except uniset2.UException, e:
self.error("%s"% e.getError())
raise False
return True if raw == None or len(raw) == 0:
if self.initOK == True:
return {}
def _get_data(self): # забиваем нулями.. (нужно подумать, правильно ли это, можно сделать defaultValue в настройках)
self.initOK = True
for id, sid in self.sensors:
data[id] = 0
return data
data = {} raw = raw[-1].split(',')
for netid,id in self.sensors: if len(raw) < len(self.sensors):
data[netid] = self.uproxy.getValue(id) if len(raw) > 0:
self.debug("_get_data ERROR: len data=%d < len sensors=%d"%(len(raw),len(self.sensors)))
return {}
if len(data) == 0: i = 0
return None for id,sid in self.sensors:
data[id] = raw[i]
i += 1
except (ValueError, AttributeError):
return {}
return data return data
def update(self, interval): def _get_raw_data(self):
"""
Get raw data from executed command
:return: str
"""
try:
p = Popen(self.command, shell=True, stdout=PIPE, stderr=PIPE)
except Exception as e:
self.error("Executing command", self.command, "resulted in error:", str(e))
return None
if not self.uproxy.askIsOK() and not self.uproxy.reaskSensors(): data = []
return False for line in p.stdout.readlines():
data.append(str(line.decode()))
prev_smOK = self.smOK if len(data) == 0:
self.smOK = self.uproxy.smIsOK() # self.error("No data collected.")
return None
return data
if prev_smOK != self.smOK and self.smOK: def check(self):
self.info("SM exist OK. Reask sensors..") # Считаем что у нас всегда, всё хорошо, даже если SM недоступна
self.uproxy.reaskSensors() return True
return super(self.__class__, self).update(interval) # def update(self, interval):
# super(self.__class__, self).update(interval)
# return True
if __name__ == "__main__": if __name__ == "__main__":
config = {} config = {}
config['confile'] = './test.xml' config['confile'] = './test.xml'
config['port'] = 2809 config['port'] = 53817
config['uname'] = 'TestProc' config['uname'] = 'TestProc'
config['update_every'] = update_every config['update_every'] = update_every
config['priority'] = priority config['priority'] = priority
config['retries'] = retries config['retries'] = retries
serv = Service(config,"test") serv = Service(config,"test")
config2 = {} # config2 = {}
config2['confile'] = './test.xml' # config2['confile'] = './test.xml'
config2['port'] = 2809 # config2['port'] = 52809
config2['uname'] = 'TestProc1' # config2['uname'] = 'TestProc1'
config2['update_every'] = update_every # config2['update_every'] = update_every
config2['priority'] = priority # config2['priority'] = priority
config2['retries'] = retries # config2['retries'] = retries
#
serv2 = Service(config2,"test") # serv2 = Service(config2,"test")
...@@ -67,20 +67,15 @@ ...@@ -67,20 +67,15 @@
# AUTO-DETECTION JOBS # AUTO-DETECTION JOBS
# only one of them will run (they have the same name) # only one of them will run (they have the same name)
unet: #project1t:
confile: /home/pv/Projects.com/uniset-2.0/conf/test.xml # confile: /path/to/project1/conf/configure.xml
name: utest # name: utest
uname: TestProc # port: 53817
port: 53817
#project2:
# confile: /path/to/project2/conf/configure.xml
# port: 53817
# uniset_command: /usr/bin/ctl-admin-project2.sh
project2: #project3:
confile: /home/pv/Projects.com/uniset-2.0/conf/test.xml # confile: /path/to/project2/conf/configure.xml
name: project2
uname: TestProc1
port: 53817
project3:
confile: /home/pv/Projects.com/uniset-2.0/conf/test.xml
name: project3
uname: TestProc2
port: 53817
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment