2 This is ostensibly the place where Scripts specific code should live.
15 from wizard import deploy, shell, install, util, user
19 "joomla", "e107", "gallery2", "advancedbook", "phpical",
20 "trac", "turbogears", "django", "rails",
21 # these are technically deprecated
22 "advancedpoll", "gallery",
27 homedir, _, web_path = dir.partition("/web_scripts")
29 # XXX: To be truly correct, we should emulate the logic of suexec; but
30 # looking at the home directory is a pretty good stopgap for now
31 name = homedir.rpartition("/")[2]
32 yield urlparse.ParseResult(
34 name + ".scripts.mit.edu",
37 yield urlparse.ParseResult(
40 "/~" + name + web_path.rstrip('/'),
43 logging.info("Directory location did not contain web_scripts: %s", dir)
45 def user_quota(dir=None):
47 Returns a tuple (quota usage, quota limit). Works only for scripts
48 servers. Values are in bytes. Returns ``(0, None)`` if we couldn't figure it out.
51 # sometimes the volume is busy; so we try several times
52 for i in range(0, end + 1):
54 return _user_quota(dir)
55 except QuotaParseError as e:
58 time.sleep(3) # give it a chance to unbusy
59 assert False # should not get here
61 def _user_quota(dir=None):
62 # XXX: The correct way is to implement Python modules implementing
63 # bindings for all the appropriate interfaces
65 def parse_last_quote(ret):
66 return ret.rstrip('\'').rpartition('\'')[2]
71 cell = parse_last_quote(sh.eval("fs", "whichcell", "-path", dir))
72 except shell.CallError:
75 if e.errno == errno.ENOENT:
81 volume = parse_last_quote(sh.eval("fs", "lsmount", dir))[1:]
83 except shell.CallError:
84 dir = os.path.dirname(dir)
86 if e.errno == errno.ENOENT:
89 if not volume: return unknown
91 result = sh.eval("vos", "examine", "-id", volume, "-cell", cell).splitlines()
94 result = sh.eval("/usr/sbin/vos", "examine", "-id", volume, "-cell", cell).splitlines()
97 except shell.CallError:
99 logging.debug("vos examine output was:\n\n" + "\n".join(result))
101 usage = int(result[0].split()[3]) * 1024
102 limit = int(result[3].split()[1]) * 1024 # XXX: FRAGILE
104 raise QuotaParseError("vos examine output was:\n\n" + "\n".join(result))
105 return (usage, limit)
107 class QuotaParseError(wizard.Error):
108 """Could not parse quota information."""
109 def __init__(self, msg):
114 ERROR: Could not parse quota. %s
118 if url.driver == "mysql":
120 url.host, url.username, url.password = shell.Shell().eval("/mit/scripts/sql/bin/get-password").split()
122 except shell.CallError:
129 if url.host == "sql.mit.edu":
131 shell.call("/mit/scripts/sql/bin/drop-database", url.database)
133 except shell.CallError:
137 def user_email(name):
138 # XXX: simplistic install which doesn't work most of the time
139 return "%s@scripts.mit.edu" % name
143 Returns username of the person operating this script based
144 off of the :envvar:`SSH_GSSAPI_NAME` environment variable.
148 :envvar:`SSH_GSSAPI_NAME` is not set by a vanilla OpenSSH
149 distributions. Scripts servers are patched to support this
150 environment variable.
152 principal = os.getenv("SSH_GSSAPI_NAME")
155 instance, _, _ = principal.partition("@")
156 if instance.endswith("/root"):
157 username, _, _ = principal.partition("/")
162 def user_passwd(dir, uid):
163 # XXX: simplistic heuristic for detecting AFS. The correct thing to
164 # is either to statfs and match magic number, use one of the
165 # vos tools or check mounted directories.
166 if not dir.startswith("/afs/"):
169 result = shell.eval("hesinfo", str(uid), "uid")
170 except shell.CallError:
172 name, password, uid, gid, gecos, homedir, console = result.split(":")
173 realname = gecos.split(",")[0]
174 return user.Info(name, uid, gid, realname, homedir, console)
176 class MysqlStrategy(install.Strategy):
178 Performs scripts specific guesses for MySQL variables. This
179 may create an appropriate database for the user.
182 provides = frozenset(["dsn"])
184 """Uses the SQL programs in the scripts locker"""
185 logging.info("Attempting wizard_scripts MySQL strategy")
186 if self.application.database != "mysql":
187 raise install.StrategyFailed
189 self._triplet = shell.eval("/mit/scripts/sql/bin/get-password").split()
191 raise install.StrategyFailed
192 except shell.CallError:
193 raise install.StrategyFailed
194 if len(self._triplet) != 3:
195 raise install.StrategyFailed
196 self._username = os.getenv('USER')
197 if self._username is None:
198 raise install.StrategyFailed
199 def execute(self, options):
200 """Creates a new database for the user using ``get-next-database`` and ``create-database``."""
201 host, username, password = self._triplet
203 name = shell.eval("/mit/scripts/sql/bin/get-next-database", os.path.basename(self.dir))
204 database = shell.eval("/mit/scripts/sql/bin/create-database", name)
206 raise CreateDatabaseError
207 options.dsn = sqlalchemy.engine.url.URL("mysql", username=username, password=password, host=host, database=database)
209 class EmailStrategy(install.Strategy):
210 """Performs script specific guess for email."""
211 provides = frozenset(["email"])
213 """Uses :envvar:`USER` and assumes you are an MIT affiliate."""
214 # XXX: This might be buggy, because locker might be set to USER
215 self._user = os.getenv("USER")
216 if self._user is None:
217 raise install.StrategyFailed
218 def execute(self, options):
220 options.email = self._user + "@mit.edu"
222 class CreateDatabaseError(wizard.Error):
223 """Could not create a database with the create-database script."""
227 ERROR: We were unable to create a database for you: this may indicate
228 that you have exceeded your quota of databases or indicate a problem
229 with the sql.mit.edu service. Please mail scripts@mit.edu with this