Always perform the list output anonymously
[mirror/userdir-ldap.git] / ud-host
1 #!/usr/bin/env python
2 # -*- mode: python -*-
3
4 #   Copyright (c) 2000-2001  Jason Gunthorpe <jgg@debian.org>
5 #   Copyright (c) 2001       Ryan Murray <rmurray@debian.org>
6 #   Copyright (c) 2003       James Troup <troup@debian.org>
7 #   Copyright (c) 2004       Joey Schulze <joey@infodrom.org>
8 #
9 #   This program is free software; you can redistribute it and/or modify
10 #   it under the terms of the GNU General Public License as published by
11 #   the Free Software Foundation; either version 2 of the License, or
12 #   (at your option) any later version.
13 #
14 #   This program is distributed in the hope that it will be useful,
15 #   but WITHOUT ANY WARRANTY; without even the implied warranty of
16 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 #   GNU General Public License for more details.
18 #
19 #   You should have received a copy of the GNU General Public License
20 #   along with this program; if not, write to the Free Software
21 #   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22
23 # This script is an interactive way to manipulate fields in the LDAP directory.
24 # When run it connects to the directory using the current users ID and fetches
25 # all the attributes for the first machine. It then formats them nicely and
26 # allows the user to change them.
27 #
28 #  Usage: userinfo -a <user> -u <user> -c <user> -r
29 #    -a    Set the authentication user (the user whose password you are
30 #          going to enter)
31 #    -h    Set the host to display
32
33 import string, time, os, pwd, sys, getopt, ldap, crypt, whrandom, readline, copy;
34 from userdir_ldap import *;
35
36 RootMode = 0;
37 AttrInfo = {"description": ["Machine Descr.", 1],
38             "hostname": ["Host names", 2],
39             "status": ["Status", 3],
40             "l": ["Location", 4],
41             "sponsor": ["Sponsors", 5],
42             "distribution": ["Distribution", 6],
43             "access": ["Access", 7],
44             "admin": ["Admin", 8],
45             "architecture": ["Architecture", 9],
46             "machine": ["Machine Hardware", 10],
47             "memory": ["Memory", 11],
48             "disk": ["Disk", 12],
49             "sshRSAHostKey": ["SSH Host Keys", 14],
50             "bandwidth": ["Bandwidth", 15]};
51
52 AttrPrompt = {"description": ["Purpose of the machine"],
53               "hostname": ["The hostnames for the box (ipv4/ipv6)"],
54               "status": ["Blank if Up, explaination if not"],
55               "l": ["Physical location"],
56               "sponsor": ["Sponsors and their URLs"],
57               "distribution": ["The distribution version"],
58               "access": ["all, developer only, restricted"],
59               "admin": ["Admin email address"],
60               "architecture": ["Debian Architecture string"],
61               "machine": ["Hardware description"],
62               "memory": ["Installed RAM"],
63               "disk": ["Disk Space, RAID levels, etc"],
64               "sshRSAHostKey": ["A copy of /etc/ssh/ssh_*host_key.pub"],
65               "bandwidth": ["Available outbound"]};
66
67 # Create a map of IDs to desc,value,attr
68 OrderedIndex = {};
69 for at in AttrInfo.keys():
70    if (AttrInfo[at][1] != 0):
71       OrderedIndex[AttrInfo[at][1]] = [AttrInfo[at][0], "", at];
72 OrigOrderedIndex = copy.deepcopy(OrderedIndex);
73
74 # Print out the automatic time stamp information
75 def PrintModTime(Attrs):
76    Stamp = GetAttr(Attrs,"modifyTimestamp","");
77    if len(Stamp) >= 13:
78       Time = (int(Stamp[0:4]),int(Stamp[4:6]),int(Stamp[6:8]),
79               int(Stamp[8:10]),int(Stamp[10:12]),int(Stamp[12:14]),0,0,-1);
80       print "%-24s:" % ("Record last modified on"), time.strftime("%a %d/%m/%Y %X UTC",Time),
81       print "by",ldap.explode_dn(GetAttr(Attrs,"modifiersName"),1)[0];
82
83    Stamp = GetAttr(Attrs,"createTimestamp","");
84    if len(Stamp) >= 13:
85       Time = (int(Stamp[0:4]),int(Stamp[4:6]),int(Stamp[6:8]),
86               int(Stamp[8:10]),int(Stamp[10:12]),int(Stamp[12:14]),0,0,-1);
87       print "%-24s:" % ("Record created on"), time.strftime("%a %d/%m/%Y %X UTC",Time);
88
89 # Display all of the attributes in a numbered list
90 def ShowAttrs(Attrs):
91    print;
92    PrintModTime(Attrs);
93
94    for at in Attrs[1].keys():
95       if AttrInfo.has_key(at):
96          if AttrInfo[at][1] == 0:
97             print "      %-18s:" % (AttrInfo[at][0]),
98             for x in Attrs[1][at]:
99                print "'%s'" % (x),
100             print;
101          else:
102             OrderedIndex[AttrInfo[at][1]][1] = Attrs[1][at];
103
104    Keys = OrderedIndex.keys();
105    Keys.sort();
106    for at in Keys:
107       if at < 100 or RootMode != 0:
108          print " %3u) %-18s: " % (at,OrderedIndex[at][0]),
109          for x in OrderedIndex[at][1]:
110             print "'%s'" % (re.sub('[\n\r]','?',x)),
111          print;
112
113 def Overview(Attrs):
114    """Display a one-line overview for a given host"""
115    if 'status' in Attrs[1].keys():
116       status = Attrs[1]['status'][0]
117    else:
118       status = ''
119    print "%-12s  %-10s  %-38s  %-25s %s" % (\
120       Attrs[1]['host'][0], \
121       Attrs[1]['architecture'][0], \
122       Attrs[1]['distribution'][0], \
123       Attrs[1]['access'][0], \
124       status)
125
126 # Change a single attribute
127 def ChangeAttr(Attrs,Attr):
128    if (Attr == "sponsor" or Attr == "hostname" or Attr == "sshRSAHostKey"):
129       return MultiChangeAttr(Attrs,Attr);
130
131    print "Old value: '%s'" % (GetAttr(Attrs,Attr,""));
132    print "Press enter to leave unchanged and a single space to set to empty";
133    NewValue = raw_input("New? ");
134
135    # Empty string
136    if (NewValue == ""):
137       print "Leaving unchanged.";
138       return;
139
140    # Single space designates delete, trap the delete error
141    if (NewValue == " "):
142       print "Deleting.",;
143       try:
144          l.modify_s(HostDn,[(ldap.MOD_DELETE,Attr,None)]);
145       except ldap.NO_SUCH_ATTRIBUTE:
146          pass;
147
148       print;
149       Attrs[1][Attr] = [""];
150       return;
151
152    # Set a new value
153    print "Setting.",;
154    l.modify_s(HostDn,[(ldap.MOD_REPLACE,Attr,NewValue)]);
155    Attrs[1][Attr] = [NewValue];
156    print;
157
158 def MultiChangeAttr(Attrs,Attr):
159    # Make sure that we have an entry
160    if not Attrs[1].has_key(Attr):
161       Attrs[1][Attr] = [];
162
163    Attrs[1][Attr].sort();
164    print "Old values: ",Attrs[1][Attr];
165
166    Mode = string.upper(raw_input("[D]elete or [A]dd? "));
167    if (Mode != 'D' and Mode != 'A'):
168       return;
169
170    NewValue = raw_input("Value? ");
171    # Empty string
172    if (NewValue == ""):
173       print "Leaving unchanged.";
174       return;
175
176    # Delete
177    if (Mode == "D"):
178       print "Deleting.",;
179       try:
180          l.modify_s(HostDn,[(ldap.MOD_DELETE,Attr,NewValue)]);
181       except ldap.NO_SUCH_ATTRIBUTE:
182          print "Failed";
183
184       print;
185       Attrs[1][Attr].remove(NewValue);
186       return;
187
188    # Set a new value
189    print "Setting.",;
190    l.modify_s(HostDn,[(ldap.MOD_ADD,Attr,NewValue)]);
191    Attrs[1][Attr].append(NewValue);
192    print;
193
194 # Main program starts here
195 User = pwd.getpwuid(os.getuid())[0];
196 BindUser = User;
197 # Process options
198 (options, arguments) = getopt.getopt(sys.argv[1:], "nh:a:rl")
199 for (switch, val) in options:
200    if (switch == '-h'):
201       Host = val;
202    elif (switch == '-a'):
203       BindUser = val;
204    elif (switch == '-r'):
205       RootMode = 1;
206    elif (switch == '-n'):
207       BindUser = "";
208    elif (switch == '-l'):
209       BindUser = "";
210       ListMode = 1
211
212 if (BindUser != ""):
213    l = passwdAccessLDAP(LDAPServer, BaseDn, BindUser)
214 else:
215    l = ldap.open(LDAPServer);
216    l.simple_bind_s("","")
217
218 HBaseDn = "ou=hosts,dc=debian,dc=org";
219
220 if ListMode == 1:
221    Attrs = l.search_s(HBaseDn,ldap.SCOPE_ONELEVEL,"host=*")
222    hosts = []
223    for hAttrs in Attrs:
224       hosts.append(hAttrs[1]['host'][0])
225    hosts.sort()
226
227    print "%-12s  %-10s  %-38s  %-25s %s" % ("Host name","Arch","Distribution","Access","Status")
228    print "-"*115
229    for host in hosts:
230       for hAttrs in Attrs:
231          if host == hAttrs[1]['host'][0]:
232             Overview(hAttrs)
233    sys.exit(0)
234
235 HostDn = "host=" + Host + "," + HBaseDn;
236
237 # Query the server for all of the attributes
238 Attrs = l.search_s(HBaseDn,ldap.SCOPE_ONELEVEL,"host=" + Host);
239 if len(Attrs) == 0:
240    print "Host",Host,"was not found.";
241    sys.exit(0);
242
243 # repeatedly show the account configuration
244 while(1):
245    ShowAttrs(Attrs[0]);
246    if (BindUser == ""):
247       sys.exit(0);
248
249    if RootMode == 1:
250       print "   a) Arbitary Change";
251    print "   n) New Host";
252    print "   d) Delete Host";
253    print "   u) Switch Hosts";
254    print "   x) Exit";
255
256    # Prompt
257    Response = raw_input("Change? ");
258    if (Response == "x" or Response == "X" or Response == "q" or
259        Response == "quit" or Response == "exit"):
260       break;
261
262    # Change who we are looking at
263    if (Response == 'u' or Response == 'U'):
264       NewHost = raw_input("Host? ");
265       if NewHost == "":
266          continue;
267       NAttrs = l.search_s(HBaseDn,ldap.SCOPE_ONELEVEL,"host=" + NewHost);
268       if len(NAttrs) == 0:
269          print "Host",NewHost,"was not found.";
270          continue;
271       Attrs = NAttrs;
272       Host = NewHost;
273       HostDn = "host=" + Host + "," + HBaseDn;
274       OrderedIndex = copy.deepcopy(OrigOrderedIndex);
275       continue;
276
277    # Create a new entry and change to it Change who we are looking at
278    if (Response == 'n' or Response == 'N'):
279       NewHost = raw_input("Host? ");
280       if NewHost == "":
281          continue;
282       NAttrs = l.search_s(HBaseDn,ldap.SCOPE_ONELEVEL,"host=" + NewHost);
283       if len(NAttrs) != 0:
284          print "Host",NewHost,"already exists.";
285          continue;
286       NewHostName = raw_input("Hostname? ");
287       if NewHost == "":
288          continue;
289       Dn = "host=" + NewHost + "," + HBaseDn;
290       l.add_s(Dn,[("host", NewHost),
291                   ("hostname", NewHostName),
292                   ("objectClass", ("top", "debianServer"))]);
293
294       # Switch
295       NAttrs = l.search_s(HBaseDn,ldap.SCOPE_ONELEVEL,"host=" + NewHost);
296       if len(NAttrs) == 0:
297          print "Host",NewHost,"was not found.";
298          continue;
299       Attrs = NAttrs;
300       Host = NewHost;
301       HostDn = "host=" + Host + "," + HBaseDn;
302       OrderedIndex = copy.deepcopy(OrigOrderedIndex);
303       continue;
304
305    # Handle changing an arbitary value
306    if (Response == "a"):
307       Attr = raw_input("Attr? ");
308       ChangeAttr(Attrs[0],Attr);
309       continue;
310
311    if (Response == 'd'):
312       Really = raw_input("Really (type yes)? ");
313       if Really != 'yes':
314           continue;
315       print "Deleting",HostDn;
316       l.delete_s(HostDn);
317       continue;
318
319    # Convert the integer response
320    try:
321       ID = int(Response);
322       if (not OrderedIndex.has_key(ID) or (ID > 100 and RootMode == 0)):
323          raise ValueError;
324    except ValueError:
325       print "Invalid";
326       continue;
327
328    # Print the what to do prompt
329    print "Changing LDAP entry '%s' (%s)" % (OrderedIndex[ID][0],OrderedIndex[ID][2]);
330    print AttrPrompt[OrderedIndex[ID][2]][0];
331    ChangeAttr(Attrs[0],OrderedIndex[ID][2]);