import urlparse
import time
import errno
+import sqlalchemy
import wizard
-from wizard import shell, util
+from wizard import deploy, shell, install, util, user
+
+def dummy_apps():
+ return [
+ "joomla", "e107", "gallery2", "advancedbook", "phpical",
+ "trac", "turbogears", "django", "rails",
+ # these are technically deprecated
+ "advancedpoll", "gallery",
+ ]
def deploy_web(dir):
# try the directory
homedir, _, web_path = dir.partition("/web_scripts")
if web_path:
+ # XXX: To be truly correct, we should emulate the logic of suexec; but
+ # looking at the home directory is a pretty good stopgap for now
+ name = homedir.rpartition("/")[2]
yield urlparse.ParseResult(
"http",
- util.get_dir_owner(homedir) + ".scripts.mit.edu",
+ name + ".scripts.mit.edu",
web_path.rstrip('/'),
"", "", "")
yield urlparse.ParseResult(
"http",
"scripts.mit.edu",
- "/~" + util.get_dir_owner(homedir) + web_path.rstrip('/'),
+ "/~" + name + web_path.rstrip('/'),
"", "", "")
+ else:
+ logging.info("Directory location did not contain web_scripts: %s", dir)
def user_quota(dir=None):
"""
Returns a tuple (quota usage, quota limit). Works only for scripts
- servers. Values are in KiB. Returns ``(0, None)`` if we couldn't figure it out.
+ servers. Values are in bytes. Returns ``(0, None)`` if we couldn't figure it out.
"""
end = 2
# sometimes the volume is busy; so we try several times
return unknown
except shell.CallError:
return unknown
+ logging.debug("vos examine output was:\n\n" + "\n".join(result))
try:
- usage = int(result[0].split()[3])
- limit = int(result[3].split()[1]) # XXX: FRAGILE
+ usage = int(result[0].split()[3]) * 1024
+ limit = int(result[3].split()[1]) * 1024 # XXX: FRAGILE
except ValueError:
raise QuotaParseError("vos examine output was:\n\n" + "\n".join(result))
return (usage, limit)
return url
except shell.CallError:
pass
+ except OSError:
+ pass
+ return None
+
+def sql_drop(url):
+ if url.host == "sql.mit.edu":
+ try:
+ shell.call("/mit/scripts/sql/bin/drop-database", url.database)
+ return True
+ except shell.CallError:
+ pass
return None
+
+def user_email(name):
+ # XXX: simplistic install which doesn't work most of the time
+ return "%s@scripts.mit.edu" % name
+
+def user_operator():
+ """
+ Returns username of the person operating this script based
+ off of the :envvar:`SSH_GSSAPI_NAME` environment variable.
+
+ .. note::
+
+ :envvar:`SSH_GSSAPI_NAME` is not set by a vanilla OpenSSH
+ distributions. Scripts servers are patched to support this
+ environment variable.
+ """
+ principal = os.getenv("SSH_GSSAPI_NAME")
+ if not principal:
+ return None
+ instance, _, _ = principal.partition("@")
+ if instance.endswith("/root"):
+ username, _, _ = principal.partition("/")
+ else:
+ username = instance
+ return username
+
+def user_passwd(dir, uid):
+ # XXX: simplistic heuristic for detecting AFS. The correct thing to
+ # is either to statfs and match magic number, use one of the
+ # vos tools or check mounted directories.
+ if not dir.startswith("/afs/"):
+ return None
+ try:
+ result = shell.eval("hesinfo", str(uid), "uid")
+ except shell.CallError:
+ return None
+ name, password, uid, gid, gecos, homedir, console = result.split(":")
+ realname = gecos.split(",")[0]
+ return user.Info(name, uid, gid, realname, homedir, console)
+
+class MysqlStrategy(install.Strategy):
+ """
+ Performs scripts specific guesses for MySQL variables. This
+ may create an appropriate database for the user.
+ """
+ side_effects = True
+ provides = frozenset(["dsn"])
+ def prepare(self):
+ """Uses the SQL programs in the scripts locker"""
+ logging.info("Attempting wizard_scripts MySQL strategy")
+ if self.application.database != "mysql":
+ raise install.StrategyFailed
+ try:
+ self._triplet = shell.eval("/mit/scripts/sql/bin/get-password").split()
+ except OSError:
+ raise install.StrategyFailed
+ except shell.CallError:
+ raise install.StrategyFailed
+ if len(self._triplet) != 3:
+ raise install.StrategyFailed
+ self._username = os.getenv('USER')
+ if self._username is None:
+ raise install.StrategyFailed
+ def execute(self, options):
+ """Creates a new database for the user using ``get-next-database`` and ``create-database``."""
+ host, username, password = self._triplet
+ # race condition
+ name = shell.eval("/mit/scripts/sql/bin/get-next-database", os.path.basename(self.dir))
+ database = shell.eval("/mit/scripts/sql/bin/create-database", name)
+ if not database:
+ raise CreateDatabaseError
+ options.dsn = sqlalchemy.engine.url.URL("mysql", username=username, password=password, host=host, database=database)
+
+class EmailStrategy(install.Strategy):
+ """Performs script specific guess for email."""
+ provides = frozenset(["email"])
+ def prepare(self):
+ """Uses :envvar:`USER` and assumes you are an MIT affiliate."""
+ # XXX: This might be buggy, because locker might be set to USER
+ self._user = os.getenv("USER")
+ if self._user is None:
+ raise install.StrategyFailed
+ def execute(self, options):
+ """No-op."""
+ options.email = self._user + "@mit.edu"
+
+class CreateDatabaseError(wizard.Error):
+ """Could not create a database with the create-database script."""
+ def __str__(self):
+ return """
+
+ERROR: We were unable to create a database for you: this may indicate
+that you have exceeded your quota of databases or indicate a problem
+with the sql.mit.edu service. Please mail scripts@mit.edu with this
+error message."""