payment: note that Stripe has instituted an additional 1% fee for non-US cards
[hcoop/portal.git] / stats.sml
CommitLineData
b6e23181
AC
1structure Stats :> STATS =
2struct
5bdc0d94
CE
3 val webbw = Config.statsRoot ^ "webbw"
4 val webbw_last = Config.statsRoot ^ "webbw.last"
5 val webbw_last2 = Config.statsRoot ^ "webbw.last2"
b6e23181
AC
6
7 type host = {ssl : bool,
8 hostname : string,
9 id : string}
10
b13a9a37
AC
11 fun checkSsl (node, host) =
12 let
13 val id = case String.tokens (fn ch => ch = #".") host of
14 [] => node ^ "/" ^ host
15 | first :: rest =>
16 case rev (String.tokens (fn ch => ch = #"_") first) of
17 "ssl" :: rest' => node ^ "/" ^ String.concatWith "_" (rev rest')
18 ^ "." ^ String.concatWith "." rest ^ ".ssl"
19 | _ => node ^ "/" ^ host
20 in
21 case String.fields (fn ch => ch = #".") host of
22 first::rest =>
23 (case String.fields (fn ch => ch = #"_") first of
24 [first, "ssl"] => {ssl = true, hostname = String.concatWith "." (first::rest),
25 id = id}
26 | _ => {ssl = false, hostname = host, id = id})
27 | _ => {ssl = false, hostname = host, id = id}
28 end
b6e23181
AC
29
30 fun getWebbw last =
31 let
32 val fname =
33 case last of
34 2 => webbw_last2
35 | 1 => webbw_last
36 | 0 => webbw
37 | _ => raise Fail "Asked for too old of a bandwidth file"
38
39 val inf = TextIO.openIn fname
40
41 val sum = case TextIO.inputLine inf of
42 NONE => raise Fail "Can't read webbw"
43 | SOME l =>
44
45 case String.tokens Char.isSpace l of
46 [_, n] => valOf (Int.fromString n)
47 | _ => raise Fail "Bad total in webbw"
48
49 fun readEm L =
50 case TextIO.inputLine inf of
51 (NONE | SOME "\n") => List.rev L
52 | SOME l =>
53 case String.tokens (fn ch => Char.isSpace ch orelse ch = #":") l of
b13a9a37
AC
54 [d, n] =>
55 (case String.tokens (fn ch => ch = #"@") d of
56 [d, node] => readEm ({host = checkSsl (node, d), size = valOf (Int.fromString n)} :: L)
57 | _ => raise Fail ("Bad row in webbw [2]: " ^ l))
58 | _ => raise Fail ("Bad row in webbw [1]: " ^ l)
b6e23181
AC
59
60 fun splitLast [] = raise Fail "Not enough items for splitLast"
61 | splitLast [x] = ([], x)
62 | splitLast (h::t) =
63 let
64 val (l, x) = splitLast t
65 in
66 (h::l, x)
67 end
68
69 fun readGroups L =
70 case TextIO.inputLine inf of
71 NONE => List.rev L
72 | SOME l =>
73 case String.tokens (fn ch => Char.isSpace ch orelse ch = #":" orelse ch = #"[" orelse ch = #"]" orelse ch = #",") l of
74 d :: rest =>
75 let
76 val (l, x) = splitLast rest
b13a9a37
AC
77
78 fun split s =
79 case String.tokens (fn ch => ch = #"@") s of
80 [host, node] => (node, host)
81 | _ => raise Fail ("Bad row in webbw [3]: " ^ s)
b6e23181 82 in
b13a9a37 83 readGroups ({user = d, hosts = map (checkSsl o split) l, size = valOf (Int.fromString x)} :: L)
b6e23181 84 end
b13a9a37 85 | _ => raise Fail ("Bad row in webbw [4]: " ^ l)
b6e23181
AC
86 in
87 TextIO.inputLine inf;
b13a9a37 88 {total = sum, vhosts = readEm [], users = readGroups []}
b6e23181
AC
89 before TextIO.closeIn inf
90 end
91
432aa258
AC
92 fun getWebbwUser {user, last} =
93 let
94 val {vhosts, users, ...} = getWebbw last
95 in
96 case List.find (fn {user = u, ...} => u = user) users of
97 NONE => {total = 0, vhosts = []}
98 | SOME {hosts, size, ...} =>
99 {total = size, vhosts = List.filter (fn {host, ...} => List.exists (fn host' => host' = host) hosts) vhosts}
100 end
101
102
103 type disk = {uname : string,
23bfc877 104 kbs : int}
b6e23181 105
e27a09d3
AC
106 structure StringKey = struct
107 type ord_key = string
108 val compare = String.compare
109 end
110
111 structure SM = BinaryMapFn(StringKey)
112
b6e23181
AC
113 fun getDiskUsage () =
114 let
80da0f7a 115 val proc = Unix.execute ("/bin/sh", ["-c", "/usr/bin/vos listvol gibran"])
b6e23181
AC
116 val inf = Unix.textInstreamOf proc
117
23bfc877
AC
118 fun loop acc =
119 case TextIO.inputLine inf of
120 NONE => acc
121 | SOME line =>
122 case String.tokens Char.isSpace line of
e27a09d3
AC
123 [vol, _, _, kbs, _, _] =>
124 let
125 val kbsOld = case SM.find (acc, vol) of
126 NONE => 0
127 | SOME n => n
128
129 val uname = case String.tokens (fn ch => ch = #".") vol of
130 [_, uname] =>
131 ((Posix.SysDB.getpwnam uname;
132 SOME uname)
133 handle OS.SysErr _ => NONE)
134 | _ => NONE
135
136 val acc = case uname of
137 NONE => acc
138 | SOME uname => SM.insert (acc, uname, valOf (Int.fromString kbs) + kbsOld)
139 in
23bfc877 140 loop acc
e27a09d3
AC
141 end
142 | _ => acc
143
144 val _ = TextIO.inputLine inf
145 val users = map (fn (uname, kbs) => {uname = uname, kbs = kbs}) (SM.listItemsi (loop SM.empty))
b6e23181 146 in
e27a09d3
AC
147 ignore (Unix.reap proc);
148 ListMergeSort.sort (fn ({kbs = kbs1, ...}, {kbs = kbs2, ...}) => kbs1 < kbs2) users
b6e23181
AC
149 end
150
151end