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

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

add support to run cancel command

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