summaryrefslogtreecommitdiffstats
path: root/ipaserver/install/plugins/rename_managed.py
blob: e0fa36bb73d4821143b52af8dc34e8047b204c74 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# Authors:
#   Rob Crittenden <rcritten@redhat.com>
#
# Copyright (C) 2011  Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

from ipaserver.install.plugins import FIRST, LAST
from ipaserver.install.plugins.baseupdate import PreUpdate, PostUpdate
from ipalib import api, errors
from ipapython import ipautil
from ipapython.dn import DN, EditableDN

def entry_to_update(entry):
    """
    Convert an entry into a name/value pair list that looks like an update.

    An entry is a dict.

    An update is a list of name/value pairs.
    """
    update = []
    for attr in entry.keys():
        if isinstance(entry[attr], list):
            for i in xrange(len(entry[attr])):
                update.append('%s:%s' % (str(attr), str(entry[attr][i])))
        else:
            update.append('%s:%s' % (str(attr), str(entry[attr])))

    return update

class GenerateUpdateMixin(object):
    def generate_update(self, deletes=False):
        """
        We need to separate the deletes that need to happen from the
        new entries that need to be added.
        """
        ldap = self.obj.backend

        suffix = ipautil.realm_to_suffix(api.env.realm)
        searchfilter = '(objectclass=*)'
        definitions_managed_entries = []

        old_template_container = DN(('cn', 'etc'), suffix)
        new_template_container = DN(('cn', 'Templates'), ('cn', 'Managed Entries'), ('cn', 'etc'), suffix)

        old_definition_container = DN(('cn', 'managed entries'), ('cn', 'plugins'), ('cn', 'config'), suffix)
        new_definition_container = DN(('cn', 'Definitions'), ('cn', 'Managed Entries'), ('cn', 'etc'), suffix)

        definitions_dn = DN(('cn', 'Definitions'))
        update_list = []
        restart = False

        # If the old entries don't exist the server has already been updated.
        try:
            definitions_managed_entries, truncated = ldap.find_entries(
                searchfilter, ['*'], old_definition_container,
                ldap.SCOPE_ONELEVEL)
        except errors.NotFound, e:
            return (False, update_list)

        for entry in definitions_managed_entries:
            assert isinstance(entry.dn, DN)
            if deletes:
                old_dn = entry.data['managedtemplate'][0]
                assert isinstance(old_dn, DN)
                try:
                    (old_dn, entry) = ldap.get_entry(old_dn, ['*'])
                except errors.NotFound, e:
                    pass
                else:
                    # Compute the new dn by replacing the old container with the new container
                    new_dn = EditableDN(old_dn)
                    if new_dn.replace(old_template_container, new_template_container) != 1:
                        self.error("unable to replace '%s' with '%s' in '%s'",
                                   old_template_container, new_template_container, old_dn)
                        continue

                    new_dn = DN(new_dn)

                    # The old attributes become defaults for the new entry
                    new_update = {'dn': new_dn,
                                  'default': entry_to_update(entry)}

                    # Delete the old entry
                    old_update = {'dn': old_dn, 'deleteentry': None}

                    # Add the delete and replacement updates to the list of all updates
                    update_list.append({old_dn: old_update, new_dn: new_update})

            else:
                # Update the template dn by replacing the old containter with the new container
                old_dn = entry.data['managedtemplate'][0]
                new_dn = EditableDN(old_dn)
                if new_dn.replace(old_template_container, new_template_container) != 1:
                    self.error("unable to replace '%s' with '%s' in '%s'",
                               old_template_container, new_template_container, old_dn)
                    continue
                new_dn = DN(new_dn)
                entry.data['managedtemplate'] = new_dn

                # Edit the dn, then convert it back to an immutable DN
                old_dn = entry.dn
                new_dn = EditableDN(old_dn)
                if new_dn.replace(old_definition_container, new_definition_container) != 1:
                    self.error("unable to replace '%s' with '%s' in '%s'",
                               old_definition_container, new_definition_container, old_dn)
                    continue
                new_dn = DN(new_dn)

                # The old attributes become defaults for the new entry
                new_update = {'dn': new_dn,
                              'default': entry_to_update(entry.data)}

                # Add the replacement update to the collection of all updates
                update_list.append({new_dn: new_update})

        if len(update_list) > 0:
            restart = True
            update_list.sort(reverse=True)

        return (restart, update_list)

class update_managed_post_first(PreUpdate, GenerateUpdateMixin):
    """
    Update managed entries
    """
    order=FIRST

    def execute(self, **options):
        # Never need to restart with the pre-update changes
        (ignore, update_list) = self.generate_update(False)

        return (False, True, update_list)

api.register(update_managed_post_first)

class update_managed_post(PostUpdate, GenerateUpdateMixin):
    """
    Update managed entries
    """
    order=LAST

    def execute(self, **options):
        (restart, update_list) = self.generate_update(True)

        return (restart, True, update_list)

api.register(update_managed_post)