Beispiel #1
0
 def gen_ini(self):
     yield '\n[{0}]\n'.format(self.name)
     sections_dict = OrderedDict()
     for name, value in self.iteritems():
         if com_regx.match(name):
             yield '{0}\n'.format(value)
         elif isinstance(value, _Section):
             sections_dict.update({name: value})
         else:
             yield '{0} {1} {2}\n'.format(name, self.sep, value)
     for name, value in sections_dict.iteritems():
         for line in value.gen_ini():
             yield line
Beispiel #2
0
 def gen_ini(self):
     yield "{0}[{1}]{0}".format(os.linesep, self.name)
     sections_dict = OrderedDict()
     for name, value in six.iteritems(self):
         if com_regx.match(name):
             yield "{0}{1}".format(value, os.linesep)
         elif isinstance(value, _Section):
             sections_dict.update({name: value})
         else:
             yield "{0}{1}{2}{3}".format(
                 name, (" {0} ".format(self.sep) if self.sep != " " else self.sep), value, os.linesep
             )
     for name, value in six.iteritems(sections_dict):
         for line in value.gen_ini():
             yield line
Beispiel #3
0
 def _uncomment_if_commented(self, opt_key):
     # should be called only if opt_key is not already present
     # will uncomment the key if commented and create a place holder
     # for the key where the correct value can be update later
     # used to preserve the ordering of comments and commented options
     # and to make sure options without sectons go above any section
     options_backup = OrderedDict()
     comment_index = None
     for key, value in self.iteritems():
         if comment_index is not None:
             options_backup.update({key: value})
             continue
         if '#comment' not in key:
             continue
         opt_match = opt_regx.match(value.lstrip('#'))
         if opt_match and opt_match.group(2) == opt_key:
             comment_index = key
     for key in options_backup:
         self.pop(key)
     self.pop(comment_index, None)
     super(_Section, self).update({opt_key: None})
     for key, value in options_backup.iteritems():
         super(_Section, self).update({key: value})
Beispiel #4
0
    def serialize(cls, id_=None):
        # The order matters
        serialized = OrderedDict()
        if id_ is not None:
            # This is meant as a configuration section, sub json schema
            serialized['id'] = '{0}/{1}.json#'.format(BASE_SCHEMA_URL, id_)
        else:
            # Main configuration block, json schema
            serialized['$schema'] = 'http://json-schema.org/draft-04/schema#'
        if cls.title is not None:
            serialized['title'] = cls.title
        if cls.description is not None:
            if cls.description == cls.__doc__:
                serialized['description'] = textwrap.dedent(cls.description).strip()
            else:
                serialized['description'] = cls.description

        required = []
        ordering = []
        serialized['type'] = 'object'
        properties = OrderedDict()
        cls.after_items_update = []
        for name in cls._order:
            skip_order = False
            if name in cls._sections:  # pylint: disable=E1135
                section = cls._sections[name]
                serialized_section = section.serialize(None if section.__flatten__ is True else name)
                if section.__flatten__ is True:
                    # Flatten the configuration section into the parent
                    # configuration
                    properties.update(serialized_section['properties'])
                    if 'x-ordering' in serialized_section:
                        ordering.extend(serialized_section['x-ordering'])
                    if 'required' in serialized_section:
                        required.extend(serialized_section['required'])
                    if hasattr(section, 'after_items_update'):
                        cls.after_items_update.extend(section.after_items_update)
                    skip_order = True
                else:
                    # Store it as a configuration section
                    properties[name] = serialized_section

            if name in cls._items:  # pylint: disable=E1135
                config = cls._items[name]
                # Handle the configuration items defined in the class instance
                if config.__flatten__ is True:
                    serialized_config = config.serialize()
                    cls.after_items_update.append(serialized_config)
                    skip_order = True
                else:
                    properties[name] = config.serialize()

                if config.required:
                    # If it's a required item, add it to the required list
                    required.append(name)

            if skip_order is False:
                # Store the order of the item
                if name not in ordering:
                    ordering.append(name)

        if properties:
            serialized['properties'] = properties

        # Update the serialized object with any items to include after properties
        if cls.after_items_update:
            after_items_update = {}
            for entry in cls.after_items_update:
                name, data = next(six.iteritems(entry))
                if name in after_items_update:
                    after_items_update[name].extend(data)
                else:
                    after_items_update[name] = data
            serialized.update(after_items_update)

        if required:
            # Only include required if not empty
            serialized['required'] = required
        if ordering:
            # Only include ordering if not empty
            serialized['x-ordering'] = ordering
        serialized['additionalProperties'] = cls.__allow_additional_items__
        return serialized
