~ubuntu-branches/ubuntu/saucy/drizzle/saucy-proposed

« back to all changes in this revision

Viewing changes to tests/kewpie/lib/server_mgmt/server.py

  • Committer: Package Import Robot
  • Author(s): Clint Byrum
  • Date: 2012-06-19 10:46:49 UTC
  • mfrom: (1.1.6)
  • mto: This revision was merged to the branch mainline in revision 29.
  • Revision ID: package-import@ubuntu.com-20120619104649-e2l0ggd4oz3um0f4
Tags: upstream-7.1.36-stable
ImportĀ upstreamĀ versionĀ 7.1.36-stable

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#! /usr/bin/env python
 
2
# -*- mode: python; indent-tabs-mode: nil; -*-
 
3
# vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
 
4
#
 
5
# Copyright (C) 2010,2011 Patrick Crews
 
6
#
 
7
# This program is free software; you can redistribute it and/or modify
 
8
# it under the terms of the GNU General Public License as published by
 
9
# the Free Software Foundation; either version 2 of the License, or
 
10
# (at your option) any later version.
 
11
#
 
12
# This program is distributed in the hope that it will be useful,
 
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
15
# GNU General Public License for more details.
 
16
#
 
17
# You should have received a copy of the GNU General Public License
 
18
# along with this program; if not, write to the Free Software
 
19
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
20
 
 
21
 
 
22
""" server.py:  generic server object used by the server
 
23
    manager.  This contains the generic methods for all 
 
24
    servers.  Specific types (Drizzle, MySQL, etc) should
 
25
    inherit from this guy
 
26
 
 
27
"""
 
28
 
 
29
# imports
 
30
import os
 
31
import time
 
32
import subprocess
 
33
 
 
34
from lib.util.mysql_methods import execute_query
 
35
 
 
36
class Server(object):
 
37
    """ the server class from which other servers
 
38
        will inherit - contains generic methods
 
39
        certain methods will be overridden by more
 
40
        specific ones
 
41
 
 
42
    """
 
43
 
 
44
    def __init__(self
 
45
                , name
 
46
                , server_manager
 
47
                , code_tree
 
48
                , default_storage_engine
 
49
                , server_options
 
50
                , requester
 
51
                , test_executor = None
 
52
                , workdir_root = None):
 
53
        self.skip_keys = [ 'server_manager'
 
54
                         , 'system_manager'
 
55
                         , 'dirset'
 
56
                         , 'preferred_base_port'
 
57
                         , 'no_secure_file_priv'
 
58
                         , 'secure_file_string'
 
59
                         , 'port_block'
 
60
                         ]
 
61
        self.debug = server_manager.debug
 
62
        self.verbose = server_manager.verbose
 
63
        self.initial_run = 1
 
64
        self.timer_increment = .5
 
65
        self.owner = requester
 
66
        self.test_executor = test_executor
 
67
        self.server_options = server_options
 
68
        self.default_storage_engine = default_storage_engine
 
69
        self.server_manager = server_manager
 
70
        # We register with server_manager asap
 
71
        self.server_manager.log_server(self, requester)
 
72
 
 
73
        self.system_manager = self.server_manager.system_manager
 
74
        self.code_tree = code_tree
 
75
        self.version = self.code_tree.server_version
 
76
        self.type = self.code_tree.type
 
77
        self.valgrind = self.system_manager.valgrind
 
78
        self.gdb = self.system_manager.gdb
 
79
        if self.valgrind:
 
80
            self.valgrind_time_buffer = 10
 
81
        else:
 
82
            self.valgrind_time_buffer = 1
 
83
        self.cmd_prefix = self.system_manager.cmd_prefix
 
84
        self.logging = self.system_manager.logging
 
85
        self.no_secure_file_priv = self.server_manager.no_secure_file_priv
 
86
        self.name = name
 
87
        self.status = 0 # stopped, 1 = running
 
88
        self.tried_start = 0
 
89
        self.failed_test = 0 # was the last test a failure?  our state is suspect
 
90
        self.server_start_timeout = 60 * self.valgrind_time_buffer
 
91
        self.pid = None
 
92
        self.ip_address = '127.0.0.1'
 
93
        self.need_reset = False
 
