Пример #1
0
def test_delete(topology_st, backend):
    """Delete the backend and check that mapping tree and index were deleted too

    :id: d44dac3a-dae8-48e8-bd43-5be15237d093
    :setup: Standalone instance
    :steps:
        1. Create a backend
        2. Delete the backend
        3. Check all backend indexes were deleted
        4. Check backend mapping tree was deleted
    :expectedresults:
        1. Operation should be successful
        2. Operation should be successful
        3. There should be no backend indexes
        4. There should be no backend mapping tree
    """

    log.info('Delete a backend')
    backend.delete()

    log.info("Check that all indices are deleted")
    indexes = Indexes(topology_st.standalone, "cn=index,{}".format(backend.dn))
    assert not indexes.list()

    with pytest.raises(ldap.NO_SUCH_OBJECT):
        mts = MappingTrees(topology_st.standalone)
        mts.get(BACKEND_NAME_1)
Пример #2
0
def test_healthcheck_disabled_suffix(topology_st):
    """Test that we report when a suffix is disabled

    :id: 49ebce72-7e7b-4eff-8bd9-8384d12251b4
    :setup: Standalone Instance
    :steps:
        1. Disable suffix
        2. Use HealthCheck without --json option
        3. Use HealthCheck with --json option
    :expectedresults:
        1. Success
        2. HealthCheck should return code DSBLE0002
        3. HealthCheck should return code DSBLE0002
    """

    RET_CODE = 'DSBLE0002'

    mts = MappingTrees(topology_st.standalone)
    mt = mts.get(DEFAULT_SUFFIX)
    mt.replace("nsslapd-state", "disabled")

    run_healthcheck_and_flush_log(topology_st,
                                  topology_st.standalone,
                                  RET_CODE,
                                  json=False)
    run_healthcheck_and_flush_log(topology_st,
                                  topology_st.standalone,
                                  RET_CODE,
                                  json=True)

    # reset the suffix state
    mt.replace("nsslapd-state", "backend")
Пример #3
0
def test_mappingtree(topology):

    mts = MappingTrees(topology.standalone)
    mt = mts.create(properties={
        'cn': ["dc=newexample,dc=com",],
        'nsslapd-state' : 'backend',
        'nsslapd-backend' : 'someRoot',
        })

    rmt = mts.get('someRoot')
    rmt.delete()
Пример #4
0
def test_healthcheck_unable_to_query_backend(topology_st):
    """Check if HealthCheck returns DSBLE0002 code

    :id: 716b1ff1-94bd-4780-98b8-96ff8ef21e30
    :setup: Standalone instance
    :steps:
        1. Create DS instance
        2. Create a new root suffix and database
        3. Disable new suffix
        4. Use HealthCheck without --json option
        5. Use HealthCheck with --json option
    :expectedresults:
        1. Success
        2. Success
        3. Success
        4. HealthCheck should return code DSBLE0002
        5. HealthCheck should return code DSBLE0002
    """

    RET_CODE = 'DSBLE0002'
    NEW_SUFFIX = 'dc=test,dc=com'
    NEW_BACKEND = 'userData'

    standalone = topology_st.standalone

    log.info('Create new suffix')
    backends = Backends(standalone)
    backends.create(properties={
        'cn': NEW_BACKEND,
        'nsslapd-suffix': NEW_SUFFIX,
    })

    log.info('Disable the newly created suffix')
    mts = MappingTrees(standalone)
    mt_new = mts.get(NEW_SUFFIX)
    mt_new.replace('nsslapd-state', 'disabled')

    run_healthcheck_and_flush_log(topology_st,
                                  standalone,
                                  RET_CODE,
                                  json=False)
    run_healthcheck_and_flush_log(topology_st, standalone, RET_CODE, json=True)

    log.info('Enable the suffix again and check if nothing is broken')
    mt_new.replace('nsslapd-state', 'backend')
    run_healthcheck_and_flush_log(topology_st,
                                  standalone,
                                  RET_CODE,
                                  json=False)
    run_healthcheck_and_flush_log(topology_st, standalone, RET_CODE, json=True)
