[389-ds-base] branch 389-ds-base-1.3.9 updated: Ticket 49958: extended search fail to match entries
by pagure@pagure.io
This is an automated email from the git hooks/post-receive script.
mreynolds pushed a commit to branch 389-ds-base-1.3.9
in repository 389-ds-base.
The following commit(s) were added to refs/heads/389-ds-base-1.3.9 by this push:
new 37f4f70 Ticket 49958: extended search fail to match entries
37f4f70 is described below
commit 37f4f70b1d3e6e5aad79b250a306bf89e3502320
Author: Thierry Bordaz <tbordaz(a)redhat.com>
AuthorDate: Mon Sep 24 14:14:16 2018 +0200
Ticket 49958: extended search fail to match entries
Bug Description:
During an extended search, a structure is created for each filter component.
The structure contains the keys generated from the assertion and using the given
matching rule indexer.
Later the keys will be compared (with the MR) with keys generated from the
attribute values of the candidate entries.
The bug is that parsing the assertion, instead of removing the heading spaces
the routine clear the assertion that is empty. So the generated keys is NULL.
Fix Description:
The fix consists to only remove heading spaces
https://pagure.io/389-ds-base/issue/49958
Reviewed by: Mark Reynolds
Platforms tested: F27
Flag Day: no
Doc impact: no
---
dirsrvtests/tests/suites/filter/filter_test.py | 200 +++++++++++++++++++++++++
ldap/servers/plugins/collation/orfilter.c | 4 +-
2 files changed, 201 insertions(+), 3 deletions(-)
diff --git a/dirsrvtests/tests/suites/filter/filter_test.py b/dirsrvtests/tests/suites/filter/filter_test.py
index 280db68..4d2dd30 100644
--- a/dirsrvtests/tests/suites/filter/filter_test.py
+++ b/dirsrvtests/tests/suites/filter/filter_test.py
@@ -82,6 +82,206 @@ def test_filter_search_original_attrs(topology_st):
log.info('test_filter_search_original_attrs: PASSED')
+(a)pytest.mark.bz1511462
+def test_filter_scope_one(topology_st):
+ """Test ldapsearch with scope one gives only single entry
+
+ :id: cf5a6078-bbe6-4d43-ac71-553c45923f91
+ :setup: Standalone instance
+ :steps:
+ 1. Search cn=Directory Administrators,dc=example,dc=com using ldapsearch with
+ scope one using base as dc=example,dc=com
+ 2. Check that search should return only one entry
+ :expectedresults:
+ 1. This should pass
+ 2. This should pass
+ """
+
+ parent_dn="dn: dc=example,dc=com"
+ child_dn="dn: cn=Directory Administrators,dc=example,dc=com"
+
+ log.info('Search user using ldapsearch with scope one')
+ results = topology_st.standalone.search_s(DEFAULT_SUFFIX, ldap.SCOPE_ONELEVEL,'cn=Directory Administrators',['cn'] )
+ log.info(results)
+
+ log.info('Search should only have one entry')
+ assert len(results) == 1
+
+(a)pytest.mark.ds47313
+def test_filter_with_attribute_subtype(topology_st):
+ """Adds 2 test entries and Search with
+ filters including subtype and !
+
+ :id: 0e69f5f2-6a0a-480e-8282-fbcc50231908
+ :setup: Standalone instance
+ :steps:
+ 1. Add 2 entries and create 3 filters
+ 2. Search for entry with filter: (&(cn=test_entry en only)(!(cn=test_entry fr)))
+ 3. Search for entry with filter: (&(cn=test_entry en only)(!(cn;fr=test_entry fr)))
+ 4. Search for entry with filter: (&(cn=test_entry en only)(!(cn;en=test_entry en)))
+ 5. Delete the added entries
+ :expectedresults:
+ 1. Operation should be successful
+ 2. Search should be successful
+ 3. Search should be successful
+ 4. Search should not be successful
+ 5. Delete the added entries
+ """
+
+ # bind as directory manager
+ topology_st.standalone.log.info("Bind as %s" % DN_DM)
+ topology_st.standalone.simple_bind_s(DN_DM, PASSWORD)
+
+ # enable filter error logging
+ # mod = [(ldap.MOD_REPLACE, 'nsslapd-errorlog-level', '32')]
+ # topology_st.standalone.modify_s(DN_CONFIG, mod)
+
+ topology_st.standalone.log.info("\n\n######################### ADD ######################\n")
+
+ # Prepare the entry with cn;fr & cn;en
+ entry_name_fr = '%s fr' % (ENTRY_NAME)
+ entry_name_en = '%s en' % (ENTRY_NAME)
+ entry_name_both = '%s both' % (ENTRY_NAME)
+ entry_dn_both = 'cn=%s, %s' % (entry_name_both, SUFFIX)
+ entry_both = Entry(entry_dn_both)
+ entry_both.setValues('objectclass', 'top', 'person')
+ entry_both.setValues('sn', entry_name_both)
+ entry_both.setValues('cn', entry_name_both)
+ entry_both.setValues('cn;fr', entry_name_fr)
+ entry_both.setValues('cn;en', entry_name_en)
+
+ # Prepare the entry with one member
+ entry_name_en_only = '%s en only' % (ENTRY_NAME)
+ entry_dn_en_only = 'cn=%s, %s' % (entry_name_en_only, SUFFIX)
+ entry_en_only = Entry(entry_dn_en_only)
+ entry_en_only.setValues('objectclass', 'top', 'person')
+ entry_en_only.setValues('sn', entry_name_en_only)
+ entry_en_only.setValues('cn', entry_name_en_only)
+ entry_en_only.setValues('cn;en', entry_name_en)
+
+ topology_st.standalone.log.info("Try to add Add %s: %r" % (entry_dn_both, entry_both))
+ topology_st.standalone.add_s(entry_both)
+
+ topology_st.standalone.log.info("Try to add Add %s: %r" % (entry_dn_en_only, entry_en_only))
+ topology_st.standalone.add_s(entry_en_only)
+
+ topology_st.standalone.log.info("\n\n######################### SEARCH ######################\n")
+
+ # filter: (&(cn=test_entry en only)(!(cn=test_entry fr)))
+ myfilter = '(&(sn=%s)(!(cn=%s)))' % (entry_name_en_only, entry_name_fr)
+ topology_st.standalone.log.info("Try to search with filter %s" % myfilter)
+ ents = topology_st.standalone.search_s(SUFFIX, ldap.SCOPE_SUBTREE, myfilter)
+ assert len(ents) == 1
+ assert ensure_str(ents[0].sn) == entry_name_en_only
+ topology_st.standalone.log.info("Found %s" % ents[0].dn)
+
+ # filter: (&(cn=test_entry en only)(!(cn;fr=test_entry fr)))
+ myfilter = '(&(sn=%s)(!(cn;fr=%s)))' % (entry_name_en_only, entry_name_fr)
+ topology_st.standalone.log.info("Try to search with filter %s" % myfilter)
+ ents = topology_st.standalone.search_s(SUFFIX, ldap.SCOPE_SUBTREE, myfilter)
+ assert len(ents) == 1
+ assert ensure_str(ents[0].sn) == entry_name_en_only
+ topology_st.standalone.log.info("Found %s" % ents[0].dn)
+
+ # filter: (&(cn=test_entry en only)(!(cn;en=test_entry en)))
+ myfilter = '(&(sn=%s)(!(cn;en=%s)))' % (entry_name_en_only, entry_name_en)
+ topology_st.standalone.log.info("Try to search with filter %s" % myfilter)
+ ents = topology_st.standalone.search_s(SUFFIX, ldap.SCOPE_SUBTREE, myfilter)
+ assert len(ents) == 0
+ topology_st.standalone.log.info("Found none")
+
+ topology_st.standalone.log.info("\n\n######################### DELETE ######################\n")
+
+ topology_st.standalone.log.info("Try to delete %s " % entry_dn_both)
+ topology_st.standalone.delete_s(entry_dn_both)
+
+ topology_st.standalone.log.info("Try to delete %s " % entry_dn_en_only)
+ topology_st.standalone.delete_s(entry_dn_en_only)
+
+ log.info('Testcase PASSED')
+
+(a)pytest.mark.bz1615155
+def test_extended_search(topology_st):
+ """Test we can search with equality extended matching rule
+
+ :id:
+ :setup: Standalone instance
+ :steps:
+ 1. Add a test user with 'sn: ext-test-entry'
+ 2. Search '(cn:de:=ext-test-entry)'
+ 3. Search '(sn:caseIgnoreIA5Match:=EXT-TEST-ENTRY)'
+ 4. Search '(sn:caseIgnoreMatch:=EXT-TEST-ENTRY)'
+ 5. Search '(sn:caseExactMatch:=EXT-TEST-ENTRY)'
+ 6. Search '(sn:caseExactMatch:=ext-test-entry)'
+ 7. Search '(sn:caseExactIA5Match:=EXT-TEST-ENTRY)'
+ 8. Search '(sn:caseExactIA5Match:=ext-test-entry)'
+ :expectedresults:
+ 1. This should pass
+ 2. This should return one entry
+ 3. This should return one entry
+ 4. This should return one entry
+ 5. This should return NO entry
+ 6. This should return one entry
+ 7. This should return NO entry
+ 8. This should return one entry
+ 3. return one entry
+ """
+ log.info('Running test_filter_escaped...')
+
+ ATTR_VAL = 'ext-test-entry'
+ USER1_DN = "uid=%s,%s" % (ATTR_VAL, DEFAULT_SUFFIX)
+
+ try:
+ topology_st.standalone.add_s(Entry((USER1_DN, {'objectclass': "top extensibleObject".split(),
+ 'sn': ATTR_VAL.encode(),
+ 'cn': ATTR_VAL.encode(),
+ 'uid': ATTR_VAL.encode()})))
+ except ldap.LDAPError as e:
+ log.fatal('test_extended_search: Failed to add test user ' + USER1_DN + ': error ' +
+ e.message['desc'])
+ assert False
+
+ # filter: '(cn:de:=ext-test-entry)'
+ myfilter = '(cn:de:=%s)' % ATTR_VAL
+ topology_st.standalone.log.info("Try to search with filter %s" % myfilter)
+ ents = topology_st.standalone.search_s(SUFFIX, ldap.SCOPE_SUBTREE, myfilter)
+ assert len(ents) == 1
+
+ # filter: '(sn:caseIgnoreIA5Match:=EXT-TEST-ENTRY)'
+ myfilter = '(cn:caseIgnoreIA5Match:=%s)' % ATTR_VAL.upper()
+ topology_st.standalone.log.info("Try to search with filter %s" % myfilter)
+ ents = topology_st.standalone.search_s(SUFFIX, ldap.SCOPE_SUBTREE, myfilter)
+ assert len(ents) == 1
+
+ # filter: '(sn:caseIgnoreMatch:=EXT-TEST-ENTRY)'
+ myfilter = '(cn:caseIgnoreMatch:=%s)' % ATTR_VAL.upper()
+ topology_st.standalone.log.info("Try to search with filter %s" % myfilter)
+ ents = topology_st.standalone.search_s(SUFFIX, ldap.SCOPE_SUBTREE, myfilter)
+ assert len(ents) == 1
+
+ # filter: '(sn:caseExactMatch:=EXT-TEST-ENTRY)'
+ myfilter = '(cn:caseExactMatch:=%s)' % ATTR_VAL.upper()
+ topology_st.standalone.log.info("Try to search with filter %s" % myfilter)
+ ents = topology_st.standalone.search_s(SUFFIX, ldap.SCOPE_SUBTREE, myfilter)
+ assert len(ents) == 0
+
+ # filter: '(sn:caseExactMatch:=ext-test-entry)'
+ myfilter = '(cn:caseExactMatch:=%s)' % ATTR_VAL
+ topology_st.standalone.log.info("Try to search with filter %s" % myfilter)
+ ents = topology_st.standalone.search_s(SUFFIX, ldap.SCOPE_SUBTREE, myfilter)
+ assert len(ents) == 1
+
+ # filter: '(sn:caseExactIA5Match:=EXT-TEST-ENTRY)'
+ myfilter = '(cn:caseExactIA5Match:=%s)' % ATTR_VAL.upper()
+ topology_st.standalone.log.info("Try to search with filter %s" % myfilter)
+ ents = topology_st.standalone.search_s(SUFFIX, ldap.SCOPE_SUBTREE, myfilter)
+ assert len(ents) == 0
+
+ # filter: '(sn:caseExactIA5Match:=ext-test-entry)'
+ myfilter = '(cn:caseExactIA5Match:=%s)' % ATTR_VAL
+ topology_st.standalone.log.info("Try to search with filter %s" % myfilter)
+ ents = topology_st.standalone.search_s(SUFFIX, ldap.SCOPE_SUBTREE, myfilter)
+ assert len(ents) == 1
if __name__ == '__main__':
# Run isolated
diff --git a/ldap/servers/plugins/collation/orfilter.c b/ldap/servers/plugins/collation/orfilter.c
index 7705de9..c092d77 100644
--- a/ldap/servers/plugins/collation/orfilter.c
+++ b/ldap/servers/plugins/collation/orfilter.c
@@ -531,10 +531,8 @@ or_filter_create(Slapi_PBlock *pb)
default:
break;
}
- for (; len > 0 && *val != ' '; ++val, --len)
+ for (; len > 0 && *val == ' '; ++val, --len)
;
- if (len > 0)
- ++val, --len; /* skip the space */
bv.bv_len = len;
bv.bv_val = (len > 0) ? val : NULL;
} else { /* mrOID does not identify an ordering rule. */
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
4 years, 1 month
[389-ds-base] branch 389-ds-base-1.4.0 updated: Ticket 50340 - structs for diabled plugins will not be freed
by pagure@pagure.io
This is an automated email from the git hooks/post-receive script.
lkrispen pushed a commit to branch 389-ds-base-1.4.0
in repository 389-ds-base.
The following commit(s) were added to refs/heads/389-ds-base-1.4.0 by this push:
new dd14c53 Ticket 50340 - structs for diabled plugins will not be freed
dd14c53 is described below
commit dd14c53cc12a4347cfb765a30854decae8f38499
Author: Ludwig Krispenz <lkrispen(a)redhat.com>
AuthorDate: Thu Apr 25 10:39:17 2019 +0200
Ticket 50340 - structs for diabled plugins will not be freed
Bug: when plugins are loaded from dse.ldif enabled plugins will be added to
the list of the plugin type and freed when plugins are stopped.
But the memory allocated for disabled plugins will remain allocated and
and be reported.
Fix: free plugin if not enabled after loading
This will alos let the many leaks reported for "GrowStuff" disappear.
The fix also contains one missing free for slapi_ch_smprintf allocated memory
Reviewed by: Mark, thanks
---
ldap/servers/slapd/ldaputil.c | 1 +
ldap/servers/slapd/plugin.c | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/ldap/servers/slapd/ldaputil.c b/ldap/servers/slapd/ldaputil.c
index a215e90..082250d 100644
--- a/ldap/servers/slapd/ldaputil.c
+++ b/ldap/servers/slapd/ldaputil.c
@@ -779,6 +779,7 @@ slapi_ldap_init_ext(
if (PR_SUCCESS != PR_CallOnce(&ol_init_callOnce, internal_ol_init_init)) {
slapi_log_err(SLAPI_LOG_ERR, "slapi_ldap_init_ext",
"Could not perform internal ol_init init\n");
+ slapi_ch_free_string(&makeurl);
rc = -1;
goto done;
}
diff --git a/ldap/servers/slapd/plugin.c b/ldap/servers/slapd/plugin.c
index 9e1c1b4..601a012 100644
--- a/ldap/servers/slapd/plugin.c
+++ b/ldap/servers/slapd/plugin.c
@@ -3040,7 +3040,7 @@ plugin_setup(Slapi_Entry *plugin_entry, struct slapi_componentid *group, slapi_p
}
PLUGIN_CLEANUP:
- if (status) {
+ if (status || !enabled) {
plugin_free(plugin);
}
slapi_ch_free((void **)&configdir);
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
4 years, 1 month
[389-ds-base] branch 389-ds-base-1.4.0 updated: Ticket 50327 - Add replication conflict support to UI
by pagure@pagure.io
This is an automated email from the git hooks/post-receive script.
mreynolds pushed a commit to branch 389-ds-base-1.4.0
in repository 389-ds-base.
The following commit(s) were added to refs/heads/389-ds-base-1.4.0 by this push:
new 4f710b3 Ticket 50327 - Add replication conflict support to UI
4f710b3 is described below
commit 4f710b35ca5e29ad32ef49f3faa3086a58ba6bf7
Author: Mark Reynolds <mreynolds(a)redhat.com>
AuthorDate: Fri Apr 19 16:50:36 2019 -0400
Ticket 50327 - Add replication conflict support to UI
Description: Added a page under the monitor tab to view and management
replication conflict and glue entries.
https://pagure.io/389-ds-base/issue/50327
Reviewed by: spichugi(Thanks!)
(cherry picked from commit 21e10bd59dc6c3094337c8340a28a588ddd7cfa4)
---
src/cockpit/389-console/src/css/ds.css | 26 +-
.../389-console/src/lib/database/chaining.jsx | 2 +-
.../389-console/src/lib/monitor/monitorModals.jsx | 178 +++++++++
.../389-console/src/lib/monitor/monitorTables.jsx | 391 +++++++++++++++++++-
.../389-console/src/lib/monitor/replMonitor.jsx | 406 ++++++++++++++++++++-
src/cockpit/389-console/src/monitor.jsx | 50 +++
src/lib389/lib389/conflicts.py | 13 +-
7 files changed, 1047 insertions(+), 19 deletions(-)
diff --git a/src/cockpit/389-console/src/css/ds.css b/src/cockpit/389-console/src/css/ds.css
index e0ceeb8..1ad8d5c 100644
--- a/src/cockpit/389-console/src/css/ds.css
+++ b/src/cockpit/389-console/src/css/ds.css
@@ -701,8 +701,9 @@ td {
width: 450px;
}
-.ds-modal-wide {
- min-width: 850px;
+.ds-modal-wide .modal-content{
+ width: 850px !important;
+ min-width: 850px !important;
vertical-align: middle;
}
@@ -1025,8 +1026,9 @@ td {
line-height: 0;
}
-.ds-accordian-div {
+.ds-modal-row {
margin-left: 20px;
+ margin-right: 0px !important;
}
.ds-chaining-list {
@@ -1105,6 +1107,24 @@ textarea {
overflow-y: scroll;
}
+.ds-conflict {
+ margin-top: 5px;
+ padding-top: 5px;
+ vertical-align: top;
+ width: 375px;
+ height: 275px;
+ max-height: 350px !important;
+ white-space: pre;
+ line-height: 1.5;
+ font-family: monospace !important;
+ overflow-y: auto;
+ overflow-x: scroll;
+}
+
+.ds-conflict-btn {
+ width: 110px;
+}
+
option {
color: #181818;
}
diff --git a/src/cockpit/389-console/src/lib/database/chaining.jsx b/src/cockpit/389-console/src/lib/database/chaining.jsx
index f20fca6..3dd3ec4 100644
--- a/src/cockpit/389-console/src/lib/database/chaining.jsx
+++ b/src/cockpit/389-console/src/lib/database/chaining.jsx
@@ -1041,7 +1041,7 @@ export class ChainingConfig extends React.Component {
<CustomCollapse>
<div className="ds-accordion-panel">
- <div className="ds-accordian-div">
+ <div className="ds-margin-left">
<div className="ds-container">
<div className="ds-inline">
<div>
diff --git a/src/cockpit/389-console/src/lib/monitor/monitorModals.jsx b/src/cockpit/389-console/src/lib/monitor/monitorModals.jsx
index a63c67a..5593cb0 100644
--- a/src/cockpit/389-console/src/lib/monitor/monitorModals.jsx
+++ b/src/cockpit/389-console/src/lib/monitor/monitorModals.jsx
@@ -491,6 +491,165 @@ class WinsyncAgmtDetailsModal extends React.Component {
}
}
+class ConflictCompareModal extends React.Component {
+ render() {
+ const {
+ showModal,
+ conflictEntry,
+ validEntry,
+ swapFunc,
+ convertFunc,
+ deleteFunc,
+ handleConvertChange,
+ closeHandler,
+ } = this.props;
+
+ let ignoreAttrs = ['createtimestamp', 'creatorsname', 'modifytimestamp',
+ 'modifiersname', 'entryid', 'entrydn', 'parentid', 'numsubordinates'];
+ let conflict = "dn: " + conflictEntry.dn + "\n";
+ let valid = "dn: " + validEntry.dn + "\n";
+ let conflictChildren = "0";
+ let validChildren = "0";
+
+ for (const key in conflictEntry.attrs) {
+ if (key == "numsubordinates") {
+ conflictChildren = conflictEntry.attrs[key];
+ }
+ if (!ignoreAttrs.includes(key)) {
+ for (let attr of conflictEntry.attrs[key]) {
+ conflict += key + ": " + attr + "\n";
+ }
+ }
+ }
+ for (const key in validEntry.attrs) {
+ if (key == "numsubordinates") {
+ validChildren = <font color="red">{validEntry.attrs[key]}</font>;
+ }
+ if (!ignoreAttrs.includes(key)) {
+ for (let attr of validEntry.attrs[key]) {
+ valid += key + ": " + attr + "\n";
+ }
+ }
+ }
+
+ return (
+ <Modal show={showModal} className="ds-modal-wide" onHide={closeHandler}>
+ <div className="ds-no-horizontal-scrollbar">
+ <Modal.Header>
+ <button
+ className="close"
+ onClick={closeHandler}
+ aria-hidden="true"
+ aria-label="Close"
+ >
+ <Icon type="pf" name="close" />
+ </button>
+ <Modal.Title>
+ Resolve Replication Conflicts
+ </Modal.Title>
+ </Modal.Header>
+ <Modal.Body>
+ <Form horizontal autoComplete="off">
+ <div className="ds-modal-row">
+ <Row>
+ <Col sm={5}>
+ <Row>
+ <h3>Conflict Entry</h3>
+ </Row>
+ <Row>
+ <textarea className="ds-conflict" value={conflict} readOnly />
+ </Row>
+ <p />
+ <Row>
+ <p>Child Entries: <b>{conflictChildren}</b></p>
+ </Row>
+ </Col>
+ <Col sm={1} />
+ <Col sm={5}>
+ <Row>
+ <h3>Valid Entry</h3>
+ </Row>
+ <Row>
+ <textarea className="ds-conflict" value={valid} readOnly />
+ </Row>
+ <p />
+ <Row>
+ <p>Child Entries: <b>{validChildren}</b></p>
+ </Row>
+ </Col>
+ </Row>
+ <hr />
+ <Row>
+ <h4>You can convert the <b>Conflict Entry</b> into a new valid entry by providing a new RDN value below, like "<i>cn=NEW_RDN</i>"</h4>
+ </Row>
+ <Row>
+ <Col sm={2}>
+ <Button
+ bsStyle="primary"
+ className="ds-conflict-btn"
+ onClick={() => {
+ convertFunc(conflictEntry.dn);
+ }}
+ >
+ Convert Conflict
+ </Button>
+ </Col>
+ <Col sm={4}>
+ <input onChange={handleConvertChange} type="text" placeholder="Enter new RDN here" size="30" />
+ </Col>
+ </Row>
+ <p />
+ <Row>
+ <h4>Or, you can replace, or swap, the <b>Valid Entry</b> (and its child entries) with the <b>Conflict Entry</b></h4>
+ </Row>
+ <Row>
+ <Col sm={3}>
+ <Button
+ bsStyle="primary"
+ className="ds-conflict-btn"
+ onClick={() => {
+ swapFunc(conflictEntry.dn);
+ }}
+ >
+ Swap Entries
+ </Button>
+ </Col>
+ </Row>
+ <p />
+ <Row>
+ <h4>Or, you can delete the <b>Conflict Entry</b></h4>
+ </Row>
+ <Row>
+ <Col sm={3}>
+ <Button
+ bsStyle="primary"
+ className="ds-conflict-btn"
+ onClick={() => {
+ deleteFunc(conflictEntry.dn);
+ }}
+ >
+ Delete Conflict
+ </Button>
+ </Col>
+ </Row>
+ </div>
+ </Form>
+ </Modal.Body>
+ <Modal.Footer>
+ <Button
+ bsStyle="default"
+ className="btn-cancel"
+ onClick={closeHandler}
+ >
+ Close
+ </Button>
+ </Modal.Footer>
+ </div>
+ </Modal>
+ );
+ }
+}
+
// Prototypes and defaultProps
AgmtDetailsModal.propTypes = {
showModal: PropTypes.bool,
@@ -550,10 +709,29 @@ ReplLoginModal.defaultProps = {
error: {},
};
+ConflictCompareModal.propTypes = {
+ showModal: PropTypes.bool,
+ conflictEntry: PropTypes.object,
+ validEntry: PropTypes.object,
+ swapFunc: PropTypes.func,
+ convertFunc: PropTypes.func,
+ closeHandler: PropTypes.func,
+};
+
+ConflictCompareModal.defaultProps = {
+ showModal: false,
+ conflictEntry: {},
+ validEntry: {},
+ swapFunc: noop,
+ convertFunc: noop,
+ closeHandler: noop,
+};
+
export {
TaskLogModal,
AgmtDetailsModal,
ReplLagReportModal,
ReplLoginModal,
WinsyncAgmtDetailsModal,
+ ConflictCompareModal,
};
diff --git a/src/cockpit/389-console/src/lib/monitor/monitorTables.jsx b/src/cockpit/389-console/src/lib/monitor/monitorTables.jsx
index 592e514..c401196 100644
--- a/src/cockpit/389-console/src/lib/monitor/monitorTables.jsx
+++ b/src/cockpit/389-console/src/lib/monitor/monitorTables.jsx
@@ -1218,6 +1218,372 @@ class LagReportTable extends React.Component {
}
}
+class GlueTable extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ fieldsToSearch: ["dn", "created"],
+ rowKey: "dn",
+ columns: [
+ {
+ property: "dn",
+ header: {
+ label: "Glue Entry",
+ props: {
+ index: 0,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 0
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "desc",
+ header: {
+ label: "Conflict Description",
+ props: {
+ index: 1,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 1
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "created",
+ header: {
+ label: "Created",
+ props: {
+ index: 2,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 2
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+
+ {
+ property: "actions",
+ header: {
+ props: {
+ index: 3,
+ rowSpan: 1,
+ colSpan: 1
+ },
+ formatters: [actionHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 3
+ },
+ formatters: [
+ (value, { rowData }) => {
+ return [
+ <td key={rowData.dn[0]}>
+ <DropdownButton id={rowData.dn[0]}
+ bsStyle="default" title="Actions">
+ <MenuItem eventKey="1" onClick={() => {
+ this.props.convertGlue(rowData.dn[0]);
+ }}>
+ Convert Glue Entry
+ </MenuItem>
+ <MenuItem eventKey="2" onClick={() => {
+ this.props.deleteGlue(rowData.dn[0]);
+ }}>
+ Delete Glue Entry
+ </MenuItem>
+ </DropdownButton>
+ </td>
+ ];
+ }
+ ]
+ }
+ }
+ ]
+ };
+ this.getColumns = this.getColumns.bind(this);
+ this.getSingleColumn = this.getSingleColumn.bind(this);
+ } // Constructor
+
+ getColumns() {
+ return this.state.columns;
+ }
+
+ getSingleColumn () {
+ return [
+ {
+ property: "msg",
+ header: {
+ label: "Replication Glue Entries",
+ props: {
+ index: 0,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 0
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ ];
+ }
+
+ render() {
+ let glueTable;
+ if (this.props.glues.length < 1) {
+ glueTable =
+ <DSTable
+ noSearchBar
+ getColumns={this.getSingleColumn}
+ rowKey={"msg"}
+ rows={[{msg: "No glue entries"}]}
+ />;
+ } else {
+ let rows = [];
+ for (let glue of this.props.glues) {
+ rows.push({
+ dn: [glue.dn],
+ desc: glue.attrs.nsds5replconflict,
+ created: [get_date_string(glue.attrs.createtimestamp[0])],
+ });
+ }
+
+ glueTable =
+ <DSTable
+ getColumns={this.getColumns}
+ fieldsToSearch={this.state.fieldsToSearch}
+ toolBarSearchField={this.state.searchField}
+ rowKey={this.state.rowKey}
+ rows={rows}
+ disableLoadingSpinner
+ toolBarPagination={[6, 12, 24, 48, 96]}
+ toolBarPaginationPerPage={6}
+ />;
+ }
+
+ return (
+ <div className="ds-margin-top-xlg">
+ {glueTable}
+ </div>
+ );
+ }
+}
+
+class ConflictTable extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ fieldsToSearch: ["dn", "desc"],
+ rowKey: "dn",
+ columns: [
+ {
+ property: "dn",
+ header: {
+ label: "Conflict DN",
+ props: {
+ index: 0,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 0
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "desc",
+ header: {
+ label: "Conflict Description",
+ props: {
+ index: 1,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 1
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "created",
+ header: {
+ label: "Created",
+ props: {
+ index: 2,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 2
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+
+ {
+ property: "actions",
+ header: {
+ props: {
+ index: 3,
+ rowSpan: 1,
+ colSpan: 1
+ },
+ formatters: [actionHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 3
+ },
+ formatters: [
+ (value, { rowData }) => {
+ return [
+ <td key={rowData.dn[0]}>
+ <Button
+ onClick={() => {
+ this.props.resolveConflict(rowData.dn[0]);
+ }}
+ >
+ Resolve
+ </Button>
+ </td>
+ ];
+ }
+ ]
+ }
+ }
+ ]
+ };
+ this.getColumns = this.getColumns.bind(this);
+ this.getSingleColumn = this.getSingleColumn.bind(this);
+ } // Constructor
+
+ getColumns() {
+ return this.state.columns;
+ }
+
+ getSingleColumn () {
+ return [
+ {
+ property: "msg",
+ header: {
+ label: "Replication Conflict Entries",
+ props: {
+ index: 0,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 0
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ ];
+ }
+
+ render() {
+ let conflictTable;
+ if (this.props.conflicts.length < 1) {
+ conflictTable =
+ <DSTable
+ noSearchBar
+ getColumns={this.getSingleColumn}
+ rowKey={"msg"}
+ rows={[{msg: "No conflict entries"}]}
+ />;
+ } else {
+ let rows = [];
+ for (let conflict of this.props.conflicts) {
+ rows.push({
+ dn: [conflict.dn],
+ desc: conflict.attrs.nsds5replconflict,
+ created: [get_date_string(conflict.attrs.createtimestamp[0])],
+ });
+ }
+
+ conflictTable =
+ <DSTable
+ getColumns={this.getColumns}
+ fieldsToSearch={this.state.fieldsToSearch}
+ toolBarSearchField={this.state.searchField}
+ rowKey={this.state.rowKey}
+ rows={rows}
+ disableLoadingSpinner
+ toolBarPagination={[6, 12, 24, 48, 96]}
+ toolBarPaginationPerPage={6}
+ />;
+ }
+
+ return (
+ <div className="ds-margin-top-xlg">
+ {conflictTable}
+ </div>
+ );
+ }
+}
+
// Proptypes and defaults
LagReportTable.propTypes = {
@@ -1244,7 +1610,6 @@ AgmtTable.defaultProps = {
pokeAgmt: noop
};
-// Proptyes and defaults
WinsyncAgmtTable.propTypes = {
agmts: PropTypes.array,
viewAgmt: PropTypes.func,
@@ -1285,6 +1650,28 @@ AbortCleanALLRUVTable.defaultProps = {
viewLog: PropTypes.func,
};
+ConflictTable.propTypes = {
+ conflicts: PropTypes.array,
+ resolveConflict: PropTypes.func,
+};
+
+ConflictTable.defaultProps = {
+ conflicts: [],
+ resolveConflict: noop,
+};
+
+GlueTable.propTypes = {
+ glues: PropTypes.array,
+ convertGlue: PropTypes.func,
+ deleteGlue: PropTypes.func,
+};
+
+GlueTable.defaultProps = {
+ glues: PropTypes.array,
+ convertGlue: noop,
+ deleteGlue: noop,
+};
+
export {
ConnectionTable,
AgmtTable,
@@ -1292,4 +1679,6 @@ export {
LagReportTable,
CleanALLRUVTable,
AbortCleanALLRUVTable,
+ ConflictTable,
+ GlueTable,
};
diff --git a/src/cockpit/389-console/src/lib/monitor/replMonitor.jsx b/src/cockpit/389-console/src/lib/monitor/replMonitor.jsx
index a742a0d..ca2e354 100644
--- a/src/cockpit/389-console/src/lib/monitor/replMonitor.jsx
+++ b/src/cockpit/389-console/src/lib/monitor/replMonitor.jsx
@@ -8,7 +8,9 @@ import {
AgmtTable,
WinsyncAgmtTable,
CleanALLRUVTable,
- AbortCleanALLRUVTable
+ AbortCleanALLRUVTable,
+ ConflictTable,
+ GlueTable,
} from "./monitorTables.jsx";
import {
TaskLogModal,
@@ -16,6 +18,7 @@ import {
WinsyncAgmtDetailsModal,
ReplLagReportModal,
ReplLoginModal,
+ ConflictCompareModal,
} from "./monitorModals.jsx";
import {
Nav,
@@ -41,9 +44,18 @@ export class ReplMonitor extends React.Component {
showInitConfirm: false,
showLoginModal: false,
showLagReport: false,
+ showCompareModal: false,
+ showConfirmDeleteGlue: false,
+ showConfirmConvertGlue: false,
+ showConfirmSwapConflict: false,
+ showConfirmConvertConflict: false,
+ showConfirmDeleteConflict: false,
reportLoading: false,
lagAgmts: [],
agmt: "",
+ convertRDN: "",
+ glueEntry: "",
+ conflictEntry: "",
binddn: "cn=Directory Manager",
bindpw: "",
errObj: {}
@@ -70,6 +82,253 @@ export class ReplMonitor extends React.Component {
this.closeLogModal = this.closeLogModal.bind(this);
this.handleLoginModal = this.handleLoginModal.bind(this);
this.closeLoginModal = this.closeLoginModal.bind(this);
+ // Conflict entry functions
+ this.convertConflict = this.convertConflict.bind(this);
+ this.swapConflict = this.swapConflict.bind(this);
+ this.deleteConflict = this.deleteConflict.bind(this);
+ this.resolveConflict = this.resolveConflict.bind(this);
+ this.convertGlue = this.convertGlue.bind(this);
+ this.deleteGlue = this.deleteGlue.bind(this);
+ this.closeCompareModal = this.closeCompareModal.bind(this);
+ this.confirmDeleteGlue = this.confirmDeleteGlue.bind(this);
+ this.confirmConvertGlue = this.confirmConvertGlue.bind(this);
+ this.closeConfirmDeleteGlue = this.closeConfirmDeleteGlue.bind(this);
+ this.closeConfirmConvertGlue = this.closeConfirmConvertGlue.bind(this);
+ this.handleConvertChange = this.handleConvertChange.bind(this);
+
+ this.confirmDeleteConflict = this.confirmDeleteConflict.bind(this);
+ this.confirmConvertConflict = this.confirmConvertConflict.bind(this);
+ this.confirmSwapConflict = this.confirmSwapConflict.bind(this);
+
+ this.closeConfirmDeleteConflict = this.closeConfirmDeleteConflict.bind(this);
+ this.closeConfirmConvertConflict = this.closeConfirmConvertConflict.bind(this);
+ this.closeConfirmSwapConflict = this.closeConfirmSwapConflict.bind(this);
+ }
+
+ convertConflict (dn) {
+ let cmd = ["dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "repl-conflict", "convert", dn, "--new-rdn=" + this.state.convertRDN];
+ log_cmd("convertConflict", "convert conflict entry", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ this.props.reloadConflicts();
+ this.props.addNotification(
+ "success",
+ `Replication conflict entry was converted into a valid entry`
+ );
+ this.setState({
+ showCompareModal: false,
+ convertRDN: ""
+ });
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.props.addNotification(
+ "error",
+ `Failed to convert conflict entry entry: ${dn} - ${errMsg.desc}`
+ );
+ });
+ }
+
+ swapConflict (dn) {
+ let cmd = ["dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "repl-conflict", "swap", dn];
+ log_cmd("swapConflict", "swap in conflict entry", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ this.props.reloadConflicts();
+ this.props.addNotification(
+ "success",
+ `Replication Conflict Entry is now the Valid Entry`
+ );
+ this.setState({
+ showCompareModal: false,
+ });
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.props.addNotification(
+ "error",
+ `Failed to swap in conflict entry: ${dn} - ${errMsg.desc}`
+ );
+ });
+ }
+
+ deleteConflict (dn) {
+ let cmd = ["dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "repl-conflict", "delete", dn];
+ log_cmd("deleteConflict", "Delete conflict entry", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ this.props.reloadConflicts();
+ this.props.addNotification(
+ "success",
+ `Replication conflict entry was deleted`
+ );
+ this.setState({
+ showCompareModal: false,
+ });
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.props.addNotification(
+ "error",
+ `Failed to delete conflict entry: ${dn} - ${errMsg.desc}`
+ );
+ });
+ }
+
+ resolveConflict (dn) {
+ let cmd = ["dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "repl-conflict", "compare", dn];
+ log_cmd("resolveConflict", "Compare conflict entry with valid entry", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ let entries = JSON.parse(content);
+ this.setState({
+ cmpConflictEntry: entries.items[0],
+ cmpValidEntry: entries.items[1],
+ showCompareModal: true,
+ });
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.props.addNotification(
+ "error",
+ `Failed to get conflict entries: ${dn} - ${errMsg.desc}`
+ );
+ });
+ }
+
+ confirmConvertGlue (dn) {
+ this.setState({
+ showConfirmConvertGlue: true,
+ glueEntry: dn
+ });
+ }
+
+ closeConfirmConvertGlue () {
+ this.setState({
+ showConfirmConvertGlue: false,
+ glueEntry: ""
+ });
+ }
+
+ convertGlue (dn) {
+ let cmd = ["dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "repl-conflict", "convert-glue", dn];
+ log_cmd("convertGlue", "Convert glue entry to normal entry", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ this.props.reloadConflicts();
+ this.props.addNotification(
+ "success",
+ `Replication glue entry was converted`
+ );
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.props.addNotification(
+ "error",
+ `Failed to convert glue entry: ${dn} - ${errMsg.desc}`
+ );
+ });
+ }
+
+ confirmDeleteGlue (dn) {
+ this.setState({
+ showConfirmDeleteGlue: true,
+ glueEntry: dn
+ });
+ }
+
+ deleteGlue (dn) {
+ let cmd = ["dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "repl-conflict", "delete-glue", dn];
+ log_cmd("deleteGlue", "Delete glue entry", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ this.props.reloadConflicts();
+ this.props.addNotification(
+ "success",
+ `Replication glue entry was deleted`
+ );
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.props.addNotification(
+ "error",
+ `Failed to delete glue entry: ${dn} - ${errMsg.desc}`
+ );
+ });
+ }
+
+ closeConfirmDeleteGlue () {
+ this.setState({
+ showConfirmDeleteGlue: false,
+ glueEntry: ""
+ });
+ }
+
+ confirmConvertConflict (dn) {
+ if (this.state.convertRDN == "") {
+ this.props.addNotification(
+ "error",
+ `You must provide a RDN if you want to convert the Conflict Entry`
+ );
+ return;
+ }
+ this.setState({
+ showConfirmConvertConflict: true,
+ conflictEntry: dn
+ });
+ }
+
+ closeConfirmConvertConflict () {
+ this.setState({
+ showConfirmConvertConflict: false,
+ conflictEntry: ""
+ });
+ }
+
+ confirmSwapConflict (dn) {
+ this.setState({
+ showConfirmSwapConflict: true,
+ conflictEntry: dn
+ });
+ }
+
+ closeConfirmSwapConflict () {
+ this.setState({
+ showConfirmSwapConflict: false,
+ conflictEntry: ""
+ });
+ }
+
+ confirmDeleteConflict (dn) {
+ this.setState({
+ showConfirmDeleteConflict: true,
+ conflictEntry: dn
+ });
+ }
+
+ closeConfirmDeleteConflict () {
+ this.setState({
+ showConfirmDeleteConflict: false,
+ conflictEntry: ""
+ });
+ }
+
+ closeCompareModal () {
+ this.setState({
+ showCompareModal: false
+ });
}
handleNavSelect(key) {
@@ -354,13 +613,23 @@ export class ReplMonitor extends React.Component {
});
}
+ handleConvertChange(e) {
+ const value = e.target.value;
+ this.setState({
+ convertRDN: value,
+ });
+ }
+
render() {
let replAgmts = this.props.data.replAgmts;
let replWinsyncAgmts = this.props.data.replWinsyncAgmts;
let cleanTasks = this.props.data.cleanTasks;
let abortTasks = this.props.data.abortTasks;
+ let conflictEntries = this.props.data.conflicts;
+ let glueEntries = this.props.data.glues;
let agmtDetailModal = "";
let winsyncAgmtDetailModal = "";
+ let compareConflictModal = "";
if (this.state.showAgmtModal) {
agmtDetailModal =
@@ -381,6 +650,19 @@ export class ReplMonitor extends React.Component {
initAgmt={this.confirmWinsyncInit}
/>;
}
+ if (this.state.showCompareModal) {
+ compareConflictModal =
+ <ConflictCompareModal
+ showModal
+ conflictEntry={this.state.cmpConflictEntry}
+ validEntry={this.state.cmpValidEntry}
+ swapFunc={this.confirmSwapConflict}
+ convertFunc={this.confirmConvertConflict}
+ deleteFunc={this.confirmDeleteConflict}
+ handleConvertChange={this.handleConvertChange}
+ closeHandler={this.closeCompareModal}
+ />;
+ }
let cleanNavTitle = 'CleanAllRUV Tasks <font size="1">(' + cleanTasks.length + ')</font>';
let abortNavTitle = 'Abort CleanAllRUV Tasks <font size="1">(' + abortTasks.length + ')</font>';
@@ -414,9 +696,60 @@ export class ReplMonitor extends React.Component {
</TabContent>
</div>;
- let AgmtNavTitle = 'Agreements <font size="1">(' + replAgmts.length + ')</font>';
- let WinsyncAgmtNavTitle = 'Winsync Agreements <font size="1">(' + replWinsyncAgmts.length + ')</font>';
- let TasksNavTitle = 'Tasks <font size="1">(' + (cleanTasks.length + abortTasks.length) + ')</font>';
+ let conflictNavTitle = 'Conflict Entries <font size="1">(' + conflictEntries.length + ')</font>';
+ let glueNavTitle = 'Glue Entries <font size="1">(' + glueEntries.length + ')</font>';
+ let conflictContent =
+ <div>
+ <Nav bsClass="nav nav-tabs nav-tabs-pf">
+ <NavItem className="ds-nav-med" eventKey={1}>
+ <div dangerouslySetInnerHTML={{__html: conflictNavTitle}} />
+ </NavItem>
+ <NavItem className="ds-nav-med" eventKey={2}>
+ <div dangerouslySetInnerHTML={{__html: glueNavTitle}} />
+ </NavItem>
+ </Nav>
+ <TabContent>
+ <TabPane eventKey={1}>
+ <div className="ds-indent ds-margin-top-lg">
+ <p>
+ Replication conflict entries occur when two entries are created with the same
+ DN on different servers. The automatic conflict resolution procedure renames
+ the last entry created to include the entry's unique identifier (nsuniqueid)
+ in the DN. There are several ways to resolve a conflict, but that is up to
+ you on which option to use.
+ </p>
+ <ConflictTable
+ conflicts={conflictEntries}
+ resolveConflict={this.resolveConflict}
+ key={conflictEntries}
+ />
+ </div>
+ </TabPane>
+ <TabPane eventKey={2}>
+ <div className="ds-indent ds-margin-top-lg">
+ <p>
+ When a <b>Delete</b> operation is replicated and the consumer server finds that the entry to be
+ deleted has child entries, the conflict resolution procedure creates a "<i>glue entry</i>" to
+ avoid having orphaned entries in the database. In the same way, when an <b>Add</b> operation is
+ replicated and the consumer server cannot find the parent entry, the conflict resolution
+ procedure creates a "<i>glue entry</i>" representing the parent so that the new entry is not an
+ orphan entry.
+ </p>
+ <GlueTable
+ glues={glueEntries}
+ convertGlue={this.confirmConvertGlue}
+ deleteGlue={this.confirmDeleteGlue}
+ key={glueEntries}
+ />
+ </div>
+ </TabPane>
+ </TabContent>
+ </div>;
+
+ let replAgmtNavTitle = 'Replication Agreements <font size="1">(' + replAgmts.length + ')</font>';
+ let winsyncNavTitle = 'Winsync Agreements <font size="1">(' + replWinsyncAgmts.length + ')</font>';
+ let tasksNavTitle = 'Tasks <font size="1">(' + (cleanTasks.length + abortTasks.length) + ')</font>';
+ let conflictsNavTitle = 'Conflicts <font size="1">(' + (conflictEntries.length + glueEntries.length) + ')</font>';
return (
<div id="monitor-suffix-page" className="container-fluid ds-tab-table">
@@ -424,18 +757,21 @@ export class ReplMonitor extends React.Component {
<div>
<Nav bsClass="nav nav-tabs nav-tabs-pf">
<NavItem eventKey={1}>
- <div dangerouslySetInnerHTML={{__html: AgmtNavTitle}} />
+ <div dangerouslySetInnerHTML={{__html: replAgmtNavTitle}} />
</NavItem>
<NavItem eventKey={2}>
- <div dangerouslySetInnerHTML={{__html: WinsyncAgmtNavTitle}} />
+ <div dangerouslySetInnerHTML={{__html: winsyncNavTitle}} />
</NavItem>
<NavItem eventKey={3}>
- <div dangerouslySetInnerHTML={{__html: TasksNavTitle}} />
+ <div dangerouslySetInnerHTML={{__html: tasksNavTitle}} />
+ </NavItem>
+ <NavItem eventKey={4}>
+ <div dangerouslySetInnerHTML={{__html: conflictsNavTitle}} />
</NavItem>
</Nav>
<TabContent>
<TabPane eventKey={1}>
- <div className="ds-margin-top-lg">
+ <div className="ds-indent ds-tab-table">
<AgmtTable
agmts={replAgmts}
pokeAgmt={this.pokeAgmt}
@@ -451,9 +787,8 @@ export class ReplMonitor extends React.Component {
</Button>
</div>
</TabPane>
-
<TabPane eventKey={2}>
- <div className="ds-margin-top-lg">
+ <div className="dds-indent ds-tab-table">
<WinsyncAgmtTable
agmts={replWinsyncAgmts}
pokeAgmt={this.pokeWinsyncAgmt}
@@ -461,7 +796,6 @@ export class ReplMonitor extends React.Component {
/>
</div>
</TabPane>
-
<TabPane eventKey={3}>
<div className="ds-indent ds-tab-table">
<TabContainer id="task-tabs" defaultActiveKey={1}>
@@ -469,6 +803,13 @@ export class ReplMonitor extends React.Component {
</TabContainer>
</div>
</TabPane>
+ <TabPane eventKey={4}>
+ <div className="ds-indent ds-tab-table">
+ <TabContainer id="task-tabs" defaultActiveKey={1}>
+ {conflictContent}
+ </TabContainer>
+ </div>
+ </TabPane>
</TabContent>
</div>
</TabContainer>
@@ -508,8 +849,49 @@ export class ReplMonitor extends React.Component {
pokeAgmt={this.pokeAgmt}
viewAgmt={this.showAgmtModal}
/>
+ <ConfirmPopup
+ showModal={this.state.showConfirmDeleteGlue}
+ closeHandler={this.closeConfirmDeleteGlue}
+ actionFunc={this.deleteGlue}
+ actionParam={this.state.glueEntry}
+ msg="Are you really sure you want to delete this glue entry and its child entries?"
+ msgContent={this.state.glueEntry}
+ />
+ <ConfirmPopup
+ showModal={this.state.showConfirmConvertGlue}
+ closeHandler={this.closeConfirmConvertGlue}
+ actionFunc={this.convertGlue}
+ actionParam={this.state.glueEntry}
+ msg="Are you really sure you want to convert this glue entry to a regular entry?"
+ msgContent={this.state.glueEntry}
+ />
+ <ConfirmPopup
+ showModal={this.state.showConfirmConvertConflict}
+ closeHandler={this.closeConfirmConvertConflict}
+ actionFunc={this.convertConflict}
+ actionParam={this.state.conflictEntry}
+ msg="Are you really sure you want to convert this conflict entry to a regular entry?"
+ msgContent={this.state.conflictEntry}
+ />
+ <ConfirmPopup
+ showModal={this.state.showConfirmSwapConflict}
+ closeHandler={this.closeConfirmSwapConflict}
+ actionFunc={this.swapConflict}
+ actionParam={this.state.conflictEntry}
+ msg="Are you really sure you want to swap this conflict entry with the valid entry (this would remove the valid entry and any child entries it might have)?"
+ msgContent={this.state.conflictEntry}
+ />
+ <ConfirmPopup
+ showModal={this.state.showConfirmDeleteConflict}
+ closeHandler={this.closeConfirmDeleteConflict}
+ actionFunc={this.deleteConflict}
+ actionParam={this.state.conflictEntry}
+ msg="Are you really sure you want to delete this conflict entry?"
+ msgContent={this.state.conflictEntry}
+ />
{agmtDetailModal}
{winsyncAgmtDetailModal}
+ {compareConflictModal}
</div>
);
}
@@ -524,6 +906,7 @@ ReplMonitor.propTypes = {
addNotification: PropTypes.func,
reloadAgmts: PropTypes.func,
reloadWinsyncAgmts: PropTypes.func,
+ reloadConflicts: PropTypes.func,
};
ReplMonitor.defaultProps = {
@@ -533,6 +916,7 @@ ReplMonitor.defaultProps = {
addNotification: noop,
reloadAgmts: noop,
reloadWinsyncAgmts: noop,
+ reloadConflicts: noop,
};
export default ReplMonitor;
diff --git a/src/cockpit/389-console/src/monitor.jsx b/src/cockpit/389-console/src/monitor.jsx
index ae67f4a..89d47b0 100644
--- a/src/cockpit/389-console/src/monitor.jsx
+++ b/src/cockpit/389-console/src/monitor.jsx
@@ -131,6 +131,8 @@ export class Monitor extends React.Component {
this.replSuffixChange = this.replSuffixChange.bind(this);
this.reloadReplAgmts = this.reloadReplAgmts.bind(this);
this.reloadReplWinsyncAgmts = this.reloadReplWinsyncAgmts.bind(this);
+ this.loadConflicts = this.loadConflicts.bind(this);
+ this.loadGlues = this.loadGlues.bind(this);
// Logging
this.loadMonitor = this.loadMonitor.bind(this);
this.refreshAccessLog = this.refreshAccessLog.bind(this);
@@ -661,6 +663,53 @@ export class Monitor extends React.Component {
...this.state[this.state.replSuffix],
abortTasks: config.items,
},
+ }, this.loadConflicts());
+ })
+ .fail(() => {
+ // Notification of failure (could only be server down)
+ this.setState({
+ replLoading: false,
+ });
+ });
+ }
+
+ loadConflicts() {
+ let cmd = ["dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "repl-conflict", "list", this.state.replSuffix];
+ log_cmd("loadConflicts", "Load conflict entries", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ let config = JSON.parse(content);
+ this.setState({
+ [this.state.replSuffix]: {
+ ...this.state[this.state.replSuffix],
+ conflicts: config.items,
+ glues: []
+ },
+ }, this.loadGlues());
+ })
+ .fail(() => {
+ // Notification of failure (could only be server down)
+ this.setState({
+ replLoading: false,
+ });
+ });
+ }
+
+ loadGlues() {
+ let cmd = ["dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "repl-conflict", "list-glue", this.state.replSuffix];
+ log_cmd("loadGlues", "Load glue entries", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ let config = JSON.parse(content);
+ this.setState({
+ [this.state.replSuffix]: {
+ ...this.state[this.state.replSuffix],
+ glues: config.items,
+ },
}, this.setState(
{
replLoading: false,
@@ -1089,6 +1138,7 @@ export class Monitor extends React.Component {
addNotification={this.addNotification}
reloadAgmts={this.reloadReplAgmts}
reloadWinsyncAgmts={this.reloadReplWinsyncAgmts}
+ reloadConflicts={this.loadConflicts}
key={this.state.replSuffix}
/>
</div>
diff --git a/src/lib389/lib389/conflicts.py b/src/lib389/lib389/conflicts.py
index b1f86e0..ec7b67f 100644
--- a/src/lib389/lib389/conflicts.py
+++ b/src/lib389/lib389/conflicts.py
@@ -9,6 +9,7 @@
import ldap
from lib389._mapped_object import DSLdapObject, DSLdapObjects, _gen_filter
+from lib389.utils import is_a_dn
class ConflictEntry(DSLdapObject):
@@ -27,11 +28,14 @@ class ConflictEntry(DSLdapObject):
self._object_filter = '(objectclass=ldapsubentry)'
def convert(self, new_rdn):
- """Convert conflict entry to a vlid entry, but we need to
+ """Convert conflict entry to a valid entry, but we need to
give the conflict entry a new rdn since we are not replacing
the existing valid counterpart entry.
"""
+ if not is_a_dn(new_rdn):
+ raise ValueError("The new RDN (" + new_rdn + ") is not a valid DN")
+
# Get the conflict entry info
conflict_value = self.get_attr_val_utf8('nsds5ReplConflict')
entry_dn = conflict_value.split(' ', 3)[2]
@@ -62,10 +66,13 @@ class ConflictEntry(DSLdapObject):
new_rdn = "{}={}".format(rdn_attr, entry_rdn)
tmp_rdn = new_rdn + 'tmp'
- # Delete valid entry (to be replaced by conflict entry)
+ # Delete valid entry and its children (to be replaced by conflict entry)
original_entry = DSLdapObject(self._instance, dn=entry_dn)
original_entry._protected = False
- original_entry.delete()
+ filterstr = "(|(objectclass=*)(objectclass=ldapsubentry))"
+ ents = self._instance.search_s(original_entry._dn, ldap.SCOPE_SUBTREE, filterstr, escapehatch='i am sure')
+ for ent in sorted(ents, key=lambda e: len(e.dn), reverse=True):
+ self._instance.delete_ext_s(ent.dn, serverctrls=self._server_controls, clientctrls=self._client_controls, escapehatch='i am sure')
# Rename conflict entry to tmp rdn so we can clean up the rdn attr
self.rename(tmp_rdn, deloldrdn=False)
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
4 years, 1 month
[389-ds-base] branch 389-ds-base-1.4.0 updated: Ticket 50327 - Add replication conflict entry support to lib389/CLI
by pagure@pagure.io
This is an automated email from the git hooks/post-receive script.
mreynolds pushed a commit to branch 389-ds-base-1.4.0
in repository 389-ds-base.
The following commit(s) were added to refs/heads/389-ds-base-1.4.0 by this push:
new fec59e0 Ticket 50327 - Add replication conflict entry support to lib389/CLI
fec59e0 is described below
commit fec59e010010b30fd0e04f148b0066204eb65251
Author: Mark Reynolds <mreynolds(a)redhat.com>
AuthorDate: Tue Apr 16 15:23:16 2019 -0400
Ticket 50327 - Add replication conflict entry support to lib389/CLI
Description: Added Conflict Entry and Glue entry classes to lib389,
and updated dsconf to allow for conflict entry management.
Made some other minor changes to mapped objects:
- Added an attribute list option to display()
- Added a recursive delete option to delete()
https://pagure.io/389-ds-base/issue/50327
Reviewed by: firstyear, lkrispen, and spichugi(Thanks!!!)
(cherry picked from commit 4f7c05e2879cee7d205531edb64b19ad799e20bd)
---
src/lib389/cli/dsconf | 2 +
src/lib389/lib389/_mapped_object.py | 18 ++-
src/lib389/lib389/cli_conf/conflicts.py | 127 +++++++++++++++
src/lib389/lib389/cli_conf/monitor.py | 1 +
src/lib389/lib389/conflicts.py | 175 +++++++++++++++++++++
src/lib389/lib389/tests/cli/conf_conflicts_test.py | 161 +++++++++++++++++++
6 files changed, 476 insertions(+), 8 deletions(-)
diff --git a/src/lib389/cli/dsconf b/src/lib389/cli/dsconf
index 37e6282..9f747c0 100755
--- a/src/lib389/cli/dsconf
+++ b/src/lib389/cli/dsconf
@@ -30,6 +30,7 @@ from lib389.cli_conf import pwpolicy as cli_pwpolicy
from lib389.cli_conf import backup as cli_backup
from lib389.cli_conf import replication as cli_replication
from lib389.cli_conf import chaining as cli_chaining
+from lib389.cli_conf import conflicts as cli_repl_conflicts
from lib389.cli_base import disconnect_instance, connect_instance
from lib389.cli_base.dsrc import dsrc_to_ldap, dsrc_arg_concat
from lib389.cli_base import setup_script_logger
@@ -85,6 +86,7 @@ cli_pwpolicy.create_parser(subparsers)
cli_replication.create_parser(subparsers)
cli_sasl.create_parser(subparsers)
cli_schema.create_parser(subparsers)
+cli_repl_conflicts.create_parser(subparsers)
argcomplete.autocomplete(parser)
diff --git a/src/lib389/lib389/_mapped_object.py b/src/lib389/lib389/_mapped_object.py
index a141877..9486979 100644
--- a/src/lib389/lib389/_mapped_object.py
+++ b/src/lib389/lib389/_mapped_object.py
@@ -1,5 +1,5 @@
# --- BEGIN COPYRIGHT BLOCK ---
-# Copyright (C) 2016 Red Hat, Inc.
+# Copyright (C) 2019 Red Hat, Inc.
# Copyright (C) 2019 William Brown <william(a)blackhats.net.au>
# All rights reserved.
#
@@ -142,12 +142,11 @@ class DSLdapObject(DSLogging):
return True
- def display(self):
+ def display(self, attrlist=['*']):
"""Get an entry but represent it as a string LDIF
:returns: LDIF formatted string
"""
-
e = self._instance.search_ext_s(self._dn, ldap.SCOPE_BASE, self._object_filter, attrlist=["*"], serverctrls=self._server_controls, clientctrls=self._client_controls)[0]
return e.__repr__()
@@ -464,7 +463,8 @@ class DSLdapObject(DSLogging):
all_attrs_dict = self.get_all_attrs()
# removing _compate_exclude attrs from all attrs
compare_attrs = set(all_attrs_dict.keys()) - set(self._compare_exclude)
- compare_attrs_dict = {attr:all_attrs_dict[attr] for attr in compare_attrs}
+ compare_attrs_dict = {attr: all_attrs_dict[attr] for attr in compare_attrs}
+
return compare_attrs_dict
def get_all_attrs(self, use_json=False):
@@ -495,7 +495,9 @@ class DSLdapObject(DSLogging):
self._log.debug("%s get_attrs_vals_utf8(%r)" % (self._dn, keys))
if self._instance.state != DIRSRV_STATE_ONLINE:
raise ValueError("Invalid state. Cannot get properties on instance that is not ONLINE")
- entry = self._instance.search_ext_s(self._dn, ldap.SCOPE_BASE, self._object_filter, attrlist=keys, serverctrls=self._server_controls, clientctrls=self._client_controls, escapehatch='i am sure')[0]
+ entry = self._instance.search_ext_s(self._dn, ldap.SCOPE_BASE, self._object_filter, attrlist=keys,
+ serverctrls=self._server_controls, clientctrls=self._client_controls,
+ escapehatch='i am sure')[0]
vset = entry.getValuesSet(keys)
r = {}
for (k, vo) in vset.items():
@@ -637,7 +639,7 @@ class DSLdapObject(DSLogging):
pass
# Modifies the DN of an entry to the new fqdn provided
- def rename(self, new_rdn, newsuperior=None):
+ def rename(self, new_rdn, newsuperior=None, deloldrdn=True):
"""Renames the object within the tree.
If you provide a newsuperior, this will move the object in the tree.
@@ -660,7 +662,7 @@ class DSLdapObject(DSLogging):
return
self._instance.rename_s(self._dn, new_rdn, newsuperior, serverctrls=self._server_controls, clientctrls=self._client_controls)
search_base = self._basedn
- if newsuperior != None:
+ if newsuperior is not None:
# Well, the new DN should be rdn + newsuperior.
self._dn = '%s,%s' % (new_rdn, newsuperior)
else:
@@ -672,7 +674,7 @@ class DSLdapObject(DSLogging):
# assert we actually got the change right ....
- def delete(self):
+ def delete(self, recursive=False):
"""Deletes the object defined by self._dn.
This can be changed with the self._protected flag!
"""
diff --git a/src/lib389/lib389/cli_conf/conflicts.py b/src/lib389/lib389/cli_conf/conflicts.py
new file mode 100644
index 0000000..620f68c
--- /dev/null
+++ b/src/lib389/lib389/cli_conf/conflicts.py
@@ -0,0 +1,127 @@
+# --- BEGIN COPYRIGHT BLOCK ---
+# Copyright (C) 2019 Red Hat, Inc.
+# All rights reserved.
+#
+# License: GPL (version 3 or any later version).
+# See LICENSE for details.
+# --- END COPYRIGHT BLOCK ---
+
+import json
+from lib389.conflicts import (ConflictEntries, ConflictEntry, GlueEntries, GlueEntry)
+
+conflict_attrs = ['nsds5replconflict', '*']
+
+
+def list_conflicts(inst, basedn, log, args):
+ conflicts = ConflictEntries(inst, args.suffix).list()
+ if args.json:
+ results = []
+ for conflict in conflicts:
+ results.append(json.loads(conflict.get_all_attrs_json()))
+ log.info(json.dumps({'type': 'list', 'items': results}))
+ else:
+ if len(conflicts) > 0:
+ for conflict in conflicts:
+ log.info(conflict.display(conflict_attrs))
+ else:
+ log.info("There were no conflict entries found under the suffix")
+
+
+def cmp_conflict(inst, basedn, log, args):
+ conflict = ConflictEntry(inst, args.DN)
+ valid_entry = conflict.get_valid_entry()
+
+ if args.json:
+ results = []
+ results.append(json.loads(conflict.get_all_attrs_json()))
+ results.append(json.loads(valid_entry.get_all_attrs_json()))
+ log.info(json.dumps({'type': 'list', 'items': results}))
+ else:
+ log.info("Conflict Entry:\n")
+ log.info(conflict.display(conflict_attrs))
+ log.info("Valid Entry:\n")
+ log.info(valid_entry.display(conflict_attrs))
+
+
+def del_conflict(inst, basedn, log, args):
+ conflict = ConflictEntry(inst, args.DN)
+ conflict.delete()
+
+
+def swap_conflict(inst, basedn, log, args):
+ conflict = ConflictEntry(inst, args.DN)
+ conflict.swap()
+
+
+def convert_conflict(inst, basedn, log, args):
+ conflict = ConflictEntry(inst, args.DN)
+ conflict.convert(args.new_rdn)
+
+
+def list_glue(inst, basedn, log, args):
+ glues = GlueEntries(inst, args.suffix).list()
+ if args.json:
+ results = []
+ for glue in glues:
+ results.append(json.loads(glue.get_all_attrs_json()))
+ log.info(json.dumps({'type': 'list', 'items': results}))
+ else:
+ if len(glues) > 0:
+ for glue in glues:
+ log.info(glue.display(conflict_attrs))
+ else:
+ log.info("There were no glue entries found under the suffix")
+
+
+def del_glue(inst, basedn, log, args):
+ glue = GlueEntry(inst, args.DN)
+ glue.delete_all()
+
+
+def convert_glue(inst, basedn, log, args):
+ glue = GlueEntry(inst, args.DN)
+ glue.convert()
+
+
+def create_parser(subparsers):
+ conflict_parser = subparsers.add_parser('repl-conflict', help="Manage replication conflicts")
+ subcommands = conflict_parser.add_subparsers(help='action')
+
+ # coinflict entry arguments
+ list_parser = subcommands.add_parser('list', help="List conflict entries")
+ list_parser.add_argument('suffix', help='The backend name, or suffix, to look for conflict entries')
+ list_parser.set_defaults(func=list_conflicts)
+
+ cmp_parser = subcommands.add_parser('compare', help="Compare the conflict entry with its valid counterpart")
+ cmp_parser.add_argument('DN', help='The DN of the conflict entry')
+ cmp_parser.set_defaults(func=cmp_conflict)
+
+ del_parser = subcommands.add_parser('delete', help="Delete a conflict entry")
+ del_parser.add_argument('DN', help='The DN of the conflict entry')
+ del_parser.set_defaults(func=del_conflict)
+
+ replace_parser = subcommands.add_parser('swap', help="Replace the valid entry with the conflict entry")
+ replace_parser.add_argument('DN', help='The DN of the conflict entry')
+ replace_parser.set_defaults(func=swap_conflict)
+
+ replace_parser = subcommands.add_parser('convert', help="Convert the conflict entry to a valid entry, "
+ "while keeping the original valid entry counterpart. "
+ "This requires that the converted conflict entry have "
+ "a new RDN value. For example: \"cn=my_new_rdn_value\".")
+ replace_parser.add_argument('DN', help='The DN of the conflict entry')
+ replace_parser.add_argument('--new-rdn', required=True, help="The new RDN for the converted conflict entry. "
+ "For example: \"cn=my_new_rdn_value\"")
+ replace_parser.set_defaults(func=convert_conflict)
+
+ # Glue entry arguments
+ list_glue_parser = subcommands.add_parser('list-glue', help="List replication glue entries")
+ list_glue_parser.add_argument('suffix', help='The backend name, or suffix, to look for glue entries')
+ list_glue_parser.set_defaults(func=list_glue)
+
+ del_glue_parser = subcommands.add_parser('delete-glue', help="Delete the glue entry and its child entries")
+ del_glue_parser.add_argument('DN', help='The DN of the glue entry')
+ del_glue_parser.set_defaults(func=del_glue)
+
+ convert_glue_parser = subcommands.add_parser('convert-glue', help="Convert the glue entry into a regular entry")
+ convert_glue_parser.add_argument('DN', help='The DN of the glue entry')
+ convert_glue_parser.set_defaults(func=convert_glue)
diff --git a/src/lib389/lib389/cli_conf/monitor.py b/src/lib389/lib389/cli_conf/monitor.py
index a704bea..53637e1 100644
--- a/src/lib389/lib389/cli_conf/monitor.py
+++ b/src/lib389/lib389/cli_conf/monitor.py
@@ -1,5 +1,6 @@
# --- BEGIN COPYRIGHT BLOCK ---
# Copyright (C) 2019 William Brown <william(a)blackhats.net.au>
+# Copyright (C) 2019 Red Hat, Inc.
# All rights reserved.
#
# License: GPL (version 3 or any later version).
diff --git a/src/lib389/lib389/conflicts.py b/src/lib389/lib389/conflicts.py
new file mode 100644
index 0000000..b1f86e0
--- /dev/null
+++ b/src/lib389/lib389/conflicts.py
@@ -0,0 +1,175 @@
+# --- BEGIN COPYRIGHT BLOCK ---
+# Copyright (C) 2019 Red Hat, Inc.
+# All rights reserved.
+#
+# License: GPL (version 3 or any later version).
+# See LICENSE for details.
+# --- END COPYRIGHT BLOCK ---
+#
+
+import ldap
+from lib389._mapped_object import DSLdapObject, DSLdapObjects, _gen_filter
+
+
+class ConflictEntry(DSLdapObject):
+ """A replication conflict entry
+
+ :param instance: An instance
+ :type instance: lib389.DirSrv
+ :param dn: The DN of the conflict entry
+ :type dn: str
+ """
+ def __init__(self, instance, dn=None):
+ super(ConflictEntry, self).__init__(instance, dn)
+ self._rdn_attribute = 'cn'
+ self._create_objectclasses = ['ldapsubentry']
+ self._protected = False
+ self._object_filter = '(objectclass=ldapsubentry)'
+
+ def convert(self, new_rdn):
+ """Convert conflict entry to a vlid entry, but we need to
+ give the conflict entry a new rdn since we are not replacing
+ the existing valid counterpart entry.
+ """
+
+ # Get the conflict entry info
+ conflict_value = self.get_attr_val_utf8('nsds5ReplConflict')
+ entry_dn = conflict_value.split(' ', 3)[2]
+ entry_rdn = ldap.explode_dn(entry_dn, 1)[0]
+ rdn_attr = entry_dn.split('=', 1)[0]
+
+ # Rename conflict entry
+ self.rename(new_rdn, deloldrdn=False)
+
+ # Cleanup entry
+ self.remove(rdn_attr, entry_rdn)
+ if self.present('objectclass', 'ldapsubentry'):
+ self.remove('objectclass', 'ldapsubentry')
+ self.remove_all('nsds5ReplConflict')
+
+ def swap(self):
+ """Make the conflict entry the real valid entry. Delete old valid entry,
+ and rename the conflict
+ """
+
+ # Get the conflict entry info
+ conflict_value = self.get_attr_val_utf8('nsds5ReplConflict')
+ entry_dn = conflict_value.split(' ', 3)[2]
+ entry_rdn = ldap.explode_dn(entry_dn, 1)[0]
+
+ # Gather the RDN details
+ rdn_attr = entry_dn.split('=', 1)[0]
+ new_rdn = "{}={}".format(rdn_attr, entry_rdn)
+ tmp_rdn = new_rdn + 'tmp'
+
+ # Delete valid entry (to be replaced by conflict entry)
+ original_entry = DSLdapObject(self._instance, dn=entry_dn)
+ original_entry._protected = False
+ original_entry.delete()
+
+ # Rename conflict entry to tmp rdn so we can clean up the rdn attr
+ self.rename(tmp_rdn, deloldrdn=False)
+
+ # Cleanup entry
+ self.remove(rdn_attr, entry_rdn)
+ if self.present('objectclass', 'ldapsubentry'):
+ self.remove('objectclass', 'ldapsubentry')
+ self.remove_all('nsds5ReplConflict')
+
+ # Rename to the final/correct rdn
+ self.rename(new_rdn, deloldrdn=True)
+
+ def get_valid_entry(self):
+ """Get the conflict entry's valid counterpart entry
+ """
+ # Get the conflict entry info
+ conflict_value = self.get_attr_val_utf8('nsds5ReplConflict')
+ entry_dn = conflict_value.split(' ', 3)[2]
+
+ # Get the valid entry
+ return DSLdapObject(self._instance, dn=entry_dn)
+
+
+class ConflictEntries(DSLdapObjects):
+ """Represents the set of tombstone objects that may exist on
+ this replica. Tombstones are locally generated, so they are
+ unique to individual masters, and may or may not correlate
+ to tombstones on other masters.
+
+ :param instance: An instance
+ :type instance: lib389.DirSrv
+ :param basedn: Tree to search for tombstones in
+ :type basedn: str
+ """
+ def __init__(self, instance, basedn):
+ super(ConflictEntries, self).__init__(instance)
+ self._objectclasses = ['ldapsubentry']
+ # Try some common ones ....
+ self._filterattrs = ['nsds5replconflict', 'objectclass']
+ self._childobject = ConflictEntry
+ self._basedn = basedn
+
+ def _get_objectclass_filter(self):
+ return "(&(objectclass=ldapsubentry)(nsds5replconflict=*))"
+
+
+class GlueEntry(DSLdapObject):
+ """A replication glue entry
+
+ :param instance: An instance
+ :type instance: lib389.DirSrv
+ :param dn: The DN of the conflict entry
+ :type dn: str
+ """
+ def __init__(self, instance, dn=None):
+ super(GlueEntry, self).__init__(instance, dn)
+ self._rdn_attribute = ''
+ self._create_objectclasses = ['glue']
+ self._protected = False
+ self._object_filter = '(objectclass=glue)'
+
+ def convert(self):
+ """Convert entry into real entry
+ """
+ self.remove_all('nsds5replconflict')
+ self.remove('objectclass', 'glue')
+
+ def delete_all(self):
+ """Remove glue entry and its children. Depending on the situation the URP
+ mechanism can turn the parent glue entry into a tombstone before we get
+ a chance to delete it. This results in a NO_SUCH_OBJECT exception
+ """
+ delete_count = 0
+ filterstr = "(|(objectclass=*)(objectclass=ldapsubentry))"
+ ents = self._instance.search_s(self._dn, ldap.SCOPE_SUBTREE, filterstr, escapehatch='i am sure')
+ for ent in sorted(ents, key=lambda e: len(e.dn), reverse=True):
+ try:
+ self._instance.delete_ext_s(ent.dn, serverctrls=self._server_controls, clientctrls=self._client_controls, escapehatch='i am sure')
+ delete_count += 1
+ except ldap.NO_SUCH_OBJECT as e:
+ if len(ents) > 0 and delete_count == (len(ents) - 1):
+ # This is the parent glue entry - it was removed by URP
+ pass
+ else:
+ raise e
+
+
+class GlueEntries(DSLdapObjects):
+ """Represents the set of glue entries that may exist on
+ this replica.
+
+ :param instance: An instance
+ :type instance: lib389.DirSrv
+ :param basedn: Tree to search for tombstones in
+ :type basedn: str
+ """
+ def __init__(self, instance, basedn):
+ super(GlueEntries, self).__init__(instance)
+ self._objectclasses = ['glue']
+ # Try some common ones ....
+ self._filterattrs = ['nsds5replconflict', 'objectclass']
+ self._childobject = GlueEntry
+ self._basedn = basedn
+
+ def _get_objectclass_filter(self):
+ return _gen_filter(['objectclass'], ['glue'])
diff --git a/src/lib389/lib389/tests/cli/conf_conflicts_test.py b/src/lib389/lib389/tests/cli/conf_conflicts_test.py
new file mode 100644
index 0000000..1624c28
--- /dev/null
+++ b/src/lib389/lib389/tests/cli/conf_conflicts_test.py
@@ -0,0 +1,161 @@
+# --- BEGIN COPYRIGHT BLOCK ---
+# Copyright (C) 2018 Red Hat, Inc.
+# All rights reserved.
+#
+# License: GPL (version 3 or any later version).
+# See LICENSE for details.
+# --- END COPYRIGHT BLOCK ---
+
+
+import io
+import sys
+import pytest
+import time
+import json
+from lib389.cli_base import LogCapture, FakeArgs
+from lib389.utils import *
+from lib389._constants import *
+from lib389.idm.nscontainer import nsContainers
+from lib389.topologies import topology_m2 as topo
+from lib389.cli_conf.conflicts import (list_conflicts, cmp_conflict, del_conflict, swap_conflict,
+ convert_conflict, list_glue, del_glue, convert_glue)
+from lib389.utils import ds_is_older
+pytestmark = pytest.mark.skipif(ds_is_older('1.4.0'), reason="Not implemented")
+
+
+def _create_container(inst, dn, name):
+ """Creates container entry"""
+ containers = nsContainers(inst, dn)
+ container = containers.create(properties={'cn': name})
+ time.sleep(1)
+ return container
+
+
+def _delete_container(container):
+ """Deletes container entry"""
+ container.delete()
+ time.sleep(1)
+
+
+def test_conflict_cli(topo):
+ """Test manageing replication conflict entries
+ :id: 800f432a-52ab-4661-ac66-a2bdd9b984d8
+ :setup: two masters
+ :steps:
+ 1. Create replication conflict entries
+ 2. List conflicts
+ 3. Compare conflict entry
+ 4. Delete conflict
+ 5. Resurrect conflict
+ 6. Swap conflict
+ 7. List glue entry
+ 8. Delete glue entry
+ 9. Convert glue entry
+
+ :expectedresults:
+ 1. Success
+ 2. Success
+ 3. Success
+ 4. Success
+ 5. Success
+ 6. Success
+ 7. Success
+ 8. Success
+ 9. Success
+ 10. Success
+ """
+
+ # Setup our default parameters for CLI functions
+ topo.logcap = LogCapture()
+ sys.stdout = io.StringIO()
+ args = FakeArgs()
+ args.DN = ""
+ args.suffix = DEFAULT_SUFFIX
+ args.json = True
+
+ m1 = topo.ms["master1"]
+ m2 = topo.ms["master2"]
+
+ topo.pause_all_replicas()
+
+ # Create entries
+ _create_container(m1, DEFAULT_SUFFIX, 'conflict_parent1')
+ _create_container(m2, DEFAULT_SUFFIX, 'conflict_parent1')
+ _create_container(m1, DEFAULT_SUFFIX, 'conflict_parent2')
+ _create_container(m2, DEFAULT_SUFFIX, 'conflict_parent2')
+ cont_parent_m1 = _create_container(m1, DEFAULT_SUFFIX, 'conflict_parent3')
+ cont_parent_m2 = _create_container(m2, DEFAULT_SUFFIX, 'conflict_parent3')
+ cont_glue_m1 = _create_container(m1, DEFAULT_SUFFIX, 'conflict_parent4')
+ cont_glue_m2 = _create_container(m2, DEFAULT_SUFFIX, 'conflict_parent4')
+
+ # Create the conflicts
+ _delete_container(cont_parent_m1)
+ _create_container(m2, cont_parent_m2.dn, 'conflict_child1')
+ _delete_container(cont_glue_m1)
+ _create_container(m2, cont_glue_m2.dn, 'conflict_child2')
+
+ # Resume replication
+ topo.resume_all_replicas()
+ time.sleep(5)
+
+ # Test "list"
+ list_conflicts(m2, None, topo.logcap.log, args)
+ conflicts = json.loads(topo.logcap.outputs[0].getMessage())
+ assert len(conflicts['items']) == 4
+ conflict_1_DN = conflicts['items'][0]['dn']
+ conflict_2_DN = conflicts['items'][1]['dn']
+ conflict_3_DN = conflicts['items'][2]['dn']
+ topo.logcap.flush()
+
+ # Test compare
+ args.DN = conflict_1_DN
+ cmp_conflict(m2, None, topo.logcap.log, args)
+ conflicts = json.loads(topo.logcap.outputs[0].getMessage())
+ assert len(conflicts['items']) == 2
+ topo.logcap.flush()
+
+ # Test delete
+ del_conflict(m2, None, topo.logcap.log, args)
+ list_conflicts(m2, None, topo.logcap.log, args)
+ conflicts = json.loads(topo.logcap.outputs[0].getMessage())
+ assert len(conflicts['items']) == 3
+ topo.logcap.flush()
+
+ # Test swap
+ args.DN = conflict_2_DN
+ swap_conflict(m2, None, topo.logcap.log, args)
+ list_conflicts(m2, None, topo.logcap.log, args)
+ conflicts = json.loads(topo.logcap.outputs[0].getMessage())
+ assert len(conflicts['items']) == 2
+ topo.logcap.flush()
+
+ # Test conflict convert
+ args.DN = conflict_3_DN
+ args.new_rdn = "cn=testing convert"
+ convert_conflict(m2, None, topo.logcap.log, args)
+ list_conflicts(m2, None, topo.logcap.log, args)
+ conflicts = json.loads(topo.logcap.outputs[0].getMessage())
+ assert len(conflicts['items']) == 1
+ topo.logcap.flush()
+
+ # Test list glue entries
+ list_glue(m2, None, topo.logcap.log, args)
+ glues = json.loads(topo.logcap.outputs[0].getMessage())
+ assert len(glues['items']) == 2
+ topo.logcap.flush()
+
+ # Test delete glue entries
+ args.DN = "cn=conflict_parent3,dc=example,dc=com"
+ del_glue(m2, None, topo.logcap.log, args)
+ list_glue(m2, None, topo.logcap.log, args)
+ glues = json.loads(topo.logcap.outputs[0].getMessage())
+ assert len(glues['items']) == 1
+ topo.logcap.flush()
+
+ # Test convert glue entries
+ args.DN = "cn=conflict_parent4,dc=example,dc=com"
+ convert_glue(m2, None, topo.logcap.log, args)
+ list_glue(m2, None, topo.logcap.log, args)
+ glues = json.loads(topo.logcap.outputs[0].getMessage())
+ assert len(glues['items']) == 0
+ topo.logcap.flush()
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
4 years, 1 month
[389-ds-base] branch 389-ds-base-1.3.9 updated: Ticket 50329 - Possible Security Issue: DOS due to ioblocktimeout not applying to TLS
by pagure@pagure.io
This is an automated email from the git hooks/post-receive script.
tbordaz pushed a commit to branch 389-ds-base-1.3.9
in repository 389-ds-base.
The following commit(s) were added to refs/heads/389-ds-base-1.3.9 by this push:
new dd4b69b Ticket 50329 - Possible Security Issue: DOS due to ioblocktimeout not applying to TLS
dd4b69b is described below
commit dd4b69b5586fa92f32a014ebcc27d122256c5473
Author: Thierry Bordaz <tbordaz(a)redhat.com>
AuthorDate: Thu Mar 28 14:10:54 2019 +0100
Ticket 50329 - Possible Security Issue: DOS due to ioblocktimeout not applying to TLS
Bug Description:
A secure socket is configured in blocking mode. If an event
is detected on a secure socket a worker, tries to read the request.
The read can hang indefinitely if there is nothing to read.
As a consequence ioblocktimeout is not enforced when reading secure socket
Fix Description:
The fix is specific to secure socket read.
Before reading it polls the socket for a read. The socket is poll
(with a 0.1s timeout) until read is possible or sum of poll timeout
is greater than ioblocktimeout.
https://pagure.io/389-ds-base/issue/50329
Reviewed by: Mark Reynolds
Platforms tested: F28
Flag Day: no
Doc impact: no
---
ldap/servers/slapd/connection.c | 80 ++++++++++++++++++++++++++++++++++++++---
1 file changed, 76 insertions(+), 4 deletions(-)
diff --git a/ldap/servers/slapd/connection.c b/ldap/servers/slapd/connection.c
index 188383b..ba2b18f 100644
--- a/ldap/servers/slapd/connection.c
+++ b/ldap/servers/slapd/connection.c
@@ -937,6 +937,10 @@ connection_free_private_buffer(Connection *conn)
#define CONN_TURBO_PERCENTILE 50 /* proportion of threads allowed to be in turbo mode */
#define CONN_TURBO_HYSTERESIS 0 /* avoid flip flopping in and out of turbo mode */
+#define CONN_TIMEOUT_READ_SECURE 100 /* on secure connection a read can block if there
+ * nothing to read. This timeout (millisecond)
+ * is the maximum delay a worker will poll the connection
+ */
void
connection_make_new_pb(Slapi_PBlock *pb, Connection *conn)
{
@@ -1088,11 +1092,78 @@ get_next_from_buffer(void *buffer __attribute__((unused)), size_t buffer_size __
return 0;
}
+/* this function is specific to secure socket that are in blocking mode
+ * It polls the socket to check if we can read it
+ * It returns
+ * ret < 0: a poll failed (different from E_WOULD_BLOCK) 'err' is set
+ * ret = 0: the socket can not be read for ioblocktimeout
+ * ret > 0: the socket can be read
+ */
+static int
+connection_poll_read_secure(Connection *conn, int32_t ioblocktimeout_config, PRInt32 *err)
+{
+ int32_t ioblocktimeout_waits = ioblocktimeout_config / CONN_TIMEOUT_READ_SECURE;
+ int32_t waits_done = 0;
+ struct PRPollDesc pr_pd;
+ PRInt32 ret;
+ PRInt32 syserr = 0;
+ PRIntervalTime timeout = PR_MillisecondsToInterval(CONN_TIMEOUT_READ_SECURE);
+
+ /* The purpose of the loop is to check we can read the socket within the ioblocktimeout limit */
+ do {
+ pr_pd.fd = (PRFileDesc *) conn->c_prfd;
+ pr_pd.in_flags = PR_POLL_READ;
+ pr_pd.out_flags = 0;
+ ret = PR_Poll(&pr_pd, 1, timeout);
+ if (ret > 1) {
+ /* most frequent case. Socket can be read right now so exit from the loop */
+ break;
+ } else if (ret == 0) {
+ /* timeout */
+ waits_done++;
+ } else if (ret < 0) {
+ syserr = PR_GetOSError();
+ if (SLAPD_SYSTEM_WOULD_BLOCK_ERROR(syserr)) {
+ /* If we would block, let's count it as a timeout and continue the loop */
+ waits_done++;
+ ret = 0;
+ } else {
+ /* A failure on a poll, no need to continue */
+ *err = PR_GetError();
+ break;
+ }
+ }
+
+ if (waits_done > ioblocktimeout_waits) {
+ /* We have been polling longer than ioblocktimeout
+ * It is time to say it hang for too long
+ */
+ slapi_log_err(SLAPI_LOG_INFO, "connection_poll_read_secure",
+ "Timeout (%d ms) while reading secured conn %" PRIu64 "\n", ioblocktimeout_config, conn->c_connid);
+ ret = 0;
+ break;
+ }
+ } while (ret == 0);
+
+ /* At this point we have 3 options
+ * ret < 0: a poll failed
+ * ret = 0: the socket can not be read for ioblocktimeout
+ * ret > 0: the socket can be read
+ */
+ return (int) ret;
+}
/* Either read read data into the connection buffer, or fail with err set */
static int
-connection_read_ldap_data(Connection *conn, PRInt32 *err)
+connection_read_ldap_data(Connection *conn, int32_t ioblocktimeout_config, PRInt32 *err)
{
int ret = 0;
+ if (conn->c_flags & CONN_FLAG_SSL) {
+ ret = connection_poll_read_secure(conn, ioblocktimeout_config, err);
+ if (ret <= 0) {
+ /* The socket hang for ioblocktimeout or poll failed */
+ return ret;
+ }
+ }
ret = PR_Recv(conn->c_prfd, conn->c_private->c_buffer, conn->c_private->c_buffer_size, 0, PR_INTERVAL_NO_WAIT);
if (ret < 0) {
*err = PR_GetError();
@@ -1137,7 +1208,7 @@ connection_read_operation(Connection *conn, Operation *op, ber_tag_t *tag, int *
{
ber_len_t len = 0;
int ret = 0;
- int waits_done = 0;
+ int32_t waits_done = 0;
ber_int_t msgid;
int new_operation = 1; /* Are we doing the first I/O read for a new operation ? */
char *buffer = conn->c_private->c_buffer;
@@ -1172,12 +1243,13 @@ connection_read_operation(Connection *conn, Operation *op, ber_tag_t *tag, int *
}
/* If we still haven't seen a complete PDU, read from the network */
while (*tag == LBER_DEFAULT) {
- int ioblocktimeout_waits = config_get_ioblocktimeout() / CONN_TURBO_TIMEOUT_INTERVAL;
+ int32_t ioblocktimeout_config = config_get_ioblocktimeout();
+ int32_t ioblocktimeout_waits = ioblocktimeout_config / CONN_TURBO_TIMEOUT_INTERVAL;
/* We should never get here with data remaining in the buffer */
PR_ASSERT(!new_operation || !conn_buffered_data_avail_nolock(conn, &conn_closed));
/* We make a non-blocking read call */
if (CONNECTION_BUFFER_OFF != conn->c_private->use_buffer) {
- ret = connection_read_ldap_data(conn, &err);
+ ret = connection_read_ldap_data(conn, ioblocktimeout_config, &err);
} else {
ret = get_next_from_buffer(NULL, 0, &len, tag, op->o_ber, conn);
if (ret == -1) {
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
4 years, 1 month
[389-ds-base] branch 389-ds-base-1.4.0 updated: Ticket 50329 - Possible Security Issue: DOS due to ioblocktimeout not applying to TLS
by pagure@pagure.io
This is an automated email from the git hooks/post-receive script.
tbordaz pushed a commit to branch 389-ds-base-1.4.0
in repository 389-ds-base.
The following commit(s) were added to refs/heads/389-ds-base-1.4.0 by this push:
new fcf2b5d Ticket 50329 - Possible Security Issue: DOS due to ioblocktimeout not applying to TLS
fcf2b5d is described below
commit fcf2b5ddb2876a95aeb5ed19bae66fd1ed029929
Author: Thierry Bordaz <tbordaz(a)redhat.com>
AuthorDate: Thu Mar 28 14:10:54 2019 +0100
Ticket 50329 - Possible Security Issue: DOS due to ioblocktimeout not applying to TLS
Bug Description:
A secure socket is configured in blocking mode. If an event
is detected on a secure socket a worker, tries to read the request.
The read can hang indefinitely if there is nothing to read.
As a consequence ioblocktimeout is not enforced when reading secure socket
Fix Description:
The fix is specific to secure socket read.
Before reading it polls the socket for a read. The socket is poll
(with a 0.1s timeout) until read is possible or sum of poll timeout
is greater than ioblocktimeout.
https://pagure.io/389-ds-base/issue/50329
Reviewed by: Mark Reynolds
Platforms tested: F28
Flag Day: no
Doc impact: no
---
ldap/servers/slapd/connection.c | 80 ++++++++++++++++++++++++++++++++++++++---
1 file changed, 76 insertions(+), 4 deletions(-)
diff --git a/ldap/servers/slapd/connection.c b/ldap/servers/slapd/connection.c
index 8b88568..8db7256 100644
--- a/ldap/servers/slapd/connection.c
+++ b/ldap/servers/slapd/connection.c
@@ -941,6 +941,10 @@ connection_free_private_buffer(Connection *conn)
#define CONN_TURBO_PERCENTILE 50 /* proportion of threads allowed to be in turbo mode */
#define CONN_TURBO_HYSTERESIS 0 /* avoid flip flopping in and out of turbo mode */
+#define CONN_TIMEOUT_READ_SECURE 100 /* on secure connection a read can block if there
+ * nothing to read. This timeout (millisecond)
+ * is the maximum delay a worker will poll the connection
+ */
void
connection_make_new_pb(Slapi_PBlock *pb, Connection *conn)
{
@@ -1092,11 +1096,78 @@ get_next_from_buffer(void *buffer __attribute__((unused)), size_t buffer_size __
return 0;
}
+/* this function is specific to secure socket that are in blocking mode
+ * It polls the socket to check if we can read it
+ * It returns
+ * ret < 0: a poll failed (different from E_WOULD_BLOCK) 'err' is set
+ * ret = 0: the socket can not be read for ioblocktimeout
+ * ret > 0: the socket can be read
+ */
+static int
+connection_poll_read_secure(Connection *conn, int32_t ioblocktimeout_config, PRInt32 *err)
+{
+ int32_t ioblocktimeout_waits = ioblocktimeout_config / CONN_TIMEOUT_READ_SECURE;
+ int32_t waits_done = 0;
+ struct PRPollDesc pr_pd;
+ PRInt32 ret;
+ PRInt32 syserr = 0;
+ PRIntervalTime timeout = PR_MillisecondsToInterval(CONN_TIMEOUT_READ_SECURE);
+
+ /* The purpose of the loop is to check we can read the socket within the ioblocktimeout limit */
+ do {
+ pr_pd.fd = (PRFileDesc *) conn->c_prfd;
+ pr_pd.in_flags = PR_POLL_READ;
+ pr_pd.out_flags = 0;
+ ret = PR_Poll(&pr_pd, 1, timeout);
+ if (ret > 1) {
+ /* most frequent case. Socket can be read right now so exit from the loop */
+ break;
+ } else if (ret == 0) {
+ /* timeout */
+ waits_done++;
+ } else if (ret < 0) {
+ syserr = PR_GetOSError();
+ if (SLAPD_SYSTEM_WOULD_BLOCK_ERROR(syserr)) {
+ /* If we would block, let's count it as a timeout and continue the loop */
+ waits_done++;
+ ret = 0;
+ } else {
+ /* A failure on a poll, no need to continue */
+ *err = PR_GetError();
+ break;
+ }
+ }
+
+ if (waits_done > ioblocktimeout_waits) {
+ /* We have been polling longer than ioblocktimeout
+ * It is time to say it hang for too long
+ */
+ slapi_log_err(SLAPI_LOG_INFO, "connection_poll_read_secure",
+ "Timeout (%d ms) while reading secured conn %" PRIu64 "\n", ioblocktimeout_config, conn->c_connid);
+ ret = 0;
+ break;
+ }
+ } while (ret == 0);
+
+ /* At this point we have 3 options
+ * ret < 0: a poll failed
+ * ret = 0: the socket can not be read for ioblocktimeout
+ * ret > 0: the socket can be read
+ */
+ return (int) ret;
+}
/* Either read read data into the connection buffer, or fail with err set */
static int
-connection_read_ldap_data(Connection *conn, PRInt32 *err)
+connection_read_ldap_data(Connection *conn, int32_t ioblocktimeout_config, PRInt32 *err)
{
int ret = 0;
+ if (conn->c_flags & CONN_FLAG_SSL) {
+ ret = connection_poll_read_secure(conn, ioblocktimeout_config, err);
+ if (ret <= 0) {
+ /* The socket hang for ioblocktimeout or poll failed */
+ return ret;
+ }
+ }
ret = PR_Recv(conn->c_prfd, conn->c_private->c_buffer, conn->c_private->c_buffer_size, 0, PR_INTERVAL_NO_WAIT);
if (ret < 0) {
*err = PR_GetError();
@@ -1141,7 +1212,7 @@ connection_read_operation(Connection *conn, Operation *op, ber_tag_t *tag, int *
{
ber_len_t len = 0;
int ret = 0;
- int waits_done = 0;
+ int32_t waits_done = 0;
ber_int_t msgid;
int new_operation = 1; /* Are we doing the first I/O read for a new operation ? */
char *buffer = conn->c_private->c_buffer;
@@ -1176,12 +1247,13 @@ connection_read_operation(Connection *conn, Operation *op, ber_tag_t *tag, int *
}
/* If we still haven't seen a complete PDU, read from the network */
while (*tag == LBER_DEFAULT) {
- int ioblocktimeout_waits = config_get_ioblocktimeout() / CONN_TURBO_TIMEOUT_INTERVAL;
+ int32_t ioblocktimeout_config = config_get_ioblocktimeout();
+ int32_t ioblocktimeout_waits = ioblocktimeout_config / CONN_TURBO_TIMEOUT_INTERVAL;
/* We should never get here with data remaining in the buffer */
PR_ASSERT(!new_operation || !conn_buffered_data_avail_nolock(conn, &conn_closed));
/* We make a non-blocking read call */
if (CONNECTION_BUFFER_OFF != conn->c_private->use_buffer) {
- ret = connection_read_ldap_data(conn, &err);
+ ret = connection_read_ldap_data(conn, ioblocktimeout_config, &err);
} else {
ret = get_next_from_buffer(NULL, 0, &len, tag, op->o_ber, conn);
if (ret == -1) {
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
4 years, 1 month
[389-ds-base] branch 389-ds-base-1.3.9 updated: Ticket 49990 - Increase the default FD limits
by pagure@pagure.io
This is an automated email from the git hooks/post-receive script.
mreynolds pushed a commit to branch 389-ds-base-1.3.9
in repository 389-ds-base.
The following commit(s) were added to refs/heads/389-ds-base-1.3.9 by this push:
new a6112a4 Ticket 49990 - Increase the default FD limits
a6112a4 is described below
commit a6112a4ba6e8136419901313a22edab211c05120
Author: Mark Reynolds <mreynolds(a)redhat.com>
AuthorDate: Fri Apr 5 09:16:02 2019 -0400
Ticket 49990 - Increase the default FD limits
Description: As discussed in the ticket, this fix sets the maxdescriptors
to the maximum allowed by the OS/systemd. If this limit can
not be obtained then we fall back to 8192 as the limit
https://pagure.io/389-ds-base/issue/49990
Reviewed by: tbordaz & firstyear(Thanks!!)
(cherry picked from commit 8ca142034a051122b78bdaa3a948d3c50d4cca7e)
(cherry picked from commit 2c583a97cffa54a7da9922215ae37156174a37c5)
---
.../tests/suites/resource_limits/fdlimits_test.py | 63 ++++++++++++++++++++++
ldap/servers/slapd/libglobs.c | 26 +++++----
ldap/servers/slapd/main.c | 5 +-
ldap/servers/slapd/proto-slap.h | 4 +-
ldap/servers/slapd/slap.h | 6 +--
wrappers/systemd.template.service.in | 1 -
wrappers/systemd.template.sysconfig | 3 +-
7 files changed, 90 insertions(+), 18 deletions(-)
diff --git a/dirsrvtests/tests/suites/resource_limits/fdlimits_test.py b/dirsrvtests/tests/suites/resource_limits/fdlimits_test.py
new file mode 100644
index 0000000..e5b14a7
--- /dev/null
+++ b/dirsrvtests/tests/suites/resource_limits/fdlimits_test.py
@@ -0,0 +1,63 @@
+import logging
+import pytest
+import os
+import ldap
+from lib389._constants import *
+from lib389.topologies import topology_st
+
+logging.getLogger(__name__).setLevel(logging.INFO)
+log = logging.getLogger(__name__)
+
+FD_ATTR = "nsslapd-maxdescriptors"
+SYSTEMD_VAL = "16384"
+CUSTOM_VAL = "9000"
+TOO_HIGH_VAL = "65536"
+TOO_LOW_VAL = "0"
+
+
+def test_fd_limits(topology_st):
+ """Test the default limits, and custom limits
+
+ :id: fa0a5106-612f-428f-84c0-9c85c34d0433
+ :setup: Standalone Instance
+ :steps:
+ 1. Check default limit
+ 2. Change default limit
+ 3. Check invalid/too high limit is rejected
+ 4. Check invalid/too low limit is rejected
+ :expectedresults:
+ 1. Success
+ 2. Success
+ 3. Success
+ 4 Success
+ """
+
+ # Check systemd default
+ max_fd = topology_st.standalone.config.get_attr_val_utf8(FD_ATTR)
+ assert max_fd == SYSTEMD_VAL
+
+ # Check custom value is applied
+ topology_st.standalone.config.set(FD_ATTR, CUSTOM_VAL)
+ max_fd = topology_st.standalone.config.get_attr_val_utf8(FD_ATTR)
+ assert max_fd == CUSTOM_VAL
+
+ # Attempt to use val that is too high
+ with pytest.raises(ldap.UNWILLING_TO_PERFORM):
+ topology_st.standalone.config.set(FD_ATTR, TOO_HIGH_VAL)
+ max_fd = topology_st.standalone.config.get_attr_val_utf8(FD_ATTR)
+ assert max_fd == CUSTOM_VAL
+
+ # Attempt to use val that is too low
+ with pytest.raises(ldap.OPERATIONS_ERROR):
+ topology_st.standalone.config.set(FD_ATTR, TOO_LOW_VAL)
+ max_fd = topology_st.standalone.config.get_attr_val_utf8(FD_ATTR)
+ assert max_fd == CUSTOM_VAL
+
+ log.info("Test PASSED")
+
+
+if __name__ == '__main__':
+ # Run isolated
+ # -s for DEBUG mode
+ CURRENT_FILE = os.path.realpath(__file__)
+ pytest.main(["-s", CURRENT_FILE])
diff --git a/ldap/servers/slapd/libglobs.c b/ldap/servers/slapd/libglobs.c
index 59f8d06..91c3a4a 100644
--- a/ldap/servers/slapd/libglobs.c
+++ b/ldap/servers/slapd/libglobs.c
@@ -131,6 +131,7 @@
#if defined(LINUX)
#include <malloc.h>
#endif
+#include <sys/resource.h>
#define REMOVE_CHANGELOG_CMD "remove"
@@ -1465,6 +1466,8 @@ void
FrontendConfig_init(void)
{
slapdFrontendConfig_t *cfg = getFrontendConfig();
+ struct rlimit rlp;
+ int64_t maxdescriptors = SLAPD_DEFAULT_MAXDESCRIPTORS;
#if SLAPI_CFG_USE_RWLOCK == 1
/* initialize the read/write configuration lock */
@@ -1480,6 +1483,11 @@ FrontendConfig_init(void)
exit(-1);
}
#endif
+ /* Default the maximum fd's to the maximum allowed */
+ if (getrlimit(RLIMIT_NOFILE, &rlp) == 0) {
+ maxdescriptors = (int64_t)rlp.rlim_max;
+ }
+
/* Take the lock to make sure we barrier correctly. */
CFG_LOCK_WRITE(cfg);
@@ -1514,7 +1522,7 @@ FrontendConfig_init(void)
/* minssf is applied to rootdse, by default */
init_minssf_exclude_rootdse = cfg->minssf_exclude_rootdse = LDAP_OFF;
cfg->validate_cert = SLAPD_DEFAULT_VALIDATE_CERT;
- cfg->maxdescriptors = SLAPD_DEFAULT_MAXDESCRIPTORS;
+ cfg->maxdescriptors = maxdescriptors;
cfg->groupevalnestlevel = SLAPD_DEFAULT_GROUPEVALNESTLEVEL;
cfg->snmp_index = SLAPD_DEFAULT_SNMP_INDEX;
cfg->SSLclientAuth = SLAPD_DEFAULT_SSLCLIENTAUTH;
@@ -1665,8 +1673,7 @@ FrontendConfig_init(void)
init_ndn_cache_enabled = cfg->ndn_cache_enabled = LDAP_ON;
cfg->ndn_cache_max_size = SLAPD_DEFAULT_NDN_SIZE;
init_sasl_mapping_fallback = cfg->sasl_mapping_fallback = LDAP_OFF;
- init_ignore_vattrs =
- cfg->ignore_vattrs = LDAP_OFF;
+ init_ignore_vattrs = cfg->ignore_vattrs = LDAP_OFF;
cfg->sasl_max_bufsize = SLAPD_DEFAULT_SASL_MAXBUFSIZE;
cfg->unhashed_pw_switch = SLAPD_DEFAULT_UNHASHED_PW_SWITCH;
init_return_orig_type = cfg->return_orig_type = LDAP_OFF;
@@ -4011,13 +4018,12 @@ config_set_maxthreadsperconn(const char *attrname, char *value, char *errorbuf,
return retVal;
}
-#include <sys/resource.h>
-int
+int32_t
config_set_maxdescriptors(const char *attrname, char *value, char *errorbuf, int apply)
{
- int retVal = LDAP_SUCCESS;
- long nValue = 0;
- int maxVal = 65535;
+ int32_t retVal = LDAP_SUCCESS;
+ int64_t nValue = 0;
+ int64_t maxVal = 65535;
struct rlimit rlp;
char *endp = NULL;
@@ -5493,11 +5499,11 @@ config_get_maxthreadsperconn()
return slapi_atomic_load_32(&(slapdFrontendConfig->maxthreadsperconn), __ATOMIC_ACQUIRE);
}
-int
+int64_t
config_get_maxdescriptors(void)
{
slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
- int retVal;
+ int64_t retVal;
CFG_LOCK_READ(slapdFrontendConfig);
retVal = slapdFrontendConfig->maxdescriptors;
diff --git a/ldap/servers/slapd/main.c b/ldap/servers/slapd/main.c
index 219c912..5e24b3b 100644
--- a/ldap/servers/slapd/main.c
+++ b/ldap/servers/slapd/main.c
@@ -1074,7 +1074,10 @@ main(int argc, char **argv)
slapi_ch_free((void **)&versionstring);
}
- /* -sduloutre: compute_init() and entry_computed_attr_init() moved up */
+ /* log the max fd limit as it is typically set in env/systemd */
+ slapi_log_err(SLAPI_LOG_INFO, "main",
+ "Setting the maximum file descriptor limit to: %ld\n",
+ config_get_maxdescriptors());
if (mcfg.slapd_exemode != SLAPD_EXEMODE_REFERRAL) {
int rc;
diff --git a/ldap/servers/slapd/proto-slap.h b/ldap/servers/slapd/proto-slap.h
index 79017e6..a0648ca 100644
--- a/ldap/servers/slapd/proto-slap.h
+++ b/ldap/servers/slapd/proto-slap.h
@@ -383,7 +383,7 @@ int config_set_malloc_mxfast(const char *attrname, char *value, char *errorbuf,
int config_set_malloc_trim_threshold(const char *attrname, char *value, char *errorbuf, int apply);
int config_set_malloc_mmap_threshold(const char *attrname, char *value, char *errorbuf, int apply);
#endif
-int config_set_maxdescriptors(const char *attrname, char *value, char *errorbuf, int apply);
+int32_t config_set_maxdescriptors(const char *attrname, char *value, char *errorbuf, int apply);
int config_set_localuser(const char *attrname, char *value, char *errorbuf, int apply);
int config_set_maxsimplepaged_per_conn(const char *attrname, char *value, char *errorbuf, int apply);
@@ -465,7 +465,7 @@ char *config_get_workingdir(void);
char *config_get_encryptionalias(void);
int32_t config_get_threadnumber(void);
int config_get_maxthreadsperconn(void);
-int config_get_maxdescriptors(void);
+int64_t config_get_maxdescriptors(void);
int config_get_reservedescriptors(void);
int config_get_ioblocktimeout(void);
int config_get_idletimeout(void);
diff --git a/ldap/servers/slapd/slap.h b/ldap/servers/slapd/slap.h
index 618e245..bce7209 100644
--- a/ldap/servers/slapd/slap.h
+++ b/ldap/servers/slapd/slap.h
@@ -351,8 +351,8 @@ typedef void (*VFPV)(); /* takes undefined arguments */
#define SLAPD_DEFAULT_PAGEDSIZELIMIT 0
#define SLAPD_DEFAULT_PAGEDSIZELIMIT_STR "0"
-#define SLAPD_DEFAULT_MAXDESCRIPTORS 1024
-#define SLAPD_DEFAULT_MAXDESCRIPTORS_STR "1024"
+#define SLAPD_DEFAULT_MAXDESCRIPTORS 8192
+#define SLAPD_DEFAULT_MAXDESCRIPTORS_STR "8192"
#define SLAPD_DEFAULT_MAX_FILTER_NEST_LEVEL 40
#define SLAPD_DEFAULT_MAX_FILTER_NEST_LEVEL_STR "40"
#define SLAPD_DEFAULT_GROUPEVALNESTLEVEL 0
@@ -2254,7 +2254,7 @@ typedef struct _slapdFrontendConfig
int idletimeout;
slapi_int_t ioblocktimeout;
slapi_onoff_t lastmod;
- int maxdescriptors;
+ int64_t maxdescriptors;
int conntablesize;
slapi_int_t maxthreadsperconn;
int outbound_ldap_io_timeout;
diff --git a/wrappers/systemd.template.service.in b/wrappers/systemd.template.service.in
index 0d88900..4c1b13d 100644
--- a/wrappers/systemd.template.service.in
+++ b/wrappers/systemd.template.service.in
@@ -28,7 +28,6 @@ EnvironmentFile=@initconfigdir@/@package_name@-%i
PIDFile=@localstatedir@/run/@package_name(a)/slapd-%i.pid
ExecStartPre=@sbindir@/ds_systemd_ask_password_acl @instconfigdir(a)/slapd-%i/dse.ldif
ExecStart=@sbindir@/ns-slapd -D @instconfigdir@/slapd-%i -i @localstatedir@/run/@package_name(a)/slapd-%i.pid
-
# Hardening options:
# PrivateDevices=true
# ProtectSystem=true
diff --git a/wrappers/systemd.template.sysconfig b/wrappers/systemd.template.sysconfig
index 903876b..76c004d 100644
--- a/wrappers/systemd.template.sysconfig
+++ b/wrappers/systemd.template.sysconfig
@@ -7,7 +7,8 @@
# This controls the number of file handles avaliable. File handles
# correlate to sockets for the process, and our access to logs and
-# databases.
+# databases. Note, the configuration setting in Directory Server,
+# "nsslapd-maxdescriptors", can override this limit.
LimitNOFILE=16384
# You can limit the memory in the cgroup with these, and ns-slapd
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
4 years, 1 month
[389-ds-base] branch 389-ds-base-1.4.0 updated: Ticket 49990 - Increase the default FD limits
by pagure@pagure.io
This is an automated email from the git hooks/post-receive script.
mreynolds pushed a commit to branch 389-ds-base-1.4.0
in repository 389-ds-base.
The following commit(s) were added to refs/heads/389-ds-base-1.4.0 by this push:
new 2c583a9 Ticket 49990 - Increase the default FD limits
2c583a9 is described below
commit 2c583a97cffa54a7da9922215ae37156174a37c5
Author: Mark Reynolds <mreynolds(a)redhat.com>
AuthorDate: Fri Apr 5 09:16:02 2019 -0400
Ticket 49990 - Increase the default FD limits
Description: As discussed in the ticket, this fix sets the maxdescriptors
to the maximum allowed by the OS/systemd. If this limit can
not be obtained then we fall back to 8192 as the limit
https://pagure.io/389-ds-base/issue/49990
Reviewed by: tbordaz & firstyear(Thanks!!)
(cherry picked from commit 8ca142034a051122b78bdaa3a948d3c50d4cca7e)
---
.../tests/suites/resource_limits/fdlimits_test.py | 63 ++++++++++++++++++++++
ldap/servers/slapd/libglobs.c | 26 +++++----
ldap/servers/slapd/main.c | 5 +-
ldap/servers/slapd/proto-slap.h | 4 +-
ldap/servers/slapd/slap.h | 6 +--
wrappers/systemd.template.service.in | 1 -
wrappers/systemd.template.sysconfig | 3 +-
7 files changed, 90 insertions(+), 18 deletions(-)
diff --git a/dirsrvtests/tests/suites/resource_limits/fdlimits_test.py b/dirsrvtests/tests/suites/resource_limits/fdlimits_test.py
new file mode 100644
index 0000000..e5b14a7
--- /dev/null
+++ b/dirsrvtests/tests/suites/resource_limits/fdlimits_test.py
@@ -0,0 +1,63 @@
+import logging
+import pytest
+import os
+import ldap
+from lib389._constants import *
+from lib389.topologies import topology_st
+
+logging.getLogger(__name__).setLevel(logging.INFO)
+log = logging.getLogger(__name__)
+
+FD_ATTR = "nsslapd-maxdescriptors"
+SYSTEMD_VAL = "16384"
+CUSTOM_VAL = "9000"
+TOO_HIGH_VAL = "65536"
+TOO_LOW_VAL = "0"
+
+
+def test_fd_limits(topology_st):
+ """Test the default limits, and custom limits
+
+ :id: fa0a5106-612f-428f-84c0-9c85c34d0433
+ :setup: Standalone Instance
+ :steps:
+ 1. Check default limit
+ 2. Change default limit
+ 3. Check invalid/too high limit is rejected
+ 4. Check invalid/too low limit is rejected
+ :expectedresults:
+ 1. Success
+ 2. Success
+ 3. Success
+ 4 Success
+ """
+
+ # Check systemd default
+ max_fd = topology_st.standalone.config.get_attr_val_utf8(FD_ATTR)
+ assert max_fd == SYSTEMD_VAL
+
+ # Check custom value is applied
+ topology_st.standalone.config.set(FD_ATTR, CUSTOM_VAL)
+ max_fd = topology_st.standalone.config.get_attr_val_utf8(FD_ATTR)
+ assert max_fd == CUSTOM_VAL
+
+ # Attempt to use val that is too high
+ with pytest.raises(ldap.UNWILLING_TO_PERFORM):
+ topology_st.standalone.config.set(FD_ATTR, TOO_HIGH_VAL)
+ max_fd = topology_st.standalone.config.get_attr_val_utf8(FD_ATTR)
+ assert max_fd == CUSTOM_VAL
+
+ # Attempt to use val that is too low
+ with pytest.raises(ldap.OPERATIONS_ERROR):
+ topology_st.standalone.config.set(FD_ATTR, TOO_LOW_VAL)
+ max_fd = topology_st.standalone.config.get_attr_val_utf8(FD_ATTR)
+ assert max_fd == CUSTOM_VAL
+
+ log.info("Test PASSED")
+
+
+if __name__ == '__main__':
+ # Run isolated
+ # -s for DEBUG mode
+ CURRENT_FILE = os.path.realpath(__file__)
+ pytest.main(["-s", CURRENT_FILE])
diff --git a/ldap/servers/slapd/libglobs.c b/ldap/servers/slapd/libglobs.c
index 67a48b3..4789ec5 100644
--- a/ldap/servers/slapd/libglobs.c
+++ b/ldap/servers/slapd/libglobs.c
@@ -131,6 +131,7 @@
#if defined(LINUX)
#include <malloc.h>
#endif
+#include <sys/resource.h>
#define REMOVE_CHANGELOG_CMD "remove"
@@ -1518,6 +1519,8 @@ void
FrontendConfig_init(void)
{
slapdFrontendConfig_t *cfg = getFrontendConfig();
+ struct rlimit rlp;
+ int64_t maxdescriptors = SLAPD_DEFAULT_MAXDESCRIPTORS;
#if SLAPI_CFG_USE_RWLOCK == 1
/* initialize the read/write configuration lock */
@@ -1533,6 +1536,11 @@ FrontendConfig_init(void)
exit(-1);
}
#endif
+ /* Default the maximum fd's to the maximum allowed */
+ if (getrlimit(RLIMIT_NOFILE, &rlp) == 0) {
+ maxdescriptors = (int64_t)rlp.rlim_max;
+ }
+
/* Take the lock to make sure we barrier correctly. */
CFG_LOCK_WRITE(cfg);
@@ -1567,7 +1575,7 @@ FrontendConfig_init(void)
/* minssf is applied to rootdse, by default */
init_minssf_exclude_rootdse = cfg->minssf_exclude_rootdse = LDAP_OFF;
cfg->validate_cert = SLAPD_DEFAULT_VALIDATE_CERT;
- cfg->maxdescriptors = SLAPD_DEFAULT_MAXDESCRIPTORS;
+ cfg->maxdescriptors = maxdescriptors;
cfg->groupevalnestlevel = SLAPD_DEFAULT_GROUPEVALNESTLEVEL;
cfg->snmp_index = SLAPD_DEFAULT_SNMP_INDEX;
cfg->SSLclientAuth = SLAPD_DEFAULT_SSLCLIENTAUTH;
@@ -1718,8 +1726,7 @@ FrontendConfig_init(void)
init_ndn_cache_enabled = cfg->ndn_cache_enabled = LDAP_ON;
cfg->ndn_cache_max_size = SLAPD_DEFAULT_NDN_SIZE;
init_sasl_mapping_fallback = cfg->sasl_mapping_fallback = LDAP_OFF;
- init_ignore_vattrs =
- cfg->ignore_vattrs = LDAP_OFF;
+ init_ignore_vattrs = cfg->ignore_vattrs = LDAP_OFF;
cfg->sasl_max_bufsize = SLAPD_DEFAULT_SASL_MAXBUFSIZE;
cfg->unhashed_pw_switch = SLAPD_DEFAULT_UNHASHED_PW_SWITCH;
init_return_orig_type = cfg->return_orig_type = LDAP_OFF;
@@ -4279,13 +4286,12 @@ config_set_maxthreadsperconn(const char *attrname, char *value, char *errorbuf,
return retVal;
}
-#include <sys/resource.h>
-int
+int32_t
config_set_maxdescriptors(const char *attrname, char *value, char *errorbuf, int apply)
{
- int retVal = LDAP_SUCCESS;
- long nValue = 0;
- int maxVal = 65535;
+ int32_t retVal = LDAP_SUCCESS;
+ int64_t nValue = 0;
+ int64_t maxVal = 65535;
struct rlimit rlp;
char *endp = NULL;
@@ -5761,11 +5767,11 @@ config_get_maxthreadsperconn()
return slapi_atomic_load_32(&(slapdFrontendConfig->maxthreadsperconn), __ATOMIC_ACQUIRE);
}
-int
+int64_t
config_get_maxdescriptors(void)
{
slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
- int retVal;
+ int64_t retVal;
CFG_LOCK_READ(slapdFrontendConfig);
retVal = slapdFrontendConfig->maxdescriptors;
diff --git a/ldap/servers/slapd/main.c b/ldap/servers/slapd/main.c
index 185ba90..33ca9ce 100644
--- a/ldap/servers/slapd/main.c
+++ b/ldap/servers/slapd/main.c
@@ -1090,7 +1090,10 @@ main(int argc, char **argv)
slapi_ch_free((void **)&versionstring);
}
- /* -sduloutre: compute_init() and entry_computed_attr_init() moved up */
+ /* log the max fd limit as it is typically set in env/systemd */
+ slapi_log_err(SLAPI_LOG_INFO, "main",
+ "Setting the maximum file descriptor limit to: %ld\n",
+ config_get_maxdescriptors());
if (mcfg.slapd_exemode != SLAPD_EXEMODE_REFERRAL) {
int rc;
diff --git a/ldap/servers/slapd/proto-slap.h b/ldap/servers/slapd/proto-slap.h
index f87c747..dce4243 100644
--- a/ldap/servers/slapd/proto-slap.h
+++ b/ldap/servers/slapd/proto-slap.h
@@ -393,7 +393,7 @@ int config_set_malloc_mxfast(const char *attrname, char *value, char *errorbuf,
int config_set_malloc_trim_threshold(const char *attrname, char *value, char *errorbuf, int apply);
int config_set_malloc_mmap_threshold(const char *attrname, char *value, char *errorbuf, int apply);
#endif
-int config_set_maxdescriptors(const char *attrname, char *value, char *errorbuf, int apply);
+int32_t config_set_maxdescriptors(const char *attrname, char *value, char *errorbuf, int apply);
int config_set_localuser(const char *attrname, char *value, char *errorbuf, int apply);
int config_set_maxsimplepaged_per_conn(const char *attrname, char *value, char *errorbuf, int apply);
@@ -475,7 +475,7 @@ char *config_get_workingdir(void);
char *config_get_encryptionalias(void);
int32_t config_get_threadnumber(void);
int config_get_maxthreadsperconn(void);
-int config_get_maxdescriptors(void);
+int64_t config_get_maxdescriptors(void);
int config_get_reservedescriptors(void);
int config_get_ioblocktimeout(void);
int config_get_idletimeout(void);
diff --git a/ldap/servers/slapd/slap.h b/ldap/servers/slapd/slap.h
index c7becf5..2d2de11 100644
--- a/ldap/servers/slapd/slap.h
+++ b/ldap/servers/slapd/slap.h
@@ -351,8 +351,8 @@ typedef void (*VFPV)(); /* takes undefined arguments */
#define SLAPD_DEFAULT_PAGEDSIZELIMIT 0
#define SLAPD_DEFAULT_PAGEDSIZELIMIT_STR "0"
-#define SLAPD_DEFAULT_MAXDESCRIPTORS 1024
-#define SLAPD_DEFAULT_MAXDESCRIPTORS_STR "1024"
+#define SLAPD_DEFAULT_MAXDESCRIPTORS 8192
+#define SLAPD_DEFAULT_MAXDESCRIPTORS_STR "8192"
#define SLAPD_DEFAULT_MAX_FILTER_NEST_LEVEL 40
#define SLAPD_DEFAULT_MAX_FILTER_NEST_LEVEL_STR "40"
#define SLAPD_DEFAULT_GROUPEVALNESTLEVEL 0
@@ -2294,7 +2294,7 @@ typedef struct _slapdFrontendConfig
int idletimeout;
slapi_int_t ioblocktimeout;
slapi_onoff_t lastmod;
- int maxdescriptors;
+ int64_t maxdescriptors;
int conntablesize;
slapi_int_t maxthreadsperconn;
int outbound_ldap_io_timeout;
diff --git a/wrappers/systemd.template.service.in b/wrappers/systemd.template.service.in
index 3c1d368..978bbbb 100644
--- a/wrappers/systemd.template.service.in
+++ b/wrappers/systemd.template.service.in
@@ -28,7 +28,6 @@ EnvironmentFile=@initconfigdir@/@package_name@-%i
PIDFile=@localstatedir@/run/@package_name(a)/slapd-%i.pid
ExecStartPre=@libexecdir@/ds_systemd_ask_password_acl @instconfigdir(a)/slapd-%i/dse.ldif
ExecStart=@sbindir@/ns-slapd -D @instconfigdir@/slapd-%i -i @localstatedir@/run/@package_name(a)/slapd-%i.pid
-
# Hardening options:
# PrivateDevices=true
# ProtectSystem=true
diff --git a/wrappers/systemd.template.sysconfig b/wrappers/systemd.template.sysconfig
index 903876b..76c004d 100644
--- a/wrappers/systemd.template.sysconfig
+++ b/wrappers/systemd.template.sysconfig
@@ -7,7 +7,8 @@
# This controls the number of file handles avaliable. File handles
# correlate to sockets for the process, and our access to logs and
-# databases.
+# databases. Note, the configuration setting in Directory Server,
+# "nsslapd-maxdescriptors", can override this limit.
LimitNOFILE=16384
# You can limit the memory in the cgroup with these, and ns-slapd
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
4 years, 1 month
[389-ds-base] branch 389-ds-base-1.3.9 updated: Ticket 50053 - Subtree password policy overrides a user-defined password policy
by pagure@pagure.io
This is an automated email from the git hooks/post-receive script.
mreynolds pushed a commit to branch 389-ds-base-1.3.9
in repository 389-ds-base.
The following commit(s) were added to refs/heads/389-ds-base-1.3.9 by this push:
new 4a01095 Ticket 50053 - Subtree password policy overrides a user-defined password policy
4a01095 is described below
commit 4a01095505a7e8cdee632b20dbc1b9e39780cc45
Author: Mark Reynolds <mreynolds(a)redhat.com>
AuthorDate: Mon Apr 15 11:23:46 2019 -0400
Ticket 50053 - Subtree password policy overrides a user-defined password policy
Bug Description:
When an entry contains an attribute that is also defined by a cos definition
a specifier defines which values win: the real values that are in the entry or the
virtual values that are cos defined.
The specifier 'default' means that the real values are the winners (returned).
'operational-default' has the same behavior but just specify that the attribute
is operational.
The bug is that when real values exists, the 'operational-default' specifier
drops the real values in favor of the virtual ones.
Fix Description:
Change the test, so that real values are not kept for 'operation-default'
Note: the full routine cos_cache_query_attr looks quite messy and error prone
It would be nice to rewrite it when we have time
https://pagure.io/389-ds-base/issue/50053
---
ldap/servers/plugins/cos/cos_cache.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ldap/servers/plugins/cos/cos_cache.c b/ldap/servers/plugins/cos/cos_cache.c
index 5e0cf17..64c0441 100644
--- a/ldap/servers/plugins/cos/cos_cache.c
+++ b/ldap/servers/plugins/cos/cos_cache.c
@@ -2279,7 +2279,7 @@ cos_cache_query_attr(cos_cache *ptheCache, vattr_context *context, Slapi_Entry *
/* now for the tests */
/* would we be allowed to supply this attribute if we had one? */
- if (entry_has_value && !pAttr->attr_override && !pAttr->attr_operational && !pAttr->attr_operational_default) {
+ if (entry_has_value && !pAttr->attr_override && !pAttr->attr_operational) {
/* answer: no, move on to the next attribute */
attr_index++;
continue;
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
4 years, 1 month
[389-ds-base] branch master updated: Ticket 50306 - (cont typo) Move connection config inside struct
by pagure@pagure.io
This is an automated email from the git hooks/post-receive script.
tbordaz pushed a commit to branch master
in repository 389-ds-base.
The following commit(s) were added to refs/heads/master by this push:
new 117d4ba Ticket 50306 - (cont typo) Move connection config inside struct
117d4ba is described below
commit 117d4ba0901c6d4be5bfbadb97510701395975a2
Author: Thierry Bordaz <tbordaz(a)redhat.com>
AuthorDate: Mon Apr 15 11:30:42 2019 +0200
Ticket 50306 - (cont typo) Move connection config inside struct
Bug Description:
typo where ioblocktimeout was erronously computed from maxbersize
Fix Description:
move c_maxbersize to c_ioblocktimeout
https://pagure.io/389-ds-base/issue/50306
Reviewed by: Thierry Bordaz
Platforms tested: F28
Flag Day: no
Doc impact: no
---
ldap/servers/slapd/connection.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ldap/servers/slapd/connection.c b/ldap/servers/slapd/connection.c
index 3a7620b..8a17df7 100644
--- a/ldap/servers/slapd/connection.c
+++ b/ldap/servers/slapd/connection.c
@@ -1175,7 +1175,7 @@ connection_read_operation(Connection *conn, Operation *op, ber_tag_t *tag, int *
}
/* If we still haven't seen a complete PDU, read from the network */
while (*tag == LBER_DEFAULT) {
- int32_t ioblocktimeout_waits = conn->c_maxbersize / CONN_TURBO_TIMEOUT_INTERVAL;
+ int32_t ioblocktimeout_waits = conn->c_ioblocktimeout / CONN_TURBO_TIMEOUT_INTERVAL;
/* We should never get here with data remaining in the buffer */
PR_ASSERT(!new_operation || !conn_buffered_data_avail_nolock(conn, &conn_closed));
/* We make a non-blocking read call */
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
4 years, 1 month