]> scripts.mit.edu Git - wizard.git/blob - wizard/scripts.py
Update TODO.
[wizard.git] / wizard / scripts.py
1 """
2 This is ostensibly the place where Scripts specific code should live.
3 """
4
5 import os
6 import shlex
7 import errno
8 import logging
9 import urlparse
10 import time
11 import errno
12
13 import wizard
14 from wizard import shell, util
15
16 def fill_url(dir, url=None):
17     """
18     Attempts to determine the URL a directory would be web-accessible at.
19     If ``url`` is specified, automatically use it.
20     """
21     if url:
22         return url
23
24     # hook hook
25
26     # try the directory
27     homedir, _, web_path = dir.partition("/web_scripts")
28     if web_path:
29         return urlparse.ParseResult(
30                 "http",
31                 util.get_dir_owner(homedir) + ".scripts.mit.edu",
32                 web_path.rstrip('/'),
33                 "", "", "")
34
35     # try the environment
36     host = os.getenv("WIZARD_WEB_HOST")
37     path = os.getenv("WIZARD_WEB_PATH")
38     if host is not None and path is not None:
39         return urlparse.ParseResult(
40                 "http",
41                 host,
42                 path.rstrip('/'),
43                 "", "", "")
44
45     return None
46
47 def get_quota_usage_and_limit(dir=None):
48     """
49     Returns a tuple (quota usage, quota limit).  Works only for scripts
50     servers.  Values are in KiB.  Returns ``(0, None)`` if we couldn't figure it out.
51     """
52     end = 2
53     # sometimes the volume is busy; so we try several times
54     for i in range(0, end + 1):
55         try:
56             return _get_quota_usage_and_limit(dir)
57         except QuotaParseError as e:
58             if i == end:
59                 raise e
60             time.sleep(3) # give it a chance to unbusy
61     assert False # should not get here
62
63 def _get_quota_usage_and_limit(dir=None):
64     # XXX: The correct way is to implement Python modules implementing
65     # bindings for all the appropriate interfaces
66     def parse_last_quote(ret):
67         return ret.rstrip('\'').rpartition('\'')[2]
68     if dir is None:
69         dir = os.getcwd()
70     sh = shell.Shell()
71     try:
72         cell = parse_last_quote(sh.eval("fs", "whichcell", "-path", dir))
73     except shell.CallError:
74         return (0, None)
75     except OSError as e:
76         if e.errno == errno.ENOENT:
77             return (0, None)
78         raise
79     mount = None
80     while dir:
81         try:
82             volume = parse_last_quote(sh.eval("fs", "lsmount", dir))[1:]
83             break
84         except shell.CallError:
85             dir = os.path.dirname(dir)
86         except OSError as e:
87             if e.errno == errno.ENOENT:
88                 return (0, None)
89             raise
90     if not volume: return (0, None)
91     try:
92         result = sh.eval("vos", "examine", "-id", volume, "-cell", cell).splitlines()
93     except OSError:
94         try:
95             result = sh.eval("/usr/sbin/vos", "examine", "-id", volume, "-cell", cell).splitlines()
96         except OSError:
97             return (0, None)
98     except shell.CallError:
99         return (0, None)
100     try:
101         usage = int(result[0].split()[3])
102         limit = int(result[3].split()[1]) # XXX: FRAGILE
103     except ValueError:
104         raise QuotaParseError("vos examine output was:\n\n" + "\n".join(result))
105     return (usage, limit)
106
107 # XXX: Possibly in the wrong module
108 def get_disk_usage(dir=None, excluded_dir=".git"):
109     """
110     Recursively determines the disk usage of a directory, excluding
111     .git directories.  Value is in bytes.
112     """
113     if dir is None: dir = os.getcwd()
114     sum_sizes = 0
115     for root, _, files in os.walk(dir):
116         for name in files:
117             if not os.path.join(root, name).startswith(dir + excluded_dir):
118                 file = os.path.join(root, name)
119                 try:
120                     sum_sizes += os.path.getsize(file)
121                 except OSError as e:
122                     if e.ENOENT:
123                         logging.warning("%s disappeared before we could stat", file)
124                     else:
125                         raise
126     return sum_sizes
127
128 class QuotaParseError(wizard.Error):
129     """Could not parse quota information."""
130     def __init__(self, msg):
131         self.msg = msg
132     def __str__(self):
133         return """
134
135 ERROR: Could not parse quota. %s
136 """ % self.msg