Package omero :: Package plugins :: Module admin
[hide private]
[frames] | no frames]

Source Code for Module omero.plugins.admin

   1  #!/usr/bin/env python 
   2  # -*- coding: utf-8 -*- 
   3  """ 
   4   :author: Josh Moore, josh at glencoesoftware.com 
   5   
   6   OMERO Grid admin controller 
   7   
   8   This is a python wrapper around icegridregistry/icegridnode for master 
   9   and various other tools needed for administration. 
  10   
  11   Copyright 2008 Glencoe Software, Inc.  All Rights Reserved. 
  12   Use is subject to license terms supplied in LICENSE.txt 
  13   
  14  """ 
  15   
  16  import re 
  17  import os 
  18  import sys 
  19  import stat 
  20  import platform 
  21  import portalocker 
  22   
  23  from path import path 
  24   
  25  import omero 
  26  import omero.config 
  27   
  28  from omero.cli import CLI 
  29  from omero.cli import BaseControl 
  30  from omero.cli import DirectoryType 
  31  from omero.cli import NonZeroReturnCode 
  32  from omero.cli import VERSION 
  33   
  34  from omero.plugins.prefs import with_config 
  35   
  36  from omero_ext.which import whichall 
  37  from omero_version import ice_compatibility 
  38   
  39  try: 
  40      import pywintypes 
  41      import win32service 
  42      import win32evtlogutil 
  43      import win32api 
  44      import win32security 
  45      has_win32 = True 
  46  except ImportError: 
  47      has_win32 = False 
  48   
  49  DEFAULT_WAIT = 300 
  50   
  51  HELP = """Administrative tools including starting/stopping OMERO. 
  52   
  53  Environment variables: 
  54   OMERO_MASTER 
  55   OMERO_NODE 
  56   
  57  Configuration properties: 
  58   omero.windows.user 
  59   omero.windows.pass 
  60   
  61  """ + "\n" + "="*50 + "\n" 
