~ubuntu-branches/ubuntu/quantal/mysql-workbench/quantal

« back to all changes in this revision

Viewing changes to plugins/wb.admin/backend/wb_server_control.py

  • Committer: Package Import Robot
  • Author(s): Dmitry Smirnov
  • Date: 2012-03-01 21:57:30 UTC
  • Revision ID: package-import@ubuntu.com-20120301215730-o7y8av8y38n162ro
Tags: upstream-5.2.38+dfsg
ImportĀ upstreamĀ versionĀ 5.2.38+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
 
2
#
 
3
# This program is free software; you can redistribute it and/or
 
4
# modify it under the terms of the GNU General Public License as
 
5
# published by the Free Software Foundation; version 2 of the
 
6
# License.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 
16
# 02110-1301  USA
 
17
 
 
18
import threading
 
19
from threading import currentThread
 
20
import sys
 
21
import os
 
22
 
 
23
import grt
 
24
 
 
25
import mforms
 
26
 
 
27
from wb_server_management import wbaOS
 
28
from wb_common import OperationCancelledError, InvalidPasswordError, dprint_ex
 
29
 
 
30
import re
 
31
import socket
 
32
 
 
33
#-------------------------------------------------------------------------------
 
34
def get_local_ip_list():
 
35
    cmd    = "/bin/sh -c ifconfig"
 
36
    regexp = "inet addr:([0-9a-f:\.]+)"
 
37
 
 
38
    if hasattr(sys, 'getwindowsversion'):
 
39
        cmd    = 'ipconfig'
 
40
        regexp = "IPv4.*: +([0-9a-f:\.]+)"
 
41
    elif 'darwin' in sys.platform:
 
42
        regexp = "inet ([0-9a-f\.:]+) netmask"
 
43
 
 
44
    out = None
 
45
    try:
 
46
      out = os.popen(cmd)
 
47
    except Exception, e:
 
48
      # We do not care if execution failed, the list will just be empty
 
49
      #print e
 
50
      out = None
 
51
    except:
 
52
      print "Unknown exception while running '%s'"%cmd
 
53
 
 
54
    try:
 
55
      ret = []
 
56
      if out:
 
57
          result = out.read()
 
58
          m = re.findall(regexp, result)
 
59
 
 
60
          for ip in m:
 
61
              try:
 
62
                  t_ip = socket.gethostbyname(ip)
 
63
                  ret.append(ip)
 
64
              except socket.gaierror,e:
 
65
                  print ip, str(e)
 
66
    except Exception, e:
 
67
      # We do not care if execution failed, the list will just be empty
 
68
      #print e
 
69
      ret = []
 
70
    except:
 
71
      print "Unknown exception while running '%s'"%cmd
 
72
      ret = []
 
73
 
 
74
    return ret
 
75
 
 
76
local_ip_list = get_local_ip_list()
 
77
 
 
78
#===============================================================================
 
79
class ServerProfile(object):
 
80
    def __init__(self, instance_profile, force_remote_admin_off=False):
 
81
        self.disable_remote_admin = force_remote_admin_off
 
82
        self.__settings = instance_profile
 
83
        self.expanded_config_file_path = None
 
84
    
 
85
    def get_settings_object(self):
 
86
        return self.__settings
 
87
    
 
88
    def _int_server_info(self, name, default_value=0):
 
89
        # always returns an int, a None value will be replaced with default_value
 
90
        try:
 
91
            value = self.__settings.serverInfo[name]
 
92
        except KeyError, exc:
 
93
            value = default_value
 
94
        if type(value) is not int:
 
95
            try:
 
96
                value= int(value)
 
97
            except:
 
98
                value= default_value
 
99
        if value is None:
 
100
            return default_value
 
101
        return value
 
102
    
 
103
    def _int_login_info(self, name, default_value=0):
 
104
        # always returns an int, a None value will be replaced with default_value
 
105
        try:
 
106
            value = self.__settings.loginInfo[name]
 
107
        except KeyError, exc:
 
108
            value = default_value
 
109
        if type(value) is not int:
 
110
            try:
 
111
                value= int(value)
 
112
            except:
 
113
                value= default_value
 
114
        if value is None:
 
115
            return default_value
 
116
        return value
 
117
 
 
118
    def _str_server_info(self, name, default_value=""):
 
119
        # always returns an str or unicode, a None value will be replaced with default_value
 
120
        try:
 