Пример #5
0
def test_healthcheck_backend_missing_mapping_tree(topology_st):
    """Check if HealthCheck returns DSBLE0001 and DSBLE0003 code

    :id: 4c83ffcf-01a4-4ec8-a3d2-01022b566225
    :setup: Standalone instance
    :steps:
        1. Create DS instance
        2. Disable the dc=example,dc=com backend suffix entry in the mapping tree
        3. Use HealthCheck without --json option
        4. Use HealthCheck with --json option
        5. Enable the dc=example,dc=com backend suffix entry in the mapping tree
        6. Use HealthCheck without --json option
        7. Use HealthCheck with --json option
    :expectedresults:
        1. Success
        2. Success
        3. Healthcheck reports DSBLE0001 and DSBLE0003 codes and related details
        4. Healthcheck reports DSBLE0001 and DSBLE0003 codes and related details
        5. Success
        6. Healthcheck reports no issue found
        7. Healthcheck reports no issue found
    """

    RET_CODE1 = 'DSBLE0001'
    RET_CODE2 = 'DSBLE0003'

    standalone = topology_st.standalone

    log.info(
        'Delete the dc=example,dc=com backend suffix entry in the mapping tree'
    )
    mts = MappingTrees(standalone)
    mt = mts.get(DEFAULT_SUFFIX)
    mt.delete()

    run_healthcheck_and_flush_log(topology_st,
                                  standalone,
                                  RET_CODE1,
                                  json=False,
                                  searched_code2=RET_CODE2)
    run_healthcheck_and_flush_log(topology_st,
                                  standalone,
                                  RET_CODE1,
                                  json=True,
                                  searched_code2=RET_CODE2)

    log.info('Create the dc=example,dc=com backend suffix entry')
    mts.create(
        properties={
            'cn': DEFAULT_SUFFIX,
            'nsslapd-state': 'backend',
            'nsslapd-backend': 'userRoot',
        })

    run_healthcheck_and_flush_log(topology_st,
                                  standalone,
                                  CMD_OUTPUT,
                                  json=False)
    run_healthcheck_and_flush_log(topology_st,
                                  standalone,
                                  JSON_OUTPUT,
                                  json=True)
Пример #6
0
class ChainingLink(DSLdapObject):
    """Chaining Backend DSLdapObject with:
    - must attributes = ['cn', 'nsslapd-suffix', 'nsmultiplexorbinddn',
                         'nsmultiplexorcredentials', 'nsfarmserverurl'
    - RDN attribute is 'cn'

    :param instance: An instance
    :type instance: lib389.DirSrv
    :param dn: Entry DN
    :type dn: str
    """

    _must_attributes = ['nsslapd-suffix', 'cn']

    def __init__(self, instance, dn=None, rdn=None):
        super(ChainingLink, self).__init__(instance, dn)
        self._rdn_attribute = 'cn'
        self._must_attributes = [
            'nsslapd-suffix', 'cn', 'nsmultiplexorbinddn',
            'nsmultiplexorcredentials', 'nsfarmserverurl'
        ]
        self._create_objectclasses = [
            'top', 'extensibleObject', BACKEND_OBJECTCLASS_VALUE
        ]
        self._protected = False
        self._basedn = "cn=chaining database,cn=plugins,cn=config"
        self._mts = MappingTrees(self._instance)

    def get_monitor(self):
        """Get a MonitorChaining(DSLdapObject) for the chaining link
        :returns - chaining monitor entry
        """
        return MonitorChaining(instance=self._instance,
                               dn="cn=monitor,%s" % self._dn)

    def del_link(self):
        """
        Remove the link from the parent suffix backend entry
        Delete chaining monitor entry
        Delete chaining entry
        """

        rdn = self.get_attr_val_utf8_l('cn')
        try:
            mt = self._mts.get(selector=rdn)
            mt.delete()
        except ldap.NO_SUCH_OBJECT:
            # Righto, it's already gone! Do nothing ...
            pass

        # Delete the monitoring entry
        monitor = self.get_monitor(rdn)
        monitor.delete()

        # Delete the link
        self.delete()

    def create(self, rdn=None, properties=None, basedn=None):
        """Create the link entry, and the mapping tree entry(if needed)
        """

        # Create chaining entry
        super(ChainingLink, self).create(rdn, properties, basedn)

        # Create mapping tree entry
        dn_comps = ldap.explode_dn(properties['nsslapd-suffix'][0])
        parent_suffix = ','.join(dn_comps[1:])
        mt_properties = {
            'cn': properties['nsslapd-suffix'][0],
            'nsslapd-state': 'backend',
            'nsslapd-backend': properties['cn'][0],
            'nsslapd-parent-suffix': parent_suffix
        }
        try:
            self._mts.ensure_state(properties=mt_properties)
        except ldap.ALREADY_EXISTS:
            pass
