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

Last change on this file since 6761 was 6761, checked in by kbut, 20 months ago

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