121
            value= self.__settings.serverInfo[name]
 
122
        except KeyError, exc:
 
123
            value = default_value
 
124
        if value is None:
 
125
            value= default_value
 
126
        elif type(value) is not str and type(value) is not unicode:
 
127
            value= str(value)
 
128
        return value
 
129
 
 
130
    def _str_login_info(self, name, default_value=""):
 
131
        # always returns an str or unicode, a None value will be replaced with default_value
 
132
        try:
 
133
            value= self.__settings.loginInfo[name]
 
134
        except KeyError, exc:
 
135
            value = default_value
 
136
        if value is None:
 
137
            value= default_value
 
138
        elif type(value) is not str and type(value) is not unicode:
 
139
            value= str(value)
 
140
        return value
 
141
 
 
142
    @property
 
143
    def name(self):
 
144
        return self.__settings.name
 
145
 
 
146
    @property
 
147
    def db_connection_params(self):
 
148
        return self.__settings.connection
 
149
 
 
150
    @property
 
151
    def host_os(self):
 
152
      if hasattr(sys, 'getwindowsversion'):
 
153
        return wbaOS.windows
 
154
      elif ('inux' in sys.platform):
 
155
        return wbaOS.linux
 
156
      elif ('arwin' in sys.platform):
 
157
        return wbaOS.darwin
 
158
      return wbaOS.unknown
 
159
 
 
160
    # Must return smth from wbaOS
 
161
    @property
 
162
    def target_os(self):
 
163
      # This call must return either value from profile for remote server
 
164
      # If remote server is not set, which means local server, then returned value
 
165
      # must be equal to self.host_os
 
166
      ret = wbaOS.unknown
 
167
      
 
168
      try:
 
169
        system = self.__settings.serverInfo['sys.system']
 
170
      except:
 
171
        system = None
 
172
      if system is not None and type(system) is str or type(system) is unicode:
 
173
        system = system.strip(" \r\t\n").lower()
 
174
        if system == 'windows':
 
175
          ret = wbaOS.windows
 
176
        elif system in ('linux', 'freebsd', 'opensolaris'):
 
177
          ret = wbaOS.linux
 
178
        elif ('macos' in system):
 
179
          ret = wbaOS.darwin
 
180
      else:
 
181
        ret = self.host_os
 
182
      
 
183
      return ret
 
184
 
 
185
    @property
 
186
    def host_is_windows(self):
 
187
        return self.host_os == wbaOS.windows
 
188
    
 
189
    @property
 
190
    def target_is_windows(self):
 
191
        return self.target_os == wbaOS.windows
 
192
 
 
193
 
 
194
    @property
 
195
    def remote_admin_enabled(self):
 
196
        return (self.uses_ssh or (not self.is_local and self.uses_wmi)) and not self.disable_remote_admin
 
197
    
 
198
    @property
 
199
    def admin_enabled(self):
 
200
        return self.is_local or self.remote_admin_enabled
 
201
        
 
202
    @property
 
203
    def is_local(self):
 
204
        local_addrs = ["localhost", "", "0", "127.0.0.1"] + local_ip_list
 
205
        if self.uses_wmi:
 
206
            return self.wmi_hostname in local_addrs
 
207
        elif self.uses_ssh:
 
208
            return self.ssh_hostname in local_addrs
 
209
        else:
 
210
            if self.db_connection_params:
 
211
                params = self.db_connection_params.parameterValues
 
212
                if params:
 
213
                    if self.db_connection_params.driver:
 
214
                        if self.db_connection_params.driver.name == "MysqlNative":
 
215
                            return params["hostName"] in local_addrs
 
216
                        elif self.db_connection_params.driver.name == "MysqlNativeSocket":
 
217
                            return True
 
218
                        elif self.db_connection_params.driver.name == "MysqlNativeSSH":
 
219
                            return False
 
220
                    else:
 
221
                        from wb_common import NoDriverInConnection
 
222
                        raise NoDriverInConnection, """Workbench has not found a driver for the connection
 
223
that is being used by this server instance.
 
224
Please edit your connection settings and try again."""
 
225
            return False
 
226
 
 
227
 
 
228
    @property
 
229
    def connect_method(self):
 
230
      # choices are ['local', 'ssh', 'wmi', 'none'] 
 
231
      # 'none' is for remote server ip with "No Remote Management" selected
 
232
      ret = None
 
233
      if self.uses_wmi:
 
