diff --git a/lam/HISTORY b/lam/HISTORY index cacc030c..aeff1aba 100644 --- a/lam/HISTORY +++ b/lam/HISTORY @@ -1,5 +1,7 @@ August 2011 3.5.0 - - New module "General information": shows internal data about accounts (e.g. creation time) + - New modules: + -> "General information": shows internal data about accounts (e.g. creation time) + -> "Quota": manage filesystem quota inside LDAP (Linux DiskQuota) (RFE 1811449) - inetOrgPerson: New attributes o, employeeNumber, initials - Unix: Support to create home directories on multiple servers and also for existing users - Server information shows data from cn=monitor diff --git a/lam/docs/manual-sources/howto.xml b/lam/docs/manual-sources/howto.xml index 255c68de..74938409 100644 --- a/lam/docs/manual-sources/howto.xml +++ b/lam/docs/manual-sources/howto.xml @@ -1651,12 +1651,19 @@ Have fun!
- Quota + Filesystem quota (lamdaemon) You can manage file system quotas with LAM. This requires to - setup lamdaemon. File system quotas - are not stored inside LAM but managed directly on the specified - servers. + setup lamdaemon. LAM connects to + your server via SSH and manages the disk filesystem quotas. The quotas + are stored directly on the filesystem. This is the default mechanism + to store quotas for most systems. + + Please add the module "Quota (quota)" for users to your LAM + server profile to enable this feature. + + If you store the quota information directly inside LDAP please + see the next section. @@ -1667,6 +1674,30 @@ Have fun!
+
+ Filesystem quota (LDAP) + + You can store your filesystem quotas directly in LDAP. See + Linux + DiskQuota for details since it requires quota tools that + support LDAP. You will need to install the quota LDAP schema to manage + the object class "systemQuotas". + + Please add the module "Quota (systemQuotas)" for users to your + LAM server profile to enable this feature. + + If you store the quota information on the filesystem please see + the previous section. + + + + + + + + +
+
Kolab @@ -3724,6 +3755,26 @@ Have fun! + + + + + + + + Filesystem quotas + + systemQuotas + + quota.schema + + Linux + DiskQuota + + + + @@ -3739,7 +3790,7 @@ Have fun! Part of OpenLDAP installation - These account type is only available in LAM Pro. + This account type is only available in LAM Pro. diff --git a/lam/docs/manual-sources/images/mod_systemQuotas.png b/lam/docs/manual-sources/images/mod_systemQuotas.png new file mode 100644 index 00000000..61117964 Binary files /dev/null and b/lam/docs/manual-sources/images/mod_systemQuotas.png differ diff --git a/lam/docs/manual-sources/images/schema_quota.png b/lam/docs/manual-sources/images/schema_quota.png new file mode 100644 index 00000000..7e8d7017 Binary files /dev/null and b/lam/docs/manual-sources/images/schema_quota.png differ diff --git a/lam/lib/modules/systemQuotas.inc b/lam/lib/modules/systemQuotas.inc new file mode 100644 index 00000000..72150c04 --- /dev/null +++ b/lam/lib/modules/systemQuotas.inc @@ -0,0 +1,378 @@ + array('posixAccount'), 'conflicts' => array()); + // managed object classes + $return['objectClasses'] = array('systemQuotas'); + // managed attributes + $return['attributes'] = array('quota'); + // help Entries + $return['help'] = array( + 'quota' => array( + "Headline" => _("Quota"), + "Text" => _("Please enter the quota settings for this user. The syntax is: {mount point},{soft block limit},{hard block limit},{soft inode limit},{hard inode limit}.") + . ' ' . _('Multiple values are separated by semicolon.') + ) + ); + // profile elements + $profileContainer = new htmlTable(); + $profileContainer->addElement(new htmlTableExtendedInputField(_('Quota'), 'systemQuotas_quota', null, 'quota')); + $return['profile_options'] = $profileContainer; + // upload fields + $return['upload_columns'] = array( + array( + 'name' => 'systemQuotas_quota', + 'description' => _('Quota'), + 'help' => 'quota', + 'example' => '/home/smiller,50000,60000,10000,12000', + ) + ); + // available PDF fields + $return['PDF_fields'] = array( + 'quota' => _('Quota') + ); + return $return; + } + + /** + * This function fills the $messages variable with output messages from this module. + */ + public function load_Messages() { + $this->messages['path'][0] = array('ERROR', _('Mountpoint'), _('Mountpoint contains invalid characters.')); + $this->messages['path'][1] = array('ERROR', _('Account %s:'), _('Mountpoint contains invalid characters.')); + $this->messages['softblock'][0] = array('ERROR', _('Block soft quota'), _('Block soft quota contains invalid characters. Only natural numbers are allowed.')); + $this->messages['softblock'][1] = array('ERROR', _('Account %s:'), _('Block soft quota contains invalid characters. Only natural numbers are allowed.')); + $this->messages['hardblock'][0] = array('ERROR', _('Block hard quota'), _('Block hard quota contains invalid characters. Only natural numbers are allowed.')); + $this->messages['hardblock'][1] = array('ERROR', _('Account %s:'), _('Block hard quota contains invalid characters. Only natural numbers are allowed.')); + $this->messages['softinode'][0] = array('ERROR', _('Inode soft quota'), _('Inode soft quota contains invalid characters. Only natural numbers are allowed.')); + $this->messages['softinode'][1] = array('ERROR', _('Account %s:'), _('Inode soft quota contains invalid characters. Only natural numbers are allowed.')); + $this->messages['hardinode'][0] = array('ERROR', _('Inode hard quota'), _('Inode hard quota contains invalid characters. Only natural numbers are allowed.')); + $this->messages['hardinode'][1] = array('ERROR', _('Account %s:'), _('Inode hard quota contains invalid characters. Only natural numbers are allowed.')); + $this->messages['block_cmp'][0] = array('ERROR', _('Block quota'), _('Block soft quota must be smaller than block hard quota.')); + $this->messages['block_cmp'][1] = array('ERROR', _('Account %s:'), _('Block soft quota must be smaller than block hard quota.')); + $this->messages['inode_cmp'][0] = array('ERROR', _('Inode quota'), _('Inode soft quota must be smaller than inode hard quota.')); + $this->messages['inode_cmp'][1] = array('ERROR', _('Account %s:'), _('Inode soft quota must be smaller than inode hard quota.')); + } + + /** + * Returns the HTML meta data for the main account page. + * + * @return htmlElement HTML meta data + */ + public function display_html_attributes() { + $container = new htmlTable(); + $spacer = new htmlSpacer('10px', null); + // caption + $container->addElement(new htmlOutputText(_('Mountpoint'))); + $container->addElement($spacer); + $container->addElement(new htmlOutputText(_('Soft block limit'))); + $container->addElement($spacer); + $container->addElement(new htmlOutputText(_('Hard block limit'))); + $container->addElement($spacer); + $container->addElement(new htmlOutputText(_('Soft inode limit'))); + $container->addElement($spacer); + $container->addElement(new htmlOutputText(_('Hard inode limit')), true); + // existing entries + if (isset($this->attributes['quota'][0])) { + natcasesort($this->attributes['quota']); + $this->attributes['quota'] = array_values($this->attributes['quota']); + for ($i = 0; $i < sizeof($this->attributes['quota']); $i++) { + $parts = explode(',', $this->attributes['quota'][$i]); + $container->addElement(new htmlInputField('path_' . $i, $parts[0], 20)); + $container->addElement($spacer); + $container->addElement(new htmlInputField('softBlock_' . $i, $parts[1], 10)); + $container->addElement($spacer); + $container->addElement(new htmlInputField('hardBlock_' . $i, $parts[2], 10)); + $container->addElement($spacer); + $container->addElement(new htmlInputField('softInode_' . $i, $parts[3], 10)); + $container->addElement($spacer); + $container->addElement(new htmlInputField('hardInode_' . $i, $parts[4], 10)); + $container->addElement(new htmlButton('del_' . $i, 'del.png', true), true); + } + } + // new entry + $container->addElement(new htmlInputField('path', null, 20)); + $container->addElement($spacer); + $container->addElement(new htmlInputField('softBlock', 0, 10)); + $container->addElement($spacer); + $container->addElement(new htmlInputField('hardBlock', 0, 10)); + $container->addElement($spacer); + $container->addElement(new htmlInputField('softInode', 0, 10)); + $container->addElement($spacer); + $container->addElement(new htmlInputField('hardInode', 0, 10)); + $container->addElement(new htmlButton('add', 'add.png', true)); + return $container; + } + + /** + * Processes user input of the primary module page. + * It checks if all input values are correct and updates the associated LDAP attributes. + * + * @return array list of info/error messages + */ + public function process_attributes() { + $return = array(); + if (!isset($this->attributes['quota'][0])) { + $this->attributes['quota'] = array(); + } + // check existing entries + for ($i = 0; $i < sizeof($this->attributes['quota']); $i++) { + if (isset($_POST['del_' . $i])) { + unset($this->attributes['quota'][$i]); + $this->attributes['quota'] = array_values($this->attributes['quota']); + $i--; + continue; + } + $path = $_POST['path_' . $i]; + $softBlock = $_POST['softBlock_' . $i]; + if ($softBlock == '') $softBlock = '0'; + $hardBlock = $_POST['hardBlock_' . $i]; + if ($hardBlock == '') $hardBlock = '0'; + $softInode = $_POST['softInode_' . $i]; + if ($softInode == '') $softInode = '0'; + $hardInode = $_POST['hardInode_' . $i]; + if ($hardInode == '') $hardInode = '0'; + $this->attributes['quota'][$i] = $path . ',' . $softBlock . ',' . $hardBlock . ',' . + $softInode . ',' . $hardInode; + $return = array_merge($return, $this->checkQuota($path, $softBlock, $hardBlock, $softInode, $hardInode)); + } + // check for new entry + if (isset($_POST['add'])) { + $path = $_POST['path']; + $softBlock = $_POST['softBlock']; + if ($softBlock == '') $softBlock = '0'; + $hardBlock = $_POST['hardBlock']; + if ($hardBlock == '') $hardBlock = '0'; + $softInode = $_POST['softInode']; + if ($softInode == '') $softInode = '0'; + $hardInode = $_POST['hardInode']; + if ($hardInode == '') $hardInode = '0'; + $this->attributes['quota'][] = $path . ',' . $softBlock . ',' . $hardBlock . ',' . + $softInode . ',' . $hardInode; + $return = array_merge($return, $this->checkQuota($path, $softBlock, $hardBlock, $softInode, $hardInode)); + } + $this->attributes['quota'] = array_unique($this->attributes['quota']); + return $return; + } + + /** + * Checks if the quota parameters are valid. + * + * @param String $path mountpoint + * @param int $softBlock soft block limit + * @param int $hardBlock hard block limit + * @param int $softInode soft inode limit + * @param int $hardInode hard inode limit + * @param boolean $uploadIndex position is upload table + * @return array array where error messages are returned + */ + private function checkQuota($path, $softBlock, $hardBlock, $softInode, $hardInode, $uploadIndex = null) { + $return = array(); + if (!get_preg($path, 'filePath')) { + if ($uploadIndex == null) { + $return[] = $this->messages['path'][0]; + } + else { + $error = $this->messages['path'][1]; + $error[] = array($uploadIndex); + $return[] = $error; + } + } + if (!get_preg($softBlock, 'digit')) { + if ($uploadIndex == null) { + $return[] = $this->messages['softblock'][0]; + } + else { + $error = $this->messages['softblock'][1]; + $error[] = array($uploadIndex); + $return[] = $error; + } + } + if (!get_preg($hardBlock, 'digit')) { + if ($uploadIndex == null) { + $return[] = $this->messages['hardblock'][0]; + } + else { + $error = $this->messages['hardblock'][1]; + $error[] = array($uploadIndex); + $return[] = $error; + } + } + if (!get_preg($softInode, 'digit')) { + if ($uploadIndex == null) { + $return[] = $this->messages['softinode'][0]; + } + else { + $error = $this->messages['softinode'][1]; + $error[] = array($uploadIndex); + $return[] = $error; + } + } + if (!get_preg($hardInode, 'digit')) { + if ($uploadIndex == null) { + $return[] = $this->messages['hardinode'][0]; + } + else { + $error = $this->messages['hardinode'][1]; + $error[] = array($uploadIndex); + $return[] = $error; + } + } + if ($softBlock > $hardBlock) { + if ($uploadIndex == null) { + $return[] = $this->messages['block_cmp'][0]; + } + else { + $error = $this->messages['block_cmp'][1]; + $error[] = array($uploadIndex); + $return[] = $error; + } + } + if ($softInode > $hardInode) { + if ($uploadIndex == null) { + $return[] = $this->messages['inode_cmp'][0]; + } + else { + $error = $this->messages['inode_cmp'][1]; + $error[] = array($uploadIndex); + $return[] = $error; + } + } + return $return; + } + + /** + * Checks input values of account profiles. + * + * @param array $options a hash array (name => value) containing the options + * @return array list of error messages (array(type, title, text)) to generate StatusMessages, if any + */ + function check_profileOptions($options) { + $messages = parent::check_profileOptions($options); + $quotas = explode(';', $options['systemQuotas_quota'][0]); + for ($q = 0; $q < sizeof($quotas); $q++) { + $parts = explode(',', $quotas[$q]); + $messages = array_merge($messages, $this->checkQuota($parts[0], $parts[1], $parts[2], $parts[3], $parts[4])); + } + return $messages; + } + + /** + * Loads the values of an account profile into internal variables. + * + * @param array $profile hash array with profile values (identifier => value) + */ + function load_profile($profile) { + // profile mappings in meta data + parent::load_profile($profile); + if (isset($profile['systemQuotas_quota'][0]) && ($profile['systemQuotas_quota'][0] != '')) { + $this->attributes['quota'] = explode(';', $profile['systemQuotas_quota'][0]); + } + } + + /** + * In this function the LDAP account is built up. + * + * @param array $rawAccounts list of hash arrays (name => value) from user input + * @param array $partialAccounts list of hash arrays (name => value) which are later added to LDAP + * @param array $ids list of IDs for column position (e.g. "posixAccount_uid" => 5) + * @param array $selectedModules list of selected account modules + * @return array list of error messages if any + */ + public function build_uploadAccounts($rawAccounts, $ids, &$partialAccounts, $selectedModules) { + $messages = array(); + for ($i = 0; $i < sizeof($rawAccounts); $i++) { + // add object class + if (!in_array('systemQuotas', $partialAccounts[$i]['objectClass'])) $partialAccounts[$i]['objectClass'][] = 'systemQuotas'; + // add quota + if (isset($rawAccounts[$i][$ids['systemQuotas_quota']]) && ($rawAccounts[$i][$ids['systemQuotas_quota']] != '')) { + $quotas = explode(';', $rawAccounts[$i][$ids['systemQuotas_quota']]); + for ($q = 0; $q < sizeof($quotas); $q++) { + $parts = explode(',', $quotas[$q]); + $messages = array_merge($messages, $this->checkQuota($parts[0], $parts[1], $parts[2], $parts[3], $parts[4], $i)); + $partialAccounts[$i]['quota'][] = $quotas[$q]; + } + } + } + return $messages; + } + + /** + * Returns a list of PDF entries + */ + public function get_pdfEntries() { + $return = array(); + if (isset($this->attributes['quota'][0])) { + $quotas[] = '' . + '' . _('Mountpoint') . '' . + '' . _('Soft block') . '' . + '' . _('Hard block') . '' . + '' . _('Soft inode') . '' . + '' . _('Hard inode') . ''; + for ($i = 0; $i < sizeof($this->attributes['quota']); $i++) { + $parts = explode(',', $this->attributes['quota'][$i]); + $quotas[] = '' . + '' . $parts[0] . '' . + '' . $parts[1] . '' . + '' . $parts[2] . '' . + '' . $parts[3] . '' . + '' . $parts[4] . ''; + } + $return['systemQuotas_quota'] = $quotas; + } + return $return; + } + +} + + +?>