| |
@@ -10,8 +10,10 @@
|
| |
import ldap
|
| |
|
| |
from lib389._entry import Entry
|
| |
-
|
| |
+ from lib389._constants import DIRSRV_STATE_ONLINE
|
| |
from lib389._mapped_object import DSLdapObject, DSLdapObjects, _gen_and, _gen_filter, _term_gen
|
| |
+ from lib389.utils import ensure_bytes
|
| |
+
|
| |
|
| |
class Tombstone(DSLdapObject):
|
| |
"""A tombstone is created during a conflict or a delete in a
|
| |
@@ -28,16 +30,139 @@
|
| |
self._rdn_attribute = 'nsUniqueId'
|
| |
self._create_objectclasses = ['nsTombStone']
|
| |
self._protected = True
|
| |
+ self._entry_rdn = self._dn.split(',')[1]
|
| |
+ self._grandparent_dn = self._dn.split(',', 2)[-1]
|
| |
# We need to always add this filter, else we won't see the ts
|
| |
- self._object_filter = '(objectclass=nsTombStone)'
|
| |
+ self._object_filter = '(&(objectclass=nsTombStone)({}))'.format(self._entry_rdn)
|
| |
+
|
| |
+ def raw_entry(self):
|
| |
+ """Get an Entry object
|
| |
+
|
| |
+ :returns: Entry object
|
| |
+ """
|
| |
+
|
| |
+ return self._instance.search_ext_s(self._grandparent_dn, ldap.SCOPE_SUBTREE, self._object_filter, attrlist=["*"],
|
| |
+ serverctrls=self._server_controls, clientctrls=self._client_controls)[0]
|
| |
+
|
| |
+ def exists(self):
|
| |
+ """Check if the entry exists
|
| |
+
|
| |
+ :returns: True if it exists
|
| |
+ """
|
| |
+
|
| |
+ try:
|
| |
+ self._instance.search_ext_s(self._grandparent_dn, ldap.SCOPE_SUBTREE, self._object_filter, attrsonly=1,
|
| |
+ serverctrls=self._server_controls, clientctrls=self._client_controls)
|
| |
+ except ldap.NO_SUCH_OBJECT:
|
| |
+ return False
|
| |
+
|
| |
+ return True
|
| |
+
|
| |
+ def display(self):
|
| |
+ """Get an entry but represent it as a string LDIF
|
| |
+
|
| |
+ :returns: LDIF formatted string
|
| |
+ """
|
| |
+
|
| |
+ e = self._instance.search_ext_s(self._grandparent_dn, ldap.SCOPE_SUBTREE, self._object_filter, attrlist=["*"],
|
| |
+ serverctrls=self._server_controls, clientctrls=self._client_controls)[0]
|
| |
+ return e.__repr__()
|
| |
+
|
| |
+ def present(self, attr, value=None):
|
| |
+ """Assert that some attr, or some attr / value exist on the entry.
|
| |
+
|
| |
+ :param attr: an attribute name
|
| |
+ :type attr: str
|
| |
+ :param value: an attribute value
|
| |
+ :type value: str
|
| |
+
|
| |
+ :returns: True if attr is present
|
| |
+ """
|
| |
+
|
| |
+ if self._instance.state != DIRSRV_STATE_ONLINE:
|
| |
+ raise ValueError("Invalid state. Cannot get presence on instance that is not ONLINE")
|
| |
+ self._log.debug("%s present(%r) %s", self._dn, attr, value)
|
| |
+
|
| |
+ self._instance.search_ext_s(self._grandparent_dn, ldap.SCOPE_SUBTREE, self._object_filter, attrlist=[attr, ],
|
| |
+ serverctrls=self._server_controls, clientctrls=self._client_controls)[0]
|
| |
+ values = self.get_attr_vals_bytes(attr)
|
| |
+ self._log.debug("%s contains %s", self._dn, values)
|
| |
+
|
| |
+ if value is None:
|
| |
+ # We are just checking if SOMETHING is present ....
|
| |
+ return len(values) > 0
|
| |
+ else:
|
| |
+ # Check if a value really does exist.
|
| |
+ return ensure_bytes(value).lower() in [x.lower() for x in values]
|
| |
+
|
| |
+ def get_all_attrs(self, use_json=False):
|
| |
+ """Get a dictionary having all the attributes of the entry
|
| |
+
|
| |
+ :returns: Dict with real attributes and operational attributes
|
| |
+ """
|
| |
+
|
| |
+ self._log.debug("%s get_all_attrs", self._dn)
|
| |
+ if self._instance.state != DIRSRV_STATE_ONLINE:
|
| |
+ raise ValueError("Invalid state. Cannot get properties on instance that is not ONLINE")
|
| |
+ else:
|
| |
+ # retrieving real(*) and operational attributes(+)
|
| |
+ attrs_entry = self._instance.search_ext_s(self._grandparent_dn, ldap.SCOPE_SUBTREE, self._object_filter, attrlist=["*", "+"],
|
| |
+ serverctrls=self._server_controls, clientctrls=self._client_controls)[0]
|
| |
+ # getting dict from 'entry' object
|
| |
+ attrs_dict = attrs_entry.data
|
| |
+ return attrs_dict
|
| |
+
|
| |
+ def get_attrs_vals(self, keys, use_json=False):
|
| |
+ self._log.debug("%s get_attrs_vals(%r)", self._dn, keys)
|
| |
+ if self._instance.state != DIRSRV_STATE_ONLINE:
|
| |
+ raise ValueError("Invalid state. Cannot get properties on instance that is not ONLINE")
|
| |
+ else:
|
| |
+ entry = self._instance.search_ext_s(self._grandparent_dn, ldap.SCOPE_SUBTREE, self._object_filter, attrlist=keys,
|
| |
+ serverctrls=self._server_controls, clientctrls=self._client_controls)[0]
|
| |
+ return entry.getValuesSet(keys)
|
| |
+
|
| |
+ def get_attr_vals(self, key, use_json=False):
|
| |
+ self._log.debug("%s get_attr_vals(%r)", self._dn, key)
|
| |
+ # We might need to add a state check for NONE dn.
|
| |
+ if self._instance.state != DIRSRV_STATE_ONLINE:
|
| |
+ raise ValueError("Invalid state. Cannot get properties on instance that is not ONLINE")
|
| |
+ # In the future, I plan to add a mode where if local == true, we
|
| |
+ # can use get on dse.ldif to get values offline.
|
| |
+ else:
|
| |
+ # It would be good to prevent the entry code intercepting this ....
|
| |
+ # We have to do this in this method, because else we ignore the scope base.
|
| |
+ entry = self._instance.search_ext_s(self._grandparent_dn, ldap.SCOPE_SUBTREE, self._object_filter, attrlist=[key],
|
| |
+ serverctrls=self._server_controls, clientctrls=self._client_controls)[0]
|
| |
+
|
| |
+ vals = entry.getValues(key)
|
| |
+ if use_json:
|
| |
+ result = {key: []}
|
| |
+ for val in vals:
|
| |
+ result[key].append(val)
|
| |
+ return result
|
| |
+ else:
|
| |
+ return vals
|
| |
+
|
| |
+ def get_attr_val(self, key, use_json=False):
|
| |
+ self._log.debug("%s getVal(%r)", self._dn, key)
|
| |
+ # We might need to add a state check for NONE dn.
|
| |
+ if self._instance.state != DIRSRV_STATE_ONLINE:
|
| |
+ raise ValueError("Invalid state. Cannot get properties on instance that is not ONLINE")
|
| |
+ # In the future, I plan to add a mode where if local == true, we
|
| |
+ # can use get on dse.ldif to get values offline.
|
| |
+ else:
|
| |
+ entry = self._instance.search_ext_s(self._grandparent_dn, ldap.SCOPE_SUBTREE, self._object_filter, attrlist=[key],
|
| |
+ serverctrls=self._server_controls, clientctrls=self._client_controls)[0]
|
| |
+ return entry.getValue(key)
|
| |
|
| |
def revive(self):
|
| |
"""Revive this object within the tree.
|
| |
|
| |
This duplicates "as much as possible", excluding some internal attributes.
|
| |
"""
|
| |
+
|
| |
orig_dn = self.get_attr_val_utf8('nscpEntryDN')
|
| |
- self._log.info("Reviving %s -> %s" % (self.dn, orig_dn))
|
| |
+ self._log.info("Reviving %s -> %s", self.dn, orig_dn)
|
| |
# Get all our attributes
|
| |
properties = self.get_all_attrs()
|
| |
properties.pop('nsuniqueid', None)
|
| |
@@ -57,6 +182,7 @@
|
| |
e.update(properties)
|
| |
self._instance.add_ext_s(e, serverctrls=self._server_controls, clientctrls=self._client_controls)
|
| |
|
| |
+
|
| |
class Tombstones(DSLdapObjects):
|
| |
"""Represents the set of tombstone objects that may exist on
|
| |
this replica. Tombstones are locally generated, so they are
|
| |
Description - the issues:
cleanallruv - make abort tasks 'certify and adjust timeout;
encryption_cl5 - add the encryption attributes to the schema
(we had used extensibleObject before but now we use nsChangelogConfig);
tombstone - fix how Tombstone(DSLdapObject) handles the searches.
https://pagure.io/389-ds-base/issue/49761
Reviewed by: ?