234
        ret = 'wmi'
 
235
      elif self.uses_ssh:
 
236
        ret = 'ssh'
 
237
      else:
 
238
        if self.is_local:
 
239
          ret = 'local'
 
240
        else:
 
241
          ret = 'none'
 
242
      return ret
 
243
 
 
244
 
 
245
    @property
 
246
    def uses_ssh(self):
 
247
        return self._int_server_info("remoteAdmin")
 
248
 
 
249
    # If WMI admin was selected as mgmt mean (can be for local or remote)
 
250
    @property
 
251
    def uses_wmi(self):
 
252
        return self._int_server_info("windowsAdmin")
 
253
 
 
254
    @property
 
255
    def ssh_username(self):
 
256
        return self._str_login_info("ssh.userName")
 
257
 
 
258
    @property
 
259
    def ssh_hostname(self):
 
260
        return self._str_login_info("ssh.hostName")
 
261
 
 
262
    @property
 
263
    def ssh_port(self):
 
264
        return self._int_login_info("ssh.port", 22)
 
265
 
 
266
    @property
 
267
    def ssh_usekey(self):
 
268
        return self._int_login_info("ssh.useKey")
 
269
 
 
270
    @property
 
271
    def ssh_key(self):
 
272
        return self._str_login_info("ssh.key")
 
273
    
 
274
    @property
 
275
    def wmi_hostname(self):
 
276
        return self._str_login_info("wmi.hostName")
 
277
    
 
278
    @property
 
279
    def wmi_username(self):
 
280
        return self._str_login_info("wmi.userName")
 
281
 
 
282
    @property
 
283
    def wmi_service_name(self):
 
284
        return self._str_server_info("sys.mysqld.service_name")
 
285
        
 
286
    @property
 
287
    def start_server_cmd(self):
 
288
        return self._str_server_info("sys.mysqld.start")
 
289
    
 
290
    @property
 
291
    def stop_server_cmd(self):
 
292
        return self._str_server_info("sys.mysqld.stop")
 
293
        
 
294
    @property
 
295
    def check_server_status_cmd(self):
 
296
        return self._str_server_info("sys.mysqld.status")
 
297
        
 
298
    @property
 
299
    def use_sudo(self):
 
300
        return self._int_server_info("sys.usesudo")
 
301
 
 
302
    @property
 
303
    def sudo_prefix(self):
 
304
        return self._str_server_info("sys.sudo", "sudo") or "sudo"
 
305
 
 
306
    @property
 
307
    def use_sudo_for_status(self):
 
308
        return self._int_server_info("sys.usesudostatus")
 
309
        
 
310
    
 
311
    @property
 
312
    def config_file_path(self):
 
313
        """ path to configuration file (eg my.cnf)
 
314
        may contain path variables that need to be expanded, such as %ProgramPath%
 
315
        """
 
316
        if self.expanded_config_file_path:
 
317
            return self.expanded_config_file_path
 
318
        return self._str_server_info('sys.config.path', '').strip(" \r\t\n\b")
 
319
 
 
320
 
 
321
    @property
 
322
    def config_file_section(self):
 
323
        return self._str_server_info('sys.config.section', '').strip(" \r\t\n\b")
 
324
 
 
325
    @property
 
326
    def sys_script(self):
 
327
        return self._str_server_info('sys.script', '')
 
328
 
 
329
    #-------------------------------------------------------
 
330
    def server_version(self):
 
331
        return self._str_server_info("serverVersion", None)
 
332
 
 
333
    def set_server_version(self, version):
 
334
        self.__settings.serverInfo["serverVersion"] = version
 
335
        
 
336
    server_version = property(server_version, set_server_version)
 
337
        
 
338
    #-------------------------------------------------------     
 
339
    def log_output(self):
 
340
        return self._str_server_info("logOutput")
 
341
 
 
342
    def set_log_output(self, output):
 
343
        self.__settings.serverInfo['logOutput'] = output
 
344
        
 
345
    log_output = property(log_output, set_log_output)
 
346
        
 
347
    #-------------------------------------------------------     
 
348
    def general_log_enabled(self):
 
349
        return self._int_server_info("generalLogEnabled")
 
350
 
 
351
    def set_general_log_enabled(self, value):
 
352
        self.__settings.serverInfo['generalLogEnabled'] = value
 
353
        
 
354
    general_log_enabled = property(general_log_enabled, set_general_log_enabled)
 
