|
|
#-*- coding: utf-8 -*-
|
|
|
|
|
|
# Copyright 2008-2013 Calculate Ltd. http://www.calculate-linux.org
|
|
|
#
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
# you may not use this file except in compliance with the License.
|
|
|
# You may obtain a copy of the License at
|
|
|
#
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
|
#
|
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
# See the License for the specific language governing permissions and
|
|
|
# limitations under the License.
|
|
|
|
|
|
import os
|
|
|
from os import path
|
|
|
import subprocess
|
|
|
from subprocess import Popen,PIPE,STDOUT
|
|
|
import stat
|
|
|
from shutil import copytree, rmtree,copy2
|
|
|
from calculate.lib import cl_overriding
|
|
|
import re
|
|
|
import sys
|
|
|
from itertools import *
|
|
|
import tarfile
|
|
|
import select
|
|
|
import fcntl
|
|
|
|
|
|
from contextlib import contextmanager
|
|
|
import signal
|
|
|
|
|
|
@contextmanager
|
|
|
def timeout(seconds):
|
|
|
def timeout_handler(signum,frame):
|
|
|
pass
|
|
|
|
|
|
original_handler = signal.signal(signal.SIGALRM, timeout_handler)
|
|
|
|
|
|
try:
|
|
|
signal.alarm(seconds)
|
|
|
yield
|
|
|
finally:
|
|
|
signal.alarm(0)
|
|
|
signal.signal(signal.SIGALRM, original_handler)
|
|
|
|
|
|
try:
|
|
|
from magic import (open as type_file, MAGIC_NONE, MAGIC_CONTINUE,
|
|
|
MAGIC_MIME_TYPE, MAGIC_COMPRESS,
|
|
|
MAGIC_MIME_ENCODING, MAGIC_SYMLINK)
|
|
|
except ImportError as e:
|
|
|
try:
|
|
|
from magic import (open as type_file, NONE as MAGIC_NONE,
|
|
|
CONTINUE as MAGIC_CONTNUE, MIME_TYPE as MAGIC_MIME_TYPE,
|
|
|
COMPRESS as MAGIC_COMPRESS,MIME_ENCODING as MIME_ENCODING,
|
|
|
SYMLINK as MAGIC_SYMLINK)
|
|
|
except:
|
|
|
type_file = None
|
|
|
MAGIC_NONE = 0
|
|
|
MAGIC_SYMLINK = 0x002
|
|
|
MAGIC_COMPRESS = 0x004
|
|
|
MAGIC_CONTINUE = 0x020
|
|
|
MAGIC_MIME_TYPE = 0x010
|
|
|
MAGIC_MIME_ENCODING = 0x400
|
|
|
|
|
|
from calculate.lib.cl_lang import setLocalTranslate
|
|
|
setLocalTranslate('cl_lib3',sys.modules[__name__])
|
|
|
|
|
|
reSearch = lambda pattern,listar:map(lambda x:x.groups(),
|
|
|
filter(None,
|
|
|
map(pattern.search,listar)))
|
|
|
|
|
|
class FilesError(Exception):
|
|
|
pass
|
|
|
|
|
|
class process:
|
|
|
"""Execute system command by Popen
|
|
|
|
|
|
Examples:
|
|
|
|
|
|
execute program and get result:
|
|
|
if process("/bin/gzip","/boot/somefile").success():
|
|
|
print "Gzip success"
|
|
|
|
|
|
unzip and process unzip data by cpio (list files):
|
|
|
processGzip = process("/bin/gzip","-dc","/boot/initrd")
|
|
|
processCpio = process("/bin/cpio","-tf",stdin=processGzip)
|
|
|
filelist = processCpio.readlines()
|
|
|
|
|
|
execute command and send data:
|
|
|
processGrub = process("/sbin/grub")
|
|
|
processGrub.write("root (hd0,0)\n")
|
|
|
processGrub.write("setup (hd0)\n")
|
|
|
processGrub.write("quit\n")
|
|
|
isok = processGrub.success()
|
|
|
|
|
|
union stdout and stderr:
|
|
|
process("/bin/ls","/",stderr=STDOUT)
|
|
|
|
|
|
result to stdout:
|
|
|
process("/bin/ls",stdout=None)
|
|
|
|
|
|
get data from keyboard:
|
|
|
process("/bin/cat",stdin=None)
|
|
|
"""
|
|
|
def __init__(self,command,*params,**kwarg):
|
|
|
if not "stdin" in kwarg:
|
|
|
stdin=self._defaultStdin
|
|
|
else:
|
|
|
if kwarg["stdin"] == None:
|
|
|
stdin = self._keyboardStdin
|
|
|
else:
|
|
|
stdin=kwarg["stdin"].getStdout
|
|
|
self.stdout = kwarg.get("stdout",PIPE)
|
|
|
self.envdict = kwarg.get("envdict",os.environ.copy())
|
|
|
self.envdict["LANG"] = kwarg.get('lang','C')
|
|
|
self.langc = "langc" in kwarg
|
|
|
|
|
|
self.stderr = kwarg.get("stderr",PIPE)
|
|
|
self.cwd = kwarg.get("cwd",None)
|
|
|
self.command = getProgPath(command)
|
|
|
if not self.command:
|
|
|
raise FilesError(_("Command not found '%s'")%
|
|
|
command)
|
|
|
self.command = [self.command] + list(params)
|
|
|
self.stdin = stdin
|
|
|
self.iter = None
|
|
|
self.pipe = None
|
|
|
self.cacheresult = None
|
|
|
self.cacheerr = None
|
|
|
self.communicated = False
|
|
|
|
|
|
def _open(self):
|
|
|
"""Open pipe if it not open"""
|
|
|
if not self.pipe:
|
|
|
self.pipe = Popen(self.command,
|
|
|
stdout=self.stdout,
|
|
|
stdin=self.stdin(),
|
|
|
stderr=self.stderr,
|
|
|
cwd=self.cwd,
|
|
|
close_fds=True,
|
|
|
env=self.envdict)
|
|
|
|
|
|
def _defaultStdin(self):
|
|
|
"""Return default stdin"""
|
|
|
return PIPE
|
|
|
|
|
|
def _keyboardStdin(self):
|
|
|
"""Return keyboard stdin"""
|
|
|
return None
|
|
|
|
|
|
def getStdout(self):
|
|
|
"""Get current stdout"""
|
|
|
self.close()
|
|
|
self._open()
|
|
|
return self.pipe.stdout
|
|
|
|
|
|
def write(self,data):
|
|
|
"""Write to process stdin"""
|
|
|
self._open()
|
|
|
self.pipe.stdin.write(data)
|
|
|
self.pipe.stdin.flush()
|
|
|
|
|
|
def close(self):
|
|
|
"""Close stdin"""
|
|
|
if self.pipe:
|
|
|
self.pipe.stdin.close()
|
|
|
|
|
|
def readerr(self):
|
|
|
self.read()
|
|
|
return self.cacheerr
|
|
|
|
|
|
def readByLine(self):
|
|
|
|
|
|
try:
|
|
|
self._open()
|
|
|
if self.cacheresult is None:
|
|
|
self.cacheresult = []
|
|
|
s = self.pipe.stdout.readline()
|
|
|
while s:
|
|
|
yield s
|
|
|
s = self.pipe.stdout.readline()
|
|
|
if self.pipe.stderr:
|
|
|
self.cacheerr = self.pipe.stderr.read()
|
|
|
else:
|
|
|
self.cacheerr = ""
|
|
|
|
|
|
except KeyboardInterrupt:
|
|
|
self.kill()
|
|
|
raise KeyboardInterrupt
|
|
|
finally:
|
|
|
self.cacheresult = "\n".join(self.cacheresult)
|
|
|
|
|
|
def read(self):
|
|
|
"""Read all data"""
|
|
|
try:
|
|
|
self._open()
|
|
|
if self.cacheresult is None:
|
|
|
self.cacheresult,self.cacheerr = self.pipe.communicate()
|
|
|
return self.cacheresult
|
|
|
except KeyboardInterrupt:
|
|
|
self.kill()
|
|
|
raise KeyboardInterrupt
|
|
|
|
|
|
def readlines(self):
|
|
|
"""Read lines"""
|
|
|
return self.read().split('\n')
|
|
|
|
|
|
def __iter__(self):
|
|
|
"""Get iterator"""
|
|
|
if not self.iter:
|
|
|
self.iter = iter(self.readlines())
|
|
|
return self.iter
|
|
|
|
|
|
def kill(self):
|
|
|
"""Kill this process"""
|
|
|
if self.pipe:
|
|
|
self.pipe.kill()
|
|
|
|
|
|
def next(self):
|
|
|
"""Next string from stdout"""
|
|
|
return self.__iter__().next()
|
|
|
|
|
|
def returncode(self):
|
|
|
"""Get return code"""
|
|
|
self.read()
|
|
|
return self.pipe.returncode
|
|
|
|
|
|
def success(self):
|
|
|
"""Success or not"""
|
|
|
return self.returncode() == 0
|
|
|
|
|
|
def failed(self):
|
|
|
"""Failed or not"""
|
|
|
return self.returncode() != 0
|
|
|
|
|
|
|
|
|
def getModeFile(nameFile, mode="all"):
|
|
|
"""Выдает информацию о файле
|
|
|
mode=="all"
|
|
|
права файла, владелец, группа файла
|
|
|
mode=="mode"
|
|
|
права файла
|
|
|
mode=="owner"
|
|
|
владелец, группа файла
|
|
|
"""
|
|
|
fst = os.lstat(nameFile)
|
|
|
if mode == "all":
|
|
|
return (stat.S_IMODE(fst.st_mode), fst.st_uid, fst.st_gid)
|
|
|
if mode == "mode":
|
|
|
return stat.S_IMODE(fst.st_mode)
|
|
|
if mode == "owner":
|
|
|
return fst.st_uid, fst.st_gid
|
|
|
|
|
|
def chownR(directory, uid, gid):
|
|
|
"""Recusive chown"""
|
|
|
def chownPaths(rootPath, listPath, uid, gid):
|
|
|
for chPath in listPath:
|
|
|
chownPath = path.join(rootPath, chPath)
|
|
|
statInfo = os.lstat(chownPath)[stat.ST_MODE]
|
|
|
if stat.S_ISLNK(statInfo):
|
|
|
os.lchown(chownPath, uid, gid)
|
|
|
else:
|
|
|
os.chown(chownPath, uid, gid)
|
|
|
|
|
|
for root, dirs, files in os.walk(directory):
|
|
|
# меняем владельца директории
|
|
|
os.chown(root, uid, gid)
|
|
|
# Меняем владельца директорий
|
|
|
chownPaths(root, dirs, uid, gid)
|
|
|
# Меняем владельца файлов
|
|
|
chownPaths(root, files, uid, gid)
|
|
|
return True
|
|
|
|
|
|
class proxy_type_file:
|
|
|
def __init__(self,flags):
|
|
|
self.flags = flags
|
|
|
|
|
|
def load(self):
|
|
|
pass
|
|
|
|
|
|
def setflags(self,flags):
|
|
|
self.flags = flags
|
|
|
|
|
|
def close(self):
|
|
|
pass
|
|
|
|
|
|
def _get_cmd_by_flags(self,flags):
|
|
|
"""
|
|
|
Получить команду для выполнения по флагам
|
|
|
"""
|
|
|
paramdict = {MAGIC_CONTINUE:"-k",
|
|
|
MAGIC_SYMLINK:"-L",
|
|
|
MAGIC_MIME_TYPE:"--mime-type",
|
|
|
MAGIC_MIME_ENCODING:"--mime-encoding",
|
|
|
MAGIC_COMPRESS:"-z"}
|
|
|
appendParam = map(lambda x:paramdict[x],
|
|
|
filter(lambda x:flags & x,
|
|
|
sorted(paramdict.keys())))
|
|
|
fileCmd = getProgPath('/usr/bin/file')
|
|
|
return [fileCmd,'-b']+appendParam
|
|
|
|
|
|
def file(self,filename):
|
|
|
if path.exists(filename):
|
|
|
processFile = \
|
|
|
process(*(self._get_cmd_by_flags(self.flags)+[filename]))
|
|
|
if processFile.success():
|
|
|
return processFile.read().rstrip()
|
|
|
return None
|
|
|
|
|
|
class typeFile:
|
|
|
"""Получение типа файла"""
|
|
|
|
|
|
def __init__(self, magic=MAGIC_MIME_ENCODING|MAGIC_MIME_TYPE):
|
|
|
if type_file is None:
|
|
|
self.magicObject = proxy_type_file(MAGIC_NONE)
|
|
|
else:
|
|
|
self.magicObject = type_file(MAGIC_NONE)
|
|
|
self.magicObject.load()
|
|
|
self.magicObject.setflags(magic)
|
|
|
|
|
|
def __del__(self):
|
|
|
"""Закрываем magic"""
|
|
|
self.magicObject.close()
|
|
|
|
|
|
def getMType(self, filename):
|
|
|
"""Информация о типе файла"""
|
|
|
ret = self.magicObject.file(filename)
|
|
|
# fix for kernel 3.7.7 (bad work samba)
|
|
|
if ret is None and self.magicObject.errno() == 5:
|
|
|
r,w = os.pipe()
|
|
|
devnull = os.open(os.devnull,os.O_WRONLY)
|
|
|
cat = subprocess.Popen(['/bin/cat',filename],stdout=w,
|
|
|
stderr=devnull,close_fds=True)
|
|
|
ret = self.magicObject.descriptor(r)
|
|
|
os.close(w)
|
|
|
os.close(devnull)
|
|
|
cat.wait()
|
|
|
return ret
|
|
|
|
|
|
def isBinary(self, filename):
|
|
|
"""является ли файл бинарным"""
|
|
|
mime = self.getMType(filename)
|
|
|
if mime.startswith("text"):
|
|
|
return False
|
|
|
else:
|
|
|
return True
|
|
|
|
|
|
class scanDirectory:
|
|
|
"""Класс для cканирования директории"""
|
|
|
|
|
|
def processingFile(self, pathname, prefix):
|
|
|
"""Обработка в случае файла"""
|
|
|
return True
|
|
|
|
|
|
def processingDirectory(self, pathname, prefix):
|
|
|
"""Обработка в случае директории если возвращаем None то пропуск дир."""
|
|
|
return True
|
|
|
|
|
|
def scanningDirectory(self, scanDir, skipFile=[], skipDir=[],
|
|
|
prefix=None, flagDir=False):
|
|
|
"""Сканирование и обработка шаблонов в директории scanDir"""
|
|
|
ret = True
|
|
|
if not prefix:
|
|
|
prefix = path.join(scanDir,"")[:-1]
|
|
|
if not flagDir:
|
|
|
# проверка корневой директории
|
|
|
retDir = self.processingDirectory(scanDir, scanDir)
|
|
|
if retDir is None:
|
|
|
return None
|
|
|
elif retDir is False:
|
|
|
return False
|
|
|
if flagDir or stat.S_ISDIR(os.lstat(scanDir)[stat.ST_MODE]):
|
|
|
for absPath in sorted(listDirectory(scanDir,fullPath=True)):
|
|
|
relPath = absPath.split(prefix)[1]
|
|
|
stInfo = os.lstat(absPath)
|
|
|
statInfo = stInfo[stat.ST_MODE]
|
|
|
if stat.S_ISREG(statInfo):
|
|
|
# Обработка файла
|
|
|
if relPath in skipFile:
|
|
|
continue
|
|
|
if not self.processingFile(absPath, prefix):
|
|
|
return False
|
|
|
elif stat.S_ISDIR(statInfo):
|
|
|
# Обработка директории
|
|
|
if relPath in skipDir:
|
|
|
continue
|
|
|
retDir = self.processingDirectory(absPath, prefix)
|
|
|
if retDir is None:
|
|
|
continue
|
|
|
elif retDir is False:
|
|
|
return False
|
|
|
ret = self.scanningDirectory(absPath, skipFile,
|
|
|
skipDir, prefix, True)
|
|
|
if ret is False:
|
|
|
return False
|
|
|
return ret
|
|
|
|
|
|
class processProgress(process):
|
|
|
"""
|
|
|
Generator like process progress
|
|
|
"""
|
|
|
def __init__(self,command,*params,**kwarg):
|
|
|
process.__init__(self,command,*params,**kwarg)
|
|
|
self.readsize = kwarg.get("readsize",10)
|
|
|
self.delimeter = re.compile("\n")
|
|
|
self.init(**kwarg)
|
|
|
|
|
|
def init(self,*args,**kwarg):
|
|
|
pass
|
|
|
|
|
|
def processInit(self):
|
|
|
pass
|
|
|
|
|
|
def processEnd(self):
|
|
|
pass
|
|
|
|
|
|
def processString(self,strdata):
|
|
|
self.cacheresult.append(strdata)
|
|
|
return strdata
|
|
|
|
|
|
def processBuffer(self,buf):
|
|
|
pass
|
|
|
|
|
|
def progress(self):
|
|
|
try:
|
|
|
res = self.processInit()
|
|
|
if res != None: yield res
|
|
|
self._open()
|
|
|
if self.cacheresult is None:
|
|
|
self.cacheresult = []
|
|
|
self.buf = ""
|
|
|
fd = self.pipe.stdout.fileno()
|
|
|
fl = fcntl.fcntl(fd,fcntl.F_GETFL)
|
|
|
fcntl.fcntl(fd,fcntl.F_SETFL,fl | os.O_NONBLOCK)
|
|
|
hasData = True
|
|
|
while hasData or self.pipe.poll() is None:
|
|
|
hasData = False
|
|
|
try:
|
|
|
part = self.pipe.stdout.read(self.readsize)
|
|
|
if not part:
|
|
|
continue
|
|
|
except IOError as e:
|
|
|
# re-raise not "Resource temporary unavailable"
|
|
|
if e.errno != 11:
|
|
|
raise
|
|
|
continue
|
|
|
hasData = True
|
|
|
if self.buf:
|
|
|
self.buf += part
|
|
|
else:
|
|
|
self.buf = part
|
|
|
res = self.processBuffer(part)
|
|
|
if res:
|
|
|
yield res
|
|
|
while self.delimeter.search(self.buf):
|
|
|
strdata, self.buf = self.delimeter.split(self.buf,1)
|
|
|
res = self.processString(strdata)
|
|
|
if res:
|
|
|
yield res
|
|
|
res = self.processEnd()
|
|
|
if res != None: yield res
|
|
|
except KeyboardInterrupt:
|
|
|
self.pipe.kill()
|
|
|
raise
|
|
|
finally:
|
|
|
if self.cacheresult:
|
|
|
self.cacheresult = "\n".join(self.cacheresult)
|
|
|
else:
|
|
|
self.cacheresult = ""
|
|
|
|
|
|
def countFiles(dirpath,onefilesystem=True):
|
|
|
"""
|
|
|
Count files in specified dirpath
|
|
|
"""
|
|
|
num = 1
|
|
|
for dirpath,dirnames,filenames in os.walk(dirpath):
|
|
|
num += len(set(dirnames) | set(filenames))
|
|
|
if onefilesystem:
|
|
|
mountDirs = filter(lambda x:path.ismount(path.join(dirpath,x)),
|
|
|
dirnames)
|
|
|
for dirname in mountDirs:
|
|
|
dirnames.remove(dirname)
|
|
|
return num
|
|
|
|
|
|
def getFilesCount(directory):
|
|
|
"""Alias for compatibility"""
|
|
|
return countFiles(directory,onefilesystem=False)
|
|
|
|
|
|
|
|
|
def runOsCommand(cmd,in_str=None, env_dict=None):
|
|
|
"""Run system command
|
|
|
|
|
|
Параметры:
|
|
|
cmd внешняя программа
|
|
|
in_str данные передаваемые программе на страндартный вход.
|
|
|
env_dict словарь переменных окружения
|
|
|
Возвращает (код возврата, список stdout+stderr)
|
|
|
"""
|
|
|
pipe = subprocess.Popen(cmd, stdin=subprocess.PIPE,
|
|
|
stdout=subprocess.PIPE,
|
|
|
stderr=subprocess.PIPE,
|
|
|
env=env_dict,
|
|
|
close_fds=True,
|
|
|
shell=True)
|
|
|
fout, fin, ferr = (pipe.stdout, pipe.stdin, pipe.stderr)
|
|
|
# если есть данные на вход, передать их
|
|
|
if in_str:
|
|
|
fin.write(in_str)
|
|
|
fin.close()
|
|
|
res = map(lambda x: x.rstrip(), fout.readlines())
|
|
|
fout.close()
|
|
|
res += map(lambda x: x.rstrip(), ferr.readlines())
|
|
|
ferr.close()
|
|
|
# Код возврата
|
|
|
retcode = pipe.wait()
|
|
|
return retcode, res
|
|
|
|
|
|
def copyDir(srcDir, destDir):
|
|
|
"""Копируем директорию srcDir в destDir
|
|
|
|
|
|
При копировании сохраняются владелец, группа, права
|
|
|
"""
|
|
|
def ignoreFile(pathname, names):
|
|
|
"""Игнорирование сокетов при копировании"""
|
|
|
ignore = []
|
|
|
for name in names:
|
|
|
if stat.S_ISSOCK(os.lstat(path.join(pathname, name))[stat.ST_MODE]):
|
|
|
ignore.append(name)
|
|
|
return ignore
|
|
|
|
|
|
copytree(srcDir, destDir, ignore=ignoreFile)
|
|
|
return True
|
|
|
|
|
|
def removeDir(rmDir):
|
|
|
"""Рекурсивное удаление директории"""
|
|
|
rmtree(rmDir)
|
|
|
return True
|
|
|
|
|
|
def getRunCommands(not_chroot=False):
|
|
|
"""List run program"""
|
|
|
def getCmd(procNum):
|
|
|
cmdLineFile = '/proc/%s/cmdline'%procNum
|
|
|
try:
|
|
|
if path.exists(cmdLineFile):
|
|
|
if not_chroot:
|
|
|
root_link = '/proc/%s/root'%procNum
|
|
|
if os.readlink(root_link) != "/":
|
|
|
return ""
|
|
|
return open(cmdLineFile,'r').read().strip()
|
|
|
except Exception as e:
|
|
|
pass
|
|
|
return ""
|
|
|
if not os.access('/proc',os.R_OK):
|
|
|
return []
|
|
|
return filter(None,
|
|
|
map(getCmd,
|
|
|
filter(lambda x:x.isdigit(),
|
|
|
listDirectory('/proc'))))
|
|
|
|
|
|
|
|
|
def isMount(pathname):
|
|
|
"""В случае монтирования директории выдает другой примонтированный путь"""
|
|
|
findPath = [path.abspath(pathname)]
|
|
|
if findPath[0].startswith('/dev'):
|
|
|
info = device.getUdevDeviceInfo(name=findPath[0])
|
|
|
if 'DM_VG_NAME' in info and 'DM_LV_NAME' in info:
|
|
|
lvmDeviceName = \
|
|
|
'/dev/mapper/{vg}-{lv}'.format(vg=info['DM_VG_NAME'],
|
|
|
lv=info['DM_LV_NAME'])
|
|
|
findPath.append(lvmDeviceName)
|
|
|
mtabFile = '/etc/mtab'
|
|
|
if not os.access(mtabFile,os.R_OK):
|
|
|
return ""
|
|
|
return filter(lambda x: x!=findPath,
|
|
|
reduce(lambda x,y: y,
|
|
|
ifilter(lambda x: any(p in x for p in findPath),
|
|
|
imap(lambda x: [x[0], x[1]],
|
|
|
imap(lambda x: x.split(" "),
|
|
|
open(mtabFile)))), [""]))[0]
|
|
|
|
|
|
def commonPath(*paths):
|
|
|
"""Return common path from list of paths"""
|
|
|
paths = map(lambda x:path.normpath(x).split('/'),paths)
|
|
|
res = map(lambda x:x[0],
|
|
|
filter(lambda x:filter(lambda y:x[0]==y,x[1:]),zip(*paths)))
|
|
|
return "/".join(res)
|
|
|
|
|
|
def childMounts(pathname):
|
|
|
"""Get all mount points which contain path"""
|
|
|
if pathname != "none":
|
|
|
absPath = path.abspath(pathname)
|
|
|
else:
|
|
|
absPath = pathname
|
|
|
mtabFile = '/etc/mtab'
|
|
|
if not os.access(mtabFile,os.R_OK):
|
|
|
return ""
|
|
|
return reduce(lambda x,y: x + [y],
|
|
|
filter(lambda x: commonPath(absPath,x[0])==absPath or \
|
|
|
commonPath(absPath,x[1])==absPath,
|
|
|
map(lambda x: [x[0], x[1]],
|
|
|
map(lambda x: x.split(" "),
|
|
|
open(mtabFile)))),
|
|
|
[])
|
|
|
|
|
|
|
|
|
def pathJoin(*paths):
|
|
|
"""Складывает пути, в отличии от os.path.join, складывает абсолютные пути"""
|
|
|
if len(paths)==1:
|
|
|
return paths[0]
|
|
|
return reduce(path.join,
|
|
|
filter(lambda x:x and x != "/",
|
|
|
map(lambda x: x.startswith("/") and x[1:] or x,
|
|
|
paths[1:])),paths[0])
|
|
|
|
|
|
def checkDigestFile(digestfile):
|
|
|
"""Check digest by digestfile"""
|
|
|
reEntry = re.compile(r"# (\S+) HASH\n(\S+) (\S+)",re.S)
|
|
|
result = []
|
|
|
for alg,hashdata,filename in \
|
|
|
reEntry.findall(open(digestfile,'r').read()):
|
|
|
if hasattr(hashlib,alg.lower()):
|
|
|
hashobj = getattr(hashlib,alg.lower())
|
|
|
filename = path.join(path.dirname(digestfile),filename)
|
|
|
if os.path.exists(filename):
|
|
|
digest = hashobj(open(filename,'r').read())
|
|
|
result.append((alg,
|
|
|
digest.hexdigest().upper() == hashdata.upper()))
|
|
|
return result
|
|
|
|
|
|
def listDirectory(directory,fullPath=False,onlyDir=False):
|
|
|
"""Get files from directory, if it exists"""
|
|
|
if not path.exists(directory):
|
|
|
return []
|
|
|
try:
|
|
|
if fullPath:
|
|
|
if onlyDir:
|
|
|
return filter(lambda x:path.isdir(x),
|
|
|
map(lambda x:path.join(directory,x),
|
|
|
os.listdir(directory)))
|
|
|
else:
|
|
|
return map(lambda x:path.join(directory,x),
|
|
|
os.listdir(directory))
|
|
|
else:
|
|
|
if onlyDir:
|
|
|
return filter(lambda x:path.isdir(path.join(directory,x)),
|
|
|
os.listdir(directory))
|
|
|
else:
|
|
|
return os.listdir(directory)
|
|
|
except OSError:
|
|
|
pass
|
|
|
return []
|
|
|
|
|
|
def makeDirectory(pathname):
|
|
|
"""Make directory and parent.
|
|
|
|
|
|
Return False on error"""
|
|
|
try:
|
|
|
parent = path.split(path.normpath(pathname))[0]
|
|
|
if not path.exists(parent):
|
|
|
makeDirectory(parent)
|
|
|
else:
|
|
|
if path.exists(pathname):
|
|
|
return True
|
|
|
os.mkdir(pathname)
|
|
|
return True
|
|
|
except Exception, e:
|
|
|
return False
|
|
|
|
|
|
def readLinesFile(filename):
|
|
|
"""Read file by line"""
|
|
|
try:
|
|
|
if path.exists(filename):
|
|
|
for line in open(filename,'r'):
|
|
|
yield line.rstrip('\n')
|
|
|
except (OSError,IOError):
|
|
|
pass
|
|
|
finally:
|
|
|
raise StopIteration
|
|
|
|
|
|
def readFile(filename,tailbyte=None):
|
|
|
"""
|
|
|
Прочитать целый файл или вернуть пустую строку.
|
|
|
tailbyte: прочитать только последнее указанное количество байт
|
|
|
"""
|
|
|
try:
|
|
|
if path.exists(filename):
|
|
|
with open(filename,'r') as f:
|
|
|
if tailbyte:
|
|
|
seeksize = max(0,os.stat(filename).st_size-tailbyte)
|
|
|
if seeksize:
|
|
|
f.seek(seeksize)
|
|
|
return f.read()
|
|
|
except (OSError,IOError):
|
|
|
pass
|
|
|
return ""
|
|
|
|
|
|
def writeFile(filename):
|
|
|
"""
|
|
|
Открыть файл на запись и создать необходимые каталоги
|
|
|
"""
|
|
|
dn = path.dirname(filename)
|
|
|
if not path.exists(dn):
|
|
|
os.makedirs(dn)
|
|
|
return open(filename,'w')
|
|
|
|
|
|
import common
|
|
|
|
|
|
class getProgPath:
|
|
|
"""Get full path of program or False"""
|
|
|
cache = {}
|
|
|
|
|
|
def __call__(self,progname):
|
|
|
baseprogname = path.basename(progname)
|
|
|
if baseprogname in self.cache:
|
|
|
return self.cache[baseprogname]
|
|
|
env = {"LANG":"C"}
|
|
|
env.update(os.environ.items() + [("PATH",common.getpathenv())] +\
|
|
|
env.items())
|
|
|
res = runOsCommand("which %s"%baseprogname,env_dict=env)
|
|
|
if path.isabs(progname) and path.exists(progname):
|
|
|
self.cache[baseprogname] = progname
|
|
|
return self.cache[baseprogname]
|
|
|
elif res[0] == 0:
|
|
|
self.cache[baseprogname] = res[1][0].strip()
|
|
|
return self.cache[baseprogname]
|
|
|
else:
|
|
|
return False
|
|
|
|
|
|
getProgPath = getProgPath()
|
|
|
|
|
|
def checkUtils(*utils):
|
|
|
"""Check utils, exit if it not found and return fullpath"""
|
|
|
retval = []
|
|
|
for util in utils:
|
|
|
utilPath = getProgPath(util)
|
|
|
if not utilPath:
|
|
|
raise FilesError(_("Command not found '%s'")%
|
|
|
path.basename(util))
|
|
|
retval.append(utilPath)
|
|
|
if len(retval) == 1:
|
|
|
return retval[0]
|
|
|
else:
|
|
|
return retval
|
|
|
|
|
|
def getCmdLineParam(paramName):
|
|
|
"""Get value of param /proc/cmdline. If param not found then empty.
|
|
|
"""
|
|
|
cmdLine = '/proc/cmdline'
|
|
|
paramName = "%s="%paramName
|
|
|
params = \
|
|
|
map(lambda x:x.partition('=')[2],
|
|
|
filter(lambda x:x.startswith(paramName),
|
|
|
readFile(cmdLine).split(' ')))
|
|
|
if params:
|
|
|
return params[-1]
|
|
|
else:
|
|
|
return ""
|
|
|
|
|
|
from device import getUUIDDict
|
|
|
|
|
|
def isFstabMount(pathname,mapDevUuid={},listFstab=[]):
|
|
|
"""Get mount point or device from fstab"""
|
|
|
def removeQuotes(s):
|
|
|
return s.replace('"','').replace("'","")
|
|
|
if pathname == "swap":
|
|
|
absPath = "swap"
|
|
|
else:
|
|
|
absPath = path.abspath(pathname)
|
|
|
if not mapDevUuid:
|
|
|
mapDevUuid.update(getUUIDDict())
|
|
|
# convert fstab to
|
|
|
# [['/dev/sda3', '/', '', 'reiserfs', 'noatime', '', '', '0', '2\n'],
|
|
|
# ['/dev/sda5', '/var/calculate', 'reiserfs', 'noatime', '0', '0\n']]
|
|
|
#if not listFstab:
|
|
|
if not listFstab:
|
|
|
listFstab.extend(
|
|
|
map(lambda x: [mapDevUuid.get(removeQuotes(x[0]),x[0]),
|
|
|
x[1] if x[2] != "swap" else "swap"],
|
|
|
filter(lambda x: len(x) >= 4,
|
|
|
map(lambda x: filter(lambda x: x,
|
|
|
x.replace('\t',' ').split(' ')),
|
|
|
filter(lambda x: not x.startswith('#') and x.strip(),
|
|
|
open("/etc/fstab"))))))
|
|
|
# get mount point or device or dir
|
|
|
return filter(lambda x: x!=absPath,
|
|
|
reduce(lambda x,y: y,
|
|
|
filter(lambda x: absPath in x and x[1] != "none",
|
|
|
listFstab),[""]))[0]
|
|
|
|
|
|
class SingletonParam(type):
|
|
|
def __init__(cls, name, bases, dict):
|
|
|
super(SingletonParam, cls).__init__(name, bases, dict)
|
|
|
cls.instance = {}
|
|
|
|
|
|
def __call__(cls,*args,**kw):
|
|
|
keyarg = args[0] if args else ""
|
|
|
if not keyarg in cls.instance:
|
|
|
cls.instance[keyarg] = \
|
|
|
super(SingletonParam, cls).__call__(*args, **kw)
|
|
|
return cls.instance[keyarg]
|
|
|
|
|
|
import device
|
|
|
|
|
|
class FStab(object):
|
|
|
"""Data reader for fstab"""
|
|
|
__metaclass__ = SingletonParam
|
|
|
fstab_file = '/etc/fstab'
|
|
|
NAME, DIR, TYPE, OPTS, FREQ, PASSNO = range(0,6)
|
|
|
|
|
|
def __init__(self,fstab_file=None,devs=[]):
|
|
|
if fstab_file:
|
|
|
self.fstab_file = fstab_file
|
|
|
self.cache = []
|
|
|
self.rotateCache = []
|
|
|
self.dictUUID = getUUIDDict(devs=devs)
|
|
|
self.rebuildCache()
|
|
|
|
|
|
def rebuildCache(self):
|
|
|
"""Rebuild cache from fstab file"""
|
|
|
def setlen(ar):
|
|
|
return ar[:6]+[""]*(6-len(ar))
|
|
|
self.cache = \
|
|
|
map(lambda x:setlen(map(lambda y:y.strip(),x.split())),
|
|
|
filter(lambda x:x.strip() and not x.lstrip().startswith("#"),
|
|
|
open(self.fstab_file,'r').read().split('\n')))
|
|
|
for data in self.cache:
|
|
|
convertDev = lambda x: path.realpath(x) if x.startswith('/') else x
|
|
|
data[0] = device.getUdevDeviceInfo(
|
|
|
name=convertDev(self.dictUUID.get(data[0],data[0]))
|
|
|
).get('DEVNAME',data[0])
|
|
|
data[1] = data[1] if data[2] != "swap" else "swap"
|
|
|
self.rotateCache = zip(*self.cache)
|
|
|
|
|
|
def getBy(self,what=DIR,where=NAME,_in=None,eq=None,
|
|
|
noteq=None,allentry=False):
|
|
|
"""Get data from fstab"""
|
|
|
if not eq is None:
|
|
|
filterfunc = lambda x: x[where] == eq
|
|
|
elif not _in is None:
|
|
|
filterfunc = lambda x: _in in x[where]
|
|
|
else:
|
|
|
filterfunc = lambda x: x[where] != noteq
|
|
|
res = map(lambda x:x[what],filter(filterfunc,self.cache))
|
|
|
if allentry:
|
|
|
return res
|
|
|
else:
|
|
|
return "" if not res else res[-1]
|
|
|
|
|
|
def getFields(self,*fields):
|
|
|
"""Get all data by specifie fields"""
|
|
|
return zip(*reduce(lambda x,y:x+[self.rotateCache[y]],fields,[]))
|
|
|
|
|
|
def isExists(self,what=DIR,eq=None,noteq=None):
|
|
|
"""Field with condition exist in fstab"""
|
|
|
if not eq is None:
|
|
|
filterfunc = lambda x: x[what] == eq
|
|
|
else:
|
|
|
filterfunc = lambda x: x[what] != noteq
|
|
|
return bool(filter(filterfunc,self.cache))
|
|
|
|
|
|
def tarLinks(rootpath,archpath,skip=[]):
|
|
|
"""
|
|
|
Поместить все симлинки в архив
|
|
|
"""
|
|
|
links = []
|
|
|
lenprefix = len(path.normpath(rootpath))+1
|
|
|
if path.exists(archpath):
|
|
|
os.unlink(archpath)
|
|
|
# create arch
|
|
|
tar = tarfile.open(archpath,"w:bz2")
|
|
|
# find links
|
|
|
if skip:
|
|
|
reSkip = re.compile("|".join(map(lambda x:x.replace("*",".*"),
|
|
|
skip))).search
|
|
|
else:
|
|
|
reSkip = lambda x:False
|
|
|
for link in ifilterfalse(reSkip,
|
|
|
find(rootpath,filetype="l",onefilesystem=True)):
|
|
|
ti = tar.gettarinfo(link)
|
|
|
ti.name = link[lenprefix:]
|
|
|
tar.addfile(ti)
|
|
|
links.append(link)
|
|
|
tar.close()
|
|
|
return links
|
|
|
|
|
|
def getch():
|
|
|
"""
|
|
|
Get char from keyboard
|
|
|
"""
|
|
|
import sys, tty, termios
|
|
|
|
|
|
fd = sys.stdin.fileno()
|
|
|
old_settings = termios.tcgetattr(fd)
|
|
|
try:
|
|
|
tty.setraw(sys.stdin.fileno())
|
|
|
ch = sys.stdin.read(1)
|
|
|
finally:
|
|
|
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
|
|
|
return ch
|
|
|
|
|
|
def find(directory,onefilesystem=False,filetype=None,
|
|
|
fullpath=True, depth=None, downfilter=None):
|
|
|
"""
|
|
|
Поиск файлов в каталоге
|
|
|
"""
|
|
|
ldirectory = len(directory)
|
|
|
if downfilter or onefilesystem or depth:
|
|
|
_downfilter = lambda dp: (
|
|
|
(depth is None or dp[ldirectory:].count('/') < depth) and
|
|
|
(not downfilter or downfiler(dp)) and
|
|
|
(not onefilesystem or not path.ismount(dp)))
|
|
|
else:
|
|
|
_downfilter = None
|
|
|
|
|
|
if filetype:
|
|
|
def dirfilter(dp):
|
|
|
link = path.islink(dp)
|
|
|
return ("l" in filetype and link) or ("d" in filetype and not link)
|
|
|
_dirfilter = dirfilter
|
|
|
else:
|
|
|
_dirfilter = None
|
|
|
|
|
|
if fullpath or _dirfilter or _downfilter:
|
|
|
_dirfullpath = lambda root,fn: path.join(root,fn)
|
|
|
else:
|
|
|
_dirfullpath = lambda root,fn:fn
|
|
|
|
|
|
if filetype:
|
|
|
def filefilter(fp):
|
|
|
link = path.islink(fp)
|
|
|
return ("l" in filetype and link) or ("f" in filetype and not link)
|
|
|
_filefilter = filefilter
|
|
|
else:
|
|
|
_filefilter = None
|
|
|
|
|
|
if fullpath or _filefilter:
|
|
|
_filefullpath = lambda root,fn: path.join(root,fn)
|
|
|
else:
|
|
|
_filefullpath = lambda root,fn:fn
|
|
|
|
|
|
for root,directories,files in os.walk(directory):
|
|
|
for fn in files:
|
|
|
fp = _filefullpath(root,fn)
|
|
|
if not _filefilter or _filefilter(fp):
|
|
|
yield fp if fullpath else fn
|
|
|
for dn in directories[:]:
|
|
|
dp = _dirfullpath(root,dn)
|
|
|
if not _dirfilter or _dirfilter(dp):
|
|
|
yield dp if fullpath else dn
|
|
|
if _downfilter and not _downfilter(dp):
|
|
|
directories.remove(dn)
|
|
|
|
|
|
def copyWithPath(fn,dest,prefix=None):
|
|
|
"""
|
|
|
Копировать файл в dst с созданием каталогов
|
|
|
|
|
|
source: префикс исходного файла
|
|
|
dest: каталог для копирывания
|
|
|
fn: имя файла
|
|
|
"""
|
|
|
try:
|
|
|
if prefix:
|
|
|
destFile = pathJoin(dest,fn[len(prefix):])
|
|
|
else:
|
|
|
destFile = pathJoin(dest,path.basename(fn))
|
|
|
destDir = path.dirname(destFile)
|
|
|
if not path.exists(destDir):
|
|
|
os.makedirs(destDir)
|
|
|
copy2(fn,destFile)
|
|
|
except Exception as e:
|
|
|
raise FilesError(
|
|
|
_("Failed to copy '%(src)s' to '%(dst)s'")%
|
|
|
{'src':fn,
|
|
|
'dst':destFile})
|
|
|
|
|
|
def getLoopFromPath(directory):
|
|
|
"""
|
|
|
Получить loop, использующие файлы в данной директории
|
|
|
"""
|
|
|
losetup = getProgPath('losetup')
|
|
|
p = process(losetup,'-a')
|
|
|
rePattern = re.compile('(^\/dev\/loop[^:]+)[^(]+\(([^)]+)\).*')
|
|
|
return map(lambda x:x[0],
|
|
|
filter(lambda x:x[1].startswith(directory),
|
|
|
reSearch(rePattern,
|
|
|
p.readlines())))
|
|
|
|
|
|
def getMdRaidDevices():
|
|
|
"""
|
|
|
Получить словарь: какой mdraid какие использует устройства
|
|
|
"""
|
|
|
reMdInfo = re.compile('^(\w+)\s+:\s+active.*?raid\d+\s+(.*)')
|
|
|
return dict(map(lambda x:(x[0],map(lambda x:x.partition('[')[0],
|
|
|
x[1].split())),
|
|
|
reSearch(reMdInfo,readLinesFile('/proc/mdstat'))))
|
|
|
|
|
|
def sambaPasswordCheck(username,password,server,resource):
|
|
|
"""
|
|
|
Подключиться к указанному samba ресурсу сервера по логину паролю
|
|
|
"""
|
|
|
smbclient = getProgPath('/usr/sbin/smbclient')
|
|
|
if not smbclient:
|
|
|
raise FilesError(_("Command not found '%s'")%"smbclint")
|
|
|
p = process(smbclient,"-U",username,"//%s/%s"%(server,resource),
|
|
|
envdict={'PASSWD':password})
|
|
|
return p.success()
|
|
|
|
|
|
class PercentProgress(processProgress):
|
|
|
"""
|
|
|
Объект выдает прогресс, ища в выводе \d+%
|
|
|
|
|
|
Args:
|
|
|
part: количество прогрессов в программе
|
|
|
delimeter: разделители строк по умолчанию \n и \r
|
|
|
cachefilter: фильтр вывода программы (регулярная строка)
|
|
|
startpart: используется для вывода процентов прогрессбар
|
|
|
с определенного места (0 по умолчанию)
|
|
|
end: конечный прогрессбар (по умолчанию)
|
|
|
atty: для получения данных создается pty
|
|
|
"""
|
|
|
def init(self,*args,**kwargs):
|
|
|
self.rePerc = re.compile("(\d+)%",re.S)
|
|
|
self.part = kwargs.get("part",1)
|
|
|
if self.part < 1:
|
|
|
self.part = 1
|
|
|
self.add_offset = 100 / self.part
|
|
|
self.offset = 0 + kwargs.get("startpart",0)*self.add_offset
|
|
|
self.is_end = kwargs.get("end",True)
|
|
|
self.stderr = STDOUT
|
|
|
self.delimeter = re.compile("[%s]"%kwargs.get("delimeter","\n\r"))
|
|
|
self.cachedata = re.compile(kwargs.get("cachefilter",
|
|
|
"((?:\[31;01m\*|\[33;01m\*|error:|warning:|fatal:).*)"))
|
|
|
self.atty = kwargs.get("atty",False)
|
|
|
self.alldata = ""
|
|
|
|
|
|
def _open(self):
|
|
|
self.master,self.slave = None,None
|
|
|
if self.atty:
|
|
|
if not self.pipe:
|
|
|
self.master,self.slave = os.openpty()
|
|
|
self.pipe = Popen(self.command,
|
|
|
stdout=self.slave,
|
|
|
stderr=self.slave,
|
|
|
cwd=self.cwd,
|
|
|
close_fds=True,
|
|
|
env=self.envdict)
|
|
|
self.pipe.stdout = os.fdopen(self.master,'r')
|
|
|
else:
|
|
|
process._open(self)
|
|
|
|
|
|
def processInit(self):
|
|
|
self.percent = 0
|
|
|
self.showval = 0
|
|
|
if not self.offset:
|
|
|
return 0
|
|
|
|
|
|
def processEnd(self):
|
|
|
if not self.slave is None:
|
|
|
os.close(self.slave)
|
|
|
if self.is_end:
|
|
|
self.percent = 100
|
|
|
return 100
|
|
|
|
|
|
def processString(self, strdata):
|
|
|
self.alldata += strdata
|
|
|
match = self.rePerc.search(strdata)
|
|
|
resSearch = self.cachedata.search(strdata)
|
|
|
if resSearch:
|
|
|
self.cacheresult.append(
|
|
|
re.sub("\[31;01m\*|\[33;01m\*|\[.*?m",
|
|
|
"", resSearch.group(1)))
|
|
|
if match:
|
|
|
percent = int(match.group(1))
|
|
|
if percent < self.percent:
|
|
|
self.offset = self.offset + self.add_offset
|
|
|
percent = percent / self.part
|
|
|
if percent != self.percent:
|
|
|
self.percent = percent
|
|
|
showval = min(99,self.percent + self.offset)
|
|
|
if showval != self.showval:
|
|
|
self.showval = showval
|
|
|
return self.showval
|
|
|
|
|
|
|
|
|
def set_active_tty(ttynum):
|
|
|
"""
|
|
|
Установить активный терминал
|
|
|
"""
|
|
|
chvt = getProgPath("/usr/bin/chvt2")
|
|
|
if chvt:
|
|
|
os.system('chvt %d &>/dev/null' % ttynum)
|