Beispiel #5
0
def iostat(zpool=None, sample_time=5, parsable=True):
    '''
    Display I/O statistics for the given pools

    zpool : string
        optional name of storage pool

    sample_time : int
        seconds to capture data before output
        default a sample of 5 seconds is used
    parsable : boolean
        display data in pythonc values (True, False, Bytes,...)

    .. versionadded:: 2016.3.0
    .. versionchanged:: Fluorine

        Added ```parsable``` parameter that defaults to True

    CLI Example:

    .. code-block:: bash

        salt '*' zpool.iostat myzpool

    '''
    ret = OrderedDict()

    ## get iostat output
    res = __salt__['cmd.run_all'](
        __utils__['zfs.zpool_command'](command='iostat',
                                       flags=['-v'],
                                       target=[zpool, sample_time, 2]),
        python_shell=False,
    )

    if res['retcode'] != 0:
        return __utils__['zfs.parse_command_result'](res)

    # NOTE: command output for reference
    # =====================================================================
    #                               capacity     operations    bandwidth
    # pool                       alloc   free   read  write   read  write
    # -------------------------  -----  -----  -----  -----  -----  -----
    # mypool                      648G  1.18T     10      6  1.30M   817K
    #   mirror                    648G  1.18T     10      6  1.30M   817K
    #     c0tXXXXCXXXXXXXXXXXd0      -      -      9      5  1.29M   817K
    #     c0tXXXXCXXXXXXXXXXXd0      -      -      9      5  1.29M   817K
    #     c0tXXXXCXXXXXXXXXXXd0      -      -      9      5  1.29M   817K
    # -------------------------  -----  -----  -----  -----  -----  -----
    # =====================================================================

    ## parse iostat output
    # NOTE: hardcode the header
    #       the double header line is hard to parse, we opt to
    #       hardcode the header fields
    header = [
        'name',
        'capacity-alloc',
        'capacity-free',
        'operations-read',
        'operations-write',
        'bandwith-read',
        'bandwith-write',
    ]
    root_vdev = None
    vdev = None
    dev = None
    current_data = OrderedDict()
    for line in res['stdout'].splitlines():
        # NOTE: skip header
        if line.strip() == '' or \
           line.strip().split()[-1] in ['write', 'bandwidth']:
            continue

        # NOTE: reset pool on line separator
        if line.startswith('-') and line.endswith('-'):
            ret.update(current_data)
            current_data = OrderedDict()
            continue

        # NOTE: transform data into dict
        io_data = OrderedDict(
            list(
                zip(
                    header,
                    [x for x in line.strip().split(' ') if x not in ['']],
                )))

        # NOTE: normalize values
        if parsable:
            # NOTE: raw numbers and pythonic types
            io_data = __utils__['zfs.from_auto_dict'](io_data)
        else:
            # NOTE: human readable zfs types
            io_data = __utils__['zfs.to_auto_dict'](io_data)

        # NOTE: store io_data in the proper location
        if line.startswith(' ' * 4):
            dev = io_data['name']
            current_data[root_vdev][vdev][dev] = io_data
        elif line.startswith(' ' * 2):
            dev = None
            vdev = io_data['name']
            current_data[root_vdev][vdev] = io_data
        else:
            dev = vdev = None
            root_vdev = io_data['name']
            current_data[root_vdev] = io_data

        # NOTE: name already used as identifier, drop duplicate data
        del io_data['name']

    return ret
