SLIDER-620. Execute command should allow polling for daemons that may stop within seconds
diff --git a/app-packages/hbase-win/package/scripts/params.py b/app-packages/hbase-win/package/scripts/params.py
index 90d274c..5a54e25 100644
--- a/app-packages/hbase-win/package/scripts/params.py
+++ b/app-packages/hbase-win/package/scripts/params.py
@@ -44,11 +44,11 @@
"""
Read various ports
"""
-rest_port = default("configurations/global/hbase_rest_port", 1700)
-thrift_port = default("configurations/global/hbase_thrift_port", 9090)
-thrift2_port = default("configurations/global/hbase_thrift2_port", 9091)
-thrift_info_port = default("configurations/global/hbase_info_thrift_port", 9095)
-thrift2_info_port = default("configurations/global/hbase_info_thrift2_port", 9096)
+rest_port = default("/configurations/global/hbase_rest_port", 1700)
+thrift_port = default("/configurations/global/hbase_thrift_port", 9090)
+thrift2_port = default("/configurations/global/hbase_thrift2_port", 9091)
+thrift_info_port = default("/configurations/global/hbase_info_thrift_port", 9095)
+thrift2_info_port = default("/configurations/global/hbase_info_thrift2_port", 9096)
"""
Compute or read various heap sizes
@@ -59,9 +59,9 @@
regionserver_xmn_percent = config['configurations']['hbase-env']['hbase_regionserver_xmn_ratio']
regionserver_xmn_size = calc_xmn_from_xms(regionserver_heapsize, regionserver_xmn_percent, regionserver_xmn_max)
-restserver_heapsize = default("configurations/hbase-env/hbase_restserver_heapsize", "512m")
-thriftserver_heapsize = default("configurations/hbase-env/hbase_thriftserver_heapsize", "512m")
-thrift2server_heapsize = default("configurations/hbase-env/hbase_thrift2server_heapsize", "512m")
+restserver_heapsize = default("/configurations/hbase-env/hbase_restserver_heapsize", "512m")
+thriftserver_heapsize = default("/configurations/hbase-env/hbase_thriftserver_heapsize", "512m")
+thrift2server_heapsize = default("/configurations/hbase-env/hbase_thrift2server_heapsize", "512m")
hbase_env_sh_template = config['configurations']['hbase-env']['content']
java_library_path = config['configurations']['global']['java_library_path']
diff --git a/app-packages/memcached-win/metainfo.xml b/app-packages/memcached-win/metainfo.xml
index c7e5881..093001b 100644
--- a/app-packages/memcached-win/metainfo.xml
+++ b/app-packages/memcached-win/metainfo.xml
@@ -23,17 +23,23 @@
<comment>Memcache is a network accessible key/value storage system, often used as a distributed cache.</comment>
<version>1.0.0</version>
<exportedConfigs>None</exportedConfigs>
+ <exportGroups>
+ <exportGroup>
+ <name>Servers</name>
+ <exports>
+ <export>
+ <name>host_port</name>
+ <value>${MEMCACHED_HOST}:${site.global.listen_port}</value>
+ </export>
+ </exports>
+ </exportGroup>
+ </exportGroups>
<components>
<component>
<name>MEMCACHED</name>
<category>MASTER</category>
- <componentExports>
- <componentExport>
- <name>host_port</name>
- <value>${THIS_HOST}:${site.global.listen_port}</value>
- </componentExport>
- </componentExports>
+ <compExports>Servers-host_port</compExports>
<commandScript>
<script>scripts/memcached.py</script>
<scriptType>PYTHON</scriptType>
diff --git a/app-packages/memcached-win/package/scripts/memcached.py b/app-packages/memcached-win/package/scripts/memcached.py
index cd560dd..c272e47 100644
--- a/app-packages/memcached-win/package/scripts/memcached.py
+++ b/app-packages/memcached-win/package/scripts/memcached.py
@@ -39,7 +39,8 @@
Execute(process_cmd,
logoutput=False,
wait_for_finish=False,
- pid_file=params.pid_file
+ pid_file=params.pid_file,
+ poll_after = 5
)
def stop(self, env):
@@ -49,8 +50,7 @@
def status(self, env):
import params
env.set_params(params)
- #Check process status need to be changed for Windows
- #check_process_status(params.pid_file)
+ check_process_status(params.pid_file)
if __name__ == "__main__":
Memcached().execute()
diff --git a/app-packages/memcached-win/package/scripts/params.py b/app-packages/memcached-win/package/scripts/params.py
index fab3714..056a3b9 100644
--- a/app-packages/memcached-win/package/scripts/params.py
+++ b/app-packages/memcached-win/package/scripts/params.py
@@ -25,8 +25,8 @@
app_root = config['configurations']['global']['app_root']
java64_home = config['hostLevelParams']['java_home']
-app_user = config['configurations']['global']['app_user']
pid_file = config['configurations']['global']['pid_file']
+
additional_cp = config['configurations']['global']['additional_cp']
xmx_val = config['configurations']['global']['xmx_val']
xms_val = config['configurations']['global']['xms_val']
diff --git a/app-packages/memcached/README.txt b/app-packages/memcached/README.txt
index d9d8810..fc0e4f3 100644
--- a/app-packages/memcached/README.txt
+++ b/app-packages/memcached/README.txt
@@ -19,7 +19,16 @@
To create the app package you will need the Memcached tarball copied to a specific location.
-Replace the placeholder tarball for JMemcached.
+Replace the placeholder tarball for JMemcached. The tarball must have all the jar files at the
+root directory.
+Example:
+ tar -tvf jmemcached-1.0.0.tar
+ -rw-r--r-- ./jmemcached-cli-1.0.0.jar
+ -rwxr-xr-x ./jmemcached-core-1.0.0.jar
+
+If not modify, appConfig.json to have correct application install root.
+ "site.global.app_root": "${AGENT_WORK_ROOT}/app/install/my_sub_root_for_jars",
+
cp ~/Downloads/jmemcached-1.0.0.tar package/files/
rm package/files/jmemcached-1.0.0.tar.REPLACE
diff --git a/app-packages/memcached/package/scripts/memcached.py b/app-packages/memcached/package/scripts/memcached.py
index 897a993..986b61e 100644
--- a/app-packages/memcached/package/scripts/memcached.py
+++ b/app-packages/memcached/package/scripts/memcached.py
@@ -39,7 +39,8 @@
Execute(process_cmd,
logoutput=False,
wait_for_finish=False,
- pid_file=params.pid_file
+ pid_file=params.pid_file,
+ poll_after = 5
)
def stop(self, env):
diff --git a/slider-agent/src/main/python/resource_management/core/logger.py b/slider-agent/src/main/python/resource_management/core/logger.py
index b80042a..5d6e414 100644
--- a/slider-agent/src/main/python/resource_management/core/logger.py
+++ b/slider-agent/src/main/python/resource_management/core/logger.py
@@ -29,7 +29,15 @@
# unprotected_strings : protected_strings map
sensitive_strings = {}
-
+
+ @staticmethod
+ def error(text):
+ Logger.logger.error(Logger.get_protected_text(text))
+
+ @staticmethod
+ def warning(text):
+ Logger.logger.warning(Logger.get_protected_text(text))
+
@staticmethod
def info(text):
Logger.logger.info(Logger.get_protected_text(text))
@@ -39,6 +47,14 @@
Logger.logger.debug(Logger.get_protected_text(text))
@staticmethod
+ def error_resource(resource):
+ Logger.error(Logger.get_protected_text(Logger._get_resource_repr(resource)))
+
+ @staticmethod
+ def warning_resource(resource):
+ Logger.warning(Logger.get_protected_text(Logger._get_resource_repr(resource)))
+
+ @staticmethod
def info_resource(resource):
Logger.info(Logger.get_protected_text(Logger._get_resource_repr(resource)))
@@ -92,4 +108,4 @@
if arguments_str:
arguments_str = arguments_str[:-2]
- return "{0} {{{1}}}".format(resource, arguments_str)
\ No newline at end of file
+ return unicode("{0} {{{1}}}").format(resource, arguments_str)
\ No newline at end of file
diff --git a/slider-agent/src/main/python/resource_management/core/providers/system.py b/slider-agent/src/main/python/resource_management/core/providers/system.py
index f32ee3b..6f56967 100644
--- a/slider-agent/src/main/python/resource_management/core/providers/system.py
+++ b/slider-agent/src/main/python/resource_management/core/providers/system.py
@@ -253,7 +253,7 @@
cwd=self.resource.cwd, env=self.resource.environment,
preexec_fn=_preexec_fn(self.resource), user=self.resource.user,
wait_for_finish=self.resource.wait_for_finish, timeout=self.resource.timeout,
- pid_file=self.resource.pid_file)
+ pid_file=self.resource.pid_file, poll_after=self.resource.poll_after)
break
except Fail as ex:
if i == self.resource.tries-1: # last try
diff --git a/slider-agent/src/main/python/resource_management/core/providers/windows/system.py b/slider-agent/src/main/python/resource_management/core/providers/windows/system.py
index 9514bdb..6167977 100644
--- a/slider-agent/src/main/python/resource_management/core/providers/windows/system.py
+++ b/slider-agent/src/main/python/resource_management/core/providers/windows/system.py
@@ -62,9 +62,11 @@
result_env[str(key)] = str(os.pathsep.join(set(all_values)))
return result_env
+
# Execute command. As windows stack heavily relies on proper environment it is better to reload fresh environment
# on every execution. env variable will me merged with fresh environment for user.
-def _call_command(command, logoutput=False, cwd=None, env=None, wait_for_finish=True, timeout=None, user=None, pid_file_name=None):
+def _call_command(command, logoutput=False, cwd=None, env=None, wait_for_finish=True, timeout=None, user=None,
+ pid_file_name=None, poll_after=None):
# TODO implement user
Logger.info("Executing %s" % (command))
proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
@@ -73,26 +75,41 @@
if not wait_for_finish:
Logger.debug("No need to wait for the process to exit. Will leave the process running ...")
code = 0
+ logAnyway = False
if pid_file_name:
Logger.debug("Writing the process id %s to file %s" % (str(proc.pid), pid_file_name))
pidfile = open(pid_file_name, 'w')
pidfile.write(str(proc.pid))
pidfile.close()
Logger.info("Wrote the process id to file %s" % pid_file_name)
- return code, None, None
+
+ ## wait poll_after seconds and poll
+ if poll_after:
+ time.sleep(poll_after)
+ if proc.poll() is None:
+ return code, None, None # if still running then return
+ else:
+ logAnyway = True # assume failure and log
+ Logger.warning("Process is not up after the polling interval " + str(poll_after) + " seconds.")
+ else:
+ return code, None, None
if timeout:
q = Queue()
- t = threading.Timer( timeout, on_timeout, [proc, q] )
+ t = threading.Timer(timeout, on_timeout, [proc, q])
t.start()
out, err = proc.communicate()
code = proc.returncode
- if logoutput and out:
- Logger.info(out)
- if logoutput and err:
- Logger.info(err)
+ if logoutput or logAnyway:
+ if out:
+ Logger.info("Out: " + str(out))
+ if err:
+ Logger.info("Err: " + str(err))
+ if code:
+ Logger.info("Ret Code: " + str(code))
+
return code, out, err
# see msdn Icacls doc for rights
@@ -183,7 +200,7 @@
cwd=self.resource.cwd, env=self.resource.environment,
wait_for_finish=self.resource.wait_for_finish,
timeout=self.resource.timeout, user=self.resource.user,
- pid_file_name=self.resource.pid_file)
+ pid_file_name=self.resource.pid_file, poll_after=self.resource.poll_after)
if code != 0 and not self.resource.ignore_failures:
raise Fail("Failed to execute " + self.resource.command)
break
diff --git a/slider-agent/src/main/python/resource_management/core/resources/system.py b/slider-agent/src/main/python/resource_management/core/resources/system.py
index 3c3513b..f751d52 100644
--- a/slider-agent/src/main/python/resource_management/core/resources/system.py
+++ b/slider-agent/src/main/python/resource_management/core/resources/system.py
@@ -106,6 +106,7 @@
if wait_for_finish is True then optionally the caller can ask for the pid to be written
"""
pid_file = ResourceArgument()
+ poll_after = ResourceArgument() #seconds
class ExecuteScript(Resource):
diff --git a/slider-agent/src/main/python/resource_management/core/shell.py b/slider-agent/src/main/python/resource_management/core/shell.py
index fb2c946..95d18fc 100644
--- a/slider-agent/src/main/python/resource_management/core/shell.py
+++ b/slider-agent/src/main/python/resource_management/core/shell.py
@@ -29,17 +29,18 @@
from exceptions import Fail
from exceptions import ExecuteTimeoutException
from resource_management.core.logger import Logger
+import time
def checked_call(command, logoutput=False,
- cwd=None, env=None, preexec_fn=None, user=None, wait_for_finish=True, timeout=None, pid_file=None):
- return _call(command, logoutput, True, cwd, env, preexec_fn, user, wait_for_finish, timeout, pid_file)
+ cwd=None, env=None, preexec_fn=None, user=None, wait_for_finish=True, timeout=None, pid_file=None, poll_after=None):
+ return _call(command, logoutput, True, cwd, env, preexec_fn, user, wait_for_finish, timeout, pid_file, poll_after)
def call(command, logoutput=False,
- cwd=None, env=None, preexec_fn=None, user=None, wait_for_finish=True, timeout=None, pid_file=None):
- return _call(command, logoutput, False, cwd, env, preexec_fn, user, wait_for_finish, timeout, pid_file)
+ cwd=None, env=None, preexec_fn=None, user=None, wait_for_finish=True, timeout=None, pid_file=None, poll_after=None):
+ return _call(command, logoutput, False, cwd, env, preexec_fn, user, wait_for_finish, timeout, pid_file, poll_after)
def _call(command, logoutput=False, throw_on_failure=True,
- cwd=None, env=None, preexec_fn=None, user=None, wait_for_finish=True, timeout=None, pid_file_name=None):
+ cwd=None, env=None, preexec_fn=None, user=None, wait_for_finish=True, timeout=None, pid_file_name=None, poll_after=None):
"""
Execute shell command
@@ -66,13 +67,25 @@
cwd=cwd, env=env, shell=False,
preexec_fn=preexec_fn)
+ logAnyway = False
if not wait_for_finish:
if pid_file_name:
pidfile = open(pid_file_name, 'w')
pidfile.write(str(proc.pid))
pidfile.close()
- return None, None
-
+
+ ## wait poll_after seconds and poll
+ if poll_after:
+ time.sleep(poll_after)
+ if proc.poll() is None:
+ return None, None #if still running then return
+ else:
+ logAnyway = True #assume failure and log
+ Logger.warning("Process is not up after the polling interval " + str(poll_after) + " seconds.")
+ else:
+ return None, None
+
+
if timeout:
q = Queue()
t = threading.Timer( timeout, on_timeout, [proc, q] )
@@ -89,7 +102,7 @@
code = proc.returncode
- if logoutput and out:
+ if (logoutput or logAnyway) and out:
Logger.info(out)
if throw_on_failure and code:
diff --git a/slider-agent/src/main/python/resource_management/libraries/functions/check_process_status.py b/slider-agent/src/main/python/resource_management/libraries/functions/check_process_status.py
index b491f66..8f10455 100644
--- a/slider-agent/src/main/python/resource_management/libraries/functions/check_process_status.py
+++ b/slider-agent/src/main/python/resource_management/libraries/functions/check_process_status.py
@@ -42,7 +42,7 @@
"""
if not pid_file or not os.path.isfile(pid_file):
if not pid_file:
- Logger.warn("pid_file is not valid")
+ Logger.warning("pid_file is not valid")
else:
Logger.info("pid file does not exist {0}".format(pid_file))
raise ComponentIsNotRunning()
diff --git a/slider-agent/src/test/python/resource_management/TestExecuteResource.py b/slider-agent/src/test/python/resource_management/TestExecuteResource.py
index f0a4539..113644d 100644
--- a/slider-agent/src/test/python/resource_management/TestExecuteResource.py
+++ b/slider-agent/src/test/python/resource_management/TestExecuteResource.py
@@ -62,6 +62,31 @@
self.assertTrue(popen_mock.called, 'subprocess.Popen should have been called!')
self.assertFalse(proc_communicate_mock.called, 'proc.communicate should not have been called!')
+ @patch('subprocess.Popen.communicate')
+ @patch('subprocess.Popen')
+ def test_attribute_wait_and_poll(self, popen_mock, proc_communicate_mock):
+ with Environment("/") as env:
+ try:
+ Execute('echo "1"',
+ wait_for_finish=False,
+ poll_after = 5)
+ self.assertTrue(False, "Should fail as process does not run for 5 seconds")
+ except Fail as e:
+ self.assertTrue("returned 1" in e.message)
+ pass
+
+ self.assertTrue(popen_mock.called, 'subprocess.Popen should have been called!')
+ self.assertFalse(proc_communicate_mock.called, 'proc.communicate should not have been called!')
+
+ @patch('subprocess.Popen.communicate')
+ def test_attribute_wait_and_poll_and_success(self, proc_communicate_mock):
+ with Environment("/") as env:
+ Execute('sleep 6',
+ wait_for_finish=False,
+ poll_after = 2)
+
+ self.assertFalse(proc_communicate_mock.called, 'proc.communicate should not have been called!')
+
@patch.object(os.path, "exists")
@patch.object(subprocess, "Popen")
def test_attribute_creates(self, popen_mock, exists_mock):