355
        
 
356
    #-------------------------------------------------------     
 
357
    def slow_log_enabled(self):
 
358
        return self._int_server_info("slowLogEnabled")
 
359
 
 
360
    def set_slow_log_enabled(self, value):
 
361
        self.__settings.serverInfo['slowLogEnabled'] = value
 
362
        
 
363
    slow_log_enabled = property(slow_log_enabled, set_slow_log_enabled)
 
364
 
 
365
    #-------------------------------------------------------
 
366
    def general_log_file_path(self):
 
367
        return self._str_server_info("generalLogFilePath")
 
368
    
 
369
    def set_general_log_file_path(self, path):
 
370
        self.__settings.serverInfo['generalLogFilePath'] = path
 
371
        
 
372
    general_log_file_path = property(general_log_file_path, set_general_log_file_path)
 
373
        
 
374
    #-------------------------------------------------------
 
375
    def slow_log_file_path(self):
 
376
        return self._str_server_info("slowLogFilePath")
 
377
    
 
378
    def set_slow_log_file_path(self, path):
 
379
        self.__settings.serverInfo['slowLogFilePath'] = path
 
380
        
 
381
    slow_log_file_path = property(slow_log_file_path, set_slow_log_file_path)
 
382
    
 
383
    #-------------------------------------------------------
 
384
    def error_log_file_path(self):
 
385
        return self._str_server_info("errorLogFilePath")
 
386
    
 
387
    def set_error_log_file_path(self, path):
 
388
        self.__settings.serverInfo['errorLogFilePath'] = path
 
389
    
 
390
    error_log_file_path = property(error_log_file_path, set_error_log_file_path)
 
391
 
 
392
 
 
393
class PasswordHandler(object):
 
394
    def __init__(self, server_profile):
 
395
        self.server_profile = server_profile
 
396
        self.pwd_store = {}
 
397
    
 
398
    def get_password_parameters(self, service_type):
 
399
        # known service types: remoteshell, ssh, sshkey, file, service.startstop, service.status
 
400
        # these are mapped to
 
401
        # known password types: ssh, sshkey, sudo, UAC, wmi
 
402
        # which are then mapped to (title, service, account) or "UAC" or None
 
403
 
 
404
        profile = self.server_profile
 
405
 
 
406
        password_type = None
 
407
        if service_type == "file":
 
408
            if profile.target_is_windows:
 
409
                # in windows, auth is handled by UAC which is done externally
 
410
                password_type = "UAC"
 
411
            else:
 
412
                password_type = "sudo"
 
413
        elif service_type == "service.startstop":
 
414
            if profile.target_is_windows:
 
415
                password_type = "wmi"
 
416
            else:
 
417
                if profile.use_sudo:
 
418
                    password_type = "sudo"
 
419
                else:
 
420
                    password_type = None
 
421
        elif service_type == "service.status":
 
422
            if profile.target_is_windows:
 
423
                password_type = None
 
424
            else:
 
425
                if profile.use_sudo_for_status:
 
426
                    password_type = "sudo"
 
427
                else:
 
428
                    password_type = None
 
429
        elif service_type == "remoteshell":
 
430
            if profile.uses_ssh:
 
431
                if profile.ssh_usekey:
 
432
                    password_type = "sshkey"
 
433
                else:
 
434
                    password_type = "ssh"
 
435
        elif service_type == "ssh":
 
436
            password_type = "ssh"
 
437
        elif service_type == "sshkey":
 
438
            password_type = "sshkey"
 
439
        else:
 
440
            raise Exception("Unknown password type: %s" % service_type)
 
441
 
 
442
 
 
443
        # sudo pwd may be either user's password, or root password. It depends on sudo setup
 
444
        if password_type == "sudo":
 
445
            sudo_type = "sudo"
 
446
            # if su is to be used instead of sudo, then we need the root password instead of login user
 
447
            if profile.sudo_prefix and "sudo" not in profile.sudo_prefix:
 
448
                sudo_type = "root"
 
449
 
 
450
            if profile.uses_ssh:
 
451
                if sudo_type == "sudo":
 
452
                    account = profile.ssh_username
 
453
                else:
 
454
                    account = "root"
 
455
                title = "%s Password Required"%sudo_type
 
456
                service = "%s@%s"%(sudo_type, profile.ssh_hostname)
 
457
            else:
 
458
                if sudo_type == "sudo":
 