Beispiel #6
0
def managed(name, entries, connect_spec=None):
    """Ensure the existence (or not) of LDAP entries and their attributes

    Example:

    .. code-block:: yaml

        ldapi:///:
          ldap.managed:
            - connect_spec:
                bind:
                  method: sasl

            - entries:

              # make sure the entry doesn't exist
              - cn=foo,ou=users,dc=example,dc=com:
                - delete_others: True

              # make sure the entry exists with only the specified
              # attribute values
              - cn=admin,dc=example,dc=com:
                - delete_others: True
                - replace:
                    cn:
                      - admin
                    description:
                      - LDAP administrator
                    objectClass:
                      - simpleSecurityObject
                      - organizationalRole
                    userPassword:
                      - {{pillar.ldap_admin_password}}

              # make sure the entry exists, its olcRootDN attribute
              # has only the specified value, the olcRootDN attribute
              # doesn't exist, and all other attributes are ignored
              - 'olcDatabase={1}hdb,cn=config':
                - replace:
                    olcRootDN:
                      - cn=admin,dc=example,dc=com
                    # the admin entry has its own password attribute
                    olcRootPW: []

              # note the use of 'default'.  also note how you don't
              # have to use list syntax if there is only one attribute
              # value
              - cn=foo,ou=users,dc=example,dc=com:
                - delete_others: True
                - default:
                    userPassword: changeme
                    shadowLastChange: 0
                    # keep sshPublicKey if present, but don't create
                    # the attribute if it is missing
                    sshPublicKey: []
                - replace:
                    cn: foo
                    uid: foo
                    uidNumber: 1000
                    gidNumber: 1000
                    gecos: Foo Bar
                    givenName: Foo
                    sn: Bar
                    homeDirectory: /home/foo
                    loginShell: /bin/bash
                    objectClass:
                      - inetOrgPerson
                      - posixAccount
                      - top
                      - ldapPublicKey
                      - shadowAccount

    :param name:
        The URL of the LDAP server.  This is ignored if
        ``connect_spec`` is either a connection object or a dict with
        a ``'url'`` entry.

    :param entries:
        A description of the desired state of zero or more LDAP
        entries.

        ``entries`` is an iterable of dicts.  Each of these dict's
        keys are the distinguished names (DNs) of LDAP entries to
        manage.  Each of these dicts is processed in order.  A later
        dict can reference an LDAP entry that was already mentioned in
        an earlier dict, which makes it possible for later dicts to
        enhance or alter the desired state of an LDAP entry.

        The DNs are mapped to a description of the LDAP entry's
        desired state.  These LDAP entry descriptions are themselves
        iterables of dicts.  Each dict in the iterable is processed in
        order.  They contain directives controlling the entry's state.
        The key names the directive type and the value is state
        information for the directive.  The specific structure of the
        state information depends on the directive type.

        The structure of ``entries`` looks like this::

            [{dn1: [{directive1: directive1_state,
                     directive2: directive2_state},
                    {directive3: directive3_state}],
              dn2: [{directive4: directive4_state,
                     directive5: directive5_state}]},
             {dn3: [{directive6: directive6_state}]}]

        These are the directives:

        * ``'delete_others'``
            Boolean indicating whether to delete attributes not
            mentioned in this dict or any of the other directive
            dicts for this DN.  Defaults to ``False``.

            If you don't want to delete an attribute if present, but
            you also don't want to add it if it is missing or modify
            it if it is present, you can use either the ``'default'``
            directive or the ``'add'`` directive with an empty value
            list.

        * ``'default'``
            A dict mapping an attribute name to an iterable of default
            values for that attribute.  If the attribute already
            exists, it is left alone.  If not, it is created using the
            given list of values.

            An empty value list is useful when you don't want to
            create an attribute if it is missing but you do want to
            preserve it if the ``'delete_others'`` key is ``True``.

        * ``'add'``
            Attribute values to add to the entry.  This is a dict
            mapping an attribute name to an iterable of values to add.

            An empty value list is useful when you don't want to
            create an attribute if it is missing but you do want to
            preserve it if the ``'delete_others'`` key is ``True``.

        * ``'delete'``
            Attribute values to remove from the entry.  This is a dict
            mapping an attribute name to an iterable of values to
            delete from the attribute.  If the iterable is empty, all
            of the attribute's values are deleted.

        * ``'replace'``
            Attributes to replace.  This is a dict mapping an
            attribute name to an iterable of values.  Any existing
            values for the attribute are deleted, then the given
            values are added.  The iterable may be empty.

        In the above directives, the iterables of attribute values may
        instead be ``None``, in which case an empty list is used, or a
        scalar such as a string or number, in which case a new list
        containing the scalar is used.

        Note that if all attribute values are removed from an entry,
        the entire entry is deleted.

    :param connect_spec:
        See the description of the ``connect_spec`` parameter of the
        :py:func:`ldap3.connect <salt.modules.ldap3.connect>` function
        in the :py:mod:`ldap3 <salt.modules.ldap3>` execution module.
        If this is a dict and the ``'url'`` entry is not specified,
        the ``'url'`` entry is set to the value of the ``name``
        parameter.

    :returns:
        A dict with the following keys:

        * ``'name'``
            This is the same object passed to the ``name`` parameter.

        * ``'changes'``
            This is a dict describing the changes made (or, in test
            mode, the changes that would have been attempted).  If no
            changes were made (or no changes would have been
            attempted), then this dict is empty.  Only successful
            changes are included.

            Each key is a DN of an entry that was changed (or would
            have been changed).  Entries that were not changed (or
            would not have been changed) are not included.  The value
            is a dict with two keys:

            * ``'old'``
                The state of the entry before modification.  If the
                entry did not previously exist, this key maps to
                ``None``.  Otherwise, the value is a dict mapping each
                of the old entry's attributes to a list of its values
                before any modifications were made.  Unchanged
                attributes are excluded from this dict.

            * ``'new'``
                The state of the entry after modification.  If the
                entry was deleted, this key maps to ``None``.
                Otherwise, the value is a dict mapping each of the
                entry's attributes to a list of its values after the
                modifications were made.  Unchanged attributes are
                excluded from this dict.

            Example ``'changes'`` dict where a new entry was created
            with a single attribute containing two values::

                {'dn1': {'old': None,
                         'new': {'attr1': ['val1', 'val2']}}}

            Example ``'changes'`` dict where a new attribute was added
            to an existing entry::

                {'dn1': {'old': {},
                         'new': {'attr2': ['val3']}}}

        * ``'result'``
            One of the following values:

            * ``True`` if no changes were necessary or if all changes
              were applied successfully.
            * ``False`` if at least one change was unable to be applied.
            * ``None`` if changes would be applied but it is in test
              mode.
    """
    if connect_spec is None:
        connect_spec = {}
    try:
        connect_spec.setdefault("url", name)
    except AttributeError:
        # already a connection object
        pass

    connect = __salt__["ldap3.connect"]

    # hack to get at the ldap3 module to access the ldap3.LDAPError
    # exception class.  https://github.com/saltstack/salt/issues/27578
    ldap3 = inspect.getmodule(connect)

    with connect(connect_spec) as l:

        old, new = _process_entries(l, entries)

        # collect all of the affected entries (only the key is
        # important in this dict; would have used an OrderedSet if
        # there was one)
        dn_set = OrderedDict()
        dn_set.update(old)
        dn_set.update(new)

        # do some cleanup
        dn_to_delete = set()
        for dn in dn_set:
            o = old.get(dn, {})
            n = new.get(dn, {})
            for x in o, n:
                to_delete = set()
                for attr, vals in x.items():
                    if not vals:
                        # clean out empty attribute lists
                        to_delete.add(attr)
                for attr in to_delete:
                    del x[attr]
            if o == n:
                # clean out unchanged entries
                dn_to_delete.add(dn)
        for dn in dn_to_delete:
            for x in old, new:
                x.pop(dn, None)
            del dn_set[dn]

        ret = {
            "name": name,
            "changes": {},
            "result": None,
            "comment": "",
        }

        if old == new:
            ret["comment"] = "LDAP entries already set"
            ret["result"] = True
            return ret

        if __opts__["test"]:
            ret["comment"] = "Would change LDAP entries"
            changed_old = old
            changed_new = new
            success_dn_set = dn_set
        else:
            # execute the changes
            changed_old = OrderedDict()
            changed_new = OrderedDict()
            # assume success; these will be changed on error
            ret["result"] = True
            ret["comment"] = "Successfully updated LDAP entries"
            errs = []
            success_dn_set = OrderedDict()
            for dn in dn_set:
                o = old.get(dn, {})
                n = new.get(dn, {})

                try:
                    # perform the operation
                    if o:
                        if n:
                            op = "modify"
                            assert o != n
                            __salt__["ldap3.change"](l, dn, o, n)
                        else:
                            op = "delete"
                            __salt__["ldap3.delete"](l, dn)
                    else:
                        op = "add"
                        assert n
                        __salt__["ldap3.add"](l, dn, n)

                    # update these after the op in case an exception
                    # is raised
                    changed_old[dn] = o
                    changed_new[dn] = n
                    success_dn_set[dn] = True
                except ldap3.LDAPError as err:
                    log.exception("failed to %s entry %s (%s)", op, dn, err)
                    errs.append((op, dn, err))
                    continue

            if errs:
                ret["result"] = False
                ret["comment"] = "failed to " + ", ".join(
                    (op + " entry " + dn + "(" + str(err) + ")"
                     for op, dn, err in errs))

    # set ret['changes'].  filter out any unchanged attributes, and
    # convert the value sets to lists before returning them to the
    # user (sorted for easier comparisons)
    for dn in success_dn_set:
        o = changed_old.get(dn, {})
        n = changed_new.get(dn, {})
        changes = {}
        ret["changes"][dn] = changes
        for x, xn in ((o, "old"), (n, "new")):
            if not x:
                changes[xn] = None
                continue
            changes[xn] = {
                attr: sorted(vals)
                for attr, vals in x.items()
                if o.get(attr, ()) != n.get(attr, ())
            }

    return ret
