# Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; version 2 of the # License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # 02110-1301 USA from wb import DefineModule import grt import os import tempfile import time from mforms import Utilities, AppView, newSectionBox, App, newButton, newLabel, newBox import mforms import wb_admin_main import wb_admin_utils import wba_ssh_ui import wb_admin_ssh import wb_admin_control from wb_server_control import PasswordHandler from wb_server_control import ServerProfile from workbench.db_utils import MySQLConnection, MySQLError, QueryError, escape_sql_string from wb_common import OperationCancelledError, InvalidPasswordError, NoDriverInConnection, Users from wb_server_management import local_run_cmd, local_get_cmd_output from grt import log_info, log_error, log_warning, log_debug _this_file = os.path.basename(__file__) # define this Python module as a GRT module ModuleInfo = DefineModule(name= "WbAdmin", author= "Oracle Corp.", version="2.0") class DBError(Exception): pass tab_references = [] #=============================================================================== # #=============================================================================== class AdministratorTab(AppView): #top_box holds all sections top_vbox = None monitor = None configuration = None ctrl_be = None #--------------------------------------------------------------------------- def __init__(self, server_instance_settings): AppView.__init__(self, False, "admin", True) server_profile = ServerProfile(server_instance_settings, False) self.ctrl_be = wb_admin_control.WbAdminControl(server_profile, connect_sql=True) self.ctrl_be.init() version = self.ctrl_be.get_server_version() if type(version) is tuple: valid_versions = ((4,0), (4,1), (5,0), (5,1), (5,2), (5,4), (5,5), (5,6), (6,0)) if version[:2] not in valid_versions: print version, "UNSUPPORTED" log_warning(_this_file, "%s: Server version %s is NOT supported\n" % (self.__class__.__name__, str(version)) ) else: log_info(_this_file, "%s: Server version %s is supported\n" % (self.__class__.__name__, str(version)) ) self.on_close(wb_admin_utils.weakcb(self, "handle_on_close")) # Create sections and add them to the admin page. self.configuration = wb_admin_main.WbAdminMainView(server_profile, self.ctrl_be, self.monitor) self.add(self.configuration, True, True) #section = self.add_section("Configuration", False, False) #section.set_content(self.configuration) #--------------------------------------------------------------------------- def handle_on_close(self): App.get().set_status_text("Closing Administator.") self.configuration.shutdown() if not self.configuration.closing: return False self.ctrl_be.shutdown() tab_references.remove(self) self.release() return True #--------------------------------------------------------------------------- def wait_server_check(self, timeout): # wait for the server status check to be performed until the given timeout t = time.time() while self.ctrl_be.is_server_running() and time.time() - t < timeout: time.sleep(0.5) #--------------------------------------------------------------------------- def add_section(self, label_text, is_content_expandable, header_mode): section = newSectionBox(is_content_expandable, label_text, header_mode) section.set_name(label_text) self.add(section, not is_content_expandable, True) return section #------------------------------------------------------------------------------- def do_open_administrator(server_instance): validate_setting(server_instance.serverInfo, "sys.usesudo", norm_to_switch, None)#"Server profile has no indication of sudo usage") validate_setting(server_instance.serverInfo, "sys.usesudostatus", norm_to_switch, None) if server_instance.serverInfo["sys.system"] != "Windows": #validate_setting(server_instance.serverInfo, "sys.sudo", make_str_existing, "Server profile has no privileges elevation command defined") #if server_instance.serverInfo.has_key("sys.sudo") and server_instance.serverInfo["sys.sudo"].strip(" \r\t\n") == "": # Utilities.show_warning("WB Administrator", "Server profile has empty privileges elevation command defined. Some functionality maybe unavailable", "OK", "", "") try: if not server_instance.serverInfo["sys.sudo"]: # don't break settings that were working perfectly before, assume a valid default server_instance.serverInfo["sys.sudo"] = "/usr/bin/sudo -p EnterPasswordHere /bin/bash -c" except: server_instance.serverInfo["sys.sudo"] = "/usr/bin/sudo -p EnterPasswordHere /bin/bash -c" app = App.get() try: adminTab = AdministratorTab(server_instance) except wb_admin_ssh.ConnectionError, exc: Utilities.show_error("Error Connecting to Server (%s@%s)" % (server_instance.loginInfo["ssh.userName"], server_instance.loginInfo["ssh.hostName"]), str(exc), "OK", "", "") app.set_status_text("Could not Open WB Admin") return None except MySQLError, exc: if exc.message: Utilities.show_error("Error Connecting to MySQL Server (%s)" % exc.location, str(exc), "OK", "", "") app.set_status_text("Could not Open WB Admin") return None except OperationCancelledError, exc: app.set_status_text("Cancelled (%s)"%exc) return None except NoDriverInConnection, exc: Utilities.show_error('Missing connection driver', str(exc), 'OK', '', '') app.set_status_text("Could not Open WB Admin") return None except Exception, exc: import traceback traceback.print_exc() Utilities.show_error("Error Starting Workbench Administrator", "%s: %s" % (type(exc).__name__, exc), "OK", "", "") app.set_status_text("Could not Open WB Admin") return None version = adminTab.ctrl_be.get_server_version() if version and version[0] < 5: Utilities.show_error("Unsupported Server Version", "The version of the server you're trying to connect to is %i.%i, which is not supported by Workbench."%version[:2], "Close", "Ignore", "") app.set_status_text("Could not Open WB Admin") return None app.dock_view(adminTab, "maintab") app.set_view_title(adminTab, "Admin (%s)" % (server_instance.name)) tab_references.append(adminTab) app.set_status_text("WB Admin Opened") log_info(_this_file, "do_open_administrator(): Completed\n") return adminTab #------------------------------------------------------------------------------- def validate_setting(settings, option, norm_cb, msg): if settings.has_key(option): if norm_cb is not None: norm_cb(settings, option) else: if msg is not None: Utilities.show_warning("WB Administartor", msg, "OK", "", "") norm_cb(settings, option) #------------------------------------------------------------------------------- def norm_to_switch(settings, option): value = 0 if settings.has_key(option): value = settings[option] if value > 0: value = 1 else: value = 0 settings[option] = value #------------------------------------------------------------------------------- def make_str_existing(settings, option): if not settings.has_key(option): settings[option] = "" #------------------------------------------------------------------------------- @ModuleInfo.plugin("wb.admin.open", type="standalone", caption= "Initialize WB Admin", pluginMenu= "Administrator") @ModuleInfo.export(grt.INT, grt.classes.db_mgmt_ServerInstance) def openAdministrator(server_instance): do_open_administrator(server_instance) return 1 # this shouldn't be exported as a plugin... and besides it crashes #@ModuleInfo.plugin("wb.admin.filterDebugger", type="standalone", caption= "Test filters", pluginMenu= "Utilities") @ModuleInfo.export(grt.INT, grt.DICT, grt.DICT) def openFilterDebugger(loginInfo, serverInfo): wb_admin_control.run_filter_debugger(loginInfo, serverInfo) return 1 @ModuleInfo.plugin("wb.admin.listServices", type="standalone") @ModuleInfo.export(grt.STRING, grt.DICT) def listWindowsServices(server_instance): return wb_admin_utils.list_windows_services(server_instance) #@ModuleInfo.plugin("wb.admin.remoteFileSelector", type="standalone") @ModuleInfo.export(grt.STRING, grt.classes.db_mgmt_ServerInstance) def openRemoteFileSelector(serverInstance): profile = ServerProfile(serverInstance) return wba_ssh_ui.remote_file_selector(profile, PasswordHandler(profile)) def selectServer(title): # No need to select an instance if there's only one: if len(grt.root.wb.rdbmsMgmt.storedInstances) == 1: return grt.root.wb.rdbmsMgmt.storedInstances[0] window = mforms.Form(None) window.set_title(title) box = mforms.newBox(False) window.set_content(box) box.set_padding(12) box.set_spacing(12) label = mforms.newLabel("Select Server to Connect to:") box.add(label, False, True) listbox = mforms.newListBox(False) box.add(listbox, True, True) listbox.show(); for inst in grt.root.wb.rdbmsMgmt.storedInstances: listbox.add_item(inst.name) if len(grt.root.wb.rdbmsMgmt.storedInstances) > 0: listbox.set_selected(0) else: Utilities.show_warning("No Database Server Instances", '''You have not defined a database server instance. At least one server instance is needed. Please define one by clicking in "New Server Instance" and retry.''', "OK", "", "") return None bbox = mforms.newBox(True) box.add(bbox, False, True) bbox.set_spacing(8) ok = mforms.newButton() ok.set_text("OK") bbox.add_end(ok, False, True) cancel = mforms.newButton() cancel.set_text("Cancel") bbox.add_end(cancel, False, True) window.set_size(400, 300) window.center() if window.run_modal(ok, cancel): i = listbox.get_selected_index() if i >= 0: return grt.root.wb.rdbmsMgmt.storedInstances[i] return None @ModuleInfo.plugin("wb.admin.dumpManager", type="standalone", caption= "Open Database Export/Import Manager", pluginMenu= "Administrator") @ModuleInfo.export(grt.INT) def openExportImport(): server = selectServer("Import/Export MySQL Data") if server: tab = do_open_administrator(server) if tab: tab.wait_server_check(4) tab.configuration.switch_to("DATA EXPORT / RESTORE", "Data Export") return 1 return 0 @ModuleInfo.plugin("wb.admin.securityManager", type="standalone", caption= "Open Security Manager", pluginMenu= "Administrator") @ModuleInfo.export(grt.INT) def openSecurityManager(): server = selectServer("Security Manager") if server: tab = do_open_administrator(server) if tab: tab.wait_server_check(4) tab.configuration.switch_to("SECURITY", "Users and Privileges") return 1 return 0 class PasswordExpiredDialog(mforms.Form): def __init__(self, conn): mforms.Form.__init__(self, None) self._conn = conn self.set_title("Password Expired") vbox = mforms.newBox(False) vbox.set_padding(12) box = mforms.newTable() box.set_row_count(4) box.set_column_count(2) box.set_column_spacing(8) box.set_row_spacing(12) vbox.add(box, True, True) user = conn.parameterValues["userName"] box.add(newLabel("Password for MySQL account '%s' from %s is expired\nand must be changed before the account can be used.\n\nPlease pick a new password:" % (user, conn.hostIdentifier.replace("Mysql@", ""))), 0, 2, 0, 1, 0) self.old_password = mforms.newTextEntry(mforms.PasswordEntry) box.add(newLabel("Old Password:", True), 0, 1, 1, 2, mforms.HFillFlag) box.add(self.old_password, 1, 2, 1, 2, mforms.HFillFlag|mforms.HExpandFlag) self.password = mforms.newTextEntry(mforms.PasswordEntry) box.add(newLabel("New Password:", True), 0, 1, 2, 3, mforms.HFillFlag) box.add(self.password, 1, 2, 2, 3, mforms.HFillFlag|mforms.HExpandFlag) self.confirm = mforms.newTextEntry(mforms.PasswordEntry) box.add(newLabel("Confirm:", True), 0, 1, 3, 4, mforms.HFillFlag) box.add(self.confirm, 1, 2, 3, 4, mforms.HFillFlag|mforms.HExpandFlag) bbox = newBox(True) bbox.set_spacing(8) self.ok = newButton() self.ok.set_text("OK") self.cancel = newButton() self.cancel.set_text("Cancel") mforms.Utilities.add_end_ok_cancel_buttons(bbox, self.ok, self.cancel) vbox.add_end(bbox, False, True) self.set_content(vbox) self.set_size(500, 240) self.center() def run(self): if self.run_modal(self.ok, self.cancel): con = self._conn old_multi_statements = con.parameterValues.get("CLIENT_MULTI_STATEMENTS") old_script = con.parameterValues.get("preInit") con.parameterValues["CLIENT_MULTI_STATEMENTS"] = 1 con.parameterValues["preInit"] = "SET PASSWORD = PASSWORD('%s')" % escape_sql_string(self.password.get_string_value()) retry = False result = 1 c = MySQLConnection(con, password = self.old_password.get_string_value()) # connect to server so that preInitScript will do the password reset work try: c.connect() except MySQLError, e: if mforms.Utilities.show_error("Reset Password", str(e), "Retry", "Cancel", "") == mforms.ResultOk: retry = True result = 0 if old_script is not None: con.parameterValues["preInit"] = old_script else: del con.parameterValues["preInit"] if old_multi_statements is not None: con.parameterValues["CLIENT_MULTI_STATEMENTS"] = old_multi_statements else: del con.parameterValues["CLIENT_MULTI_STATEMENTS"] if retry: return self.run() return result return 0 @ModuleInfo.export(grt.INT, grt.classes.db_mgmt_Connection) def handleExpiredPassword(conn): dlg = PasswordExpiredDialog(conn) return dlg.run() @ModuleInfo.plugin("wb.admin.placeholder", type="standalone", caption= "Open Administrator", pluginMenu= "Administrator") @ModuleInfo.export(grt.INT) def tmpAdministratorShortcut(): server = selectServer("Server Administration") if server: tab = do_open_administrator(server) return 1 #------------------------------------------------------------------------------- @ModuleInfo.export(grt.INT, grt.STRING) def testAdministrator(what): import wb_admin_test wb_admin_test.run() import sys sys.exit(0) # TODO return code here return 1 def check_if_config_file_has_section(config_file, section): for line in config_file: if line.strip() == "[%s]"%section: return True return False test_ssh_connection = None test_ssh_connection_is_windows = None @ModuleInfo.export(grt.STRING, grt.STRING, grt.classes.db_mgmt_ServerInstance) def testInstanceSettingByName(what, server_instance): global test_ssh_connection profile = ServerProfile(server_instance) if what == "connect_to_host": if test_ssh_connection: test_ssh_connection = None print "Connecting to %s" % profile.ssh_hostname try: test_ssh_connection = wb_admin_control.WbAdminControl(profile, connect_sql=False) test_ssh_connection.init() grt.send_info("connected.") except Exception, exc: import traceback traceback.print_exc() return "ERROR "+str(exc) except: print "Unknown error" return "ERROR" if not test_ssh_connection.is_ssh_connected(): test_ssh_connection = None return "ERROR Error connecting to host" result, rc = test_ssh_connection.execute_filtered_command("uname") if rc != 0: # error calling uname, possibly windows result, rc = test_ssh_connection.execute_filtered_command("ver") test_ssh_connection_is_windows = True else: test_ssh_connection_is_windows = False print "OK, Operating System is '%s'" % result.strip() if not result: return "ERROR" return "OK" elif what == "disconnect": if test_ssh_connection: test_ssh_connection = None return "OK" elif what == "check_privileges": return "ERROR" elif what in ("find_config_file", "check_config_path", "check_config_section"): config_file = profile.config_file_path print "Check if %s exists in remote host" % config_file try: if not test_ssh_connection.ssh.file_exists(config_file): return "ERROR File %s doesn't exist" % config_file else: print "File was found in expected location" except IOError: return 'ERROR Could not verify the existence of the file %s' % config_file if what == "check_config_path": return "OK" section = profile.config_file_section cfg_file_content = "" print "Check if %s section exists in %s" % (section, config_file) try: #local_file = test_ssh_connection.fetch_file(config_file) cfg_file_content = test_ssh_connection.server_helper.get_file_content(path=config_file) except Exception, exc: import traceback traceback.print_exc() return "ERROR "+str(exc) if ("[" + section + "]") in cfg_file_content: return "OK" return "ERROR Couldn't find section %s in the remote config file %s" % (section, config_file) elif what in ("find_config_file/local", "check_config_path/local", "check_config_section/local"): config_file = profile.config_file_path config_file = wb_admin_control.WbAdminControl(profile, connect_sql=False).expand_path_variables(config_file) print "Check if %s can be accessed" % config_file if os.path.exists(config_file): print "File was found at the expected location" else: return "ERROR File %s doesn't exist" % config_file if what == "check_config_path/local": return "OK" section = profile.config_file_section print "Check if section for instance %s exists in %s" % (section, config_file) if check_if_config_file_has_section(open(config_file, "r"), section): print "[%s] section found in configuration file" % section return "OK" return "ERROR Couldn't find section [%s] in the config file %s" % (section, config_file) elif what == "find_error_files": return "ERROR" elif what == "check_admin_commands": path = profile.start_server_cmd cmd_start= None if path.startswith("/"): cmd_start = path.split()[0] if not test_ssh_connection.ssh.file_exists(cmd_start): return "ERROR %s is invalid" % path path = profile.stop_server_cmd if path.startswith("/"): cmd = path.split()[0] if cmd != cmd_start and not test_ssh_connection.ssh.file_exists(cmd): return "ERROR %s is invalid" % path command = profile.check_server_status_cmd print "Checking command '%s'" % command #rc = test_ssh_connection.is_running() rc = test_ssh_connection.server_control.get_status() print "Server detected as %s" % (rc and "running" or "stopped"), return "OK" elif what == "check_admin_commands/local": path = profile.start_server_cmd cmd_start= None if path.startswith("/"): cmd_start = path.split()[0] if not os.path.exists(cmd_start): return "ERROR %s is invalid" % path path = profile.stop_server_cmd if path.startswith("/"): cmd = path.split()[0] if cmd != cmd_start and not os.path.exists(cmd): return "ERROR %s is invalid" % path command = profile.check_server_status_cmd print "Checking command '%s'" % command result, rc = local_get_cmd_output(command, Users.CURRENT, None) print "Server detected as %s" % (result and "running" or "stopped") return "OK "+(result and "running" or "stopped") return "ERROR bad command" @ModuleInfo.export(grt.DICT, grt.classes.db_mgmt_ServerInstance) def detectInstanceSettings(server_instance): #form = Form() #form.run(None, None) return {} @ModuleInfo.export(grt.INT, grt.classes.db_mgmt_ServerInstance) def testInstanceSettings(server_instance): return 0