62 63 64 -class AdminControl(BaseControl):
65
66 - def _complete(self, text, line, begidx, endidx):
67 """ 68 Returns a file after "deploy", "start", or "startasync" 69 and otherwise delegates to the BaseControl 70 """ 71 for s in (" deploy ", " start ", " startasync "): 72 l = len(s) 73 i = line.find(s) 74 if i >= 0: 75 f = line[i+l:] 76 return self._complete_file(f) 77 return BaseControl._complete(self, text, line, begidx, endidx)
78
79 - def _configure(self, parser):
80 sub = parser.sub() 81 self.actions = {} 82 83 class Action(object): 84 def __init__(this, name, help, wait=False): 85 this.parser = sub.add_parser(name, help=help, 86 description=help) 87 this.parser.set_defaults(func=getattr(self, name)) 88 self.actions[name] = this.parser 89 if wait: 90 this.parser.add_argument( 91 "--wait", type=float, default=DEFAULT_WAIT, 92 help="Seconds to wait for operation")
93 94 Action( 95 "start", 96 """Start icegridnode daemon and waits for required components to \ 97 come up, i.e. status == 0 98 99 If the first argument can be found as a file, it will be deployed as the 100 application descriptor rather than etc/grid/default.xml. All other arguments 101 will be used as targets to enable optional sections of the descriptor""", 102 wait=True) 103 104 Action("startasync", "The same as start but returns immediately",) 105 106 Action("restart", "stop && start", wait=True) 107 108 Action( 109 "restartasync", """The same as restart but returns as soon as \ 110 starting has begun.""", 111 wait=True) 112 113 Action("status", """Status of server 114 115 Returns with 0 status if a node ping is successful and if some SessionManager 116 returns an OMERO-specific exception on a bad login. This can be used in shell 117 scripts, e.g.: 118 119 $ omero admin status && echo "server started" 120 """) 121 122 Action( 123 "stop", 124 """Initiates node shutdown and waits for status to return a \ 125 non-0 value""", 126 wait=True) 127 128 Action("stopasync", "The same as stop but returns immediately") 129 130 Action( 131 "deploy", 132 """Deploy the given deployment descriptor. See etc/grid/*.xml 133 134 If the first argument is not a file path, etc/grid/default.xml will be 135 deployed by default. Same functionality as start, but requires that the node 136 already be running. This may automatically restart some server components.""") 137 138 Action( 139 "ice", "Drop user into icegridadmin console or execute arguments") 140 141 fixpyramids = Action( 142 "fixpyramids", "Remove empty pyramid pixels files").parser 143 # See cleanse options below 144 145 Action( 146 "diagnostics", 147 "Run a set of checks on the current, preferably active server") 148 149 Action( 150 "waitup", 151 "Used by start after calling startasync to wait on status==0", 152 wait=True) 153 154 Action( 155 "waitdown", 156 "Used by stop after calling stopasync to wait on status!=0", 157 wait=True) 158 159 reindex = Action( 160 "reindex", 161 """Re-index the Lucene index 162 163 Command-line tool for re-index the database. This command must be run on the 164 machine where /OMERO/FullText is located. 165 166 Examples: 167 bin/omero admin reindex --full \ 168 # All objects 169 bin/omero admin reindex --class ome.model.core.Image \ 170 # Only images 171 JAVA_OPTS="-Dlog4j.configuration=stderr.xml" bin/omero admin reindex --full\ 172 # Passing arguments to Java 173 174 175 LIMITATION: omero.db.pass values do not currently get passed to the Java 176 process. You will need to all passwordless login to PostgreSQL. In 177 fact, only the following properties are passed: 178 179 omero.data.dir 180 omero.search.* 181 omero.db.* (excluding pass) 182 183 """).parser 184 reindex.add_argument( 185 "--jdwp", help="Activate remote debugging") 186 group = reindex.add_mutually_exclusive_group() 187 group.add_argument( 188 "--full", action="store_true", 189 help="Reindexes all non-excluded tables sequentially") 190 group.add_argument( 191 "--events", action="store_true", 192 help="Reindexes all non-excluded event logs chronologically") 193 group.add_argument( 194 "--class", nargs="+", 195 help="Reindexes the given classes sequentially") 196 197 ports = Action( 198 "ports", 199 """Allows modifying the ports from a standard OMERO install 200 201 To have two OMERO's running on the same machine, several ports must be 202 modified from their default values. 203 Internally, this command uses the omero.install.change_ports module. 204 Changing the ports on a running server is usually not what you want and 205 will be prevented. Use --skipcheck to change the ports anyway. 206 207 Examples: 208 209 %(prog)s --prefix=1 # sets ports to: 14061, \ 210 14063, 14064 211 %(prog)s --prefix=1 --revert # sets ports back to: \ 212 4061, 4063, 4064 213 %(prog)s --registry=4444 --tcp=5555 --ssl=6666 # sets ports to: 4444 \ 214 5555 6666 215 216 """).parser 217 ports.add_argument( 218 "--prefix", 219 help="Adds a prefix to each port ON TOP OF any other settings") 220 ports.add_argument( 221 "--registry", default="4061", 222 help="Registry port. (default: %(default)s)") 223 ports.add_argument( 224 "--tcp", default="4063", 225 help="The tcp port to be used by Glacier2 (default: %(default)s)") 226 ports.add_argument( 227 "--ssl", default="4064", 228 help="The ssl port to be used by Glacier2 (default: %(default)s)") 229 ports.add_argument( 230 "--revert", action="store_true", 231 help="Used to rollback from the given settings to the defaults") 232 ports.add_argument( 233 "--skipcheck", action="store_true", 234 help="Skips the check if the server is already running") 235 236 sessionlist = Action( 237 "sessionlist", "List currently running sessions").parser 238 sessionlist.add_login_arguments() 239 240 cleanse = Action("cleanse", """Remove binary data files from OMERO 241 242 Deleting an object from OMERO currently does not remove the binary data. Use 243 this command either manually or in a cron job periodically to remove Pixels 244 and other data. 245 246 This is done by checking that for all the files in the given directory, a 247 matching entry exists on the server. THE /OMERO DIRECTORY MUST MATCH THE 248 DATABASE YOU ARE RUNNING AGAINST. 249 250 This command must be run on the machine where, for example, /OMERO/ is 251 located. 252 253 Examples: 254 bin/omero admin cleanse --dry-run /OMERO # Lists files that will be \ 255 deleted 256 bin/omero admin cleanse /OMERO # Actually delete them. 257 bin/omero admin cleanse /volumes/data/OMERO # Delete from a standard \ 258 location. 259 260 """).parser 261 262 for x in (cleanse, fixpyramids): 263 x.add_argument( 264 "--dry-run", action="store_true", 265 help="Print out which files would be deleted") 266 x.add_argument( 267 "data_dir", type=DirectoryType(), 268 help="omero.data.dir directory value (e.g. /OMERO") 269 x.add_login_arguments() 270 271 Action("checkwindows", "Run simple check of the local installation " 272 "(Windows-only)") 273 Action("checkice", "Run simple check of the Ice installation") 274 275 Action("events", "Print event log (Windows-only)") 276 277 self.actions["ice"].add_argument( 278 "argument", nargs="*", 279 help="""Arguments joined together to make an Ice command. If not \ 280 present, the user will enter a console""") 281 282 self.actions["status"].add_argument( 283 "node", nargs="?", default="master") 284 self.actions["status"].add_argument( 285 "--nodeonly", action="store_true", 286 help="If set, then only tests if the icegridnode is running") 287 288 for name in ("start", "startasync"): 289 self.actions[name].add_argument( 290 "-u", "--user", 291 help="Service Log On As user name. If none given, the value" 292 " of omero.windows.user will be used. (Windows-only)") 293 self.actions[name].add_argument( 294 "-w", "--password", 295 help="Service Log On As user password. If none given, the" 296 " value of omero.windows.pass will be used. (Windows-only)") 297 298 for k in ("start", "startasync", "deploy", "restart", "restartasync"): 299 self.actions[k].add_argument( 300 "file", nargs="?", 301 help="Application descriptor. If not provided, a default" 302 " will be used") 303 self.actions[k].add_argument( 304 "targets", nargs="*", 305 help="Targets within the application descriptor which " 306 " should be activated. Common values are: \"debug\", " 307 "\"trace\" ") 308 309 # DISABLED = """ see: http://www.zeroc.com/forums/bug-reports/\ 310 # 4237-sporadic-freeze-errors-concurrent-icegridnode-access.html 311 # restart [filename] [targets] : Calls stop followed by start \ 312 # args 313 # restartasync [filename] [targets] : Calls stop followed by \ 314 # startasync args 315 # """ 316 317 # 318 # Windows utility methods 319 # 320 if has_win32:
321 - def _query_service(unused, svc_name):
322 hscm = win32service.OpenSCManager( 323 None, None, win32service.SC_MANAGER_ALL_ACCESS) 324 try: 325 try: 326 hs = win32service.OpenService( 327 hscm, svc_name, win32service.SERVICE_ALL_ACCESS) 328 except: 329 return "DOESNOTEXIST" 330 try: 331 q = win32service.QueryServiceStatus(hs) 332 type, state, ctrl, err, svcerr, svccp, svcwh = q 333 if state == win32service.SERVICE_STOPPED: 334 return "STOPPED" 335 else: 336 return "unknown" 337 finally: 338 win32service.CloseServiceHandle(hs) 339 finally: 340 win32service.CloseServiceHandle(hscm)
341
342 - def events(self, svc_name):
343 def DumpRecord(record): 344 if str(record.SourceName) == svc_name: 345 self.ctx.out("Time: %s" % record.TimeWritten) 346 self.ctx.out("Rec: %s" % record.RecordNumber) 347 for si in record.StringInserts: 348 self.ctx.out(si) 349 self.ctx.out("="*20)
350 win32evtlogutil.FeedEventLogRecords(DumpRecord) 351 352 else: 353
354 - def events(self, svc_name):
355 self.ctx.die( 356 666, "Could not import win32service and/or win32evtlogutil")
357
358 - def _query_service(self, svc_name):
359 self.ctx.die( 360 666, "Could not import win32service and/or win32evtlogutil")
361 362 # 363 # End Windows Methods 364 # 365
366 - def _node(self, omero_node=None):
367 """ 368 Overrides the regular node() logic to return the value of 369 OMERO_MASTER or "master" 370 """ 371 if omero_node is not None: 372 os.environ["OMERO_MASTER"] = omero_node 373 374 if "OMERO_MASTER" in os.environ: 375 return os.environ["OMERO_MASTER"] 376 else: 377 return "master"
378
379 - def _cmd(self, *command_arguments):
380 """ 381 Used to generate an icegridadmin command line argument list 382 """ 383 command = ["icegridadmin", self._intcfg()] 384 command.extend(command_arguments) 385 return command
386
387 - def _descript(self, args):
388 if args.file is not None: 389 # Relative to cwd 390 descript = path(args.file).abspath() 391 if not descript.exists(): 392 self.ctx.dbg("No such file: %s -- Using as target" % descript) 393 args.targets.insert(0, args.file) 394 descript = None 395 else: 396 descript = None 397 398 if descript is None: 399 __d__ = "default.xml" 400 if self._isWindows(): 401 __d__ = "windefault.xml" 402 descript = self.ctx.dir / "etc" / "grid" / __d__ 403 self.ctx.err("No descriptor given. Using %s" 404 % os.path.sep.join(["etc", "grid", __d__])) 405 return descript
406
407 - def checkwindows(self, args):
408 """ 409 Checks that the templates file as defined in etc\Windows.cfg 410 can be found. 411 """ 412 self.check_access(os.R_OK) 413 if not self._isWindows(): 414 self.ctx.die(123, "Not Windows") 415 416 import Ice 417 key = "IceGrid.Node.Data" 418 properties = Ice.createProperties([self._icecfg()]) 419 nodedata = properties.getProperty(key) 420 if not nodedata: 421 self.ctx.die(300, 422 "Bad configuration: No IceGrid.Node.Data property") 423 nodepath = path(nodedata) 424 pp = nodepath.parpath(self.ctx.dir) 425 if pp: 426 return 427 if nodepath == r"c:\omero_dist\var\master": 428 self.ctx.out("Found default value: %s" % nodepath) 429 self.ctx.out("Attempting to correct...") 430 from omero.install.win_set_path import win_set_path 431 count = win_set_path(dir=self.ctx.dir) 432 if count: 433 return 434 self.ctx.die(400, """ 435 436 %s is not in this directory. Aborting... 437 438 Please see the installation instructions on modifying 439 the files for your installation (%s) 440 with bin\winconfig.bat 441 442 """ % (nodedata, self.ctx.dir))
443 444 ############################################## 445 # 446 # Commands 447 # 448 449 @with_config
450 - def startasync(self, args, config):
451 """ 452 First checks for a valid installation, then checks the grid, 453 then registers the action: "node HOST start" 454 """ 455 456 self.check_access(config=config) 457 self.checkice() 458 self.check_node(args) 459 if self._isWindows(): 460 self.checkwindows(args) 461 462 if 0 == self.status(args, node_only=True): 463 self.ctx.die(876, "Server already running") 464 465 self._initDir() 466 # Do a check to see if we've started before. 467 self._regdata() 468 self.check([]) 469 470 user = args.user 471 pasw = args.password 472 descript = self._descript(args) 473 474 if self._isWindows(): 475 svc_name = "OMERO.%s" % args.node 476 output = self._query_service(svc_name) 477 478 # Now check if the server exists 479 if 0 <= output.find("DOESNOTEXIST"): 480 binpath = """icegridnode.exe "%s" --deploy "%s" --service\ 481 %s""" % (self._icecfg(), descript, svc_name) 482 483 # By default: "NT Authority\Local System" 484 if not user: 485 try: 486 user = config.as_map()["omero.windows.user"] 487 except KeyError: 488 user = None 489 if user is not None and len(user) > 0: 490 if "\\" not in user: 491 computername = win32api.GetComputerName() 492 user = "\\".join([computername, user]) 493 try: 494 # See #9967, code based on http://mail.python.org/\ 495 # pipermail/python-win32/2010-October/010791.html 496 self.ctx.out("Granting SeServiceLogonRight to service" 497 " user \"%s\"" % user) 498 policy_handle = win32security.LsaOpenPolicy( 499 None, win32security.POLICY_ALL_ACCESS) 500 sid_obj, domain, tmp = \ 501 win32security.LookupAccountName(None, user) 502 win32security.LsaAddAccountRights( 503 policy_handle, sid_obj, ('SeServiceLogonRight',)) 504 win32security.LsaClose(policy_handle) 505 except pywintypes.error, details: 506 self.ctx.die(200, "Error during service user set up:" 507 " (%s) %s" % (details[0], details[2])) 508 if not pasw: 509 try: 510 pasw = config.as_map()["omero.windows.pass"] 511 except KeyError: 512 pasw = self._ask_for_password( 513 " for service user \"%s\"" % user) 514 else: 515 pasw = None 516 517 hscm = win32service.OpenSCManager( 518 None, None, win32service.SC_MANAGER_ALL_ACCESS) 519 try: 520 self.ctx.out("Installing %s Windows service." % svc_name) 521 hs = win32service.CreateService( 522 hscm, svc_name, svc_name, 523 win32service.SERVICE_ALL_ACCESS, 524 win32service.SERVICE_WIN32_OWN_PROCESS, 525 win32service.SERVICE_AUTO_START, 526 win32service.SERVICE_ERROR_NORMAL, binpath, None, 0, 527 None, user, pasw) 528 self.ctx.out("Successfully installed %s Windows service." 529 % svc_name) 530 win32service.CloseServiceHandle(hs) 531 finally: 532 win32service.CloseServiceHandle(hscm) 533 534 # Then check if the server is already running 535 if 0 <= output.find("RUNNING"): 536 self.ctx.die(201, "%s is already running. Use stop first" 537 % svc_name) 538 539 # Finally, try to start the service - delete if startup fails 540 hscm = win32service.OpenSCManager( 541 None, None, win32service.SC_MANAGER_ALL_ACCESS) 542 try: 543 try: 544 hs = win32service.OpenService( 545 hscm, svc_name, win32service.SC_MANAGER_ALL_ACCESS) 546 win32service.StartService(hs, None) 547 self.ctx.out("Starting %s Windows service." % svc_name) 548 except pywintypes.error, details: 549 self.ctx.out("%s service startup failed: (%s) %s" 550 % (svc_name, details[0], details[2])) 551 win32service.DeleteService(hs) 552 self.ctx.die(202, "%s service deleted." % svc_name) 553 finally: 554 win32service.CloseServiceHandle(hs) 555 win32service.CloseServiceHandle(hscm) 556 557 else: 558 command = [ 559 "icegridnode", "--daemon", "--pidfile", str(self._pid()), 560 "--nochdir", self._icecfg(), "--deploy", str(descript) 561 ] + args.targets 562 self.ctx.rv = self.ctx.call(command)
563 564 @with_config
565 - def start(self, args, config):
566 self.startasync(args, config) 567 try: 568 self.waitup(args) 569 except NonZeroReturnCode, nzrc: 570 # stop() may itself throw, 571 # if it does not, then we rethrow 572 # the original 573 self.ctx.err('Calling "stop" on remaining components') 574 self.stop(args, config) 575 raise nzrc
576 577 @with_config
578 - def deploy(self, args, config):
579 self.check_access() 580 self.checkice() 581 descript = self._descript(args) 582 583 # TODO : Doesn't properly handle whitespace 584 # Though users can workaround with something like: 585 # bin/omero admin deploy etc/grid/a\\\\ b.xml 586 command = ["icegridadmin", self._intcfg(), "-e", 587 " ".join(["application", "update", str(descript)] + 588 args.targets)] 589 self.ctx.call(command)
590
591 - def status(self, args, node_only=False):
592 self.check_node(args) 593 command = self._cmd("-e", "node ping %s" % self._node()) 594 self.ctx.rv = self.ctx.popen(command).wait() # popen 595 596 # node_only implies that "up" need not check for all 597 # of blitz to be accessible but just that if the node 598 # is running. 599 if not node_only: 600 node_only = getattr(args, "nodeonly", False) 601 602 if self.ctx.rv == 0 and not node_only: 603 try: 604 import Ice 605 ic = Ice.initialize([self._intcfg()]) 606 try: 607 sm = self.session_manager(ic) 608 try: 609 sm.create("####### STATUS CHECK ########", None) 610 # Not adding "omero.client.uuid" 611 except omero.WrappedCreateSessionException: 612 # Only the server will throw one of these 613 self.ctx.dbg("Server reachable") 614 self.ctx.rv = 0 615 finally: 616 ic.destroy() 617 except Exception, exc: 618 self.ctx.rv = 1 619 self.ctx.dbg("Server not reachable: "+str(exc)) 620 621 return self.ctx.rv
622
623 - def wait_for_icedb(self, args, config):
624 """ 625 Since the stop and start are separately protected by 626 the lock on config.xml, we need to wait for some time 627 to hopefully let the icegridnode process release the 628 file locks. 629 """ 630 self.ctx.sleep(1) # put in sleep to try to prevent "db locked" (#7325)
631 632 @with_config
633 - def restart(self, args, config):
634 if not self.stop(args, config): 635 self.ctx.die(54, "Failed to shutdown") 636 self.wait_for_icedb(args, config) 637 self.start(args, config)
638 639 @with_config
640 - def restartasync(self, args, config):
641 if not self.stop(args, config): 642 self.ctx.die(54, "Failed to shutdown") 643 self.wait_for_icedb(args, config) 644 self.startasync(args, config)
645
646 - def waitup(self, args):
647 """ 648 Loops 30 times with 10 second pauses waiting for status() 649 to return 0. If it does not, then ctx.die() is called. 650 """ 651 self.check_access(os.R_OK) 652 self.ctx.out("Waiting on startup. Use CTRL-C to exit") 653 count, loop_secs, time_msg = self.loops_and_wait(args) 654 while True: 655 count = count - 1 656 if count == 0: 657 self.ctx.die(43, "\nFailed to startup some components after" 658 " %s" % time_msg) 659 elif 0 == self.status(args, node_only=False): 660 break 661 else: 662 self.ctx.out(".", newline=False) 663 self.ctx.sleep(loop_secs)
664
665 - def waitdown(self, args):
666 """ 667 Returns true if the server went down 668 """ 669 self.check_access(os.R_OK) 670 self.ctx.out("Waiting on shutdown. Use CTRL-C to exit") 671 count, loop_secs, time_msg = self.loops_and_wait(args) 672 while True: 673 count = count - 1 674 if count == 0: 675 self.ctx.die(44, "\nFailed to shutdown some components after" 676 " %s" % time_msg) 677 return False 678 elif 0 != self.status(args, node_only=True): 679 break 680 else: 681 self.ctx.out(".", newline=False) 682 self.ctx.sleep(loop_secs) 683 self.ctx.rv = 0 684 return True
685
686 - def loops_and_wait(self, args):
687 """ 688 If present, get the wait time from the args argument 689 and calculate the number of loops and the wait time 690 needed. If not present in args, use a default value. 691 """ 692 693 if not hasattr(args, "wait"): 694 # This might happen if a new command starts using 695 # waitup/waitdown without setting wait=True for 696 # Action() 697 args.wait = DEFAULT_WAIT 698 699 total_secs = args.wait 700 loop_secs = total_secs / 30.0 701 return 30, loop_secs, "%s seconds" % total_secs
702 703 @with_config
704 - def stopasync(self, args, config):
705 """ 706 Returns true if the server was already stopped 707 """ 708 self.check_node(args) 709 if 0 != self.status(args, node_only=True): 710 self.ctx.err("Server not running") 711 return True 712 elif self._isWindows(): 713 svc_name = "OMERO.%s" % args.node 714 output = self._query_service(svc_name) 715 if 0 <= output.find("DOESNOTEXIST"): 716 self.ctx.die(203, "%s does not exist. Use 'start' first." 717 % svc_name) 718 hscm = win32service.OpenSCManager( 719 None, None, win32service.SC_MANAGER_ALL_ACCESS) 720 try: 721 hs = win32service.OpenService( 722 hscm, svc_name, win32service.SC_MANAGER_ALL_ACCESS) 723 win32service.ControlService( 724 hs, win32service.SERVICE_CONTROL_STOP) 725 win32service.DeleteService(hs) 726 self.ctx.out("%s service deleted." % svc_name) 727 finally: 728 win32service.CloseServiceHandle(hs) 729 win32service.CloseServiceHandle(hscm) 730 else: 731 command = self._cmd("-e", "node shutdown %s" % self._node()) 732 try: 733 self.ctx.call(command) 734 except NonZeroReturnCode, nzrc: 735 self.ctx.rv = nzrc.rv 736 self.ctx.out("Was the server already stopped?")
737 738 @with_config
739 - def stop(self, args, config):
740 if not self.stopasync(args, config): 741 return self.waitdown(args) 742 return True
743
744 - def check(self, args):
745 # print "Check db. Have a way to load the db control" 746 pass
747
748 - def ice(self, args):
749 self.check_access() 750 command = self._cmd() 751 if len(args.argument) > 0: 752 command.extend(["-e", " ".join(args.argument)]) 753 return self.ctx.call(command) 754 else: 755 self.ctx.call(command)
756 757 @with_config
758 - def fixpyramids(self, args, config):
759 self.check_access() 760 from omero.util.cleanse import fixpyramids 761 client = self.ctx.conn(args) 762 client.getSessionId() 763 fixpyramids(data_dir=args.data_dir, dry_run=args.dry_run, 764 query_service=client.sf.getQueryService(), 765 config_service=client.sf.getConfigService())
766 767 @with_config
768 - def diagnostics(self, args, config):
769 self.check_access() 770 config = config.as_map() 771 omero_data_dir = '/OMERO' 772 try: 773 omero_data_dir = config['omero.data.dir'] 774 except KeyError: 775 pass 776 777 from omero.util.temp_files import gettempdir 778 # gettempdir returns ~/omero/tmp/omero_%NAME/%PROCESS 779 # To find something more generally useful for calculating 780 # size, we go up two directories 781 omero_temp_dir = gettempdir() 782 omero_temp_dir = os.path.abspath( 783 os.path.join(omero_temp_dir, os.path.pardir, os.path.pardir)) 784 785 self.ctx.out(""" 786 %s 787 OMERO Diagnostics %s 788 %s 789 """ % ("="*80, VERSION, "="*80)) 790 791 def sz_str(sz): 792 for x in ["KB", "MB", "GB"]: 793 sz /= 1000 794 if sz < 1000: 795 break 796 sz = "%.1f %s" % (sz, x) 797 return sz
798 799 def item(cat, msg): 800 cat = cat + ":" 801 cat = "%-12s" % cat 802 self.ctx.out(cat, False) 803 msg = "%-30s " % msg 804 self.ctx.out(msg, False) 805 806 def exists(p): 807 if p.isdir(): 808 if not p.exists(): 809 self.ctx.out("doesn't exist") 810 else: 811 self.ctx.out("exists") 812 else: 813 if not p.exists(): 814 self.ctx.out("n/a") 815 else: 816 warn = 0 817 err = 0 818 for l in p.lines(): 819 # ensure errors/warnings search is case-insensitive 820 lcl = l.lower() 821 found_err = lcl.find("error") >= 0 822 found_warn = lcl.find("warn") >= 0 823 824 if found_err: 825 err += 1 826 elif found_warn: 827 warn += 1 828 msg = "" 829 if warn or err: 830 msg = " errors=%-4s warnings=%-4s" % (err, warn) 831 self.ctx.out("%-12s %s" % (sz_str(p.size), msg)) 832 833 def version(cmd): 834 """ 835 Returns a true response only 836 if a valid version was found. 837 """ 838 item("Commands", "%s" % " ".join(cmd)) 839 try: 840 p = self.ctx.popen(cmd) 841 except OSError: 842 self.ctx.err("not found") 843 return False 844 845 p.wait() 846 io = p.communicate() 847 try: 848 v = io[0].split() 849 v.extend(io[1].split()) 850 v = "".join(v) 851 m = re.match("^\D*(\d[.\d]+\d)\D?.*$", v) 852 v = "%-10s" % m.group(1) 853 self.ctx.out(v, False) 854 try: 855 where = whichall(cmd[0]) 856 sz = len(where) 857 if sz == 0: 858 where = "unknown" 859 else: 860 where = where[0] 861 if sz > 1: 862 where += " -- %s others" % sz 863 864 except: 865 where = "unknown" 866 self.ctx.out("(%s)" % where) 867 return True 868 except Exception, e: 869 self.ctx.err("error:%s" % e) 870 return False 871 872 import logging 873 logging.basicConfig() 874 from omero.util.upgrade_check import UpgradeCheck 875 check = UpgradeCheck("diagnostics") 876 check.run() 877 if check.isUpgradeNeeded(): 878 self.ctx.out("") 879 880 version(["java", "-version"]) 881 version(["python", "-V"]) 882 version(["icegridnode", "--version"]) 883 iga = version(["icegridadmin", "--version"]) 884 version(["psql", "--version"]) 885 886 def get_ports(input): 887 router_lines = [line for line in input.split("\n") 888 if line.find("ROUTER") >= 0] 889 890 ssl_port = None 891 tcp_port = None 892 for line in router_lines: 893 if not ssl_port and line.find("ROUTERPORT") >= 0: 894 m = re.match(".*?(\d+).*?$", line) 895 if m: 896 ssl_port = m.group(1) 897 898 if not tcp_port and line.find("INSECUREROUTER") >= 0: 899 m = re.match("^.*?-p (\d+).*?$", line) 900 if m: 901 tcp_port = m.group(1) 902 return ssl_port, tcp_port 903 904 self.ctx.out("") 905 if not iga: 906 self.ctx.out( 907 "No icegridadmin available: Cannot check server list") 908 else: 909 item("Server", "icegridnode") 910 p = self.ctx.popen(self._cmd("-e", "server list")) # popen 911 rv = p.wait() 912 io = p.communicate() 913 if rv != 0: 914 self.ctx.out("not started") 915 self.ctx.dbg(""" 916 Stdout:\n%s 917 Stderr:\n%s 918 """ % io) 919 else: 920 self.ctx.out("running") 921 servers = io[0].split() 922 servers.sort() 923 for s in servers: 924 item("Server", "%s" % s) 925 p2 = self.ctx.popen( 926 self._cmd("-e", "server state %s" % s)) # popen 927 p2.wait() 928 io2 = p2.communicate() 929 if io2[1]: 930 self.ctx.err(io2[1].strip()) 931 elif io2[0]: 932 self.ctx.out(io2[0].strip()) 933 else: 934 self.ctx.err("UNKNOWN!") 935 if self._isWindows(): 936 # Print the OMERO server Windows service details 937 hscm = win32service.OpenSCManager( 938 None, None, win32service.SC_MANAGER_ALL_ACCESS) 939 services = win32service.EnumServicesStatus(hscm) 940 omesvcs = tuple((sname, fname) for sname, fname, status 941 in services if "OMERO" in fname) 942 for sname, fname in omesvcs: 943 item("Server", fname) 944 hsc = win32service.OpenService( 945 hscm, sname, win32service.SC_MANAGER_ALL_ACCESS) 946 logonuser = win32service.QueryServiceConfig(hsc)[7] 947 if win32service.QueryServiceStatus(hsc)[1] == \ 948 win32service.SERVICE_RUNNING: 949 self.ctx.out("active (running as %s)" % logonuser) 950 else: 951 self.ctx.out("inactive") 952 win32service.CloseServiceHandle(hsc) 953 win32service.CloseServiceHandle(hscm) 954 955 # List SSL & TCP ports of deployed applications 956 self.ctx.out("") 957 p = self.ctx.popen(self._cmd("-e", "application list")) # popen 958 rv = p.wait() 959 io = p.communicate() 960 if rv != 0: 961 self.ctx.out("Cannot list deployed applications.") 962 self.ctx.dbg(""" 963 Stdout:\n%s 964 Stderr:\n%s 965 """ % io) 966 else: 967 applications = io[0].split() 968 applications.sort() 969 for s in applications: 970 p2 = self.ctx.popen( 971 self._cmd("-e", "application describe %s" % s)) 972 io2 = p2.communicate() 973 if io2[1]: 974 self.ctx.err(io2[1].strip()) 975 elif io2[0]: 976 ssl_port, tcp_port = get_ports(io2[0]) 977 item("%s" % s, "SSL port") 978 if not ssl_port: 979 self.ctx.err("Not found") 980 else: 981 self.ctx.out("%s" % ssl_port) 982 983 item("%s" % s, "TCP port") 984 if not tcp_port: 985 self.ctx.err("Not found") 986 else: 987 self.ctx.out("%s" % tcp_port) 988 else: 989 self.ctx.err("UNKNOWN!") 990 991 def log_dir(log, cat, cat2, knownfiles): 992 self.ctx.out("") 993 item(cat, "%s" % log.abspath()) 994 exists(log) 995 self.ctx.out("") 996 997 if log.exists(): 998 files = log.files() 999 files = set([x.basename() for x in files]) 1000 # Adding known names just in case 1001 for x in knownfiles: 1002 files.add(x) 1003 files = list(files) 1004 files.sort() 1005 for x in files: 1006 item(cat2, x) 1007 exists(log / x) 1008 item(cat2, "Total size") 1009 sz = 0 1010 for x in log.walkfiles(): 1011 sz += x.size 1012 self.ctx.out("%-.2f MB" % (float(sz)/1000000.0)) 1013 1014 log_dir(self.ctx.dir / "var" / "log", "Log dir", "Log files", 1015 ["Blitz-0.log", "Tables-0.log", "Processor-0.log", 1016 "Indexer-0.log", "FileServer.log", "MonitorServer.log", 1017 "DropBox.log", "TestDropBox.log", "OMEROweb.log"]) 1018 1019 # Parsing well known issues 1020 self.ctx.out("") 1021 ready = re.compile(".*?ome.services.util.ServerVersionCheck\ 1022 .*OMERO.Version.*Ready..*?") 1023 db_ready = re.compile(".*?Did.you.create.your.database[?].*?") 1024 data_dir = re.compile(".*?Unable.to.initialize:.FullText.*?") 1025 pg_password = re.compile(".*?org.postgresql.util.PSQLException:\ 1026 .FATAL:.password.*?authentication.failed.for.user.*?") 1027 pg_user = re.compile(""".*?org.postgresql.util.PSQLException:\ 1028 .FATAL:.role.".*?".does.not.exist.*?""") 1029 pg_conn = re.compile(""".*?org.postgresql.util.PSQLException:\ 1030 .Connection.refused.""") 1031 1032 issues = { 1033 ready: "=> Server restarted <=", 1034 db_ready: "Your database configuration is invalid", 1035 data_dir: "Did you create your omero.data.dir? E.g. /OMERO", 1036 pg_password: "Your postgres password seems to be invalid", 1037 pg_user: "Your postgres user is invalid", 1038 pg_conn: "Your postgres hostname and/or port is invalid" 1039 } 1040 1041 try: 1042 for file in ('Blitz-0.log',): 1043 1044 p = self.ctx.dir / "var" / "log" / file 1045 import fileinput 1046 for line in fileinput.input([str(p)]): 1047 lno = fileinput.filelineno() 1048 for k, v in issues.items(): 1049 if k.match(line): 1050 item('Parsing %s' % file, "[line:%s] %s" 1051 % (lno, v)) 1052 self.ctx.out("") 1053 break 1054 except: 1055 self.ctx.err("Error while parsing logs") 1056 1057 self.ctx.out("") 1058 1059 def env_val(val): 1060 item("Environment", "%s=%s" 1061 % (val, os.environ.get(val, "(unset)"))) 1062 self.ctx.out("") 1063 env_val("OMERO_HOME") 1064 env_val("OMERO_NODE") 1065 env_val("OMERO_MASTER") 1066 env_val("OMERO_TEMPDIR") 1067 env_val("PATH") 1068 env_val("ICE_HOME") 1069 env_val("LD_LIBRARY_PATH") 1070 env_val("DYLD_LIBRARY_PATH") 1071 1072 self.ctx.out("") 1073 for dir_name, dir_path, dir_size in ( 1074 ("data", omero_data_dir, ""), 1075 ("temp", omero_temp_dir, True)): 1076 dir_path_exists = os.path.exists(dir_path) 1077 is_writable = os.access(dir_path, os.R_OK | os.W_OK) 1078 if dir_size and dir_path_exists: 1079 dir_size = self.getdirsize(omero_temp_dir) 1080 dir_size = " (Size: %s)" % dir_size 1081 self.ctx.out("OMERO %s dir: '%s'\tExists? %s\tIs writable? %s%s" % 1082 (dir_name, dir_path, dir_path_exists, is_writable, 1083 dir_size)) 1084 1085 from omero.plugins.web import WebControl 1086 try: 1087 WebControl().status(args) 1088 except: 1089 self.ctx.out("OMERO.web not installed!") 1090
1091 - def getdirsize(self, directory):
1092 total = 0 1093 for values in os.walk(directory): 1094 for filename in values[2]: 1095 total += os.path.getsize(os.path.join(values[0], filename)) 1096 return total
1097
1098 - def session_manager(self, communicator):
1099 import IceGrid 1100 import Glacier2 1101 iq = communicator.stringToProxy("IceGrid/Query") 1102 iq = IceGrid.QueryPrx.checkedCast(iq) 1103 sm = iq.findAllObjectsByType("::Glacier2::SessionManager")[0] 1104 sm = Glacier2.SessionManagerPrx.checkedCast(sm) 1105 return sm
1106
1107 - def can_access(self, filepath, mask=os.R_OK | os.W_OK):
1108 """ 1109 Check that the given path belongs to 1110 or is accessible by the current user 1111 on Linux systems. 1112 """ 1113 1114 if "Windows" == platform.system(): 1115 return 1116 1117 pathobj = path(filepath) 1118 1119 if not pathobj.exists(): 1120 self.ctx.die(8, "FATAL: OMERO directory does not exist: %s" 1121 % pathobj) 1122 1123 owner = os.stat(filepath)[stat.ST_UID] 1124 if owner == 0: 1125 msg = "" 1126 msg += "FATAL: OMERO directory which needs to be writeable"\ 1127 " belongs to root: %s\n" % filepath 1128 msg += "Please use \"chown -R NEWUSER %s\" and run as then"\ 1129 " run %s as NEWUSER" % (filepath, sys.argv[0]) 1130 self.ctx.die(9, msg) 1131 else: 1132 if not os.access(filepath, mask): 1133 self.ctx.die(10, "FATAL: Cannot access %s, a required" 1134 " file/directory for OMERO" % filepath)
1135
1136 - def check_access(self, mask=os.R_OK | os.W_OK, config=None):
1137 """Check that 'var' is accessible by the current user.""" 1138 1139 var = self.ctx.dir / 'var' 1140 if not os.path.exists(var): 1141 self.ctx.out("Creating directory %s" % var) 1142 os.makedirs(var, 0700) 1143 else: 1144 self.can_access(var, mask) 1145 1146 if config is not None: 1147 omero_data_dir = '/OMERO' 1148 config = config.as_map() 1149 try: 1150 omero_data_dir = config['omero.data.dir'] 1151 except KeyError: 1152 pass 1153 self.can_access(omero_data_dir) 1154 for p in os.listdir(var): 1155 subpath = os.path.join(var, p) 1156 if os.path.isdir(subpath): 1157 self.can_access(subpath, mask)
1158
1159 - def check_node(self, args):
1160 """ 1161 If the args argparse.Namespace argument has no "node" attribute, 1162 then assign one. 1163 """ 1164 if not hasattr(args, "node"): 1165 args.node = self._node()
1166
1167 - def checkice(self, args=None):
1168 """ 1169 Checks for Ice version 3.4 1170 1171 See ticket:2514, ticket:1260 1172 """ 1173 1174 def _check(msg, vers): 1175 compat = ice_compatibility.split(".") 1176 vers = vers.split(".") 1177 if compat[0:2] != vers[0:2]: 1178 self.ctx.die(164, "%s is not compatible with %s: %s" 1179 % (msg, ".".join(compat), ".".join(vers)))
1180 1181 import Ice 1182 vers = Ice.stringVersion() 1183 _check("IcePy version", vers) 1184 1185 popen = self.ctx.popen(["icegridnode", "--version"]) 1186 vers = popen.communicate()[1] 1187 _check("icegridnode version", vers) 1188
1189 - def open_config(self, unused):
1190 """ 1191 Callers are responsible for closing the 1192 returned ConfigXml object. 1193 """ 1194 cfg_xml = self.ctx.dir / "etc" / "grid" / "config.xml" 1195 cfg_tmp = self.ctx.dir / "etc" / "grid" / "config.xml.tmp" 1196 grid_dir = self.ctx.dir / "etc" / "grid" 1197 if not cfg_xml.exists() and self.can_access(grid_dir): 1198 if cfg_tmp.exists() and self.can_access(cfg_tmp): 1199 self.ctx.dbg("Removing old config.xml.tmp") 1200 cfg_tmp.remove() 1201 config = omero.config.ConfigXml(str(cfg_tmp)) 1202 try: 1203 self.ctx.controls["config"].upgrade(None, config) 1204 finally: 1205 config.close() 1206 self.ctx.err("Creating %s" % cfg_xml) 1207 cfg_tmp.rename(str(cfg_xml)) 1208 1209 try: 1210 try: 1211 config = omero.config.ConfigXml(str(cfg_xml)) 1212 except Exception, e: 1213 self.ctx.die(577, str(e)) 1214 if config.save_on_close: 1215 config.save() 1216 else: 1217 self.ctx.err("%s read-only" % cfg_xml) 1218 except portalocker.LockException: 1219 try: 1220 config.close() 1221 except: 1222 pass 1223 self.ctx.die(111, "Could not acquire lock on %s" % cfg_xml) 1224 1225 return config
1226 1227 @with_config
1228 - def reindex(self, args, config):
1229 self.check_access(config=config) 1230 import omero.java 1231 server_dir = self.ctx.dir / "lib" / "server" 1232 log4j = "-Dlog4j.configuration=log4j-cli.properties" 1233 classpath = [file.abspath() for file in server_dir.files("*.jar")] 1234 xargs = [log4j, "-Xmx1024M", "-cp", os.pathsep.join(classpath)] 1235 1236 cfg = config.as_map() 1237 config.close() # Early close. See #9800 1238 for x in ("name", "user", "host", "port"): 1239 # NOT passing password on command-line 1240 k = "omero.db.%s" % x 1241 if k in cfg: 1242 v = cfg[k] 1243 xargs.append("-D%s=%s" % (k, v)) 1244 if "omero.data.dir" in cfg: 1245 xargs.append("-Domero.data.dir=%s" % cfg["omero.data.dir"]) 1246 for k, v in cfg.items(): 1247 if k.startswith("omero.search"): 1248 xargs.append("-D%s=%s" % (k, cfg[k])) 1249 1250 cmd = ["ome.services.fulltext.Main"] 1251 1252 if args.full: 1253 cmd.append("full") 1254 elif args.events: 1255 cmd.append("events") 1256 elif getattr(args, "class"): 1257 cmd.append("reindex") 1258 cmd.extend(getattr(args, "class")) 1259 else: 1260 self.ctx.die(502, "No valid action: %s" % args) 1261 1262 debug = False 1263 if getattr(args, "jdwp"): 1264 debug = True 1265 1266 self.ctx.dbg( 1267 "Launching Java: %s, debug=%s, xargs=%s" % (cmd, debug, xargs)) 1268 p = omero.java.popen( 1269 cmd, debug=debug, xargs=xargs, stdout=sys.stdout, 1270 stderr=sys.stderr) # FIXME. Shouldn't use std{out,err} 1271 self.ctx.rv = p.wait()
1272
1273 - def ports(self, args):
1274 self.check_access() 1275 from omero.install.change_ports import change_ports 1276 if not args.skipcheck: 1277 if 0 == self.status(args, node_only=True): 1278 self.ctx.die( 1279 100, "Can't change ports while the server is running!") 1280 1281 # Resetting return value. 1282 self.ctx.rv = 0 1283 1284 if args.prefix: 1285 for x in ("registry", "tcp", "ssl"): 1286 setattr(args, x, "%s%s" % (args.prefix, getattr(args, x))) 1287 change_ports( 1288 args.ssl, args.tcp, args.registry, args.revert, dir=self.ctx.dir)
1289
1290 - def cleanse(self, args):
1291 self.check_access() 1292 from omero.util.cleanse import cleanse 1293 client = self.ctx.conn(args) 1294 cleanse(data_dir=args.data_dir, dry_run=args.dry_run, 1295 query_service=client.sf.getQueryService(), 1296 config_service=client.sf.getConfigService())
1297
1298 - def sessionlist(self, args):
1299 client = self.ctx.conn(args) 1300 service = client.sf.getQueryService() 1301 params = omero.sys.ParametersI() 1302 query = "select s from Session s join fetch s.node n join fetch"\ 1303 " s.owner o where s.closed is null and n.id != 0" 1304 results = service.findAllByQuery(query, params) 1305 mapped = list() 1306 for s in results: 1307 rv = list() 1308 mapped.append(rv) 1309 if not s.isLoaded(): 1310 rv.append("") 1311 rv.append("id=%s" % s.id.val) 1312 rv.append("") 1313 rv.append("") 1314 rv.append("") 1315 rv.append("insufficient privileges") 1316 else: 1317 rv.append(s.node.id) 1318 rv.append(s.uuid) 1319 rv.append(s.started) 1320 rv.append(s.owner.omeName) 1321 if s.userAgent is None: 1322 rv.append("") 1323 else: 1324 rv.append(s.userAgent) 1325 if client.getSessionId() == s.uuid.val: 1326 rv.append("current session") 1327 else: 1328 rv.append("") 1329 self.ctx.controls["hql"].display( 1330 mapped, ("node", "session", "started", "owner", "agent", "notes"))
1331 try: 1332 register("admin", AdminControl, HELP) 1333 except NameError: 1334 if __name__ == "__main__": 1335 cli = CLI() 1336 cli.register("admin", AdminControl, HELP) 1337 cli.invoke(sys.argv[1:]) 1338