source: lmd/trunk/fuentes/lmd-server.install/usr/share/n4d/python-plugins/LmdServer.py @ 2670

Last change on this file since 2670 was 2670, checked in by joamuran, 3 years ago

added lient manager

File size: 35.2 KB
Line 
1import socket
2import subprocess
3import sys
4
5import threading
6import time
7import datetime
8
9import json
10
11import os
12import signal
13import re
14
15import shutil
16import tempfile
17
18import tarfile
19import apt_pkg
20
21import lliurex.net
22
23class LmdServer:
24        def __init__(self):
25
26                self.last_job_id=0;
27                self.joblist=[]
28                self.global_listeners = {}
29                self.thread_jobs = {}
30                # Threading Multicast process to all listeners
31                self.multicast_thread = threading.Thread()
32                self.logfolder = '/run/lmdserver'
33                self.ltsp_path = '/opt/ltsp'
34                self.locks = {}
35                # Clean log folder and create
36                if (os.path.exists(self.logfolder)):
37                        shutil.rmtree(self.logfolder)
38                os.mkdir(self.logfolder)
39               
40                pass
41        #def __init__
42       
43        def set_default_boot(self,imgid):
44                dbm = objects['LmdBootManager'].getDefaultBootImage()
45                if dbm['default_boot'] == "":
46                        objects['LmdBootManager'].setDefaultBootImage(imgid)
47
48
49        def check_chroot_exists(self, name):
50                '''
51                Check if exists chroot /opt/ltsp/name and /opt/ltsp/images/name.img
52                DATE: 03/04/2014
53                '''
54                path=os.path.join("/opt/ltsp",name)
55                imagepath=os.path.join("/opt/ltsp/images",name+".img")
56               
57                #print "cheking "+name
58                if(os.path.isdir(path)):
59                        #print "111"
60                        return {"status": True, "error": 'chroot_exists'}
61               
62                if (os.path.isfile(imagepath)):
63                        #print "22222"
64                        return {"status": True, "error": 'image_exists'}
65                return {"status": False}
66
67               
68        def create_imageWS(self, imgid, name, template, description='', bgimg=''):
69                try:
70                        # creates an image from an specified template
71               
72                        # Check if template exists
73                        path="/etc/ltsp/templates/"+template;
74                        if(os.path.isfile(path)):
75                                extraLliurexOpts="--accept-unsigned-packages --purge-chroot"
76                                # if template exists, create an image
77                                print "[LmdServer] Create_imageWS from "+path
78                                command="ltsp-build-client --config "+path+" "+extraLliurexOpts+" --chroot "+imgid+"&& service nbd-server restart";
79                               
80                                ret=objects['TaskMan'].newTask(command);
81                                                               
82                                if ret["status"]==True: ## Task has launched ok
83                                                               
84                                        metadata = {'id':imgid, 'name' : name,
85                                                        'desc' : description ,
86                                                        "template" : template,
87                                                        'img': bgimg,
88                                                        'taskid': ret["msg"],
89                                                        'ltsp_fatclient': 'undefined',
90                                                        'ldm_session': 'default',
91                                                        'fat_ram_threshold': 'default',
92                                                        'lmd_extra_params':'' }
93                                        metadata_string = unicode(json.dumps(metadata,indent=4,encoding="utf-8",ensure_ascii=False)).encode("utf-8")
94                                        objects['LmdImageManager'].setImage(imgid,metadata_string)
95                                        self.set_default_boot(imgid)
96
97                                        # Registering imgid for boot PXE MEnu
98                               
99                                        objects['LlxBootManager'].pushToBootList("ltsp_label"+str(imgid));
100                               
101                                        return {"status": True, "msg": ret["msg"]}
102
103                                else:
104                                        if ret["msg"]=="SERVER_BUSY":
105                                                return {'status':False, 'msg':'SERVER_BUSY'}
106                                        else:
107                                                return {'status':False, 'msg':'EXCEPTION'}
108                               
109                        else:
110                                return {'status':False, 'msg':'TEMPLATE_NOT:EXISTS'}
111                       
112                        pass
113                except Exception as e:
114                        print "Except: "+str(e)
115                        return e
116       
117       
118       
119       
120        def create_image(self, ip, port, imgid, name, template, description='', bgimg='', srv_ip='127.0.0.1'):
121                try:
122                        # creates an image from an specified template
123               
124                        # Check if template exists
125                        path="/etc/ltsp/templates/"+template;
126                        if(os.path.isfile(path)):
127                                extraLliurexOpts="--accept-unsigned-packages --purge-chroot"
128                                # if template exists, create an image
129                                print "[LmdServer] Create_image from "+path
130                                command="ltsp-build-client --config "+path+" "+extraLliurexOpts+" --chroot "+imgid+"&& service nbd-server restart";
131                               
132                                metadata = {'id':imgid, 'name' : name,
133                                                        'desc' : description ,
134                                                        "template" : template,
135                                                        'img': bgimg,
136                                                        'ltsp_fatclient': 'undefined',
137                                                        'ldm_session': 'default',
138                                                        'fat_ram_threshold': 'default',
139                                                        'lmd_extra_params':'' }
140                               
141                                metadata_string = unicode(json.dumps(metadata,indent=4,encoding="utf-8",ensure_ascii=False)).encode("utf-8")
142                                objects['LmdImageManager'].setImage(imgid,metadata_string)
143                                self.set_default_boot(imgid)
144                                #command="cat "+path+"; sleep 15; cat "+path+"; echo 'command is: ltsp-build-client --config "+path+" "+extraLliurexOpts+" --chroot "+imgid+"'";
145                                #command="/tmp/miscript"
146                                #command="tree /"
147                                result=self.do_command(ip, port, command, srv_ip,target=imgid)
148                               
149                                # Registering imgid for boot PXE MEnu
150                               
151                                if (result==0):
152                                        objects['LlxBootManager'].pushToBootList("ltsp_label"+str(imgid));
153                                else:
154                                        # Image has not been built correctly
155                                        metadata = {'id':imgid, 'name' : name,
156                                                'desc' : description ,
157                                                "template" : template,
158                                                'img': bgimg,
159                                                'ltsp_fatclient': 'undefined',
160                                                'ldm_session': 'default',
161                                                'fat_ram_threshold': 'default',
162                                                'status':'broken',
163                                                'lmd_extra_params':'' }
164                                       
165                               
166                                return result
167
168                        else:
169                                return "{'status':'false', 'error':'template does not exists'}"
170                       
171                        pass
172                except Exception as e:
173                        print "Except: "+str(e)
174                        return e
175               
176       
177        def refresh_image(self, ip, username, password, port, imgid, srv_ip='127.0.0.1'):
178               
179                # Rebuild img file for imgid
180                import xmlrpclib
181
182                try:
183                        # umount anything
184                        path="/opt/ltsp/"+imgid
185                       
186                        server = xmlrpclib.ServerProxy("https://localhost:9779")
187                        server.umount_chroot((username,password),'LmdImageManager',path);
188                       
189                        # Let's rebuild image
190                        print "[LmdServer] Refreshing image for "+str(imgid)
191
192                        # Trying to solve the non-zenity problem
193                        #
194                        command="ltsp-chroot -p -m -a "+imgid+" dpkg-reconfigure libgl1-mesa-dri ;  "
195                       
196                       
197                        command=command + "ltsp-chroot -p -m -a "+imgid+" /usr/share/ltsp/update-kernels && "
198                        command=command + "ltsp-update-kernels "+imgid+" && ltsp-update-image "+imgid+"&& service nbd-server restart"; 
199                        result=self.do_command(ip, port, command, srv_ip,target=imgid)
200                        objects['LlxBootManager'].pushToBootList("ltsp_label"+str(imgid));
201                        return {"status": True, "msg": 'Image Updated'}
202
203                               
204                        pass
205                except Exception as e:
206                        print "Except: "+str(e)
207                        return {"False": True, "msg": str(e)}
208
209       
210               
211        def refresh_imageWS(self, imgid):
212               
213                try:
214                       
215                        # umount anything
216                        path="/opt/ltsp/"+imgid                                         
217                        objects['LmdImageManager'].umount_chroot(path);
218                       
219                        command="ltsp-chroot -p -m -a "+imgid+" dpkg-reconfigure libgl1-mesa-dri ;  "
220                        command=command + "ltsp-chroot -p -m -a "+imgid+" /usr/share/ltsp/update-kernels && "
221                        command=command + "ltsp-update-kernels "+imgid+" && ltsp-update-image "+imgid+"&& service nbd-server restart";
222                               
223                        # Let's rebuild image
224                       
225                        ret=objects['TaskMan'].newTask(command);
226                        if ret["status"]==True: ## Task has launched ok
227                               
228                               
229                                print "[LmdServer] Refreshing image for "+str(imgid)
230                               
231                                objects['LlxBootManager'].pushToBootList("ltsp_label"+str(imgid));
232                               
233                                # Setting new taskid to the image, so we know that it is refreshing
234                                objects['LmdImageManager'].setNewTaskIdForImage(imgid, ret["msg"]);
235                                return {"status": True, "msg": ret["msg"]} # Returns task id!!
236                               
237       
238                        else:
239                                if ret["msg"]=="SERVER_BUSY":
240                                        return {'status':False, 'msg':'SERVER_BUSY'}
241                                else:
242                                        return {'status':False, 'msg':'EXCEPTION'}
243                       
244                       
245                except Exception as e:
246                        print "Except: "+str(e)
247                        return {"False": True, "msg": str(e)}
248
249       
250               
251       
252       
253        def export_image(self, ip, port, imgid, srv_ip='127.0.0.1'):
254                # Deprecated: Replaced by CloneOrExportWS
255                try:
256                        # creates an image from an specified template
257               
258                        # Check if template exists
259                        print "[LmdServer] Export_image from "+imgid
260                       
261                        name_file = str(imgid) + "_" + time.strftime("%Hh%Mm%Ss_%d%m%Y") + '.tgz'
262
263                        command="ltsp-import-export export /tmp/"+ name_file +" "+str(imgid);
264
265                        result=self.do_command(ip, port, command, srv_ip,target=imgid)
266                        return {"status": True, "msg": str(result)}
267                        pass
268                except Exception as e:
269                        print "Except: "+str(e)
270                        return {"status": False, "msg": str(e)}
271
272        def import_image(self, ip, port, imgid, path, srv_ip='127.0.0.1'):
273                # Deprecated: Replaced by CloneOrExportWS and extra apps...
274                try:
275                       
276                        import xmlrpclib
277                        client=xmlrpclib.ServerProxy("https://127.0.0.1:9779")
278                        n4d_server_ip=client.get_variable("", "VariablesManager", "SRV_IP")
279
280                        #if(ip==srv_ip)
281                        print ("COMPARING: "+lliurex.net.get_ip_from_host(ip)+ "and "+ n4d_server_ip);
282                        if(lliurex.net.get_ip_from_host(ip)==n4d_server_ip or ip==srv_ip):
283                                print "[LmdServer] Import_image from ",path, " to ", imgid
284
285                                tar = tarfile.open(path)
286                                l = tar.getnames()
287                                folder = l[0]
288                                imgid = folder
289                                f = tar.extractfile(folder + '/' + folder + '.json')
290                                exec("json_content="+"".join(f.readlines()))
291                                try:
292                                        new_name = folder
293                                        count = 1
294                                        while os.path.exists(os.path.join(self.ltsp_path,new_name)):
295                                                        new_name = folder + "_" + str(count)
296                                                        count += 1
297
298                                        extra_opts = ""
299                                        if folder != new_name:
300                                                extra_opts = new_name
301                                except Exception as e:
302                                        extra_opts = ""
303                                command="ltsp-import-export import "+str(path)+" "+folder+" "+extra_opts+" && service nbd-server restart";
304                               
305                                if extra_opts != "":
306                                        imgid = extra_opts
307                                        json_content['id'] = imgid
308                                        json_content['name'] = imgid
309                                json_file = open('/etc/ltsp/images/' + imgid + '.json','w')
310                                data = unicode(json.dumps(json_content,indent=4,encoding='utf-8',ensure_ascii=False)).encode('utf-8')
311                                json_file.write(data)
312                                json_file.close()
313                                self.set_default_boot(imgid)
314                                result=self.do_command(ip, port, command, srv_ip,target=imgid)
315                                return {"status": True, "msg": str(result)}
316                                pass
317                        else:
318                                print "[LmdServer] ERROR. Trying to import image from out of server";
319                                return {"status": False, "msg": "Not in server"}
320                except Exception as e:
321                        print "Except: "+str(e)
322                        return {"status": False, "msg": str(e)}
323
324
325        def CreateImgJSON(self, imgid, newLabel, newid, description, path):
326                #self.CreateImgJSON(targetid, newLabel, newid, newDesc, new_json_descriptor_file);
327                try:
328                       
329                        # Removing previous .json into image
330                        files=os.listdir('/opt/ltsp/'+imgid );
331                        for i in files:
332                                f,ext=os.path.splitext(i)
333                                if (ext==".json"):
334                                        os.remove('/opt/ltsp/' +imgid+"/"+i);
335
336                        # Create and copy new json
337                        json_file = open('/etc/ltsp/images/' + imgid + '.json','r')
338                        jsonClient=json.loads(json_file.read());
339                        json_file.close();             
340                        #print jsonClient;
341                        jsonClient['name']=newLabel;
342                        jsonClient['id']=newid;
343                        jsonClient['desc']=description;
344                       
345                        json_file = open(path,'w')
346                        metadata_string = unicode(json.dumps(jsonClient,indent=4,encoding="utf-8",ensure_ascii=False)).encode("utf-8")
347                        json_file.write(metadata_string);
348                        json_file.close();
349                       
350                        return {"status": True, "msg": "Saved"}
351               
352                except Exception as e:
353                        print "Except: "+str(e)
354                        return {"status": False, "msg": str(e)}
355               
356       
357        def CloneOrExportWS(self, targetid, newid, newLabel, newDesc, is_export):
358                '''
359                Export or Clone an image via web
360                # New Version of import/export
361                # Last Modification: 23/09/2016
362                '''
363               
364                new_json_descriptor_file="/tmp/"+newid+".json";
365                original_path_image="/opt/ltsp/"+targetid;
366                self.CreateImgJSON(targetid, newLabel, newid, newDesc, new_json_descriptor_file);
367               
368                if str(is_export)=="True": #Export to a tar.gz with lmdimg extension
369                        #command="lmd-export-img.sh "+targetid+" "+newLabel+" "+newDesc;
370                       
371                        command="lmd-export-img.sh "+new_json_descriptor_file+" "+original_path_image+" "+newid+".lmdimg"
372                else:
373                        #command="lmd-clone-img.sh "+targetid+" "+newLabel+" "+newDesc;
374                       
375                        command="echo "+targetid+" "+original_path_image+" "+newid;
376                        command="lmd-clone-img.sh "+new_json_descriptor_file+" "+original_path_image+" "+newid;
377                       
378               
379                try:
380                        ret=objects['TaskMan'].newTask(command);
381                        if ret["status"]==True: ## Task has launched ok
382                                # Returns true and ret.msg, that is job id
383                                print ("**********************************************");
384                                return {"status": True, "msg": ret["msg"]}
385                                pass
386                       
387                        else:
388                                if ret["msg"]=="SERVER_BUSY":
389                                        return {'status':False, 'msg':'SERVER_BUSY'}
390                                else:
391                                        return {'status':False, 'msg':'EXCEPTION'}
392                except Exception as e:
393                        print "Except: "+str(e)
394                        return {"status": False, "msg": str(e)}
395               
396                return {"status": True, "msg": "Done"}
397       
398       
399        def ImportImageWS(self, filename):
400               
401                command="lmd-import-from-admin-center.sh "+filename;
402                       
403                try:
404                        ret=objects['TaskMan'].newTask(command);
405                        if ret["status"]==True: ## Task has launched ok
406                                # Returns true and ret.msg, that is job id
407                                return {"status": True, "msg": ret["msg"]}
408                                pass
409                       
410                        else:
411                                if ret["msg"]=="SERVER_BUSY":
412                                        return {'status':False, 'msg':'SERVER_BUSY'}
413                                else:
414                                        return {'status':False, 'msg':'EXCEPTION'}
415                except Exception as e:
416                        print "Except: "+str(e)
417                        return {"status": False, "msg": str(e)}
418               
419                return {"status": True, "msg": "Done"}
420               
421                pass
422       
423        def getExportedList(self):
424                exported_path="/var/www/exported";
425               
426                try:
427                        list=os.listdir(exported_path)
428                        return {"status":True, "msg":list}
429                        pass
430                except Exception as e:
431                        return {"status":False, "msg": str(e)}
432                        pass
433                       
434
435        def deploy_minimal_client(self, ip, port, srv_ip='127.0.0.1'):
436                # DEPRECATED: Replaced by deploy_minimal_clientWS
437                try:
438                        command="/usr/sbin/mini-light-client.py";
439                        imgid="mini-light-client"
440                       
441                        lng="es_ES.UTF-8"
442                        language="es_ES"
443       
444                        # After that, set image as available
445                        metadata = {'status':'enabled-non-editable',
446                                                'id':'mini-light-client',
447                                                'name':'Client Lleuger Minim',
448                                                'template':'Default by LMD',
449                                                'desc':'Minimal thin client -not fat- for Lliurex LTSP.',
450                                                'img':'llx-client16.png',
451                                                'ltsp_fatclient': 'false',
452                                                'ldm_session': 'startxfce4',
453                                                'fat_ram_threshold': 'default',
454                                                'lmd_extra_params':'XKBLAYOUT=es LDM_LANGUAGE="%s" LOCALE_DIR=%s'%(lng,language)}
455                        metadata_string = unicode(json.dumps(metadata,indent=4,encoding="utf-8",ensure_ascii=False)).encode("utf-8")
456                        objects['LmdImageManager'].setImage('mini-light-client',metadata_string)
457                        self.set_default_boot(imgid)
458                        result=self.do_command(ip, port, command, srv_ip,target=imgid)
459                                       
460                        if result==0:
461                                # Step 2. Writing lts.conf for mini-light-client
462                               
463                                print "[LMDServer] Writing lts.conf for mini-light-client"
464                               
465                                if not os.path.exists('/var/lib/tftpboot/ltsp/mini-light-client'):
466                                        os.makedirs('/var/lib/tftpboot/ltsp/mini-light-client')
467                               
468                                f=open('/var/lib/tftpboot/ltsp/mini-light-client/lts.conf', 'w')
469                                f.write('[Default]\n')
470                                f.write('LDM_LANGUAGE=es_ES.UTF-8\n')
471                                f.write('LTSP_FATCLIENT=false\n')
472                                f.close()
473                               
474                                # Step 3. Registering imgid for boot PXE menu
475                                objects['LlxBootManager'].pushToBootList("ltsp_label"+str(imgid));
476                               
477                                try:
478                                        f=open("/etc/default/locale")
479                                        lines=f.readlines()
480                                        f.close()
481                                        for line in lines:
482                                                if "LANG=" in line:
483                                                        lng=line.strip().split("=")[1].strip("'").strip('"')
484                                                        if "es_ES" in lng:
485                                                                language="es"
486                                                        if "ca_ES" in lng:
487                                                                language="ca_ES@valencia"
488                                                       
489                                except:
490                                        return {"status": False, "msg": str(result)}
491                                        pass
492                               
493                                return {"status": True, "msg": str(result)}
494                                pass
495               
496                        else:
497                                # result is not 0 -> Image creation cancelled
498                                return {"status": True, "msg": str(result)}
499                                pass
500                       
501                except Exception as e:
502                        print "Except: "+str(e)
503                        return {"status": False, "msg": str(e)}
504       
505
506        #def deploy_minimal_clientWS(self, ws):
507        def deploy_minimal_clientWS(self):
508                #ws=objects['TaskMan'].getWS() -> no cal??
509                try:
510                        command="/usr/sbin/mini-light-client.py";
511                        #command="tasktest hola";
512                       
513                       
514                        #result=self.do_command(ip, port, command, srv_ip,target=imgid)
515                        ret=objects['TaskMan'].newTask(command);
516                        if ret["status"]==True: ## Task has launched ok
517                                #print ret["status"]
518                               
519                                # Writing  json metadata in /etc/ltsp/images/
520                                imgid="mini-light-client"
521                               
522                                lng="es_ES.UTF-8"
523                                language="es_ES"
524                                # After that, set image as available
525                                metadata = {'status':'enabled-non-editable',
526                                                        'taskid': ret["msg"],
527                                                        'id':'mini-light-client',
528                                                        'name':'Client Lleuger Minim',
529                                                        'template':'Default by LMD',
530                                                        'desc':'Minimal thin client -not fat- for Lliurex LTSP.',
531                                                        'img':'llx-client16.png',
532                                                        'ltsp_fatclient': 'false',
533                                                        'ldm_session': 'startxfce4',
534                                                        'fat_ram_threshold': 'default',
535                                                        'lmd_extra_params':'XKBLAYOUT=es LDM_LANGUAGE="%s" LOCALE_DIR=%s'%(lng,language)}
536                                metadata_string = unicode(json.dumps(metadata,indent=4,encoding="utf-8",ensure_ascii=False)).encode("utf-8")
537                                objects['LmdImageManager'].setImage('mini-light-client',metadata_string)
538                                self.set_default_boot(imgid)
539                               
540                                # Writing lts.conf
541                                print "[LMDServer] Writing lts.conf for mini-light-client"
542                                if not os.path.exists('/var/lib/tftpboot/ltsp/mini-light-client'):
543                                        os.makedirs('/var/lib/tftpboot/ltsp/mini-light-client')
544                                f=open('/var/lib/tftpboot/ltsp/mini-light-client/lts.conf', 'w')
545                                f.write('[Default]\n')
546                                f.write('LDM_LANGUAGE=es_ES.UTF-8\n')
547                                f.write('LTSP_FATCLIENT=false\n')
548                                f.close()
549                                # Step 3. Registering imgid for boot PXE menu
550                                objects['LlxBootManager'].pushToBootList("ltsp_label"+str(imgid));
551                                try:
552                                        f=open("/etc/default/locale")
553                                        lines=f.readlines()
554                                        f.close()
555                                        for line in lines:
556                                                if "LANG=" in line:
557                                                        lng=line.strip().split("=")[1].strip("'").strip('"')
558                                                        if "es_ES" in lng:
559                                                                language="es"
560                                                        if "ca_ES" in lng:
561                                                                language="ca_ES@valencia"
562                                                       
563                                except:
564                                        return {"status": False, "msg": ret["msg"]}
565                                        pass
566                                                               
567                                # Returns true and ret.msg, that is job id
568                                return {"status": True, "msg": ret["msg"]}
569                                pass
570                       
571                        else:
572                                if ret["msg"]=="SERVER_BUSY":
573                                        return {'status':False, 'msg':'SERVER_BUSY'}
574                                else:
575                                        return {'status':False, 'msg':'EXCEPTION'}
576               
577                       
578                except Exception as e:
579                        print "Except: "+str(e)
580                        return {"status": False, "msg": str(e)}
581       
582
583
584        def do_command(self, ip, port, command, srv_ip='127.0.0.1',target=None):
585                try:
586                        # Add Job
587                        '''job={
588                                'job_id':str(self.last_job_id),
589                                'srv_ip': None,                                         #  Server (me) IP addr
590                                'process': None,
591                                'status':'started',
592                                'msg':'',                               # Error code
593                                'target':'None',
594                                'command':command,
595                                'started_by':None,
596                                'listeners': [{'ip':ip,
597                                                'port':port,
598                                                'socket':None}],
599                                'filepipe': ''
600                                }'''
601                        call_info = _n4d_get_user()
602                        job={
603                                'job_id':str(self.last_job_id),
604                                'srv_ip': srv_ip,
605                                'process': None,
606                                'status':'started',
607                                'msg':None,
608                                'target': target,
609                                'command':command,
610                                'started_by':str(ip),
611                                'listeners': [],
612                                'filepipe': '',
613                                'seek' : 0,
614                                'method':call_info['method'],
615                                'class': call_info['class']
616                               
617                        }
618                       
619                        lock = threading.Lock()
620                        self.locks[job['job_id']]={}
621                        self.locks[job['job_id']]['lock'] = lock
622                       
623                        self.locks[job['job_id']]['lock'].acquire()
624                        # Exec command
625                        # proc = subprocess.Popen([command],  shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, preexec_fn=os.setsid)
626                       
627                        # Prepare file pipe
628                        temp = tempfile.NamedTemporaryFile(prefix='pipe_', dir=self.logfolder, delete=False)
629                       
630                        # New exec command, ignoring stderr stdin for now
631                        proc = subprocess.Popen([command],  shell=True, stdout=temp, preexec_fn=os.setsid)
632                       
633                       
634                        # Add job to tasklist
635                        job['process']=proc                                     # Assotiate process to job
636                        job['status']="running"
637                        job['filepipe'] = temp.name
638
639                       
640                        self.joblist.append(job)                                #Adding job
641                        self.add_listener_to_job(ip, port, job) # Adding the client ip:port to listeners list
642                       
643                        ## PAUSA PARA PROVOCAR UNA CONDICION DE CARRERA
644                        ##
645                        ## time.sleep(2)
646                        ## FIN DE LA PAUSA
647                        ##
648                        #self.joblist.append(job)                               #Adding job  # Moved before add_listener_to_job
649               
650                        # Increase last_job_id
651                        self.last_job_id=self.last_job_id+1
652                        # Multicast process to all listeners
653                        print "[LmdServer] WAITING ...:"+str(datetime.datetime.now())
654                        ret=job['process'].poll()
655                       
656                        while ret is None:
657                                time.sleep(1)
658                                ret=job['process'].poll()
659                       
660                       
661                        #temp.close()
662                        if (str(ret)=='0'):
663                                job['status']="finished"
664                        elif (str(ret)=='-9'):                 
665                                job['status']="cancelled"
666                        else: # return code 1 when install fails
667                                job['msg']="broken"
668                        print "[LmdServer] END WAIT AT"+str(datetime.datetime.now())
669                        print "[LmdServer] FINISHING!!!, return code: "+str(ret)
670                               
671                        # Force umount (to avoid morrir destruction in mirrononnet)
672                        proc=subprocess.call(["/usr/share/lmd-scripts/umount-chroot.sh"])
673
674                        # Sending last line to log for all listeners
675                        line="<b>Finished with status:"+job['status']+" and Response: "+job['msg']+" </b>\n"
676                        aux = open(job['filepipe'],'a')
677                        aux.writelines(line)
678                        aux.close()
679
680                        # Append result of job and release mutex. Now all inform_me return finish
681                        self.locks[job['job_id']]['result'] = str(ret)
682                        self.locks[job['job_id']]['lock'].release()
683                        print  str(ret)
684                        return str(ret)
685               
686                except Exception as e:
687                        job['status']="Error"
688                        print '[LmdServer]',e
689                        if (ret is None):
690                                job['msg']="Err code None (running)"
691                        elif (ret<0):
692                                job['msg']="Interrupted by signal: "+str(ret)
693                        else:
694                                job['msg']="Aparently all went well: "+str(ret)
695                       
696       
697                        # Append result of job and release mutex. Now all inform_me return finish
698                        self.locks[job['jobid']]['result'] = str(ret)
699                        self.locks[job['jobid']]['lock'].release()
700
701                        return str(e)
702               
703               
704        def inform_me(self,job_id):
705                '''
706                        Return result of job when finish
707                '''
708
709                self.locks[job_id]['lock'].acquire()
710                self.locks[job_id]['lock'].release()
711               
712                return {'status':True,'msg':self.locks[job_id]['result']}
713
714
715        def add_listener_to_job(self, ip, port, job):
716                '''
717                Description:
718                * Internal Function
719                * Adds a listener identified by an ip and a port to a job object
720               
721                How it Works:
722                * Creates a socket to send information to a listening socket in any client
723               
724                Last update:
725                    * 5/02/2014: Added Functionality
726                    * 2/04/2014: Add reference with job_id
727                '''
728               
729                try:
730                        # Create a socket for new listener
731                        srv=socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
732                       
733                        # connection to socket to send log
734                        srv.connect((ip, int(port)))
735                       
736                        # Defines a new listener
737                        listener={'ip':ip,
738                                'port':port,
739                                'socket':srv}
740               
741                        # Add listener to job
742                        job['listeners'].append(listener)
743                        print "[LmdServer] add_listener_to_job: adding "+str(listener)+"to listeners for job "+str(job['job_id'])
744                       
745                        # Sending status of job
746                        listener['socket'].send("<b>Start listening on "+str(listener['port'])+"</b>")                 
747                        listener['socket'].send("<b> Task:"+str(job['command'])+"</b>")
748                        listener['socket'].send("<b> Started by: "+str(job['started_by'])+"</b>")
749                       
750                       
751                               
752                        # Add reference with job_id
753                        tuple_ip_port = (str(ip),str(port))
754                        self.global_listeners[tuple_ip_port] = job['job_id']
755                        #print "Len",len(self.global_listeners)
756                        #print "isAlive",self.multicast_thread.isAlive()
757                        if not self.multicast_thread.isAlive():
758                                #print "lanzando el thread"
759                                self.multicast_thread = threading.Thread(target=self.Multicast, args=())
760                                self.multicast_thread.daemon = True
761                                self.multicast_thread.start()
762                                pass
763                       
764                        return port
765                       
766                except Exception as e:
767                        print("[LmdServer] add_listener_to_job Exception: "+str(e))
768                        return -1
769               
770                pass
771               
772        def add_listener(self, ip, port, job_id):
773                '''
774                Description:
775                * n4d method
776                * Adds a listener identified by an ip and a port to a job object identified by its id
777               
778                How it Works:
779                * Search whic job has certain job_id and adds a listener to it
780               
781                Last update:
782                    * 5/02/2014: Added Functionality
783                '''
784                from operator import itemgetter
785               
786                #print ("[add_listener DEBUG] address: "+str(ip)+":"+str(port)+" job id: "+str(job_id))
787                try:
788                        # Get job identified by job_id
789                        current_job_index=map(itemgetter('job_id'), self.joblist).index(str(job_id))
790                        current_job=self.joblist[current_job_index]
791                        # Add listener to current_job
792                        self.add_listener_to_job(ip, port, current_job)
793                except Exception as e:
794                        print ("[LmdServer] add_listener Exception "+str(e))
795                pass
796
797
798        def send(self, ip, job_id, info):
799                '''
800                receives info to send to stdin
801                '''
802                from operator import itemgetter
803
804                try:
805                        current_job_index=map(itemgetter('job_id'), self.joblist).index(str(job_id))
806                        current_job=self.joblist[current_job_index]
807                        # Get listener identified by (ip:port)
808                       
809                        #current_job['process'].stdin.writelines(str(info))
810                        if(current_job['status']=='running'):
811                                current_job['process'].stdin.write(str(info)+"\n")
812                        #current_job['process'].write(info);
813                except Exception as e:
814                        print ("ERROR writing to process: "+str(e));
815               
816               
817
818        def remove_listener(self, ip, port, job_id):
819                '''
820                Description:
821                * n4d method
822                * Removes listener identified by an ip and a port to a job object identified by its id
823               
824                How it Works:
825                * Searches which job has certain job_id (if specified) and removes it
826                * If job_id is not specified, get it from ip:port
827               
828                Last update:
829                    * 6/02/2014: Added Functionality
830                    * 2/04/2014: Added checking for job_id and close socket
831                '''
832               
833                       
834                from operator import itemgetter
835                try:
836                        # If job_id is None, search job_ib by ip and port
837                        tuple_ip_port = (str(ip),str(port))
838                        if job_id == None:
839                                job_id = self.global_listeners[tuple_ip_port]
840                        # Get job identified by job_id
841                        print self.joblist
842                        print tuple_ip_port
843                        print jobid
844                        current_job_index=map(itemgetter('job_id'), self.joblist).index(job_id)
845                        current_job=self.joblist[current_job_index]
846                        # Get listener identified by (ip:port)
847                        current_listener_index=map(itemgetter('ip','port'), current_job['listeners']).index((ip,int (port, base=10)))
848
849                        # Close port and remove listener to current_job
850                        current_job['listeners'][current_listener_index]['socket'].close()
851                        current_job['listeners'].remove(current_job['listeners'][current_listener_index])
852                        #remove listener reference
853                        self.global_listeners.pop(tuple_ip_port)
854                        return True
855                       
856                except Exception as e:
857                        print ("[LmdServer] remove_listener Exception "+str(e))
858                        return False
859                pass
860               
861       
862        def cancel_job(self, ip, port, job_id):
863                '''
864                Description:
865                * n4d method
866                *
867               
868                TO DO ...               
869                Same functionality that remove_listener and in addition, kills process identified by job
870               
871                '''
872                # Remove listener
873                from operator import itemgetter
874                try:
875                        # If job_id is None, search job_ib by ip and port
876                        tuple_ip_port = (str(ip),str(port))
877                        print (tuple_ip_port)
878                        if job_id == None:
879                                print self.global_listeners
880                                job_id = self.global_listeners[tuple_ip_port]
881                                print str(job_id)
882                               
883                        # Get job identified by job_id
884                        current_job_index=map(itemgetter('job_id'), self.joblist).index(job_id)
885                        print current_job_index
886                        current_job=self.joblist[current_job_index]
887                        print current_job
888                        # Get listener identified by (ip:port)
889                        try:
890                                current_listener_index=map(itemgetter('ip','port'), current_job['listeners']).index((ip,int (port, base=10)))
891                                print current_listener_index
892                        except:
893                                #print "*** jobid: ***"
894                                #print port
895                                print "*** port: ***"
896                                print port
897                                print "*** current_job: ***"
898                                print current_job['listeners']
899                                print "*** joblist: ***"
900                                print self.getJobList();
901                                current_listener_index=None
902                                for listener in current_job['listeners']:
903                                        self.remove_listener(str(ip),str(port), job_id);
904
905                        # Kill process (only addition to remove_listener)
906                        os.killpg(current_job['process'].pid,signal.SIGKILL)
907                       
908                        # Removes target
909                        print "Removing target "+ str (current_job['target']) 
910                        r=objects['LmdImageManager'].deleteImage(current_job['target']);
911                        print str(r);
912                        print "Removied target"
913                       
914                        current_job['status']='broken'
915                       
916                        if current_listener_index!=None:
917                                # Close port and remove listener to current_job
918                                current_job['listeners'][current_listener_index]['socket'].close()
919                                current_job['listeners'].remove(current_job['listeners'][current_listener_index])
920                                #remove listener reference
921                                self.global_listeners.pop(tuple_ip_port)
922                       
923                        return True
924                       
925                except Exception as e:
926                        print ("[LmdServer] cancel_job Exception "+str(e))
927                        return False
928                pass
929
930               
931               
932               
933               
934       
935        def getJobList(self):
936                '''
937                Description:
938                * N4D Method
939                * Return JSON with the job list
940                '''
941                #import urllib
942               
943                #ret= urllib.quote(str(self.joblist)[1:-1])
944                #ret= (str(self.joblist)[1:-1]).replace("'", "\"");
945                #ret= (str(self.joblist)).replace("'", "\"");
946                '''ret=ret.replace("<", "\"");
947                ret=ret.replace(">", "\"");'''
948                ret='[';
949                count=0;
950                for job in self.joblist:
951                        if (count>0):
952                                ret=ret+','
953                        ret=ret+'{"job_id":"'+str(job['job_id'])+'"'
954                        ret=ret+', "srv_ip":"'+str(job['srv_ip'])+'"'                   
955                        ret=ret+', "status":"'+str(job['status'])+'"'
956                        ret=ret+', "msg":"'+str(job['msg'])+'"'
957                        ret=ret+', "target":"'+str(job['target'])+'"'
958                        ret=ret+', "command":"'+str(job['command'])+'"'
959                        ret=ret+', "started_by":"'+str(job['started_by'])+'"}'
960                        count=count+1
961                        #print "*********************"
962                        #print "Local listeners"
963                        #print job['listeners']
964                        #print "*********************"
965                       
966                       
967               
968                ret=ret+']'
969                #print (ret)
970                #print "*********************"
971                #print "globals listeners"
972                #print self.global_listeners
973                #print "*********************"
974                return str(ret)
975               
976        def Multicast(self):
977                '''
978                Description:
979                * Internam method
980                * Multicast the output of all processes to its listeners
981               
982                How it works:
983                * Traverses the list of jobs and write its output to all its listeners
984               
985                * Last Update: 13/02/2014
986                '''
987                try:
988                        while len(self.global_listeners) > 0 :
989                                counter = 0
990                                #print "joblist",self.joblist
991                                #print "global_listeners",self.global_listeners
992                                for job in self.joblist:
993                                        if True : #job['status'] != "finished":
994                                                counter += 1
995                                                try:
996                                                        if not self.thread_jobs.has_key(job['job_id']):
997                                                                self.thread_jobs[job['job_id']] = threading.Thread(target=self.send_info_by_socket, args=(job,))
998                                                                self.thread_jobs[job['job_id']].daemon = True
999                                                                self.thread_jobs[job['job_id']].start()
1000                                                except Exception as e:
1001                                                        print e
1002                                if counter == 0:
1003                                        break
1004                                time.sleep(1)
1005                except Exception as e:
1006                        print "[LmdServer] EXCEPTION in Multicast: "+str(e)
1007                       
1008        def send_info_by_socket(self,job):
1009                try:
1010                        if not os.path.exists(job['filepipe']):
1011                                return False
1012                        pipe = open(job['filepipe'],'r')
1013                        pipe.seek(job['seek'],0)
1014                        try:
1015                                line = pipe.readline()
1016                                while (line and len(self.global_listeners)>0):
1017                                        for listener in job['listeners']:
1018                                                if(listener['socket']!=None):
1019                                                        try:
1020                                                                listener['socket'].send(line)
1021                                                        except Exception as e:
1022                                                                print "[LmdServer] EXCEPTION in Multicast internal loop: "+str(e)
1023                                                        continue
1024                                        line=pipe.readline()
1025                                        job['seek'] = pipe.tell()
1026                        except Exception as e:
1027                                print "[LMDServer] Exception wghile reading pipe "+str(e)
1028                                pass
1029                       
1030                        if self.thread_jobs.has_key(job['job_id']):
1031                                self.thread_jobs.pop(job['job_id'])
1032                               
1033                except Exception as e:
1034                                print "[LMDServer] Exception wghile reading pipe "+str(e)
1035                                pass
1036        def getLastJobId(self):
1037                return self.last_job_id
1038
1039       
1040        def getJobStatus(self, jobid):
1041                for i in self.joblist:
1042                        if i['job_id']==jobid:
1043                                return {"status": True, "msg": str(i['status'])}
1044               
1045                return {"status": False, "msg": 'bad luck, guy'}
1046
1047        def check_lmd_status(self):
1048                '''
1049                Description:
1050                * N4D Method
1051                * check status of lmd
1052                '''
1053                import os.path
1054                if (os.path.isfile("/tmp/.lmd-editing-chroot")):
1055                        return {"status": True, "lmd_status": 'lmd-editing-chroot'}
1056                else:
1057                        return {"status": True, "lmd_status": 'lmd-chroot-available'}
1058               
1059        def chek_minimal_client(self):
1060                if (os.path.isfile("/etc/ltsp/images/mini-light-client.json") and os.path.isfile("/opt/ltsp/images/mini-light-client.img") ):
1061                        return {"status": True}
1062                else:
1063                        return {"status": False}
1064               
1065
1066        def get_versions_13(self):
1067                '''
1068                Checks if there is any old image un system (13.06)
1069                '''
1070               
1071                ltsp_dir="/opt/ltsp/"
1072                nbd_dir="/etc/nbd-server/conf.d/"
1073               
1074                ret_list=[];
1075
1076                #OBSOLETE REMOVED METHOD
1077                #RETURN EMPTY LIST TO LMD-GUI TO AVOID LLIUREX 13 WARNING
1078                #for name in os.listdir(ltsp_dir):
1079                #       dir=ltsp_dir+name
1080                #       
1081                #       needs_update=False
1082                #       if (name!='images' and os.path.isdir(dir)):
1083                #               proc = subprocess.Popen(["chroot "+dir +" lliurex-version"], stdout=subprocess.PIPE, shell=True)
1084                #               (out, err) = proc.communicate()
1085                #               
1086                #               if ((not "16.05" in out) and out!="" ):
1087                #                       # check nbd...
1088                #                       for nbd_descriptor in os.listdir(nbd_dir):
1089                #                               if (self.line_in_file("["+ltsp_dir+name+"]", nbd_dir+nbd_descriptor)):
1090                #                                       needs_update=True
1091                #       if (needs_update==True):
1092                #               ret_list.append(name)
1093                #
1094                return ret_list
1095
1096        def line_in_file(self, line_to_find, filename):
1097                '''
1098                Aux function: ask for if certain line is contained in a file
1099                '''
1100                fd = open(filename, 'r')
1101                lines = fd.readlines()
1102                fd.close()
1103                for line in lines:
1104                        if str(line_to_find) in line:
1105                                return True
1106                               
1107                return False
1108       
1109        def update_images(self, ip, port, imagelist, srv_ip):
1110               
1111                try:
1112               
1113                        imagelist_string = " ".join(imagelist)
1114                        ##print imagelist," is ", type(imagelist)
1115                        ### n4d-client -c LmdServer -m update_images -u joamuran -p lliurex -a  2 3 "['pajarito', 'perro']"
1116                       
1117                        # Prepare and launch command
1118                        command="/usr/share/lmd-scripts/lmd-upgrade-images.sh "+imagelist_string;
1119                        result=self.do_command(ip, port, command, srv_ip, None)
1120                       
1121                        # Add image description
1122                        #imagelist=imagelist_string.replace("[","").replace("]","").replace(" ","").replace("'", "").replace(","," ");
1123                        #print imagelist_string;
1124                        print imagelist;
1125                        #print imagelist.split(" ");
1126                        for i in imagelist:
1127                                metadata = {'id':i,
1128                                        'name' : i,
1129                                        'desc' : 'Upgraded from Lliurex 13.06',
1130                                        'template' : 'none',
1131                                        'ltsp_fatclient': 'undefined',
1132                                        'ldm_session': 'default',
1133                                        'fat_ram_threshold':'default',
1134                                        'lmd_extra_params':''}
1135                               
1136                                metadata_string = unicode(json.dumps(metadata,indent=4,encoding="utf-8",ensure_ascii=False)).encode("utf-8")
1137                               
1138                                objects['LmdImageManager'].setImage(i,metadata_string)
1139                                self.set_default_boot(imgid)
1140                        return {"status": True}
1141                except Exception as e:
1142                        print "Exception", e
1143                        return {"status": False, 'msg':str(e)}
1144
1145
1146        def delete_images(self,imagelist):
1147               
1148                try:
1149               
1150                        for img in imagelist:
1151                                print img
1152                                path_chroot=os.path.join("/opt/ltsp/", img)
1153                                path_tftpboot=os.path.join("/var/lib/tftpboot/ltsp/", img)
1154                                path_img=os.path.join("/opt/ltsp/images/", img+".img")
1155                                path_nbd=os.path.join("/etc/nbd-server/conf.d/", "ltsp_"+img+".conf")
1156                                if (os.path.exists(path_chroot)):
1157                                        shutil.rmtree(path_chroot)
1158                                        #print "deleting: ",path_chroot
1159                                if (os.path.exists(path_tftpboot)):
1160                                        shutil.rmtree(path_tftpboot)
1161                                if (os.path.exists(path_img)):
1162                                        #print "deleting: ",path_img
1163                                        shutil.rmtree(path_img);
1164                                if (os.path.exists(path_nbd)):
1165                                        #print "deleting: ",path_nbd
1166                                        shutil.rmtree(path_nbd);
1167                               
1168                        return {"status": True, 'msg':'Finished'}
1169
1170                except Exception as e:
1171                        print "Exception", e
1172                        return {"status": False, 'msg':str(e)}
1173
1174        def LmdServerVersion(self):
1175                info=subprocess.check_output(["apt-cache","policy","lmd-server"])
1176                lines=str(info).split('\n')
1177                version=lines[1][13:]
1178                return (version)
1179
1180        def check_update_images(self):
1181                list_dirs = [ os.path.join(self.ltsp_path,x) for x in os.listdir(self.ltsp_path) if os.path.isdir(os.path.join(self.ltsp_path,x)) ]
1182                list_need_update = []
1183                for chroot in list_dirs:
1184                        available_file = os.path.join(chroot,'var','lib','dpkg','available')
1185                        if os.path.exists(available_file):
1186                                available_fd = open(available_file,'r')
1187                                available_content = available_fd.readlines()
1188                                try:
1189                                        pos = available_content.index('Package: lmd-client\n')
1190                                        version = None
1191                                        for i in range(pos + 1 ,len(available_content)):
1192                                                if available_content[i] == '\n':
1193                                                        break
1194                                                if available_content[i].startswith('Version'):
1195                                                        version = available_content[i][8:].strip()
1196                                        if version != None :
1197                                                apt_pkg.init()
1198                                                if apt_pkg.version_compare(version,'0.15') < 0 :
1199                                                        list_need_update.append(os.path.basename(chroot))
1200                                except Exception as e:
1201                                        pass
1202                if len(list_need_update) > 0:
1203                        return [True,list_need_update]
1204                else:
1205                        return [False,[]]
1206                       
1207               
1208               
Note: See TracBrowser for help on using the repository browser.