Added delete function
[mirror/userdir-ldap.git] / ud-host
1 #!/usr/bin/env python
2 # -*- mode: python -*-
3 # This script is an interactive way to manipulate fields in the LDAP directory.
4 # When run it connects to the directory using the current users ID and fetches
5 # all the attributes for the first machine. It then formats them nicely and 
6 # allows the user to change them.
7 #
8 #  Usage: userinfo -a <user> -u <user> -c <user> -r
9 #    -a    Set the authentication user (the user whose password you are 
10 #          going to enter)
11 #    -h    Set the host to display
12
13 import string, time, os, pwd, sys, getopt, ldap, crypt, whrandom, readline, copy;
14 from userdir_ldap import *;
15
16 RootMode = 0;
17 AttrInfo = {"description": ["Machine Descr.", 1],
18             "hostname": ["Host names", 2],
19             "status": ["Status", 3],
20             "l": ["Location", 4],
21             "sponsor": ["Sponsors", 5],
22             "distribution": ["Distribution", 6],
23             "access": ["Access", 7],
24             "admin": ["Admin", 8],
25             "architecture": ["Architecture", 9],
26             "machine": ["Machine Hardware", 10],
27             "memory": ["Memory", 11],
28             "disk": ["Disk", 12],
29             "sshrsahostkey": ["SSH RSA", 14],
30             "sshdsahostkey": ["SSH DSA", 15],
31             "bandwidth": ["Bandwidth", 16]};
32
33 AttrPrompt = {"description": ["Purpose of the machine"],
34               "hostname": ["The hostnames for the box (ipv4/ipv6)"],
35               "status": ["Blank if Up, explaination if not"],
36               "l": ["Physical location"],
37               "sponsor": ["Sponsors and their URLs"],
38               "distribution": ["The distribution version"],
39               "access": ["all, developer only, restricted"],
40               "admin": ["Admin email address"],
41               "architecture": ["Debian Arhitecture string"],
42               "machine": ["Hardware description"],
43               "memory": ["Installed RAM"],
44               "disk": ["Disk Space, RAID levels, etc"],
45               "sshrsahostkey": ["A copy of /etc/ssh/ssh_host_key.pub"],
46               "sshdsahostkey": ["A copy of /etc/ssh/ssh_host_key.pub.dsa"],
47               "bandwidth": ["Available outbound"]};
48
49 # Create a map of IDs to desc,value,attr
50 OrderedIndex = {};
51 for at in AttrInfo.keys():
52    if (AttrInfo[at][1] != 0):
53       OrderedIndex[AttrInfo[at][1]] = [AttrInfo[at][0], "", at];
54 OrigOrderedIndex = copy.deepcopy(OrderedIndex);
55
56 # Print out the automatic time stamp information
57 def PrintModTime(Attrs):
58    Stamp = GetAttr(Attrs,"modifytimestamp","");
59    if len(Stamp) >= 13:
60       Time = (int(Stamp[0:4]),int(Stamp[4:6]),int(Stamp[6:8]),
61               int(Stamp[8:10]),int(Stamp[10:12]),int(Stamp[12:14]),0,0,-1);
62       print "%-24s:" % ("Record last modified on"), time.strftime("%a %d/%m/%Y %X UTC",Time),
63       print "by",ldap.explode_dn(GetAttr(Attrs,"modifiersname"),1)[0];
64
65    Stamp = GetAttr(Attrs,"createtimestamp","");
66    if len(Stamp) >= 13:
67       Time = (int(Stamp[0:4]),int(Stamp[4:6]),int(Stamp[6:8]),
68               int(Stamp[8:10]),int(Stamp[10:12]),int(Stamp[12:14]),0,0,-1);
69       print "%-24s:" % ("Record created on"), time.strftime("%a %d/%m/%Y %X UTC",Time);
70
71 # Display all of the attributes in a numbered list
72 def ShowAttrs(Attrs):
73    print;
74    PrintModTime(Attrs);
75
76    for at in Attrs[1].keys():
77       if AttrInfo.has_key(at):
78          if AttrInfo[at][1] == 0:
79             print "      %-18s:" % (AttrInfo[at][0]),
80             for x in Attrs[1][at]:
81                print "'%s'" % (x),
82             print;
83          else:
84             OrderedIndex[AttrInfo[at][1]][1] = Attrs[1][at];
85                                        
86    Keys = OrderedIndex.keys();
87    Keys.sort();
88    for at in Keys:
89       if at < 100 or RootMode != 0:
90          print " %3u) %-18s: " % (at,OrderedIndex[at][0]),
91          for x in OrderedIndex[at][1]:
92             print "'%s'" % (re.sub('[\n\r]','?',x)),
93          print;
94
95 # Change a single attribute
96 def ChangeAttr(Attrs,Attr):
97    if (Attr == "sponsor" or Attr == "hostname"):
98       return MultiChangeAttr(Attrs,Attr);
99
100    print "Old value: '%s'" % (GetAttr(Attrs,Attr,""));
101    print "Press enter to leave unchanged and a single space to set to empty";
102    NewValue = raw_input("New? ");
103   
104    # Empty string
105    if (NewValue == ""):
106       print "Leaving unchanged.";
107       return;
108
109    # Single space designates delete, trap the delete error
110    if (NewValue == " "):
111       print "Deleting.",;
112       try:
113          l.modify_s(HostDn,[(ldap.MOD_DELETE,Attr,None)]);
114       except ldap.NO_SUCH_ATTRIBUTE:
115          pass;
116
117       print;
118       Attrs[1][Attr] = [""];
119       return;
120
121    # Set a new value
122    print "Setting.",;
123    l.modify_s(HostDn,[(ldap.MOD_REPLACE,Attr,NewValue)]);
124    Attrs[1][Attr] = [NewValue];
125    print;
126
127 def MultiChangeAttr(Attrs,Attr):
128    # Make sure that we have an entry
129    if not Attrs[1].has_key(Attr):
130       Attrs[1][Attr] = [];
131
132    Attrs[1][Attr].sort();
133    print "Old values: ",Attrs[1][Attr];
134
135    Mode = string.upper(raw_input("[D]elete or [A]dd? "));
136    if (Mode != 'D' and Mode != 'A'):
137       return;
138
139    NewValue = raw_input("Value? ");
140    # Empty string
141    if (NewValue == ""):
142       print "Leaving unchanged.";
143       return;
144    
145    # Delete   
146    if (Mode == "D"):
147       print "Deleting.",;
148       try:
149          l.modify_s(HostDn,[(ldap.MOD_DELETE,Attr,NewValue)]);
150       except ldap.NO_SUCH_ATTRIBUTE:
151          print "Failed";
152
153       print;
154       Attrs[1][Attr].remove(NewValue);
155       return;
156
157    # Set a new value
158    print "Setting.",;
159    l.modify_s(HostDn,[(ldap.MOD_ADD,Attr,NewValue)]);
160    Attrs[1][Attr].append(NewValue);
161    print;
162
163 # Main program starts here
164 User = pwd.getpwuid(os.getuid())[0];
165 BindUser = User;
166 # Process options
167 (options, arguments) = getopt.getopt(sys.argv[1:], "nh:a:r")
168 for (switch, val) in options:
169    if (switch == '-h'):
170       Host = val;
171    elif (switch == '-a'):
172       BindUser = val;
173    elif (switch == '-r'):
174       RootMode = 1;
175    elif (switch == '-n'):
176       BindUser = "";
177
178 if (BindUser != ""):
179    print "Accessing LDAP entry",
180 if (BindUser != User):
181    if (BindUser != ""):
182       print "as '" + BindUser + "'";
183 else:
184    print;
185 if (BindUser != ""):
186    Password = getpass(BindUser + "'s password: ");
187
188 # Connect to the ldap server
189 l = ldap.open(LDAPServer);
190 UserDn = "uid=" + BindUser + "," + BaseDn;
191 if (BindUser != ""):
192    l.simple_bind_s(UserDn,Password);
193 else:
194    l.simple_bind_s("","");
195    
196 HBaseDn = "ou=hosts,dc=debian,dc=org";    
197 HostDn = "host=" + Host + "," + HBaseDn;
198
199 # Query the server for all of the attributes
200 Attrs = l.search_s(HBaseDn,ldap.SCOPE_ONELEVEL,"host=" + Host);
201 if len(Attrs) == 0:
202    print "Host",Host,"was not found.";
203    sys.exit(0); 
204
205 # repeatedly show the account configuration
206 while(1):
207    ShowAttrs(Attrs[0]);
208    if (BindUser == ""):
209       sys.exit(0);
210
211    if RootMode == 1:
212       print "   a) Arbitary Change";
213    print "   n) New Host";
214    print "   d) Delete Host";
215    print "   u) Switch Hosts";
216    print "   x) Exit";
217    
218    # Prompt
219    Response = raw_input("Change? ");
220    if (Response == "x" or Response == "X" or Response == "q" or 
221        Response == "quit" or Response == "exit"):
222       break;
223
224    # Change who we are looking at
225    if (Response == 'u' or Response == 'U'):
226       NewHost = raw_input("Host? ");
227       if NewHost == "":
228          continue;
229       NAttrs = l.search_s(HBaseDn,ldap.SCOPE_ONELEVEL,"host=" + NewHost);
230       if len(NAttrs) == 0:
231          print "Host",NewHost,"was not found.";
232          continue;
233       Attrs = NAttrs;
234       Host = NewHost;
235       HostDn = "host=" + Host + "," + HBaseDn;
236       OrderedIndex = copy.deepcopy(OrigOrderedIndex);
237       continue;
238
239    # Create a new entry and change to it Change who we are looking at
240    if (Response == 'n' or Response == 'N'):
241       NewHost = raw_input("Host? ");
242       if NewHost == "":
243          continue;
244       NAttrs = l.search_s(HBaseDn,ldap.SCOPE_ONELEVEL,"host=" + NewHost);
245       if len(NAttrs) != 0:
246          print "Host",NewHost,"already exists.";
247          continue;
248       Dn = "host=" + NewHost + "," + HBaseDn;
249       l.add_s(Dn,[("host",NewHost),
250                   ("objectclass","top")]);
251                   
252       # Switch            
253       NAttrs = l.search_s(HBaseDn,ldap.SCOPE_ONELEVEL,"host=" + NewHost);
254       if len(NAttrs) == 0:
255          print "Host",NewHost,"was not found.";
256          continue;
257       Attrs = NAttrs;
258       Host = NewHost;
259       HostDn = "host=" + Host + "," + HBaseDn;
260       OrderedIndex = copy.deepcopy(OrigOrderedIndex);
261       continue;
262   
263    # Handle changing an arbitary value
264    if (Response == "a"):
265       Attr = raw_input("Attr? ");
266       ChangeAttr(Attrs[0],Attr);
267       continue;
268
269    if (Response == 'd'):
270       Really = raw_input("Really (type yes)? ");
271       if Really != 'yes': 
272           continue;
273       print "Deleting",HostDn;
274       l.delete_s(HostDn);
275       continue;
276   
277    # Convert the integer response
278    try:
279       ID = int(Response);
280       if (not OrderedIndex.has_key(ID) or (ID > 100 and RootMode == 0)):
281          raise ValueError;
282    except ValueError:
283       print "Invalid";
284       continue;
285
286    # Print the what to do prompt
287    print "Changing LDAP entry '%s' (%s)" % (OrderedIndex[ID][0],OrderedIndex[ID][2]);
288    print AttrPrompt[OrderedIndex[ID][2]][0];
289    ChangeAttr(Attrs[0],OrderedIndex[ID][2]);