blob: a6c8c361c4dca28261690a2beb2ca56007226a20 [file] [log] [blame]
#!/usr/bin/env python
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
# This is a common util lib for gpmigrator and gpmigrator_mirror.
#
import pickle, base64
import subprocess, csv
import optparse, tempfile, re, traceback
from datetime import date
from time import localtime, strftime, sleep, time
from threading import Thread
from Queue import Queue
import urllib # for passing strings across gpssh
import shutil # for copying
from pygresql import pg # Database interaction
from gppylib.gplog import * # Greenplum logging facility
from gppylib.commands import base # Greenplum layer for worker pools
from gppylib.commands import unix # Greenplum layer for unix interaction
from gppylib.commands.gp import GpCreateDBIdFile
from gppylib.db import dbconn
from gppylib.gpversion import GpVersion
from gppylib.gparray import GpArray, GpDB
from gppylib.gphostcache import GpHostCache
from gppylib.gpcoverage import GpCoverage
from gppylib.operations.gpMigratorUtil import *
libdir = os.path.join(sys.path[0], 'lib/')
logger = get_default_logger()
MIGRATIONUSER = 'gpmigrator'
LOCKEXT = '.gpmigrator_orig'
WORKDIR = 'gpmigrator'
BACKUPDIR = 'backup'
UPGRADEDIR = 'upgrade'
PARALLELISM = 16
#============================================================
def make_conn(ug, user, db, options, port, sockdir):
retries = 5
for i in range(retries):
try:
logger.debug("making database connection: user = %s, "
"dbname = %s port = %i"
% (user, db, port))
conn = pg.connect(user=user,
dbname=db,
opt=options,
port=port)
break
except pg.InternalError, e:
if 'too many clients already' in str(e) and i < retries:
logger.warning('Max Connection reached, attempt %d / %d' % (i+1, retries))
sleep(2)
continue
raise ConnectionError(str(e))
return conn
#============================================================
def cli_help(execname):
help_path = os.path.join(sys.path[0], '..', 'docs', 'cli_help', execname + '_help');
f = None
try:
try:
f = open(help_path);
return f.read(-1)
except:
return ''
finally:
if f: f.close()
#============================================================
def usage(execname):
print cli_help(execname) or __doc__
#============================================================
class ConnectionError(StandardError): pass
class UpgradeError(StandardError): pass
class CmdError(StandardError):
def __init__(self, cmd, stdout, stderr):
self.cmd = cmd
self.stdout = stdout
self.stderr = stderr
def __str__(self):
return self.stderr
#============================================================
def is_supported_version(version, upgrade=True):
'''
Checks that a given GpVersion Object is supported for upgrade/downgrade.
We do not support:
Versions < PREVIOUS MINOR RELEASE
Versions > main
Versions with unusual "builds" (eg 3.4.0.0_EAP1)
'''
if upgrade:
upstr = "upgrade"
else:
upstr = "downgrade"
# Acceptable builds are 'dev', 'filerep', and any build that consists
# entirely of digits
build = version.getVersionBuild()
if not re.match(r"(dev|filerep|\d+|build|Preview_v1)", build):
raise UpgradeError(
"HAWQ '%s' is not supported for %s"
% (str(version), upstr))
if version >= "1.0.0.0" and version <= 'main':
return True
if version < "1.0.0.0":
raise UpgradeError(
"HAWQ '%s' is not supported for %s"
% (str(version), upstr))
else:
raise UpgradeError(
"To %s HAWQ '%s' use the %s tool "
"shipped with that release"
% (upstr, str(version), upstr))
#============================================================
class GpUpgradeCmd(base.Command):
def __init__(self, name, cmd, ctxt=base.LOCAL, remoteHost=None):
cmdStr = ' '.join(cmd)
base.Command.__init__(self, name, cmdStr, ctxt, remoteHost)
#============================================================
class GPUpgradeBase(object):
def __init__(self):
self.cmd = 'MASTER' # Command being run
self.array = None
self.dbs = {} # dict of databases in the array
self.dbup = None
self.debug = False
self.faultinjection = None
self.hostcache = None
self.interrupted = False # SIGINT recieved
self.logdir = None
self.logfile = None
self.masterdir = None # Master Data Directory
self.masterport = 5432 # Master Port
self.mirrors = [] # list of mirrors
self.newenv = None # enviornment: new gp env
self.newhome = None # New gphome/bin directory
self.oldenv = None # enviornment: old gp env
self.oldhome = None # Old gphome/bin directory
self.option = None # Argument passed to cmd
self.path = None # Default path
self.pool = None # worker pool
self.user = None # db admin user
self.workdir = None # directory: masterdir/gpmigrator
self.checkonly = False # checkcat only; do not run upgrade
#------------------------------------------------------------
def RunCmd(self, cmd, env=None, utility=False, supressDebug=False, shell=False):
'''
Runs a single command on this machine
'''
if type(cmd) in [type('string'), type(u'unicode')]:
cmdstr = cmd
cmd = cmd.split(' ')
elif type(cmd) == type(['list']):
cmdstr = ' '.join(cmd)
else:
logger.warn("Unknown RunCmd datatype '%s'" % str(type(cmd)))
cmdstr = str(cmd)
if self.debug and not supressDebug:
logger.debug("ENV: " + str(env))
logger.debug("CMD: " + cmdstr)
# If utility mode has been specified add role to env
if env == None:
env = {'PATH': self.path}
if utility:
env['PGOPTIONS'] = '-c gp_session_role=utility'
try:
pipe = subprocess.Popen(cmd, env=env,
shell=shell,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
close_fds=True)
result = pipe.communicate();
except OSError, e:
raise CmdError(cmdstr, '', str(e))
if self.debug and not supressDebug:
logger.debug(result[1])
if pipe.returncode:
raise CmdError(' '.join(cmd), result[0], result[1])
else:
return result[0].strip();
#------------------------------------------------------------
def SetupEnv(self, gphome, masterdir):
'''
Sets up environment variables for Greenplum Administration
'''
home = os.environ.get('HOME')
lpath = os.environ.get('LD_LIBRARY_PATH')
dypath = os.environ.get('DYLD_LIBRARY_PATH')
# Add $GPHOME/bin to the path for this environment
path = '%s/bin:%s/ext/python/bin:%s' % (gphome, gphome, self.path)
if lpath:
lpath = '%s/lib:%s/ext/python/lib:%s' % (gphome, gphome, lpath)
else:
lpath = '%s/lib:%s/ext/python/lib' % (gphome, gphome)
if dypath:
dypath = '%s/lib:%s/ext/python/lib:%s' % (gphome, gphome, dypath)
else:
dypath = '%s/lib:%s/ext/python/lib' % (gphome, gphome)
env = {}
env['HOME'] = home
env['USER'] = self.user
env['LOGNAME'] = self.user
env['GPHOME'] = gphome
env['PATH'] = path
env['LD_LIBRARY_PATH'] = lpath
env['DYLD_LIBRARY_PATH'] = dypath
env['PYTHONPATH'] = os.path.join(gphome, 'lib', 'python')
env['PYTHONHOME'] = os.path.join(gphome, 'ext', 'python')
if masterdir:
env['MASTER_DATA_DIRECTORY'] = masterdir
env['PGPORT'] = str(self.masterport)
return env
#------------------------------------------------------------
def Select(self, qry, db='template0', port=None, forceutility=False, forceUseEnvUser=False):
# forceUseEnvUser: user will be GPMIGRATOR if we're in lockdown mode(gpmigrator); otherwise
# it'll be the env user. Setting forceuseEnvUser will always use env user,
# even though we're in gpmigrator and in lockdown.
'''
Execute a SQL query and return an array of result rows
+ Single columns will be returned as an array of values
+ Multiple columns will be returned as an array of tuples
'''
#
# If we are executing from one of the segments then we don't
# actually know where the data directory is for the segment
# is so we can't check if we are in lockdown mode
#
# Fortunately we never actually lock the segments, so we can
# always run as current user.
#
if self.cmd == 'MASTER':
if not self.dbup:
raise UpgradeError('database is down')
[env, utility, upgrade] = self.dbup
locked = os.path.join(env['MASTER_DATA_DIRECTORY'],
'pg_hba.conf'+LOCKEXT)
if os.path.exists(locked) and not forceUseEnvUser:
user = MIGRATIONUSER
else:
user = env['USER']
else:
# If we lockdown on segments this needs to change, some method
# would need to be made to determine if the segment in question
# is locked
user = self.user
utility = True
if port == None:
port = self.masterport
elif port != self.masterport:
utility = True
if utility or forceutility:
options = '-c gp_session_role=utility'
else:
options = ''
if self.__dict__.get('sock_dir'):
sockdir = self.sock_dir
else:
sockdir = '/tmp/'
conn = make_conn(self, user, db, options, port, sockdir)
curs = None
logger.debug('executing query ' + qry)
try:
curs = conn.query(qry)
except Exception, e:
conn.close()
logger.fatal('Error executing SQL')
raise ConnectionError('%s\nsql> %s' % (str(e), qry))
rows = []
for tuple in curs.dictresult():
if len(tuple) == 1:
rows.append(tuple.values()[0])
else:
rows.append(tuple)
conn.close()
return rows
#------------------------------------------------------------
def Update(self, qry, db='template0', port=None, forceutility=False, upgradeMode=False, modSysTbl=False, forceUseEnvUser=False, defSysTbl=False):
# forceUseEnvUser: user will be GPMIGRATOR if we're in lockdown mode(gpmigrator); otherwise
# it'll be the env user. Setting forceuseEnvUser will always use env user,
# even though we're in gpmigrator and in lockdown.
'''
Execute a SQL query expecting no output
'''
# If we are executing from one of the segments then we don't
# have the current database state, so we have to know what to
# do via other means.
#
# The assumption is:
# A) utility = true
# B) pguser = MIGRATIONUSER
#
# If we ever need to run as the non-upgrade user from a segment
# this code will obviously need to change
#
if self.cmd == 'MASTER':
if not self.dbup:
raise UpgradeError('database is down')
[env, utility, upgrade] = self.dbup
locked = os.path.join(env['MASTER_DATA_DIRECTORY'],
'pg_hba.conf'+LOCKEXT)
if os.path.exists(locked) and not forceUseEnvUser:
user = MIGRATIONUSER
else:
user = env['USER']
else:
# If we lockdown on segments this needs to change, some method
# would need to be made to determine if the segment in question
# is locked
user = self.user
utility = True
if port == None:
port = self.masterport
options = ' '
if modSysTbl and defSysTbl: options += ' -c allow_system_table_mods=all'
elif modSysTbl: options += ' -c allow_system_table_mods=dml'
elif defSysTbl: options += ' -c allow_system_table_mods=ddl'
if utility or forceutility: options += ' -c gp_session_role=utility'
if upgradeMode: options += ' -c gp_maintenance_conn=true'
if self.__dict__.get('sock_dir'):
sockdir = self.sock_dir
else:
sockdir = '/tmp/'
conn = make_conn(self, user, db, options, port, sockdir)
logger.debug('executing query ' + qry)
try:
conn.query(qry)
except Exception, e:
logger.fatal('Error executing SQL')
raise ConnectionError('%s\nsql> %s' % (str(e), qry))
finally:
conn.close()
#------------------------------------------------------------
def CheckUp(self, tryconn = False):
'''
Checks if one of the postmasters is up and running.
Return the environment of the running server
'''
olddir = self.oldenv['MASTER_DATA_DIRECTORY']
newdir = self.newenv['MASTER_DATA_DIRECTORY']
env = None
if olddir and os.path.exists('%s/postmaster.pid' % olddir):
logger.debug('CheckUp: found old postmaster running')
env = self.oldenv
if newdir and os.path.exists('%s/postmaster.pid' % newdir):
logger.debug('CheckUp: found new postmaster running')
env = self.newenv
if not env:
logger.info('Postmaster not running')
raise UpgradeError("Postmaster failed to start")
if tryconn:
try:
logger.debug('trying to establish connection to database')
logger.debug('user = %s, db = template0, port = %i, dir = %s'
% (self.user, self.masterport, self.sock_dir))
conn = make_conn(self, self.user, 'template0', '', self.masterport,
self.sock_dir)
except Exception:
raise UpgradeError("Found a postmaster.pid but postmaster " +
"running")
conn.close()
return env
#------------------------------------------------------------
def CheckDown(self, warn=False):
'''
Checks that neither postmaster is running and that the database
was cleanly shutdown.
'''
if self.cmd == 'MASTER':
datadirs = [ self.masterdir ]
else:
datadirs = self.datadirs
shutdown_re = re.compile('Database cluster state: *(.*)')
for oldseg in datadirs:
(d, content) = os.path.split(oldseg)
newseg = os.path.join(d, WORKDIR, UPGRADEDIR, content)
# Only check the upgrade directory if its not legacy
if self.datadirs:
dirs = [oldseg, newseg]
else:
dirs = [oldseg]
for dir in dirs:
if dir == oldseg: env = self.oldenv
else: env = self.newenv
# upgrade directory might not actually exist
if not os.path.isdir(dir):
continue
pid = os.path.join(dir, 'postmaster.pid')
if os.path.exists(pid):
raise UpgradeError("Greenplum process running: " + pid)
shutdown = self.RunCmd('pg_controldata ' + dir, env=env)
for line in shutdown.split('\n'):
m = shutdown_re.match(line)
if m:
if m.group(1) == 'shut down':
break
msg = 'pg_controldata: "Database cluster state: %s"\n' % m.group(1)
msg += 'Greenplum segment %s did not shutdown cleanly' % dir
if warn:
logger.warn(msg)
else:
raise UpgradeError(msg)
if self.cmd == 'MASTER' and self.datadirs:
self.CallSlaves('CHKDOWN')
return True
#------------------------------------------------------------
# In hawq2.0, because command changed, now utility and upgrade mode are conflict
# And also upgrade mode changed to only start master, because segment catalog will be re-init
def Startup(self, env, utility=False, upgrade=False):
'''
Starts up the specified database
'''
if self.dbup:
raise UpgradeError('database already started')
self.dbup = [env, utility, upgrade]
isu = ""
if utility:
isu = " in utility mode"
elif upgrade:
isu = " in upgrade mode"
if (env == self.oldenv):
logger.info('Starting old Greenplum postmaster%s' % isu)
else:
logger.info('Starting new Greenplum postmaster%s' % isu)
try:
if (env == self.oldenv):
cmd = "gpstart -a"
else:
#upgrade mode in hawq2.0 only start master node.
if utility or upgrade:
cmd = "hawq start master -a"
else:
cmd = "hawq start cluster -a"
if utility:
cmd = cmd +' -m'
env['GPSTART_INTERNAL_MASTER_ONLY'] = '1'
if upgrade: cmd = cmd +' -U upgrade'
cmd += ' -l %s' % self.logdir
locked = os.path.join(env['MASTER_DATA_DIRECTORY'],
'pg_hba.conf'+LOCKEXT)
try:
if os.path.exists(locked):
env['PGUSER'] = MIGRATIONUSER
logger.debug("lockfile: '%s' exists" % locked)
else:
logger.debug("lockfile: '%s' does not exist" % locked)
logger.debug("Starting cluster with env = %s" % str(env))
pid = subprocess.Popen(cmd, preexec_fn=os.setpgrp,
env=env, shell=True,
stdout=self.logfile,
stderr=self.logfile,
close_fds=True)
finally:
if os.path.exists(locked):
del env['PGUSER']
# Ignore interrupt requests until startup is done
error = None
retcode = None
while retcode == None:
try:
retcode = pid.wait();
except KeyboardInterrupt, e:
if not self.interrupted:
logger.fatal('***************************************')
logger.fatal('SIGINT-: Upgrade Aborted')
logger.fatal('***************************************')
logger.info( 'Performing clean abort')
self.interrupted = True
error = e
else:
logger.info( 'SIGINT-: Still processing shutdown')
if retcode < 0:
raise UpgradeError("Startup terminated by signal");
today = date.today().strftime('%Y%m%d')
logname = os.path.join(self.logdir, 'gpstart_%s.log' % today)
if retcode == 1:
logger.warn('***************************************')
logger.warn('Warnings generated starting cluster')
logger.warn('Check %s for detailed warnings' % logname)
logger.warn('***************************************')
if retcode > 1:
logger.fatal('***************************************')
logger.fatal('Startup failed with error code %d' % retcode)
logger.fatal('Check %s for detailed warnings' % logname)
logger.fatal('***************************************')
raise UpgradeError('Startup failed')
# If we recieved an interrupt, resignal it now that the startup is done
if error:
raise error
except OSError, e:
logger.fatal(str(e))
raise UpgradeError('Startup failed')
self.CheckUp();
#------------------------------------------------------------
def Shutdown(self):
'''
Stops the specified database
'''
if not self.dbup:
return
[env, utility, upgrade] = self.dbup
dir = env['MASTER_DATA_DIRECTORY']
if not os.path.exists('%s/postmaster.pid' % dir):
logger.warn(
'Shutdown skipped - %s/postmaster.pid not found' % dir)
return
if (env == self.oldenv):
logger.info('Shutting down old Greenplum postmaster')
else:
logger.info('Shutting down new Greenplum postmaster')
locked = os.path.join(env['MASTER_DATA_DIRECTORY'],
'pg_hba.conf'+LOCKEXT)
try:
# Note on arguments to gpstop:
# This code has gone back and forth on -s vs -f for shutdown:
#
# -f aborts active connections. This is a good thing. If
# a user or script snuck in a connection before we were able
# to establish the lockdown then we want to abort that
# connection otherwise it will cause the upgrade to fail and
# that is bad.
#
# Prior versions would sometimes issue a kill for fast
# shutdown, this is a problem since we need the database
# shutdown cleanly with no pending xlog transactions.
# Because of that we switched to -s.
#
# -s causes problems because it is a stop that will fail if a
# session is connected and we want to abort active sessions.
# Because of that we switched back to -f.
#
# The belief is currently that the current version of gpstop
# should be good with passing -f. To help safeguard this belief
# there is a check when we set the catalog version to ensure
# that the database shutdown cleanly.
#
# If this needs to be changed again please read the above,
# consider what happens if you try to upgrade with an active
# connection open to the database, and procede cautiously.
if (env == self.oldenv):
if utility: cmd = 'gpstop -a -f -m'
else: cmd = 'gpstop -a -f'
else:
if utility or upgrade: cmd = 'hawq stop master -a -M fast'
else: cmd = 'hawq stop cluster -a -M fast'
cmd += ' -l %s' % self.logdir
locked = os.path.join(env['MASTER_DATA_DIRECTORY'],
'pg_hba.conf'+LOCKEXT)
try:
if os.path.exists(locked):
env['PGUSER'] = MIGRATIONUSER
logger.debug('handling locked shutdown')
logger.debug('shutting down with env: %s' % str(env))
logger.debug('shutting down with command: %s' % cmd)
pid = subprocess.Popen(cmd, preexec_fn=os.setpgrp,
env=env, shell=True,
stdout=self.logfile,
stderr=self.logfile,
close_fds=True)
finally:
if os.path.exists(locked):
del env['PGUSER']
self.dbup = None
# Ignore interrupt requests until shutdown is done
error = None
retcode = None
while retcode == None:
try:
retcode = pid.wait();
except KeyboardInterrupt, e:
if not self.interrupted:
logger.fatal('***************************************')
logger.fatal('SIGINT-: Upgrade Aborted')
logger.fatal('***************************************')
logger.info( 'Performing clean abort')
self.interrupted = True
error = e
else:
logger.info( 'SIGINT-: Still processing shutdown')
if retcode < 0:
raise UpgradeError("Shutdown terminated by signal");
today = date.today().strftime('%Y%m%d')
logname = os.path.join(self.logdir, 'gpstop_%s.log' % today)
if retcode == 1:
logger.warn('***************************************')
logger.warn('Warnings generated stopping cluster')
logger.warn('Check %s for detailed warnings' % logname)
logger.warn('***************************************')
if retcode > 1:
logger.fatal('***************************************')
logger.fatal('Shutdown failed with error code %d' % retcode)
logger.fatal('Check %s for detailed error' % logname)
logger.fatal('***************************************')
raise UpgradeError('Shutdown failed')
# If we recieved an interrupt, resignal it now that the startup is done
if error:
raise error
except OSError, e:
logger.fatal(str(e))
raise UpgradeError('Shutdown failed')
self.CheckDown();
#------------------------------------------------------------
def PreUpgradeCheck(self):
'''
Check master/segment binary version, GUC, free space, catalog
'''
self.CheckBinaryVersion()
self.CheckGUCs()
# XXX: cannot rely on gp_toolkit to check free disk space
# self.CheckFreeSpace()
# XXX: gpcheckcat not yet designed for HAWQ
# self.CheckCatalog()
#------------------------------------------------------------
def CheckBinaryVersion(self):
'''
Validate that the correct binary is installed in all segments
'''
logger.info('Checking Segment binary version')
hosts = self.Select("select distinct hostname from gp_segment_configuration");
masterversion = self.getversion(self.newhome, self.newenv)
cmdStr = '%s/bin/pg_ctl --hawq-version' % self.newhome
for uh in hosts:
cmd = base.Command(uh, cmdStr, base.REMOTE, uh)
self.pool.addCommand(cmd)
self.pool.join()
items = self.pool.getCompletedItems()
for i in items:
if i.results.rc:
logger.error("error on host %s with error: %s" % (i.remoteHost, i.results.stderr))
raise UpgradeError('Cannot verify segment GPDB binary')
if not i.results.stdout:
logger.error("could not find version string from host %s with command: %s" % (i.remoteHost, cmdStr))
raise UpgradeError('Cannot verify segment GPDB binary')
version_string = i.results.stdout.strip()
if version_string != masterversion:
logger.error("version string on host %s: '%s' does not match expected: '%s'" % (i.remoteHost, version_string, masterversion))
raise UpgradeError('Master/Segment binary mismatch')
#------------------------------------------------------------
def CheckGUCs(self):
'''
Validate that the GUCs are set properly
- gp_external_enable_exec must be set to true
'''
logger.info('Checking GUCs')
disable_exec_ext = self.Select("select current_setting('gp_external_enable_exec')");
if (disable_exec_ext[0] == 'off'):
logger.fatal('***************************************')
logger.fatal("gp_external_enable_exec is set to 'false'.")
logger.fatal("Please set gp_external_enable_exec to 'true'.")
logger.fatal('***************************************')
raise UpgradeError('Invalid GUC value')
#------------------------------------------------------------
def CheckFreeSpace(self):
'''
Validate that we've enough space for upgrade.
Right now, we require 2GB. It's arbitrary.
'''
logger.info('Checking Free Space')
nospacesegs = self.Select("select dfsegment from gp_toolkit.gp_disk_free where dfspace::float/1024/1024 < 2")
if (len(nospacesegs)>0):
logger.fatal('***************************************')
logger.fatal('The following segment ID has less than 2GB of free space.')
logger.fatal('Please make sure that there is at least 2GB of free space on each segment before upgrade.')
logger.fatal(nospacesegs)
logger.fatal('***************************************')
raise UpgradeError('Insufficient Space on Segment')
df=unix.DiskFree.get_disk_free_info_local('gpmigrator_check_freespace', self.masterdir)
mdf = float(df[3])/1024/1024
if (mdf < 2):
logger.fatal('***************************************')
logger.fatal('The Master data directory has only %sGB of free space.' % mdf)
logger.fatal('Please make sure that there is at least 2GB of free space on the master.')
logger.fatal('***************************************')
raise UpgradeError('Insufficient Space on Master')
#------------------------------------------------------------
def CheckCatalog(self):
'''
Validate that the created catalog looks good
'''
logger.info('Checking Catalog')
self.CheckUp()
[env, utility, upgrade] = self.dbup
locked = os.path.join(env['MASTER_DATA_DIRECTORY'],
'pg_hba.conf'+LOCKEXT)
if os.path.exists(locked):
user = MIGRATIONUSER
else:
user = env['USER']
exec_cmd = libdir + '/gpcheckcat -p %d -U %s -B %i ' % \
(self.masterport, user, PARALLELISM)
oids = sorted(self.dbs.keys())
for dboid in oids:
db = self.dbs[dboid]
# Because hawq1.x allow user to connnect template0, here we can't skip
#if db == 'template0':
#continue
if db == 'gpperfmon':
self.Checkgpperfmon()
cmd = exec_cmd + db
logger.info('... Checking ' + db)
logger.debug("CMD: " + cmd)
outfilename = os.path.join(self.workdir, 'gpcheckcat_%s.log' % db)
outfilename = outfilename.replace(' ', '_')
outfile = open(outfilename, 'w')
pipe = subprocess.Popen(cmd, shell=True, stdout=outfile, stderr=outfile,close_fds=True)
retcode = pipe.wait()
if retcode < 0:
raise UpgradeError('Catalog Check terminated by signal')
if retcode > 0:
raise UpgradeError('Catalog Check Failed - see %s for details' % outfilename)
#------------------------------------------------------------
def Checkgpperfmon(self):
'''
gpperfmon validation check
1. move outstanding flat file data to data.<time> dir
2. dry run the upgrade script and then rollback
'''
try:
logger.info("Checking gpperfmon")
perfdatadir = os.path.join(self.masterdir, "gpperfmon", "data")
if (os.path.isdir(perfdatadir)):
perfdatabkup = os.path.join(self.masterdir, "gpperfmon", "data.%s" % time())
os.rename(perfdatadir, perfdatabkup)
# test run gpperfmon upgrade script
rolname = self.Select("select rolname from pg_authid where oid=10")[0]
fname = os.path.join(self.newhome, "lib/gpperfmon/gpperfmon42.sql")
sql = open(fname, 'r').read()
sql = "BEGIN;\nSET SESSION AUTHORIZATION %s;\n%s\nROLLBACK;" % (rolname, sql)
self.Update(sql, db='gpperfmon')
except BaseException, e:
sys.stderr.write(traceback.format_exc())
sys.stderr.write(str(e))
raise e
#------------------------------------------------------------
def PerformPostUpgrade(self):
'''
Handles various post upgrade tasks including:
- Populates the hawq_toolkit schema
- Performs updates for the gpperfmon database
'''
try:
# Get the admin role and all the databases
logger.info("Installing hawq_toolkit")
rolname = self.Select("select rolname from pg_authid where oid=10")[0]
# Read the toolkit sql file into memory
fname = '%s/share/postgresql/gp_toolkit.sql' % self.newhome
sql = open(fname, 'r').read()
# Set our role to the admin role then execute the sql script
sql = "SET SESSION AUTHORIZATION %s;\n%s" % (rolname, sql)
oids = sorted(self.dbs.keys())
for dboid in oids:
db = self.dbs[dboid]
if db == 'template0':
continue
self.Update(sql, db=db)
except BaseException, e:
sys.stderr.write(traceback.format_exc())
sys.stderr.write(str(e))
raise e
#------------------------------------------------------------
def getversion(self, home,env):
binary = os.path.join(home, 'bin', 'pg_ctl')
if not os.path.exists(binary):
raise UpgradeError(binary + ' not found')
try:
return self.RunCmd('pg_ctl --hawq-version', env=env)
except CmdError:
pass
conf = '%s/include/pg_config.h' % home
if os.path.exists(conf):
cmd = 'grep PG_VERSION_STR ' + conf
try:
return self.RunCmd(cmd, env=self.oldenv)
except Exception, e:
logger.fatal(str(e))
raise UpgradeError('Unable to determine version of %s' % home)