|
|
|
@ -254,7 +254,7 @@ class KafkaService(KafkaPathResolverMixin, JmxMixin, Service):
@@ -254,7 +254,7 @@ class KafkaService(KafkaPathResolverMixin, JmxMixin, Service):
|
|
|
|
|
def alive(self, node): |
|
|
|
|
return len(self.pids(node)) > 0 |
|
|
|
|
|
|
|
|
|
def start(self, add_principals="", use_zk_to_create_topic=True): |
|
|
|
|
def start(self, add_principals=""): |
|
|
|
|
if self.zk_client_secure and not self.zk.zk_client_secure_port: |
|
|
|
|
raise Exception("Unable to start Kafka: TLS to Zookeeper requested but Zookeeper secure port not enabled") |
|
|
|
|
self.open_port(self.security_protocol) |
|
|
|
@ -281,7 +281,7 @@ class KafkaService(KafkaPathResolverMixin, JmxMixin, Service):
@@ -281,7 +281,7 @@ class KafkaService(KafkaPathResolverMixin, JmxMixin, Service):
|
|
|
|
|
topic_cfg = {} |
|
|
|
|
|
|
|
|
|
topic_cfg["topic"] = topic |
|
|
|
|
self.create_topic(topic_cfg, use_zk_to_create_topic=use_zk_to_create_topic) |
|
|
|
|
self.create_topic(topic_cfg) |
|
|
|
|
|
|
|
|
|
def _ensure_zk_chroot(self): |
|
|
|
|
self.logger.info("Ensuring zk_chroot %s exists", self.zk_chroot) |
|
|
|
@ -445,25 +445,61 @@ class KafkaService(KafkaPathResolverMixin, JmxMixin, Service):
@@ -445,25 +445,61 @@ class KafkaService(KafkaPathResolverMixin, JmxMixin, Service):
|
|
|
|
|
clean_shutdown=False, allow_fail=True) |
|
|
|
|
node.account.ssh("sudo rm -rf -- %s" % KafkaService.PERSISTENT_ROOT, allow_fail=False) |
|
|
|
|
|
|
|
|
|
def _kafka_topics_cmd(self, node, use_zk_connection=True): |
|
|
|
|
def _kafka_topics_cmd(self, node, force_use_zk_connection): |
|
|
|
|
""" |
|
|
|
|
Returns kafka-topics.sh command path with jaas configuration and krb5 environment variable |
|
|
|
|
set. If Admin client is not going to be used, don't set the environment variable. |
|
|
|
|
""" |
|
|
|
|
kafka_topic_script = self.path.script("kafka-topics.sh", node) |
|
|
|
|
skip_security_settings = use_zk_connection or not node.version.topic_command_supports_bootstrap_server() |
|
|
|
|
skip_security_settings = force_use_zk_connection or not self.all_nodes_topic_command_supports_bootstrap_server() |
|
|
|
|
return kafka_topic_script if skip_security_settings else \ |
|
|
|
|
"KAFKA_OPTS='-D%s -D%s' %s" % (KafkaService.JAAS_CONF_PROPERTY, KafkaService.KRB5_CONF, kafka_topic_script) |
|
|
|
|
|
|
|
|
|
def _kafka_topics_cmd_config(self, node, use_zk_connection=True): |
|
|
|
|
def _kafka_topics_cmd_config(self, node, force_use_zk_connection): |
|
|
|
|
""" |
|
|
|
|
Return --command-config parameter to the kafka-topics.sh command. The config parameter specifies |
|
|
|
|
the security settings that AdminClient uses to connect to a secure kafka server. |
|
|
|
|
""" |
|
|
|
|
skip_command_config = use_zk_connection or not node.version.topic_command_supports_bootstrap_server() |
|
|
|
|
skip_command_config = force_use_zk_connection or not self.all_nodes_topic_command_supports_bootstrap_server() |
|
|
|
|
return "" if skip_command_config else " --command-config <(echo '%s')" % (self.security_config.client_config()) |
|
|
|
|
|
|
|
|
|
def create_topic(self, topic_cfg, node=None, use_zk_to_create_topic=True): |
|
|
|
|
def all_nodes_topic_command_supports_bootstrap_server(self): |
|
|
|
|
for node in self.nodes: |
|
|
|
|
if not node.version.topic_command_supports_bootstrap_server(): |
|
|
|
|
return False |
|
|
|
|
return True |
|
|
|
|
|
|
|
|
|
def all_nodes_topic_command_supports_if_not_exists_with_bootstrap_server(self): |
|
|
|
|
for node in self.nodes: |
|
|
|
|
if not node.version.topic_command_supports_if_not_exists_with_bootstrap_server(): |
|
|
|
|
return False |
|
|
|
|
return True |
|
|
|
|
|
|
|
|
|
def all_nodes_configs_command_uses_bootstrap_server(self): |
|
|
|
|
for node in self.nodes: |
|
|
|
|
if not node.version.kafka_configs_command_uses_bootstrap_server(): |
|
|
|
|
return False |
|
|
|
|
return True |
|
|
|
|
|
|
|
|
|
def all_nodes_configs_command_uses_bootstrap_server_scram(self): |
|
|
|
|
for node in self.nodes: |
|
|
|
|
if not node.version.kafka_configs_command_uses_bootstrap_server_scram(): |
|
|
|
|
return False |
|
|
|
|
return True |
|
|
|
|
|
|
|
|
|
def all_nodes_acl_command_supports_bootstrap_server(self): |
|
|
|
|
for node in self.nodes: |
|
|
|
|
if not node.version.acl_command_supports_bootstrap_server(): |
|
|
|
|
return False |
|
|
|
|
return True |
|
|
|
|
|
|
|
|
|
def all_nodes_reassign_partitions_command_supports_bootstrap_server(self): |
|
|
|
|
for node in self.nodes: |
|
|
|
|
if not node.version.reassign_partitions_command_supports_bootstrap_server(): |
|
|
|
|
return False |
|
|
|
|
return True |
|
|
|
|
|
|
|
|
|
def create_topic(self, topic_cfg, node=None): |
|
|
|
|
"""Run the admin tool create topic command. |
|
|
|
|
Specifying node is optional, and may be done if for different kafka nodes have different versions, |
|
|
|
|
and we care where command gets run. |
|
|
|
@ -475,12 +511,13 @@ class KafkaService(KafkaPathResolverMixin, JmxMixin, Service):
@@ -475,12 +511,13 @@ class KafkaService(KafkaPathResolverMixin, JmxMixin, Service):
|
|
|
|
|
self.logger.info("Creating topic %s with settings %s", |
|
|
|
|
topic_cfg["topic"], topic_cfg) |
|
|
|
|
|
|
|
|
|
use_zk_connection = topic_cfg.get('if-not-exists', False) or use_zk_to_create_topic |
|
|
|
|
force_use_zk_connection = not self.all_nodes_topic_command_supports_bootstrap_server() or\ |
|
|
|
|
(topic_cfg.get('if-not-exists', False) and not self.all_nodes_topic_command_supports_if_not_exists_with_bootstrap_server()) |
|
|
|
|
|
|
|
|
|
cmd = fix_opts_for_new_jvm(node) |
|
|
|
|
cmd += "%(kafka_topics_cmd)s %(connection_string)s --create --topic %(topic)s " % { |
|
|
|
|
'kafka_topics_cmd': self._kafka_topics_cmd(node, use_zk_connection), |
|
|
|
|
'connection_string': self._topic_command_connect_setting(node, use_zk_connection), |
|
|
|
|
'kafka_topics_cmd': self._kafka_topics_cmd(node, force_use_zk_connection), |
|
|
|
|
'connection_string': self._topic_command_connect_setting(node, force_use_zk_connection), |
|
|
|
|
'topic': topic_cfg.get("topic"), |
|
|
|
|
} |
|
|
|
|
if 'replica-assignment' in topic_cfg: |
|
|
|
@ -500,12 +537,12 @@ class KafkaService(KafkaPathResolverMixin, JmxMixin, Service):
@@ -500,12 +537,12 @@ class KafkaService(KafkaPathResolverMixin, JmxMixin, Service):
|
|
|
|
|
for config_name, config_value in topic_cfg["configs"].items(): |
|
|
|
|
cmd += " --config %s=%s" % (config_name, str(config_value)) |
|
|
|
|
|
|
|
|
|
cmd += self._kafka_topics_cmd_config(node, use_zk_connection) |
|
|
|
|
cmd += self._kafka_topics_cmd_config(node, force_use_zk_connection) |
|
|
|
|
|
|
|
|
|
self.logger.info("Running topic creation command...\n%s" % cmd) |
|
|
|
|
node.account.ssh(cmd) |
|
|
|
|
|
|
|
|
|
def delete_topic(self, topic, node=None, use_zk_to_delete_topic=False): |
|
|
|
|
def delete_topic(self, topic, node=None): |
|
|
|
|
""" |
|
|
|
|
Delete a topic with the topics command |
|
|
|
|
:param topic: |
|
|
|
@ -516,22 +553,27 @@ class KafkaService(KafkaPathResolverMixin, JmxMixin, Service):
@@ -516,22 +553,27 @@ class KafkaService(KafkaPathResolverMixin, JmxMixin, Service):
|
|
|
|
|
node = self.nodes[0] |
|
|
|
|
self.logger.info("Deleting topic %s" % topic) |
|
|
|
|
|
|
|
|
|
force_use_zk_connection = not self.all_nodes_topic_command_supports_bootstrap_server() |
|
|
|
|
|
|
|
|
|
cmd = fix_opts_for_new_jvm(node) |
|
|
|
|
cmd += "%s %s --topic %s --delete %s" % \ |
|
|
|
|
(self._kafka_topics_cmd(node=node, use_zk_connection=use_zk_to_delete_topic), |
|
|
|
|
self._topic_command_connect_setting(node=node, use_zk_connection=use_zk_to_delete_topic), |
|
|
|
|
topic, self._kafka_topics_cmd_config(node=node, use_zk_connection=use_zk_to_delete_topic)) |
|
|
|
|
(self._kafka_topics_cmd(node, force_use_zk_connection), |
|
|
|
|
self._topic_command_connect_setting(node, force_use_zk_connection), |
|
|
|
|
topic, self._kafka_topics_cmd_config(node, force_use_zk_connection)) |
|
|
|
|
self.logger.info("Running topic delete command...\n%s" % cmd) |
|
|
|
|
node.account.ssh(cmd) |
|
|
|
|
|
|
|
|
|
def describe_topic(self, topic, node=None, use_zk_to_describe_topic=True): |
|
|
|
|
def describe_topic(self, topic, node=None): |
|
|
|
|
if node is None: |
|
|
|
|
node = self.nodes[0] |
|
|
|
|
|
|
|
|
|
force_use_zk_connection = not self.all_nodes_topic_command_supports_bootstrap_server() |
|
|
|
|
|
|
|
|
|
cmd = fix_opts_for_new_jvm(node) |
|
|
|
|
cmd += "%s %s --topic %s --describe %s" % \ |
|
|
|
|
(self._kafka_topics_cmd(node=node, use_zk_connection=use_zk_to_describe_topic), |
|
|
|
|
self._topic_command_connect_setting(node=node, use_zk_connection=use_zk_to_describe_topic), |
|
|
|
|
topic, self._kafka_topics_cmd_config(node=node, use_zk_connection=use_zk_to_describe_topic)) |
|
|
|
|
(self._kafka_topics_cmd(node, force_use_zk_connection), |
|
|
|
|
self._topic_command_connect_setting(node, force_use_zk_connection), |
|
|
|
|
topic, self._kafka_topics_cmd_config(node, force_use_zk_connection)) |
|
|
|
|
|
|
|
|
|
self.logger.info("Running topic describe command...\n%s" % cmd) |
|
|
|
|
output = "" |
|
|
|
@ -539,14 +581,16 @@ class KafkaService(KafkaPathResolverMixin, JmxMixin, Service):
@@ -539,14 +581,16 @@ class KafkaService(KafkaPathResolverMixin, JmxMixin, Service):
|
|
|
|
|
output += line |
|
|
|
|
return output |
|
|
|
|
|
|
|
|
|
def list_topics(self, node=None, use_zk_to_list_topic=True): |
|
|
|
|
def list_topics(self, node=None): |
|
|
|
|
if node is None: |
|
|
|
|
node = self.nodes[0] |
|
|
|
|
|
|
|
|
|
force_use_zk_connection = not self.all_nodes_topic_command_supports_bootstrap_server() |
|
|
|
|
|
|
|
|
|
cmd = fix_opts_for_new_jvm(node) |
|
|
|
|
cmd += "%s %s --list %s" % (self._kafka_topics_cmd(node, use_zk_to_list_topic), |
|
|
|
|
self._topic_command_connect_setting(node, use_zk_to_list_topic), |
|
|
|
|
self._kafka_topics_cmd_config(node, use_zk_to_list_topic)) |
|
|
|
|
cmd += "%s %s --list %s" % (self._kafka_topics_cmd(node, force_use_zk_connection), |
|
|
|
|
self._topic_command_connect_setting(node, force_use_zk_connection), |
|
|
|
|
self._kafka_topics_cmd_config(node, force_use_zk_connection)) |
|
|
|
|
for line in node.account.ssh_capture(cmd): |
|
|
|
|
if not line.startswith("SLF4J"): |
|
|
|
|
yield line.rstrip() |
|
|
|
@ -578,7 +622,7 @@ class KafkaService(KafkaPathResolverMixin, JmxMixin, Service):
@@ -578,7 +622,7 @@ class KafkaService(KafkaPathResolverMixin, JmxMixin, Service):
|
|
|
|
|
|
|
|
|
|
def _connect_setting_kafka_configs(self, node): |
|
|
|
|
# Use this for everything related to kafka-configs except User SCRAM Credentials |
|
|
|
|
if node.version.kafka_configs_command_uses_bootstrap_server(): |
|
|
|
|
if self.all_nodes_configs_command_uses_bootstrap_server(): |
|
|
|
|
return "--bootstrap-server %s --command-config <(echo '%s')" % (self.bootstrap_servers(self.security_protocol), |
|
|
|
|
self.security_config.client_config()) |
|
|
|
|
else: |
|
|
|
@ -586,13 +630,32 @@ class KafkaService(KafkaPathResolverMixin, JmxMixin, Service):
@@ -586,13 +630,32 @@ class KafkaService(KafkaPathResolverMixin, JmxMixin, Service):
|
|
|
|
|
|
|
|
|
|
def _connect_setting_kafka_configs_scram(self, node): |
|
|
|
|
# Use this for kafka-configs when operating on User SCRAM Credentials |
|
|
|
|
if node.version.kafka_configs_command_uses_bootstrap_server_scram(): |
|
|
|
|
if self.all_nodes_configs_command_uses_bootstrap_server_scram(): |
|
|
|
|
return "--bootstrap-server %s --command-config <(echo '%s')" %\ |
|
|
|
|
(self.bootstrap_servers(self.security_protocol), |
|
|
|
|
self.security_config.client_config(use_inter_broker_mechanism_for_client = True)) |
|
|
|
|
else: |
|
|
|
|
return "--zookeeper %s %s" % (self.zk_connect_setting(), self.zk.zkTlsConfigFileOption()) |
|
|
|
|
|
|
|
|
|
def kafka_acls_cmd(self, node, force_use_zk_connection): |
|
|
|
|
""" |
|
|
|
|
Returns kafka-acls.sh command path with jaas configuration and krb5 environment variable |
|
|
|
|
set. If Admin client is not going to be used, don't set the environment variable. |
|
|
|
|
""" |
|
|
|
|
kafka_acls_script = self.path.script("kafka-acls.sh", node) |
|
|
|
|
skip_security_settings = force_use_zk_connection or not self.all_nodes_acl_command_supports_bootstrap_server() |
|
|
|
|
return kafka_acls_script if skip_security_settings else \ |
|
|
|
|
"KAFKA_OPTS='-D%s -D%s' %s" % (KafkaService.JAAS_CONF_PROPERTY, KafkaService.KRB5_CONF, kafka_acls_script) |
|
|
|
|
|
|
|
|
|
def run_cli_tool(self, node, cmd): |
|
|
|
|
output = "" |
|
|
|
|
self.logger.debug(cmd) |
|
|
|
|
for line in node.account.ssh_capture(cmd): |
|
|
|
|
if not line.startswith("SLF4J"): |
|
|
|
|
output += line |
|
|
|
|
self.logger.debug(output) |
|
|
|
|
return output |
|
|
|
|
|
|
|
|
|
def parse_describe_topic(self, topic_description): |
|
|
|
|
"""Parse output of kafka-topics.sh --describe (or describe_topic() method above), which is a string of form |
|
|
|
|
PartitionCount:2\tReplicationFactor:2\tConfigs: |
|
|
|
@ -624,7 +687,7 @@ class KafkaService(KafkaPathResolverMixin, JmxMixin, Service):
@@ -624,7 +687,7 @@ class KafkaService(KafkaPathResolverMixin, JmxMixin, Service):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _connect_setting_reassign_partitions(self, node): |
|
|
|
|
if node.version.reassign_partitions_command_supports_bootstrap_server(): |
|
|
|
|
if self.all_nodes_reassign_partitions_command_supports_bootstrap_server(): |
|
|
|
|
return "--bootstrap-server %s " % self.bootstrap_servers(self.security_protocol) |
|
|
|
|
else: |
|
|
|
|
return "--zookeeper %s " % self.zk_connect_setting() |
|
|
|
@ -748,37 +811,68 @@ class KafkaService(KafkaPathResolverMixin, JmxMixin, Service):
@@ -748,37 +811,68 @@ class KafkaService(KafkaPathResolverMixin, JmxMixin, Service):
|
|
|
|
|
self.stop_node(node, clean_shutdown, timeout_sec) |
|
|
|
|
self.start_node(node, timeout_sec) |
|
|
|
|
|
|
|
|
|
def _describe_topic_line_for_partition(self, partition, describe_topic_output): |
|
|
|
|
# Lines look like this: Topic: test_topic Partition: 0 Leader: 3 Replicas: 3,2 Isr: 3,2 |
|
|
|
|
grep_for = "Partition: %i\t" % (partition) # be sure to include trailing tab, otherwise 1 might match 10 (for example) |
|
|
|
|
found_lines = [line for line in describe_topic_output.splitlines() if grep_for in line] |
|
|
|
|
return None if not found_lines else found_lines[0] |
|
|
|
|
|
|
|
|
|
def isr_idx_list(self, topic, partition=0): |
|
|
|
|
""" Get in-sync replica list the given topic and partition. |
|
|
|
|
""" |
|
|
|
|
self.logger.debug("Querying zookeeper to find in-sync replicas for topic %s and partition %d" % (topic, partition)) |
|
|
|
|
zk_path = "/brokers/topics/%s/partitions/%d/state" % (topic, partition) |
|
|
|
|
partition_state = self.zk.query(zk_path, chroot=self.zk_chroot) |
|
|
|
|
node = self.nodes[0] |
|
|
|
|
if not self.all_nodes_topic_command_supports_bootstrap_server(): |
|
|
|
|
self.logger.debug("Querying zookeeper to find in-sync replicas for topic %s and partition %d" % (topic, partition)) |
|
|
|
|
zk_path = "/brokers/topics/%s/partitions/%d/state" % (topic, partition) |
|
|
|
|
partition_state = self.zk.query(zk_path, chroot=self.zk_chroot) |
|
|
|
|
|
|
|
|
|
if partition_state is None: |
|
|
|
|
raise Exception("Error finding partition state for topic %s and partition %d." % (topic, partition)) |
|
|
|
|
if partition_state is None: |
|
|
|
|
raise Exception("Error finding partition state for topic %s and partition %d." % (topic, partition)) |
|
|
|
|
|
|
|
|
|
partition_state = json.loads(partition_state) |
|
|
|
|
self.logger.info(partition_state) |
|
|
|
|
partition_state = json.loads(partition_state) |
|
|
|
|
self.logger.info(partition_state) |
|
|
|
|
|
|
|
|
|
isr_idx_list = partition_state["isr"] |
|
|
|
|
else: |
|
|
|
|
self.logger.debug("Querying Kafka Admin API to find in-sync replicas for topic %s and partition %d" % (topic, partition)) |
|
|
|
|
describe_output = self.describe_topic(topic, node) |
|
|
|
|
self.logger.debug(describe_output) |
|
|
|
|
requested_partition_line = self._describe_topic_line_for_partition(partition, describe_output) |
|
|
|
|
# e.g. Topic: test_topic Partition: 0 Leader: 3 Replicas: 3,2 Isr: 3,2 |
|
|
|
|
if not requested_partition_line: |
|
|
|
|
raise Exception("Error finding partition state for topic %s and partition %d." % (topic, partition)) |
|
|
|
|
isr_csv = requested_partition_line.split()[9] # 10th column from above |
|
|
|
|
isr_idx_list = [int(i) for i in isr_csv.split(",")] |
|
|
|
|
|
|
|
|
|
isr_idx_list = partition_state["isr"] |
|
|
|
|
self.logger.info("Isr for topic %s and partition %d is now: %s" % (topic, partition, isr_idx_list)) |
|
|
|
|
return isr_idx_list |
|
|
|
|
|
|
|
|
|
def replicas(self, topic, partition=0): |
|
|
|
|
""" Get the assigned replicas for the given topic and partition. |
|
|
|
|
""" |
|
|
|
|
self.logger.debug("Querying zookeeper to find assigned replicas for topic %s and partition %d" % (topic, partition)) |
|
|
|
|
zk_path = "/brokers/topics/%s" % (topic) |
|
|
|
|
assignment = self.zk.query(zk_path, chroot=self.zk_chroot) |
|
|
|
|
node = self.nodes[0] |
|
|
|
|
if not self.all_nodes_topic_command_supports_bootstrap_server(): |
|
|
|
|
self.logger.debug("Querying zookeeper to find assigned replicas for topic %s and partition %d" % (topic, partition)) |
|
|
|
|
zk_path = "/brokers/topics/%s" % (topic) |
|
|
|
|
assignment = self.zk.query(zk_path, chroot=self.zk_chroot) |
|
|
|
|
|
|
|
|
|
if assignment is None: |
|
|
|
|
raise Exception("Error finding partition state for topic %s and partition %d." % (topic, partition)) |
|
|
|
|
if assignment is None: |
|
|
|
|
raise Exception("Error finding partition state for topic %s and partition %d." % (topic, partition)) |
|
|
|
|
|
|
|
|
|
assignment = json.loads(assignment) |
|
|
|
|
self.logger.info(assignment) |
|
|
|
|
assignment = json.loads(assignment) |
|
|
|
|
self.logger.info(assignment) |
|
|
|
|
|
|
|
|
|
replicas = assignment["partitions"][str(partition)] |
|
|
|
|
replicas = assignment["partitions"][str(partition)] |
|
|
|
|
else: |
|
|
|
|
self.logger.debug("Querying Kafka Admin API to find replicas for topic %s and partition %d" % (topic, partition)) |
|
|
|
|
describe_output = self.describe_topic(topic, node) |
|
|
|
|
self.logger.debug(describe_output) |
|
|
|
|
requested_partition_line = self._describe_topic_line_for_partition(partition, describe_output) |
|
|
|
|
# e.g. Topic: test_topic Partition: 0 Leader: 3 Replicas: 3,2 Isr: 3,2 |
|
|
|
|
if not requested_partition_line: |
|
|
|
|
raise Exception("Error finding partition state for topic %s and partition %d." % (topic, partition)) |
|
|
|
|
isr_csv = requested_partition_line.split()[7] # 8th column from above |
|
|
|
|
replicas = [int(i) for i in isr_csv.split(",")] |
|
|
|
|
|
|
|
|
|
self.logger.info("Assigned replicas for topic %s and partition %d is now: %s" % (topic, partition, replicas)) |
|
|
|
|
return [self.get_node(replica) for replica in replicas] |
|
|
|
@ -786,17 +880,29 @@ class KafkaService(KafkaPathResolverMixin, JmxMixin, Service):
@@ -786,17 +880,29 @@ class KafkaService(KafkaPathResolverMixin, JmxMixin, Service):
|
|
|
|
|
def leader(self, topic, partition=0): |
|
|
|
|
""" Get the leader replica for the given topic and partition. |
|
|
|
|
""" |
|
|
|
|
self.logger.debug("Querying zookeeper to find leader replica for topic %s and partition %d" % (topic, partition)) |
|
|
|
|
zk_path = "/brokers/topics/%s/partitions/%d/state" % (topic, partition) |
|
|
|
|
partition_state = self.zk.query(zk_path, chroot=self.zk_chroot) |
|
|
|
|
node = self.nodes[0] |
|
|
|
|
if not self.all_nodes_topic_command_supports_bootstrap_server(): |
|
|
|
|
self.logger.debug("Querying zookeeper to find leader replica for topic %s and partition %d" % (topic, partition)) |
|
|
|
|
zk_path = "/brokers/topics/%s/partitions/%d/state" % (topic, partition) |
|
|
|
|
partition_state = self.zk.query(zk_path, chroot=self.zk_chroot) |
|
|
|
|
|
|
|
|
|
if partition_state is None: |
|
|
|
|
raise Exception("Error finding partition state for topic %s and partition %d." % (topic, partition)) |
|
|
|
|
|
|
|
|
|
if partition_state is None: |
|
|
|
|
raise Exception("Error finding partition state for topic %s and partition %d." % (topic, partition)) |
|
|
|
|
partition_state = json.loads(partition_state) |
|
|
|
|
self.logger.info(partition_state) |
|
|
|
|
|
|
|
|
|
partition_state = json.loads(partition_state) |
|
|
|
|
self.logger.info(partition_state) |
|
|
|
|
leader_idx = int(partition_state["leader"]) |
|
|
|
|
else: |
|
|
|
|
self.logger.debug("Querying Kafka Admin API to find leader for topic %s and partition %d" % (topic, partition)) |
|
|
|
|
describe_output = self.describe_topic(topic, node) |
|
|
|
|
self.logger.debug(describe_output) |
|
|
|
|
requested_partition_line = self._describe_topic_line_for_partition(partition, describe_output) |
|
|
|
|
# e.g. Topic: test_topic Partition: 0 Leader: 3 Replicas: 3,2 Isr: 3,2 |
|
|
|
|
if not requested_partition_line: |
|
|
|
|
raise Exception("Error finding partition state for topic %s and partition %d." % (topic, partition)) |
|
|
|
|
leader_idx = int(requested_partition_line.split()[5]) # 6th column from above |
|
|
|
|
|
|
|
|
|
leader_idx = int(partition_state["leader"]) |
|
|
|
|
self.logger.info("Leader for topic %s and partition %d is now: %d" % (topic, partition, leader_idx)) |
|
|
|
|
return self.get_node(leader_idx) |
|
|
|
|
|
|
|
|
@ -840,13 +946,7 @@ class KafkaService(KafkaPathResolverMixin, JmxMixin, Service):
@@ -840,13 +946,7 @@ class KafkaService(KafkaPathResolverMixin, JmxMixin, Service):
|
|
|
|
|
(consumer_group_script, |
|
|
|
|
self.bootstrap_servers(self.security_protocol), |
|
|
|
|
command_config) |
|
|
|
|
output = "" |
|
|
|
|
self.logger.debug(cmd) |
|
|
|
|
for line in node.account.ssh_capture(cmd): |
|
|
|
|
if not line.startswith("SLF4J"): |
|
|
|
|
output += line |
|
|
|
|
self.logger.debug(output) |
|
|
|
|
return output |
|
|
|
|
return self.run_cli_tool(node, cmd) |
|
|
|
|
|
|
|
|
|
def describe_consumer_group(self, group, node=None, command_config=None): |
|
|
|
|
""" Describe a consumer group. |
|
|
|
@ -877,12 +977,12 @@ class KafkaService(KafkaPathResolverMixin, JmxMixin, Service):
@@ -877,12 +977,12 @@ class KafkaService(KafkaPathResolverMixin, JmxMixin, Service):
|
|
|
|
|
def zk_connect_setting(self): |
|
|
|
|
return self.zk.connect_setting(self.zk_chroot, self.zk_client_secure) |
|
|
|
|
|
|
|
|
|
def _topic_command_connect_setting(self, node, use_zk_connection=True): |
|
|
|
|
def _topic_command_connect_setting(self, node, force_use_zk_connection): |
|
|
|
|
""" |
|
|
|
|
Checks if --bootstrap-server config is supported, if yes then returns a string with |
|
|
|
|
bootstrap server, otherwise returns zookeeper connection string. |
|
|
|
|
""" |
|
|
|
|
if node.version.topic_command_supports_bootstrap_server() and not use_zk_connection: |
|
|
|
|
if not force_use_zk_connection and self.all_nodes_topic_command_supports_bootstrap_server(): |
|
|
|
|
connection_setting = "--bootstrap-server %s" % (self.bootstrap_servers(self.security_protocol)) |
|
|
|
|
else: |
|
|
|
|
connection_setting = "--zookeeper %s" % (self.zk_connect_setting()) |
|
|
|
|