Beispiel #7
0
def managed(name, entries, connect_spec=None):
    '''Ensure the existance (or not) of LDAP entries and their attributes

    Example:

    .. code-block:: yaml

        ldapi:///:
          ldap.managed:
            - connect_spec:
                bind:
                  method: sasl

            - entries:

              # make sure the entry doesn't exist
              - cn=foo,ou=users,dc=example,dc=com:
                - delete_others: True

              # make sure the entry exists with only the specified
              # attribute values
              - cn=admin,dc=example,dc=com:
                - delete_others: True
                - replace:
                    cn:
                      - admin
                    description:
                      - LDAP administrator
                    objectClass:
                      - simpleSecurityObject
                      - organizationalRole
                    userPassword:
                      - {{pillar.ldap_admin_password}}

              # make sure the entry exists, its olcRootDN attribute
              # has only the specified value, the olcRootDN attribute
              # doesn't exist, and all other attributes are ignored
              - 'olcDatabase={1}hdb,cn=config':
                - replace:
                    olcRootDN:
                      - cn=admin,dc=example,dc=com
                    # the admin entry has its own password attribute
                    olcRootPW: []

              # note the use of 'default'.  also note how you don't
              # have to use list syntax if there is only one attribute
              # value
              - cn=foo,ou=users,dc=example,dc=com:
                - delete_others: True
                - default:
                    userPassword: changeme
                    shadowLastChange: 0
                    # keep sshPublicKey if present, but don't create
                    # the attribute if it is missing
                    sshPublicKey: []
                - replace:
                    cn: foo
                    uid: foo
                    uidNumber: 1000
                    gidNumber: 1000
                    gecos: Foo Bar
                    givenName: Foo
                    sn: Bar
                    homeDirectory: /home/foo
                    loginShell: /bin/bash
                    objectClass:
                      - inetOrgPerson
                      - posixAccount
                      - top
                      - ldapPublicKey
                      - shadowAccount

    :param name:
        The URL of the LDAP server.  This is ignored if
        ``connect_spec`` is either a connection object or a dict with
        a ``'url'`` entry.

    :param entries:
        A description of the desired state of zero or more LDAP
        entries.

        ``entries`` is an iterable of dicts.  Each of these dict's
        keys are the distinguished names (DNs) of LDAP entries to
        manage.  Each of these dicts is processed in order.  A later
        dict can reference an LDAP entry that was already mentioned in
        an earlier dict, which makes it possible for later dicts to
        enhance or alter the desired state of an LDAP entry.

        The DNs are mapped to a description of the LDAP entry's
        desired state.  These LDAP entry descriptions are themselves
        iterables of dicts.  Each dict in the iterable is processed in
        order.  They contain directives controlling the entry's state.
        The key names the directive type and the value is state
        information for the directive.  The specific structure of the
        state information depends on the directive type.

        The structure of ``entries`` looks like this::

            [{dn1: [{directive1: directive1_state,
                     directive2: directive2_state},
                    {directive3: directive3_state}],
              dn2: [{directive4: directive4_state,
                     directive5: directive5_state}]},
             {dn3: [{directive6: directive6_state}]}]

        These are the directives:

        * ``'delete_others'``
            Boolean indicating whether to delete attributes not
            mentioned in this dict or any of the other directive
            dicts for this DN.  Defaults to ``False``.

            If you don't want to delete an attribute if present, but
            you also don't want to add it if it is missing or modify
            it if it is present, you can use either the ``'default'``
            directive or the ``'add'`` directive with an empty value
            list.

        * ``'default'``
            A dict mapping an attribute name to an iterable of default
            values for that attribute.  If the attribute already
            exists, it is left alone.  If not, it is created using the
            given list of values.

            An empty value list is useful when you don't want to
            create an attribute if it is missing but you do want to
            preserve it if the ``'delete_others'`` key is ``True``.

        * ``'add'``
            Attribute values to add to the entry.  This is a dict
            mapping an attribute name to an iterable of values to add.

            An empty value list is useful when you don't want to
            create an attribute if it is missing but you do want to
            preserve it if the ``'delete_others'`` key is ``True``.

        * ``'delete'``
            Attribute values to remove from the entry.  This is a dict
            mapping an attribute name to an iterable of values to
            delete from the attribute.  If the iterable is empty, all
            of the attribute's values are deleted.

        * ``'replace'``
            Attributes to replace.  This is a dict mapping an
            attribute name to an iterable of values.  Any existing
            values for the attribute are deleted, then the given
            values are added.  The iterable may be empty.

        In the above directives, the iterables of attribute values may
        instead be ``None``, in which case an empty list is used, or a
        scalar such as a string or number, in which case a new list
        containing the scalar is used.

        Note that if all attribute values are removed from an entry,
        the entire entry is deleted.

    :param connect_spec:
        See the description of the ``connect_spec`` parameter of the
        :py:func:`ldap3.connect <salt.modules.ldap3.connect>` function
        in the :py:mod:`ldap3 <salt.modules.ldap3>` execution module.
        If this is a dict and the ``'url'`` entry is not specified,
        the ``'url'`` entry is set to the value of the ``name``
        parameter.

    :returns:
        A dict with the following keys:

        * ``'name'``
            This is the same object passed to the ``name`` parameter.

        * ``'changes'``
            This is a dict describing the changes made (or, in test
            mode, the changes that would have been attempted).  If no
            changes were made (or no changes would have been
            attempted), then this dict is empty.  Only successful
            changes are included.

            Each key is a DN of an entry that was changed (or would
            have been changed).  Entries that were not changed (or
            would not have been changed) are not included.  The value
            is a dict with two keys:

            * ``'old'``
                The state of the entry before modification.  If the
                entry did not previously exist, this key maps to
                ``None``.  Otherwise, the value is a dict mapping each
                of the old entry's attributes to a list of its values
                before any modifications were made.  Unchanged
                attributes are excluded from this dict.

            * ``'new'``
                The state of the entry after modification.  If the
                entry was deleted, this key maps to ``None``.
                Otherwise, the value is a dict mapping each of the
                entry's attributes to a list of its values after the
                modifications were made.  Unchanged attributes are
                excluded from this dict.

            Example ``'changes'`` dict where a new entry was created
            with a single attribute containing two values::

                {'dn1': {'old': None,
                         'new': {'attr1': ['val1', 'val2']}}}

            Example ``'changes'`` dict where a new attribute was added
            to an existing entry::

                {'dn1': {'old': {},
                         'new': {'attr2': ['val3']}}}

        * ``'result'``
            One of the following values:

            * ``True`` if no changes were necessary or if all changes
              were applied successfully.
            * ``False`` if at least one change was unable to be applied.
            * ``None`` if changes would be applied but it is in test
              mode.
    '''
    if connect_spec is None:
        connect_spec = {}
    try:
        connect_spec.setdefault('url', name)
    except AttributeError:
        # already a connection object
        pass

    connect = __salt__['ldap3.connect']

    # hack to get at the ldap3 module to access the ldap3.LDAPError
    # exception class.  https://github.com/saltstack/salt/issues/27578
    ldap3 = inspect.getmodule(connect)

    with connect(connect_spec) as l:

        old, new = _process_entries(l, entries)

        # collect all of the affected entries (only the key is
        # important in this dict; would have used an OrderedSet if
        # there was one)
        dn_set = OrderedDict()
        dn_set.update(old)
        dn_set.update(new)

        # do some cleanup
        dn_to_delete = set()
        for dn in dn_set:
            o = old.get(dn, {})
            n = new.get(dn, {})
            for x in o, n:
                to_delete = set()
                for attr, vals in six.iteritems(x):
                    if not len(vals):
                        # clean out empty attribute lists
                        to_delete.add(attr)
                for attr in to_delete:
                    del x[attr]
            if o == n:
                # clean out unchanged entries
                dn_to_delete.add(dn)
        for dn in dn_to_delete:
            for x in old, new:
                x.pop(dn, None)
            del dn_set[dn]

        ret = {
            'name': name,
            'changes': {},
            'result': None,
            'comment': '',
        }

        if old == new:
            ret['comment'] = 'LDAP entries already set'
            ret['result'] = True
            return ret

        if __opts__['test']:
            ret['comment'] = 'Would change LDAP entries'
            changed_old = old
            changed_new = new
            success_dn_set = dn_set
        else:
            # execute the changes
            changed_old = OrderedDict()
            changed_new = OrderedDict()
            # assume success; these will be changed on error
            ret['result'] = True
            ret['comment'] = 'Successfully updated LDAP entries'
            errs = []
            success_dn_set = OrderedDict()
            for dn in dn_set:
                o = old.get(dn, {})
                n = new.get(dn, {})

                try:
                    # perform the operation
                    if len(o):
                        if len(n):
                            op = 'modify'
                            assert o != n
                            __salt__['ldap3.change'](l, dn, o, n)
                        else:
                            op = 'delete'
                            __salt__['ldap3.delete'](l, dn)
                    else:
                        op = 'add'
                        assert len(n)
                        __salt__['ldap3.add'](l, dn, n)

                    # update these after the op in case an exception
                    # is raised
                    changed_old[dn] = o
                    changed_new[dn] = n
                    success_dn_set[dn] = True
                except ldap3.LDAPError:
                    log.exception('failed to %s entry %s', op, dn)
                    errs.append((op, dn))
                    continue

            if len(errs):
                ret['result'] = False
                ret['comment'] = 'failed to ' \
                                 + ', '.join((op + ' entry ' + dn
                                              for op, dn in errs))

    # set ret['changes'].  filter out any unchanged attributes, and
    # convert the value sets to lists before returning them to the
    # user (sorted for easier comparisons)
    for dn in success_dn_set:
        o = changed_old.get(dn, {})
        n = changed_new.get(dn, {})
        changes = {}
        ret['changes'][dn] = changes
        for x, xn in ((o, 'old'), (n, 'new')):
            if not len(x):
                changes[xn] = None
                continue
            changes[xn] = dict(((attr, sorted(vals))
                                for attr, vals in six.iteritems(x)
                                if o.get(attr, ()) != n.get(attr, ())))

    return ret
