Package gavo :: Package user :: Module admin
[frames] | no frames]

Source Code for Module gavo.user.admin

  1  """ 
  2  DC administration interface. 
  3  """ 
  4   
  5  #c Copyright 2008-2019, the GAVO project 
  6  #c 
  7  #c This program is free software, covered by the GNU GPL.  See the 
  8  #c COPYING file in the source distribution. 
  9   
 10   
 11  from __future__ import print_function 
 12   
 13  import os 
 14  import sys 
 15   
 16  from gavo import base 
 17  from gavo import rscdesc  #noflake: for cache registration 
 18  from gavo.base import sqlsupport 
 19  from gavo.protocols import uws 
 20  from gavo.utils import Arg, exposedFunction, makeCLIParser 
21 22 23 -class ArgError(base.Error):
24 pass
25 26 27 @exposedFunction([ 28 Arg("user", help="the user name"), 29 Arg("password", help="a password for the user"), 30 Arg("remarks", help="optional remarks", 31 default="", nargs='?')], 32 help="add a user/password pair and a matching group to the DC server")
33 -def adduser(querier, args):
34 try: 35 querier.query("INSERT INTO dc.users (username, password, remarks)" 36 " VALUES (%(user)s, %(password)s, %(remarks)s)", args.__dict__) 37 except base.IntegrityError: 38 raise base.ui.logOldExc(ArgError("User %s already exists." 39 " Use 'changeuser' command to edit."%args.user)) 40 querier.query("INSERT INTO dc.groups (username, groupname)" 41 " VALUES (%(user)s, %(user)s)", args.__dict__)
42 43 44 @exposedFunction([ 45 Arg("user", help="the user name to remove")], 46 help="remove a user from the DC server")
47 -def deluser(querier, args):
48 cursor = querier.connection.cursor() 49 cursor.execute("DELETE FROM dc.users WHERE username=%(user)s", 50 args.__dict__) 51 rowsAffected = cursor.rowcount 52 cursor.execute("DELETE FROM dc.groups WHERE username=%(user)s", 53 args.__dict__) 54 rowsAffected += cursor.rowcount 55 cursor.close() 56 if not rowsAffected: 57 sys.stderr.write("Warning: No rows deleted while deleting user %s\n"% 58 args.user)
59 60 61 @exposedFunction([ 62 Arg("user", help="the user name"), 63 Arg("password", help="a password for the user"), 64 Arg("remarks", help="optional remarks", 65 default="", nargs='?')], 66 help="change remarks and/or password for a DC user")
67 -def changeuser(querier, args):
68 if args.remarks is None: 69 c = querier.query("UPDATE dc.users SET password=%(password)s" 70 " WHERE username=%(user)s", args.__dict__) 71 else: 72 c = querier.query("UPDATE dc.users SET password=%(password)s," 73 " remarks=%(remarks)s WHERE username=%(user)s", args.__dict__) 74 if not c.rowcount: 75 sys.stderr.write("Warning: No rows changed for user %s\n"%args.user)
76 77 78 @exposedFunction([ 79 Arg("user", help="a user name"), 80 Arg("group", help="the group to add the user to")], 81 help="add a user to a group")
82 -def addtogroup(querier, args):
83 try: 84 querier.query("INSERT INTO dc.groups (username, groupname)" 85 " VALUES (%(user)s, %(group)s)", args.__dict__) 86 except sqlsupport.IntegrityError: 87 raise base.ui.logOldExc(ArgError("User %s doesn't exist."%args.user))
88 89 90 @exposedFunction([ 91 Arg("user", help="a user name"), 92 Arg("group", help="the group to remove the user from")], 93 help="remove a user from a group")
94 -def delfromgroup(querier, args):
95 c = querier.query("DELETE FROM dc.groups WHERE groupname=%(group)s" 96 " and username=%(user)s", args.__dict__) 97 if not c.rowcount: 98 sys.stderr.write("Warning: No rows deleted while deleting user" 99 " %s from group %s\n"%(args.user, args.group))
100
101 102 @exposedFunction(help="list users known to the DC") 103 -def listusers(querier, args):
104 data = list(querier.query("SELECT username, groupname, remarks" 105 " FROM dc.users NATURAL JOIN dc.groups ORDER BY username")) 106 curUser = None 107 for user, group, remark in data: 108 if user!=curUser: 109 print("\n%s (%s) --"%(user, remark), end=' ') 110 curUser = user 111 print(group, end=' ') 112 print()
113 114 115 @exposedFunction([ 116 Arg("-f", help="also remove all jobs in ERROR and ABORTED states (only use" 117 " if you are sure what you are doing).", action="store_true", 118 dest="includeFailed"), 119 Arg("-p", help="also remove all jobs in PENDING states (only use" 120 " if you are sure what you are doing).", action="store_true", 121 dest="includeForgotten"), 122 Arg("--all", help="remove all jobs (this is extremely unfriendly." 123 " Don't use this on public UWSes)", action="store_true", 124 dest="includeAll"), 125 Arg("--nuke-completed", help="also remove COMPLETEd jobs (this is" 126 " unfriendly. Don't do this on public UWSes).", action="store_true", 127 dest="includeCompleted"),], 128 help="remove expired UWS jobs")
129 -def cleantap(querier, args):
130 from gavo.protocols import tap 131 tap.WORKER_SYSTEM.cleanupJobsTable(includeFailed=args.includeFailed, 132 includeCompleted=args.includeCompleted, 133 includeAll=args.includeAll, 134 includeForgotten=args.includeForgotten)
135 136 137 @exposedFunction([ 138 Arg("jobId", help="id of the job to abort"), 139 Arg("helpMsg", help="A helpful message to add to the abort message")], 140 help="manually abort a TAP job and send some message to a user")
141 -def tapabort(querier, args):
142 from gavo.protocols import tap 143 144 tap.WORKER_SYSTEM.changeToPhase(args.jobId, uws.ERROR, 145 "Job aborted by an administrator, probably because the query\n" 146 " should be written differently to be less of a resource hog.\n" 147 " Here's what the administrator had to say:\n\n"+args.helpMsg+ 148 "\n\nIf you have further questions, just send a mail to "+ 149 base.getMetaText(base.caches.getRD("//tap").getById("run"), 150 "contact.email"))
151 152 153 @exposedFunction([Arg(help="identifier of the deleted service", 154 dest="svcId")], 155 help="Declare an identifier as deleted (for when" 156 " you've removed the RD but the identifier still floats on" 157 " some registries)")
158 -def declaredel(querier, args):
159 import datetime 160 161 from gavo import registry 162 from gavo import rsc 163 164 authority, path = registry.parseIdentifier(args.svcId) 165 if authority!=base.getConfig("ivoa", "authority"): 166 raise base.ReportableError("You can only declare ivo ids from your" 167 " own authority as deleted.") 168 idParts = path.split("/") 169 svcsRD = base.caches.getRD("//services") 170 171 # mark in resources table 172 resTable = rsc.TableForDef(svcsRD.getById("resources"), 173 connection=querier.connection) 174 newRow = resTable.tableDef.getDefaults() 175 newRow["sourceRD"] = "/".join(idParts[:-1]) 176 newRow["resId"] = idParts[-1] 177 newRow["deleted"] = True 178 newRow["title"] = "Ex "+args.svcId 179 newRow["dateUpdated"] = newRow["recTimestamp"] = datetime.datetime.utcnow() 180 resTable.addRow(newRow) 181 182 # mark in sets table 183 resTable = rsc.TableForDef(svcsRD.getById("sets"), 184 connection=querier.connection) 185 newRow = resTable.tableDef.getDefaults() 186 newRow["sourceRD"] = "/".join(idParts[:-1]) 187 newRow["renderer"] = "null" 188 newRow["resId"] = idParts[-1] 189 newRow["setName"] = "ivo_managed" 190 newRow["deleted"] = True 191 resTable.addRow(newRow)
192 193 194 @exposedFunction([Arg(help="rd#table-id of the table containing the" 195 " products that should get cached previews", dest="tableId"), 196 Arg("-w", type=str, 197 help="width to compute the preview for", dest="width", default="200"),], 198 help="Precompute previews for the product interface columns in a table.")
199 -def cacheprev(querier, args):
200 from gavo.web.productrender import PreviewCacheManager 201 from twisted.internet import reactor 202 203 basePath = base.getConfig("inputsDir") 204 td = base.resolveId(None, args.tableId) 205 rows = querier.queryToDicts( 206 td.getSimpleQuery(["accref", "mime"])) 207 208 def runNext(token): 209 try: 210 row = rows.next() 211 res = PreviewCacheManager.getPreviewFor(row["mime"], 212 [str(os.path.join(basePath, row["accref"])), str(args.width)] 213 ) 214 215 if getattr(res, "result", None): # don't add a callback on a 216 # fired deferred or you'll exhaust the stack 217 reactor.callLater(0.1, runNext, "continue") 218 else: 219 res.addCallback(runNext) 220 return res 221 except StopIteration: 222 pass 223 except: 224 import traceback 225 traceback.print_exc() 226 reactor.stop() 227 return ""
228 229 reactor.callLater(0, runNext, "startup") 230 reactor.run() 231 232 233 @exposedFunction([Arg(help="rd#table-id of the table to look at", 234 dest="tableId")], 235 help="Make suggestions for UCDs of columns not having one (based" 236 " on their descriptions; this uses a GAVO web service).")
237 -def suggestucds(querier, args):
238 import SOAPpy 239 import urllib 240 from gavo import api 241 242 wsdlURL = "http://dc.zah.uni-heidelberg.de/ucds/ui/ui/soap/go/go?wsdl" 243 proxy = SOAPpy.WSDL.Proxy(urllib.urlopen(wsdlURL).read()) 244 td = api.getReferencedElement(args.tableId, forceType=api.TableDef) 245 for col in td: 246 if (not col.ucd or col.ucd=="XXX") and col.description: 247 try: 248 res = [(row["score"], row["ucd"]) 249 for row in proxy.useService(col.description)] 250 res.sort() 251 res.reverse() 252 print(col.name) 253 for score, ucd in res: 254 print(" ", ucd) 255 except SOAPpy.Types.faultType: 256 # remote failure, guess it's "no matches" (TODO: distinguish) 257 pass
258 259 260 @exposedFunction([Arg(help="rd#table-id of the table of interest", 261 dest="tableId")], 262 help="Show the statements to create the indices on a table.")
263 -def indexStatements(querier, args):
264 import re 265 td = base.resolveId(None, args.tableId) 266 for ind in td.indices: 267 print("\n".join(re.sub(r"\s+", " ", s) for s in ind.iterCode()))
268 269 270 @exposedFunction([Arg(help="rd#exec-id of the execute element to run (note:" 271 " the title won't work, you have to give the thing an id to use adm exec).", 272 dest="execId")], 273 help="Execute the contents of an RD execute element. You must" 274 " give that element an explicit id in order to make this work.")
275 -def execute(querier, args):
276 from gavo.user import logui 277 logui.LoggingUI(base.ui) 278 execEl = base.resolveCrossId(args.execId) 279 execEl.callable().join()
280 281 282 @exposedFunction([Arg(help="Package resource path" 283 " (like '/inputs/__system__/scs.rd); for system RDs, the special" 284 " //rd-id syntax is supported.", 285 dest="path")], 286 help="Dump the source of a distribution file; this is useful when you want" 287 " to override them and you are running DaCHS from a zipped egg")
288 -def dumpDF(querier, args):
289 import pkg_resources 290 if args.path.startswith("//"): 291 args.path = "inputs/__system__"+args.path[1:]+".rd" 292 with pkg_resources.resource_stream('gavo', "resources/"+args.path) as f: 293 sys.stdout.write(f.read())
294 295 296 @exposedFunction([Arg(help="XML file", dest="path")], 297 help="Validate a file against built-in VO schemas and with built-in" 298 " schema validator.")
299 -def xsdValidate(querier, args):
300 from gavo.helpers import testtricks 301 msgs = testtricks.getXSDErrors(open(args.path).read()) 302 if not msgs: 303 print("-- valid") 304 else: 305 print(msgs)
306 307 308 @exposedFunction([Arg(help="IVOID to mark as deleted", dest="ivoid")], 309 help="Add a registry entry for a deleted record with IVOID. You should" 310 " not usually have to do this, except when you added an identifier" 311 " meta item to a service that was already published without also" 312 " changing its XML id.")
313 -def makeDeletedRecord(querier, args):
314 from gavo.registry import publication 315 publication.makeDeletedRecord(args.ivoid, querier.connection)
316 317 318 @exposedFunction([], help="Update the TAP_SCHEMA metadata for all" 319 " RDs mentioned in TAP_SCHEMA.")
320 -def updateTAPSchema(querier, args):
321 from gavo.protocols import tap 322 for rdId, in querier.query("select sourcerd from TAP_SCHEMA.tables"): 323 rd = base.caches.getRD(rdId) 324 tap.publishToTAP(rd, querier.connection)
325
326 327 -def main():
328 with base.AdhocQuerier(base.getWritableAdminConn) as querier: 329 args = makeCLIParser(globals()).parse_args() 330 args.subAction(querier, args)
331