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