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

Last change on this file since 6843 was 6843, checked in by kbut, 3 years ago

Add feature to delay refresh image

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