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

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

Added import/export/clone and edit options for image capabilities

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