Skip to content

Commit

Permalink
Summary and Collectinfo Improvements (#29)
Browse files Browse the repository at this point in the history
* TOOLS-984: [asadm-collectinfoanalyser] Fix to accept folder path with multiple collectinfo files/snapshots.

* TOOLS-986: [asadm] Modify to deal with config name change from "repl-factor" to "replication-factor".

* TOOLS-1003: [asadm] Modify to allow ssh with default credentials.

* TOOLS-1014: [asadm-healthcheck] Add query that compares and highlights values found in aerospike.conf that differ in collectinfo/live cluster.

* TOOLS-1019: [asadm-collectinfoanalyser] Add 'show latency' command.

* TOOLS-1020: [asadm-healthcheck] Remove difference warning for config items which should/could be different.

* TOOLS-1032: [asadm] Include rack-aware as a part of the feature listing.

* TOOLS-1033: [asadm] Modify summary to show edition prefix E or C with Aerospike Server Version.

* TOOLS-1034: [asadm] Modify Summary output to show more details.

* TOOLS-1039: [asadm-healthcheck] Add warning for device re-use on a node.

* TOOLS-1041: [asadm] Add kernel version in OS field in summary.

* TOOLS-1044: [asadm] Add ARP count as new command in collectinfo.

* TOOLS-1045: [asadm] Fix to display complete and correct configured IPv6 address.

* TOOLS-1046: [asadm-healthcheck] Add rule to check stop-writes.

* TOOLS-1047: [asadm] Add option to allow user to define collectinfo name's prefix.
  • Loading branch information
hbpatre committed Dec 19, 2017
1 parent 34fdaf5 commit 89aabe1
Show file tree
Hide file tree
Showing 22 changed files with 1,452 additions and 321 deletions.
284 changes: 187 additions & 97 deletions lib/basiccontroller.py

Large diffs are not rendered by default.

80 changes: 58 additions & 22 deletions lib/client/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

from lib.client import util
from lib.client.assocket import ASSocket
from lib.collectinfo_parser import conf_parser
from lib.collectinfo_parser.full_parser import parse_system_live_command

#### Remote Server connection module
Expand Down Expand Up @@ -155,6 +156,9 @@ def __init__(self, address, port=3000, tls_name=None, timeout=5, user=None,
except Exception:
pass

# configurations from conf file
self.conf_data = {}

def _initialize_socket_pool(self):
self.socket_pool = {}
self.socket_pool[self.port] = set()
Expand Down Expand Up @@ -280,7 +284,7 @@ def is_XDR_enabled(self):
if isinstance(config, Exception):
return False
try:
xdr_enabled = config['xdr']['enable-xdr']
xdr_enabled = config['enable-xdr']
return xdr_enabled == 'true'
except Exception:
pass
Expand Down Expand Up @@ -764,28 +768,28 @@ def info_get_config(self, stanza="", namespace="", namespace_id=""):
config = {}
if stanza == 'namespace':
if namespace != "":
config[stanza] = {namespace: util.info_to_dict(
config = {namespace: util.info_to_dict(
self.info("get-config:context=namespace;id=%s" % namespace))}
if namespace_id == "":
namespaces = self.info_namespaces()
if namespaces and namespace in namespaces:
namespace_id = namespaces.index(namespace)
if namespace_id != "":
config[stanza][namespace]["nsid"] = str(namespace_id)
config[namespace]["nsid"] = str(namespace_id)
else:
namespace_configs = {}
namespaces = self.info_namespaces()
for index, namespace in enumerate(namespaces):
namespace_config = self.info_get_config(
'namespace', namespace, namespace_id=index)
namespace_config = namespace_config['namespace'][namespace]
namespace_config = namespace_config[namespace]
namespace_configs[namespace] = namespace_config
config['namespace'] = namespace_configs
config = namespace_configs

elif stanza == '' or stanza == 'service':
config['service'] = util.info_to_dict(self.info("get-config:"))
config = util.info_to_dict(self.info("get-config:"))
elif stanza != 'all':
config[stanza] = util.info_to_dict(
config = util.info_to_dict(
self.info("get-config:context=%s" % stanza))
elif stanza == "all":
config['namespace'] = self.info_get_config("namespace")
Expand All @@ -794,6 +798,35 @@ def info_get_config(self, stanza="", namespace="", namespace_id=""):
# config["network"] = self.info_get_config("network")
return config

@return_exceptions
def info_get_originalconfig(self, stanza=""):
"""
Get the original config (from conf file) for a node. This should include the following
stanzas: Service, Network, XDR, DC, and Namespace
Returns:
dict -- stanza --> [namespace] --> param --> value
"""
config = {}
if not self.localhost:
return config

if not self.conf_data:
conf_path = "/etc/aerospike/aerospike.conf"
self.conf_data = conf_parser.parse_file(conf_path)
if "namespace" in self.conf_data:
for ns in self.conf_data["namespace"].keys():
if "service" in self.conf_data["namespace"][ns]:
self.conf_data["namespace"][ns] = self.conf_data["namespace"][ns]["service"]

try:
config = self.conf_data[stanza]

except Exception:
pass

return config

def _update_total_latency(self, t_rows, row):
if not row or not isinstance(row, list):
return t_rows
Expand Down Expand Up @@ -855,15 +888,15 @@ def info_latency(self, back=None, duration=None, slice_tm=None, ns_set=None):
start_time = None
columns = []
ns_hist_pattern = '{([A-Za-z_\d-]+)}-([A-Za-z_-]+)'
total_key = (" ", "total")
total_key = "total"

while tdata != []:
row = tdata.pop(0)
if not row:
continue
row = row.split(",")
# row format : <IP[:PORT]>,<USER_ID>,<PASSWORD or PASSPHRASE>,<SSH_KEY>
# only IP:PORT and USER_ID are compulsory entries, so we need to check row length for 2

# neglect if error string
if len(row) < 2:
continue

Expand Down Expand Up @@ -896,12 +929,14 @@ def info_latency(self, back=None, duration=None, slice_tm=None, ns_set=None):
if hist_name not in data:
data[hist_name] = {}
if ns:
ns_key = (ns, "namespace")
ns_key = "namespace"
if ns_key not in data[hist_name]:
data[hist_name][ns_key] = {}
data[hist_name][ns_key]["columns"] = columns
data[hist_name][ns_key]["values"] = []
data[hist_name][ns_key][
if ns not in data[hist_name][ns_key]:
data[hist_name][ns_key][ns] = {}
data[hist_name][ns_key][ns]["columns"] = columns
data[hist_name][ns_key][ns]["values"] = []
data[hist_name][ns_key][ns][
"values"].append(copy.deepcopy(row))
if total_key not in data[hist_name]:
data[hist_name][total_key] = {}
Expand Down Expand Up @@ -979,18 +1014,19 @@ def info_dc_get_config(self):
Returns:
dict -- {dc_name1:{config_name : config_value, ...}, dc_name2:{config_name : config_value, ...}}
"""

if self.is_feature_present('xdr'):
configs = self.info("get-dc-config")
if not configs or isinstance(configs, Exception):
configs = self.info("get-dc-config:")
if not configs or isinstance(configs, Exception):
return {}
return util.info_to_dict_multi_level(configs, ["dc-name", "DC_Name"])
return util.info_to_dict_multi_level(configs, ["dc-name", "DC_Name"], ignore_field_without_key_value_delimiter=False)

configs = self.xdr_info("get-dc-config")
if not configs or isinstance(configs, Exception):
return {}
return util.info_to_dict_multi_level(configs, ["dc-name", "DC_Name"])
return util.info_to_dict_multi_level(configs, ["dc-name", "DC_Name"], ignore_field_without_key_value_delimiter=False)

@return_exceptions
def info_XDR_get_config(self):
Expand All @@ -1002,13 +1038,13 @@ def info_XDR_get_config(self):
# required for old aerospike server versions (<3.8)
xdr_configs_xdr = self.xdr_info('get-config')
if xdr_configs_xdr and not isinstance(xdr_configs_xdr, Exception):
xdr_configs_xdr = {'xdr': util.info_to_dict(xdr_configs_xdr)}
if xdr_configs_xdr['xdr'] and not isinstance(xdr_configs_xdr['xdr'], Exception):
if xdr_configs and xdr_configs['xdr'] and not isinstance(xdr_configs['xdr'], Exception):
xdr_configs['xdr'].update(xdr_configs_xdr['xdr'])
xdr_configs_xdr = util.info_to_dict(xdr_configs_xdr)
if xdr_configs_xdr and not isinstance(xdr_configs_xdr, Exception):
if xdr_configs and not isinstance(xdr_configs, Exception):
xdr_configs.update(xdr_configs_xdr)
else:
xdr_configs = {}
xdr_configs['xdr'] = xdr_configs_xdr['xdr']
xdr_configs = xdr_configs_xdr

return xdr_configs

@return_exceptions
Expand Down
39 changes: 34 additions & 5 deletions lib/client/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import pipes


def info_to_dict(value, delimiter=';'):
def info_to_dict(value, delimiter=';', ignore_field_without_key_value_delimiter=True):
"""
Simple function to convert string to dict
"""
Expand All @@ -31,8 +31,37 @@ def info_to_dict(value, delimiter=';'):
return value

stat_dict = {}
stat_param = itertools.imap(lambda sp: info_to_tuple(sp, "="),
info_to_list(value, delimiter))
_tmp_value_list = info_to_list(value, delimiter)
_value_list = []
delimiter2 = "="

if ignore_field_without_key_value_delimiter:
_value_list = _tmp_value_list

else:
# Sometimes value contains confusing delimiter
# In such cases, after splitting on delimiter, we get items without next delimiter (=).
# By default we ignore such items. But in some cases like dc configs we need to accept those and append to previous item.
# For ex. "dc-name=REMOTE_DC_1:nodes=2000:10:3:0:0:0:100:d+3000:int-ext-ipmap=172.68.17.123...."
# In this example, first split will give ["dc-name=REMOTE_DC_1", "nodes=2000", "10", "3",
# "0", "0", "100", "d+3000", "int-ext-ipmap=172.68.17.123", ....]. In such cases we need to append items
# (10, 3, 0, 0, 100, "d+3000") to previous valid item ("nodes=2000") with delimiter (":").
# It gives "nodes=2000:10:3:0:0:0:100:d+3000".

for _v in _tmp_value_list:
if delimiter2 not in _v:
try:
_value_list[-1] = str(_value_list[-1]) + delimiter + str(_v)

except Exception:
pass

else:
_value_list.append(_v)

stat_param = itertools.imap(lambda sp: info_to_tuple(sp, delimiter2),
_value_list)

for g in itertools.groupby(stat_param, lambda x: x[0]):
try:
value = map(lambda v: v[1], g[1])
Expand All @@ -48,7 +77,7 @@ def info_to_dict(value, delimiter=';'):
return stat_dict


def info_to_dict_multi_level(value, keyname, delimiter1=';', delimiter2=':'):
def info_to_dict_multi_level(value, keyname, delimiter1=';', delimiter2=':', ignore_field_without_key_value_delimiter=True):
"""
Simple function to convert string to dict where string is format like
field1_section1=value1<delimiter2>field2_section1=value2<delimiter2>... <delimiter1> field1_section2=value3<delimiter2>field2_section2=value4<delimiter2>...
Expand All @@ -68,7 +97,7 @@ def info_to_dict_multi_level(value, keyname, delimiter1=';', delimiter2=':'):
return value_dict

for v in value_list:
values = info_to_dict(v, delimiter2)
values = info_to_dict(v, delimiter2, ignore_field_without_key_value_delimiter=ignore_field_without_key_value_delimiter)
if not values or isinstance(values, Exception):
continue
for _k in keyname:
Expand Down
12 changes: 10 additions & 2 deletions lib/collectinfo/cinfolog.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,13 @@ def get_data(self, type="", stanza=""):
data[node] = copy.deepcopy(d)
continue

if stanza in ['namespace', 'bin', 'bins', 'set', 'sindex']:
if stanza in ['namespace', 'bin', 'bins', 'set', 'sindex', 'namespace_list']:
d = d["namespace"]

if stanza == "namespace_list":
data[node] = d.keys()
continue

for ns_name in d.keys():
try:
if stanza == "namespace":
Expand Down Expand Up @@ -322,12 +326,16 @@ def __init__(self, cinfo_path, files, reader):
self.snapshots = {}
self.data = {}
parse_info_all(files, self.data, True)

if self.data:
for ts in self.data:
for ts in sorted(self.data.keys(), reverse=True):
if self.data[ts]:
for cl in self.data[ts]:
self.snapshots[ts] = CollectinfoSnapshot(cl, ts, self.data[ts][cl], cinfo_path)

# Since we are not dealing with timeseries we should fetch only one snapshot
break

def destroy(self):
try:
del self.files
Expand Down
9 changes: 9 additions & 0 deletions lib/collectinfo/loghdlr.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ def get_cinfo_log_at(self, timestamp=""):
def info_getconfig(self, stanza="", flip=False):
return self._fetch_from_cinfo_log(type="config", stanza=stanza, flip=flip)

def info_get_originalconfig(self, stanza="", flip=False):
return self._fetch_from_cinfo_log(type="original_config", stanza=stanza, flip=flip)

def info_statistics(self, stanza="", flip=False):
return self._fetch_from_cinfo_log(type="statistics", stanza=stanza, flip=flip)

Expand Down Expand Up @@ -136,12 +139,18 @@ def info_histogram(self, stanza="", flip=False):
pass
return res_dict

def info_latency(self):
return self._fetch_from_cinfo_log(type="latency")

def info_meta_data(self, stanza=""):
return self._fetch_from_cinfo_log(type="meta_data", stanza=stanza)

def info_pmap(self):
return self._fetch_from_cinfo_log(type="pmap")

def info_namespaces(self):
return self._fetch_from_cinfo_log(type="config", stanza="namespace_list")

def get_sys_data(self, stanza=""):
res_dict = {}
if not stanza:
Expand Down
Loading

0 comments on commit 89aabe1

Please sign in to comment.