459
                    import pwd
 
460
                    # os.getlogin() raises errno 25 if WB is started from ubuntu menu, so never use it
 
461
                    #account = os.getlogin()
 
462
                    try:
 
463
                        account = pwd.getpwuid(os.getuid())[0]
 
464
                        if not account:
 
465
                            account = "sudo"
 
466
                    except:
 
467
                        account = "sudo"
 
468
                else:
 
469
                    account = "root"
 
470
                title = "%s Password Required"% sudo_type
 
471
                service = "%s@localhost" % sudo_type
 
472
 
 
473
            return (title, service, account)
 
474
 
 
475
        elif password_type == "wmi":
 
476
            return ("WMI Password Required", "wmi@%s" % profile.wmi_hostname, profile.wmi_username)
 
477
 
 
478
        elif password_type == "UAC":
 
479
            return "UAC"
 
480
        
 
481
        elif password_type == "ssh":
 
482
            return ("SSH Login Password Required", "ssh@%s" % profile.ssh_hostname, profile.ssh_username)
 
483
        
 
484
        elif password_type == "sshkey":
 
485
            return ("SSH Private Key Password Required", "ssh_keyfile@%s" % profile.ssh_key, profile.ssh_username)
 
486
        
 
487
        else:
 
488
            return None
 
489
        
 
490
    # call this if a InvalidPasswordError is caught
 
491
    def reset_password_for(self, service_type):
 
492
        if self.pwd_store.has_key(service_type):
 
493
            # None means the stored password was bad
 
494
            self.pwd_store[service_type] = None
 
495
 
 
496
    def get_password_for(self, service_type): # Raises OperationCancelledError if user cancels pwd dlg. 
 
497
        force_reset = False
 
498
        if self.pwd_store.has_key(service_type):
 
499
            if self.pwd_store[service_type] is None:
 
500
                force_reset = True
 
501
            else:
 
502
                return self.pwd_store[service_type]
 
503
 
 
504
        details = self.get_password_parameters(service_type)
 
505
        if not details: # No password needed for this service_type
 
506
            return None
 
507
        
 
508
        # special case for UAC used in windows
 
509
        if details == "UAC":
 
510
            return "UAC"
 
511
 
 
512
        title, service, account = details
 
513
 
 
514
        dprint_ex(2, "request password for %s => %s, %s, %s" % (service_type, title, service, account))
 
515
 
 
516
        accepted, password = mforms.Utilities.find_or_ask_for_password(title, service, account, force_reset)
 
517
        if accepted:
 
518
            if not password:
 
519
                password = ""
 
520
            self.pwd_store[service_type] = password
 
521
            return password
 
522
        else:
 
523
            raise OperationCancelledError("Password input cancelled")
 
524
 
 
525
 
 
526
class ServerControlBase(object):
 
527
    def __init__(self, serverProfile, helper, password_delegate):
 
528
        self.profile = serverProfile
 
529
        self.helper = helper
 
530
        self.password_delegate = password_delegate
 
531
        self.output_buffer = []
 
532
        # default handler will just add the output to a list to be flushed later
 
533
        self.output_handler = lambda line, output_buffer= self.output_buffer: output_buffer.append(line)
 
534
 
 
535
    def set_output_handler(self, handler): # must be called by consumer of output (ie, the start/stop Tab in WB)
 
536
        self.output_handler = handler
 
537
        if self.output_buffer:
 
538
            for line in self.output_buffer:
 
539
                self.output_handler(line)
 
540
        self.output_buffer = None
 
541
 
 
542
    def info(self, text):
 
543
        self.output_handler(text)
 
544
 
 
545
    def close(self):
 
546
        # do cleanup, free up resources etc
 
547
        pass
 
548
 
 
549
    def start(self, password): # overriden by concrete subclasses
 
550
        "Starts the server using the method specified in the instance profile"
 
551
        assert 0
 
552
 
 
553
    def start_async(self, finish_callback): # callback(status) where status can be success, bad_password or an error message
 
554
        try:
 
555
            # ask subclass to provide password
 
556
            password = self.get_password()
 
557
        except OperationCancelledError, exc:
 
558
            return False
 
559
        thread = threading.Thread(target=self.worker_thread, args=(self.start, password, finish_callback))
 
560
        thread.run()
 
561
        return True
 
562
    
 
563
    def worker_thread(self, action, password, finish_callback):
 
