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.

564 lines
25 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 sys
  16. import struct
  17. import termios
  18. import fcntl
  19. import cl_utils
  20. import cl_profile
  21. import ldap
  22. class ldapFun(cl_profile._error):
  23. '''Объект для работы с LDAP сервером
  24. подключение к серверу и поиск данных
  25. '''
  26. def __init__(self, dnUser, password, host="localhost"):
  27. self.conLdap = False
  28. # Получаем соединение с LDAP
  29. try:
  30. self.conLdap = self.__ldapConnect(dnUser, password, host)
  31. except ldap.LDAPError, e:
  32. self.setError(e[0]['desc'])
  33. def __ldapConnect(self, dnUser, password, host):
  34. """Соединение с LDAP сервером"""
  35. conLdap = ldap.initialize('ldap://%s'%host)
  36. conLdap.simple_bind_s(dnUser, password)
  37. return conLdap
  38. def ldapSearch(self,baseDN, searchScope, searchFilter, retrieveAttributes):
  39. try:
  40. ldap_result_id = self.conLdap.search(baseDN, searchScope,
  41. searchFilter,
  42. retrieveAttributes)
  43. result_set = []
  44. while 1:
  45. result_type, result_data = self.conLdap.result(ldap_result_id,
  46. 0)
  47. if (result_data == []):
  48. break
  49. else:
  50. if result_type == ldap.RES_SEARCH_ENTRY:
  51. result_set.append(result_data)
  52. except ldap.NO_SUCH_OBJECT:
  53. return []
  54. except:
  55. return False
  56. return result_set
  57. pcs = cl_utils.prettyColumnStr
  58. class cl_help:
  59. """Объект для работы со справкой, и обработкой параметров.
  60. Конструктор __init__ должен определить следующие переменные:
  61. self.chapter список разделов справки, каждый элементы состоит из
  62. имени раздела, флаг видимый/скрытый, кол-во переводов строк
  63. после названия раздела, количество строк после раздела,
  64. тип раздела
  65. Пример: [("Copyright",False,0,2),"options"]
  66. self.relService словарь связей сервисов и действующих опций
  67. ключ - название сервиса, значение - список отображаемых
  68. разделов отмеченных как "options"
  69. Пример: {"samba":[_("Common options"),
  70. _("Service Samba options")]}
  71. self.relOptions словарь связей длинных опций помощи и выводимых разделов
  72. помощи с опциями
  73. ключ - параметр справки, значение список отображаемых
  74. разделов справки
  75. Пример: {"help-ldap":[_("Common options"),
  76. _("Service LDAP options)]}
  77. self.progName словарь имена используемых программ и их номера для
  78. доступа к переменным
  79. Пример: {'cl-groupadd':0, 'cl-groupdel':1}
  80. self.data список данных для справки, каждый элемент словарь:
  81. progAccess: список номеров программ отображающих
  82. Пример: {'progAccess':(0,),
  83. 'shortOption':"g",
  84. 'longOption':"gid",
  85. 'optVal':"GID",
  86. 'helpChapter':_("Options"),
  87. 'help':_("use GID for the new group")
  88. },
  89. после заполнения параметров необходимо выполнить
  90. self._cl_help__setParamHelp() для заполнения справки
  91. """
  92. def __init__(self, cmdName):
  93. # ширина консоли взята за 80
  94. # -1 чтобы компенсировать расстрояние между колонками
  95. self.consolewidth = 79
  96. self.column_width = 32
  97. self.cmdName = cmdName
  98. #короткие опции командной строки
  99. self.shortOpt = []
  100. #длинные опции командной строки
  101. self.longOpt = []
  102. # массив разделов (заполняется в __setParamHelp)
  103. self.chapterBloc = []
  104. #optEnd = ""
  105. #if "user" in self.cmdName and not "mod" in self.cmdName:
  106. #optEnd = _("user")
  107. #elif "group" in self.cmdName and not "mod" in self.cmdName:
  108. #optEnd = _("group")
  109. #self.__setParamHelp()
  110. def getChapterNumber(self,NameChapter):
  111. """Получить номер раздела по имени"""
  112. num = 0
  113. for i in self.chapter:
  114. if i[0] == NameChapter:
  115. return num
  116. num += 1
  117. return False
  118. def __setParamHelp(self):
  119. """Внутренняя функция формирования справки по данным
  120. Перебирает все элементы списка data, проверяет их на доступность
  121. данной программы, разбирает опции на среди data и формирует
  122. для по ним справку.
  123. """
  124. # сформировать нужное количество блоков раздела
  125. self.chapterBloc = [""]*len(self.chapter)
  126. #
  127. sp = {}
  128. i = 0
  129. # перебираем все элементы справки собираем элементы опции
  130. # так же формируем разделы не опции
  131. for par in self.data:
  132. # перебираем только те опции, которые принадлежат команде
  133. if self.access(par):
  134. # есть короткая (возможно есть и длинная)
  135. if par.has_key("shortOption"):
  136. sp[par["shortOption"]+":"+par["helpChapter"]] = i
  137. # есть только длинная опция
  138. elif par.has_key("longOption"):
  139. sp[par["longOption"]+":"+par["helpChapter"]] = i
  140. # формирование разделов не опций
  141. else:
  142. helpTxt = par['help']
  143. numChapter = self.getChapterNumber(par['helpChapter'])
  144. self.addChapterHelp(numChapter,helpTxt)
  145. i += 1
  146. # перебираем все "собранные" опции
  147. # опции перебираются по порядку в списке date
  148. # для сортировки по ключам следует применить код:
  149. # for index in sorted(sp.keys()):
  150. # par = self.data[sp[index]]
  151. for index in sorted(sp.values()):
  152. par = self.data[index]
  153. numChapter = self.getChapterNumber(par['helpChapter'])
  154. # если есть и короткая и длинная
  155. if "shortOption" in par and "longOption" in par:
  156. paraminfo = "-%s, --%s "%(par["shortOption"],par["longOption"])
  157. # если есть только короткая
  158. elif "shortOption" in par:
  159. paraminfo = "-%s "%par["shortOption"]
  160. # если только длинная
  161. else:
  162. paraminfo = "--%s "%par["longOption"]
  163. # если указан параметр для опции
  164. if "optVal" in par:
  165. optVal = par["optVal"]
  166. else:
  167. optVal = ""
  168. # вывод вида: " [-o, ][--option] [PARAM]" "helpstring"
  169. helpTxt = pcs(" "+paraminfo+optVal, self.column_width, \
  170. par['help'], self.consolewidth-self.column_width)
  171. # добавить строку в нужный раздел
  172. self.addChapterHelp(numChapter,helpTxt)
  173. def getHelp(self, optionsChapters=False):
  174. """Выдать справку.
  175. Выдает справку в случае если указан optionsChapters, то фильтрует по
  176. типу разделов.
  177. Параметры:
  178. optionsChapters Flase или список опциональных разделов для
  179. отображения
  180. Возвращаемые параметры:
  181. Строка со справкой.
  182. """
  183. # Выдать справку
  184. help = ""
  185. # перебираем все элементы справочных блоков
  186. iterChapterBloc = iter(self.chapterBloc)
  187. # перебираем все разделы по параметрам
  188. for (nameChapter, visibleChapter, beforeStrChapter, \
  189. afterStrChapter, typeChapter) in self.chapter:
  190. # получаем следующий блок (т.о. textChapterBloc соответ, chapter)
  191. textChapterBloc = iterChapterBloc.next()
  192. # если тип раздела опциональный
  193. if optionsChapters and typeChapter=="options":
  194. # проверяем нужно ли его отображать
  195. if not (nameChapter in optionsChapters):
  196. continue
  197. bef = "\n"*beforeStrChapter
  198. aft = "\n"*afterStrChapter
  199. # если блок не пустой и раздел отображаемый
  200. if len(textChapterBloc) > 0:
  201. if visibleChapter:
  202. help += nameChapter + ": " + bef
  203. help += textChapterBloc + aft
  204. help = help.rstrip()+"\n"
  205. return help
  206. def addChapterHelp(self, numChapter, helpTxt):
  207. """Добавить в раздел помощи numChapteк тектстовую строку helpTxt
  208. Параметры:
  209. numChapter номер раздела в который нужно добавить данные справки
  210. helpTxt строка, содержащая данные
  211. """
  212. self.chapterBloc[numChapter] += helpTxt
  213. return True
  214. def addData(self,dataHash):
  215. # На будущее (добавляет опции)
  216. self.data.append(dataHash)
  217. return True
  218. def handleCheckAccess(self,dataHash):
  219. """Замещаемый дополнительный обработчик проверки
  220. доступности опции.
  221. Входные параметры:
  222. dataHash элементы списка данных справки (self.data)
  223. """
  224. return True
  225. def access(self,dataHash):
  226. """Доступна ли опция вызывающей программе
  227. Параметры:
  228. dataHash словарь элемент типа self.data
  229. Возвращаемые параметры:
  230. True/False доступна/недоступна
  231. """
  232. # доступна ли опция вызывающей программе
  233. # опция без progAccess доступна
  234. numProg = self.progName[self.cmdName]
  235. if 'progAccess' in dataHash:
  236. if numProg in dataHash['progAccess']:
  237. # вызов дополнительной проверки доступа к опции
  238. return self.handleCheckAccess(dataHash)
  239. else:
  240. return False
  241. else:
  242. # вызов дополнительной проверки доступа к опции
  243. return self.handleCheckAccess(dataHash)
  244. def getTypeChapter(self, nameChapter):
  245. """Получить тип раздела по его имени
  246. Параметры:
  247. nameChapter название раздела
  248. Возвращаемые параметры:
  249. строка тип раздела
  250. Flase(Boolean) такой раздел отсутствует
  251. """
  252. # фильтруем список по имени раздела, помещаем в список тип раздела
  253. filtered = [typeChapter for name, na, na, na, typeChapter \
  254. in self.chapter if name == nameChapter]
  255. # если среди фильтрованных есть хоть один элемент
  256. if len(filtered) > 0:
  257. # возвращаем - он запрашиваемый
  258. return filtered[0]
  259. else:
  260. # такой раздел отсутствует
  261. return False
  262. def clearAllOpt(self):
  263. """Очистить все опции, полученные посредством getAllOpt"""
  264. if len(self.shortOpt) > 0:
  265. self.shortOpt = []
  266. if len(self.longOpt) > 0:
  267. self.longOpt = []
  268. return True
  269. def getAllOpt(self,typeOpt="all", optionsChapters=False):
  270. """Получить все доступные опции
  271. Параметры:
  272. typeOpt 'short'/'long'/'all', вернуть короткие или длинные
  273. опции или все (возвращаются кортежем)
  274. optionsChapters фильтр для опций по типам разделов (список,кортеж)
  275. Возвращаемые параметры:
  276. строка коротки или список строк длинных опций ('hb:c:wg:G:k:ms:u:')
  277. """
  278. # Выдать все действующие опции
  279. if typeOpt=="short" or typeOpt=="all":
  280. if len(self.shortOpt) == 0:
  281. for par in self.data:
  282. if optionsChapters and\
  283. self.getTypeChapter(par['helpChapter'])=="options":
  284. if not (par['helpChapter'] in optionsChapters):
  285. continue
  286. if par.has_key("shortOption") and self.access(par):
  287. if par.has_key("optVal"):
  288. self.shortOpt.append(par["shortOption"]+':')
  289. else:
  290. self.shortOpt.append(par["shortOption"])
  291. if typeOpt=="long" or typeOpt=="all":
  292. if len(self.longOpt) == 0:
  293. for par in self.data:
  294. if optionsChapters and\
  295. self.getTypeChapter(par['helpChapter'])=="options":
  296. #print par["longOption"]
  297. if not (par['helpChapter'] in optionsChapters):
  298. continue
  299. if par.has_key("longOption") and self.access(par):
  300. if par.has_key("optVal"):
  301. self.longOpt.append(par["longOption"]+'=')
  302. else:
  303. self.longOpt.append(par["longOption"])
  304. if typeOpt=="short":
  305. return "".join(self.shortOpt)
  306. elif typeOpt=="long":
  307. return self.longOpt
  308. elif typeOpt=="all":
  309. return ("".join(self.shortOpt),self.longOpt)
  310. def getShortOpt(self,option):
  311. """Из любой опции получить короткую опцию.
  312. Фильтрация также происходит и по названию команды.
  313. Параметры:
  314. option запрашиваемая опция
  315. Возвращаемые параметры:
  316. короткая опция, если же для длинной опции нет короткой, возвращается
  317. пустая строка.
  318. """
  319. # Из любой опции получаем короткую опцию
  320. for par in self.data:
  321. if par.has_key("shortOption") and self.access(par):
  322. if (par.has_key("longOption") and\
  323. par["longOption"] == option) or \
  324. par["shortOption"] == option:
  325. return par["shortOption"]
  326. break
  327. return ""
  328. class cl_smartcon(object):
  329. def getconsolewidth(self):
  330. """Получить ширину текущей консоли"""
  331. s = struct.pack("HHHH", 0, 0, 0, 0)
  332. fd_stdout = sys.stdout.fileno()
  333. try:
  334. x = fcntl.ioctl(fd_stdout, termios.TIOCGWINSZ, s)
  335. except IOError:
  336. # если ошибка то ширина 80 символов
  337. return 80
  338. #(rows, cols, x pixels, y pixels)
  339. return struct.unpack("HHHH", x)[1]
  340. def printRight(self, offsetLeft, offsetRight):
  341. """Добавляет необходимое количество пробелов:
  342. количество пробелов = (ширина консоли - offsetLeft - offsetRight)
  343. """
  344. cols = self.getconsolewidth()
  345. for i in range(cols - offsetLeft - offsetRight):
  346. sys.stdout.write(" ")
  347. def colorPrint(self,attr,fg,bg,string):
  348. """Раскрашивает выводимое сообщение
  349. Параметры:
  350. attr - это атрибут
  351. fg - цвет символа
  352. bg - цвет фона
  353. в случае если параметр равен "" то он не изменяется
  354. attr может принимать следующие значения:
  355. 0 сбросить все атрибуты (вернуться в нормальный режим)
  356. 1 яркий (обычно включает толстый шрифт)
  357. 2 тусклый
  358. 3 подчёркнутый
  359. 5 мигающий
  360. 7 реверсный
  361. 8 невидимый
  362. fg может принимать следующие значения:
  363. 30 чёрный
  364. 31 красный
  365. 32 зелёный
  366. 33 жёлтый
  367. 34 синий
  368. 35 фиолетовый
  369. 36 голубой
  370. 37 белый
  371. bg может принимать следующие значения:
  372. 40 чёрный
  373. 41 красный
  374. 42 зелёный
  375. 43 жёлтый
  376. 44 синий
  377. 45 фиолетовый
  378. 46 голубой
  379. 47 белый
  380. """
  381. lst = []
  382. if attr:
  383. lst.append(attr)
  384. if fg:
  385. lst.append(fg)
  386. if bg:
  387. lst.append(bg)
  388. sys.stdout.write("\033[%sm%s\033[0m" %(";".join(lst),string))
  389. def redBrightPrint(self, string):
  390. """Печатает яркое красное сообщение"""
  391. self.colorPrint("1","31","",string)
  392. def greenBrightPrint(self, string):
  393. """Печатает яркое зеленое сообщение"""
  394. self.colorPrint("1","32","",string)
  395. def yellowBrightPrint(self, string):
  396. """Печатает яркое желтое сообщение"""
  397. self.colorPrint("1","33","",string)
  398. def blueBrightPrint(self, string):
  399. """Печатает яркое cинее сообщение"""
  400. self.colorPrint("1","34","",string)
  401. def lenString(self, string):
  402. """Получаем длинну строки"""
  403. stringUnicode = cl_utils._toUNICODE(string)
  404. lenString = len(stringUnicode)
  405. return lenString
  406. def defaultPrint(self, string):
  407. sys.stdout.write(string)
  408. sys.stdout.flush()
  409. def printLine(self, argL, argR, offsetL=0, printBR=True):
  410. """Печатает справа и слева консоли цветные сообщения"""
  411. #Допустимые цвета
  412. colorDict = {\
  413. # цвет по умолчанию
  414. '':self.defaultPrint,
  415. # ярко зеленый
  416. 'greenBr':self.greenBrightPrint,
  417. # ярко голубой
  418. 'blueBr':self.blueBrightPrint,
  419. # ярко красный
  420. 'redBr':self.redBrightPrint,
  421. # ярко желтый
  422. 'yellowBr':self.yellowBrightPrint,
  423. }
  424. # cмещение от левого края консоли
  425. #offsetL = 0
  426. for color,leftString in argL:
  427. offsetL += self.lenString(leftString)
  428. if colorDict.has_key(color):
  429. # печатаем и считаем смещение
  430. colorDict[color](leftString)
  431. else:
  432. colorDict[''](leftString)
  433. # cмещение от правого края консоли
  434. offsetR = 0
  435. for color,rightString in argR:
  436. offsetR += self.lenString(rightString)
  437. # Добавляем пробелы
  438. if offsetR:
  439. self.printRight(offsetL, offsetR)
  440. for color,rightString in argR:
  441. if colorDict.has_key(color):
  442. # печатаем и считаем смещение
  443. colorDict[color](rightString)
  444. else:
  445. colorDict[''](rightString)
  446. if printBR:
  447. print ""
  448. def printNotOK(self, string, offsetL=0, printBR=True):
  449. """Вывод на печать в случае сбоя"""
  450. self.printLine((('greenBr',' * '),
  451. ('',string),
  452. ),
  453. (('blueBr','['),
  454. ('redBr',' !! '),
  455. ('blueBr',']'),
  456. ), offsetL, printBR)
  457. def printOnlyNotOK(self, string, offsetL=0, printBR=True):
  458. """Вывод на печать в случае сбоя"""
  459. self.printLine((('', string),),
  460. (('blueBr','['),
  461. ('redBr',' !! '),
  462. ('blueBr',']'),
  463. ), offsetL, printBR)
  464. def printOK(self, string, offsetL=0, printBR=True):
  465. """Вывод на печать в случае успеха"""
  466. self.printLine((('greenBr',' * '),
  467. ('',string),
  468. ),
  469. (('blueBr','['),
  470. ('greenBr',' ok '),
  471. ('blueBr',']'),
  472. ), offsetL, printBR)
  473. def printOnlyOK(self, string, offsetL=0, printBR=True):
  474. """Вывод на печать в случае успеха"""
  475. self.printLine((('',string),),
  476. (('blueBr','['),
  477. ('greenBr',' ok '),
  478. ('blueBr',']'),
  479. ), offsetL, printBR)
  480. def printWARNING(self, string, offsetL=0, printBR=True):
  481. """Вывод на печать предупреждения"""
  482. self.printLine((('yellowBr',' * '),
  483. ('',string),
  484. ),
  485. (('',''),
  486. ), offsetL, printBR)
  487. def printERROR(self, string, offsetL=0, printBR=True):
  488. """Вывод на печать предупреждения"""
  489. self.printLine((('redBr',' * '),
  490. ('',string),
  491. ),
  492. (('',''),
  493. ), offsetL, printBR)
  494. def printSUCCESS(self, string, offsetL=0, printBR=True):
  495. """Вывод на печать в случае успеха без [ok] справа"""
  496. self.printLine((('greenBr',' * '),
  497. ('',string),
  498. ),
  499. (('',''),
  500. ), offsetL, printBR)