ct 0.10.8.114__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cantools/__init__.py +24 -0
- cantools/_db.py +142 -0
- cantools/_memcache.py +76 -0
- cantools/_pay.py +46 -0
- cantools/admin.py +31 -0
- cantools/cfg.py +347 -0
- cantools/config.py +131 -0
- cantools/db/__init__.py +18 -0
- cantools/db/admin.py +27 -0
- cantools/db/gae/__init__.py +0 -0
- cantools/db/gae/model.py +127 -0
- cantools/db/gae/properties.py +35 -0
- cantools/db/wp.py +99 -0
- cantools/geo.py +188 -0
- cantools/hooks.py +13 -0
- cantools/scripts/__init__.py +0 -0
- cantools/scripts/bench.py +167 -0
- cantools/scripts/builder.py +272 -0
- cantools/scripts/deploy.py +154 -0
- cantools/scripts/doc.py +239 -0
- cantools/scripts/index.py +226 -0
- cantools/scripts/init.py +345 -0
- cantools/scripts/migrate.py +593 -0
- cantools/scripts/pubsub/__init__.py +28 -0
- cantools/scripts/pubsub/actor.py +13 -0
- cantools/scripts/pubsub/bots.py +143 -0
- cantools/scripts/pubsub/channel.py +85 -0
- cantools/scripts/pubsub/ps.py +145 -0
- cantools/scripts/pubsub/user.py +51 -0
- cantools/scripts/start.py +53 -0
- cantools/scripts/util.py +24 -0
- cantools/util/__init__.py +78 -0
- cantools/util/admin.py +620 -0
- cantools/util/data.py +109 -0
- cantools/util/media.py +303 -0
- cantools/util/package.py +125 -0
- cantools/util/system.py +73 -0
- cantools/web/__init__.py +9 -0
- cantools/web/dez_server/__init__.py +1 -0
- cantools/web/dez_server/controller.py +129 -0
- cantools/web/dez_server/cron.py +115 -0
- cantools/web/dez_server/daemons.py +64 -0
- cantools/web/dez_server/mail.py +24 -0
- cantools/web/dez_server/response.py +63 -0
- cantools/web/dez_server/routes.py +21 -0
- cantools/web/dez_server/server.py +229 -0
- cantools/web/dez_server/sms.py +12 -0
- cantools/web/gae_server.py +68 -0
- cantools/web/util.py +552 -0
- ct-0.10.8.114.dist-info/LICENSE +9 -0
- ct-0.10.8.114.dist-info/METADATA +25 -0
- ct-0.10.8.114.dist-info/RECORD +55 -0
- ct-0.10.8.114.dist-info/WHEEL +5 -0
- ct-0.10.8.114.dist-info/entry_points.txt +10 -0
- ct-0.10.8.114.dist-info/top_level.txt +1 -0
cantools/util/admin.py
ADDED
|
@@ -0,0 +1,620 @@
|
|
|
1
|
+
import os, sys, rel, time, datetime
|
|
2
|
+
from cantools.util import cmd, output, error, log, set_log, close_log, read, write, confirm, rm
|
|
3
|
+
|
|
4
|
+
coremods = ["screen", "ctstart", "ctpubsub", "ctutil", "ctinit", "dez_reverse_proxy", "dez_websocket_proxy"]
|
|
5
|
+
installers = ["apt", "yum", "pkg", "brew"]
|
|
6
|
+
|
|
7
|
+
def _starter(sname=None):
|
|
8
|
+
if sname == "None":
|
|
9
|
+
sname = None
|
|
10
|
+
starter = "screen -wipe ; screen -L -dm"
|
|
11
|
+
return sname and "%s -S %s"%(starter, sname) or starter
|
|
12
|
+
|
|
13
|
+
def _which(*names):
|
|
14
|
+
success = True
|
|
15
|
+
for name in names:
|
|
16
|
+
if not output("which %s"%(name,)):
|
|
17
|
+
log("%s not in path!"%(name,))
|
|
18
|
+
success = False
|
|
19
|
+
return success
|
|
20
|
+
|
|
21
|
+
def first(*names):
|
|
22
|
+
for name in names:
|
|
23
|
+
if _which(name):
|
|
24
|
+
log(name)
|
|
25
|
+
return name
|
|
26
|
+
|
|
27
|
+
def installer():
|
|
28
|
+
return first(*installers)
|
|
29
|
+
|
|
30
|
+
def install(*pkgs):
|
|
31
|
+
pacman = installer()
|
|
32
|
+
pline = " ".join(pkgs)
|
|
33
|
+
log("using %s to install %s"%(pacman, pline), important=True)
|
|
34
|
+
cmd("%s install %s"%(pacman, pline), sudo=True)
|
|
35
|
+
|
|
36
|
+
def snapinstall(pkg, classic=False):
|
|
37
|
+
iline = "snap install %s"%(pkg,)
|
|
38
|
+
if classic:
|
|
39
|
+
iline = "%s --classic"%(iline,)
|
|
40
|
+
cmd(iline, sudo=True)
|
|
41
|
+
|
|
42
|
+
def simplecfg(fname, sequential=False):
|
|
43
|
+
if not os.path.exists(fname):
|
|
44
|
+
return log("configuration file not found: %s"%(fname,))
|
|
45
|
+
data = [] if sequential else {}
|
|
46
|
+
for line in read(fname).split("\n"):
|
|
47
|
+
if not line or line.startswith("#"):
|
|
48
|
+
continue
|
|
49
|
+
variety = "basic"
|
|
50
|
+
if ":" in line:
|
|
51
|
+
variety, line = line.split(":", 1)
|
|
52
|
+
if sequential:
|
|
53
|
+
data.append({"variety": variety, "line": line})
|
|
54
|
+
else:
|
|
55
|
+
if variety not in data:
|
|
56
|
+
data[variety] = []
|
|
57
|
+
data[variety].append(line)
|
|
58
|
+
return data
|
|
59
|
+
|
|
60
|
+
def certs(dpath="/root", sname=None):
|
|
61
|
+
os.chdir(dpath)
|
|
62
|
+
set_log("cron-certs.log")
|
|
63
|
+
log("attempting to renew certs", important=True)
|
|
64
|
+
cmd('certbot renew --pre-hook "killall screen" --post-hook "%s"'%(_starter(sname),))
|
|
65
|
+
log("goodbye")
|
|
66
|
+
close_log()
|
|
67
|
+
|
|
68
|
+
def pcount(pname):
|
|
69
|
+
log("checking count: %s"%(pname,), important=True)
|
|
70
|
+
num = int(output("ps -ef | grep %s | egrep -v 'screener|pcount|grep' | wc -l"%(pname,)))
|
|
71
|
+
log("%s count: %s"%(pname, num), 1)
|
|
72
|
+
return num
|
|
73
|
+
|
|
74
|
+
def pcheck(pname, target, starter):
|
|
75
|
+
if target and pcount(pname) != target:
|
|
76
|
+
log("not enough %s processes - restarting screen!"%(pname,), 1)
|
|
77
|
+
log(output("screen -Q windows"), important=True)
|
|
78
|
+
cmd("killall screen; %s"%(starter,))
|
|
79
|
+
return True
|
|
80
|
+
|
|
81
|
+
def binpath(bpath="/usr/bin/"):
|
|
82
|
+
log("checking %s core modules"%(len(coremods),), important=True)
|
|
83
|
+
missings = []
|
|
84
|
+
for name in coremods:
|
|
85
|
+
mpath = "%s%s"%(bpath, name)
|
|
86
|
+
log("checking %s"%(name,), 1)
|
|
87
|
+
if not os.path.exists(mpath):
|
|
88
|
+
mloc = output("which %s"%(name,))
|
|
89
|
+
mloc or error("%s not installed"%(name,))
|
|
90
|
+
log("%s not in %s - found at %s instead"%(name, bpath, mloc))
|
|
91
|
+
missings.append(mloc)
|
|
92
|
+
if missings and confirm("symlink %s modules to %s"%(len(missings), bpath)):
|
|
93
|
+
os.chdir(bpath)
|
|
94
|
+
for mloc in missings:
|
|
95
|
+
cmd("ln -s %s"%(mloc,))
|
|
96
|
+
log("goodbye", important=True)
|
|
97
|
+
|
|
98
|
+
def postup():
|
|
99
|
+
from cantools.util.package import refresh_plugins
|
|
100
|
+
refresh_plugins()
|
|
101
|
+
binpath()
|
|
102
|
+
|
|
103
|
+
def zipit(fname, oname=None, remove=False, keepsyms=False):
|
|
104
|
+
log("zipping up %s"%(fname,), important=True)
|
|
105
|
+
if keepsyms == "ask":
|
|
106
|
+
keepsyms = confirm("keep symlinks", True)
|
|
107
|
+
ops = keepsyms and "ry" or "r"
|
|
108
|
+
oname = oname or "%s.zip"%(fname,)
|
|
109
|
+
cmd("zip -%s %s %s"%(ops, oname, fname))
|
|
110
|
+
remove and cmd("rm -rf %s"%(fname,))
|
|
111
|
+
|
|
112
|
+
def termap(term, default=1):
|
|
113
|
+
m = {}
|
|
114
|
+
terms = term.split("|")
|
|
115
|
+
for key in terms:
|
|
116
|
+
if ":" in key:
|
|
117
|
+
key, val = key.split(":")
|
|
118
|
+
val = int(val)
|
|
119
|
+
else:
|
|
120
|
+
val = default
|
|
121
|
+
m[key] = val
|
|
122
|
+
print(m)
|
|
123
|
+
return m
|
|
124
|
+
|
|
125
|
+
def dedchek(logname="screenlog.0", flag="server not ready"):
|
|
126
|
+
ldata = read(logname, default="")
|
|
127
|
+
count = len(ldata.split(flag)) - 1
|
|
128
|
+
set_log("dedchek.log")
|
|
129
|
+
log("dedchek: %s"%(count,), important=True)
|
|
130
|
+
if count:
|
|
131
|
+
from cantools.web import email_admins
|
|
132
|
+
email_admins("dedchek", "\n\n".join([
|
|
133
|
+
"unresponsive count: %s"%(count,),
|
|
134
|
+
"rotating log!",
|
|
135
|
+
"restarting screen!"
|
|
136
|
+
]))
|
|
137
|
+
tstamp = str(datetime.datetime.now()).replace(" ", "_")
|
|
138
|
+
dname = "%sarchive"%(logname,)
|
|
139
|
+
if not os.path.isdir(dname):
|
|
140
|
+
cmd("mkdir %s"%(dname,))
|
|
141
|
+
cmd("mv %s %s"%(logname, os.path.join(dname, tstamp)))
|
|
142
|
+
cmd("killall screen; %s"%(_starter(),))
|
|
143
|
+
log("goodbye", important=True)
|
|
144
|
+
close_log()
|
|
145
|
+
|
|
146
|
+
def islocked(pname): # eg "dpkg" or "apt/lists"
|
|
147
|
+
unlocked = "Lock acquired"
|
|
148
|
+
lpath = "/var/lib/%s/lock"%(pname,)
|
|
149
|
+
log("testing %s lock at %s"%(pname, lpath))
|
|
150
|
+
fbase = "flock --timeout 0 --exclusive --nonblock"
|
|
151
|
+
fcmd = '%s %s -c "echo %s" || echo Lock is held'%(fbase, lpath, unlocked)
|
|
152
|
+
op = output(fcmd, sudo=True, silent=True, loud=True)
|
|
153
|
+
return op != unlocked
|
|
154
|
+
|
|
155
|
+
def screener(ctnum=None, dpath="/root", drpnum=None, psnum=None, sname=None, tmap=None):
|
|
156
|
+
os.chdir(dpath)
|
|
157
|
+
set_log("scrn.log")
|
|
158
|
+
log("checking modules", important=True)
|
|
159
|
+
_which(*coremods) or error("update your path!")
|
|
160
|
+
starter = _starter(sname)
|
|
161
|
+
if ctnum and type(ctnum) is not int:
|
|
162
|
+
ctnum = ctnum.isdigit() and int(ctnum)
|
|
163
|
+
if drpnum and type(drpnum) is not int:
|
|
164
|
+
drpnum = drpnum.isdigit() and int(drpnum)
|
|
165
|
+
if psnum and type(psnum) is not int:
|
|
166
|
+
psnum = psnum.isdigit() and int(psnum)
|
|
167
|
+
if "No Sockets found" in output("screen -list"):
|
|
168
|
+
log("no screen! restarting", 1)
|
|
169
|
+
cmd(starter)
|
|
170
|
+
else:
|
|
171
|
+
restarted = pcheck("ctstart", ctnum, starter) or pcheck("dez_reverse_proxy", drpnum, starter) or pcheck("ctpubsub", psnum, starter)
|
|
172
|
+
if tmap:
|
|
173
|
+
for k, v in termap(tmap).items():
|
|
174
|
+
restarted = restarted or pcheck(k, v, starter)
|
|
175
|
+
log("goodbye", important=True)
|
|
176
|
+
close_log()
|
|
177
|
+
|
|
178
|
+
def check(cmd="df"):
|
|
179
|
+
oz = output(cmd).split("\n")
|
|
180
|
+
for o in oz:
|
|
181
|
+
if o.endswith("/"):
|
|
182
|
+
return int(o.rsplit(" ", 2)[1][:-1])
|
|
183
|
+
|
|
184
|
+
def plugdirs(cb, pdir=".ctplug", filt="ct", ask=True):
|
|
185
|
+
opath = os.path.abspath(".")
|
|
186
|
+
os.chdir(pdir)
|
|
187
|
+
pz = os.listdir(".")
|
|
188
|
+
log("%s items in directory"%(len(pz),))
|
|
189
|
+
if filt:
|
|
190
|
+
pz = list(filter(lambda name : name.startswith(filt), pz))
|
|
191
|
+
log("found %s plugins:\n\n%s"%(len(pz), "\n".join(pz)))
|
|
192
|
+
if ask and not confirm("process %s plugins"%(len(pz),), True):
|
|
193
|
+
return
|
|
194
|
+
for p in pz:
|
|
195
|
+
log(p)
|
|
196
|
+
os.chdir(p)
|
|
197
|
+
cb()
|
|
198
|
+
os.chdir("..")
|
|
199
|
+
os.chdir(opath)
|
|
200
|
+
|
|
201
|
+
def gc(pdir=".ctplug", filt="ct", ask=False):
|
|
202
|
+
plugdirs(lambda : cmd("git gc"), pdir, filt, ask)
|
|
203
|
+
|
|
204
|
+
def cleanup(force=False):
|
|
205
|
+
if force or confirm("garbage collect git repos"):
|
|
206
|
+
gc()
|
|
207
|
+
if force or confirm("vacuum up old journals"):
|
|
208
|
+
cmd("journalctl --vacuum-size=50M")
|
|
209
|
+
if force or confirm("remove old packages"):
|
|
210
|
+
cmd("apt autoremove")
|
|
211
|
+
if force or confirm("clean up screenlogs"):
|
|
212
|
+
slogs = output("du -a . | sort -n -r | head -n 20 | grep screenlog")
|
|
213
|
+
if slogs:
|
|
214
|
+
lines = slogs.split("\n")
|
|
215
|
+
paths = [l.split("\t").pop() for l in lines]
|
|
216
|
+
plen = len(paths)
|
|
217
|
+
log("founds %s screenlogs:\n\n%s"%(plen, "\n".join(paths)), important=True)
|
|
218
|
+
if plen and (force or confirm("delete %s screenlogs"%(plen,))):
|
|
219
|
+
for slog in paths:
|
|
220
|
+
rm(slog)
|
|
221
|
+
log("all clear!")
|
|
222
|
+
|
|
223
|
+
def matchline(oline, start=None, end=None, sudo=False, loud=True):
|
|
224
|
+
for line in output(oline, sudo=sudo, loud=loud).split("\n"):
|
|
225
|
+
if start and line.startswith(start):
|
|
226
|
+
return line
|
|
227
|
+
if end and line.endswith(end):
|
|
228
|
+
return line
|
|
229
|
+
|
|
230
|
+
def sysup(upit=False, upct=False, dpath="."):
|
|
231
|
+
from cantools.web import email_admins
|
|
232
|
+
os.chdir(dpath)
|
|
233
|
+
set_log("cron-sysup.log")
|
|
234
|
+
log("updating package index", important=True)
|
|
235
|
+
if islocked("dpkg") or islocked("apt/lists"):
|
|
236
|
+
email_admins("can't update package list", "dpkg or apt/lists file is locked :(")
|
|
237
|
+
cmd("apt update", sudo=True)
|
|
238
|
+
alist = output("apt list --upgradable")
|
|
239
|
+
adrep = []
|
|
240
|
+
if alist.endswith("Listing..."):
|
|
241
|
+
adrep.append("no system updates available")
|
|
242
|
+
else:
|
|
243
|
+
ublock = alist.split("Listing...\n").pop()
|
|
244
|
+
ulist = ublock.split("\n")
|
|
245
|
+
ulen = len(ulist)
|
|
246
|
+
log("%s upgrades available"%(ulen,))
|
|
247
|
+
adrep.append("%s system updates %s:"%(ulen, upit and "attempted" or "available"))
|
|
248
|
+
adrep.append(ublock)
|
|
249
|
+
if upit:
|
|
250
|
+
log("upgrading %s packages"%(ulen,), important=True)
|
|
251
|
+
adrep.append(matchline("apt upgrade -y", end=" not upgraded.", sudo=True))
|
|
252
|
+
if upct:
|
|
253
|
+
adrep.append("updating web framework and plugins")
|
|
254
|
+
fullupline = matchline("ctinit -du", "Successfully installed ")
|
|
255
|
+
fullupline and adrep.append(fullupline)
|
|
256
|
+
shouldReboot = False
|
|
257
|
+
if os.path.exists("/var/run/reboot-required"):
|
|
258
|
+
upaks = output("cat /var/run/reboot-required.pkgs", loud=True)
|
|
259
|
+
adrep.append("updates include: %s"%(upaks,))
|
|
260
|
+
adrep.append("system restart required!")
|
|
261
|
+
if upit == "auto":
|
|
262
|
+
shouldReboot = True
|
|
263
|
+
adrep.append("restarting system!")
|
|
264
|
+
if adrep:
|
|
265
|
+
adrep = "\n\n".join(adrep)
|
|
266
|
+
log(adrep, important=True)
|
|
267
|
+
email_admins("system updates", adrep)
|
|
268
|
+
log("goodbye")
|
|
269
|
+
shouldReboot and reboot()
|
|
270
|
+
close_log()
|
|
271
|
+
|
|
272
|
+
def reboot(wait=5):
|
|
273
|
+
log("rebooting in %s seconds!"%(wait,))
|
|
274
|
+
if wait:
|
|
275
|
+
time.sleep(int(wait))
|
|
276
|
+
cmd("reboot")
|
|
277
|
+
|
|
278
|
+
def running(proc):
|
|
279
|
+
if not "active (running)" in output("service %s status"%(proc,)):
|
|
280
|
+
return log("%s isn't running!!!"%(proc,))
|
|
281
|
+
log("%s is running"%(proc,))
|
|
282
|
+
return True
|
|
283
|
+
|
|
284
|
+
def upcheck(*procs):
|
|
285
|
+
from cantools.web import email_admins
|
|
286
|
+
set_log("cron-upcheck.log")
|
|
287
|
+
log("checking services: %s"%(", ".join(procs),), important=True)
|
|
288
|
+
restarts = []
|
|
289
|
+
for proc in procs:
|
|
290
|
+
if not running(proc):
|
|
291
|
+
restarts.append(proc)
|
|
292
|
+
log("restarting %s!"%(proc,), important=True)
|
|
293
|
+
servicer(proc)
|
|
294
|
+
restarts and email_admins("services restarted", "\n\n".join(restarts))
|
|
295
|
+
log("goodbye")
|
|
296
|
+
close_log()
|
|
297
|
+
|
|
298
|
+
def vitals(clean=False, thresh=90, dpath="."):
|
|
299
|
+
if clean == "False":
|
|
300
|
+
clean = False
|
|
301
|
+
thresh = int(thresh)
|
|
302
|
+
from cantools.web import email_admins
|
|
303
|
+
os.chdir(dpath)
|
|
304
|
+
set_log("cron-vitals.log")
|
|
305
|
+
log("scanning vitals", important=True)
|
|
306
|
+
lz = []
|
|
307
|
+
hdrive = check()
|
|
308
|
+
lz.append("hard drive usage: %s%%"%(hdrive,))
|
|
309
|
+
inodes = check("df -i")
|
|
310
|
+
lz.append("inode usage: %s%%"%(inodes,))
|
|
311
|
+
memuse = float(output("free | grep Mem | awk '{print $3/$2 * 100.0}'"))
|
|
312
|
+
lz.append("memory usage: %s%%"%(memuse,))
|
|
313
|
+
for l in lz:
|
|
314
|
+
log(l)
|
|
315
|
+
if hdrive > thresh or inodes > thresh or memuse > thresh:
|
|
316
|
+
log("threshold exceeded - notifying admins")
|
|
317
|
+
if hdrive > thresh and clean:
|
|
318
|
+
log("cleaning up!")
|
|
319
|
+
cleanup(True)
|
|
320
|
+
lz.append("cleaned up hard drive!")
|
|
321
|
+
email_admins("threshold exceeded", "\n".join(lz))
|
|
322
|
+
log("goodbye")
|
|
323
|
+
close_log()
|
|
324
|
+
|
|
325
|
+
def sslredirect(port=80):
|
|
326
|
+
from dez.http.reverseproxy import startreverseproxy
|
|
327
|
+
from cantools.config import Config
|
|
328
|
+
startreverseproxy(Config({
|
|
329
|
+
"port": port,
|
|
330
|
+
"ssl_redirect": "auto"
|
|
331
|
+
}))
|
|
332
|
+
|
|
333
|
+
def blacklist(ip):
|
|
334
|
+
if not confirm("blacklist %s?"%(ip,)):
|
|
335
|
+
return log("okay, bye!")
|
|
336
|
+
from cantools.web import controller
|
|
337
|
+
from cantools import config
|
|
338
|
+
controller.setBlacklist()
|
|
339
|
+
blist = config.web.blacklist.obj()
|
|
340
|
+
if ip in blist:
|
|
341
|
+
return log("%s is already blacklisted! reason: %s"%(ip, blist[ip]))
|
|
342
|
+
log("adding %s to black.list"%(ip,))
|
|
343
|
+
blist[ip] = input("reason? [default: 'manual ban'] ") or "manual ban"
|
|
344
|
+
log("saving black.list")
|
|
345
|
+
write(blist, "black.list", isjson=True)
|
|
346
|
+
confirm("restart screen?") and cmd("killall screen ; screen -L")
|
|
347
|
+
|
|
348
|
+
def replace(flag, swap, ext="md"):
|
|
349
|
+
fz = list(filter(lambda fn : fn.endswith(ext), os.listdir()))
|
|
350
|
+
log("processing %s %s files"%(len(fz), ext))
|
|
351
|
+
for f in fz:
|
|
352
|
+
log(f, 1)
|
|
353
|
+
write(read(f).replace(flag, swap), f)
|
|
354
|
+
log("goodbye!")
|
|
355
|
+
|
|
356
|
+
def json2abi(fname):
|
|
357
|
+
write(read(fname, isjson=True)['abi'], fname.replace("json", "abi"), isjson=True)
|
|
358
|
+
|
|
359
|
+
def enc(fname, oname=None, nowrite=False, asdata=False, nolog=False, replace=None):
|
|
360
|
+
from cantools.web import enc as wenc
|
|
361
|
+
oname = oname or fname.replace("txt", "enc")
|
|
362
|
+
nolog or log("enc(%s -> %s) nowrite=%s"%(asdata and "data" or fname, oname, nowrite))
|
|
363
|
+
data = asdata and fname or read(fname)
|
|
364
|
+
if replace: # tuple
|
|
365
|
+
rfrom, rto = replace
|
|
366
|
+
data = data.replace(rfrom, rto)
|
|
367
|
+
enced = wenc(data)
|
|
368
|
+
nowrite or write(enced, oname)
|
|
369
|
+
return enced
|
|
370
|
+
|
|
371
|
+
def dec(fname, oname=None, nowrite=False, asdata=False, nolog=False, replace=None):
|
|
372
|
+
from cantools.web import dec as wdec
|
|
373
|
+
oname = oname or fname.replace("enc", "txt")
|
|
374
|
+
nolog or log("dec(%s -> %s) nowrite=%s"%(asdata and "data" or fname, oname, nowrite))
|
|
375
|
+
deced = wdec(asdata and fname or read(fname))
|
|
376
|
+
if replace: # tuple
|
|
377
|
+
rfrom, rto = replace
|
|
378
|
+
deced = deced.replace(rfrom, rto)
|
|
379
|
+
nowrite or write(deced, oname)
|
|
380
|
+
return deced
|
|
381
|
+
|
|
382
|
+
def qenc(fname, asdata=False, nolog=True):
|
|
383
|
+
enced = enc(fname, nowrite=True, asdata=asdata, nolog=nolog)
|
|
384
|
+
print(enced)
|
|
385
|
+
return enced
|
|
386
|
+
|
|
387
|
+
def qdec(fname, asdata=False, nolog=True):
|
|
388
|
+
deced = dec(fname, nowrite=True, asdata=asdata, nolog=nolog)
|
|
389
|
+
print(deced)
|
|
390
|
+
return deced
|
|
391
|
+
|
|
392
|
+
def ushort(url):
|
|
393
|
+
from cantools import config
|
|
394
|
+
csl = config.shortlinker
|
|
395
|
+
if not csl:
|
|
396
|
+
error("no shortlinker configured")
|
|
397
|
+
from urllib.parse import quote
|
|
398
|
+
from cantools.web import fetch
|
|
399
|
+
code = fetch("https://%s?u=%s"%(csl, quote(url)), ctjson=True)
|
|
400
|
+
print("code:", code)
|
|
401
|
+
return "https://%s?k=%s"%(csl, code)
|
|
402
|
+
|
|
403
|
+
class Creeper(object):
|
|
404
|
+
def __init__(self, total=120, mid=40, short=10):
|
|
405
|
+
self.total = total
|
|
406
|
+
self.mid = mid
|
|
407
|
+
self.short = short
|
|
408
|
+
self.duration = 0
|
|
409
|
+
self.last = None
|
|
410
|
+
self.diffs = []
|
|
411
|
+
self.diff = 0
|
|
412
|
+
self.start()
|
|
413
|
+
|
|
414
|
+
def signed(self, num):
|
|
415
|
+
num = round(num, 2)
|
|
416
|
+
return num < 0 and num or "+%s"%(num,)
|
|
417
|
+
|
|
418
|
+
def pad(self, s, target=10):
|
|
419
|
+
ls = len(s)
|
|
420
|
+
if ls < target:
|
|
421
|
+
return "%s%s"%(s, " " * (target - ls))
|
|
422
|
+
return s
|
|
423
|
+
|
|
424
|
+
def ave(self, size=None):
|
|
425
|
+
dz = self.diffs
|
|
426
|
+
if size == "diff":
|
|
427
|
+
diff = self.diff
|
|
428
|
+
elif size == "cur":
|
|
429
|
+
diff = dz[-1]
|
|
430
|
+
else:
|
|
431
|
+
if size:
|
|
432
|
+
nums = dz[-size:]
|
|
433
|
+
else:
|
|
434
|
+
nums = dz
|
|
435
|
+
size = len(dz)
|
|
436
|
+
diff = sum(nums) / size
|
|
437
|
+
size = "%ss"%(size,)
|
|
438
|
+
return self.pad("%s: %s"%(size, self.signed(diff)))
|
|
439
|
+
|
|
440
|
+
def calc(self, diff):
|
|
441
|
+
self.diff += diff
|
|
442
|
+
dz = self.diffs
|
|
443
|
+
dz.append(diff)
|
|
444
|
+
if len(dz) > self.total:
|
|
445
|
+
dz.pop(0)
|
|
446
|
+
dl = len(dz)
|
|
447
|
+
parts = [self.ave("cur")]
|
|
448
|
+
if dl > 1:
|
|
449
|
+
if dl > self.short:
|
|
450
|
+
parts.append(self.ave(self.short))
|
|
451
|
+
if dl > self.mid:
|
|
452
|
+
parts.append(self.ave(self.mid))
|
|
453
|
+
parts.append(self.ave())
|
|
454
|
+
parts.append(self.ave("diff"))
|
|
455
|
+
return " ; ".join(parts)
|
|
456
|
+
|
|
457
|
+
def creep(self):
|
|
458
|
+
self.duration += 1
|
|
459
|
+
current = int(output("free | grep Mem | awk '{print $3}'", silent=True))
|
|
460
|
+
if self.last:
|
|
461
|
+
print("dur: %s ; %s"%(self.duration, self.calc(current - self.last)))
|
|
462
|
+
self.last = current
|
|
463
|
+
return True
|
|
464
|
+
|
|
465
|
+
def start(self):
|
|
466
|
+
rel.signal(2, rel.abort)
|
|
467
|
+
rel.timeout(1, self.creep)
|
|
468
|
+
rel.dispatch()
|
|
469
|
+
|
|
470
|
+
def memcreep(total=120, mid=40, short=10):
|
|
471
|
+
Creeper(int(total), int(mid), int(short))
|
|
472
|
+
|
|
473
|
+
def phpver():
|
|
474
|
+
if not _which("php"): return
|
|
475
|
+
v = output("php -v")[4:7]
|
|
476
|
+
log(v)
|
|
477
|
+
return v
|
|
478
|
+
|
|
479
|
+
def javaver():
|
|
480
|
+
if not _which("javac"): return
|
|
481
|
+
v = output("javac -version")[6:8]
|
|
482
|
+
log(v)
|
|
483
|
+
return v
|
|
484
|
+
|
|
485
|
+
def withtmp(fdata, fun, owner=None, fname="_tmp"):
|
|
486
|
+
write(fdata, fname)
|
|
487
|
+
owner and cmd("chown %s:%s %s"%(owner, owner, fname), True)
|
|
488
|
+
fun()
|
|
489
|
+
os.remove(fname)
|
|
490
|
+
|
|
491
|
+
def servicer(proc, action="restart", sycon="service", ask=False):
|
|
492
|
+
if ask and not confirm("%s %s"%(action, proc)):
|
|
493
|
+
return
|
|
494
|
+
if sycon == "service":
|
|
495
|
+
conline = "service " + proc + " %s"
|
|
496
|
+
else: # systemctl
|
|
497
|
+
conline = "systemctl %s " + proc
|
|
498
|
+
cmd(conline%(action,))
|
|
499
|
+
|
|
500
|
+
def whilestopped(proc, fun, sycon="service", starter="start"):
|
|
501
|
+
servicer(proc, "stop", sycon)
|
|
502
|
+
fun()
|
|
503
|
+
servicer(proc, starter, sycon)
|
|
504
|
+
|
|
505
|
+
# mysql stuff...
|
|
506
|
+
|
|
507
|
+
MYSQL_ALTERP = "ALTER USER '%s'@'%s' IDENTIFIED BY '%s';"
|
|
508
|
+
MYSQL_RESET = """flush privileges;
|
|
509
|
+
ALTER USER '%s'@'%s' IDENTIFIED BY '%s';"""
|
|
510
|
+
MYSQL_CREATE_USER = """FLUSH PRIVILEGES;
|
|
511
|
+
CREATE USER '%s'@'%s' IDENTIFIED BY '%s';
|
|
512
|
+
GRANT ALL PRIVILEGES ON *.* TO '%s'@'%s' WITH GRANT OPTION;
|
|
513
|
+
FLUSH PRIVILEGES;"""
|
|
514
|
+
MYSQL_USERS = """select User,Host from mysql.user;"""
|
|
515
|
+
|
|
516
|
+
def mysqltmp(fdata, fun, owner="mysql", sycon="service", starter="start"):
|
|
517
|
+
withtmp(fdata, lambda : whilestopped("mysql", fun, sycon, starter), owner)
|
|
518
|
+
|
|
519
|
+
def mysqlsafe(fname="_tmp", user="root"):
|
|
520
|
+
# output("mysqld_safe --skip-grant-tables &", loud=True)
|
|
521
|
+
cmd("mysqld --skip-grant-tables --skip-networking -u %s &"%(user,))
|
|
522
|
+
time.sleep(0.5)
|
|
523
|
+
o = output("mysql < %s"%(fname,), loud=True)
|
|
524
|
+
cmd("killall mysqld")
|
|
525
|
+
return o
|
|
526
|
+
|
|
527
|
+
def mysqlreset(user="root", hostname="localhost", password=None):
|
|
528
|
+
password = password or input("new password for '%s' user? "%(user,))
|
|
529
|
+
mysqltmp(MYSQL_ALTERP%(user, hostname, password),
|
|
530
|
+
lambda : cmd("mysqld -init-file=_tmp"))
|
|
531
|
+
|
|
532
|
+
def mysqlresetnp(user="root", hostname="localhost", password=None):
|
|
533
|
+
password = password or input("new password for '%s' user? "%(user,))
|
|
534
|
+
mysqltmp(MYSQL_RESET%(user, hostname, password), mysqlsafe)
|
|
535
|
+
|
|
536
|
+
def mysqluser(user="root", hostname="localhost", password=None):
|
|
537
|
+
log("creating mysql user: %s@%s"%(user, hostname), important=True)
|
|
538
|
+
password = password or input("new password for '%s' user? "%(user,))
|
|
539
|
+
mysqltmp(MYSQL_CREATE_USER%(user, hostname,
|
|
540
|
+
password, user, hostname), mysqlsafe)
|
|
541
|
+
|
|
542
|
+
def mysqlexists(user="root", hostname="localhost", ensure=False):
|
|
543
|
+
log("checking for user '%s' at hostname '%s'"%(user, hostname), important=True)
|
|
544
|
+
for uline in mysqlsafe().split("\n"):
|
|
545
|
+
u, h = uline.split("\t")
|
|
546
|
+
if u == user and h == hostname:
|
|
547
|
+
log("user found!", important=True)
|
|
548
|
+
return True
|
|
549
|
+
log("user not found :(", important=True)
|
|
550
|
+
if ensure == "ask":
|
|
551
|
+
ensure = confirm("create %s@%s mysql user"%(user, hostname), True)
|
|
552
|
+
ensure and mysqluser(user, hostname)
|
|
553
|
+
|
|
554
|
+
def mysqlcheck(user="root", hostname="localhost", ensure=False):
|
|
555
|
+
mysqltmp(MYSQL_USERS, lambda : mysqlexists(user, hostname, ensure))
|
|
556
|
+
|
|
557
|
+
# ccbill stuff...
|
|
558
|
+
|
|
559
|
+
def _getmems(fname, simple=True, count=False, xls=False, tsv=False):
|
|
560
|
+
from cantools.util.data import getcsv, gettsv, getxls
|
|
561
|
+
members = {}
|
|
562
|
+
if xls:
|
|
563
|
+
rows = getxls(read(fname))
|
|
564
|
+
elif tsv:
|
|
565
|
+
rows = gettsv(read(fname))
|
|
566
|
+
else:
|
|
567
|
+
rows = getcsv(fname)
|
|
568
|
+
for row in rows:
|
|
569
|
+
if simple:
|
|
570
|
+
members[row[14].lower()] = row[3]
|
|
571
|
+
else: # diff (obsolete?) report format...
|
|
572
|
+
members[row[8]] = count or {
|
|
573
|
+
"date": row[13],
|
|
574
|
+
"status": row[15],
|
|
575
|
+
"initial_price": row[16],
|
|
576
|
+
"initial_period": row[17],
|
|
577
|
+
"recurring_price": row[18],
|
|
578
|
+
"recurring_period": row[19],
|
|
579
|
+
"rebills": row[20]
|
|
580
|
+
}
|
|
581
|
+
if count:
|
|
582
|
+
return len(list(members.keys()))
|
|
583
|
+
return members
|
|
584
|
+
|
|
585
|
+
def getCCmems(fname="active-members.csv", simple=True, count=False):
|
|
586
|
+
try:
|
|
587
|
+
mems = _getmems(fname, simple, count)
|
|
588
|
+
except:
|
|
589
|
+
log("getmems: csv read failed - trying tsv")
|
|
590
|
+
try:
|
|
591
|
+
mems = _getmems(fname, simple, count, tsv=True)
|
|
592
|
+
except:
|
|
593
|
+
log("getmems: tsv read failed - trying xls")
|
|
594
|
+
mems = _getmems(fname, simple, count, xls=True)
|
|
595
|
+
return mems
|
|
596
|
+
|
|
597
|
+
def ccbreport(fpath):
|
|
598
|
+
from cantools.web import fetch
|
|
599
|
+
from cantools.util import cp
|
|
600
|
+
from cantools import config
|
|
601
|
+
username = config.cache("datalink username? ")
|
|
602
|
+
password = config.cache("datalink password? ")
|
|
603
|
+
accnum = config.cache("datalink account number? ")
|
|
604
|
+
data = fetch("datalink.ccbill.com", "/data/main.cgi", timeout=10, protocol="https", qsp={
|
|
605
|
+
"clientSubacc": "0000",
|
|
606
|
+
"username": username,
|
|
607
|
+
"password": password,
|
|
608
|
+
"clientAccnum": accnum,
|
|
609
|
+
"transactionTypes": "ACTIVEMEMBERS"
|
|
610
|
+
}).decode()
|
|
611
|
+
cp(data, fpath)
|
|
612
|
+
|
|
613
|
+
def ccbmems():
|
|
614
|
+
fn = str(datetime.datetime.now()).split(" ").pop(0).replace("-", "") + ".csv"
|
|
615
|
+
fp = os.path.join("activemembers", fn)
|
|
616
|
+
log("checking for ccbill report at %s"%(fp,))
|
|
617
|
+
if not os.path.exists(fp):
|
|
618
|
+
log("file not present - acquiring from ccbill")
|
|
619
|
+
ccbreport(fp)
|
|
620
|
+
return getCCmems(fp)
|