94
        self.master = None
 
95
        self.need_to_set_master = False
 
96
 
 
97
        self.error_log = None
 
98
 
 
99
    def initialize_databases(self):
 
100
        """ Call schemawriter to make db.opt files """
 
101
        databases = [ 'test'
 
102
                    , 'mysql'
 
103
                    ]
 
104
        for database in databases:
 
105
            db_path = os.path.join(self.datadir,'local',database,'db.opt')
 
106
            cmd = "%s %s %s" %(self.schemawriter, database, db_path)
 
107
            self.system_manager.execute_cmd(cmd)
 
108
 
 
109
    def process_server_options(self):
 
110
        """Consume the list of options we have been passed.
 
111
           Return a string with them joined
 
112
 
 
113
        """
 
114
        
 
115
        return " ".join(self.server_options)
 
116
 
 
117
    def take_db_snapshot(self):
 
118
        """ Take a snapshot of our vardir for quick restores """
 
119
       
 
120
        self.logging.info("Taking clean db snapshot...")
 
121
        if os.path.exists(self.snapshot_path):
 
122
            # We need to remove an existing path as python shutil
 
123
            # doesn't want an existing target
 
124
            self.system_manager.remove_dir(self.snapshot_path)
 
125
        self.system_manager.copy_dir(self.datadir, self.snapshot_path)
 
126
 
 
127
    def restore_snapshot(self):
 
128
        """ Restore from a stored snapshot """
 
129
        
 
130
        if not os.path.exists(self.snapshot_path):
 
131
            self.logging.error("Could not find snapshot: %s" %(self.snapshot_path))
 
132
        self.system_manager.remove_dir(self.datadir)
 
133
        self.system_manager.copy_dir(self.snapshot_path, self.datadir)
 
134
        
 
135
    def is_started(self):
 
136
        """ Is the server running?  Particulars are server-dependent """
 
137
 
 
138
        return "You need to implement is_started"
 
139
 
 
140
    def get_start_cmd(self):
 
141
        """ Return the command the server_manager can use to start me """
 
142
 
 
143
        return "You need to implement get_start_cmd"
 
144
 
 
145
    def get_stop_cmd(self):
 
146
        """ Return the command the server_manager can use to stop me """
 
147
 
 
148
        return "You need to implement get_stop_cmd"
 
149
 
 
150
    def get_ping_cmd(self):
 
151
        """ Return the command that can be used to 'ping' me 
 
152
            Very similar to is_started, but different
 
153
 
 
154
            Determining if a server is still running (ping)
 
155
            may differ from the method used to determine
 
156
            server startup
 
157
 
 
158
        """
 
159
   
 
160
        return "You need to implement get_ping_cmd"
 
161
 
 
162
    def set_master(self, master_server, get_cur_log_pos = True):
 
163
        """ Do what is needed to set the server to replicate
 
164
            / consider the master_server as its 'master'
 
165
 
 
166
        """
 
167
 
 
168
        return "You need to implement set_master"
 
169
 
 
170
    def cleanup(self):
 
171
        """ Cleanup - just free ports for now..."""
 
172
        self.system_manager.port_manager.free_ports(self.port_block)
 
173
 
 
174
    def set_server_options(self, server_options):
 
175
        """ We update our server_options to the new set """
 
176
        self.server_options = server_options
 
177
 
 
178
    def reset(self):
 
179
        """ Voodoo to reset ourselves """
 
180
        self.failed_test = 0
 
181
        self.need_reset = False
 
182
 
 
183
    def get_numeric_server_id(self):
 
184
        """ Return the integer value of server-id
 
185
            Mainly for mysql / percona, but may be useful elsewhere
 
186
 
 
187
        """
 
188
 
 
189
        return int(self.name.split(self.server_manager.server_base_name)[1])
 
190
 
 
191
    def start(self, working_environ=None, expect_fail=0):
 
192
        """ Start an individual server and return
 
193
            an error code if it did not start in a timely manner
 
194
 
 
195
            Start the server, using the options in option_list
 
196
            as well as self.standard_options
 
197
            
 
198
            if expect_fail = 1, we know the server shouldn't 
 
199
            start up
 
200
 
 
201
        """
 