564
        try:
 
565
            action(password)
 
566
            finish_callback("success")
 
567
        except InvalidPasswordError, err:
 
568
            finish_callback("bad_password")
 
569
        except Exception, err:
 
570
            import traceback
 
571
            traceback.print_exc()
 
572
            finish_callback(err)
 
573
 
 
574
 
 
575
    def stop(self, password): # overriden by concrete subclasses, must be non-interactive
 
576
        assert 0
 
577
    
 
578
    def stop_async(self, finish_callback):
 
579
        try:
 
580
            password = self.get_password()
 
581
        except OperationCancelledError, exc:
 
582
            return False
 
583
        thread = threading.Thread(target=self.worker_thread, args=(self.stop, password, finish_callback))
 
584
        thread.run()
 
585
        return True
 
586
 
 
587
    def get_status(self, verbose=0): # overriden by concrete subclasses
 
588
        return 0
 
589
 
 
590
 
 
591
class ServerControlShell(ServerControlBase):
 
592
    def __init__(self, profile, helper, password_delegate):
 
593
        """ Can also throw OperationCancelledError"""
 
594
        ServerControlBase.__init__(self, profile, helper, password_delegate)
 
595
 
 
596
        # Ask for password here, as this class is instantiated from the main thread
 
597
        # The other methods may be instantiated from other threads, which will not work 
 
598
        # with the password dialogs
 
599
        
 
600
        # Ensure sudo password for status checks is cached. For other sudo uses,
 
601
        # the password should be requested when the action is started from the UI (ie, in the code called 
 
602
        # by the Start/Stop button in Server startup tab)
 
603
        if self.profile.use_sudo_for_status:
 
604
            password_delegate.get_password_for("service.status")
 
605
 
 
606
        self.prepare_filter = None
 
607
        self.apply_filter = None
 
608
 
 
609
 
 
610
    def set_filter_functions(self, prepare_f, apply_f):
 
611
        self.prepare_filter = prepare_f
 
612
        self.apply_filter = apply_f
 
613
 
 
614
 
 
615
    def get_password(self):
 
616
        return self.password_delegate.get_password_for("service.startstop")
 
617
 
 
618
 
 
619
    def start(self, password):
 
620
        """ Can also throw InvalidPasswordError"""
 
621
        self.info("Starting server...") # TODO: This does not come to info output
 
622
        try:
 
623
            r = self.helper.execute_command(self.profile.start_server_cmd, 
 
624
                                    as_admin=self.profile.use_sudo, 
 
625
                                    admin_password=password,
 
626
                                    output_handler=lambda s:self.info("Start server: %s"%(s if ((type(s) is str) or (type(s) is unicode)) else "").replace(password, "")))
 
627
        except InvalidPasswordError, exc:
 
628
            # forget password, so that next time it will be requested
 
629
            self.password_delegate.reset_password_for("service.startstop")
 
630
            raise exc
 
631
            
 
632
        if r:
 
633
            return True
 
634
        return False
 
635
 
 
636
 
 
637
    def stop(self, password):
 
638
        """ Can also throw InvalidPasswordError"""
 
639
        self.info("Stopping server...")
 
640
        try:
 
641
            def strip_pwd(output):
 
642
                if output is not None and (type(output) is str or type(output) is unicode):
 
643
                    if password is not None and password != "":
 
644
                        output = output.replace(password, "")
 
645
                else:
 
646
                    output = ""
 
647
                self.info("Stop server: %s"%str(output))
 
648
 
 
649
            r = self.helper.execute_command(self.profile.stop_server_cmd, 
 
650
                                    as_admin=self.profile.use_sudo, 
 
651
                                    admin_password=password,
 
652
                                    output_handler=strip_pwd)
 
653
        except InvalidPasswordError, exc:
 
654
            # forget password, so that next time it will be requested
 
655
            self.password_delegate.reset_password_for("service.startstop")
 
656
            raise exc
 
657
 
 
658
        if r:
 
659
            return True
 
660
        return False
 
661
 
 
662
 
 
663
    def get_status(self, verbose=1, password=None):
 
664
        """ Can also throw InvalidPasswordError"""
 
665
        if verbose > 1:
 
666
            output = lambda s:self.info("Check server: %s"%s)
 
667
        else:
 
668
            output = None
 
669
 
 
670
        password = None
 
671
        #unused user_gave_new_password = False
 
