1101 lines
47 KiB
Diff
1101 lines
47 KiB
Diff
|
Submitted By: Mario Fetka (geos_one) (mario dot fetka at gmail dot com)
|
||
|
Date: 2010-02-05
|
||
|
Initial Package Version: 2.3.2
|
||
|
Origin: https://ml.mandriva.net/wws/arc/mds-devel/2010-02/msg00004.html
|
||
|
Upstream Status: unknown
|
||
|
Description: add userquota plugin and add support for large groups quota change (ldap timeout)
|
||
|
|
||
|
diff -Naur mmc-agent-2.3.2.orig/conf/plugins/userquota.ini mmc-agent-2.3.2/conf/plugins/userquota.ini
|
||
|
--- mmc-agent-2.3.2.orig/conf/plugins/userquota.ini 1970-01-01 00:00:00.000000000 +0000
|
||
|
+++ mmc-agent-2.3.2/conf/plugins/userquota.ini 2010-01-15 06:19:24.000000000 +0000
|
||
|
@@ -0,0 +1,19 @@
|
||
|
+[main]
|
||
|
+disable = 0
|
||
|
+
|
||
|
+[diskquota]
|
||
|
+enable = 1
|
||
|
+# block size found using dumpe2fs -h /dev/vda1 | awk '/Block size:/ { print $3 }'
|
||
|
+
|
||
|
+# devicemap format: device:blocksize:displayname, ...
|
||
|
+devicemap = /dev/vda1:4096:Test Root,/dev/mapper/home:4096:Home dir
|
||
|
+softquotablocks = 0.95
|
||
|
+softquotainodes = 0.95
|
||
|
+inodesperblock = 1.60
|
||
|
+
|
||
|
+setquotascript = echo /usr/sbin/setquota $uid $softblocks $blocks $softinodes $inodes $devicepath
|
||
|
+delquotascript = echo /usr/sbin/setquota $uid 0 0 0 0 $devicepath
|
||
|
+
|
||
|
+[networkquota]
|
||
|
+enable = 1
|
||
|
+networkmap = Internet:0.0.0.0/0:any
|
||
|
diff -Naur mmc-agent-2.3.2.orig/contrib/ldap/quota.schema mmc-agent-2.3.2/contrib/ldap/quota.schema
|
||
|
--- mmc-agent-2.3.2.orig/contrib/ldap/quota.schema 1970-01-01 00:00:00.000000000 +0000
|
||
|
+++ mmc-agent-2.3.2/contrib/ldap/quota.schema 2010-01-25 02:16:23.000000000 +0000
|
||
|
@@ -0,0 +1,30 @@
|
||
|
+##
|
||
|
+## schema file for Unix Quotas
|
||
|
+## Schema for storing Unix Quotas in LDAP
|
||
|
+## OIDs are owned by Cogent Innovators, LLC
|
||
|
+##
|
||
|
+## 1.3.6.1.4.1.19937.1.1.x - attributetypes
|
||
|
+## 1.3.6.1.4.1.19937.1.2.x - objectclasses
|
||
|
+##
|
||
|
+
|
||
|
+attributetype ( 1.3.6.1.4.1.19937.1.1.1 NAME 'quota'
|
||
|
+ DESC 'Quotas (FileSystem:BlocksSoft,BlocksHard,InodesSoft,InodesHard)'
|
||
|
+ EQUALITY caseIgnoreIA5Match
|
||
|
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{255} )
|
||
|
+
|
||
|
+attributetype ( 1.3.6.1.4.1.19937.1.1.2 NAME 'networkquota'
|
||
|
+ DESC 'Network Quotas (network,protocol,bytes)'
|
||
|
+ EQUALITY caseIgnoreIA5Match
|
||
|
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{255} )
|
||
|
+
|
||
|
+objectclass ( 1.3.6.1.4.1.19937.1.2.1 NAME 'systemQuotas' SUP posixAccount AUXILIARY
|
||
|
+ DESC 'System Quotas'
|
||
|
+ MUST ( uid )
|
||
|
+ MAY ( quota $ networkquota ))
|
||
|
+
|
||
|
+objectclass ( 1.3.6.1.4.1.19937.1.2.2 NAME 'defaultQuotas'
|
||
|
+ DESC 'Quota defaults to apply to members of a group'
|
||
|
+ SUP top AUXILIARY
|
||
|
+ MUST ( cn )
|
||
|
+ MAY ( quota $ networkquota ))
|
||
|
+
|
||
|
diff -Naur mmc-agent-2.3.2.orig/contrib/userquota/README mmc-agent-2.3.2/contrib/userquota/README
|
||
|
--- mmc-agent-2.3.2.orig/contrib/userquota/README 1970-01-01 00:00:00.000000000 +0000
|
||
|
+++ mmc-agent-2.3.2/contrib/userquota/README 2010-02-04 01:42:11.000000000 +0000
|
||
|
@@ -0,0 +1,71 @@
|
||
|
+# (c) 2009 OSS - Glen Ogilvie
|
||
|
+# License: GPLv2 or above
|
||
|
+# Version: 0.0.3
|
||
|
+
|
||
|
+Description: Mandriva Directory Server plugin to store disk and network quotas in OpenLDAP and manage them
|
||
|
+using MDS. It provides multiple quotas for both, mapped from the configuration file. It also supports turning
|
||
|
+off either disk or network quotas if you only wish to quota one of them.
|
||
|
+
|
||
|
+This plugin also supports setting quotas for all the members of a group. This can be done by browsing to the
|
||
|
+group list in MDS and using the edit group action. It also allows you to set quotas per individual, although these
|
||
|
+are reset when you perform a group action.
|
||
|
+
|
||
|
+When quotas are set for a member of the group, the object classes of that member will be updated to include the systemQuotas schema.
|
||
|
+The group will also have an additional object class added to store the defaults for that group.
|
||
|
+
|
||
|
+New members of the group do not currently get the group default quotas assigned but may do in the future.
|
||
|
+
|
||
|
+
|
||
|
+Installation:
|
||
|
+cp ./etc/mmc/plugins/userquota.ini /etc/mmc/plugins/
|
||
|
+cp -r ./mmc-python/plugins/userquota /usr/lib64/python2.6/site-packages/mmc/plugins/
|
||
|
+cp -r ./mmc-web/modules/userquota /usr/share/mmc/modules/
|
||
|
+cp ./etc/openldap/schema/quota.schema /etc/openldap/schema
|
||
|
+
|
||
|
+Edit: /etc/openldap/schema/local.schema
|
||
|
+insert line: include /etc/openldap/schema/quota.schema
|
||
|
+restart slapd.
|
||
|
+
|
||
|
+Patches:
|
||
|
+These patches are against python-ldap-2.3.9-1mdv2010.0 and mmc-agent-2.3.2-8.2mdv2010.0)
|
||
|
+
|
||
|
+To resolve an ldap connection issue I had, the following files needed to be patched to use the ReconnectLDAPObject.
|
||
|
+
|
||
|
+/usr/lib64/python2.6/site-packages/mmc/plugins/base/__init__.py (use ReconnectLDAPObject and change ".modify_s(" to ".modify_ext_s("
|
||
|
+/usr/lib64/python2.6/site-packages/ldap/ldapobject.py (add pass exception after self.reconnect(self._uri))
|
||
|
+
|
||
|
+These changes are included in:
|
||
|
+patches/mmc-python-base_userquota_ldap.patch
|
||
|
+patches/ldapobject.patch
|
||
|
+
|
||
|
+cd /usr/lib64/python2.6
|
||
|
+patch -p0 < ~/userquota/patches/ldapobject.patch
|
||
|
+patch -p0 < ~/userquota/patches/patches/mmc-python-base_userquota_ldap.patch
|
||
|
+
|
||
|
+For both of these, I have sent an email to the mmc-devel mailing list to discuss options, as would rather not have to patch these files.
|
||
|
+
|
||
|
+
|
||
|
+Configuration:
|
||
|
+Edit: /etc/mmc/plugins/userquota.ini
|
||
|
+
|
||
|
+Configuration options: diskquota
|
||
|
+devicemap
|
||
|
+ - This maps your physical device that will have the quota applied to a nice human readable name and also includes the block size of the disk
|
||
|
+ used for calculation of megabytes.
|
||
|
+
|
||
|
+softquotablocks, softquotainodes, inodesperblock
|
||
|
+ - multipliers for the values passed to the quota command.
|
||
|
+
|
||
|
+setquotascript, delquotascript
|
||
|
+ - system command to run.. Note that in example, these are echo statements so they do nothing
|
||
|
+
|
||
|
+Configuration options: networkquota
|
||
|
+networkmap
|
||
|
+ - this maps the name of a network to a subnet and protocol. How the subnet and protocol are processed is up to whatever is reading the network
|
||
|
+ - quota values from each user. In my case, a custom script will read these and use them with TC for rate limiting.
|
||
|
+
|
||
|
+Documentation:
|
||
|
+See: mmc-web/modules/userquota/help/doc.txt
|
||
|
+
|
||
|
+Bugs:
|
||
|
+The connection the LDAP server may get disconnected sometimes. The code works around this by using the reconnection ldap class for python.
|
||
|
diff -Naur mmc-agent-2.3.2.orig/contrib/userquota/networkquota_example/applyTCrules.py mmc-agent-2.3.2/contrib/userquota/networkquota_example/applyTCrules.py
|
||
|
--- mmc-agent-2.3.2.orig/contrib/userquota/networkquota_example/applyTCrules.py 1970-01-01 00:00:00.000000000 +0000
|
||
|
+++ mmc-agent-2.3.2/contrib/userquota/networkquota_example/applyTCrules.py 2010-02-04 01:57:16.000000000 +0000
|
||
|
@@ -0,0 +1,156 @@
|
||
|
+#!/usr/bin/python
|
||
|
+# -*- coding: utf-8; -*-
|
||
|
+# Example to apply network quotas from ldap to tc traffic shaping rules.
|
||
|
+#
|
||
|
+# (c) 2009 Open Systems Specilists - Glen Ogilvie
|
||
|
+#
|
||
|
+# This program is free software: you can redistribute it and/or modify
|
||
|
+# it under the terms of the GNU General Public License as published by
|
||
|
+# the Free Software Foundation, either version 2 of the License, or
|
||
|
+# (at your option) any later version.
|
||
|
+#
|
||
|
+# This program is distributed in the hope that it will be useful,
|
||
|
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
+# GNU General Public License for more details.
|
||
|
+#
|
||
|
+# You should have received a copy of the GNU General Public License
|
||
|
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
+
|
||
|
+
|
||
|
+# EXAMPLE ONLY: You will need to modify this to suit your network.
|
||
|
+#
|
||
|
+import ldap
|
||
|
+import subprocess
|
||
|
+import re
|
||
|
+import warnings
|
||
|
+
|
||
|
+# hide DepercationWarning when importing MySQLdb
|
||
|
+with warnings.catch_warnings():
|
||
|
+ warnings.simplefilter("ignore", DeprecationWarning)
|
||
|
+ import MySQLdb
|
||
|
+
|
||
|
+uri = "ldap://localhost:389"
|
||
|
+base = "ou=People,dc=example,dc=com"
|
||
|
+scope = ldap.SCOPE_SUBTREE
|
||
|
+filterstr = "(objectClass=systemQuotas)"
|
||
|
+attrlist = ['uid', 'networkquota', 'uidNumber']
|
||
|
+query = """select sum(bytes_in + bytes_out) as bytes, username from
|
||
|
+ulog where username is not null and bytes_in is not null and bytes_out is not null
|
||
|
+group by username;"""
|
||
|
+
|
||
|
+# Test command to get tc filters.
|
||
|
+tcfilterlist = "cat tcexample.txt"
|
||
|
+tcapplycmd = "./tcruleadd.sh %s %x" # args: uid, uid in hex
|
||
|
+tcremovecmd = "./tcruledel.sh %s %x"
|
||
|
+
|
||
|
+
|
||
|
+
|
||
|
+def getBytes(username, network):
|
||
|
+ user = False
|
||
|
+ for dn, record in res:
|
||
|
+ if "networkquota" in record:
|
||
|
+# print record["uid"][0]
|
||
|
+ if record["uid"][0] == username:
|
||
|
+ user = True
|
||
|
+ for q in record["networkquota"]:
|
||
|
+ if q.split(',')[0] == network:
|
||
|
+ return q.split(',')[1]
|
||
|
+
|
||
|
+ if not user:
|
||
|
+ raise NameError("username: %s not in ldap" % username)
|
||
|
+ raise NameError("Getbytes function failed to find matching networkquota attribute value %s record for user: %s" % (network, username))
|
||
|
+
|
||
|
+def getUid(username):
|
||
|
+ for dn, record in res:
|
||
|
+ if record["uid"][0] == username:
|
||
|
+ return record["uidNumber"][0]
|
||
|
+ return False
|
||
|
+
|
||
|
+def hasQuota(username):
|
||
|
+ q = False
|
||
|
+ for dn, record in res:
|
||
|
+ if "networkquota" in record:
|
||
|
+ if record["uid"][0] == username:
|
||
|
+ q = True
|
||
|
+ return q
|
||
|
+
|
||
|
+def processNetworkQuotas(username, actualbytes):
|
||
|
+ if not hasQuota(username):
|
||
|
+ print "User: %s does not have a network quota set in ldap" % (username)
|
||
|
+ removeRateLimiting(username)
|
||
|
+ return False
|
||
|
+
|
||
|
+ network = "Internet:0.0.0.0/0:any"
|
||
|
+ quotabytes = getBytes(username, network)
|
||
|
+ print "User: %s has used %s of %s bytes for network: %s" % (username, actualbytes, quotabytes , network)
|
||
|
+ if int(actualbytes) > int(quotabytes):
|
||
|
+ print "%s is over the limit by %d bytes" % (username, (int(actualbytes) - int(quotabytes)))
|
||
|
+ applyRateLimiting(username)
|
||
|
+ else:
|
||
|
+ removeRateLimiting(username)
|
||
|
+
|
||
|
+
|
||
|
+def applyRateLimiting(username):
|
||
|
+ if (isRateLimited(username)):
|
||
|
+ print "User: %s already rate limited" % (username)
|
||
|
+ return True
|
||
|
+ print "Rate limiting user: %s (%s)" % (username, getUid(username))
|
||
|
+ uid = (getUid(username))
|
||
|
+ print "CMD: " + tcapplycmd % (uid, int(uid))
|
||
|
+ subprocess.Popen(tcapplycmd % (uid, int(uid)), shell=True)
|
||
|
+ return True
|
||
|
+
|
||
|
+def removeRateLimiting(username):
|
||
|
+ if (isRateLimited(username)):
|
||
|
+ print "removing quota for: " + username
|
||
|
+ uid = (getUid(username))
|
||
|
+ print "CMD: " + tcremovecmd % (uid, int(uid))
|
||
|
+ subprocess.Popen(tcremovecmd % (uid, int(uid)), shell=True)
|
||
|
+ return True
|
||
|
+ return False
|
||
|
+
|
||
|
+def isRateLimited(username):
|
||
|
+ print "Checking if user: %s (%s) is rate limited" % (username, getUid(username))
|
||
|
+ # python regex
|
||
|
+ p = re.compile('filter parent 1: protocol ip pref 1 fw handle 0x([0-9A-Fa-f]+) classid 1:[0-9A-Fa-f]+')
|
||
|
+ for r in tcrules:
|
||
|
+# print "rule: " + r
|
||
|
+ m = p.match(r)
|
||
|
+ if m:
|
||
|
+ uid = int(m.groups()[0], 16)
|
||
|
+# print "match found" + str(uid)
|
||
|
+# print "uid from username" + getUid(username)
|
||
|
+ if uid == int(getUid(username)):
|
||
|
+ print "matching rule found " + r
|
||
|
+ return True
|
||
|
+ return False
|
||
|
+# connect to ldap
|
||
|
+l = ldap.initialize(uri)
|
||
|
+res = l.search_s(base, scope, filterstr, attrlist)
|
||
|
+print "\nLDAP Search results"
|
||
|
+for dn, record in res:
|
||
|
+ if "networkquota" in record:
|
||
|
+# print "Processing: " + repr(dn)
|
||
|
+ print "%-15s: %s" % (record["uid"], record["networkquota"])
|
||
|
+# print repr(record)
|
||
|
+l.unbind()
|
||
|
+
|
||
|
+
|
||
|
+# do this only once, instead of every time function is called. Read existing TC rules.
|
||
|
+tcproc = subprocess.Popen(tcfilterlist, shell=True, stdout=subprocess.PIPE)
|
||
|
+tcrules = tcproc.communicate()[0].split("\n")
|
||
|
+
|
||
|
+# connect to Mysql
|
||
|
+db = MySQLdb.connect(passwd="passwd", db="nufw", host="laptop", user="root", port=13306)
|
||
|
+c = db.cursor(MySQLdb.cursors.DictCursor)
|
||
|
+
|
||
|
+#query = """select bytes_in + bytes_out as bytes, username from
|
||
|
+#ulog where username is not null and bytes_in is not null and bytes_out is not null;"""
|
||
|
+
|
||
|
+c.execute(query)
|
||
|
+print "\nMysql Results:"
|
||
|
+results = c.fetchall()
|
||
|
+for x in results:
|
||
|
+ print "%(username)-15s: %(bytes)s\n" % x
|
||
|
+ processNetworkQuotas(x["username"], x["bytes"])
|
||
|
diff -Naur mmc-agent-2.3.2.orig/contrib/userquota/networkquota_example/setup.txt mmc-agent-2.3.2/contrib/userquota/networkquota_example/setup.txt
|
||
|
--- mmc-agent-2.3.2.orig/contrib/userquota/networkquota_example/setup.txt 1970-01-01 00:00:00.000000000 +0000
|
||
|
+++ mmc-agent-2.3.2/contrib/userquota/networkquota_example/setup.txt 2010-02-04 01:50:02.000000000 +0000
|
||
|
@@ -0,0 +1,10 @@
|
||
|
+# remove the existing classless qdisc
|
||
|
+tc qdisc del dev eth1 root
|
||
|
+
|
||
|
+# print status
|
||
|
+tc -s qdisc ls dev eth1
|
||
|
+
|
||
|
+# add new root qdisc
|
||
|
+tc qdisc add dev eth1 root handle 1: htb default 1
|
||
|
+
|
||
|
+
|
||
|
diff -Naur mmc-agent-2.3.2.orig/contrib/userquota/networkquota_example/tcruleadd.sh mmc-agent-2.3.2/contrib/userquota/networkquota_example/tcruleadd.sh
|
||
|
--- mmc-agent-2.3.2.orig/contrib/userquota/networkquota_example/tcruleadd.sh 1970-01-01 00:00:00.000000000 +0000
|
||
|
+++ mmc-agent-2.3.2/contrib/userquota/networkquota_example/tcruleadd.sh 2010-02-04 01:59:10.000000000 +0000
|
||
|
@@ -0,0 +1,10 @@
|
||
|
+#!/bin/bash
|
||
|
+# (c) 2009 Open Systems Specilists - Glen Ogilvie. License: GPL
|
||
|
+# Examle apply shell script
|
||
|
+USERID=$1
|
||
|
+# class id is Userid in hex.
|
||
|
+CLASSID=$2
|
||
|
+RATE="20kbps burst 5k"
|
||
|
+tc class add dev eth1 parent 1: classid 1:$CLASSID htb rate $RATE
|
||
|
+tc qdisc add dev eth1 parent 1:$CLASSID handle $CLASSID: sfq perturb 10
|
||
|
+tc filter add dev eth1 parent 1: protocol ip prio 1 handle $USERID fw flowid 1:$CLASSID
|
||
|
diff -Naur mmc-agent-2.3.2.orig/contrib/userquota/networkquota_example/tcruledel.sh mmc-agent-2.3.2/contrib/userquota/networkquota_example/tcruledel.sh
|
||
|
--- mmc-agent-2.3.2.orig/contrib/userquota/networkquota_example/tcruledel.sh 1970-01-01 00:00:00.000000000 +0000
|
||
|
+++ mmc-agent-2.3.2/contrib/userquota/networkquota_example/tcruledel.sh 2010-02-04 01:58:59.000000000 +0000
|
||
|
@@ -0,0 +1,11 @@
|
||
|
+#!/bin/bash
|
||
|
+# (c) 2009 Open Systems Specilists - Glen Ogilvie. License: GPL
|
||
|
+# Examle remove shell script
|
||
|
+USERID=$1
|
||
|
+# class id is Userid in hex.
|
||
|
+CLASSID=$2
|
||
|
+RATE="20kbps burst 5k"
|
||
|
+tc filter del dev eth1 parent 1: protocol ip prio 1 handle $USERID fw flowid 1:$CLASSID
|
||
|
+tc qdisc del dev eth1 parent 1:$CLASSID handle $CLASSID: sfq perturb 10
|
||
|
+tc class del dev eth1 parent 1: classid 1:$CLASSID htb rate $RATE
|
||
|
+
|
||
|
diff -Naur mmc-agent-2.3.2.orig/mmc/plugins/base/__init__.py mmc-agent-2.3.2/mmc/plugins/base/__init__.py
|
||
|
--- mmc-agent-2.3.2.orig/mmc/plugins/base/__init__.py 2010-02-05 18:08:26.095049411 +0000
|
||
|
+++ mmc-agent-2.3.2/mmc/plugins/base/__init__.py 2010-02-05 18:14:08.567292865 +0000
|
||
|
@@ -665,9 +665,8 @@
|
||
|
if self.config.has_section(USERDEFAULT):
|
||
|
for option in self.config.options(USERDEFAULT):
|
||
|
self.userDefault["base"][option] = self.config.get(USERDEFAULT, option)
|
||
|
-
|
||
|
- self.l = ldap.open(self.ldapHost)
|
||
|
-
|
||
|
+# self.l = ldap.open(self.ldapHost)
|
||
|
+ self.l = ldap.ldapobject.ReconnectLDAPObject("ldap://%s:389" % self.ldapHost, retry_max=5, retry_delay=10)
|
||
|
# you should set this to ldap.VERSION2 if you're using a v2 directory
|
||
|
self.l.protocol_version = ldap.VERSION3
|
||
|
|
||
|
@@ -715,7 +714,7 @@
|
||
|
new = old.copy()
|
||
|
new["loginShell"] = "/bin/bash" # FIXME: should not be hardcoded but put in a conf file
|
||
|
modlist = ldap.modlist.modifyModlist(old, new)
|
||
|
- self.l.modify_s(dn, modlist)
|
||
|
+ self.l.modify_ext_s(dn, modlist)
|
||
|
return 0
|
||
|
|
||
|
def disableUser(self, login):
|
||
|
@@ -731,7 +730,7 @@
|
||
|
new = old.copy()
|
||
|
new["loginShell"] = "/bin/false" # FIXME: should not be hardcoded but put in a conf file
|
||
|
modlist = ldap.modlist.modifyModlist(old, new)
|
||
|
- self.l.modify_s(dn, modlist)
|
||
|
+ self.l.modify_ext_s(dn, modlist)
|
||
|
return 0
|
||
|
|
||
|
def isEnabled(self, login):
|
||
|
@@ -983,7 +982,7 @@
|
||
|
cngroup = cngroup.encode("utf-8")
|
||
|
uiduser = uiduser.encode("utf-8")
|
||
|
try:
|
||
|
- self.l.modify_s('cn=' + cngroup + ',' + self.baseGroupsDN, [(ldap.MOD_DELETE, 'memberUid', uiduser)])
|
||
|
+ self.l.modify_ext_s('cn=' + cngroup + ',' + self.baseGroupsDN, [(ldap.MOD_DELETE, 'memberUid', uiduser)])
|
||
|
except ldap.NO_SUCH_ATTRIBUTE:
|
||
|
# There are no member in this group
|
||
|
pass
|
||
|
@@ -1086,7 +1085,7 @@
|
||
|
cngroup = cngroup.encode("utf-8")
|
||
|
uiduser = uiduser.encode("utf-8")
|
||
|
try:
|
||
|
- self.l.modify_s('cn=' + cngroup + ',' + base, [(ldap.MOD_ADD, 'memberUid', uiduser)])
|
||
|
+ self.l.modify_ext_s('cn=' + cngroup + ',' + base, [(ldap.MOD_ADD, 'memberUid', uiduser)])
|
||
|
except ldap.TYPE_OR_VALUE_EXISTS:
|
||
|
# Try to add a the user to one of his/her group
|
||
|
# Can be safely ignored
|
||
|
@@ -1113,11 +1112,11 @@
|
||
|
elif isinstance(attrVal, xmlrpclib.Binary):
|
||
|
# Needed for binary string coming from XMLRPC
|
||
|
attrVal = str(attrVal)
|
||
|
- self.l.modify_s('uid='+uid+','+ self.baseUsersDN, [(ldap.MOD_REPLACE,attr,attrVal)])
|
||
|
+ self.l.modify_ext_s('uid='+uid+','+ self.baseUsersDN, [(ldap.MOD_REPLACE,attr,attrVal)])
|
||
|
else:
|
||
|
# Remove the attribute because its value is empty
|
||
|
try:
|
||
|
- self.l.modify_s('uid='+uid+','+ self.baseUsersDN, [(ldap.MOD_DELETE,attr, None)])
|
||
|
+ self.l.modify_ext_s('uid='+uid+','+ self.baseUsersDN, [(ldap.MOD_DELETE,attr, None)])
|
||
|
except ldap.NO_SUCH_ATTRIBUTE:
|
||
|
# The attribute has been already deleted
|
||
|
pass
|
||
|
@@ -1138,10 +1137,10 @@
|
||
|
group = group.encode("utf-8")
|
||
|
if attrVal:
|
||
|
attrVal = str(attrVal.encode("utf-8"))
|
||
|
- self.l.modify_s('cn=' + group + ','+ self.baseGroupsDN, [(ldap.MOD_REPLACE,attr,attrVal)])
|
||
|
+ self.l.modify_ext_s('cn=' + group + ','+ self.baseGroupsDN, [(ldap.MOD_REPLACE,attr,attrVal)])
|
||
|
else:
|
||
|
- self.l.modify_s('cn=' + group + ','+ self.baseGroupsDN, [(ldap.MOD_REPLACE,attr,'rien')])
|
||
|
- self.l.modify_s('cn=' + group + ','+ self.baseGroupsDN, [(ldap.MOD_DELETE,attr,'rien')])
|
||
|
+ self.l.modify_ext_s('cn=' + group + ','+ self.baseGroupsDN, [(ldap.MOD_REPLACE,attr,'rien')])
|
||
|
+ self.l.modify_ext_s('cn=' + group + ','+ self.baseGroupsDN, [(ldap.MOD_DELETE,attr,'rien')])
|
||
|
return 0
|
||
|
|
||
|
def changeUserPasswd(self, uid, passwd):
|
||
|
@@ -1158,7 +1157,7 @@
|
||
|
self.l.passwd_s('uid=' + uid + ',' + self.baseUsersDN, None, str(passwd))
|
||
|
else:
|
||
|
userpassword = self._generatePassword(passwd)
|
||
|
- self.l.modify_s('uid=' + uid + ',' + self.baseUsersDN, [(ldap.MOD_REPLACE, "userPassword", userpassword)])
|
||
|
+ self.l.modify_ext_s('uid=' + uid + ',' + self.baseUsersDN, [(ldap.MOD_REPLACE, "userPassword", userpassword)])
|
||
|
# Run ChangeUserPassword hook
|
||
|
self.runHook("base.changeuserpassword", uid, passwd)
|
||
|
|
||
|
@@ -1628,7 +1627,7 @@
|
||
|
|
||
|
# Apply modification
|
||
|
mlist = ldap.modlist.modifyModlist(attrs, newattrs)
|
||
|
- self.l.modify_s(cn, mlist)
|
||
|
+ self.l.modify_ext_s(cn, mlist)
|
||
|
|
||
|
def removeGroupObjectClass(self, group, className):
|
||
|
# Create LDAP path
|
||
|
@@ -1654,7 +1653,7 @@
|
||
|
|
||
|
# Apply modification
|
||
|
mlist = ldap.modlist.modifyModlist(attrs, newattrs)
|
||
|
- self.l.modify_s(cn, mlist)
|
||
|
+ self.l.modify_ext_s(cn, mlist)
|
||
|
|
||
|
def getAttrToDelete(self, dn, className):
|
||
|
"""retrieve all attributes to delete wich correspond to param schema"""
|
||
|
@@ -1921,7 +1920,7 @@
|
||
|
"""
|
||
|
dn = "uid=" + uid + "," + self.l.baseUsersDN
|
||
|
try:
|
||
|
- self.l.l.modify_s( self._getGpoDN(gpoName), [(ldap.MOD_ADD, 'member', dn)])
|
||
|
+ self.l.l.modify_ext_s( self._getGpoDN(gpoName), [(ldap.MOD_ADD, 'member', dn)])
|
||
|
except ldap.TYPE_OR_VALUE_EXISTS:
|
||
|
# Value already set
|
||
|
pass
|
||
|
@@ -1935,7 +1934,7 @@
|
||
|
"""
|
||
|
dn = "uid=" + uid + "," + self.l.baseUsersDN
|
||
|
try:
|
||
|
- self.l.l.modify_s(self._getGpoDN(gpoName), [(ldap.MOD_DELETE, 'member', dn)])
|
||
|
+ self.l.l.modify_ext_s(self._getGpoDN(gpoName), [(ldap.MOD_DELETE, 'member', dn)])
|
||
|
except ldap.NO_SUCH_ATTRIBUTE:
|
||
|
# Value already deleted
|
||
|
pass
|
||
|
@@ -1969,7 +1968,7 @@
|
||
|
"""
|
||
|
dn = "cn=" + group + "," + self.l.baseGroupsDN
|
||
|
try:
|
||
|
- self.l.l.modify_s( self._getGpoDN(gpoName), [(ldap.MOD_ADD, 'member', dn)])
|
||
|
+ self.l.l.modify_ext_s( self._getGpoDN(gpoName), [(ldap.MOD_ADD, 'member', dn)])
|
||
|
except ldap.TYPE_OR_VALUE_EXISTS:
|
||
|
# Value already set
|
||
|
pass
|
||
|
@@ -1983,7 +1982,7 @@
|
||
|
"""
|
||
|
dn = "cn=" + group + "," + self.l.baseGroupsDN
|
||
|
try:
|
||
|
- self.l.l.modify_s(self._getGpoDN(gpoName), [(ldap.MOD_DELETE, 'member', dn)])
|
||
|
+ self.l.l.modify_ext_s(self._getGpoDN(gpoName), [(ldap.MOD_DELETE, 'member', dn)])
|
||
|
except ldap.NO_SUCH_ATTRIBUTE:
|
||
|
# Value already deleted
|
||
|
pass
|
||
|
diff -Naur mmc-agent-2.3.2.orig/mmc/plugins/userquota/__init__.py mmc-agent-2.3.2/mmc/plugins/userquota/__init__.py
|
||
|
--- mmc-agent-2.3.2.orig/mmc/plugins/userquota/__init__.py 1970-01-01 00:00:00.000000000 +0000
|
||
|
+++ mmc-agent-2.3.2/mmc/plugins/userquota/__init__.py 2010-02-04 00:06:51.000000000 +0000
|
||
|
@@ -0,0 +1,602 @@
|
||
|
+# -*- coding: utf-8; -*-
|
||
|
+# (c) 2009 Open Systems Specilists - Glen Ogilvie
|
||
|
+#
|
||
|
+# $Id: __init__.py $
|
||
|
+#
|
||
|
+# This file is a plugin for Mandriva Management Console (MMC).
|
||
|
+#
|
||
|
+# MMC is free software; you can redistribute it and/or modify
|
||
|
+# it under the terms of the GNU General Public License as published by
|
||
|
+# the Free Software Foundation; either version 2 of the License, or
|
||
|
+# (at your option) any later version.
|
||
|
+#
|
||
|
+# MMC is distributed in the hope that it will be useful,
|
||
|
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
+# GNU General Public License for more details.
|
||
|
+#
|
||
|
+# You should have received a copy of the GNU General Public License
|
||
|
+# along with MMC; if not, write to the Free Software
|
||
|
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||
|
+
|
||
|
+import socket
|
||
|
+import ldap
|
||
|
+import logging
|
||
|
+import os
|
||
|
+import os.path
|
||
|
+import grp
|
||
|
+import tempfile
|
||
|
+
|
||
|
+from mmc.plugins.base import ldapUserGroupControl
|
||
|
+from mmc.support.config import *
|
||
|
+from mmc.support import mmctools
|
||
|
+from string import Template
|
||
|
+
|
||
|
+
|
||
|
+import mmc
|
||
|
+
|
||
|
+INI = "/etc/mmc/plugins/userquota.ini"
|
||
|
+
|
||
|
+VERSION = "0.0.3"
|
||
|
+APIVERSION = "1:0:0"
|
||
|
+REVISION = int("$Rev: 1 $".split(':')[1].strip(' $'))
|
||
|
+
|
||
|
+def getVersion(): return VERSION
|
||
|
+def getApiVersion(): return APIVERSION
|
||
|
+def getRevision(): return REVISION
|
||
|
+
|
||
|
+
|
||
|
+def activate():
|
||
|
+ return True
|
||
|
+
|
||
|
+def getActiveComponents():
|
||
|
+ return UserQuotaControl().getActiveComponents()
|
||
|
+
|
||
|
+def getDevicemap():
|
||
|
+ return UserQuotaControl().getDevicemap()
|
||
|
+
|
||
|
+def getNetworkmap():
|
||
|
+ return UserQuotaControl().getNetworkmap()
|
||
|
+
|
||
|
+def setDiskQuota(uid, device, quota):
|
||
|
+ return UserQuotaControl().setDiskQuota(uid, device, quota)
|
||
|
+
|
||
|
+def setNetworkQuota(uid, network, quota):
|
||
|
+ return UserQuotaControl().setNetworkQuota(uid, network, quota)
|
||
|
+
|
||
|
+def setGroupDiskQuota(group, device, quota, overwrite):
|
||
|
+ return UserQuotaControl().setGroupDiskQuota(group, device, quota, overwrite)
|
||
|
+
|
||
|
+def deleteGroupDiskQuota(cn, device):
|
||
|
+ return UserQuotaControl().deleteGroupDiskQuotas(cn, device)
|
||
|
+
|
||
|
+def setGroupNetworkQuota(group, network, quota, overwrite):
|
||
|
+ return UserQuotaControl().setGroupNetworkQuota(group, network, quota, overwrite)
|
||
|
+
|
||
|
+def deleteGroupNetworkQuota(cn, device):
|
||
|
+ return UserQuotaControl().deleteGroupNetworkQuotas(cn, device)
|
||
|
+
|
||
|
+
|
||
|
+def deleteDiskQuota(uid, device):
|
||
|
+ return UserQuotaControl().deleteDiskQuota(uid, device)
|
||
|
+
|
||
|
+def deleteNetworkQuota(uid, network):
|
||
|
+ return UserQuotaControl().deleteNetworkQuota(uid, network)
|
||
|
+
|
||
|
+def setUserQuotaDefaults(user, group):
|
||
|
+ return UserQuotaControl().setUserQuotaDefaults(user, group)
|
||
|
+
|
||
|
+class UserQuotaConfig(PluginConfig):
|
||
|
+
|
||
|
+ def readConf(self):
|
||
|
+ PluginConfig.readConf(self)
|
||
|
+ try: self.diskquotaenable = self.getboolean("diskquota", "enable")
|
||
|
+ except: pass
|
||
|
+ try: self.networkquotaenable = self.getboolean("networkquota", "enable")
|
||
|
+ except: pass
|
||
|
+ self.devicemap = self.get("diskquota", "devicemap").split(',')
|
||
|
+ self.inodesperblock = self.getfloat("diskquota", "inodesperblock")
|
||
|
+ self.softquotablocks = self.getfloat("diskquota", "softquotablocks")
|
||
|
+ self.softquotainodes = self.getfloat("diskquota", "softquotainodes")
|
||
|
+ self.setquotascript = self.get("diskquota", "setquotascript")
|
||
|
+ self.delquotascript = self.get("diskquota", "delquotascript")
|
||
|
+ self.networkmap = self.get("networkquota", "networkmap").split(',')
|
||
|
+
|
||
|
+ def setDefault(self):
|
||
|
+ PluginConfig.setDefault(self)
|
||
|
+ self.diskquotaenable = True
|
||
|
+ self.networkquotaenable = False
|
||
|
+
|
||
|
+
|
||
|
+class UserQuotaControl(ldapUserGroupControl):
|
||
|
+ def __init__(self, conffile=None, conffilebase=None):
|
||
|
+ mmc.plugins.base.ldapUserGroupControl.__init__(self, conffilebase)
|
||
|
+ self.configuserquota = UserQuotaConfig("userquota", conffile)
|
||
|
+ self.tempfilename = False
|
||
|
+ self.tempdelfilename = False
|
||
|
+ def getDevicemap(self):
|
||
|
+ return self.configuserquota.devicemap
|
||
|
+ def getNetworkmap(self):
|
||
|
+ return self.configuserquota.networkmap
|
||
|
+ def getActiveComponents(self):
|
||
|
+ return ({"disk":self.configuserquota.diskquotaenable, "network":self.configuserquota.networkquotaenable})
|
||
|
+
|
||
|
+ def deleteNetworkQuota(self, uid, network):
|
||
|
+ logger = logging.getLogger()
|
||
|
+ try:
|
||
|
+ currentquotas = self.getDetailedUser(uid)["networkquota"]
|
||
|
+ newquotas = []
|
||
|
+ for x in currentquotas:
|
||
|
+ if not x.split(',')[0] == network:
|
||
|
+ newquotas.append(x)
|
||
|
+
|
||
|
+ if len(newquotas) == 0:
|
||
|
+ self.changeUserAttributes(uid, 'networkquota', None)
|
||
|
+ self.delQuotaObjectClass(uid)
|
||
|
+
|
||
|
+ else:
|
||
|
+ self.changeUserAttributes(uid, 'networkquota', newquotas)
|
||
|
+
|
||
|
+ except KeyError:
|
||
|
+ pass
|
||
|
+ return True
|
||
|
+
|
||
|
+ def deleteDiskQuota(self, uid, device, single=True):
|
||
|
+ logger = logging.getLogger()
|
||
|
+# logger.info("Deleting quota for: "+ uid)
|
||
|
+ devicepath = device.split(':')[0]
|
||
|
+ try:
|
||
|
+ currentquotas = self.getDetailedUser(uid)["quota"]
|
||
|
+ newquotas = []
|
||
|
+ for x in currentquotas:
|
||
|
+ if not x.split('=')[0] == devicepath:
|
||
|
+ newquotas.append(x)
|
||
|
+
|
||
|
+ if len(newquotas) == 0:
|
||
|
+ self.changeUserAttributes(uid, 'quota', None)
|
||
|
+ self.delQuotaObjectClass(uid)
|
||
|
+
|
||
|
+ else:
|
||
|
+ self.changeUserAttributes(uid, 'quota', newquotas)
|
||
|
+
|
||
|
+ self.appendDeleteQuotatasks(uid, devicepath)
|
||
|
+ if single:
|
||
|
+ logger.info("Delete Single quota")
|
||
|
+ self.deleteQuotaOnFS();
|
||
|
+
|
||
|
+ except KeyError:
|
||
|
+ pass
|
||
|
+
|
||
|
+ return True
|
||
|
+
|
||
|
+
|
||
|
+ def setNetworkQuota(self, uid, network, quota, overwrite = "all"):
|
||
|
+ logger = logging.getLogger()
|
||
|
+ ldapquota = '%s,%s' % (network, str(int(quota) * 1048576))
|
||
|
+ logger.info("Network Quota:" + ldapquota)
|
||
|
+
|
||
|
+ if not self.hasDiskQuotaObjectClass(uid):
|
||
|
+ self.addDiskQuotaObjectClass(uid)
|
||
|
+ # @todo, fix copy from disk quotas.
|
||
|
+ try:
|
||
|
+ userdetails = self.getDetailedUser(uid)
|
||
|
+ currentquotas = userdetails["networkquota"]
|
||
|
+ newquotas = []
|
||
|
+ quotachanged = False
|
||
|
+ for x in currentquotas:
|
||
|
+ if x.split(',')[0] == network:
|
||
|
+ logger.info("Current network quota sizes: " + str(self.convertNetworkQuotaToMB(x)))
|
||
|
+ logger.info("Requested quota size: " + quota)
|
||
|
+ logger.info("Overwrite mode: " + overwrite)
|
||
|
+ if (overwrite == "none"):
|
||
|
+ logger.info('No overwrite mode set. so not overwriting.')
|
||
|
+ return False
|
||
|
+ if overwrite == "smaller" and self.convertNetworkQuotaToMB(x) > int(quota):
|
||
|
+ logger.info('Current network quota is bigger than new quota, so not overwriting')
|
||
|
+ return False
|
||
|
+ if overwrite == "larger" and int(quota) > self.convertNetworkQuotaToMB(x):
|
||
|
+ logger.info('Current network quota is smaller than new quota, so not overwriting')
|
||
|
+ return False
|
||
|
+ newquotas.append(ldapquota)
|
||
|
+ quotachanged = True
|
||
|
+ else:
|
||
|
+ newquotas.append(x)
|
||
|
+
|
||
|
+ if not quotachanged:
|
||
|
+ newquotas.append(ldapquota)
|
||
|
+
|
||
|
+ self.changeUserAttributes(uid, 'networkquota', newquotas)
|
||
|
+ except KeyError:
|
||
|
+ self.changeUserAttributes(uid, 'networkquota', ldapquota)
|
||
|
+ pass
|
||
|
+
|
||
|
+ return True
|
||
|
+
|
||
|
+ def setDiskQuota(self, uid, device, quota, overwrite = "all",single = True):
|
||
|
+ logger = logging.getLogger()
|
||
|
+ logger.info("received quota for " + uid + ", device: " + device + ", size: " + quota)
|
||
|
+ blocks = self.convertMBtoBlocks(quota, device);
|
||
|
+ softblocks = int (blocks * self.configuserquota.softquotablocks)
|
||
|
+ inodes = int(blocks * self.configuserquota.inodesperblock)
|
||
|
+ softinodes = int(inodes * self.configuserquota.softquotainodes)
|
||
|
+ devicepath = device.split(':')[0]
|
||
|
+# logger.info("Devicepath: " + devicepath)
|
||
|
+# logger.info("Blocks: " + str(blocks))
|
||
|
+# logger.info("Soft Blocks: " + str(softblocks))
|
||
|
+# logger.info("Inodes: " + str(inodes))
|
||
|
+# logger.info("Soft Inodes: " + str(softinodes))
|
||
|
+ ldapquota = '%s=%s:%s:%s:%s' % (devicepath, str(blocks), str(softblocks), str(inodes), str(softinodes))
|
||
|
+ logger.info("Quota for: " + uid + " - " + ldapquota)
|
||
|
+
|
||
|
+ if not self.hasDiskQuotaObjectClass(uid):
|
||
|
+ self.addDiskQuotaObjectClass(uid)
|
||
|
+ try:
|
||
|
+ userdetails = self.getDetailedUser(uid)
|
||
|
+ currentquotas = userdetails["quota"]
|
||
|
+ newquotas = []
|
||
|
+ quotachanged = False
|
||
|
+ for x in currentquotas:
|
||
|
+ if x.split('=')[0] == devicepath:
|
||
|
+ logger.info("Current network quota sizes: " + str(self.convertDiskQuotaToMB(x)))
|
||
|
+ logger.info("Requested quota size: " + quota)
|
||
|
+ logger.info("Overwrite mode: " + overwrite)
|
||
|
+ if overwrite == "none":
|
||
|
+ return False
|
||
|
+ if overwrite == "smaller" and self.convertDiskQuotaToMB(x) > int(quota):
|
||
|
+ logger.info('Current quota is bigger than new quota, so not overwriting')
|
||
|
+ return False
|
||
|
+ if overwrite == "larger" and int(quota) > self.convertDiskQuotaToMB(x):
|
||
|
+ logger.info('Current quota is smaller than new quota, so not overwriting')
|
||
|
+ return False
|
||
|
+
|
||
|
+ newquotas.append(ldapquota)
|
||
|
+ quotachanged = True
|
||
|
+ else:
|
||
|
+ newquotas.append(x)
|
||
|
+
|
||
|
+ if not quotachanged:
|
||
|
+ newquotas.append(ldapquota)
|
||
|
+ self.changeUserAttributes(uid, 'quota', newquotas)
|
||
|
+ except KeyError:
|
||
|
+ self.changeUserAttributes(uid, 'quota', ldapquota)
|
||
|
+ pass
|
||
|
+
|
||
|
+ self.appendQuotatasks(uid, blocks, softblocks, inodes, softinodes, devicepath)
|
||
|
+ if single:
|
||
|
+ self.applyQuotaToFS()
|
||
|
+
|
||
|
+ return True
|
||
|
+
|
||
|
+ def appendQuotatasks(self, uid, blocks, softblocks, inodes, softinodes, devicepath):
|
||
|
+ logger = logging.getLogger()
|
||
|
+ if not self.tempfilename:
|
||
|
+ self.tempfilename = tempfile.mktemp()
|
||
|
+ logger.info("Temp file: %s" % (self.tempfilename))
|
||
|
+
|
||
|
+
|
||
|
+ s = Template(self.configuserquota.setquotascript)
|
||
|
+ shellscript = s.substitute(uid=uid, blocks=blocks, softblocks=softblocks, inodes=inodes, softinodes=softinodes, devicepath=devicepath)
|
||
|
+ logger.info("Append SetQuotaScript: " + shellscript);
|
||
|
+ f = open(self.tempfilename, 'a')
|
||
|
+ f.write("%s\n" % (shellscript))
|
||
|
+ f.close
|
||
|
+
|
||
|
+
|
||
|
+ def applyQuotaToFS(self):
|
||
|
+ if not self.tempfilename:
|
||
|
+ return
|
||
|
+ logger = logging.getLogger()
|
||
|
+ shellscript = "/bin/sh %s" % (self.tempfilename)
|
||
|
+ logger.info("ApplyQuotas: " + shellscript);
|
||
|
+ def cb(shprocess):
|
||
|
+ # The callback just return the process outputs
|
||
|
+ #
|
||
|
+ if shprocess.exitCode != 0:
|
||
|
+ logger.error("Error applying quotas: " + shprocess.err)
|
||
|
+ logger.info("shell result:" + shprocess.out)
|
||
|
+ logger.error("See: " + self.tempfilename + " for details of the commands run")
|
||
|
+ else:
|
||
|
+ os.remove(self.tempfilename)
|
||
|
+
|
||
|
+ self.tempfilename = False
|
||
|
+ return shprocess.exitCode, shprocess.out, shprocess.err
|
||
|
+
|
||
|
+ d = mmctools.shLaunchDeferred(shellscript)
|
||
|
+ # shLaunchDeferred returns a Deferred() object
|
||
|
+ # We add the cb function as a callback
|
||
|
+ d.addCallback(cb)
|
||
|
+ # We return the Deferred() object
|
||
|
+ return d
|
||
|
+
|
||
|
+ def appendDeleteQuotatasks(self, uid, devicepath):
|
||
|
+ logger = logging.getLogger()
|
||
|
+ if not self.tempdelfilename:
|
||
|
+ self.tempdelfilename = tempfile.mktemp()
|
||
|
+ logger.info("Temp file: %s" % (self.tempdelfilename))
|
||
|
+
|
||
|
+ s = Template(self.configuserquota.delquotascript)
|
||
|
+ shellscript = s.substitute(uid=uid, devicepath=devicepath)
|
||
|
+ logger.info("Append DelQuotaScript: " + shellscript);
|
||
|
+ f = open(self.tempdelfilename, 'a')
|
||
|
+ f.write("%s\n" % (shellscript))
|
||
|
+ f.close
|
||
|
+
|
||
|
+
|
||
|
+ def deleteQuotaOnFS(self):
|
||
|
+ if not self.tempdelfilename:
|
||
|
+ return
|
||
|
+ logger = logging.getLogger()
|
||
|
+ shellscript = "/bin/sh %s" % (self.tempdelfilename)
|
||
|
+ logger.info("DelQuotaScript: " + shellscript);
|
||
|
+ def cb(shprocess):
|
||
|
+ # The callback just return the process outputs
|
||
|
+ if shprocess.exitCode != 0:
|
||
|
+ logger.error("Error applying del quotas: " + shprocess.err)
|
||
|
+ logger.info("shell result:" + shprocess.out)
|
||
|
+ logger.error("See: " + self.tempfilename + " for details of the commands run")
|
||
|
+ else:
|
||
|
+ os.remove(self.tempdelfilename)
|
||
|
+
|
||
|
+ self.tempdelfilename = False
|
||
|
+ return shprocess.exitCode, shprocess.out, shprocess.err
|
||
|
+ d = mmctools.shLaunchDeferred(shellscript)
|
||
|
+ # shLaunchDeferred returns a Deferred() object
|
||
|
+ # We add the cb function as a callback
|
||
|
+ d.addCallback(cb)
|
||
|
+ # We return the Deferred() object
|
||
|
+ return d
|
||
|
+
|
||
|
+ def convertMBtoBlocks(self, quota, device):
|
||
|
+ parts = device.split(':')
|
||
|
+ blocks = int(parts[1])
|
||
|
+ bytes = int(quota) * 1048576
|
||
|
+ return int(bytes / blocks)
|
||
|
+
|
||
|
+ def convertNetworkQuotaToMB(self, quota):
|
||
|
+ return int(quota.split(',')[1])/1048576
|
||
|
+
|
||
|
+ def convertDiskQuotaToMB(self,quota):
|
||
|
+ devicemap = self.getDevicemap()
|
||
|
+ devicepath = quota.split('=')[0]
|
||
|
+ for x in devicemap:
|
||
|
+ if x.split(':')[0] == devicepath:
|
||
|
+ return int(quota.split('=')[1].split(":")[0]) * int(x.split(':')[1]) / 1048576
|
||
|
+ return False
|
||
|
+
|
||
|
+
|
||
|
+ def hasDiskQuotaObjectClass(self, uid):
|
||
|
+ """
|
||
|
+ Return true if the user owns the systemQuotas objectClass.
|
||
|
+
|
||
|
+ @param uid: user name
|
||
|
+ @type uid: str
|
||
|
+
|
||
|
+ @return: return True if the user owns the mailAccount objectClass.
|
||
|
+ @rtype: boolean
|
||
|
+ """
|
||
|
+ return "systemQuotas" in self.getDetailedUser(uid)["objectClass"]
|
||
|
+
|
||
|
+ def delQuotaObjectClass(self, uid):
|
||
|
+ """
|
||
|
+ Return true if the objectClass is removed.
|
||
|
+
|
||
|
+ @return: return True if the object class is able to be removed
|
||
|
+ @rtype: boolean
|
||
|
+ """
|
||
|
+ logger = logging.getLogger()
|
||
|
+ user = self.getDetailedUser(uid)
|
||
|
+ logger.info("Del object class")
|
||
|
+
|
||
|
+ if "quota" in user.keys() or "networkquota" in user.keys():
|
||
|
+ return False
|
||
|
+
|
||
|
+ logger.info("Del object class removal")
|
||
|
+ if "systemQuotas" in user["objectClass"]:
|
||
|
+ user["objectClass"].remove("systemQuotas")
|
||
|
+ self.changeUserAttributes(uid, 'objectClass', user["objectClass"])
|
||
|
+ return True
|
||
|
+ return False
|
||
|
+
|
||
|
+ def delGroupQuotaObjectClass(self, cn):
|
||
|
+ """
|
||
|
+ Return true if the objectClass is removed.
|
||
|
+
|
||
|
+ @return: return True if the object class is able to be removed
|
||
|
+ @rtype: boolean
|
||
|
+ """
|
||
|
+ logger = logging.getLogger()
|
||
|
+ group = self.getDetailedGroup(cn)
|
||
|
+ logger.info("Del Group object class")
|
||
|
+ logger.info("group keys" + str(group.keys()))
|
||
|
+ if "quota" in group.keys() or "networkquota" in group.keys():
|
||
|
+ return False
|
||
|
+
|
||
|
+ logger.info("Del object class removal")
|
||
|
+ if "defaultQuotas" in group["objectClass"]:
|
||
|
+ group["objectClass"].remove("defaultQuotas")
|
||
|
+ logger.info("ObjectClass to save:" + str( group["objectClass"]) )
|
||
|
+ self.changeGroupAttribute(cn, 'objectClass', group["objectClass"])
|
||
|
+ return True
|
||
|
+ return False
|
||
|
+
|
||
|
+ def addDiskQuotaObjectClass(self, uid):
|
||
|
+ user = self.getDetailedUser(uid)['objectClass']
|
||
|
+ if not "systemQuotas" in user:
|
||
|
+ user.append("systemQuotas")
|
||
|
+ self.l.modify_ext_s('uid=' + uid + ',' + self.baseUsersDN, [(ldap.MOD_REPLACE, 'objectClass', user)])
|
||
|
+ return True
|
||
|
+
|
||
|
+
|
||
|
+ def setGroupDiskQuota(self, group, device, quota, overwrite):
|
||
|
+ logger = logging.getLogger()
|
||
|
+ logger.info("SetGroupDiskQuota: Overwrite mode: " + overwrite)
|
||
|
+ logger.info("ldap timeout:" + str(self.l.timeout))
|
||
|
+ self.l.set_option(ldap.OPT_NETWORK_TIMEOUT, 100)
|
||
|
+ logger.info("ldap network timeout:" + str(self.l.get_option(ldap.OPT_NETWORK_TIMEOUT)))
|
||
|
+
|
||
|
+
|
||
|
+ self.addGroupDefaultDiskQuotaObjectClass(group)
|
||
|
+
|
||
|
+ blocks = self.convertMBtoBlocks(quota, device);
|
||
|
+ softblocks = int (blocks * self.configuserquota.softquotablocks)
|
||
|
+ inodes = int(blocks * self.configuserquota.inodesperblock)
|
||
|
+ softinodes = int(inodes * self.configuserquota.softquotainodes)
|
||
|
+ devicepath = device.split(':')[0]
|
||
|
+ ldapquota = '%s=%s:%s:%s:%s' % (devicepath, str(blocks), str(softblocks), str(inodes), str(softinodes))
|
||
|
+ # @todo improve this, it's a copy of set disk quota.
|
||
|
+ try:
|
||
|
+ currentquotas = self.getDetailedGroup(group)["quota"]
|
||
|
+ newquotas = []
|
||
|
+ quotachanged = False
|
||
|
+ for x in currentquotas:
|
||
|
+ if x.split('=')[0] == devicepath:
|
||
|
+ newquotas.append(ldapquota)
|
||
|
+ quotachanged = True
|
||
|
+ else:
|
||
|
+ newquotas.append(x)
|
||
|
+
|
||
|
+ if not quotachanged:
|
||
|
+ newquotas.append(ldapquota)
|
||
|
+
|
||
|
+ self.changeGroupAttribute(group, "quota", newquotas)
|
||
|
+
|
||
|
+ except KeyError:
|
||
|
+ self.changeGroupAttribute(group, "quota", ldapquota)
|
||
|
+ pass
|
||
|
+
|
||
|
+
|
||
|
+ for uid in self.getMembers(group):
|
||
|
+ self.setDiskQuota(uid, device, quota, overwrite, single=False)
|
||
|
+
|
||
|
+ # apply the group quotas to FS.
|
||
|
+ self.applyQuotaToFS()
|
||
|
+
|
||
|
+
|
||
|
+ return True
|
||
|
+
|
||
|
+ def setGroupNetworkQuota(self, group, network, quota, overwrite):
|
||
|
+ logger = logging.getLogger()
|
||
|
+ logger.info("SetGroupNetworkQuota Overwrite mode: " + overwrite)
|
||
|
+ self.addGroupDefaultDiskQuotaObjectClass(group)
|
||
|
+ ldapquota = '%s,%s' % (network, str(int(quota) * 1048576))
|
||
|
+ # @todo improve this, it's a copy of set disk quota.
|
||
|
+ try:
|
||
|
+ currentquotas = self.getDetailedGroup(group)["networkquota"]
|
||
|
+ newquotas = []
|
||
|
+ quotachanged = False
|
||
|
+ for x in currentquotas:
|
||
|
+ if x.split(',')[0] == network:
|
||
|
+ newquotas.append(ldapquota)
|
||
|
+ quotachanged = True
|
||
|
+ else:
|
||
|
+ newquotas.append(x)
|
||
|
+
|
||
|
+ if not quotachanged:
|
||
|
+ newquotas.append(ldapquota)
|
||
|
+
|
||
|
+ self.changeGroupAttribute(group, "networkquota", newquotas)
|
||
|
+
|
||
|
+ except KeyError:
|
||
|
+ self.changeGroupAttribute(group, "networkquota", ldapquota)
|
||
|
+ pass
|
||
|
+
|
||
|
+ for uid in self.getMembers(group):
|
||
|
+ self.setNetworkQuota(uid, network, quota, overwrite)
|
||
|
+ return True
|
||
|
+
|
||
|
+ def setUserQuotaDefaults(self, uid, group):
|
||
|
+ # @todo: unfinished, does nothing yet.
|
||
|
+ logger = logging.getLogger()
|
||
|
+ logger.info("Set user quota defaults: user: " + uid + " group: " + group)
|
||
|
+ keys = []
|
||
|
+ # don't set the quota if one has been set before.
|
||
|
+ logger.info(self.getDetailedUser(uid)["objectClass"])
|
||
|
+ logger.info("Value of: self.hasDiskQuotaObjectClass(uid)" + str(self.hasDiskQuotaObjectClass(uid)))
|
||
|
+ if self.hasDiskQuotaObjectClass(uid):
|
||
|
+ logger.info("User already has quota Object class")
|
||
|
+ return keys
|
||
|
+
|
||
|
+ groupdefaults = self.getDetailedGroup(group)
|
||
|
+
|
||
|
+ # @todo, check components before action
|
||
|
+ if "quota" in groupdefaults.keys():
|
||
|
+ # copy quota values to user
|
||
|
+ logger.info("copy quota values to user:" + uid)
|
||
|
+ self.addDiskQuotaObjectClass(uid)
|
||
|
+ self.changeUserAttributes(uid, "quota", groupdefaults["quota"])
|
||
|
+ keys.append('quota')
|
||
|
+
|
||
|
+ if "networkquota" in groupdefaults.keys():
|
||
|
+ # copy networkquota values to user
|
||
|
+ logger.info("copy network quota values to user:" + uid)
|
||
|
+ self.addDiskQuotaObjectClass(uid)
|
||
|
+ self.changeUserAttributes(uid, "networkquota", groupdefaults["networkquota"])
|
||
|
+ keys.append('networkquota')
|
||
|
+
|
||
|
+ return keys
|
||
|
+
|
||
|
+ def addGroupDefaultDiskQuotaObjectClass(self, cn):
|
||
|
+ group = self.getDetailedGroup(cn)['objectClass']
|
||
|
+ if not "defaultQuotas" in group:
|
||
|
+ group.append("defaultQuotas")
|
||
|
+ self.changeGroupAttribute(cn, 'objectClass', group)
|
||
|
+ return True
|
||
|
+
|
||
|
+ def changeGroupAttribute(self, cn, attr, attrval):
|
||
|
+ self.l.modify_ext_s('cn=' + cn + ',' + self.baseGroupsDN, [(ldap.MOD_REPLACE, attr, attrval)])
|
||
|
+
|
||
|
+ def deleteGroupDiskQuotas(self, cn, device):
|
||
|
+ logger = logging.getLogger()
|
||
|
+ devicepath = device.split(':')[0]
|
||
|
+ logger.info("Delete quotas for members of:" + cn)
|
||
|
+ logger.info("ldap timeout:" + str(self.l.timeout))
|
||
|
+ self.l.set_option(ldap.OPT_NETWORK_TIMEOUT, 100)
|
||
|
+ logger.info("ldap network timeout:" + str(self.l.get_option(ldap.OPT_NETWORK_TIMEOUT)))
|
||
|
+
|
||
|
+
|
||
|
+ for uid in self.getMembers(cn):
|
||
|
+ self.deleteDiskQuota(uid, device, single=False)
|
||
|
+
|
||
|
+ self.deleteQuotaOnFS();
|
||
|
+
|
||
|
+ try:
|
||
|
+ currentquotas = self.getDetailedGroup(cn)["quota"]
|
||
|
+ newquotas = []
|
||
|
+ for x in currentquotas:
|
||
|
+ if not x.split('=')[0] == devicepath:
|
||
|
+ newquotas.append(x)
|
||
|
+
|
||
|
+ if len(newquotas) == 0:
|
||
|
+ self.changeGroupAttribute(cn, 'quota', None)
|
||
|
+ self.delGroupQuotaObjectClass(cn)
|
||
|
+ else:
|
||
|
+ self.changeGroupAttribute(cn, 'quota', newquotas)
|
||
|
+
|
||
|
+ except KeyError:
|
||
|
+ pass
|
||
|
+
|
||
|
+ return True
|
||
|
+
|
||
|
+ def deleteGroupNetworkQuotas(self, cn, network):
|
||
|
+ logger = logging.getLogger()
|
||
|
+ logger.info("Delete networkquotas for members of: " + cn)
|
||
|
+ for uid in self.getMembers(cn):
|
||
|
+ self.deleteNetworkQuota(uid, network)
|
||
|
+ try:
|
||
|
+ currentquotas = self.getDetailedGroup(cn)["networkquota"]
|
||
|
+ newquotas = []
|
||
|
+
|
||
|
+ for x in currentquotas:
|
||
|
+ if not x.split(',')[0] == network:
|
||
|
+ newquotas.append(x)
|
||
|
+
|
||
|
+ if len(newquotas) == 0:
|
||
|
+ self.changeGroupAttribute(cn, 'networkquota', None)
|
||
|
+ self.delGroupQuotaObjectClass(cn)
|
||
|
+ else:
|
||
|
+ self.changeGroupAttribute(cn, 'networkquota', newquotas)
|
||
|
+
|
||
|
+ except KeyError:
|
||
|
+ pass
|
||
|
+ return True
|
||
|
+
|
||
|
diff -Naur mmc-agent-2.3.2.orig/setup.py mmc-agent-2.3.2/setup.py
|
||
|
--- mmc-agent-2.3.2.orig/setup.py 2010-02-05 18:08:41.170067138 +0000
|
||
|
+++ mmc-agent-2.3.2/setup.py 2010-02-05 18:09:35.879049358 +0000
|
||
|
@@ -8,5 +8,5 @@
|
||
|
author_email = "cdelfosse@mandriva.com",
|
||
|
maintainer = "Cedric Delfosse",
|
||
|
maintainer_email = "cdelfosse@mandriva.com",
|
||
|
- packages = ["mmc", "mmc.support", "mmc.plugins", "mmc.plugins.base", "mmc.plugins.samba", "mmc.plugins.proxy", "mmc.plugins.mail", "mmc.plugins.network", "mmc.plugins.kerberos", "mmc.plugins.printstats", "mmc.plugins.printing"],
|
||
|
+ packages = ["mmc", "mmc.support", "mmc.plugins", "mmc.plugins.base", "mmc.plugins.samba", "mmc.plugins.proxy", "mmc.plugins.mail", "mmc.plugins.network", "mmc.plugins.kerberos", "mmc.plugins.printstats", "mmc.plugins.printing", "mmc.plugins.userquota"],
|
||
|
)
|