202
        # set pid to None for a new start
 
203
        self.pid = None
 
204
        # get our current working environment
 
205
        if not working_environ:
 
206
            working_environ = self.test_executor.working_environment
 
207
        # take care of any environment updates we need to do
 
208
        self.server_manager.handle_environment_reqs(self, working_environ)
 
209
 
 
210
        self.logging.verbose("Starting server: %s.%s" %(self.owner, self.name))
 
211
        start_cmd = self.get_start_cmd()
 
212
        self.logging.debug("Starting server with:")
 
213
        self.logging.debug("%s" %(start_cmd))
 
214
        # we signal we tried to start as an attempt
 
215
        # to catch the case where a server is just 
 
216
        # starting up and the user ctrl-c's
 
217
        # we don't know the server is running (still starting up)
 
218
        # so we give it a few
 
219
        #self.tried_start = 1
 
220
        error_log = open(self.error_log,'w')
 
221
        if start_cmd: # It will be none if --manual-gdb used
 
222
            if not self.server_manager.gdb:
 
223
                server_subproc = subprocess.Popen( start_cmd
 
224
                                                 , shell=True
 
225
                                                 , env=working_environ
 
226
                                                 , stdout=error_log
 
227
                                                 , stderr=error_log
 
228
                                                 )
 
229
                server_subproc.wait()
 
230
                server_retcode = server_subproc.returncode
 
231
            else: 
 
232
                # This is a bit hackish - need to see if there is a cleaner
 
233
                # way of handling this
 
234
                # It is annoying that we have to say stdout + stderr = None
 
235
                # We might need to further manipulate things so that we 
 
236
                # have a log
 
237
                server_subproc = subprocess.Popen( start_cmd
 
238
                                                 , shell=True
 
239
                                                 , env = working_environ
 
240
                                                 , stdin=None
 
241
                                                 , stdout=None
 
242
                                                 , stderr=None
 
243
                                                 , close_fds=True
 
244
                                                 )
 
245
        
 
246
                server_retcode = 0
 
247
        else:
 
248
            # manual-gdb issue
 
249
            server_retcode = 0
 
250
        
 
251
        timer = float(0)
 
252
        timeout = float(self.server_start_timeout)
 
253
 
 
254
        #if server_retcode: # We know we have an error, no need to wait
 
255
        #    timer = timeout
 
256
        while not self.is_started() and timer != timeout:
 
257
            time.sleep(self.timer_increment)
 
258
            # If manual-gdb, this == None and we want to give the 
 
259
            # user all the time they need
 
260
            if start_cmd:
 
261
                timer= timer + self.timer_increment
 
262
            
 
263
        if timer == timeout and not self.ping(quiet=True):
 
264
            self.logging.error(( "Server failed to start within %d seconds.  This could be a problem with the test machine or the server itself" %(timeout)))
 
265
            server_retcode = 1
 
266
     
 
267
        if server_retcode == 0:
 
268
            self.status = 1 # we are running
 
269
            if os.path.exists(self.pid_file):
 
270
                with open(self.pid_file,'r') as pid_file:
 
271
                    pid = pid_file.readline().strip()
 
272
                    pid_file.close()
 
273
                self.pid = pid
 
274
 
 
275
        if server_retcode != 0 and not expect_fail:
 
276
            self.logging.error("Server startup command: %s failed with error code %d" %( start_cmd
 
277
                                                                                  , server_retcode))
 
278
            self.logging.error("Dumping error log: %s" %(self.error_log))
 
279
            with open(self.error_log,'r') as errlog:
 
280
                for line in errlog:
 
281
                    self.logging.error(line.strip())
 
282
        elif server_retcode == 0 and expect_fail:
 
283
        # catch a startup that should have failed and report
 
284
            self.logging.error("Server startup command :%s expected to fail, but succeeded" %(start_cmd))
 
285
 
 
286
        self.tried_start = 0 
 
287
        if self.need_to_set_master:
 
288
            # TODO handle a bad slave retcode
 
289
            slave_retcode = self.set_master(self.master)
 
290
        return server_retcode ^ expect_fail
 
291
 
 
292
    def ping(self, quiet=False):
 
