source: hwdetector/trunk/fuentes/valentin.install/usr/bin/valentin @ 6430

Last change on this file since 6430 was 6430, checked in by mabarracus, 3 years ago

Threaded loading data

  • Property svn:executable set to *
File size: 15.0 KB
Line 
1#!/usr/bin/env python
2import sys,os
3sys.path.insert(0,'/usr/lib/valentin/')
4import logging
5import hwdetector.utils.log as log
6import tarfile
7import datetime
8import tempfile
9import json
10import argparse
11import zlib
12import base64
13from ruleset import ruleset
14
15try:
16    import threading
17    import gi
18    gi.require_version('Gtk','3.0')
19    from gi.repository import Gtk,GObject
20    GObject.threads_init()
21except Exception as e:
22    log.error('Unable to import Gtk libraries')
23
24
25def run_in_debug():
26    gettrace=getattr(sys,'gettrace',None)
27    if gettrace is None:
28        return False
29    elif gettrace():
30        return True
31    else:
32        return None
33
34def indent_var(st=u'',var={},indent=0):
35    creturn=u'\n'
36    if indent==0:
37        creturn=creturn*2
38    indentation=u'\t'*indent
39    try:
40        if isinstance(var,dict):
41            for x in sorted(var.keys()):
42                if not (x.lower().startswith('helper') and indent==0):
43                    st += u'{}{}\n{}{}'.format(indentation,x,indent_var(var=var[x],indent=indent+1),creturn)
44        elif isinstance(var,list) or isinstance(var,tuple):
45            if len(var) == 2 and var[0] == '__gz__':
46                content=zlib.decompress(base64.b64decode(var[1])).decode('utf-8')
47                replaced = content.replace(u'\n',indentation+u'\n'+indentation)
48                st += u'{}{}'.format(indentation,replaced)
49            else:
50                if isinstance(var,tuple):
51                    st += u'{}{}'.format(indentation,var)
52                else:
53                    for x in sorted(var):
54                        st += u'{}\n'.format(indent_var(var=x,indent=indent+1))
55        elif isinstance(var,str):
56            try:
57                var = var.encode('utf-8')
58            except:
59                var = var.decode('utf-8')
60            replaced = var.replace(u'\n',indentation+u'\n'+indentation)
61            st += u'{}{}'.format(indentation,replaced)
62        elif isinstance(var,unicode):
63            replaced = var.replace(u'\n',indentation+u'\n'+indentation)
64            st += u'{}{}'.format(indentation,replaced)
65        elif isinstance(var,bool) or isinstance(var,int) or isinstance(var,float) or var == None:
66            st += u'{}{}'.format(indentation,var)
67        else:
68            raise Exception('Unknown type var')
69        return st
70    except Exception as e:
71        return u''
72
73def make_file(capabilities,*args,**kwargs):
74    try:
75        txt = json.dumps(capabilities,indent=4,separators=(',',':'),sort_keys=True)
76        name = tempfile.mkstemp()[1]
77        with open(name,'w') as f:
78            f.write(txt.encode('utf-8'))
79        return name
80    except Exception as e:
81        return False
82
83def make_tar(file,*args,**kwargs):
84    try:
85        datestr='{:%Y%m%d%H%M}'.format(datetime.datetime.now())
86        if kwargs['fname']:
87            filename = kwargs['fname']
88        else:
89            filename = 'valentin-info-{}.tar.gz'.format(datestr)
90
91        if os.path.exists(file):
92            with tarfile.open(filename,'w:gz') as f:
93                f.add(file,arcname='debug-info-{}.txt'.format(datestr),recursive=False)
94                logger_message_file='/tmp/valentin-debug-log-messages.txt'
95                if os.path.exists(logger_message_file):
96                    f.add(logger_message_file,arcname=os.path.basename(logger_message_file))
97            return filename
98    except Exception as e:
99        return False
100
101def run_detection(*args,**kwargs):
102    log.debug("Importing hwdetector")
103    import hwdetector
104
105    log.debug("Instantiate HwDetector")
106    hwd=hwdetector.HwDetector()
107
108    #hwd.all_plugins_are_needed=True
109    if not run_in_debug():
110        log.info('USING MAX_RUNNING_TIME !!!')
111        hwd.MAX_RUNNING_TIME = 30
112
113    #hwd.fake_capabilities={'IAMGOD':'yes'}
114
115    log.debug("Calling run plugins")
116    #ret = hwd.run(needs=['ALL_TESTS'])
117    #ret = hwd.run(needs=['LLXSYSTEM_TEST','LLXNETWORK_TEST'])
118    ret = hwd.run()
119
120    log.info("Total running time: {}".format(hwd.RUNNING_TIME))
121
122    return (ret,hwd.capabilities)
123
124def load_file(*args,**kwargs):
125    if kwargs.get('fname',None):
126        filename = kwargs['fname']
127    else:
128        return False
129    if os.path.exists(filename):
130        ftxt=None
131        if tarfile.is_tarfile(filename):
132            with tarfile.open(filename,'r:gz') as tar:
133                target_file=None
134
135                for tarinfo in tar.getmembers():
136                    if 'debug-info' in tarinfo.name.lower():
137                        target_file=tarinfo.name
138                        break
139                if target_file:
140                    ftxt = tar.extractfile(target_file).read()
141        else:
142            ftxt = None
143            with open(filename,'r') as f:
144                ftxt = f.read()
145        return ftxt
146    else:
147        return False
148
149def run_analysis(*args,**kwargs):
150
151    if kwargs.get('capabilities',None):
152        fileinfo = kwargs.get('capabilities')
153    if not kwargs.get('ruleset',None):
154        fileruleset='/usr/share/valentin/valentin.rules'
155        if not os.path.exists(fileruleset):
156            return False
157    else:
158        fileruleset=kwargs.get('ruleset')[0]
159
160    rs = ruleset()
161    try:
162        rs.load_ruleset(fileruleset=fileruleset,data=fileinfo)
163        rs.make_tree()
164        rs.make_suggestion()
165    except Exception as e:
166        log.error(e)
167
168    return True
169
170
171class Handler:
172    def __init__(self,gui):
173        self.gui = gui
174
175    def destroy(self,*args,**kwargs):
176        log.debug('Exitting gui')
177        sys.exit(0)
178
179    def key_clicked(self,tselection,tpath,tviewcolumn,*args,**kwargs):
180        model,lpaths=tselection.get_selected_rows()
181        id_selected=model[lpaths][0]
182        if self.gui.coldict[id_selected]['child']:
183            tview=tselection.get_tree_view()
184            for sel in lpaths:
185                if tview.row_expanded(sel):
186                    tview.collapse_row(sel)
187                else:
188                    tview.expand_row(sel,False)
189        else:
190            self.gui.showText(id_selected)
191
192class Gui:
193    def __init__(self,*args,**kwargs):
194        self.handler = Handler(self)
195        self._load_glade(**kwargs)
196        pass
197
198    def _store_data(self,data,id=[0]):
199        if not self.store:
200            return None
201        me=id[0]
202        if isinstance(data,dict):
203            for key in sorted(data.keys()):
204                if key.startswith('HELPER'):
205                    continue
206                id[0]+=1
207                id_child=id[0]
208                col=self.store.append(self.coldict[me]['col'],[id_child,key])
209                self.coldict[me]['child'].append(id_child)
210                self.coldict[id_child]={'parent':me,'col':col,'data':data[key],'me':id_child,'child':[]}
211                self._store_data(data=data[key],id=id)
212        elif isinstance(data,list):
213            if len(data) > 1:
214                if len(data)==2 and data[0] == '__gz__':
215                    self.coldict[me]['data']=zlib.decompress(base64.b64decode(data[1]))
216                    self._store_data(data=self.coldict[me]['data'],id=id)
217                else:
218                    for x in data:
219                        id[0]+=1
220                        id_child=id[0]
221                        col=self.store.append(self.coldict[me]['col'],[id_child,'[{}]'.format(str(data.index(x))).encode('utf-8').decode('utf-8')])
222                        self.coldict[me]['child'].append(id_child)
223                        self.coldict[id_child]={'parent':me,'col':col,'data':x,'me':id_child,'child':[]}
224                        self._store_data(data=x,id=id)
225            elif len(data) == 1:
226                self.coldict[me]['data']=data[0]
227                self._store_data(data=data[0],id=id)
228        elif isinstance(data,tuple):
229            self._store_data(data=list(data),id=id)
230            pass
231        elif isinstance(data,str):
232            try:
233                data=data.encode('utf-8').decode('utf-8')
234                self.coldict[me]['data']=data
235            except:
236                pass
237        elif isinstance(data,unicode):
238            try:
239                data.decode('utf-8')
240            except:
241                data=data.encode('utf-8').decode('utf-8')
242                self.coldict[me]['data']=data
243        else:
244            self._store_data(data=str(data).encode('utf-8').decode('utf-8'),id=id)
245
246        return True
247
248    def showData(self,data,*args,**kwargs):
249        self.buffer=Gtk.TextBuffer()
250        self.textview=self.builder.get_object('textview1')
251        self.treeview=self.builder.get_object('treeview1')
252        self.store=self.builder.get_object('treestore1')
253        self.storesort=self.builder.get_object('treemodelsort1')
254        self.storesort.set_sort_column_id(1,Gtk.SortType.ASCENDING)
255        self.coldict={0:{'parent':0,'col':None,'data':data,'me':0,'child':[]}}
256        self._store_data(data=data)
257        self.treeview.set_model(self.storesort)
258
259    def showText(self,id=0,*args,**kwargs):
260        if id == 0 or not isinstance(id,int):
261            self.buffer=''
262        else:
263            coldata=self.coldict[id]
264            self.buffer.set_text(str(coldata['data']))
265
266        self.textview.set_buffer(self.buffer)
267        return True
268
269    def _show_window(self,*args,**kwargs):
270        if not (self.builder):
271            return
272        self.mainwindow=self.builder.get_object('mainwindow')
273        self.builder.connect_signals(self.handler)
274        self.treeview=self.builder.get_object('treeview1')
275        self.treeview.set_model(Gtk.TreeStore(int,str))
276        self.mainwindow.show_all()
277        pass
278
279
280    def _load_glade(self,*args,**kwargs):
281        log.debug('Loading glade file')
282        self.gladefile=kwargs.get('gladefile',None)
283        if self.gladefile and os.path.exists(self.gladefile):
284            try:
285                self.builder=Gtk.Builder()
286                self.builder.add_from_file(self.gladefile)
287                self._show_window()
288            except Exception as e:
289                log.error('Can\'t load gladefile')
290                self.builder=None
291
292
293
294
295        pass
296if __name__ == '__main__':
297    try:
298        parser = argparse.ArgumentParser(description='Simple system diagnostic tool')
299        dlevels = ['debug','info','warning','error']
300        parser.add_argument('-d','--debug-level',metavar='debug|info|warning|error',nargs='?',choices=dlevels,help='Set the debug level (default: warning)')
301        parser.add_argument('-c','--coloured',action='store_const',help='Colorize logger messages',const=True)
302
303        parser.add_argument('-f','--with-file',metavar='filename',nargs='?',help='Filename for results file (default valentin-info-(date).tar.gz')
304        parser.add_argument('-o','--to-stdout',action='store_const',help='Output results to stdout',const=True)
305        parser.add_argument('-w','--to-stdout-raw',action='store_const',help='Output results to stdout in json format',const=True)
306        parser.add_argument('-g','--graphical-mode',action='store_const',help='Show results in graphical mode',const=True)
307
308        parser.add_argument('-a','--analyze-file',metavar='filename',nargs=1,help='Filename to analyze')
309        parser.add_argument('-x','--run-detection',action='store_const',help='Run detection',const=True)
310
311        parser.add_argument('-s','--suggest',action='store_const',help='Suggest actions to repair system',const=True)
312        parser.add_argument('-r','--ruleset',metavar='filename',nargs=1,help='Load ruleset when suggest user actions')
313        args = parser.parse_args()
314
315        if not args.debug_level:
316            #log.set_level(logging.WARNING)
317            log.disable()
318        else:
319            levels={'debug':logging.DEBUG,'info':logging.INFO,'warning':logging.WARNING,'error':logging.ERROR}
320            log.set_level(levels[args.debug_level.lower()])
321
322        if args.coloured:
323            log.set_color(True)
324
325        kw=vars(args)
326
327        fname=None
328        if args.with_file:
329            fname=args.with_file
330            if '.' not in fname:
331                fname = fname + '.tar.gz'
332            kw.update({'fname':fname})
333
334        if args.analyze_file:
335            fname=args.analyze_file[0]
336            kw.update({'fname':fname})
337            capabilities=load_file(**kw)
338            if capabilities:
339                capabilities = json.loads(capabilities)
340                ret = True
341                log.info("File {} loaded".format(fname))
342                kw.update({'capabilities':capabilities})
343            else:
344                log.error("File {} can't be loaded".format(fname))
345                ret = False
346        else:
347            if args.run_detection:
348                ret,capabilities=run_detection(**kw)
349                log.info('Detection done!')
350                if args.with_file:
351                    try:
352                        file = make_file(capabilities,**kw)
353                        fname = make_tar(file,**kw)
354                        log.info("File {} created".format(fname))
355                    except Exception as e:
356                        log.error("File creation unsuccessful "+str(e))
357
358                kw.update({'capabilities':capabilities})
359            else:
360                sys.stderr.write('Missing detection option\n')
361                ret = False
362
363        if ret:
364            if args.to_stdout or args.to_stdout_raw:
365                if args.to_stdout:
366                    pr=indent_var(var=capabilities)
367                elif args.to_stdout_raw:
368                    for x in (x for x in capabilities.keys() if x.lower().startswith('helper')):
369                        del capabilities[x]
370
371                    def clear_compressed(var):
372                        try:
373                            if isinstance(var,tuple) or isinstance(var,list):
374                                if len(var) == 2 and var[0]=='__gz__':
375                                    try:
376                                        var=zlib.decompress(base64.b64decode(var[1])).decode('utf-8')
377                                    except:
378                                        pass
379                                    return var
380                                var=list(var)
381                                for i in range(len(var)):
382                                    var[i]=clear_compressed(var[i])
383                            elif isinstance(var,dict):
384                                for x in var:
385                                    var[x]=clear_compressed(var[x])
386
387                            return var
388                        except Exception as e:
389                            log.error(e)
390
391                    capabilities=clear_compressed(capabilities)
392                    pr=json.dumps(capabilities,indent=4,separators=(',',':'),sort_keys=True)
393
394                sys.stdout.write(pr.encode('utf-8'))
395                sys.stderr.write('\n')
396            elif args.graphical_mode:
397                GObject.threads_init()
398                gui = Gui(gladefile=os.getcwd()+'/../lib/valentin/varviewer.glade')
399                threading.Thread(target=gui.showData,args=(capabilities,)).start()
400                sys.exit(Gtk.main())
401
402            if args.suggest:
403                ret=run_analysis(**kw)
404
405
406        if ret:
407            log.info("Exit code = 0")
408            sys.exit(0)
409        else:
410            log.info("Exit code = 1")
411            sys.exit(1)
412
413    except Exception as e:
414        log.error("Exception occurred: {}".format(e))
415        log.error("Exit code = 1")
416        sys.exit(1)
417
Note: See TracBrowser for help on using the repository browser.