5 # 1. /etc/mail/aliasesPL, /etc/mail/virtusertable
6 # <slicename>@slices.planet-lab.org: all users and PIs of a slice
7 # pi-<loginbase>@sites.planet-lab.org: all PIs at a site
8 # tech-<loginbase>@sites.planet-lab.org: all techs at a site
9 # 2. /etc/mail/local-host-names
10 # Additional local e-mail FQDNs
14 # 1. announce@lists.planet-lab.org membership from the database and from
15 # the membership of announce-additions@lists.planet-lab.org
16 # 2. {pis,techs}@lists.planet-lab.org membership from the database.
17 # 3. cvs@lists.planet-lab.org accept_these_nonmembers from
18 # /usr/share/doc/plc/accounts.txt
22 # 1. See plc/mail/sendmail-mail.mc for the ALIAS_FILE definition that
23 # includes /etc/mail/aliasesPL.
25 # Mark Huang <mlhuang@cs.princeton.edu>
26 # Copyright (C) 2005 The Trustees of Princeton University
34 # Procmail command for aliases. See plc/mail/procmailrc for how these
35 # two scripts interact with each other.
36 procmail = "|/usr/bin/procmail -m /etc/mail/procmailrc"
38 # Parse additional options
42 usage: %s [OPTION]... [slice|pi|tech] [slicename|sitename]
44 gen_aliases.py options:
45 -n Dry run, do not sync memberships or write files
51 (plcapi, moreopts, argv) = plcapilib.plcapi(globals(), sys.argv, shortopts, longopts, moreusage)
52 for opt, optval in moreopts.iteritems():
53 if opt == "-n" or opt == "--dryrun":
56 # Parse /usr/share/doc/plc/accounts.txt (or /etc/passwd)
57 def passwd(path = '/etc/passwd'):
60 required = ['account', 'password', 'uid', 'gid', 'gecos', 'directory', 'shell']
61 optional = ['email', 'servers']
63 for line in file(path):
65 if line.strip() == '' or line[0] in '#':
68 fields = line.strip().split(':')
69 if len(fields) < len(required):
71 # {'account': 'princeton_mlh', 'password': 'x', ...}
72 entries.append(dict(zip(required + optional, fields)))
76 def GetPIs(site_id_or_login_base):
78 for site in GetSites([site_id_or_login_base], ['person_ids']):
79 persons = GetPersons(site['person_ids'], ['email', 'roles', 'enabled'])
80 pis += filter(lambda person: 'pi' in person['roles'] and person['enabled'], persons)
82 return [pi['email'] for pi in pis]
85 def GetTechs(login_base):
87 for site in GetSites([login_base], ['person_ids']):
88 persons = GetPersons(site['person_ids'], ['email', 'roles', 'enabled'])
89 techs += filter(lambda person: 'tech' in person['roles'] and person['enabled'], persons)
91 return [tech['email'] for tech in techs]
94 def GetSliceUsers(name):
95 # Get the users of the slice
98 for slice in GetSlices([name], ['site_id', 'person_ids']):
99 persons = GetPersons(slice['person_ids'], ['email', 'enabled'])
100 enabledpersons += filter(lambda person: person['enabled'], persons)
101 users += [person['email'] for person in enabledpersons]
102 # Add all the PIs for the site
103 users += GetPIs(slice['site_id'])
104 # Remove duplicates and sort
105 users = list(Set(users))
109 # Called every 10 minutes without arguments to sync
110 # {announce,pis,techs,cvs}@lists.planet-lab.org memberships and to
111 # regenerate /etc/mail/{aliasesPL,virtusertable,local-host-names}.
116 local_host_names = virtusertable = aliases = cvs_config = sys.stdout
118 local_host_names = file("local-host-names", 'w')
119 virtusertable = file("virtusertable", 'w')
120 aliases = file("aliasesPL", 'w')
121 cvs_config = tempfile.NamedTemporaryFile()
123 # Parse /usr/share/doc/plc/accounts.txt. XXX Should probably make
124 # CVS access a DB property.
126 for pw in passwd('/usr/share/doc/plc/accounts.txt'):
127 # Only allow posts from those with implicit or explicit access to
128 # all servers or explicit access to the CVS server
129 if pw.has_key('servers') and pw['servers'] not in ['*', 'cvs']:
132 # System users are those with UIDs greater than 2000 and less than 3000
133 if int(pw['uid']) >= 2000 and int(pw['uid']) < 3000:
136 cvs_nonmembers.append("'" + pw['account'] + "@planet-lab.org" + "'")
138 # Update accept_these_nonmembers property of the CVS mailing
139 # list. This ensures that those with access to the CVS server are
140 # able to post loginfo messages when they check in files. The CVS
141 # server's sendmail setup is configured to masquerade as
143 cvs_config.write("accept_these_nonmembers = [" + ", ".join(cvs_nonmembers) + "]\n")
146 config_list = os.popen("/var/mailman/bin/config_list -i %s cvs" % cvs_config.name)
147 if config_list.close() is not None:
148 raise Exception, "/var/mailman/bin/config_list cvs failed"
154 for person in GetPersons({'enabled': True}, ['person_id', 'email', 'roles']):
155 announce.append(person['email'])
156 if 'pi' in person['roles']:
157 pis.append(person['email'])
158 if 'tech' in person['roles']:
159 techs.append(person['email'])
161 # Generate announce@lists.planet-lab.org membership
163 # Merge in membership of announce-additions
164 list_members = os.popen("/var/mailman/bin/list_members announce-additions", 'r')
165 announce += map(lambda line: line.strip(), list_members.readlines())
167 # Remove duplicates and sort
168 announce = list(Set(announce))
171 sync_members = os.popen("/var/mailman/bin/sync_members %s -w=yes -g=yes -f - announce" % flags, 'w')
172 sync_members.write("\n".join(announce))
173 if sync_members.close() is not None:
174 raise Exception, "/var/mailman/bin/sync_members announce failed"
176 # Generate {pis,techs}@lists.planet-lab.org membership
178 # Remove duplicates and sort
181 techs = list(Set(techs))
184 sync_members = os.popen("/var/mailman/bin/sync_members %s -w=no -g=no -f - pis" % flags, 'w')
185 sync_members.write("\n".join(pis))
186 if sync_members.close() is not None:
187 raise Exception, "/var/mailman/bin/sync_members pis failed"
188 sync_members = os.popen("/var/mailman/bin/sync_members %s -w=no -g=no -f - techs" % flags, 'w')
189 sync_members.write("\n".join(techs))
190 if sync_members.close() is not None:
191 raise Exception, "/var/mailman/bin/sync_members techs failed"
193 # Generate local-host-names file
194 local_host_names.write("planet-lab.org\n")
195 local_host_names.write("slices.planet-lab.org\n")
196 local_host_names.write("sites.planet-lab.org\n")
198 # Generate SLICENAME@slices.planet-lab.org mapping and
199 # slice-SLICENAME alias
200 for slice in GetSlices(None, ['name']):
202 virtusertable.write("%s@slices.planet-lab.org\tslice-%s\n" % \
204 aliases.write("slice-%s: \"%s slice %s\"\n" % \
205 (name, procmail, name))
207 # Generate {pi,tech}-LOGINBASE@sites.planet-lab.org and
208 # {pi,tech}-LOGINBASE alias
209 for site in GetSites(None, ['login_base']):
210 for prefix in ['pi', 'tech']:
211 # This is probably unnecessary since the mapping is 1-1
212 virtusertable.write("%s-%s@sites.planet-lab.org\t%s-%s\n" % \
213 (prefix, site['login_base'], prefix, site['login_base']))
214 aliases.write("%s-%s: \"%s %s %s\"\n" % \
215 (prefix, site['login_base'], procmail, prefix, site['login_base']))
217 # Generate special cases. all-pi and all-tech used to be aliases
218 # for all PIs and all techs. They are now mailing lists like
219 # announce. NO-pi and NO-tech notify support that a site is
220 # missing one or the other and are used by some scripts.
221 aliases.write("all-pi: pis\n")
222 aliases.write("all-tech: techs\n")
223 aliases.write("NO-pi: support\n")
224 aliases.write("NO-tech: support\n")
227 local_host_names.close()
228 virtusertable.close()
232 # Otherwise, print space-separated list of aliases
234 if argv[0] == "slice":
235 print " ".join(GetSliceUsers(argv[1]))
236 elif argv[0] == "pi":
237 print " ".join(GetPIs(argv[1]))
238 elif argv[0] == "tech":
239 print " ".join(GetTechs(argv[1]))
242 plcapi.usage(moreusage)