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

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

add script to fix architecture when build image from iso

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