1
from landscape.diff import diff
4
class UserChanges(object):
5
"""Detect changes made since the last snapshot was taken.
7
If no snapshot is available all users and groups are reported.
8
When a snapshot is available, only the changes between the current
9
state and the snapshotted state are transmitted to the server.
12
def __init__(self, persist, provider):
13
super(UserChanges, self).__init__()
14
self._persist = persist
15
self._provider = provider
16
# FIXME This shouldn't really be necessary. Not having it
17
# here with the current factoring is also problematic. Figure
18
# out a clean way to factor this. Gustavo suggested splitting
19
# it into _build_old_data and _build_new_data and just calling
20
# that from the necessary places.
24
"""Load the previous snapshot and update current data."""
25
self._old_users = self._persist.get("users", {})
26
self._old_groups = self._persist.get("groups", {})
27
self._new_users = self._create_index(
28
"username", self._provider.get_users())
29
self._new_groups = self._create_index(
30
"name", self._provider.get_groups())
33
"""Save the current state and use it as a comparison snapshot."""
34
self._persist.set("users", self._new_users)
35
self._persist.set("groups", self._new_groups)
39
Reset the snapshot state and forget all knowledge of users and
42
self._persist.remove("users")
43
self._persist.remove("groups")
45
def _create_index(self, key, sequence):
47
Given a key and a sequence of dicts, return a dict of the form
48
C{{dict[key]: dict, ...}}.
52
index[data[key]] = data
55
def create_diff(self):
56
"""Returns the changes since the last snapshot.
58
See landscape.message_schemas.USERS schema for a description of the
59
dictionary returned by this method.
63
changes.update(self._detect_user_changes())
64
changes.update(self._detect_group_changes())
67
def _detect_user_changes(self):
69
Compare the current user snapshot to the old one and return a
70
C{dict} with C{create-users}, C{update-users} and
71
C{delete-users} fields. Fields without data aren't included
75
creates, updates, deletes = diff(self._old_users, self._new_users)
77
changes["create-users"] = list(creates.itervalues())
79
changes["update-users"] = list(updates.itervalues())
81
changes["delete-users"] = list(deletes.iterkeys())
84
def _detect_group_changes(self):
86
Compare the current group snapshot to the old one and create a
87
C{dict} with C{create-groups}, C{delete-groups},
88
C{create-group-members} and {delete-group-members} fields.
89
Fields without data aren't included in the result.
92
creates, updates, deletes = diff(self._old_groups, self._new_groups)
97
for value in creates.itervalues():
98
# Use a copy to avoid removing the 'members' element
101
members = value.pop("members")
103
create_members[value["name"]] = members
105
changes["create-groups"] = groups
107
changes["create-group-members"] = create_members
113
for groupname, new_data in updates.iteritems():
114
old_data = self._old_groups[groupname]
115
old_members = set(old_data["members"])
116
new_members = set(new_data["members"])
117
created = new_members - old_members
119
create_members[groupname] = sorted(created)
120
removed = old_members - new_members
122
remove_members[groupname] = sorted(removed)
123
if old_data["gid"] != new_data["gid"]:
124
update_groups.append({"name": groupname,
125
"gid": new_data["gid"]})
127
members = changes.setdefault("create-group-members", {})
128
members.update(create_members)
130
members = changes.setdefault("delete-group-members", {})
131
members.update(remove_members)
133
members = changes.setdefault("update-groups", [])
134
members.extend(update_groups)
137
changes["delete-groups"] = deletes.keys()