You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1533 lines
60 KiB

  1. #-*- coding: utf-8 -*-
  2. # Copyright 2010 Calculate Ltd. http://www.calculate-linux.org
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. from os import path
  16. from random import choice
  17. import string
  18. import os
  19. import types
  20. from time import sleep
  21. import re
  22. import sys
  23. import operator
  24. from cl_utils import runOsCommand,isMount,removeDir,typeFile,pathJoin, \
  25. process,getRunCommands,getTupleVersion,cmpVersion, \
  26. detectDeviceForPartition, getProgPath,listDirectory, \
  27. checkUtils,STDOUT,getUdevDeviceInfo, countPartitions
  28. from cl_vars_share import varsShare
  29. from shutil import copyfile,copytree
  30. from cl_template import _terms
  31. from subprocess import Popen,PIPE,STDOUT
  32. from cl_fill import fillVars
  33. from cl_vars_share import varsShare
  34. import threading
  35. from cl_lang import lang
  36. tr = lang()
  37. tr.setLocalDomain('cl_lib')
  38. tr.setLanguage(sys.modules[__name__])
  39. class SignalInterrupt:
  40. __interruptProcessObjs = []
  41. __sigint = False
  42. def setSignalInterrupt(self):
  43. """Handler "SIGINT"""
  44. if SignalInterrupt.__sigint is False:
  45. sys.stdout.write("\b\b")
  46. # killed processes
  47. while (SignalInterrupt.__interruptProcessObjs):
  48. process = SignalInterrupt.__interruptProcessObjs.pop()
  49. if hasattr(process, "pipe") and hasattr(process.pipe,"kill"):
  50. process.pipe.kill()
  51. SignalInterrupt.__sigint = True
  52. def getSignalInterrupt(self):
  53. return SignalInterrupt.__sigint
  54. def addInterruptProcess(self, process):
  55. SignalInterrupt.__interruptProcessObjs.append(process)
  56. class Spinner(threading.Thread):
  57. stopSignal = threading.Event()
  58. sequence= map(lambda x:x,iter("/-\\|"))
  59. write = sys.stdout.write
  60. selfthread = None
  61. def setWriteFunc(self,writeFunc):
  62. Spinner.write = writeFunc
  63. def init(self,checkFunction=None,interval=0.1):
  64. self.curpos = 0
  65. self.checkFunction = checkFunction
  66. self.interval = interval
  67. Spinner.stopSignal.clear()
  68. def stop(self):
  69. Spinner.stopSignal.set()
  70. if Spinner.selfthread:
  71. Spinner.selfthread.join(2)
  72. Spinner.selfthread = None
  73. #Spinner().write('\n')
  74. Spinner().write('\b')
  75. def run(self):
  76. self.write(self.sequence[-1])
  77. Spinner.selfthread = self
  78. while not Spinner.stopSignal.isSet():
  79. if self.checkFunction and self.checkFunction():
  80. self.write("\b%s"%self.sequence[self.curpos])
  81. self.curpos += 1
  82. if self.curpos >= len(self.sequence):
  83. self.curpos = 0
  84. sleep(self.interval)
  85. class DistributiveError(Exception):
  86. """Error for distributive operations"""
  87. pass
  88. class DistributiveRepository:
  89. varsShare = varsShare()
  90. contentCache = {}
  91. marches = ['i686','x86_64']
  92. extensiton = ['iso','tar.bz2','tar.gz','tar.7z','tar.lzma']
  93. reDistName = re.compile("""
  94. ^.*/(?P<name>%(name)s)
  95. -(?P<ver>%(ver)s)
  96. -(?P<march>%(march)s)
  97. .(?P<ext>%(ext)s)$""" %
  98. {'name':"[a-z0-9]+",
  99. 'ver':r"(\d+\.)*\d+",
  100. 'march':"|".join(marches),
  101. 'ext':"|".join(extensiton)
  102. }, re.X)
  103. def __init__(self,directories=[]):
  104. self.dirs = directories
  105. def ini_to_dict(self,filename):
  106. """Convert ini values to dict"""
  107. if os.access(filename,os.R_OK):
  108. return dict(map(lambda x: x.strip().rpartition('=')[0::2],
  109. open(filename,'r')))
  110. else:
  111. return {}
  112. def _getfromcontent(self,filename,addFunc=None):
  113. """Get info from content"""
  114. origfilename = filename
  115. if filename in DistributiveRepository.contentCache:
  116. return DistributiveRepository.contentCache[filename].copy()
  117. varsShare = self.varsShare
  118. distr = None
  119. # may be directory is isodir (directory which contains iso image)
  120. extname = "isodir"
  121. try:
  122. distr = IsoDistributive(filename)
  123. if not distr.probe() and filename.startswith('/dev'):
  124. distr.close()
  125. distr = None
  126. distr = PartitionDistributive(filename)
  127. filename = distr.convertToDirectory().directory
  128. except Exception,e:
  129. extname = "dir"
  130. d = self.ini_to_dict(path.join(filename,
  131. 'etc/calculate/calculate.ini'))
  132. if not d or not "march" in d:
  133. if path.exists(path.join(filename,'lib64')):
  134. d['march'] = 'x86_64'
  135. else:
  136. d['march']= 'i686'
  137. if d:
  138. d['ext'] = extname
  139. d["name"] = varsShare.getShortnameByMakeprofile(filename) or \
  140. varsShare.getShortnameByIni(filename) or \
  141. varsShare.detectOtherShortname(filename) or \
  142. "Linux"
  143. d['ver'] = \
  144. varsShare.getVersionFromMetapackage(filename,d["name"]) or \
  145. varsShare.getVersionFromCalculateIni(filename) or "0"
  146. reOsLinuxBuild = re.compile("^os_linux_build\s*=\s*(\S+)\s*$")
  147. os_linux_build = \
  148. map(lambda x:x.groups()[0],
  149. filter(lambda x:x,
  150. map(reOsLinuxBuild.search,
  151. reduce(lambda x,y:x+y,
  152. map(lambda x:open(x,"r").readlines(),
  153. filter(path.exists,
  154. [path.join(filename,"etc/calculate/calculate2.env")])),[]))))
  155. if os_linux_build:
  156. d['build'] = os_linux_build[-1]
  157. else:
  158. d['build'] = ""
  159. if addFunc:
  160. d = addFunc(filename,d)
  161. if distr:
  162. distr.close()
  163. DistributiveRepository.contentCache[origfilename] = d
  164. return d.copy()
  165. def _getdistrinfo(self,filename):
  166. """Get information by distributive"""
  167. varsShare = self.varsShare
  168. # if filename is directory
  169. if not path.isfile(filename):
  170. return self._getfromcontent(filename)
  171. else:
  172. match = self.reDistName.match(filename)
  173. if not match:
  174. return {}
  175. distdic = match.groupdict()
  176. distdic["build"] = ""
  177. if "ver" in distdic:
  178. if re.match("^\d{8}$", distdic["ver"]):
  179. distdic["build"] = distdic["ver"]
  180. distdic["ver"] = ""
  181. return distdic
  182. def _getAvailableShortnames(self):
  183. """Get available distributives shortnames"""
  184. distros = filter(lambda x:x,
  185. map(self.reDistName.search,self._getAvailableDistributives()))
  186. return sorted(list(set(map(lambda x:x.groupdict()['name'],distros))))
  187. def _getAvailableDistributives(self,system=None,shortname=None,march=None,
  188. version=None,op_compare=None):
  189. """Get all distributives by filter"""
  190. if op_compare is None:
  191. op_compare = operator.eq
  192. if version:
  193. version = getTupleVersion(version)
  194. def distfilter(dist):
  195. d = self._getdistrinfo(dist)
  196. if not d:
  197. return False
  198. # check filter conditions
  199. if system and self.system(d['name']) != system:
  200. return False
  201. if not "name" in d or not "ver" in d:
  202. return False
  203. if shortname and d['name'].lower() != shortname.lower():
  204. return False
  205. if march and d['march'] != march:
  206. return False
  207. if version and not op_compare(getTupleVersion(d['ver']), version):
  208. return False
  209. return True
  210. def listdistr(pathname):
  211. if path.exists(path.join(pathname,'etc/make.profile')):
  212. return [pathname]
  213. elif path.exists(path.join(pathname,'livecd')):
  214. return [pathname]
  215. elif pathname.startswith('/dev/'):
  216. return [pathname]
  217. else:
  218. # discard inner directories
  219. return filter(lambda x:not path.isdir( path.join(pathname,x)),
  220. listDirectory(pathname))
  221. # get lists files in directories
  222. allFiles = map(lambda x: map(lambda y: path.join(x,y),
  223. listdistr(x)),
  224. # discard not exists distrib directories
  225. filter(lambda x: os.access(x,os.R_OK),
  226. self.dirs))
  227. # filter distributives
  228. return filter(distfilter,
  229. # join files lists to one list
  230. reduce(lambda x,y: x + y,
  231. allFiles, []))
  232. def getDistributiveByFile(self,filename):
  233. """Get Distributive object by filename"""
  234. # MAGIC_COMPRESS 0x000004 Check inside compressed files
  235. tf = typeFile(magic=0x6|0x20)
  236. ftype = tf.getMType(filename)
  237. if "block special" in ftype:
  238. return IsoDistributive(filename)
  239. if "ISO 9660 CD-ROM" in ftype:
  240. return IsoDistributive(filename)
  241. elif "7-zip" in ftype or \
  242. "POSIX tar archive" in ftype:
  243. return ArchiveDistributive(filename)
  244. elif "Squashfs filesystem" in ftype:
  245. return SquashDistributive(filename)
  246. elif path.isdir(filename):
  247. if path.exists(path.join(filename,"livecd")):
  248. return IsoDistributive(filename)
  249. else:
  250. return DirectoryDistributive(filename)
  251. else:
  252. raise DistributiveError(_("Wrong distribution") + " '%s':\n%s"\
  253. %(filename,ftype))
  254. def extcomparator(self,*exts):
  255. avexts = ("iso","isodir","dir")
  256. vals = []
  257. for i in exts:
  258. if i in avexts:
  259. vals.append(avexts.index(i))
  260. else:
  261. vals.append(-1)
  262. return cmp(vals[0],vals[1])
  263. term = _terms()
  264. def sortdistrfunc(self,x,y):
  265. """Func of comparing two distributive"""
  266. ver1, ver2 = x[1].get('ver',""), y[1].get('ver',"")
  267. if ver1 and ver2 and ver1 != "0" and ver2 != "0" and ver1 != ver2:
  268. return cmpVersion(ver1,ver2)
  269. build1,build2 = self.term._convertVers(x[1].get('build',""),
  270. y[1].get('build',""))
  271. if build1 != build2:
  272. return cmp(build1,build2)
  273. else:
  274. ext1 = x[1].get('ext',"")
  275. ext2 = y[1].get('ext',"")
  276. return self.extcomparator(ext1,ext2)
  277. def getBestDistributive(self,system=None,shortname=None,march=None,
  278. version=None,discardType=[], op_compare=None):
  279. """Get the actualest distributive"""
  280. availDistrs = self._getAvailableDistributives(system,shortname,
  281. march,version,
  282. op_compare)
  283. availDistrs = filter(lambda x:x[1] and "ext" in x[1] and
  284. not x[1]["ext"] in discardType,
  285. map(lambda x:(x,self._getdistrinfo(x)),
  286. availDistrs))
  287. availDistrs = sorted(availDistrs,self.sortdistrfunc,reverse=True)
  288. if availDistrs:
  289. return availDistrs[0][0]
  290. else:
  291. return None
  292. def _findLatestFile(self,dirs,reMatch,keyfunc):
  293. """Find latest file in dirs, which match by reMatch,
  294. comparable part get by keyfunc"""
  295. existsdirs = filter(path.exists,dirs)
  296. listimgs = reduce(lambda x,y:x + \
  297. map(lambda x:reMatch.search(path.join(y,x)),
  298. listDirectory(y)),
  299. existsdirs,[])
  300. listimgs = filter(lambda x:x, listimgs)
  301. if listimgs:
  302. return max(listimgs,key=keyfunc).group()
  303. return ""
  304. def getBestStage(self,march=None,hardened=None):
  305. """Get latest stage by march"""
  306. if march:
  307. march = {'x86_64':'amd64'}.get(march,march)
  308. else:
  309. march = "[^-]+"
  310. if hardened is None:
  311. hardened = "(?:-hardened)?"
  312. elif hardened == True:
  313. hardened = "-hardened"
  314. elif hardened == False:
  315. hardened = ""
  316. reStage = re.compile(r'^.*/stage3-%s%s-(\d+)\.tar\.bz2$'%
  317. (march,hardened),re.S)
  318. return self._findLatestFile(self.dirs,reStage,lambda x:x.groups()[0])
  319. class Distributive(object, SignalInterrupt):
  320. """Distributive object. Parent object for all distributive."""
  321. mountError = _("Failed to mount") + " %s:\n%s"
  322. reLive = re.compile(r"^live[^.]*\.squashfs(\.(\d+))?$",re.S)
  323. flagSpinner=True
  324. def __init__(self, parent=None):
  325. self.childs = []
  326. # if specified parent
  327. if parent:
  328. # save parent type for correct resource release
  329. self.parent = parent
  330. # append this object as child to specified parent
  331. parent.childs.append(self)
  332. else:
  333. self.parent = None
  334. def detach(self):
  335. """Detach child distributive from parent.
  336. At example: ArchiveDistributive create child DirectoryDistributive by
  337. unpacking to temporary directory and at close method remove it. If the
  338. removing directroy do not need, then need call detach in
  339. DirectoryDistributive object
  340. dist1 = ArchiveDistributive(file="/os.tar.bz2",mdirectory="/tmp/test")
  341. dist2 = dist1.convertToDirectory()
  342. dist2.detach()
  343. dist1.close()
  344. ...
  345. """
  346. self.parent = None
  347. def close(self):
  348. """Release all child distributive and release himself.
  349. Need call this method at end work with object for right releasing
  350. resources.
  351. Example:
  352. dist1 = PartitionDistributive(partition="/dev/sda2")
  353. dist2 = dist1.convertToDirectory()
  354. dist1.close()
  355. """
  356. # close all child
  357. if self.childs:
  358. for child in reversed(self.childs):
  359. # check detach
  360. if child.parent:
  361. child.close()
  362. self.childs = []
  363. # if has parent
  364. if self.parent:
  365. self.parent.releaseChild(self)
  366. self.parent = None
  367. def releaseChild(self,child):
  368. """Method of release child state of distributive
  369. At example: PartitionDistributive may be DirectoryDistributive by
  370. mounting it to directory. But at end this directory must be
  371. unmounted."""
  372. pass
  373. def convertToDirectory(self):
  374. """Default c raise error about impossible convert object"""
  375. raise DistributiveError(_("Failed to convert") + " '%s' "\
  376. %self.__class__.__name__ + _("to") +\
  377. " '%s'" %"DirectoryDistributive")
  378. # def __del__(self):
  379. # """Uncomment this method for automaticaly release all distributive
  380. # instance"""
  381. # self.close()
  382. def runOsCommand(self, *argv, **kwarg):
  383. res, mes = runOsCommand(*argv,**kwarg)
  384. mes = "\n".join(map(lambda x: x.strip(), mes))
  385. return res,mes
  386. def getDirectory(self):
  387. """Get directory which contains distro"""
  388. return self.convertToDirectory().directory
  389. def getBootDirectory(self):
  390. """Get directory which contains boot"""
  391. return path.join(self.getDirectory(),"boot")
  392. def _makeDirectory(self,pathname):
  393. """Make directory and parent.
  394. If directory exists then return False else True"""
  395. try:
  396. parent = path.split(path.normpath(pathname))[0]
  397. if not path.exists(parent):
  398. self._makeDirectory(parent)
  399. else:
  400. if path.exists(pathname):
  401. return False
  402. os.mkdir(pathname)
  403. return True
  404. except Exception, e:
  405. raise DistributiveError(_("Failed to create the directory") +
  406. " '%s':\n%s" %(pathname,str(e)))
  407. except KeyboardInterrupt, e:
  408. self.setSignalInterrupt()
  409. raise DistributiveError(_("Failed to create the directory") +
  410. " '%s':\n%s" %(pathname,str(e)))
  411. def _removeDirectory(self,directory):
  412. """Remove directory and files contained in it"""
  413. try:
  414. removeDir(directory)
  415. except Exception, e:
  416. raise DistributiveError(_("Failed to remove the directory from") +\
  417. " '%s':\n%s" %(directory,str(e)))
  418. except KeyboardInterrupt, e:
  419. self.setSignalInterrupt()
  420. raise DistributiveError(_("Failed to remove the directory from") +\
  421. " '%s':\n%s" %(directory,str(e)))
  422. def _copyfile(self,infile,outfile):
  423. try:
  424. copyfile(infile,outfile)
  425. except (Exception),e:
  426. raise DistributiveError(_("Failed to copy") + " '%s' to '%s':\n%s"\
  427. %(infile,outfile,str(e)))
  428. def _copytree(self,indir,outdir):
  429. try:
  430. copytree(indir,outdir,symlinks=True)
  431. except Exception ,e:
  432. raise DistributiveError(_("Failed to copy") + " '%s' to '%s':\n%s"\
  433. %(indir,outdir,str(e)))
  434. except KeyboardInterrupt, e:
  435. self.setSignalInterrupt()
  436. raise DistributiveError(_("Failed to copy") + " '%s' to '%s':\n%s"\
  437. %(indir,outdir,str(e)))
  438. def rsync(self,fromdir,todir,hideSpin=False):
  439. """Copy files from 'fromdir' directory to 'todir' directory"""
  440. if self.flagSpinner and not hideSpin:
  441. def checkRsync():
  442. try:
  443. return len(filter(lambda x:"rsync\x00-a\x00-H\x00-x" in x,
  444. getRunCommands()))>2
  445. except:
  446. return False
  447. spin = Spinner()
  448. spin.init(checkFunction=checkRsync,interval=0.6)
  449. spin.start()
  450. rsyncCmd = varsShare().getProgPath('/usr/bin/rsync')
  451. if not rsyncCmd:
  452. raise DistributiveError(_("Failed to find '%s' command")%"rsync")
  453. try:
  454. rsyncProcess = process(rsyncCmd, "-a","-H", "-x","-XX",
  455. "%s/"%fromdir,"%s/"%todir,stderr=STDOUT)
  456. self.addInterruptProcess(rsyncProcess)
  457. res = rsyncProcess.success()
  458. errmes = rsyncProcess.read()
  459. except Exception,e:
  460. res = False
  461. errmes = str(e)
  462. except KeyboardInterrupt:
  463. self.setSignalInterrupt()
  464. if self.flagSpinner:
  465. spin.stop()
  466. raise KeyboardInterrupt()
  467. if self.flagSpinner and not hideSpin:
  468. spin.stop()
  469. if res is True:
  470. return True
  471. else:
  472. if "No space left on device" in errmes:
  473. errmes = "No space left on device"
  474. raise DistributiveError(_("Failed to copy files from") +\
  475. " '%s' " %fromdir + _("to") +\
  476. " '%s':\n%s" %(todir,errmes))
  477. def _mountToBind(self,srcDirectory,destDirectory):
  478. """Mount srcDirectory to destDirectory"""
  479. mount = process('/bin/mount',"-o","bind",srcDirectory,destDirectory,
  480. stderr=STDOUT)
  481. if mount.success():
  482. return True
  483. else:
  484. try:
  485. os.rmdir(destDirectory)
  486. except:
  487. pass
  488. raise DistributiveError(self.mountError%(srcDirectory,mount.read()))
  489. def performFormat(self):
  490. pass
  491. def formatPartition(self,format=""):
  492. pass
  493. def rndString(self):
  494. """Get random string with len 8 char"""
  495. return "".join([choice(string.ascii_letters+string.digits)
  496. for i in xrange(0,8)])
  497. def _getSquashNum(self,reMatch):
  498. if reMatch.groups()[1] and reMatch.groups()[1].isdigit():
  499. return int(reMatch.groups()[1])
  500. else:
  501. return 0
  502. def _getLastLive(self,directory):
  503. """Get last live squashfs image from directory"""
  504. squashfiles = filter(lambda x:x,
  505. map(self.reLive.search,
  506. listDirectory(directory)))
  507. if squashfiles:
  508. return max(squashfiles, key=self._getSquashNum).group()
  509. return None
  510. def _mountToDirectory(self,file,directory,mountopts="",count=2):
  511. """Mount squashfs to directory"""
  512. NO_SUCH_DEVICE = 2816
  513. if isMount(directory):
  514. raise DistributiveError(_("Failed to mount to the directory: %s\n")\
  515. %directory+ _("Directory already mounted"))
  516. mountopts = filter(lambda x:x,
  517. mountopts.split(" "))
  518. mountProcess = process('/bin/mount',file,directory,*mountopts,
  519. stderr=STDOUT)
  520. if mountProcess.success():
  521. return True
  522. else:
  523. #2816 code return by mount if device is absent (update /dev by udev)
  524. # try mount 3 times with interval 0.5 second
  525. if mountProcess.returncode() == NO_SUCH_DEVICE and count:
  526. sleep(0.5)
  527. mountProcess.close()
  528. return self._mountToDirectory(file,directory,mountopts,count-1)
  529. try:
  530. self._removeDirectory(directory)
  531. except:
  532. pass
  533. raise DistributiveError(self.mountError%(file,mountProcess.read()))
  534. def _umountDirectory(self,directory):
  535. """Umount directory"""
  536. if isMount(directory):
  537. for wait in [0,0.5,2,5]:
  538. sleep(wait)
  539. processUmount = process('/bin/umount',directory,stderr=STDOUT)
  540. if processUmount.success():
  541. return True
  542. raise DistributiveError(_("Failed to umount") + " %s:\n%s"%
  543. (directory,processUmount.read()))
  544. else:
  545. return True
  546. def _getMntDirectory(self,directory):
  547. """Get directory name, which will use for mounting or unpacking
  548. If queried name is not free then to name append random string
  549. """
  550. newDirectoryName = directory
  551. while path.exists(newDirectoryName):
  552. newDirectoryName = "%s.%s"%(directory,self.rndString())
  553. return newDirectoryName
  554. class DirectoryDistributive(Distributive):
  555. def __init__(self,directory,parent=None,mdirectory=None):
  556. Distributive.__init__(self,parent=parent)
  557. self.directory = directory
  558. self.mdirectory = mdirectory
  559. if not parent:
  560. self._makeDirectory(self.directory)
  561. def bindDirectory(self,mdirectory):
  562. for child in self.childs:
  563. if isinstance(child,DirectoryDistributive) and \
  564. mdirectory in child.directory:
  565. return child
  566. mdirectory = self._getMntDirectory(mdirectory)
  567. self._makeDirectory(mdirectory)
  568. self._mountToBind(self.directory,mdirectory)
  569. return DirectoryDistributive(mdirectory,parent=self)
  570. def releaseChild(self,child):
  571. """Remove child Directory distributive"""
  572. if isinstance(child,DirectoryDistributive):
  573. self._umountDirectory(child.directory)
  574. self._removeDirectory(child.directory)
  575. child.directory = None
  576. def convertToDirectory(self):
  577. if self.mdirectory:
  578. return self.bindDirectory(self.mdirectory)
  579. else:
  580. return self
  581. def performFormat(self):
  582. """Format for directory - removing all files"""
  583. execStr = '/bin/rm -rf --one-file-system %s'%self.directory
  584. res,errmes = self.runOsCommand(execStr)
  585. if res == 0:
  586. return True
  587. else:
  588. raise DistributiveError(_("Failed to format the partition") +
  589. " %s:\n%s"%(dev,errmes))
  590. self._makeDirectory(self.directory)
  591. def installFrom(self, source):
  592. """Install distributive to directory from source distributive"""
  593. if isinstance(source,ArchiveDistributive):
  594. source.unpackTo(self.directory)
  595. else:
  596. # get source distributive as directory distributive
  597. dFrom = source.convertToDirectory()
  598. # copy distributive from source to this
  599. self.rsync(dFrom.directory,self.directory)
  600. class DataPartition:
  601. """Data partition"""
  602. dev = None
  603. mountPoint = None
  604. fileSystem = "reiserfs"
  605. isFormat = False
  606. systemId = None
  607. partitionTable = None
  608. class MultiPartitions:
  609. """Data partition list"""
  610. listPartitions = []
  611. def addPartition(self, **argv):
  612. """Add partition in data partition list"""
  613. dictDataPart = reduce(lambda x,y:\
  614. x.update({y:getattr(DataPartition,y)}) or x,
  615. filter(lambda x: not x.startswith('_'),
  616. DataPartition.__dict__),{})
  617. updateAttrData = filter(lambda x: x[1]!=None, dictDataPart.items())
  618. defaultAttr = []
  619. for attrName, attrValue in updateAttrData:
  620. if not attrName in argv.keys():
  621. defaultAttr.append(attrName)
  622. argv[attrName] = attrValue
  623. if set(argv.keys()) != set(dictDataPart.keys()):
  624. notFoundAttr = set(dictDataPart.keys()) - set(argv.keys())
  625. if notFoundAttr:
  626. raise DistributiveError(_("The following attributes "
  627. "(%s) are not specified")\
  628. %", ".join(map(lambda x:"DataPartition.%s"%x, notFoundAttr)))
  629. unnecessaryAttr = (set(dictDataPart.keys()) ^ set(argv.keys())) -\
  630. set(dictDataPart.keys())
  631. if unnecessaryAttr:
  632. raise DistributiveError(_("Failed to use attributes (%s)")\
  633. %", ".join(map(lambda x:"DataPartition.%s"%x, unnecessaryAttr)))
  634. else:
  635. partObj = DataPartition()
  636. for attr, value in argv.items():
  637. if attr in defaultAttr:
  638. continue
  639. setattr(partObj,attr,value)
  640. self.listPartitions.append(partObj)
  641. def getSystemId(self):
  642. """Get systemID for change [None,82,...]"""
  643. return map(lambda x: x.systemId, self.listPartitions)
  644. def getPartitionTable(self):
  645. """Get systemID for change [dos,gpt,...]"""
  646. return map(lambda x: x.partitionTable, self.listPartitions)
  647. def getIsFormat(self):
  648. """Get list is format [True,...]"""
  649. return map(lambda x: x.isFormat, self.listPartitions)
  650. def getFileSystems(self):
  651. """Get list file systems ["reiserfs",...]"""
  652. return map(lambda x: x.fileSystem, self.listPartitions)
  653. def getPartitions(self):
  654. """Get list partition ["/dev/sda",...]"""
  655. return map(lambda x: x.dev, self.listPartitions)
  656. def getMountPoints(self):
  657. """Get list mount point ["/boot",...]"""
  658. return map(lambda x: x.mountPoint, self.listPartitions)
  659. class PartitionDistributive(Distributive):
  660. formatUtilities = { 'ext2':'/sbin/mkfs.ext2 %s %s',
  661. 'ext3':'/sbin/mkfs.ext3 %s %s',
  662. 'ext4':'/sbin/mkfs.ext4 %s %s',
  663. 'jfs':'/sbin/mkfs.jfs %s -f %s',
  664. 'reiserfs':'/sbin/mkfs.reiserfs %s -f %s',
  665. 'btrfs':'/sbin/mkfs.btrfs %s %s',
  666. 'nilfs2':'/sbin/mkfs.nilfs2 %s %s',
  667. 'xfs':'/sbin/mkfs.xfs %s -f %s',
  668. 'vfat':'/usr/sbin/mkfs.vfat %s -F 32 %s',
  669. 'ntfs-3g':'/usr/sbin/mkfs.ntfs %s -FQ %s',
  670. 'ntfs':'/usr/sbin/mkfs.ntfs %s -FQ %s',
  671. 'swap':'/sbin/mkswap %s'
  672. }
  673. labelForUtilities = { 'ext2':'-L %s',
  674. 'ext3':'-L %s',
  675. 'ext4':'-L %s',
  676. 'btrfs':'-L %s',
  677. 'nilfs2':'-L %s',
  678. 'jfs':'-L %s',
  679. 'reiserfs':'-l %s',
  680. 'xfs':'-L %s',
  681. 'vfat':'-n %s',
  682. 'ntfs-3g':'-L %s',
  683. 'ntfs':'-L %s',
  684. }
  685. formatId = { 'ext2' : '83',
  686. 'ext3' : '83',
  687. 'ext4' : '83',
  688. 'reiserfs' : '83',
  689. 'btrfs' : '83',
  690. 'nilfs2' : '83',
  691. 'jfs' : '83',
  692. 'xfs' : '83',
  693. 'vfat' : '0b',
  694. 'swap' : '82'
  695. }
  696. formatIdGpt = { 'ext2' : '0700',
  697. 'ext3' : '0700',
  698. 'ext4' : '0700',
  699. 'reiserfs' : '0700',
  700. 'btrfs' : '0700',
  701. 'nilfs2' : '0700',
  702. 'jfs' : '0700',
  703. 'xfs' : '0700',
  704. 'vfat' : '0700',
  705. 'swap' : '8200'
  706. }
  707. def __init__(self,partition,parent=None,mdirectory="/mnt/calculate",
  708. check=False,multipartition=None,flagRemoveDir=True,
  709. fileSystem="reiserfs", isFormat=True,systemId=None,
  710. rootLabel="Calculate", partitionTable=None):
  711. """Initialize partition distributive
  712. mdirectory - directory for mount
  713. check - check partition name and raise DistributiveError if partition
  714. has bad name
  715. """
  716. Distributive.__init__(self,parent=parent)
  717. self.partition = partition
  718. self.fileSystem = fileSystem
  719. self.mdirectory = mdirectory
  720. self.multipartition = multipartition
  721. self.flagRemoveDir = flagRemoveDir
  722. self.isFormat = isFormat
  723. self.DirectoryObject = None
  724. self.systemId = systemId
  725. self.rootLabel = rootLabel
  726. self.partitionTable = partitionTable
  727. def _mountPartition(self,partition,directory,opts=""):
  728. """Mount partition to directory"""
  729. self._makeDirectory(directory)
  730. if "ntfs" in opts:
  731. source_dir = isMount(partition)
  732. if source_dir:
  733. self._mountToBind(source_dir,directory)
  734. return
  735. self._mountToDirectory(partition,directory,opts)
  736. def _umountPartition(self,directory):
  737. """Umount partition and remove directory"""
  738. self._umountDirectory(directory)
  739. if self.flagRemoveDir:
  740. self._removeDirectory(directory)
  741. def releaseChild(self,child):
  742. """Umount child Directory distributive"""
  743. if isinstance(child,DirectoryDistributive):
  744. self._umountPartition(child.directory)
  745. child.directory = None
  746. def _mountBind(self,srcDirectory,destDirectory):
  747. """Mount directory to directory"""
  748. self._makeDirectory(destDirectory)
  749. self._makeDirectory(srcDirectory)
  750. self._mountToBind(srcDirectory,destDirectory)
  751. def postinstallMountBind(self):
  752. """Mount bind mount point and create mount dirs"""
  753. if self.multipartition and self.DirectoryObject:
  754. mulipartDataBind = filter(lambda x: x[2]=="bind",
  755. self.getMultipartData())
  756. dirObj = self.DirectoryObject
  757. mdirectory = dirObj.directory
  758. for srcDir, destDir, fileSystem, isFormat, partTable \
  759. in mulipartDataBind:
  760. realDestDir = pathJoin(mdirectory, destDir)
  761. realSrcDir = pathJoin(mdirectory, srcDir)
  762. self._mountBind(realSrcDir, realDestDir)
  763. isFormat = False
  764. partObj = PartitionDistributive(realSrcDir, flagRemoveDir=False,
  765. fileSystem=fileSystem,
  766. isFormat=isFormat,
  767. parent=dirObj)
  768. DirectoryDistributive(realDestDir,parent=partObj)
  769. def getMultipartData(self):
  770. """Get multipartition data"""
  771. mulipartData = zip(self.multipartition.getPartitions(),
  772. self.multipartition.getMountPoints(),
  773. self.multipartition.getFileSystems(),
  774. self.multipartition.getIsFormat(),
  775. self.multipartition.getPartitionTable())
  776. return mulipartData
  777. def convertToDirectory(self):
  778. """Convert partition to directory by mounting"""
  779. mdirectory = self.mdirectory
  780. for child in self.childs:
  781. if isinstance(child,DirectoryDistributive) and \
  782. mdirectory in child.directory:
  783. return child
  784. mdirectory = self._getMntDirectory(mdirectory)
  785. self._mountPartition(self.partition,mdirectory)
  786. dirObj = DirectoryDistributive(mdirectory,parent=self)
  787. if self.multipartition:
  788. mulipartDataNotBind = filter(lambda x: x[2]!="bind",
  789. self.getMultipartData())
  790. for dev, mountPoint, fileSystem, isFormat, partTable\
  791. in mulipartDataNotBind:
  792. if fileSystem!="swap":
  793. realMountPoint = pathJoin(mdirectory, mountPoint)
  794. self._mountPartition(dev,realMountPoint,"-t %s"%fileSystem)
  795. partObj = PartitionDistributive(dev, flagRemoveDir=False,
  796. fileSystem=fileSystem,
  797. isFormat=isFormat,
  798. parent=dirObj)
  799. if fileSystem!="swap":
  800. DirectoryDistributive(realMountPoint,parent=partObj)
  801. self.DirectoryObject = dirObj
  802. return dirObj
  803. def formatAllPartitions(self):
  804. """Format all partitions"""
  805. FS,DEV,NEEDFORMAT,NEWID,PARTTABLE = 0,1,2,3,4
  806. # get all information to matrix
  807. dataPartitions = zip(self.multipartition.getFileSystems() +\
  808. [self.fileSystem],
  809. self.multipartition.getPartitions() + \
  810. [self.partition],
  811. self.multipartition.getIsFormat() + \
  812. [self.isFormat],
  813. self.multipartition.getSystemId() + \
  814. [self.systemId],
  815. self.multipartition.getPartitionTable() + \
  816. [self.partitionTable])
  817. # get partition which need format
  818. formatPartitions = map(lambda x: (x[FS],x[DEV]),
  819. filter(lambda x: x[NEEDFORMAT] and x[FS]!="bind",
  820. dataPartitions))
  821. # format all get partition
  822. for fileSystem, dev in formatPartitions:
  823. if fileSystem=="swap":
  824. self.formatSwapPartition(dev)
  825. else:
  826. if dev == self.partition:
  827. self.formatPartition(dev, format=fileSystem,
  828. label=self.rootLabel)
  829. else:
  830. self.formatPartition(dev, format=fileSystem)
  831. # change system id for partitions
  832. changeidPartitions = map(lambda x: (x[NEWID],x[DEV],x[PARTTABLE]),
  833. filter(lambda x: x[NEWID],
  834. dataPartitions))
  835. for systemid, dev, partTable in changeidPartitions:
  836. self.changeSystemID(dev,systemid,partTable)
  837. return True
  838. def _checkMount(self,dev):
  839. """Checking mount point"""
  840. if isMount(dev):
  841. raise DistributiveError(
  842. _("Failed to format partition %s, because it is mounted")%dev)
  843. def formatPartition(self, dev,format="reiserfs",label=""):
  844. """Format partition"""
  845. if not format in self.formatUtilities:
  846. raise DistributiveError(
  847. _("Specified format of '%s' not supported")%format)
  848. if dev in map(lambda y: y.split(" ")[0],
  849. filter(lambda x: x.startswith("/"),
  850. open("/proc/swaps"))):
  851. raise DistributiveError(\
  852. _("Failed to format partition %s, because it is used as swap")%
  853. dev)
  854. self._checkMount(dev)
  855. if not os.access(dev,os.W_OK):
  856. raise DistributiveError(_("Failed to format the partition") +
  857. " %s:\n%s"%(dev,_("Permission denied")))
  858. if label:
  859. labelStr = self.labelForUtilities.get(format,"")
  860. if labelStr:
  861. labelStr = labelStr%label
  862. else:
  863. labelStr = ""
  864. execStr = self.formatUtilities[format]%(labelStr,dev)
  865. res,errmes = self.runOsCommand(execStr)
  866. if res == 0:
  867. return True
  868. else:
  869. raise DistributiveError(_("Failed to format the partition") +
  870. " %s:\n%s"%(dev,errmes))
  871. def performFormat(self):
  872. """Perform format for all partition of this distributive"""
  873. if self.multipartition:
  874. self.formatAllPartitions()
  875. elif self.isFormat:
  876. self.formatPartition(self.partition,format=self.fileSystem,
  877. label=self.rootLabel)
  878. if self.systemId:
  879. self.changeSystemID(self.partition,self.systemId,
  880. self.partitionTable)
  881. def changeSystemID(self,dev,systemid,parttable):
  882. """Change partition id, specified by systemid"""
  883. deviceName = detectDeviceForPartition(dev)
  884. if deviceName is None:
  885. raise DistributiveError(
  886. _("Failed to determine the parent device for %s")%dev)
  887. # device hasn't any partition
  888. elif deviceName == "":
  889. return True
  890. fdiskProg, gdiskProg = checkUtils('/sbin/fdisk','/usr/sbin/gdisk')
  891. partitionNumber = \
  892. getUdevDeviceInfo(name=dev).get('ID_PART_ENTRY_NUMBER','')
  893. devicePartitionCount = countPartitions(deviceName)
  894. if deviceName and not partitionNumber:
  895. raise DistributiveError(
  896. _("Failed to determine the partition number for %s")%dev)
  897. if parttable == "dos":
  898. fdisk = process(fdiskProg,deviceName,stderr=STDOUT)
  899. pipe = Popen([fdiskProg,deviceName],
  900. stdin=PIPE, stdout=PIPE,stderr=PIPE)
  901. if devicePartitionCount > 1:
  902. pipe.stdin.write("t\n%s\n%s\nw\n"%(partitionNumber,
  903. systemid))
  904. else:
  905. pipe.stdin.write("t\n%s\nw\n"%systemid)
  906. pipe.stdin.close()
  907. pipe.wait()
  908. elif parttable == "gpt":
  909. pipe = Popen([gdiskProg,deviceName],
  910. stdin=PIPE, stdout=PIPE,stderr=PIPE)
  911. if devicePartitionCount > 1:
  912. pipe.stdin.write("t\n%s\n%s\nw\ny\n"%(partitionNumber,
  913. systemid))
  914. else:
  915. pipe.stdin.write("t\n%s\nw\ny\n"%systemid)
  916. pipe.stdin.close()
  917. pipe.wait()
  918. for waittime in (0.1,0.2,0.5,1,2,4):
  919. if path.exists(dev):
  920. return True
  921. else:
  922. sleep(waittime)
  923. raise DistributiveError(
  924. _("Failed to found partition %s after changing the system id")%dev)
  925. def formatSwapPartition(self, dev):
  926. """Format swap partition"""
  927. if dev in map(lambda y: y.split(" ")[0],
  928. filter(lambda x: x.startswith("/"),
  929. open("/proc/swaps"))):
  930. raise DistributiveError(\
  931. _("Failed to execute 'mkswap %s', the swap partition is used "
  932. "by the current system") %dev)
  933. if isMount(dev):
  934. raise DistributiveError(
  935. _("Failed to format partition %s, because it is mounted") %dev)
  936. execStr = self.formatUtilities["swap"]%dev
  937. res,errmes = self.runOsCommand(execStr)
  938. if res == 0:
  939. return True
  940. else:
  941. raise DistributiveError(_("Failed to execute '%s'")%execStr +\
  942. "\n%s" %errmes)
  943. def installFrom(self, source):
  944. """Install distributive to partition from source distributive"""
  945. # get currect partition as directory
  946. distrTo = self.convertToDirectory()
  947. # install into directroy distributive from source
  948. distrTo.installFrom(source)
  949. class ArchiveDistributive(Distributive):
  950. def __init__(self,file,parent=None,mdirectory="/var/calculate/tmp/stage"):
  951. Distributive.__init__(self,parent=parent)
  952. self.file = file
  953. self.mdirectory = mdirectory
  954. def _detectArchive(self,file):
  955. """Detect archive by "/usr/bin/file" command
  956. Return bzip2,gzip,7z or None
  957. """
  958. res,mes = self.runOsCommand("/usr/bin/file %s"%file)
  959. if "bzip2 compressed data" in mes:
  960. return "bzip2"
  961. elif "gzip compressed data" in mes:
  962. return "gzip"
  963. elif "7-zip archive data" in mes:
  964. return "7z"
  965. elif file and file.endswith(".tar.lzma"):
  966. if path.exists('/usr/bin/7za'):
  967. return "7z"
  968. else:
  969. return "lzma"
  970. return None
  971. def _unpackArchive(self,archfile,directory):
  972. """Unpack archive"""
  973. # archive is exists
  974. if not path.exists(archfile):
  975. raise DistributiveError(_("File '%s' not found")%archfile)
  976. # detect type archive
  977. archiveType = self._detectArchive(archfile)
  978. # make directory if archive was detected normally
  979. if archiveType:
  980. self._makeDirectory(directory)
  981. # unpack archive
  982. if archiveType == "7z":
  983. res,mes = self.runOsCommand("7za x -so %s | tar xf - -C %s/"%
  984. (archfile,directory))
  985. elif archiveType == "lzma":
  986. res,mes = self.runOsCommand("lzma -dc %s | tar xf - -C %s/"%
  987. (archfile,directory))
  988. elif archiveType == "bzip2":
  989. res,mes = self.runOsCommand("tar xjf %s -C %s/"%
  990. (archfile,directory))
  991. elif archiveType == "gzip":
  992. res,mes = self.runOsCommand("tar xf %s -C %s/"%
  993. (archfile,directory))
  994. else:
  995. raise DistributiveError(_("Unknown archive type by '%s'")%
  996. archfile)
  997. if res != 0:
  998. raise DistributiveError(_("Error during unpacking\n%s")%mes)
  999. def unpackTo(self,directory):
  1000. """Unpack currect archive to directory"""
  1001. self._unpackArchive(self.file,directory)
  1002. def convertToDirectory(self):
  1003. """Get archive as directory (unpack to directory)"""
  1004. # check may be the archive already unpacked
  1005. mdirectory = self.mdirectory
  1006. for child in self.childs:
  1007. if isinstance(child,DirectoryDistributive) and \
  1008. mdirectory in child.directory:
  1009. return child
  1010. # get temporary directory for unpacking
  1011. mdirectory = self._getMntDirectory(mdirectory)
  1012. dirdist = DirectoryDistributive(mdirectory,parent=self)
  1013. self._unpackArchive(self.file,mdirectory)
  1014. return dirdist
  1015. def releaseChild(self,child):
  1016. """Remove child Directory distributive"""
  1017. if isinstance(child,DirectoryDistributive):
  1018. self._removeDirectory(child.directory)
  1019. child.directory = None
  1020. def packToArchive(self,directory,file):
  1021. res,errmes = self.runOsCommand("tar cf %s -C %s ."%(file,directory))
  1022. if res != 0:
  1023. raise DistributiveError(_("Failed to create the archive") +
  1024. " '%s':\n%s"%(file,errmes))
  1025. def installFrom(self, source):
  1026. """Install distributive to partition from source distributive"""
  1027. # get source distributive as directory distributive
  1028. dFrom = source.convertToDirectory()
  1029. # install into directroy distributive from source
  1030. self.packToArchive(dFrom.directory, self.file)
  1031. class SquashDistributive(Distributive):
  1032. def __init__(self,file,parent=None,mdirectory="/mnt/livecd",exclude=None,
  1033. compress=""):
  1034. Distributive.__init__(self,parent=parent)
  1035. self.file = file
  1036. self.mdirectory = mdirectory
  1037. self.exclude = [] if not exclude else exclude
  1038. self.compress = compress \
  1039. if compress and compress != "gzip" else \
  1040. ""
  1041. def _mountSquash(self,file,directory):
  1042. """Mount squashfs to directory"""
  1043. self._makeDirectory(directory)
  1044. self._mountToDirectory(file,directory,mountopts="-o loop -t squashfs")
  1045. def _umountSquash(self,directory):
  1046. self._umountDirectory(directory)
  1047. self._removeDirectory(directory)
  1048. def convertToDirectory(self):
  1049. mdirectory = self.mdirectory
  1050. for child in self.childs:
  1051. if isinstance(child,DirectoryDistributive) and \
  1052. mdirectory in child.directory:
  1053. return child
  1054. mdirectory = self._getMntDirectory(mdirectory)
  1055. self._mountSquash(self.file,mdirectory)
  1056. return DirectoryDistributive(mdirectory,parent=self)
  1057. def releaseChild(self,child):
  1058. """Umount child Directory distributive"""
  1059. if isinstance(child,DirectoryDistributive):
  1060. self._umountSquash(child.directory)
  1061. child.directory = None
  1062. def packToSquash(self,directory,file):
  1063. mksquashfsUtil = '/usr/bin/mksquashfs'
  1064. if not path.exists(mksquashfsUtil):
  1065. raise DistributiveError(_("Failed to create squash") +
  1066. " : %s"%_("command '%s' not found")%mksquashfsUtil)
  1067. cmd = [mksquashfsUtil, "%s/"%directory,file, "-no-progress"]
  1068. if self.compress:
  1069. cmd += ["-comp",self.compress]
  1070. if self.exclude:
  1071. cmd += ["-e"] + self.exclude
  1072. processMkSquash = process(*cmd)
  1073. if processMkSquash.failed():
  1074. raise DistributiveError(_("Failed to create squashfs") +
  1075. " '%s':\n%s"%(file,processMkSquash.read()))
  1076. def installFrom(self, source):
  1077. """Install distributive to partition from source distributive"""
  1078. # get source distributive as directory distributive
  1079. dFrom = source.convertToDirectory()
  1080. # install into directroy distributive from source
  1081. self.packToSquash(dFrom.directory, self.file)
  1082. class IsoDistributive(Distributive):
  1083. def __init__(self,file,parent=None,mdirectory="/mnt/cdrom",
  1084. bdirectory="/var/calculate/tmp/iso",exclude=None,
  1085. compress="gzip", volid="CALCULATE"):
  1086. Distributive.__init__(self,parent=parent)
  1087. self.file = file
  1088. self.volid = volid
  1089. if path.isdir(self.file):
  1090. self.mdirectory = self.file
  1091. else:
  1092. self.mdirectory = mdirectory
  1093. if file == bdirectory:
  1094. self.bdirectory = file
  1095. else:
  1096. self.bdirectory = self._getMntDirectory(bdirectory)
  1097. self.exclude = [] if not exclude else exclude
  1098. self.compress = compress
  1099. def probe(self):
  1100. """Check directory for iso content"""
  1101. try:
  1102. pathname = self.getIsoContentDirectory()
  1103. except:
  1104. return False
  1105. return path.exists(path.join(pathname,"syslinux")) and \
  1106. path.exists(path.join(pathname,"isolinux"))
  1107. def _mountIso(self,file,directory):
  1108. if self.file != self.mdirectory:
  1109. self._makeDirectory(directory)
  1110. tf = typeFile(magic=0x6|0x20)
  1111. ftype = tf.getMType(file)
  1112. if "block special" in ftype:
  1113. mopts = ""
  1114. else:
  1115. mopts = "-o loop"
  1116. self._mountToDirectory(file,directory,mountopts=mopts)
  1117. def _umountIso(self,directory):
  1118. if self.file != self.mdirectory:
  1119. self._umountDirectory(directory)
  1120. self._removeDirectory(directory)
  1121. def convertToSquash(self):
  1122. mdirectory = self.mdirectory
  1123. for child in self.childs:
  1124. if isinstance(child,SquashDistributive) and \
  1125. mdirectory in child.file:
  1126. return child
  1127. if self.mdirectory != self.file:
  1128. mdirectory = self._getMntDirectory(mdirectory)
  1129. self._mountIso(self.file,mdirectory)
  1130. fileLive = self._getLastLive(mdirectory)
  1131. if not fileLive:
  1132. self._umountIso(mdirectory)
  1133. raise DistributiveError(_("Iso %s contains no live image") %
  1134. self.file)
  1135. return SquashDistributive(path.join(mdirectory,fileLive),
  1136. parent=self,exclude=self.exclude,
  1137. compress=self.compress)
  1138. def getIsoContentDirectory(self):
  1139. """Return directory with content of iso image"""
  1140. squash = self.convertToSquash()
  1141. return path.dirname(squash.file)
  1142. def releaseChild(self,child):
  1143. """Umount child Directory distributive"""
  1144. if isinstance(child,SquashDistributive):
  1145. self._umountIso(path.dirname(child.file))
  1146. child.directory = None
  1147. def convertToDirectory(self):
  1148. return self.convertToSquash().convertToDirectory()
  1149. def packToIso(self,directory,file):
  1150. # remove previous version of iso
  1151. try:
  1152. if path.exists(file):
  1153. os.unlink(file)
  1154. except Exception, e:
  1155. raise DistributiveError(_("Failed to remove") +\
  1156. " %s:\n%s"%(file,str(e)))
  1157. except KeyboardInterrupt, e:
  1158. self.setSignalInterrupt()
  1159. raise DistributiveError(_("Failed to remove") +\
  1160. " %s:\n%s"%(file,str(e)))
  1161. mkisoUtil = '/usr/bin/mkisofs'
  1162. if not path.exists(mkisoUtil):
  1163. raise DistributiveError(_("Failed to create the iso image") +
  1164. " : %s"%_("command '%s' not found")%mkisoUtil)
  1165. efiImage = "boot/grub/efi.img"
  1166. if os.path.exists(os.path.join(directory,efiImage)):
  1167. params = ["-J","-R","-l","-no-emul-boot","-boot-load-size 4",
  1168. "-udf","-boot-info-table","-b isolinux/isolinux.bin",
  1169. "-V %s"%self.volid,
  1170. "-c isolinux/boot.cat","-eltorito-alt-boot",
  1171. "-no-emul-boot","-eltorito-platform efi",
  1172. "-eltorito-boot %s"%efiImage]
  1173. else:
  1174. params = ["-b isolinux/isolinux.bin","-no-emul-boot",
  1175. "-V %s"%self.volid,
  1176. "-boot-load-size 4","-boot-info-table","-iso-level 4",
  1177. "-hide boot.catalog"]
  1178. res,errmes = self.runOsCommand(
  1179. "%(progname)s %(params)s -o %(target)s %(source)s/"%
  1180. {'progname':mkisoUtil,
  1181. 'params':" ".join(params),
  1182. 'target':file,
  1183. 'source':directory})
  1184. if res == 0:
  1185. return True
  1186. else:
  1187. raise DistributiveError(_("Failed to create the iso image") +
  1188. " %s:\n%s"%(file,errmes))
  1189. def prepareIso(self):
  1190. raise DistributiveError(
  1191. _("Iso image cannot be created without overriding prepareIso"))
  1192. def installFrom(self, source):
  1193. """Install distributive to partition from source distributive"""
  1194. # make temporary directory for creating iso image
  1195. isoDirectory = self.bdirectory
  1196. self._makeDirectory(isoDirectory)
  1197. try:
  1198. # getting squash from source
  1199. liveimage = self._getLastLive(isoDirectory)
  1200. if liveimage:
  1201. curnum = self._getSquashNum(self.reLive.search(liveimage))
  1202. liveimage = "livecd.squashfs.%d"%(curnum+1)
  1203. else:
  1204. liveimage = "livecd.squashfs"
  1205. if isinstance(source,SquashDistributive):
  1206. self._copyfile(source.file,
  1207. path.join(isoDirectory,liveimage))
  1208. else:
  1209. distDirectory = source.convertToDirectory()
  1210. squashDistr = SquashDistributive(
  1211. path.join(isoDirectory,liveimage),
  1212. exclude=self.exclude,
  1213. compress=self.compress)
  1214. squashDistr.installFrom(distDirectory)
  1215. # prepare iso
  1216. self.prepareIso(isoDirectory)
  1217. # pack iso
  1218. if self.bdirectory != self.file:
  1219. self.packToIso(isoDirectory, self.file)
  1220. except DistributiveError,e:
  1221. raise e
  1222. except KeyboardInterrupt,e:
  1223. self.setSignalInterrupt()
  1224. raise DistributiveError(_("Keyboard interruption"))
  1225. def close(self):
  1226. # close all child
  1227. Distributive.close(self)
  1228. # remove temporary directory
  1229. if path.lexists(self.bdirectory) and self.file != self.bdirectory:
  1230. self._removeDirectory(self.bdirectory)
  1231. class FlashDistributive(PartitionDistributive):
  1232. def _checkMount(self,dev):
  1233. """Checking mount point"""
  1234. mp = isMount(dev)
  1235. if mp:
  1236. if mp.startswith('/media'):
  1237. self._umountDirectory(mp)
  1238. else:
  1239. raise DistributiveError(
  1240. _("Failed to format partition %s, because it is mounted")%dev)
  1241. def installFrom(self, source):
  1242. """Install distributive to partition from source distributive"""
  1243. # make temporary directory for creating iso image
  1244. distrTo = self.convertToDirectory()
  1245. # getting squash from source
  1246. if isinstance(source,IsoDistributive):
  1247. self.rsync(source.getIsoContentDirectory(),distrTo.directory)
  1248. else:
  1249. raise DistributiveError(
  1250. _("Installation to flash does not support %s"%
  1251. source.__class__.__name__))
  1252. class PxeDistributive(Distributive):
  1253. def __init__(self,directory,parent=None):
  1254. Distributive.__init__(self,parent=parent)
  1255. self.directory = path.join(directory,"calculate")
  1256. self.origdir = directory
  1257. def getDirectory(self):
  1258. return self.origdir
  1259. def installFrom(self,source):
  1260. # make temporary directory for creating iso image
  1261. distrTo = self.directory
  1262. # getting squash from source
  1263. if isinstance(source,IsoDistributive):
  1264. if path.exists(self.directory):
  1265. removeDir(self.directory)
  1266. self._makeDirectory(self.directory)
  1267. self.rsync(source.getIsoContentDirectory(),distrTo)
  1268. else:
  1269. raise DistributiveError(
  1270. _("Installation for PXE does not support %s"%
  1271. source.__class__.__name__))
  1272. class ScratchDistributive(Distributive):
  1273. def __init__(self,directory,parent=None,mdirectory="/mnt/install",
  1274. check=False):
  1275. Distributive.__init__(self,parent=parent)
  1276. self.directory = directory
  1277. self.mdirectory = mdirectory
  1278. if check and not (path.exists(path.join(directory,"workspace")) and \
  1279. path.exists(path.join(directory,"delta")) and \
  1280. path.exists(path.join(directory,"calculate"))):
  1281. raise DistributiveError(
  1282. _("Wrong scratch distribution in '%s'")%directory)
  1283. def _mountLayers(self,source,target):
  1284. """Mount squashfs to directory"""
  1285. self._makeDirectory(target)
  1286. self._mountToDirectory("none",target,
  1287. mountopts="-t aufs "+\
  1288. "-o udba=reval,br:%(work)s=rw:%(delta)s=ro+wh:%(static)s=ro" %\
  1289. {"work":path.join(source,"workspace"),
  1290. "delta":path.join(source,"delta"),
  1291. "static":path.join(source,"calculate")})
  1292. def _umountLayers(self,directory):
  1293. self._umountDirectory(directory)
  1294. self._removeDirectory(directory)
  1295. def _mountLiveImage(self,file,directory):
  1296. """Mount squashfs to directory"""
  1297. self._makeDirectory(directory)
  1298. self._mountToDirectory(file,directory,mountopts="-o loop -t squashfs")
  1299. def _umountLiveImage(self,directory):
  1300. self._umountDirectory(directory)
  1301. def convertToDirectory(self):
  1302. """Convert scrach directories to one directory"""
  1303. mdirectory = self.mdirectory
  1304. for child in self.childs:
  1305. if isinstance(child,DirectoryDistributive) and \
  1306. mdirectory in child.directory:
  1307. return child
  1308. mdirectory = self._getMntDirectory(mdirectory)
  1309. liveFile = self._getLastLive(self.directory)
  1310. self._mountLiveImage(path.join(self.directory,liveFile),
  1311. path.join(self.directory,"calculate"))
  1312. self._mountLayers(self.directory,mdirectory)
  1313. return DirectoryDistributive(mdirectory,parent=self)
  1314. def releaseChild(self,child):
  1315. """Umount child Directory distributive"""
  1316. if isinstance(child,DirectoryDistributive):
  1317. self._umountLayers(child.directory)
  1318. self._umountLiveImage(path.join(self.directory,"calculate"))
  1319. child.directory = None
  1320. def installFrom(self, source):
  1321. """Install distributive to partition from source distributive"""
  1322. # get source distributive as directory distributive
  1323. dFrom = source.convertToDirectory()
  1324. # install into directroy distributive from source
  1325. self.packToSquash(dFrom.directory, self.file)
  1326. class ScratchPartitionDistributive(PartitionDistributive):
  1327. def releaseChild(self,child):
  1328. """Umount child Directory distributive"""
  1329. if isinstance(child,ScratchDistributive):
  1330. self._umountPartition(child.directory)
  1331. child.directory = None
  1332. def convertToDirectory(self):
  1333. """Convert scratch partition to directory by scratch directory"""
  1334. return self.convertToScratch().convertToDirectory()
  1335. def convertToScratch(self):
  1336. mdirectory = self.mdirectory
  1337. for child in self.childs:
  1338. if isinstance(child,ScratchDistributive) and \
  1339. mdirectory in child.directory:
  1340. return child
  1341. mdirectory = self._getMntDirectory(mdirectory)
  1342. self._mountPartition(self.partition,mdirectory)
  1343. return ScratchDistributive(mdirectory,parent=self)
  1344. def prepareScratch(self,directory):
  1345. """Create need scratch directories"""
  1346. for scrDirectory in ("calculate","delta","workspace"):
  1347. self._makeDirectory(path.join(directory,scrDirectory))
  1348. def getBootDirectory(self):
  1349. """Get directory which contains boot"""
  1350. return path.join(self.convertToScratch().directory,"boot")
  1351. def postinstallMountBind(self):
  1352. pass
  1353. def installFrom(self, source):
  1354. """Install distributive to partition from source distributive"""
  1355. # get currect partition as directory
  1356. distrTo = self.convertToScratch()
  1357. scratchDirectory = distrTo.directory
  1358. try:
  1359. # getting squash from source
  1360. if isinstance(source,IsoDistributive):
  1361. source = source.convertToSquash()
  1362. if isinstance(source,SquashDistributive):
  1363. self._copyfile(source.file,
  1364. path.join(scratchDirectory,"livecd.squashfs"))
  1365. else:
  1366. distDirectory = source.convertToDirectory()
  1367. squashDistr = SquashDistributive(
  1368. path.join(scratchDirectory,"livecd.squashfs"))
  1369. squashDistr.installFrom(distDirectory)
  1370. # prepare scratch
  1371. self.prepareScratch(scratchDirectory)
  1372. except DistributiveError,e:
  1373. raise e
  1374. except KeyboardInterrupt,e:
  1375. self.setSignalInterrupt()
  1376. raise DistributiveError(_("Keyboard interruption"))