On modifie un peu le plugin de dim pour handle les bonnes choses, on ajoute un tool python, et on met mac_prises dans le dépot.
Ignore-this: a97c9dd382aae257e5d64b3fc14d0469 darcs-hash:20121211191448-b6762-f8705ea02bdfe778cd81d98ee52f2fa2533d68e0.gz
This commit is contained in:
parent
fbc7f2c0f4
commit
dfcda07054
6 changed files with 852 additions and 7 deletions
|
@ -90,7 +90,7 @@ def set_mail_invalide(adherent, mail, a_verifier, a_imprimer):
|
||||||
'-', 'mail_invalide', "Mail invalide"])
|
'-', 'mail_invalide', "Mail invalide"])
|
||||||
adherent.save()
|
adherent.save()
|
||||||
else:
|
else:
|
||||||
print "Chambre de %s : %s, impossible de générer la fiche." % (adherent.Nom().encode('utf-8'), adherent.chbre())
|
print u"Chambre de %s : %s, impossible de générer la fiche." % (adherent.Nom().encode('utf-8'), adherent.chbre())
|
||||||
a_verifier.append(mail)
|
a_verifier.append(mail)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
683
bcfg2/Tools/Python.py
Normal file
683
bcfg2/Tools/Python.py
Normal file
|
@ -0,0 +1,683 @@
|
||||||
|
"""All Python Type client support for Bcfg2."""
|
||||||
|
__revision__ = '$Revision$'
|
||||||
|
|
||||||
|
import binascii
|
||||||
|
from datetime import datetime
|
||||||
|
import difflib
|
||||||
|
import errno
|
||||||
|
import grp
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import pwd
|
||||||
|
import shutil
|
||||||
|
import stat
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
# py3k compatibility
|
||||||
|
if sys.hexversion >= 0x03000000:
|
||||||
|
unicode = str
|
||||||
|
|
||||||
|
import Bcfg2.Client.Tools
|
||||||
|
import Bcfg2.Options
|
||||||
|
from Bcfg2.Client import XML
|
||||||
|
|
||||||
|
log = logging.getLogger('python')
|
||||||
|
|
||||||
|
# map between dev_type attribute and stat constants
|
||||||
|
device_map = {'block': stat.S_IFBLK,
|
||||||
|
'char': stat.S_IFCHR,
|
||||||
|
'fifo': stat.S_IFIFO}
|
||||||
|
|
||||||
|
|
||||||
|
def calcPerms(initial, perms):
|
||||||
|
"""This compares ondisk permissions with specified ones."""
|
||||||
|
pdisp = [{1:stat.S_ISVTX, 2:stat.S_ISGID, 4:stat.S_ISUID},
|
||||||
|
{1:stat.S_IXUSR, 2:stat.S_IWUSR, 4:stat.S_IRUSR},
|
||||||
|
{1:stat.S_IXGRP, 2:stat.S_IWGRP, 4:stat.S_IRGRP},
|
||||||
|
{1:stat.S_IXOTH, 2:stat.S_IWOTH, 4:stat.S_IROTH}]
|
||||||
|
tempperms = initial
|
||||||
|
if len(perms) == 3:
|
||||||
|
perms = '0%s' % (perms)
|
||||||
|
pdigits = [int(perms[digit]) for digit in range(4)]
|
||||||
|
for index in range(4):
|
||||||
|
for (num, perm) in list(pdisp[index].items()):
|
||||||
|
if pdigits[index] & num:
|
||||||
|
tempperms |= perm
|
||||||
|
return tempperms
|
||||||
|
|
||||||
|
|
||||||
|
def normGid(entry):
|
||||||
|
"""
|
||||||
|
This takes a group name or gid and
|
||||||
|
returns the corresponding gid or False.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
return int(entry.get('group'))
|
||||||
|
except:
|
||||||
|
return int(grp.getgrnam(entry.get('group'))[2])
|
||||||
|
except (OSError, KeyError):
|
||||||
|
log.error('GID normalization failed for %s. Does group %s exist?'
|
||||||
|
% (entry.get('name'), entry.get('group')))
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def normUid(entry):
|
||||||
|
"""
|
||||||
|
This takes a user name or uid and
|
||||||
|
returns the corresponding uid or False.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
return int(entry.get('owner'))
|
||||||
|
except:
|
||||||
|
return int(pwd.getpwnam(entry.get('owner'))[2])
|
||||||
|
except (OSError, KeyError):
|
||||||
|
log.error('UID normalization failed for %s. Does owner %s exist?'
|
||||||
|
% (entry.get('name'), entry.get('owner')))
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def isString(strng, encoding):
|
||||||
|
"""
|
||||||
|
Returns true if the string contains no ASCII control characters
|
||||||
|
and can be decoded from the specified encoding.
|
||||||
|
"""
|
||||||
|
for char in strng:
|
||||||
|
if ord(char) < 9 or ord(char) > 13 and ord(char) < 32:
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
strng.decode(encoding)
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class Python(Bcfg2.Client.Tools.Tool):
|
||||||
|
"""Python File support code."""
|
||||||
|
name = 'Python'
|
||||||
|
__handles__ = [('Python', 'file'),
|
||||||
|
('Python', None)]
|
||||||
|
__req__ = {'Python': ['name']}
|
||||||
|
|
||||||
|
# grab paranoid options from /etc/bcfg2.conf
|
||||||
|
opts = {'ppath': Bcfg2.Options.PARANOID_PATH,
|
||||||
|
'max_copies': Bcfg2.Options.PARANOID_MAX_COPIES}
|
||||||
|
setup = Bcfg2.Options.OptionParser(opts)
|
||||||
|
setup.parse([])
|
||||||
|
ppath = setup['ppath']
|
||||||
|
max_copies = setup['max_copies']
|
||||||
|
|
||||||
|
def canInstall(self, entry):
|
||||||
|
"""Check if entry is complete for installation."""
|
||||||
|
if Bcfg2.Client.Tools.Tool.canInstall(self, entry):
|
||||||
|
if (entry.tag,
|
||||||
|
entry.get('type'),
|
||||||
|
entry.text,
|
||||||
|
entry.get('empty', 'false')) == ('Python',
|
||||||
|
'file',
|
||||||
|
None,
|
||||||
|
'false'):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def gatherCurrentData(self, entry):
|
||||||
|
if entry.tag == 'Python' and entry.get('type') == 'file':
|
||||||
|
try:
|
||||||
|
ondisk = os.stat(entry.get('name'))
|
||||||
|
except OSError:
|
||||||
|
entry.set('current_exists', 'false')
|
||||||
|
self.logger.debug("%s %s does not exist" %
|
||||||
|
(entry.tag, entry.get('name')))
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
entry.set('current_owner', str(ondisk[stat.ST_UID]))
|
||||||
|
entry.set('current_group', str(ondisk[stat.ST_GID]))
|
||||||
|
except (OSError, KeyError):
|
||||||
|
pass
|
||||||
|
entry.set('perms', str(oct(ondisk[stat.ST_MODE])[-4:]))
|
||||||
|
|
||||||
|
def Verifydirectory(self, entry, modlist):
|
||||||
|
"""Verify Path type='directory' entry."""
|
||||||
|
if entry.get('perms') == None or \
|
||||||
|
entry.get('owner') == None or \
|
||||||
|
entry.get('group') == None:
|
||||||
|
self.logger.error('Entry %s not completely specified. '
|
||||||
|
'Try running bcfg2-lint.' % (entry.get('name')))
|
||||||
|
return False
|
||||||
|
while len(entry.get('perms', '')) < 4:
|
||||||
|
entry.set('perms', '0' + entry.get('perms', ''))
|
||||||
|
try:
|
||||||
|
ondisk = os.stat(entry.get('name'))
|
||||||
|
except OSError:
|
||||||
|
entry.set('current_exists', 'false')
|
||||||
|
self.logger.debug("%s %s does not exist" %
|
||||||
|
(entry.tag, entry.get('name')))
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
owner = str(ondisk[stat.ST_UID])
|
||||||
|
group = str(ondisk[stat.ST_GID])
|
||||||
|
except (OSError, KeyError):
|
||||||
|
self.logger.error('User/Group resolution failed for path %s' % \
|
||||||
|
entry.get('name'))
|
||||||
|
owner = 'root'
|
||||||
|
group = '0'
|
||||||
|
finfo = os.stat(entry.get('name'))
|
||||||
|
perms = oct(finfo[stat.ST_MODE])[-4:]
|
||||||
|
if entry.get('mtime', '-1') != '-1':
|
||||||
|
mtime = str(finfo[stat.ST_MTIME])
|
||||||
|
else:
|
||||||
|
mtime = '-1'
|
||||||
|
pTrue = ((owner == str(normUid(entry))) and
|
||||||
|
(group == str(normGid(entry))) and
|
||||||
|
(perms == entry.get('perms')) and
|
||||||
|
(mtime == entry.get('mtime', '-1')))
|
||||||
|
|
||||||
|
pruneTrue = True
|
||||||
|
ex_ents = []
|
||||||
|
if entry.get('prune', 'false') == 'true' \
|
||||||
|
and (entry.tag == 'Path' and entry.get('type') == 'directory'):
|
||||||
|
# check for any extra entries when prune='true' attribute is set
|
||||||
|
try:
|
||||||
|
entries = ['/'.join([entry.get('name'), ent]) \
|
||||||
|
for ent in os.listdir(entry.get('name'))]
|
||||||
|
ex_ents = [e for e in entries if e not in modlist]
|
||||||
|
if ex_ents:
|
||||||
|
pruneTrue = False
|
||||||
|
self.logger.debug("Directory %s contains extra entries:" % \
|
||||||
|
entry.get('name'))
|
||||||
|
self.logger.debug(ex_ents)
|
||||||
|
nqtext = entry.get('qtext', '') + '\n'
|
||||||
|
nqtext += "Directory %s contains extra entries:" % \
|
||||||
|
entry.get('name')
|
||||||
|
nqtext += ":".join(ex_ents)
|
||||||
|
entry.set('qtest', nqtext)
|
||||||
|
[entry.append(XML.Element('Prune', path=x)) \
|
||||||
|
for x in ex_ents]
|
||||||
|
except OSError:
|
||||||
|
ex_ents = []
|
||||||
|
pruneTrue = True
|
||||||
|
|
||||||
|
if not pTrue:
|
||||||
|
if owner != str(normUid(entry)):
|
||||||
|
entry.set('current_owner', owner)
|
||||||
|
self.logger.debug("%s %s ownership wrong" % \
|
||||||
|
(entry.tag, entry.get('name')))
|
||||||
|
nqtext = entry.get('qtext', '') + '\n'
|
||||||
|
nqtext += "%s owner wrong. is %s should be %s" % \
|
||||||
|
(entry.get('name'), owner, entry.get('owner'))
|
||||||
|
entry.set('qtext', nqtext)
|
||||||
|
if group != str(normGid(entry)):
|
||||||
|
entry.set('current_group', group)
|
||||||
|
self.logger.debug("%s %s group wrong" % \
|
||||||
|
(entry.tag, entry.get('name')))
|
||||||
|
nqtext = entry.get('qtext', '') + '\n'
|
||||||
|
nqtext += "%s group is %s should be %s" % \
|
||||||
|
(entry.get('name'), group, entry.get('group'))
|
||||||
|
entry.set('qtext', nqtext)
|
||||||
|
if perms != entry.get('perms'):
|
||||||
|
entry.set('current_perms', perms)
|
||||||
|
self.logger.debug("%s %s permissions are %s should be %s" %
|
||||||
|
(entry.tag,
|
||||||
|
entry.get('name'),
|
||||||
|
perms,
|
||||||
|
entry.get('perms')))
|
||||||
|
nqtext = entry.get('qtext', '') + '\n'
|
||||||
|
nqtext += "%s %s perms are %s should be %s" % \
|
||||||
|
(entry.tag,
|
||||||
|
entry.get('name'),
|
||||||
|
perms,
|
||||||
|
entry.get('perms'))
|
||||||
|
entry.set('qtext', nqtext)
|
||||||
|
if mtime != entry.get('mtime', '-1'):
|
||||||
|
entry.set('current_mtime', mtime)
|
||||||
|
self.logger.debug("%s %s mtime is %s should be %s" \
|
||||||
|
% (entry.tag, entry.get('name'), mtime,
|
||||||
|
entry.get('mtime')))
|
||||||
|
nqtext = entry.get('qtext', '') + '\n'
|
||||||
|
nqtext += "%s mtime is %s should be %s" % \
|
||||||
|
(entry.get('name'), mtime, entry.get('mtime'))
|
||||||
|
entry.set('qtext', nqtext)
|
||||||
|
if entry.get('type') != 'file':
|
||||||
|
nnqtext = entry.get('qtext')
|
||||||
|
nnqtext += '\nInstall %s %s: (y/N) ' % (entry.get('type'),
|
||||||
|
entry.get('name'))
|
||||||
|
entry.set('qtext', nnqtext)
|
||||||
|
return pTrue and pruneTrue
|
||||||
|
|
||||||
|
def Installdirectory(self, entry):
|
||||||
|
"""Install Path type='directory' entry."""
|
||||||
|
if entry.get('perms') == None or \
|
||||||
|
entry.get('owner') == None or \
|
||||||
|
entry.get('group') == None:
|
||||||
|
self.logger.error('Entry %s not completely specified. '
|
||||||
|
'Try running bcfg2-lint.' % \
|
||||||
|
(entry.get('name')))
|
||||||
|
return False
|
||||||
|
self.logger.info("Installing directory %s" % (entry.get('name')))
|
||||||
|
try:
|
||||||
|
fmode = os.lstat(entry.get('name'))
|
||||||
|
if not stat.S_ISDIR(fmode[stat.ST_MODE]):
|
||||||
|
self.logger.debug("Found a non-directory entry at %s" % \
|
||||||
|
(entry.get('name')))
|
||||||
|
try:
|
||||||
|
os.unlink(entry.get('name'))
|
||||||
|
exists = False
|
||||||
|
except OSError:
|
||||||
|
self.logger.info("Failed to unlink %s" % \
|
||||||
|
(entry.get('name')))
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
self.logger.debug("Found a pre-existing directory at %s" % \
|
||||||
|
(entry.get('name')))
|
||||||
|
exists = True
|
||||||
|
except OSError:
|
||||||
|
# stat failed
|
||||||
|
exists = False
|
||||||
|
|
||||||
|
if not exists:
|
||||||
|
parent = "/".join(entry.get('name').split('/')[:-1])
|
||||||
|
if parent:
|
||||||
|
try:
|
||||||
|
os.stat(parent)
|
||||||
|
except:
|
||||||
|
self.logger.debug('Creating parent path for directory %s' % (entry.get('name')))
|
||||||
|
for idx in range(len(parent.split('/')[:-1])):
|
||||||
|
current = '/'+'/'.join(parent.split('/')[1:2+idx])
|
||||||
|
try:
|
||||||
|
sloc = os.stat(current)
|
||||||
|
except OSError:
|
||||||
|
try:
|
||||||
|
os.mkdir(current)
|
||||||
|
continue
|
||||||
|
except OSError:
|
||||||
|
return False
|
||||||
|
if not stat.S_ISDIR(sloc[stat.ST_MODE]):
|
||||||
|
try:
|
||||||
|
os.unlink(current)
|
||||||
|
os.mkdir(current)
|
||||||
|
except OSError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
os.mkdir(entry.get('name'))
|
||||||
|
except OSError:
|
||||||
|
self.logger.error('Failed to create directory %s' % \
|
||||||
|
(entry.get('name')))
|
||||||
|
return False
|
||||||
|
if entry.get('prune', 'false') == 'true' and entry.get("qtest"):
|
||||||
|
for pent in entry.findall('Prune'):
|
||||||
|
pname = pent.get('path')
|
||||||
|
ulfailed = False
|
||||||
|
if os.path.isdir(pname):
|
||||||
|
self.logger.info("Not removing extra directory %s, "
|
||||||
|
"please check and remove manually" % pname)
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
self.logger.debug("Unlinking file %s" % pname)
|
||||||
|
os.unlink(pname)
|
||||||
|
except OSError:
|
||||||
|
self.logger.error("Failed to unlink path %s" % pname)
|
||||||
|
ulfailed = True
|
||||||
|
if ulfailed:
|
||||||
|
return False
|
||||||
|
return self.Installpermissions(entry)
|
||||||
|
|
||||||
|
def Verifyfile(self, entry, _):
|
||||||
|
"""Verify Python type='file' entry."""
|
||||||
|
# permissions check + content check
|
||||||
|
permissionStatus = self.Verifydirectory(entry, _)
|
||||||
|
tbin = False
|
||||||
|
if entry.text == None and entry.get('empty', 'false') == 'false':
|
||||||
|
self.logger.error("Cannot verify incomplete Python type='%s' %s" %
|
||||||
|
(entry.get('type'), entry.get('name')))
|
||||||
|
return False
|
||||||
|
if entry.get('encoding', 'ascii') == 'base64':
|
||||||
|
tempdata = binascii.a2b_base64(entry.text)
|
||||||
|
tbin = True
|
||||||
|
elif entry.get('empty', 'false') == 'true':
|
||||||
|
tempdata = ''
|
||||||
|
else:
|
||||||
|
tempdata = entry.text
|
||||||
|
if type(tempdata) == unicode:
|
||||||
|
try:
|
||||||
|
tempdata = tempdata.encode(self.setup['encoding'])
|
||||||
|
except UnicodeEncodeError:
|
||||||
|
e = sys.exc_info()[1]
|
||||||
|
self.logger.error("Error encoding file %s:\n %s" % \
|
||||||
|
(entry.get('name'), e))
|
||||||
|
|
||||||
|
different = False
|
||||||
|
content = None
|
||||||
|
if not os.path.exists(entry.get("name")):
|
||||||
|
# first, see if the target file exists at all; if not,
|
||||||
|
# they're clearly different
|
||||||
|
different = True
|
||||||
|
content = ""
|
||||||
|
else:
|
||||||
|
# next, see if the size of the target file is different
|
||||||
|
# from the size of the desired content
|
||||||
|
try:
|
||||||
|
estat = os.stat(entry.get('name'))
|
||||||
|
except OSError:
|
||||||
|
err = sys.exc_info()[1]
|
||||||
|
self.logger.error("Failed to stat %s: %s" %
|
||||||
|
(err.filename, err))
|
||||||
|
return False
|
||||||
|
if len(tempdata) != estat[stat.ST_SIZE]:
|
||||||
|
different = True
|
||||||
|
else:
|
||||||
|
# finally, read in the target file and compare them
|
||||||
|
# directly. comparison could be done with a checksum,
|
||||||
|
# which might be faster for big binary files, but
|
||||||
|
# slower for everything else
|
||||||
|
try:
|
||||||
|
content = open(entry.get('name')).read()
|
||||||
|
except IOError:
|
||||||
|
err = sys.exc_info()[1]
|
||||||
|
self.logger.error("Failed to read %s: %s" %
|
||||||
|
(err.filename, err))
|
||||||
|
return False
|
||||||
|
different = content != tempdata
|
||||||
|
|
||||||
|
if different:
|
||||||
|
if self.setup['interactive']:
|
||||||
|
prompt = [entry.get('qtext', '')]
|
||||||
|
if not tbin and content is None:
|
||||||
|
# it's possible that we figured out the files are
|
||||||
|
# different without reading in the local file. if
|
||||||
|
# the supplied version of the file is not binary,
|
||||||
|
# we now have to read in the local file to figure
|
||||||
|
# out if _it_ is binary, and either include that
|
||||||
|
# fact or the diff in our prompts for -I
|
||||||
|
try:
|
||||||
|
content = open(entry.get('name')).read()
|
||||||
|
except IOError:
|
||||||
|
err = sys.exc_info()[1]
|
||||||
|
self.logger.error("Failed to read %s: %s" %
|
||||||
|
(err.filename, err))
|
||||||
|
return False
|
||||||
|
if tbin or not isString(content, self.setup['encoding']):
|
||||||
|
# don't compute diffs if the file is binary
|
||||||
|
prompt.append('Binary file, no printable diff')
|
||||||
|
else:
|
||||||
|
diff = self._diff(content, tempdata,
|
||||||
|
difflib.unified_diff,
|
||||||
|
filename=entry.get("name"))
|
||||||
|
if diff:
|
||||||
|
udiff = '\n'.join(diff)
|
||||||
|
try:
|
||||||
|
prompt.append(udiff.decode(self.setup['encoding']))
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
prompt.append("Binary file, no printable diff")
|
||||||
|
else:
|
||||||
|
prompt.append("Diff took too long to compute, no "
|
||||||
|
"printable diff")
|
||||||
|
prompt.append("Install %s %s: (y/N): " % (entry.tag,
|
||||||
|
entry.get('name')))
|
||||||
|
entry.set("qtext", "\n".join(prompt))
|
||||||
|
|
||||||
|
if entry.get('sensitive', 'false').lower() != 'true':
|
||||||
|
if content is None:
|
||||||
|
# it's possible that we figured out the files are
|
||||||
|
# different without reading in the local file. we
|
||||||
|
# now have to read in the local file to figure out
|
||||||
|
# if _it_ is binary, and either include the whole
|
||||||
|
# file or the diff for reports
|
||||||
|
try:
|
||||||
|
content = open(entry.get('name')).read()
|
||||||
|
except IOError:
|
||||||
|
err = sys.exc_info()[1]
|
||||||
|
self.logger.error("Failed to read %s: %s" %
|
||||||
|
(err.filename, err))
|
||||||
|
return False
|
||||||
|
|
||||||
|
if tbin or not isString(content, self.setup['encoding']):
|
||||||
|
# don't compute diffs if the file is binary
|
||||||
|
entry.set('current_bfile', binascii.b2a_base64(content))
|
||||||
|
else:
|
||||||
|
diff = self._diff(content, tempdata, difflib.ndiff,
|
||||||
|
filename=entry.get("name"))
|
||||||
|
if diff:
|
||||||
|
entry.set("current_bdiff",
|
||||||
|
binascii.b2a_base64("\n".join(diff)))
|
||||||
|
elif not tbin and isString(content, self.setup['encoding']):
|
||||||
|
entry.set('current_bfile', binascii.b2a_base64(content))
|
||||||
|
elif permissionStatus == False and self.setup['interactive']:
|
||||||
|
prompt = [entry.get('qtext', '')]
|
||||||
|
prompt.append("Install %s %s: (y/N): " % (entry.tag,
|
||||||
|
entry.get('name')))
|
||||||
|
entry.set("qtext", "\n".join(prompt))
|
||||||
|
|
||||||
|
|
||||||
|
return permissionStatus and not different
|
||||||
|
|
||||||
|
def Installfile(self, entry):
|
||||||
|
"""Install Python type='file' entry."""
|
||||||
|
self.logger.info("Installing file %s" % (entry.get('name')))
|
||||||
|
|
||||||
|
parent = "/".join(entry.get('name').split('/')[:-1])
|
||||||
|
if parent:
|
||||||
|
try:
|
||||||
|
os.stat(parent)
|
||||||
|
except:
|
||||||
|
self.logger.debug('Creating parent path for config file %s' % \
|
||||||
|
(entry.get('name')))
|
||||||
|
current = '/'
|
||||||
|
for next in parent.split('/')[1:]:
|
||||||
|
current += next + '/'
|
||||||
|
try:
|
||||||
|
sloc = os.stat(current)
|
||||||
|
try:
|
||||||
|
if not stat.S_ISDIR(sloc[stat.ST_MODE]):
|
||||||
|
self.logger.debug('%s is not a directory; recreating' \
|
||||||
|
% (current))
|
||||||
|
os.unlink(current)
|
||||||
|
os.mkdir(current)
|
||||||
|
except OSError:
|
||||||
|
return False
|
||||||
|
except OSError:
|
||||||
|
try:
|
||||||
|
self.logger.debug("Creating non-existent path %s" % current)
|
||||||
|
os.mkdir(current)
|
||||||
|
except OSError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# If we get here, then the parent directory should exist
|
||||||
|
if (entry.get("paranoid", False) in ['true', 'True']) and \
|
||||||
|
self.setup.get("paranoid", False) and not \
|
||||||
|
(entry.get('current_exists', 'true') == 'false'):
|
||||||
|
bkupnam = entry.get('name').replace('/', '_')
|
||||||
|
# current list of backups for this file
|
||||||
|
try:
|
||||||
|
bkuplist = [f for f in os.listdir(self.ppath) if
|
||||||
|
f.startswith(bkupnam)]
|
||||||
|
except OSError:
|
||||||
|
e = sys.exc_info()[1]
|
||||||
|
self.logger.error("Failed to create backup list in %s: %s" %
|
||||||
|
(self.ppath, e.strerror))
|
||||||
|
return False
|
||||||
|
bkuplist.sort()
|
||||||
|
while len(bkuplist) >= int(self.max_copies):
|
||||||
|
# remove the oldest backup available
|
||||||
|
oldest = bkuplist.pop(0)
|
||||||
|
self.logger.info("Removing %s" % oldest)
|
||||||
|
try:
|
||||||
|
os.remove("%s/%s" % (self.ppath, oldest))
|
||||||
|
except:
|
||||||
|
self.logger.error("Failed to remove %s/%s" % \
|
||||||
|
(self.ppath, oldest))
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
# backup existing file
|
||||||
|
shutil.copy(entry.get('name'),
|
||||||
|
"%s/%s_%s" % (self.ppath, bkupnam,
|
||||||
|
datetime.isoformat(datetime.now())))
|
||||||
|
self.logger.info("Backup of %s saved to %s" %
|
||||||
|
(entry.get('name'), self.ppath))
|
||||||
|
except IOError:
|
||||||
|
e = sys.exc_info()[1]
|
||||||
|
self.logger.error("Failed to create backup file for %s" % \
|
||||||
|
(entry.get('name')))
|
||||||
|
self.logger.error(e)
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
newfile = open("%s.new"%(entry.get('name')), 'w')
|
||||||
|
if entry.get('encoding', 'ascii') == 'base64':
|
||||||
|
filedata = binascii.a2b_base64(entry.text)
|
||||||
|
elif entry.get('empty', 'false') == 'true':
|
||||||
|
filedata = ''
|
||||||
|
else:
|
||||||
|
if type(entry.text) == unicode:
|
||||||
|
filedata = entry.text.encode(self.setup['encoding'])
|
||||||
|
else:
|
||||||
|
filedata = entry.text
|
||||||
|
newfile.write(filedata)
|
||||||
|
newfile.close()
|
||||||
|
try:
|
||||||
|
os.chown(newfile.name, normUid(entry), normGid(entry))
|
||||||
|
except KeyError:
|
||||||
|
self.logger.error("Failed to chown %s to %s:%s" %
|
||||||
|
(newfile.name, entry.get('owner'),
|
||||||
|
entry.get('group')))
|
||||||
|
os.chown(newfile.name, 0, 0)
|
||||||
|
except OSError:
|
||||||
|
err = sys.exc_info()[1]
|
||||||
|
self.logger.error("Could not chown %s: %s" % (newfile.name,
|
||||||
|
err))
|
||||||
|
os.chmod(newfile.name, calcPerms(stat.S_IFREG, entry.get('perms')))
|
||||||
|
os.rename(newfile.name, entry.get('name'))
|
||||||
|
if entry.get('mtime', '-1') != '-1':
|
||||||
|
try:
|
||||||
|
os.utime(entry.get('name'), (int(entry.get('mtime')),
|
||||||
|
int(entry.get('mtime'))))
|
||||||
|
except:
|
||||||
|
self.logger.error("File %s mtime fix failed" \
|
||||||
|
% (entry.get('name')))
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
except (OSError, IOError):
|
||||||
|
err = sys.exc_info()[1]
|
||||||
|
if err.errno == errno.EACCES:
|
||||||
|
self.logger.info("Failed to open %s for writing" % (entry.get('name')))
|
||||||
|
else:
|
||||||
|
print(err)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def Verifypermissions(self, entry, _):
|
||||||
|
"""Verify Path type='permissions' entry"""
|
||||||
|
if entry.get('perms') == None or \
|
||||||
|
entry.get('owner') == None or \
|
||||||
|
entry.get('group') == None:
|
||||||
|
self.logger.error('Entry %s not completely specified. '
|
||||||
|
'Try running bcfg2-lint.' % (entry.get('name')))
|
||||||
|
return False
|
||||||
|
if entry.get('recursive') in ['True', 'true']:
|
||||||
|
# verify ownership information recursively
|
||||||
|
owner = normUid(entry)
|
||||||
|
group = normGid(entry)
|
||||||
|
|
||||||
|
for root, dirs, files in os.walk(entry.get('name')):
|
||||||
|
for p in dirs + files:
|
||||||
|
path = os.path.join(root, p)
|
||||||
|
pstat = os.stat(path)
|
||||||
|
if owner != pstat.st_uid:
|
||||||
|
# owner mismatch for path
|
||||||
|
entry.set('current_owner', str(pstat.st_uid))
|
||||||
|
self.logger.debug("%s %s ownership wrong" % \
|
||||||
|
(entry.tag, path))
|
||||||
|
nqtext = entry.get('qtext', '') + '\n'
|
||||||
|
nqtext += ("Owner for path %s is incorrect. "
|
||||||
|
"Current owner is %s but should be %s\n" % \
|
||||||
|
(path, pstat.st_uid, entry.get('owner')))
|
||||||
|
nqtext += ("\nInstall %s %s: (y/N): " %
|
||||||
|
(entry.tag, entry.get('name')))
|
||||||
|
entry.set('qtext', nqtext)
|
||||||
|
return False
|
||||||
|
if group != pstat.st_gid:
|
||||||
|
# group mismatch for path
|
||||||
|
entry.set('current_group', str(pstat.st_gid))
|
||||||
|
self.logger.debug("%s %s group wrong" % \
|
||||||
|
(entry.tag, path))
|
||||||
|
nqtext = entry.get('qtext', '') + '\n'
|
||||||
|
nqtext += ("Group for path %s is incorrect. "
|
||||||
|
"Current group is %s but should be %s\n" % \
|
||||||
|
(path, pstat.st_gid, entry.get('group')))
|
||||||
|
nqtext += ("\nInstall %s %s: (y/N): " %
|
||||||
|
(entry.tag, entry.get('name')))
|
||||||
|
entry.set('qtext', nqtext)
|
||||||
|
return False
|
||||||
|
return self.Verifydirectory(entry, _)
|
||||||
|
|
||||||
|
def _diff(self, content1, content2, difffunc, filename=None):
|
||||||
|
rv = []
|
||||||
|
start = time.time()
|
||||||
|
longtime = False
|
||||||
|
for diffline in difffunc(content1.split('\n'),
|
||||||
|
content2.split('\n')):
|
||||||
|
now = time.time()
|
||||||
|
rv.append(diffline)
|
||||||
|
if now - start > 5 and not longtime:
|
||||||
|
if filename:
|
||||||
|
self.logger.info("Diff of %s taking a long time" %
|
||||||
|
filename)
|
||||||
|
else:
|
||||||
|
self.logger.info("Diff taking a long time")
|
||||||
|
longtime = True
|
||||||
|
elif now - start > 30:
|
||||||
|
if filename:
|
||||||
|
self.logger.error("Diff of %s took too long; giving up" %
|
||||||
|
filename)
|
||||||
|
else:
|
||||||
|
self.logger.error("Diff took too long; giving up")
|
||||||
|
return False
|
||||||
|
return rv
|
||||||
|
|
||||||
|
def Installpermissions(self, entry):
|
||||||
|
"""Install POSIX permissions"""
|
||||||
|
if entry.get('perms') == None or \
|
||||||
|
entry.get('owner') == None or \
|
||||||
|
entry.get('group') == None:
|
||||||
|
self.logger.error('Entry %s not completely specified. '
|
||||||
|
'Try running bcfg2-lint.' % (entry.get('name')))
|
||||||
|
return False
|
||||||
|
plist = [entry.get('name')]
|
||||||
|
if entry.get('recursive') in ['True', 'true']:
|
||||||
|
# verify ownership information recursively
|
||||||
|
owner = normUid(entry)
|
||||||
|
group = normGid(entry)
|
||||||
|
|
||||||
|
for root, dirs, files in os.walk(entry.get('name')):
|
||||||
|
for p in dirs + files:
|
||||||
|
path = os.path.join(root, p)
|
||||||
|
pstat = os.stat(path)
|
||||||
|
if owner != pstat.st_uid or group != pstat.st_gid:
|
||||||
|
# owner mismatch for path
|
||||||
|
plist.append(path)
|
||||||
|
try:
|
||||||
|
for p in plist:
|
||||||
|
os.chown(p, normUid(entry), normGid(entry))
|
||||||
|
os.chmod(p, calcPerms(stat.S_IFDIR, entry.get('perms')))
|
||||||
|
return True
|
||||||
|
except (OSError, KeyError):
|
||||||
|
self.logger.error('Permission fixup failed for %s' % \
|
||||||
|
(entry.get('name')))
|
||||||
|
return False
|
||||||
|
|
||||||
|
def InstallNone(self, entry):
|
||||||
|
return self.Installfile(entry)
|
||||||
|
|
||||||
|
def VerifyNone(self, entry, _):
|
||||||
|
return self.Verifyfile(entry, _)
|
||||||
|
|
||||||
|
def InstallPython(self, entry):
|
||||||
|
"""Dispatch install to the proper method according to type"""
|
||||||
|
ret = getattr(self, 'Install%s' % entry.get('type'))
|
||||||
|
return ret(entry)
|
||||||
|
|
||||||
|
def VerifyPython(self, entry, _):
|
||||||
|
"""Dispatch verify to the proper method according to type"""
|
||||||
|
ret = getattr(self, 'Verify%s' % entry.get('type'))
|
||||||
|
return ret(entry, _)
|
|
@ -105,7 +105,7 @@ class Python(Bcfg2.Server.Plugin.Plugin,Bcfg2.Server.Plugin.Generator):
|
||||||
def __init__(self, core, datastore):
|
def __init__(self, core, datastore):
|
||||||
Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore)
|
Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore)
|
||||||
# Les entrées pour bcfg2
|
# Les entrées pour bcfg2
|
||||||
self.Entries['ConfigFile'] = {}
|
self.Entries['Python'] = {}
|
||||||
# Correspondance entrée ConfigFile -> code
|
# Correspondance entrée ConfigFile -> code
|
||||||
self.codes = {}
|
self.codes = {}
|
||||||
# Dico ID de requête GAM -> Dossier surveillé
|
# Dico ID de requête GAM -> Dossier surveillé
|
||||||
|
@ -165,6 +165,7 @@ class Python(Bcfg2.Server.Plugin.Plugin,Bcfg2.Server.Plugin.Generator):
|
||||||
if event.filename[0] == '/' \
|
if event.filename[0] == '/' \
|
||||||
or event.filename.endswith(".COMPILED") \
|
or event.filename.endswith(".COMPILED") \
|
||||||
or event.filename.endswith("~") \
|
or event.filename.endswith("~") \
|
||||||
|
or event.filename.endswith(".swp") \
|
||||||
or event.filename.startswith(".#"):
|
or event.filename.startswith(".#"):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -195,13 +196,14 @@ class Python(Bcfg2.Server.Plugin.Plugin,Bcfg2.Server.Plugin.Generator):
|
||||||
if action in ['exists', 'created']:
|
if action in ['exists', 'created']:
|
||||||
debug("adding config file: %s" % identifier, 'green')
|
debug("adding config file: %s" % identifier, 'green')
|
||||||
self.codes[identifier] = load_file(path, logger)
|
self.codes[identifier] = load_file(path, logger)
|
||||||
self.Entries['ConfigFile'][identifier] = self.BuildEntry
|
print "Python plugin : creating %s entry due to action %s" % (identifier, action)
|
||||||
|
self.Entries['Python'][identifier] = self.BuildEntry
|
||||||
elif action == 'changed':
|
elif action == 'changed':
|
||||||
self.codes[identifier] = load_file(path, logger)
|
self.codes[identifier] = load_file(path, logger)
|
||||||
elif action == 'deleted':
|
elif action == 'deleted':
|
||||||
debug("deleting config file: %s" % identifier, 'red')
|
debug("deleting config file: %s" % identifier, 'red')
|
||||||
del self.codes[identifier]
|
del self.codes[identifier]
|
||||||
del self.Entries['ConfigFile'][identifier]
|
del self.Entries['Python'][identifier]
|
||||||
|
|
||||||
elif posixpath.isdir(path):
|
elif posixpath.isdir(path):
|
||||||
if action in ['exists', 'created']:
|
if action in ['exists', 'created']:
|
||||||
|
|
|
@ -145,7 +145,12 @@ def compileSource(source, filename="", logger = None):
|
||||||
newsource.write("))\n")
|
newsource.write("))\n")
|
||||||
newsource.write(source[start:])
|
newsource.write(source[start:])
|
||||||
if logger:
|
if logger:
|
||||||
logger.info(newsource.getvalue())
|
try:
|
||||||
|
logger.info(newsource.getvalue())
|
||||||
|
except:
|
||||||
|
print "Le logger de BCFG2 c'est de la merde, il refuse le non ascii."
|
||||||
|
print "Voici ce que j'ai essayé de logguer."
|
||||||
|
print newsource.getvalue()
|
||||||
return compile(newsource.getvalue(), filename, "exec")
|
return compile(newsource.getvalue(), filename, "exec")
|
||||||
|
|
||||||
def generate(code, environment=None, logger = None):
|
def generate(code, environment=None, logger = None):
|
||||||
|
@ -162,8 +167,9 @@ def generate(code, environment=None, logger = None):
|
||||||
except Done, _:
|
except Done, _:
|
||||||
pass
|
pass
|
||||||
except Exception, exn:
|
except Exception, exn:
|
||||||
|
print code
|
||||||
sys.stdout = save_stdout
|
sys.stdout = save_stdout
|
||||||
raise exn
|
raise
|
||||||
sys.stdout = save_stdout
|
sys.stdout = save_stdout
|
||||||
return environment.stream.getvalue()
|
return environment.stream.getvalue()
|
||||||
|
|
||||||
|
|
100
surveillance/mac_prises/mac_prise.py
Executable file
100
surveillance/mac_prises/mac_prise.py
Executable file
|
@ -0,0 +1,100 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf8 -*-
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# PEB - 01/04/2012
|
||||||
|
#
|
||||||
|
|
||||||
|
import os, sys, re
|
||||||
|
from commands import getstatusoutput
|
||||||
|
|
||||||
|
sys.path.append('/usr/scripts/gestion')
|
||||||
|
import annuaires_pg
|
||||||
|
|
||||||
|
# nécessite apparemment que l'objet conn soit bien créé lors de l'exec
|
||||||
|
# de annuaires_pg, il faut être root (ou dans je ne sais quel groupe)
|
||||||
|
# pour que l'authentification de l'user crans avec psycopg2 se fasse
|
||||||
|
# (plante lamentablement quand j'essaye avec mon compte sur vo, sous
|
||||||
|
# ipython. Mais si je sudo ipython, ça marche...
|
||||||
|
|
||||||
|
def is_really_crans(chbre):
|
||||||
|
|
||||||
|
def liste_prises_macs(switch):
|
||||||
|
u'''
|
||||||
|
Fonction générant un dictionnaire (macs) contenant pour chaque prise une
|
||||||
|
liste des macs qui y sont actives.
|
||||||
|
'''
|
||||||
|
liste_bats = ['a', 'b', 'c', 'g', 'h', 'i', 'j', 'm', 'p']
|
||||||
|
|
||||||
|
split = switch.replace('.adm.crans.org', '').split('-')
|
||||||
|
bat, num_switch = split[0][-1], int(split[1][0])
|
||||||
|
if bat not in liste_bats:
|
||||||
|
print 'Le bâtiment '+bat+' ne figure pas dans la liste des bâtiments.'
|
||||||
|
data = walk(switch, 'STATISTICS-MIB::hpSwitchPortFdbAddress')
|
||||||
|
|
||||||
|
liste_chbres = []
|
||||||
|
macs = {}
|
||||||
|
for i in data:
|
||||||
|
if i == '':
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
port = int(i.replace('STATISTICS-MIB::hpSwitchPortFdbAddress.', '').split('.')[0])
|
||||||
|
mac = data[i].replace(' ', '').lower().replace('"', '')
|
||||||
|
if not re.match('([0-9a-f]{2}){6}', mac):
|
||||||
|
mac = mac.encode('hex').lower()
|
||||||
|
mac = "%s:%s:%s:%s:%s:%s" % (mac[0:2], mac[2:4], mac[4:6], mac[6:8], mac[8:10], mac[10:12])
|
||||||
|
uplink = annuaires_pg.uplink_prises[bat]
|
||||||
|
prise = num_switch*100+port
|
||||||
|
if prise in uplink:
|
||||||
|
continue
|
||||||
|
|
||||||
|
chbre = chbre_prises(bat, prise)
|
||||||
|
|
||||||
|
if chbre in liste_chbres:
|
||||||
|
macs[chbre].append(mac+'\n')
|
||||||
|
else:
|
||||||
|
macs[chbre] = []
|
||||||
|
macs[chbre].append(mac+'\n')
|
||||||
|
liste_chbres.append(chbre)
|
||||||
|
return macs
|
||||||
|
|
||||||
|
def walk(host, oid):
|
||||||
|
u'''
|
||||||
|
Hack sale remplaçant la fonction walk contenue dans hptools, qui
|
||||||
|
splitte suivant des espaces, ce qui engendre des failles.
|
||||||
|
Ici, snmpwalk -Ox (au lieu de -Oq) fait qu'on récupère bien des macs,
|
||||||
|
et on splitte suivant le keyword Hex-STRING, qui n'est pas redondant, lui.
|
||||||
|
'''
|
||||||
|
received = __exec('snmpwalk -Ox -v 1 -c public %s %s' % (host, oid)).split('\n')
|
||||||
|
result = {}
|
||||||
|
for ligne in received:
|
||||||
|
pport, pmac = ligne.split('Hex-STRING: ')
|
||||||
|
result[pport] = pmac
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def __exec(cmd):
|
||||||
|
u'''
|
||||||
|
Hack sale pour pas loader inutilement hptools.py
|
||||||
|
Exécute une commande et retourne son status et son output.
|
||||||
|
'''
|
||||||
|
status, response = getstatusoutput(cmd)
|
||||||
|
if status:
|
||||||
|
response = response.replace('snmpget: ','')
|
||||||
|
print 'Erreur : '+response+' : '+cmd
|
||||||
|
return response
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
switchs = sys.argv[1:]
|
||||||
|
for switch in switchs:
|
||||||
|
macs = liste_prises_macs(switch)
|
||||||
|
|
||||||
|
split = switch.replace('.adm.crans.org', '').split('-')
|
||||||
|
bat, num_switch = split[0][-1], int(split[1][0])
|
||||||
|
|
||||||
|
if not os.path.isdir("bat%s/%d"%(bat, num_switch)):
|
||||||
|
os.makedirs("bat%s/%d"%(bat, num_switch))
|
||||||
|
|
||||||
|
for chbre in macs:
|
||||||
|
with open('bat%s/%d/%s%03d.macs'%(bat, num_switch, bat, prise), 'w') as f:
|
||||||
|
f.writelines(sorted(macs[prise]))
|
54
surveillance/mac_prises/mac_prise_wrapper.sh
Executable file
54
surveillance/mac_prises/mac_prise_wrapper.sh
Executable file
|
@ -0,0 +1,54 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
GIT_DIR=/usr/scripts/surveillance/mac_prises/output/
|
||||||
|
SCRIPT=/usr/scripts/surveillance/mac_prises/mac_prise.py
|
||||||
|
|
||||||
|
# Nombre de changements de mac sur une prise avant mail
|
||||||
|
WARNING=2
|
||||||
|
|
||||||
|
MAILTO=nobody@crans.org
|
||||||
|
|
||||||
|
if ! [ -d $GIT_DIR ]; then
|
||||||
|
echo -n "Création du répertoire \`$GIT_DIR'..."
|
||||||
|
mkdir $GIT_DIR
|
||||||
|
echo " Fait."
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd $GIT_DIR
|
||||||
|
|
||||||
|
if ! [ -d $GIT_DIR/.git ]; then
|
||||||
|
git init
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Récupération de la liste des switchs
|
||||||
|
SWITCHS=$(/usr/bin/host -l adm.crans.org | /usr/bin/awk '/^bat[abcghijpm]-/{print $1}')
|
||||||
|
|
||||||
|
# Nettoyage du contenu du répertoire, avec ignore-unmatch pour éviter le plantage en
|
||||||
|
# cas de répertoire vide.
|
||||||
|
/usr/bin/git rm -r -q --ignore-unmatch ./*
|
||||||
|
|
||||||
|
# Lancement du listage des macs en parallèle
|
||||||
|
/usr/bin/parallel -j 1000 python $SCRIPT -- $SWITCHS
|
||||||
|
|
||||||
|
# Ajout de tous les fichiers (à faire avant le diff, pour que les nouveaux fichiers soient pris en compte)
|
||||||
|
/usr/bin/git add *
|
||||||
|
|
||||||
|
# Récupération de statistiques
|
||||||
|
# numstat renvoie le nombre de lignes ajoutées, le nombre de lignes supprimées et le nom du fichier
|
||||||
|
# on ajoute les deux premières variables et on classe par nombre de modifs
|
||||||
|
/usr/bin/git diff --cached --numstat | /usr/bin/awk '{print $1+$2 " " $3}' | sort -rn | ( while read num file; do
|
||||||
|
if [ $num -ge $WARNING ]; then
|
||||||
|
echo $file
|
||||||
|
else
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done ) | xargs /usr/bin/git diff --cached | mail -a 'From: "Eye in the sky" <root@crans.org>' -s "Surveillance macs/prises" $MAILTO
|
||||||
|
|
||||||
|
/usr/bin/git commit -m "Updated mac list" > /dev/null
|
||||||
|
|
||||||
|
# Garbage collection toutes les 10 minutes
|
||||||
|
if [ $(expr $(date +%M) % 10) -eq 0 ]; then
|
||||||
|
/usr/bin/git gc --aggressive -q
|
||||||
|
fi
|
Loading…
Add table
Add a link
Reference in a new issue