672
 
 
673
        script = self.profile.check_server_status_cmd
 
674
 
 
675
        if verbose > 1:
 
676
            self.info("Checking server status...")
 
677
            if self.profile.use_sudo_for_status:
 
678
                self.info("Executing: %s (using sudo)" % script)
 
679
                if not password:
 
680
                    self.info("Note: no sudo password supplied")
 
681
            else:
 
682
                self.info("Executing: %s" % script)
 
683
 
 
684
        if self.prepare_filter:
 
685
            raw_script, filters = self.prepare_filter(script)
 
686
        else:
 
687
            raw_script = script
 
688
 
 
689
        lines = []
 
690
        def collect_output(line, l=lines, dump=output):
 
691
            l.append(line)
 
692
            if dump:
 
693
                dump(line)
 
694
        try:
 
695
            result = self.helper.execute_command(raw_script, 
 
696
                                    as_admin=self.profile.use_sudo_for_status, 
 
697
                                    admin_password=password, 
 
698
                                    output_handler=collect_output)
 
699
        except InvalidPasswordError, exc:
 
700
            self.info("Invalid password")
 
701
            raise exc
 
702
 
 
703
        if self.apply_filter and filters:
 
704
            filters_text, filters_code = self.apply_filter("\n".join(lines), filters)
 
705
 
 
706
            if filters_code is not None:
 
707
                if result is not None:
 
708
                    result = int(result) or filters_code
 
709
                else:
 
710
                    result = filters_code
 
711
        else:
 
712
            #output_text = "\n".join(lines)
 
713
            pass
 
714
        
 
715
    
 
716
        if verbose > 1:
 
717
            self.info("Server check returned %s" % result)
 
718
 
 
719
        status = "unknown"
 
720
        if result == 0:
 
721
            if verbose:
 
722
                self.info("Checked server status: Server is running.")    
 
723
            status = "running"
 
724
        elif result == 1:
 
725
            if verbose:
 
726
                self.info("Checked server status: Server is stopped.")
 
727
            status = "stopped"
 
728
    
 
729
        return status
 
730
 
 
731
 
 
732
class ServerControlWMI(ServerControlBase):
 
733
    def __init__(self, profile, helper, password_delegate):
 
734
        ServerControlBase.__init__(self, profile, helper, password_delegate)
 
735
        self.wmi = grt.modules.Workbench
 
736
        self.wmi_session_ids = {}
 
737
        self.shell = None
 
738
        if self.profile.is_local:
 
739
            user = ""
 
740
            server = ""
 
741
            password = ""
 
742
            self.check_and_fix_profile_for_local_windows(profile)
 
743
            self.shell = ServerControlShell(profile, helper, password_delegate)
 
744
            # Forse usage of as_admin for local windows
 
745
            self.info("Workbench will use cmd shell commands to start/stop this instance")
 
746
        else:
 
747
            user = self.profile.wmi_username
 
748
            server = self.profile.wmi_hostname
 
749
            password = self.get_password()
 
750
        try:
 
751
            sess = self.wmi.wmiOpenSession(server, user, password or "")
 
752
            self.wmi_session_ids[currentThread()] = sess
 
753
        except Exception, exc:
 
754
            import traceback
 
755
            traceback.print_exc()
 
756
            raise RuntimeError("Could not initialize WMI interface: %s"%exc)
 
757
        if self.wmi_session_id_for_current_thread <= 0:
 
758
            raise RuntimeError("Could not initialize WMI interface")
 
759
 
 
760
 
 
761
    def check_and_fix_profile_for_local_windows(self, profile):
 
762
        settings = profile.get_settings_object()
 
763
        serverInfo = settings.serverInfo
 
764
 
 
765
        serverInfo["sys.usesudo"] = 1 # Force this in any case
 
766
        # Check commands
 
767
        service = profile.wmi_service_name
 
768
        #  return self._str_server_info("sys.mysqld.service_name")
 
769
        if service == "":
 
770
            service = serverInfo["sys.mysqld.service_name"] = "MySQL"
 
771
            self.info("MySQL service was empty. Set to 'MySQL'. Check this in 'Manage Server Instances' from 'Home'.")
 
772
 
 
773
        if profile.start_server_cmd == "" or (service not in profile.start_server_cmd):
 
774
            serverInfo["sys.mysqld.start"] = "sc start " + service
 
775
            print "WMIShell: start command set to '%s'"%profile.start_server_cmd
 