Beispiel #8
0
    def gen_functions(self, pack=None, virtual_enable=True, whitelist=None,
                      provider_overrides=False):
        '''
        Return a dict of functions found in the defined module_dirs
        '''
        funcs = OrderedDict()
        self.load_modules()
        for mod in self.modules:
            # If this is a proxy minion then MOST modules cannot work.  Therefore, require that
            # any module that does work with salt-proxy-minion define __proxyenabled__ as a list
            # containing the names of the proxy types that the module supports.
            if not hasattr(mod, 'render') and 'proxy' in self.opts:
                if not hasattr(mod, '__proxyenabled__'):
                    # This is a proxy minion but this module doesn't support proxy
                    # minions at all
                    continue
                if not (self.opts['proxy']['proxytype'] in mod.__proxyenabled__ or
                        '*' in mod.__proxyenabled__):
                    # This is a proxy minion, this module supports proxy
                    # minions, but not this particular minion
                    log.debug(mod)
                    continue

            if hasattr(mod, '__opts__'):
                mod.__opts__.update(self.opts)
            else:
                mod.__opts__ = self.opts

            mod.__grains__ = self.grains
            mod.__pillar__ = self.pillar

            if pack:
                if isinstance(pack, list):
                    for chunk in pack:
                        if not isinstance(chunk, dict):
                            continue
                        try:
                            setattr(mod, chunk['name'], chunk['value'])
                        except KeyError:
                            pass
                else:
                    setattr(mod, pack['name'], pack['value'])

            # Call a module's initialization method if it exists
            if hasattr(mod, '__init__'):
                if callable(mod.__init__):
                    try:
                        mod.__init__(self.opts)
                    except TypeError:
                        pass

            # Trim the full pathname to just the module
            # this will be the short name that other salt modules and state
            # will refer to it as.
            module_name = mod.__name__.rsplit('.', 1)[-1]

            if virtual_enable:
                # if virtual modules are enabled, we need to look for the
                # __virtual__() function inside that module and run it.
                (virtual_ret, virtual_name) = self.process_virtual(mod,
                                                                   module_name)

                # if process_virtual returned a non-True value then we are
                # supposed to not process this module
                if virtual_ret is not True:
                    continue

                # update our module name to reflect the virtual name
                module_name = virtual_name

            if whitelist:
                # If a whitelist is defined then only load the module if it is
                # in the whitelist
                if module_name not in whitelist:
                    continue

            # load the functions from the module and update our dict
            funcs.update(self.load_functions(mod, module_name))

        # Handle provider overrides
        if provider_overrides and self.opts.get('providers', False):
            if isinstance(self.opts['providers'], dict):
                for mod, provider in self.opts['providers'].items():
                    newfuncs = raw_mod(self.opts, provider, funcs)
                    if newfuncs:
                        for newfunc in newfuncs:
                            f_key = '{0}{1}'.format(
                                mod, newfunc[newfunc.rindex('.'):]
                            )
                            funcs[f_key] = newfuncs[newfunc]

        # now that all the functions have been collected, iterate back over
        # the available modules and inject the special __salt__ namespace that
        # contains these functions.
        for mod in self.modules:
            if not hasattr(mod, '__salt__') or (
                not in_pack(pack, '__salt__') and
                (not str(mod.__name__).startswith('salt.loaded.int.grain') and
                 not str(mod.__name__).startswith('salt.loaded.ext.grain'))
            ):
                mod.__salt__ = funcs
            elif not in_pack(pack, '__salt__') and \
                    (str(mod.__name__).startswith('salt.loaded.int.grain') or
                     str(mod.__name__).startswith('salt.loaded.ext.grain')):
                mod.__salt__.update(funcs)
        return funcs