Пример #7
0
class Backend(DSLdapObject):
    """Backend DSLdapObject with:
    - must attributes = ['cn', 'nsslapd-suffix']
    - RDN attribute is 'cn'

    :param instance: An instance
    :type instance: lib389.DirSrv
    :param dn: Entry DN
    :type dn: str
    """

    _must_attributes = ['nsslapd-suffix', 'cn']

    def __init__(self, instance, dn=None):
        super(Backend, self).__init__(instance, dn)
        self._rdn_attribute = 'cn'
        self._must_attributes = ['nsslapd-suffix', 'cn']
        self._create_objectclasses = [
            'top', 'extensibleObject', BACKEND_OBJECTCLASS_VALUE
        ]
        self._protected = False
        # Check if a mapping tree for this suffix exists.
        self._mts = MappingTrees(self._instance)

    def lint_uid(self):
        return self.get_attr_val_utf8_l('cn').lower()

    def _lint_virt_attrs(self):
        """Check if any virtual attribute are incorrectly indexed"""
        bename = self.lint_uid()
        indexes = self.get_indexes()
        suffix = self.get_attr_val_utf8('nsslapd-suffix')
        # First check nsrole
        try:
            indexes.get('nsrole')
            report = copy.deepcopy(DSVIRTLE0001)
            report['check'] = f'backends:{bename}:virt_attrs'
            report['detail'] = report['detail'].replace('ATTR', 'nsrole')
            report['fix'] = report['fix'].replace('ATTR', 'nsrole')
            report['fix'] = report['fix'].replace('SUFFIX', suffix)
            report['fix'] = report['fix'].replace('YOUR_INSTANCE',
                                                  self._instance.serverid)
            report['items'].append(suffix)
            report['items'].append('nsrole')
            yield report
        except:
            pass

        # Check COS next
        for cosDefType in [
                CosIndirectDefinitions, CosPointerDefinitions,
                CosClassicDefinitions
        ]:
            defs = cosDefType(self._instance, suffix).list()
            for cosDef in defs:
                attrs = cosDef.get_attr_val_utf8_l("cosAttribute").split()
                for attr in attrs:
                    if attr in [
                            "default", "override", "operational",
                            "operational-default", "merge-schemes"
                    ]:
                        # We are at the end, just break out
                        break
                    try:
                        indexes.get(attr)
                        # If we got here there is an index (bad)
                        report = copy.deepcopy(DSVIRTLE0001)
                        report['check'] = f'backends:{bename}:virt_attrs'
                        report['detail'] = report['detail'].replace(
                            'ATTR', attr)
                        report['fix'] = report['fix'].replace('ATTR', attr)
                        report['fix'] = report['fix'].replace('SUFFIX', suffix)
                        report['fix'] = report['fix'].replace(
                            'YOUR_INSTANCE', self._instance.serverid)
                        report['items'].append(suffix)
                        report['items'].append("Class Of Service (COS)")
                        report['items'].append("cosAttribute: " + attr)
                        yield report
                    except:
                        # this is what we hope for
                        pass

    def _lint_search(self):
        """Perform a search and make sure an entry is accessible
        """
        dn = self.get_attr_val_utf8('nsslapd-suffix')
        bename = self.lint_uid()
        suffix = DSLdapObject(self._instance, dn=dn)
        try:
            suffix.get_attr_val('objectclass')
        except ldap.NO_SUCH_OBJECT:
            # backend root entry not created yet
            DSBLE0003['items'] = [
                dn,
            ]
            DSBLE0003['check'] = f'backends:{bename}:search'
            yield DSBLE0003
        except ldap.LDAPError as e:
            # Some other error
            DSBLE0002['detail'] = DSBLE0002['detail'].replace('ERROR', str(e))
            DSBLE0002['check'] = f'backends:{bename}:search'
            DSBLE0002['items'] = [
                dn,
            ]
            yield DSBLE0002

    def _lint_mappingtree(self):
        """Backend lint

        This should check for:
        * missing mapping tree entries for the backend
        * missing indices if we are local and have log access?
        """
        # Check for the missing mapping tree.
        suffix = self.get_attr_val_utf8('nsslapd-suffix')
        bename = self.lint_uid()
        try:
            mt = self._mts.get(suffix)
            if mt.get_attr_val_utf8(
                    'nsslapd-backend') != bename and mt.get_attr_val_utf8(
                        'nsslapd-state') != 'backend':
                raise ldap.NO_SUCH_OBJECT(
                    "We have a matching suffix, but not a backend or correct database name."
                )
        except ldap.NO_SUCH_OBJECT:
            result = DSBLE0001
            result['check'] = f'backends:{bename}:mappingtree'
            result['items'] = [
                bename,
            ]
            yield result

    def _lint_cl_trimming(self):
        """Check that cl trimming is at least defined to prevent unbounded growth"""
        bename = self.lint_uid()
        suffix = self.get_attr_val_utf8('nsslapd-suffix')
        replicas = Replicas(self._instance)
        try:
            # Check if replication is enabled
            replicas.get(suffix)
            # Check the changelog
            cl = Changelog(self._instance, suffix=suffix)
            if cl.get_attr_val_utf8('nsslapd-changelogmaxentries') is None and \
               cl.get_attr_val_utf8('nsslapd-changelogmaxage') is None:
                report = copy.deepcopy(DSCLLE0001)
                report['fix'] = report['fix'].replace('YOUR_INSTANCE',
                                                      self._instance.serverid)
                report['check'] = f'backends:{bename}::cl_trimming'
                yield report
        except:
            # Suffix is not replicated
            self._log.debug(
                f"_lint_cl_trimming - backend ({suffix}) is not replicated")
            pass

    def create_sample_entries(self, version):
        """Creates sample entries under nsslapd-suffix value

        :param version: Sample entries version, i.e. 001003006
        :type version: str
        """

        self._log.debug('Requested sample entries at version %s....' % version)
        # Grab the correct sample entry config - remember this is a function ptr.
        centries = get_sample_entries(version)
        # apply it.
        basedn = self.get_attr_val('nsslapd-suffix')
        cent = centries(self._instance, basedn)
        # Now it's built, we can get the version for logging.
        self._log.debug('Creating sample entries at version %s' % cent.version)
        cent.apply()

    def _validate(self, rdn, properties, basedn):
        # We always need to call the super validate first. This way we can
        # guarantee that properties is a dictionary.
        # However, backend can take different properties. One is
        # based on the actual key, value of the object
        # one is the "python magic" types.
        # So we actually have to do the super validation later.
        if properties is None:
            raise ldap.UNWILLING_TO_PERFORM(
                'Invalid request to create. Properties cannot be None')
        if type(properties) != dict:
            raise ldap.UNWILLING_TO_PERFORM("properties must be a dictionary")

        # This is converting the BACKEND_ types to the DS nsslapd- attribute values
        nprops = {}
        for key, value in properties.items():
            try:
                nprops[BACKEND_PROPNAME_TO_ATTRNAME[key]] = [
                    value,
                ]
            except KeyError:
                # This means, it's not a mapped value, so continue
                nprops[key] = value

        (dn, valid_props) = super(Backend, self)._validate(rdn, nprops, basedn)

        try:
            self._mts.get(ensure_str(valid_props['nsslapd-suffix'][0]))
            raise ldap.UNWILLING_TO_PERFORM(
                "Mapping tree for this suffix exists!")
        except ldap.NO_SUCH_OBJECT:
            pass
        try:
            self._mts.get(ensure_str(valid_props['cn'][0]))
            raise ldap.UNWILLING_TO_PERFORM(
                "Mapping tree for this database exists!")
        except ldap.NO_SUCH_OBJECT:
            pass
        # We have to stash our valid props so that mapping tree can use them ...
        self._nprops_stash = valid_props

        return (dn, valid_props)

    def create(self,
               dn=None,
               properties=None,
               basedn=DN_LDBM,
               create_mapping_tree=True):
        """Add a new backend entry, create mapping tree,
         and, if requested, sample entries

        :param dn: DN of the new entry
        :type dn: str
        :param properties: Attributes and parameters for the new entry
        :type properties: dict
        :param basedn: Base DN of the new entry
        :type basedn: str
        :param create_mapping_tree: If a related mapping tree node should be created
        :type create_mapping_tree: bool

        :returns: DSLdapObject of the created entry
        """

        sample_entries = False
        parent_suffix = False

        # normalize suffix (remove spaces between comps)
        if dn is not None:
            dn_comps = ldap.dn.explode_dn(dn.lower())
            dn = ",".join(dn_comps)

        if properties is not None:
            suffix_dn = properties['nsslapd-suffix'].lower()
            dn_comps = ldap.dn.explode_dn(suffix_dn)
            ndn = ",".join(dn_comps)
            properties['nsslapd-suffix'] = ndn
            sample_entries = properties.pop(BACKEND_SAMPLE_ENTRIES, False)
            parent_suffix = properties.pop('parent', False)

        # Okay, now try to make the backend.
        super(Backend, self).create(dn, properties, basedn)

        # We check if the mapping tree exists in create, so do this *after*
        if create_mapping_tree is True:
            properties = {
                'cn': self._nprops_stash['nsslapd-suffix'],
                'nsslapd-state': 'backend',
                'nsslapd-backend': self._nprops_stash['cn'],
            }
            if parent_suffix:
                # This is a subsuffix, set the parent suffix
                properties['nsslapd-parent-suffix'] = parent_suffix
            self._mts.create(properties=properties)

        # We can't create the sample entries unless a mapping tree was installed.
        if sample_entries is not False and create_mapping_tree is True:
            self.create_sample_entries(sample_entries)
        return self

    def delete(self):
        """Deletes the backend, it's mapping tree and all related indices.
        This can be changed with the self._protected flag!

        :raises: - UnwillingToPerform - if backend is protected
                 - UnwillingToPerform - if nsslapd-state is not 'backend'
        """

        if self._protected:
            raise ldap.UNWILLING_TO_PERFORM("This is a protected backend!")
        # First check if the mapping tree has our suffix still.
        # suffix = self.get_attr_val('nsslapd-suffix')
        bename = self.get_attr_val_utf8('cn')
        try:
            mt = self._mts.get(selector=bename)
            # Assert the type is "backend"
            # Are these the right types....?
            if mt.get_attr_val_utf8('nsslapd-state').lower() != 'backend':
                raise ldap.UNWILLING_TO_PERFORM(
                    'Can not delete the mapping tree, not for a backend! You may need to delete this backend via cn=config .... ;_; '
                )

            # Delete replicas first
            try:
                Replicas(self._instance).get(
                    mt.get_attr_val_utf8('cn')).delete()
            except ldap.NO_SUCH_OBJECT:
                # No replica, no problem
                pass

            # Delete our mapping tree if it exists.
            mt.delete()
        except ldap.NO_SUCH_OBJECT:
            # Righto, it's already gone! Do nothing ...
            pass

        # Now remove our children, this is all ldbm config
        self._instance.delete_branch_s(self._dn, ldap.SCOPE_SUBTREE)

    def get_suffix(self):
        return self.get_attr_val_utf8_l('nsslapd-suffix')

    def disable(self):
        # Disable backend (mapping tree)
        suffix = self.get_attr_val_utf8_l('nsslapd-suffix')
        mt = self._mts.get(suffix)
        mt.set('nsslapd-nsstate', 'Disabled')

    def enable(self):
        # Enable Backend (mapping tree)
        suffix = self.get_attr_val_utf8_l('nsslapd-suffix')
        mt = self._mts.get(suffix)
        mt.set('nsslapd-nsstate', 'Backend')

    def get_mapping_tree(self):
        suffix = self.get_attr_val_utf8('nsslapd-suffix')
        return self._mts.get(suffix)

    def get_monitor(self):
        """Get a MonitorBackend(DSLdapObject) for the backend"""

        monitor = MonitorBackend(instance=self._instance,
                                 dn="cn=monitor,%s" % self._dn)
        return monitor

    def get_indexes(self):
        """Get an Indexes(DSLdapObject) for the backend"""

        indexes = Indexes(self._instance, basedn="cn=index,%s" % self._dn)
        return indexes

    def get_index(self, attr_name):
        for index in self.get_indexes().list():
            idx_name = index.get_attr_val_utf8_l('cn').lower()
            if idx_name == attr_name.lower():
                return index
        return None

    def del_index(self, attr_name):
        for index in self.get_indexes().list():
            idx_name = index.get_attr_val_utf8_l('cn').lower()
            if idx_name == attr_name.lower():
                index.delete()
                return
        raise ValueError("Can not delete index because it does not exist")

    def add_index(self, attr_name, types, matching_rules=[], reindex=False):
        """ Add an index.

        :param attr_name - name of the attribute to index
        :param types - a List of index types(eq, pres, sub, approx)
        :param matching_rules - a List of matching rules for the index
        :param reindex - If set to True then index the attribute after creating it.
        """
        new_index = Index(self._instance)
        props = {
            'cn': attr_name,
            'nsSystemIndex': 'False',
            'nsIndexType': types,
        }
        if matching_rules is not None:
            mrs = []
            for mr in matching_rules:
                mrs.append(mr)
            props['nsMatchingRule'] = mrs
        new_index.create(properties=props, basedn="cn=index," + self._dn)

        if reindex:
            self.reindex(attr_name)

    def reindex(self, attrs=None, wait=False):
        """Reindex the attributes for this backend
        :param attrs - an optional list of attributes to index
        :param wait - Set to true to wait for task to complete
        """
        args = None
        if wait:
            args = {TASK_WAIT: True}
        bename = ensure_str(self.get_attr_val_bytes('cn'))
        reindex_task = Tasks(self._instance)
        reindex_task.reindex(benamebase=bename, attrname=attrs, args=args)

    def get_encrypted_attrs(self, just_names=False):
        """Get a list of the excrypted attributes
        :param just_names - If True only the encrypted attribute names are returned (instead of the full attribute entry)
        :returns - a list of attributes
        """
        attrs = EncryptedAttrs(self._instance, basedn=self._dn).list()
        if just_names:
            results = []
            for attr in attrs:
                results.append(attr.get_attr_val_utf8_l('cn'))
            return results
        else:
            return attrs

    def add_encrypted_attr(self, attr_name):
        """Add an encrypted attribute
        :param attr_name - name of the new encrypted attribute
        """
        new_attr = EncryptedAttr(self._instance)
        new_attr.create(basedn="cn=encrypted attributes," + self._dn,
                        properties={
                            'cn': attr_name,
                            'nsEncryptionAlgorithm': 'AES'
                        })

    def del_encrypted_attr(self, attr_name):
        """Delete encrypted attribute
        :param attr_name - Name of the encrypted attribute to delete
        """
        enc_attrs = EncryptedAttrs(self._instance,
                                   basedn="cn=encrypted attributes," +
                                   self._dn).list()
        for enc_attr in enc_attrs:
            attr = enc_attr.get_attr_val_utf8_l('cn').lower()
            if attr_name == attr.lower():
                enc_attr.delete()
                break

    def import_ldif(self,
                    ldifs,
                    chunk_size=None,
                    encrypted=False,
                    gen_uniq_id=None,
                    only_core=False,
                    include_suffixes=None,
                    exclude_suffixes=None):
        """Do an import of the suffix"""

        bs = Backends(self._instance)
        task = bs.import_ldif(self.rdn, ldifs, chunk_size, encrypted,
                              gen_uniq_id, only_core, include_suffixes,
                              exclude_suffixes)
        return task

    def export_ldif(self,
                    ldif=None,
                    use_id2entry=False,
                    encrypted=False,
                    min_base64=False,
                    no_uniq_id=False,
                    replication=False,
                    not_folded=False,
                    no_seq_num=False,
                    include_suffixes=None,
                    exclude_suffixes=None):
        """Do an export of the suffix"""

        bs = Backends(self._instance)
        task = bs.export_ldif(self.rdn, ldif, use_id2entry, encrypted,
                              min_base64, no_uniq_id, replication, not_folded,
                              no_seq_num, include_suffixes, exclude_suffixes)
        return task

    def get_vlv_searches(self, vlv_name=None):
        """Return the VLV seaches for this backend, or return a specific search
        :param vlv_name - name of a VLV search entry to return
        :returns - A list of VLV searches or a single VLV sarch entry
        """
        vlv_searches = VLVSearches(self._instance, basedn=self._dn).list()
        if vlv_name is None:
            return vlv_searches

        # return specific search
        for vlv in vlv_searches:
            search_name = vlv.get_attr_val_utf8_l('cn').lower()
            if search_name == vlv_name.lower():
                return vlv

        # No match
        raise ValueError("Failed to find VLV search entry")

    def add_vlv_search(self, vlvname, props, reindex=False):
        """Add a VLV search entry
        :param: vlvname - Name of the new VLV search entry
        :props - A dict of the attribute value pairs for the VLV search entry
        :param - reindex - Set to True to index the new attribute right away
        """
        basedn = self._dn
        vlv = VLVSearch(instance=self._instance)
        vlv.create(rdn="cn=" + vlvname, properties=props, basedn=basedn)

    def get_sub_suffixes(self):
        """Return a list of Backend's
        returns: a List of subsuffix entries
        """
        subsuffixes = []
        top_be_suffix = self.get_attr_val_utf8_l('nsslapd-suffix').lower()
        mts = self._mts.list()
        for mt in mts:
            parent_suffix = mt.get_attr_val_utf8_l('nsslapd-parent-suffix')
            if parent_suffix is None:
                continue
            if parent_suffix.lower() == top_be_suffix:
                child_suffix = mt.get_attr_val_utf8_l('cn').lower()
                be_insts = Backends(self._instance).list()
                for be in be_insts:
                    be_suffix = ensure_str(
                        be.get_attr_val_utf8_l('nsslapd-suffix')).lower()
                    if child_suffix == be_suffix:
                        subsuffixes.append(be)
                        break
        return subsuffixes

    def get_cos_indirect_defs(self):
        return CosIndirectDefinitions(self._instance, self._dn).list()

    def get_cos_pointer_defs(self):
        return CosPointerDefinitions(self._instance, self._dn).list()

    def get_cos_classic_defs(self):
        return CosClassicDefinitions(self._instance, self._dn).list()

    def get_cos_templates(self):
        return CosTemplates(self._instance, self._dn).list()