776
 
 
777
        if profile.stop_server_cmd == "" or (service not in profile.stop_server_cmd):
 
778
            serverInfo["sys.mysqld.stop"] = "sc stop " + service
 
779
            print "WMIShell: stop command set to '%s'"%profile.stop_server_cmd
 
780
 
 
781
        if profile.check_server_status_cmd == "" or (service not in profile.check_server_status_cmd):
 
782
            serverInfo["sys.mysqld.status"] = "sc query %s | wba_filter(RUNNING)"%service
 
783
            print "WMIShell: status command set to '%s'"%profile.check_server_status_cmd
 
784
 
 
785
    def set_filter_functions(self, prepare_f, apply_f):
 
786
        if self.profile.is_local and self.shell:
 
787
          print "WMIShell: Set filters for local windows cli"
 
788
          self.shell.prepare_filter = prepare_f
 
789
          self.shell.apply_filter = apply_f
 
790
 
 
791
    def get_password(self):
 
792
        if self.profile.is_local:
 
793
            return ""
 
794
        return self.password_delegate.get_password_for("service.startstop")
 
795
 
 
796
    # WMI sessions are thread specific. To make things easier, we create one for
 
797
    # each thread that uses it
 
798
    @property
 
799
    def wmi_session_id_for_current_thread(self):
 
800
        thr = currentThread()
 
801
        s = self.wmi_session_ids.get(thr)
 
802
        if s is not None:
 
803
            return s
 
804
        password = self.get_password()
 
805
 
 
806
        if self.profile.is_local:
 
807
            sess = self.wmi.wmiOpenSession('', '', '')
 
808
        else:
 
809
            sess = self.wmi.wmiOpenSession(self.profile.wmi_hostname, self.profile.wmi_username, password or "")
 
810
 
 
811
        if sess:
 
812
            self.wmi_session_ids[thr] = sess
 
813
 
 
814
        return sess
 
815
 
 
816
    def close(self):
 
817
        for s in self.wmi_session_ids.values():
 
818
            self.wmi.wmiCloseSession(s)
 
819
        self.wmi_session_ids = {}
 
820
        ServerControlBase.close() # not certain if the call is needed.
 
821
 
 
822
    def start(self, password):
 
823
        self.info("Starting server...");
 
824
        if self.shell:
 
825
            return self.shell.start(password)
 
826
        else:
 
827
            # Profile may hold the actual command passed
 
828
            service = self.profile.wmi_service_name
 
829
            action = "start"
 
830
            self.info("Starting service '%s' through WMI..." % service)
 
831
            result = self.wmi.wmiServiceControl(self.wmi_session_id_for_current_thread, service, action)
 
832
            self.info("Service start result: %s" % result)
 
833
            if result.startswith("error"):
 
834
                raise RuntimeError("Error stopping service %s through WMI\n%s" % (service, result))
 
835
            return not result.startswith("error")
 
836
 
 
837
 
 
838
    def stop(self, password):
 
839
        if self.shell:
 
840
            return self.shell.stop(password)
 
841
        else:
 
842
            service = self.profile.wmi_service_name
 
843
            action = "stop"
 
844
            self.info("Stopping service '%s' through WMI..." % service)
 
845
            result = self.wmi.wmiServiceControl(self.wmi_session_id_for_current_thread, service, action)
 
846
            self.info("Service stop result: %s" % result)
 
847
            if result.startswith("error"):
 
848
                raise RuntimeError("Error stopping service %s through WMI\n%s" % (service, result))
 
849
            return not result.startswith("error")
 
850
 
 
851
    def get_status(self, verbose=1, password=None):
 
852
        "Returned value is one of running, stopping, starting, stopped, unknown"
 
853
        service = self.profile.wmi_service_name
 
854
        action = "status"
 
855
        if verbose > 1:
 
856
            self.info("Checking service status of instance %s..." % service)
 
857
        result = self.wmi.wmiServiceControl(self.wmi_session_id_for_current_thread, service, action)
 
858
        if verbose:
 
859
            self.info("Status check of service '%s' returned %s" % (service, result))
 
860
        # translate status to something more displayable
 
861
        if result == "stop pending":
 
862
            result = "stopping"
 
863
        elif result == "start pending":
 
864
            result = "starting"
 
865
 
 
866
        return result
 
867
 
 
868
 
 
869
 
 
870
 
 
871
 
 
872
 
 
873