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