Beispiel #9
0
    def serialize(cls, id_=None):
        # The order matters
        serialized = OrderedDict()
        if id_ is not None:
            # This is meant as a configuration section, sub json schema
            serialized['id'] = '{0}/{1}.json#'.format(BASE_SCHEMA_URL, id_)
        else:
            # Main configuration block, json schema
            serialized['$schema'] = 'http://json-schema.org/draft-04/schema#'
        if cls.title is not None:
            serialized['title'] = cls.title
        if cls.description is not None:
            if cls.description == cls.__doc__:
                serialized['description'] = textwrap.dedent(cls.description).strip()
            else:
                serialized['description'] = cls.description

        required = []
        ordering = []
        serialized['type'] = 'object'
        properties = OrderedDict()
        cls.after_items_update = []
        for name in cls._order:
            skip_order = False
            if name in cls._sections:
                section = cls._sections[name]
                serialized_section = section.serialize(None if section.__flatten__ is True else name)
                if section.__flatten__ is True:
                    # Flatten the configuration section into the parent
                    # configuration
                    properties.update(serialized_section['properties'])
                    if 'x-ordering' in serialized_section:
                        ordering.extend(serialized_section['x-ordering'])
                    if 'required' in serialized_section:
                        required.extend(serialized_section['required'])
                    if hasattr(section, 'after_items_update'):
                        cls.after_items_update.extend(section.after_items_update)
                    skip_order = True
                else:
                    # Store it as a configuration section
                    properties[name] = serialized_section

            if name in cls._items:
                config = cls._items[name]
                # Handle the configuration items defined in the class instance
                if config.__flatten__ is True:
                    serialized_config = config.serialize()
                    cls.after_items_update.append(serialized_config)
                    skip_order = True
                else:
                    properties[name] = config.serialize()

                if config.required:
                    # If it's a required item, add it to the required list
                    required.append(name)

            if skip_order is False:
                # Store the order of the item
                if name not in ordering:
                    ordering.append(name)

        if properties:
            serialized['properties'] = properties

        # Update the serialized object with any items to include after properties
        if cls.after_items_update:
            after_items_update = {}
            for entry in cls.after_items_update:
                name, data = next(six.iteritems(entry))
                if name in after_items_update:
                    after_items_update[name].extend(data)
                else:
                    after_items_update[name] = data
            serialized.update(after_items_update)

        if required:
            # Only include required if not empty
            serialized['required'] = required
        if ordering:
            # Only include ordering if not empty
            serialized['x-ordering'] = ordering
        serialized['additionalProperties'] = cls.__allow_additional_items__
        return serialized