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