Пример #8
0
class Backend(DSLdapObject):
    """Backend DSLdapObject with:
    - must attributes = ['cn', 'nsslapd-suffix']
    - RDN attribute is 'cn'

    :param instance: An instance
    :type instance: lib389.DirSrv
    :param dn: Entry DN
    :type dn: str
    """

    _must_attributes = ['nsslapd-suffix', 'cn']

    def __init__(self, instance, dn=None):
        super(Backend, self).__init__(instance, dn)
        self._rdn_attribute = 'cn'
        self._must_attributes = ['nsslapd-suffix', 'cn']
        self._create_objectclasses = [
            'top', 'extensibleObject', BACKEND_OBJECTCLASS_VALUE
        ]
        self._protected = False
        self._lint_functions = [self._lint_mappingtree]
        # Check if a mapping tree for this suffix exists.
        self._mts = MappingTrees(self._instance)

    def create_sample_entries(self, version):
        """Creates sample entries under nsslapd-suffix value

        :param version: Sample entries version, i.e. 001003006
        :type version: str
        """

        self._log.debug('Creating sample entries at version %s....' % version)
        # Grab the correct sample entry config
        centries = get_sample_entries(version)
        # apply it.
        basedn = self.get_attr_val('nsslapd-suffix')
        cent = centries(self._instance, basedn)
        cent.apply()

    def _validate(self, rdn, properties, basedn):
        # We always need to call the super validate first. This way we can
        # guarantee that properties is a dictionary.
        # However, backend can take different properties. One is
        # based on the actual key, value of the object
        # one is the "python magic" types.
        # So we actually have to do the super validation later.
        if properties is None:
            raise ldap.UNWILLING_TO_PERFORM(
                'Invalid request to create. Properties cannot be None')
        if type(properties) != dict:
            raise ldap.UNWILLING_TO_PERFORM("properties must be a dictionary")

        # This is converting the BACKEND_ types to the DS nsslapd- attribute values
        nprops = {}
        for key, value in properties.items():
            try:
                nprops[BACKEND_PROPNAME_TO_ATTRNAME[key]] = [
                    value,
                ]
            except KeyError:
                # This means, it's not a mapped value, so continue
                nprops[key] = value

        (dn, valid_props) = super(Backend, self)._validate(rdn, nprops, basedn)

        try:
            self._mts.get(ensure_str(valid_props['nsslapd-suffix'][0]))
            raise ldap.UNWILLING_TO_PERFORM(
                "Mapping tree for this suffix exists!")
        except ldap.NO_SUCH_OBJECT:
            pass
        try:
            self._mts.get(ensure_str(valid_props['cn'][0]))
            raise ldap.UNWILLING_TO_PERFORM(
                "Mapping tree for this database exists!")
        except ldap.NO_SUCH_OBJECT:
            pass
        # We have to stash our valid props so that mapping tree can use them ...
        self._nprops_stash = valid_props

        return (dn, valid_props)

    def create(self, dn=None, properties=None, basedn=None):
        """Add a new backend entry, create mapping tree,
         and, if requested, sample entries

        :param rdn: RDN of the new entry
        :type rdn: str
        :param properties: Attributes and parameters for the new entry
        :type properties: dict
        :param basedn: Base DN of the new entry
        :type rdn: str

        :returns: DSLdapObject of the created entry
        """

        sample_entries = properties.pop(BACKEND_SAMPLE_ENTRIES, False)
        # Okay, now try to make the backend.
        super(Backend, self).create(dn, properties, basedn)
        # We check if the mapping tree exists in create, so do this *after*
        self._mts.create(
            properties={
                'cn': self._nprops_stash['nsslapd-suffix'],
                'nsslapd-state': 'backend',
                'nsslapd-backend': self._nprops_stash['cn'],
            })
        if sample_entries is not False:
            self.create_sample_entries(sample_entries)
        return self

    def delete(self):
        """Deletes the backend, it's mapping tree and all related indices.
        This can be changed with the self._protected flag!

        :raises: - UnwillingToPerform - if backend is protected
                 - UnwillingToPerform - if nsslapd-state is not 'backend'
        """

        if self._protected:
            raise ldap.UNWILLING_TO_PERFORM("This is a protected backend!")
        # First check if the mapping tree has our suffix still.
        # suffix = self.get_attr_val('nsslapd-suffix')
        bename = self.get_attr_val_utf8('cn')
        try:
            mt = self._mts.get(selector=bename)
            # Assert the type is "backend"
            # Are these the right types....?
            if mt.get_attr_val('nsslapd-state') != ensure_bytes('backend'):
                raise ldap.UNWILLING_TO_PERFORM(
                    'Can not delete the mapping tree, not for a backend! You may need to delete this backend via cn=config .... ;_; '
                )
            # Delete our mapping tree if it exists.
            mt.delete()
        except ldap.NO_SUCH_OBJECT:
            # Righto, it's already gone! Do nothing ...
            pass
        # Delete all our related indices
        self._instance.index.delete_all(bename)

        # Now remove our children, this is all ldbm config

        configs = self._instance.search_s(self._dn, ldap.SCOPE_ONELEVEL)
        for c in configs:
            self._instance.delete_branch_s(c.dn, ldap.SCOPE_SUBTREE)
        # The super will actually delete ourselves.
        super(Backend, self).delete()

    def _lint_mappingtree(self):
        """Backend lint

        This should check for:
        * missing mapping tree entries for the backend
        * missing indices if we are local and have log access?
        """

        # Check for the missing mapping tree.
        suffix = self.get_attr_val_utf8('nsslapd-suffix')
        bename = self.get_attr_val_bytes('cn')
        try:
            mt = self._mts.get(suffix)
            if mt.get_attr_val_bytes(
                    'nsslapd-backend') != bename and mt.get_attr_val(
                        'nsslapd-state') != ensure_bytes('backend'):
                raise ldap.NO_SUCH_OBJECT(
                    "We have a matching suffix, but not a backend or correct database name."
                )
        except ldap.NO_SUCH_OBJECT:
            result = DSBLE0001
            result['items'] = [
                bename,
            ]
            return result
        return None

    def get_mapping_tree(self):
        suffix = self.get_attr_val_utf8('nsslapd-suffix')
        return self._mts.get(suffix)

    def get_monitor(self):
        """Get a MonitorBackend(DSLdapObject) for the backend"""

        monitor = MonitorBackend(instance=self._instance,
                                 dn="cn=monitor,%s" % self._dn)
        return monitor

    def get_indexes(self):
        """Get an Indexes(DSLdapObject) for the backend"""

        indexes = Indexes(self._instance, basedn="cn=index,%s" % self._dn)
        return indexes