blob: bd2661f6f1762124616b48dea9e2adce127bf97d [file] [log] [blame]
# 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.
# Line too long - pylint: disable=C0301
from gppylib import gplog
from gppylib.commands import gp
from optparse import OptionGroup
from gppylib.gpparseopts import OptParser, OptChecker
from gppylib.mainUtils import addStandardLoggingAndHelpOptions, ProgramArgumentValidationException
from gppylib.commands.unix import kill_sequence, check_pid
from gppylib.operations.package import dereference_symlink
from psi.process import Process, NoSuchProcessError
logger = gplog.get_default_logger()
class KillError(Exception): pass
class KillProgram:
def __init__(self, options, args):
self.check = options.check
self.pid_list = args
@staticmethod
def create_parser():
"""Create the command line parser object for gpkill"""
help = []
parser = OptParser(option_class=OptChecker,
description='Check or Terminate a Greenplum Database process.',
version='%prog version $Revision: #1 $')
parser.setHelp(help)
addStandardLoggingAndHelpOptions(parser, True)
parser.remove_option('-l')
parser.remove_option('-a')
addTo = OptionGroup(parser, 'Check Options')
parser.add_option_group(addTo)
addTo.add_option('--check', metavar='pid', help='Only returns status 0 if pid may be killed without gpkill, status 1 otherwise.', action='store_true')
return parser
@staticmethod
def create_program(options, args):
return KillProgram(options, args)
def cleanup(self): pass
def run(self):
self.validate_arguments()
if self.check:
for pid in self.pid_list:
self.validate_attempt(pid)
else:
for pid in self.pid_list:
self.terminate_process(pid)
return 0
def validate_arguments(self):
if len(self.pid_list) < 1:
raise KillError('No pid specified')
int_pid_list = []
try:
for x in self.pid_list:
int_pid_list.append(int(x))
except ValueError, e:
raise KillError('Invalid pid specified (%s)' % x)
self.pid_list = int_pid_list
def validate_attempt(self, pid):
"""
Checks if we can kill the process
"""
command = self.examine_process(pid)
critical_process_prefix = ['postgres', gp.get_gphome(), dereference_symlink(gp.get_gphome())]
for prefix in critical_process_prefix:
if command.startswith(prefix):
raise KillError('process %s may not be killed' % pid)
if not command.startswith('python ' + gp.get_gphome()):
raise KillError('process %s ignored by gpkill as it is not a greenplum process' % pid)
def examine_process(self, pid):
logger.info('Examining process: pid(%s)' % pid)
try:
proc = Process(pid=pid)
except NoSuchProcessError, e:
raise KillError('Process with pid(%s) does not exist' % pid)
logger.info('process %s is %s' % (pid, proc.command.strip()))
return proc.command.strip()
def terminate_process(self, pid):
self.validate_attempt(pid)
logger.warning('Confirm [N/y]:')
confirmation = raw_input().strip().lower()
if confirmation not in ['y', 'ye', 'yes']:
raise KillError('operation aborted')
kill_sequence(pid)
if check_pid(pid):
raise KillError('Failed to kill process %s' % pid)