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.

511 lines
23 KiB

  1. #-*- coding: utf-8 -*-
  2. # Copyright 2008-2010 Mir Calculate. 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. import filecmp
  16. import string
  17. from random import choice
  18. from re import search, compile, S
  19. import os
  20. import types
  21. import subprocess
  22. def getdirlist(s_path):
  23. #Получить список директорий по указаному пути
  24. fdir=filecmp.dircmp(s_path, s_path)
  25. dir_list=fdir.common_dirs
  26. return dir_list
  27. def prettyColumnStr(*cols):
  28. '''Функция преобразования строк в текстовые колонки. Если указанный текст
  29. не помещается в колонку, то строка переносится на следующую этой же колонки
  30. перенос текста идет по словам, и текст выравнивается по ширине колонки за
  31. счет дополнительных пробелов между словами. Если в строке используется
  32. перенос строки, то текст переносится не просто на следующую строку, а также
  33. на следующую строку колонки, причем если используется \r текст выравнива-
  34. ется по ширине, а если \n, то просто перевод строки.
  35. Параметры:
  36. cols множестово пар: текст, ширина колонки, причем, если у последней
  37. колонки не указывать ширину, то она будет выведена вся.
  38. Возвращаемые параметры:
  39. строка, которую можно использовать для вывода на экран
  40. Пример: columnWrite( "Some text", 10, "Next column", 20 )
  41. '''
  42. # шаблон поиска переводов строк
  43. wherenr = compile( '[\n\r]', S )
  44. retstr = ""
  45. # перевести кортеж в список, т.к. изменяется
  46. cols = list(cols)
  47. # перевести текст в юникод, заодно перевести числа в строку
  48. noconvert = False
  49. space = u' '
  50. nospace = u''
  51. for i in xrange(0,len(cols),2):
  52. cols[i] = _toUNICODE(cols[i])
  53. # флаг "есть еще текст для вывода"
  54. repeat = True
  55. while repeat:
  56. # сбросить итератор на первый элемент
  57. q = 0
  58. repeat = False
  59. # пока не закончили перебирать параметры (перебираем по парам)
  60. while q < len(cols):
  61. # если это последний параметр, и для него не указана ширина
  62. if q == len(cols)-1:
  63. # выводим его полностью не смотря на ширину окна
  64. retstr += cols[q] + " "
  65. cols[q] = ''
  66. else:
  67. # вывести часть строки не больше указанной ширины колонки
  68. partstr = cols[q][:cols[q+1]]
  69. # искать перевод строки с полученной части
  70. brfind = wherenr.search(partstr)
  71. # если это не последняя колонка
  72. if q + 2 < len(cols):
  73. # добавить разделитель между колонками
  74. cellspacing = space
  75. else:
  76. # разделитель не нужен
  77. cellspacing = nospace
  78. # если перевод строки найден, то
  79. if brfind != None:
  80. # для текущего вывода в колонку
  81. # берем часть строки до перевода
  82. partstr = partstr[:brfind.start()]
  83. # остальная часть идет в остаток (без перевода)
  84. cols[q] = cols[q][brfind.start()+1:]
  85. # # если используется перевод каретки
  86. # if brfind.group() == '\r':
  87. # # то выравниваем по ширине колонки
  88. # partstr = partstr.ljust(cols[q+1], ' ')
  89. # else:
  90. # # добавить отступы чтобы закончить колонку
  91. partstr = partstr.ljust(cols[q+1], ' ')
  92. # если взята часть строки
  93. elif len(partstr) == cols[q+1] and partstr != cols[q]:
  94. # если взята часть строки (разрыв в слове)
  95. if cols[q][cols[q+1]] != ' ':
  96. # ищем ближайший пробел справа
  97. spacepos = partstr.rfind(' ')
  98. # если пробел найти не удалось
  99. if spacepos == -1:
  100. # то на вывод идет часть строки равной ширине
  101. cols[q] = cols[q][cols[q+1]:]
  102. # если пробел найден
  103. else:
  104. # обрезаем строку до найденного пробела
  105. partstr = partstr[:spacepos]
  106. cols[q] = cols[q][spacepos+1:]
  107. # если взята часть строки (разрыв на пробеле)
  108. else:
  109. # ислючить переносной пробел
  110. cols[q] = cols[q][cols[q+1]+1:]
  111. # выровнить текст по ширине колонки
  112. partstr = partstr.ljust(cols[q+1], ' ')
  113. #partstr = justify(partstr, cols[q+1])
  114. # остатки строки
  115. else:
  116. # добавить отступы чтобы закончить колонку
  117. partstr = partstr.ljust(cols[q+1], ' ')
  118. cols[q] = ''
  119. retstr+= partstr + cellspacing
  120. # остальную часть строки оставить на следующую итерацию
  121. # если от строки что то осаталось
  122. if len(cols[q]) > 0:
  123. # отметить запуск еще одной итерации по параметрам
  124. repeat = True
  125. # следующая пара
  126. q += 2
  127. # колонки отображены
  128. retstr += "\n"
  129. return retstr.encode('utf8')
  130. def columnStr(*cols):
  131. '''Вывод данных по колонкам, причем, если данные не вмещаются в указнаную
  132. колонку, то они переносятся на следующую строку в нужную колонку. В строку.
  133. Параметры:
  134. cols множестово пар: текст, ширина колонки, причем, если у последней
  135. колонки не указывать ширину, то она будет выведена вся.
  136. Возвращаемые параметры:
  137. строка, которую можно использовать для вывода на экран
  138. Пример: columnWrite( "Some text", 10, "Next column", 20 )
  139. '''
  140. retstr = ""
  141. # перевести кортеж в список, т.к. изменяется
  142. cols = list(cols)
  143. # перевести текст в юникод, заодно перевести числа в строку
  144. for i in xrange(0,len(cols),2):
  145. cols[i] = (str(cols[i])).decode('utf8')
  146. # флаг "есть еще текст для вывода"
  147. repeat = True
  148. while repeat:
  149. # сбросить итератор на первый элемент
  150. q = 0
  151. repeat = False
  152. # пока не закончили перебирать параметры (перебираем по парам)
  153. while q < len(cols):
  154. # если это последний параметр, и для него не указана ширина
  155. if q == len(cols)-1:
  156. # выводим его полностью не смотря на ширину окна
  157. retstr += cols[q] + " "
  158. cols[q] = ''
  159. else:
  160. # вывести часть строки не больше указанной ширины колонки
  161. retstr+=(cols[q][:cols[q+1]].ljust(cols[q+1])).encode('utf8') \
  162. + " "
  163. # остальную часть строки оставить на следующую итерацию
  164. cols[q] = cols[q][cols[q+1]:]
  165. # если от строки что то осаталось
  166. if len(cols[q]) > 0:
  167. # отметить запуск еще одной итерации по параметрам
  168. repeat = True
  169. # следующая пара
  170. q += 2
  171. # колонки отображены
  172. retstr += "\n"
  173. return retstr
  174. def columnWrite(*cols):
  175. '''Вывод данных по колонкам, причем, если данные не вмещаются в указнаную
  176. колонку, то они переносятся на следующую строку в нужную колонку.
  177. Параметры:
  178. cols множестово пар: текст, ширина колонки, причем, если у последней
  179. колонки не указывать ширину, то она будет выведена вся.
  180. Пример: columnWrite( "Some text", 10, "Next column", 20 )
  181. '''
  182. # перевести кортеж в список, т.к. изменяется
  183. cols = list(cols)
  184. # перевести текст в юникод, заодно перевести числа в строку
  185. for i in xrange(0,len(cols),2):
  186. cols[i] = (str(cols[i])).decode('utf8')
  187. # флаг "есть еще текст для вывода"
  188. repeat = True
  189. while repeat:
  190. # сбросить итератор на первый элемент
  191. q = 0
  192. repeat = False
  193. # пока не закончили перебирать параметры (перебираем по парам)
  194. while q < len(cols):
  195. # если это последний параметр, и для него не указана ширина
  196. if q == len(cols)-1:
  197. # выводим его полностью не смотря на ширину окна
  198. print cols[q].encode('utf8'),
  199. cols[q] = ''
  200. else:
  201. # вывести часть строки не больше указанной ширины колонки
  202. print (cols[q][:cols[q+1]].ljust(cols[q+1])).encode('utf8'),
  203. # остальную часть строки оставить на следующую итерацию
  204. cols[q] = cols[q][cols[q+1]:]
  205. # если от строки что то осаталось
  206. if len(cols[q]) > 0:
  207. # отметить запуск еще одной итерации по параметрам
  208. repeat = True
  209. # следующая пара
  210. q += 2
  211. # колонки отображены
  212. print
  213. def justify(s,width):
  214. '''Выровнить текст по ширине
  215. Параметры:
  216. s выводимая строка
  217. width ширина на которую надо выровнить строку
  218. Возвращаямые параметры:
  219. Выровненная строка
  220. '''
  221. # если подана строка без пробелов - прекратить обработку
  222. if s.find(' ') == -1:
  223. return s
  224. pos = 0
  225. # переводим в юникод для правильного вычисления длины
  226. try:
  227. s = s.decode( 'utf-8' )
  228. # пропуск если это не utf-8
  229. except UnicodeEncodeError:
  230. pass
  231. # пока длина строки меньше указанной
  232. while len(s) < width:
  233. # находим очередной пробел
  234. pos = s.find( ' ', pos )
  235. # если не найден искать сначала
  236. if pos == -1:
  237. pos = s.find(' ')
  238. # вставить в позицию еще один пробел
  239. s = s[:pos] +' ' +s[pos:]
  240. # оставить удвоенный пробел
  241. pos += 3
  242. # вернуть строку в utf8 если она пришла в utf8
  243. return s.encode('utf-8')
  244. def runOsCommand(cmd, inStr=None, ret_first=None, env_dict=None):
  245. """Выполняет внешнюю программу
  246. Параметры:
  247. cmd внешняя программа
  248. inStr данные передаваемые программе на страндартный вход.
  249. ret_first вернуть только первую строку
  250. env_dict словарь переменных окружения
  251. Возвращаемые параметры:
  252. строка/строки которую выведет внешняя программа
  253. Возвращает код возврата, stdout+stderr
  254. """
  255. pipe = subprocess.Popen(cmd, stdin=subprocess.PIPE,
  256. stdout=subprocess.PIPE,
  257. stderr=subprocess.PIPE,
  258. env=env_dict,
  259. close_fds=True,
  260. shell=True)
  261. fout, fin, ferr = (pipe.stdout, pipe.stdin, pipe.stderr)
  262. # если есть данные на вход, передать их
  263. if inStr:
  264. fin.write(inStr)
  265. fin.close()
  266. # Код возврата
  267. retcode = pipe.wait()
  268. res = fout.readlines()
  269. fout.close()
  270. res += ferr.readlines()
  271. ferr.close()
  272. if res:
  273. if len(res) == 1 or ret_first:
  274. return retcode, res[0].strip()
  275. else:
  276. return retcode, res
  277. return retcode, None
  278. def genpassword(passlen=9):
  279. '''Вернуть случайный пассворд указанной длины
  280. Параметры:
  281. passlen длина пароля который нужно сгенерировать
  282. Возвращаемые параметры:
  283. Сгенерированный пароль указанной длины
  284. '''
  285. res=''.join([choice(string.ascii_letters+string.digits)\
  286. for i in xrange(passlen)])
  287. return res
  288. def fillstr(char, width):
  289. '''Заполнить строку указанным числом символов. Псеводоним символ*кол-во'''
  290. return str(char) * width
  291. #вернуть пути для запуска утилит
  292. def getpathenv():
  293. bindir=['/sbin','/bin','/usr/sbin','/usr/bin']
  294. env=os.environ
  295. if env and env.has_key('PATH'):
  296. lpath=env['PATH'].split(":")
  297. npath=[]
  298. for dirname in bindir:
  299. if os.path.exists(dirname) and dirname not in lpath:
  300. npath.append(dirname)
  301. lpath=npath+lpath
  302. return ":".join(lpath)
  303. #класс для работы с установленными пакетами
  304. class pakages:
  305. #путь к директории установленнх пакетов
  306. pkgdir="/var/db/pkg/"
  307. #список установленных пакетов
  308. pkglist={}
  309. #Объект содержащий параметры пакета
  310. class pakage(object):
  311. #имя пакета с версией
  312. fullname=""
  313. #имя пакета
  314. name=""
  315. #версия
  316. ver=""
  317. #тип пакета в портежах
  318. portdir=""
  319. def __init__(self, **args):
  320. for atname,atvalue in args.items():
  321. setattr(self,atname, atvalue)
  322. def __init__(self):
  323. self.pkglist=self.__getpkglist()
  324. #разбить имя пакета на тип и имя
  325. def __getpkgver(self, fname):
  326. res=search('^(.+)\-([0-9]+.*)$',fname)
  327. if res:
  328. return res.groups()
  329. else:
  330. return (None,None)
  331. #собрать установленные в системе пакеты
  332. def __getpkglist(self):
  333. portageDirs=[]
  334. instaledPkg={}
  335. #проверим на существование директории с установленными пакетами
  336. if os.path.exists(self.pkgdir):
  337. #получим список типов пакетов
  338. portageDirs=getdirlist(self.pkgdir)
  339. if len(portageDirs)>0:
  340. #обрабатываем содержимое каждого из типов
  341. for portageDir in portageDirs:
  342. pkgList=getdirlist(self.pkgdir+portageDir)
  343. for pkg in pkgList:
  344. fullname=pkg
  345. pkgName,pkgVer= self.__getpkgver(pkg)
  346. pobj=self.pakage(fullname=fullname,
  347. name=pkgName, \
  348. ver=pkgVer,\
  349. portdir=portageDir)
  350. fpkg=portageDir+"/"+pkgName
  351. if instaledPkg.has_key(fpkg):
  352. instaledPkg[fpkg].append(pobj)
  353. else:
  354. instaledPkg[fpkg]=[pobj]
  355. return instaledPkg
  356. #разбить pkgname на составляющие имени пакета
  357. def __partname(self, pkgname):
  358. if not pkgname.strip():
  359. return False
  360. res=search('^(.+\/)?(.+)',pkgname)
  361. tname=None
  362. if res.group(1):
  363. tname=res.group(1)
  364. if res.group(2):
  365. res2=search('^(.+)(\-[0-9]+.+$)',res.group(2))
  366. if res2:
  367. name=res2.group(1)
  368. ver=res2.group(2)
  369. else:
  370. name=res.group(2)
  371. ver=None
  372. if res:
  373. if name and name[-1:]=='-':
  374. name=name[:-1]
  375. if tname and tname[-1:]=='/':
  376. tname=tname[:-1]
  377. if ver and ver[0]=='-':
  378. ver=ver[1:]
  379. return [tname, name, ver]
  380. #проверить установленн ли пакет
  381. #isinstalled('dev-db/postgresql')
  382. def isinstalled(self, pkgname):
  383. res=self.getinstpkg(pkgname)
  384. if len(res)>0:
  385. return True
  386. else:
  387. return False
  388. #вернуть список объектов pakage() соответствующих pkgname
  389. #getinstpkg('dev-db/postgresql')
  390. #в случае отсутствия пакетов возвращает пустой список
  391. def getinstpkg(self, pkgname):
  392. pinfo=self.__partname(pkgname)
  393. if pinfo:
  394. ret=[]
  395. if pinfo[0] and pinfo[1] and pinfo[2]:
  396. if not self.pkglist.has_key(pinfo[0]+'/'+pinfo[1]):
  397. return []
  398. fpkg=self.pkglist[pinfo[0]+'/'+pinfo[1]]
  399. ret=[]
  400. for i in fpkg:
  401. if i.ver==pinfo[2]:
  402. ret.append(i)
  403. return ret
  404. elif pinfo[0] and pinfo[1]:
  405. if not self.pkglist.has_key(pinfo[0]+'/'+pinfo[1]):
  406. return []
  407. return self.pkglist[pinfo[0]+'/'+pinfo[1]]
  408. elif pinfo[1] and pinfo[2]:
  409. for i in self.pkglist.keys():
  410. if search('^.+\/%s$'%(pinfo[1]),i):
  411. for el in self.pkglist[i]:
  412. if el.ver==pinfo[2]:
  413. ret.append(el)
  414. return ret
  415. elif pinfo[1]:
  416. for i in self.pkglist.keys():
  417. if search('^.+\/%s$'%(pinfo[1]),i):
  418. ret+=self.pkglist[i]
  419. return ret
  420. return []
  421. def getListPkg(self):
  422. return self.pkglist
  423. def list2str(list):
  424. '''Функция переводит список в строку'''
  425. return '['+','.join(list)+']'
  426. def str2list(s):
  427. '''Функция переводит строку в список'''
  428. return s[1:-1].split(',')
  429. def dict2str(dict):
  430. '''Функция перводит словарь в строку'''
  431. return '{'+','.join(["%s:%s" % (str(k),str(v)) \
  432. for (k,v) in dict.items()])+'}' #:
  433. def str2dict(s):
  434. '''Функция переводит строку в словарь'''
  435. dict = {}
  436. for i in s[1:-1].split(','):
  437. k,v = i.split(':')
  438. dict[k] = v
  439. return dict
  440. def convertStrListDict(val):
  441. '''Функция определеяется что на входе (строка, список, словарь)
  442. и переводит их в строку и обратно'''
  443. # если подан список
  444. if type(val) == types.ListType:
  445. return list2str(val)
  446. # если подан словарь
  447. elif type(val) == types.DictType:
  448. return dict2str(val)
  449. # если подана строка
  450. else:
  451. # если поданная строка содержит словарь
  452. if ':' in val and '{' in val:
  453. return str2dict(val)
  454. # если поданная строка содержит список
  455. elif ',' in val and '[' in val:
  456. return str2list(val)
  457. # если это просто строка
  458. else:
  459. return val
  460. def _toUNICODE(val):
  461. """перевод текста в юникод"""
  462. if type(val) == types.UnicodeType:
  463. return val
  464. else:
  465. return str(val).decode('UTF-8')