293
        """ Ping / check if the server is alive 
 
294
            Return True if server is up and running, False otherwise
 
295
        """
 
296
 
 
297
        ping_cmd = self.get_ping_cmd()
 
298
        if not quiet:
 
299
            self.logging.info("Pinging %s server on port %d" % (self.type.upper(), self.master_port))
 
300
        (retcode, output)= self.system_manager.execute_cmd(ping_cmd, must_pass = 0)
 
301
        return retcode == 0
 
302
             
 
303
 
 
304
    def stop(self):
 
305
        """ Stop an individual server if it is running """
 
306
        if self.tried_start:
 
307
            # we expect that we issued the command to start
 
308
            # the server but it isn't up and running
 
309
            # we kill a bit of time waiting for it
 
310
            attempts_remain = 10
 
311
            while not self.ping(quiet=True) and attempts_remain:
 
312
                time.sleep(1)
 
313
                attempts_remain = attempts_remain - 1
 
314
        # Now we try to shut the server down
 
315
        if self.ping(quiet=True):
 
316
            self.logging.verbose("Stopping server %s.%s" %(self.owner, self.name))
 
317
            stop_cmd = self.get_stop_cmd()
 
318
            self.logging.debug("with shutdown command:\n %s" %(stop_cmd))
 
319
            #retcode, output = self.system_manager.execute_cmd(stop_cmd)
 
320
            shutdown_subproc = subprocess.Popen( stop_cmd
 
321
                                               , shell=True
 
322
                                               )
 
323
            shutdown_subproc.wait()
 
324
            shutdown_retcode = shutdown_subproc.returncode
 
325
            # We do some monitoring for the server PID and kill it
 
326
            # if need be.  This is a bit of a band-aid for the 
 
327
            # zombie-server bug on Natty : (  Need to find the cause.
 
328
            attempts_remain = 100
 
329
            while self.system_manager.find_pid(self.pid) and attempts_remain:
 
330
                time.sleep(1)
 
331
                attempts_remain = attempts_remain - 1
 
332
                if not attempts_remain: # we kill the pid
 
333
                    if self.verbose:
 
334
                        self.logging.warning("Forcing kill of server pid: %s" %(server.pid))
 
335
                    self.system_manager.kill_pid(self.pid)
 
336
            if shutdown_retcode:
 
337
                self.logging.error("Problem shutting down server:")
 
338
                self.logging.error("%s" %(shutdown_retcode))
 
339
                self.status = 0
 
340
            else:
 
341
                self.status = 0 # indicate we are shutdown
 
342
        else:
 
343
            # make sure the server is indicated as stopped
 
344
            self.status = 0
 
345
 
 
346
    def die(self):
 
347
        """ This causes us to kill the server pid """
 
348
        self.system_manager.kill_pid(self.get_pid())
 
349
 
 
350
    def get_pid(self):
 
351
        """ We check our pid file and get what is there """
 
352
        if os.path.exists(self.pid_file):
 
353
            with open(self.pid_file,'r') as pid_file:
 
354
                pid = pid_file.readline().strip()
 
355
                pid_file.close()
 
356
            self.pid = pid
 
357
            return self.pid
 
358
 
 
359
    def get_engine_info(self):
 
360
            """ Check innodb / xtradb version """
 
361
 
 
362
            innodb_version = None
 
363
            xtradb_version = None
 
364
        #if not self.code_tree.version_checked:
 
365
            query = "SHOW VARIABLES LIKE 'innodb_version'"
 
366
            retcode, result = execute_query(query, self)
 
367
            # result format = (('innodb_version', '1.1.6-20.1'),)
 
368
            if result:
 
369
                innodb_version = result[0][1]
 
370
                split_data = innodb_version.split('-')
 
371
                if len(split_data) > 1:
 
372
                    xtradb_version = split_data[-1]
 
373
            self.code_tree.version_checked = True
 
374
            self.code_tree.innodb_version = innodb_version
 
375
            self.code_tree.xtradb_version = xtradb_version 
 
376
            return innodb_version, xtradb_version
 
377
 
 
378
    def dump_errlog(self):
 
379
        with open(self.error_log,'r') as errlog:
 
380
            data = errlog.readlines()
 
381
        return ''.join(data) 
 
382