def __update_log4j(self): append_pattern = 'log4j.appender.R.File=' conf_file = os.path.join(self.get_conf_dir(), common.LOG4J_CONF) log_file = os.path.join(self.get_path(), 'logs', 'system.log') # log4j isn't partial to Windows \. I can't imagine why not. if common.is_win(): log_file = re.sub("\\\\", "/", log_file) common.replace_in_file(conf_file, append_pattern, append_pattern + log_file) # Setting the right log level # Replace the global log level if self.__global_log_level is not None: append_pattern = 'log4j.rootLogger=' common.replace_in_file( conf_file, append_pattern, append_pattern + self.__global_log_level + ',stdout,R') # Class specific log levels for class_name in self.__classes_log_level: logger_pattern = 'log4j.logger' full_logger_pattern = logger_pattern + '.' + class_name + '=' common.replace_or_add_into_file_tail( conf_file, full_logger_pattern, full_logger_pattern + self.__classes_log_level[class_name])
def _clean_win_jmx(self): if common.get_version_from_build(node_path=self.get_path()) >= '2.1': sh_file = os.path.join(common.CASSANDRA_CONF_DIR, common.CASSANDRA_WIN_ENV) dst = os.path.join(self.get_path(), sh_file) common.replace_in_file(dst, "JMX_PORT=", " $JMX_PORT=\"" + self.jmx_port + "\"") # properly use single and double quotes to count for single quotes in the CASSANDRA_CONF path common.replace_in_file(dst,'CASSANDRA_PARAMS=',' $env:CASSANDRA_PARAMS=\'-Dcassandra' + # -Dcassandra ' -Dlogback.configurationFile=/"\' + "$env:CASSANDRA_CONF" + \'/logback.xml"\'' + # -Dlogback.configurationFile=/"$env:CASSANDRA_CONF/logback.xml" ' + \' -Dcassandra.config=file:"\' + "///$env:CASSANDRA_CONF" + \'/cassandra.yaml"\'') # -Dcassandra.config=file:"///$env:CASSANDRA_CONF/cassandra.yaml"
def _clean_win_jmx(self): if common.get_version_from_build(node_path=self.get_path()) >= "2.1": sh_file = os.path.join(common.CASSANDRA_CONF_DIR, common.CASSANDRA_WIN_ENV) dst = os.path.join(self.get_path(), sh_file) common.replace_in_file(dst, "JMX_PORT=", ' $JMX_PORT="' + self.jmx_port + '"') common.replace_in_file( dst, "CASSANDRA_PARAMS=", ' $env:CASSANDRA_PARAMS="-Dcassandra -Dlogback.configurationFile=/$env:CASSANDRA_CONF/logback.xml -Dcassandra.config=file:/$env:CASSANDRA_CONF/cassandra.yaml"', )
def __clean_bat(self): # While the Windows specific changes to the batch files to get them to run are # fairly extensive and thus pretty brittle, all the changes are very unique to # the needs of ccm and shouldn't be pushed into the main repo. # Change the nodes to separate jmx ports bin_dir = os.path.join(self.get_path(), 'bin') jmx_port_pattern = "-Dcom.sun.management.jmxremote.port=" bat_file = os.path.join(bin_dir, "cassandra.bat") common.replace_in_file(bat_file, jmx_port_pattern, " " + jmx_port_pattern + self.jmx_port + "^") # Split binaries from conf home_pattern = "if NOT DEFINED CASSANDRA_HOME set CASSANDRA_HOME=%CD%" common.replace_in_file( bat_file, home_pattern, "set CASSANDRA_HOME=" + self.get_cassandra_dir()) classpath_pattern = "set CLASSPATH=\\\"%CASSANDRA_HOME%\\\\conf\\\"" common.replace_in_file(bat_file, classpath_pattern, "set CLASSPATH=\"" + self.get_conf_dir() + "\"") # background the server process and grab the pid run_text = "\"%JAVA_HOME%\\bin\\java\" %JAVA_OPTS% %CASSANDRA_PARAMS% -cp %CASSANDRA_CLASSPATH% \"%CASSANDRA_MAIN%\"" run_pattern = ".*-cp.*" common.replace_in_file( bat_file, run_pattern, "wmic process call create '" + run_text + "' > \"" + self.get_path() + "/dirty_pid.tmp\"\n")
def __clean_bat(self): # While the Windows specific changes to the batch files to get them to run are # fairly extensive and thus pretty brittle, all the changes are very unique to # the needs of ccm and shouldn't be pushed into the main repo. # Change the nodes to separate jmx ports bin_dir = os.path.join(self.get_path(), "bin") jmx_port_pattern = "-Dcom.sun.management.jmxremote.port=" bat_file = os.path.join(bin_dir, "cassandra.bat") common.replace_in_file(bat_file, jmx_port_pattern, " " + jmx_port_pattern + self.jmx_port + "^") # Split binaries from conf home_pattern = "if NOT DEFINED CASSANDRA_HOME set CASSANDRA_HOME=%CD%" common.replace_in_file(bat_file, home_pattern, "set CASSANDRA_HOME=" + self.get_cassandra_dir()) classpath_pattern = 'set CLASSPATH=\\"%CASSANDRA_HOME%\\\\conf\\"' common.replace_in_file(bat_file, classpath_pattern, 'set CLASSPATH="' + self.get_conf_dir() + '"') # background the server process and grab the pid run_text = ( '"%JAVA_HOME%\\bin\\java" %JAVA_OPTS% %CASSANDRA_PARAMS% -cp %CASSANDRA_CLASSPATH% "%CASSANDRA_MAIN%"' ) run_pattern = ".*-cp.*" common.replace_in_file( bat_file, run_pattern, "wmic process call create '" + run_text + "' > \"" + self.get_path() + '/dirty_pid.tmp"\n', )
def remove_perf_disable_shared_mem(node): """ The Jolokia agent is incompatible with the -XX:+PerfDisableSharedMem JVM option (see https://github.com/rhuss/jolokia/issues/198 for details). This edits cassandra-env.sh (or the Windows equivalent) to remove that option. """ if common.is_win() and node.get_base_cassandra_version() >= 2.1: conf_file = os.path.join(node.get_conf_dir(), common.CASSANDRA_WIN_ENV) else: conf_file = os.path.join(node.get_conf_dir(), common.CASSANDRA_ENV) pattern = 'PerfDisableSharedMem' replacement = '' common.replace_in_file(conf_file, pattern, replacement)
def remove_perf_disable_shared_mem(node): """ The Jolokia agent is incompatible with the -XX:+PerfDisableSharedMem JVM option (see https://github.com/rhuss/jolokia/issues/198 for details). This edits cassandra-env.sh (or the Windows equivalent), or jvm.options file on 3.2+ to remove that option. """ if node.cluster.version() >= LooseVersion('3.2'): conf_file = os.path.join(node.get_conf_dir(), JVM_OPTIONS) pattern = '\-XX:\+PerfDisableSharedMem' replacement = '#-XX:+PerfDisableSharedMem' else: conf_file = node.envfilename() pattern = 'PerfDisableSharedMem' replacement = '' common.replace_in_file(conf_file, pattern, replacement)
def remove_perf_disable_shared_mem(node): """ The Jolokia agent is incompatible with the -XX:+PerfDisableSharedMem JVM option (see https://github.com/rhuss/jolokia/issues/198 for details). This edits cassandra-env.sh (or the Windows equivalent), or jvm.options file on 3.2+ to remove that option. """ if node.cluster.version() >= '3.2': conf_file = os.path.join(node.get_conf_dir(), JVM_OPTIONS) pattern = '\-XX:\+PerfDisableSharedMem' replacement = '#-XX:+PerfDisableSharedMem' else: conf_file = node.envfilename() pattern = 'PerfDisableSharedMem' replacement = '' common.replace_in_file(conf_file, pattern, replacement)
def remove_perf_disable_shared_mem(node): """ The Jolokia agent is incompatible with the -XX:+PerfDisableSharedMem JVM option (see https://github.com/rhuss/jolokia/issues/198 for details). This edits cassandra-env.sh (or the Windows equivalent), or jvm.options file on 3.2+ to remove that option. """ if node.get_cassandra_version() >= LooseVersion('3.2'): pattern = r'\-XX:\+PerfDisableSharedMem' replacement = '#-XX:+PerfDisableSharedMem' for f in glob.glob( os.path.join(node.get_conf_dir(), common.JVM_OPTS_PATTERN)): if os.path.isfile(f): common.replace_in_file(f, pattern, replacement) else: conf_file = node.envfilename() pattern = 'PerfDisableSharedMem' replacement = '' common.replace_in_file(conf_file, pattern, replacement)
def __update_log4j(self): append_pattern='log4j.appender.R.File=' conf_file = os.path.join(self.get_conf_dir(), common.LOG4J_CONF) log_file = os.path.join(self.get_path(), 'logs', 'system.log') # log4j isn't partial to Windows \. I can't imagine why not. if common.is_win(): log_file = re.sub("\\\\", "/", log_file) common.replace_in_file(conf_file, append_pattern, append_pattern + log_file) # Setting the right log level # Replace the global log level if self.__global_log_level is not None: append_pattern='log4j.rootLogger=' common.replace_in_file(conf_file, append_pattern, append_pattern + self.__global_log_level + ',stdout,R') # Class specific log levels for class_name in self.__classes_log_level: logger_pattern='log4j.logger' full_logger_pattern = logger_pattern + '.' + class_name + '=' common.replace_or_add_into_file_tail(conf_file, full_logger_pattern, full_logger_pattern + self.__classes_log_level[class_name])
def _update_log4j(self): super(DseNode, self)._update_log4j() conf_file = os.path.join(self.get_conf_dir(), common.LOG4J_CONF) append_pattern = 'log4j.appender.V.File=' log_file = os.path.join(self.get_path(), 'logs', 'solrvalidation.log') if common.is_win(): log_file = re.sub("\\\\", "/", log_file) common.replace_in_file(conf_file, append_pattern, append_pattern + log_file) append_pattern = 'log4j.appender.A.File=' log_file = os.path.join(self.get_path(), 'logs', 'audit.log') if common.is_win(): log_file = re.sub("\\\\", "/", log_file) common.replace_in_file(conf_file, append_pattern, append_pattern + log_file) append_pattern = 'log4j.appender.B.File=' log_file = os.path.join(self.get_path(), 'logs', 'audit', 'dropped-events.log') if common.is_win(): log_file = re.sub("\\\\", "/", log_file) common.replace_in_file(conf_file, append_pattern, append_pattern + log_file)
def __update_logback(self): append_pattern = "<file>.*</file>" conf_file = os.path.join(self.get_conf_dir(), common.LOGBACK_CONF) log_file = os.path.join(self.get_path(), "logs", "system.log") common.replace_in_file(conf_file, append_pattern, "<file>" + log_file + "</file>") append_pattern = "<fileNamePattern>.*</fileNamePattern>" common.replace_in_file(conf_file, append_pattern, "<fileNamePattern>" + log_file + ".%i.zip</fileNamePattern>") # Setting the right log level # Replace the global log level if self.__global_log_level is not None: append_pattern = '<root level=".*">' common.replace_in_file(conf_file, append_pattern, '<root level="' + self.__global_log_level + '">') # Class specific log levels for class_name in self.__classes_log_level: logger_pattern = '\t<logger name="' full_logger_pattern = logger_pattern + class_name + '" level=".*"/>' common.replace_or_add_into_file_tail( conf_file, full_logger_pattern, logger_pattern + class_name + '" level="' + self.__classes_log_level[class_name] + '"/>', )
def __update_logback(self): append_pattern = '<file>.*</file>' conf_file = os.path.join(self.get_conf_dir(), common.LOGBACK_CONF) log_file = os.path.join(self.get_path(), 'logs', 'system.log') common.replace_in_file(conf_file, append_pattern, '<file>' + log_file + '</file>') append_pattern = '<fileNamePattern>.*</fileNamePattern>' common.replace_in_file( conf_file, append_pattern, '<fileNamePattern>' + log_file + '.%i.zip</fileNamePattern>') # Setting the right log level # Replace the global log level if self.__global_log_level is not None: append_pattern = '<root level=".*">' common.replace_in_file( conf_file, append_pattern, '<root level="' + self.__global_log_level + '">') # Class specific log levels for class_name in self.__classes_log_level: logger_pattern = '\t<logger name="' full_logger_pattern = logger_pattern + class_name + '" level=".*"/>' common.replace_or_add_into_file_tail( conf_file, full_logger_pattern, logger_pattern + class_name + '" level="' + self.__classes_log_level[class_name] + '"/>')
def __update_envfile(self): jmx_port_pattern='JMX_PORT=' remote_debug_port_pattern='-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=' conf_file = os.path.join(self.get_conf_dir(), common.CASSANDRA_ENV) common.replace_in_file(conf_file, jmx_port_pattern, jmx_port_pattern + self.jmx_port) if self.remote_debug_port != '0': common.replace_in_file(conf_file, remote_debug_port_pattern, 'JVM_OPTS="$JVM_OPTS -Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=' + str(self.remote_debug_port) + '"') if self.cluster.version() < '2.0.1': common.replace_in_file(conf_file, "-Xss", ' JVM_OPTS="$JVM_OPTS -Xss228k"')
def start(self, join_ring=True, no_wait=False, verbose=False, update_pid=True, wait_other_notice=False, replace_token=None, replace_address=None, jvm_args=[], wait_for_binary_proto=False, profile_options=None, use_jna=False): """ Start the node. Options includes: - join_ring: if false, start the node with -Dcassandra.join_ring=False - no_wait: by default, this method returns when the node is started and listening to clients. If no_wait=True, the method returns sooner. - wait_other_notice: if True, this method returns only when all other live node of the cluster have marked this node UP. - replace_token: start the node with the -Dcassandra.replace_token option. - replace_address: start the node with the -Dcassandra.replace_address option. """ if self.is_running(): raise NodeError("%s is already running" % self.name) for itf in list(self.network_interfaces.values()): if itf is not None and replace_address is None: common.check_socket_available(itf) if wait_other_notice: marks = [ (node, node.mark_log()) for node in list(self.cluster.nodes.values()) if node.is_running() ] cdir = self.get_install_dir() launch_bin = common.join_bin(cdir, 'bin', 'dse') # Copy back the dse scripts since profiling may have modified it the previous time shutil.copy(launch_bin, self.get_bin_dir()) launch_bin = common.join_bin(self.get_path(), 'bin', 'dse') # If Windows, change entries in .bat file to split conf from binaries if common.is_win(): self.__clean_bat() if profile_options is not None: config = common.get_config() if not 'yourkit_agent' in config: raise NodeError("Cannot enable profile. You need to set 'yourkit_agent' to the path of your agent in a {0}/config".format(common.get_default_path_display_name())) cmd = '-agentpath:%s' % config['yourkit_agent'] if 'options' in profile_options: cmd = cmd + '=' + profile_options['options'] print_(cmd) # Yes, it's fragile as shit pattern=r'cassandra_parms="-Dlog4j.configuration=log4j-server.properties -Dlog4j.defaultInitOverride=true' common.replace_in_file(launch_bin, pattern, ' ' + pattern + ' ' + cmd + '"') os.chmod(launch_bin, os.stat(launch_bin).st_mode | stat.S_IEXEC) env = common.make_dse_env(self.get_install_dir(), self.get_path()) if common.is_win(): self._clean_win_jmx(); pidfile = os.path.join(self.get_path(), 'cassandra.pid') args = [launch_bin, 'cassandra'] if self.workload is not None: if 'hadoop' in self.workload: args.append('-t') if 'solr' in self.workload: args.append('-s') if 'spark' in self.workload: args.append('-k') if 'cfs' in self.workload: args.append('-c') args += [ '-p', pidfile, '-Dcassandra.join_ring=%s' % str(join_ring) ] if replace_token is not None: args.append('-Dcassandra.replace_token=%s' % str(replace_token)) if replace_address is not None: args.append('-Dcassandra.replace_address=%s' % str(replace_address)) if use_jna is False: args.append('-Dcassandra.boot_without_jna=true') args = args + jvm_args process = None if common.is_win(): # clean up any old dirty_pid files from prior runs if (os.path.isfile(self.get_path() + "/dirty_pid.tmp")): os.remove(self.get_path() + "/dirty_pid.tmp") process = subprocess.Popen(args, cwd=self.get_bin_dir(), env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE) else: process = subprocess.Popen(args, env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # Our modified batch file writes a dirty output with more than just the pid - clean it to get in parity # with *nix operation here. if common.is_win(): self.__clean_win_pid() self._update_pid(process) elif update_pid: if no_wait: time.sleep(2) # waiting 2 seconds nevertheless to check for early errors and for the pid to be set else: for line in process.stdout: if verbose: print_(line.rstrip('\n')) self._update_pid(process) if not self.is_running(): raise NodeError("Error starting node %s" % self.name, process) if wait_other_notice: for node, mark in marks: node.watch_log_for_alive(self, from_mark=mark) if wait_for_binary_proto: self.watch_log_for("Starting listening for CQL clients") # we're probably fine at that point but just wait some tiny bit more because # the msg is logged just before starting the binary protocol server time.sleep(0.2) if self.cluster.hasOpscenter(): self._start_agent() return process
def start(self, join_ring=True, no_wait=False, verbose=False, update_pid=True, wait_other_notice=False, replace_token=None, replace_address=None, jvm_args=None, wait_for_binary_proto=False, profile_options=None, use_jna=False, quiet_start=False): """ Start the node. Options includes: - join_ring: if false, start the node with -Dcassandra.join_ring=False - no_wait: by default, this method returns when the node is started and listening to clients. If no_wait=True, the method returns sooner. - wait_other_notice: if True, this method returns only when all other live node of the cluster have marked this node UP. - replace_token: start the node with the -Dcassandra.replace_token option. - replace_address: start the node with the -Dcassandra.replace_address option. """ if jvm_args is None: jvm_args = [] if self.is_running(): raise NodeError("%s is already running" % self.name) for itf in list(self.network_interfaces.values()): if itf is not None and replace_address is None: common.check_socket_available(itf) if wait_other_notice: marks = [(node, node.mark_log()) for node in list(self.cluster.nodes.values()) if node.is_running()] cdir = self.get_install_dir() launch_bin = common.join_bin(cdir, 'bin', 'dse') # Copy back the dse scripts since profiling may have modified it the previous time shutil.copy(launch_bin, self.get_bin_dir()) launch_bin = common.join_bin(self.get_path(), 'bin', 'dse') # If Windows, change entries in .bat file to split conf from binaries if common.is_win(): self.__clean_bat() if profile_options is not None: config = common.get_config() if 'yourkit_agent' not in config: raise NodeError( "Cannot enable profile. You need to set 'yourkit_agent' to the path of your agent in a {0}/config" .format(common.get_default_path_display_name())) cmd = '-agentpath:%s' % config['yourkit_agent'] if 'options' in profile_options: cmd = cmd + '=' + profile_options['options'] print_(cmd) # Yes, it's fragile as shit pattern = r'cassandra_parms="-Dlog4j.configuration=log4j-server.properties -Dlog4j.defaultInitOverride=true' common.replace_in_file(launch_bin, pattern, ' ' + pattern + ' ' + cmd + '"') os.chmod(launch_bin, os.stat(launch_bin).st_mode | stat.S_IEXEC) env = common.make_dse_env(self.get_install_dir(), self.get_path()) if common.is_win(): self._clean_win_jmx() pidfile = os.path.join(self.get_path(), 'cassandra.pid') args = [launch_bin, 'cassandra'] if self.workload is not None: if 'hadoop' in self.workload: args.append('-t') if 'solr' in self.workload: args.append('-s') if 'spark' in self.workload: args.append('-k') if 'cfs' in self.workload: args.append('-c') args += ['-p', pidfile, '-Dcassandra.join_ring=%s' % str(join_ring)] if replace_token is not None: args.append('-Dcassandra.replace_token=%s' % str(replace_token)) if replace_address is not None: args.append('-Dcassandra.replace_address=%s' % str(replace_address)) if use_jna is False: args.append('-Dcassandra.boot_without_jna=true') args = args + jvm_args process = None if common.is_win(): # clean up any old dirty_pid files from prior runs if (os.path.isfile(self.get_path() + "/dirty_pid.tmp")): os.remove(self.get_path() + "/dirty_pid.tmp") process = subprocess.Popen(args, cwd=self.get_bin_dir(), env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE) else: process = subprocess.Popen(args, env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # Our modified batch file writes a dirty output with more than just the pid - clean it to get in parity # with *nix operation here. if common.is_win(): self.__clean_win_pid() self._update_pid(process) elif update_pid: if no_wait: time.sleep( 2 ) # waiting 2 seconds nevertheless to check for early errors and for the pid to be set else: for line in process.stdout: if verbose: print_(line.rstrip('\n')) self._update_pid(process) if not self.is_running(): raise NodeError("Error starting node %s" % self.name, process) if wait_other_notice: for node, mark in marks: node.watch_log_for_alive(self, from_mark=mark) if wait_for_binary_proto: self.wait_for_binary_interface() if self.cluster.hasOpscenter(): self._start_agent() return process
def __clean_bat(self): # While the Windows specific changes to the batch files to get them to run are # fairly extensive and thus pretty brittle, all the changes are very unique to # the needs of ccm and shouldn't be pushed into the main repo. # Change the nodes to separate jmx ports bin_dir = os.path.join(self.get_path(), 'bin') jmx_port_pattern="-Dcom.sun.management.jmxremote.port=" bat_file = os.path.join(bin_dir, "cassandra.bat"); common.replace_in_file(bat_file, jmx_port_pattern, " " + jmx_port_pattern + self.jmx_port + "^") # Split binaries from conf home_pattern="if NOT DEFINED CASSANDRA_HOME set CASSANDRA_HOME=%CD%" common.replace_in_file(bat_file, home_pattern, "set CASSANDRA_HOME=" + self.get_cassandra_dir()) classpath_pattern="set CLASSPATH=\\\"%CASSANDRA_HOME%\\\\conf\\\"" common.replace_in_file(bat_file, classpath_pattern, "set CLASSPATH=\\\"" + self.get_conf_dir() + "\\\"") # escape the double quotes in name of the lib files in the classpath jar_file_pattern="do call :append \"%%i\"" for_statement="for %%i in (\"%CASSANDRA_HOME%\lib\*.jar\")" common.replace_in_file(bat_file, jar_file_pattern, for_statement + " do call :append \\\"%%i\\\"") # escape double quotes in java agent path class_dir_pattern="-javaagent:" common.replace_in_file(bat_file, class_dir_pattern, " -javaagent:\\\"%CASSANDRA_HOME%\\lib\\jamm-0.2.5.jar\\\"^") # escape the double quotes in name of the class directories class_dir_pattern="set CASSANDRA_CLASSPATH=" main_classes = "\\\"%CASSANDRA_HOME%\\build\\classes\\main\\\";" thrift_classes="\\\"%CASSANDRA_HOME%\\build\\classes\\thrift\\\"" common.replace_in_file(bat_file, class_dir_pattern, "set CASSANDRA_CLASSPATH=%CLASSPATH%;" + main_classes + thrift_classes) # background the server process and grab the pid run_text="\\\"%JAVA_HOME%\\bin\\java\\\" %JAVA_OPTS% %CASSANDRA_PARAMS% -cp %CASSANDRA_CLASSPATH% \\\"%CASSANDRA_MAIN%\\\"" run_pattern=".*-cp.*" common.replace_in_file(bat_file, run_pattern, "wmic process call create \"" + run_text + "\" > \"" + self.get_path() + "/dirty_pid.tmp\"\n")