A-Tune/collection/parser/iostat_parser.py

119 lines
4.5 KiB
Python
Raw Normal View History

#!/usr/bin/python3
# -*- coding: utf-8 -*-
# Copyright (c) 2019 Huawei Technologies Co., Ltd.
# A-Tune is licensed under the Mulan PSL v1.
# You can use this software according to the terms and conditions of the Mulan PSL v1.
# You may obtain a copy of Mulan PSL v1 at:
# http://license.coscl.org.cn/MulanPSL
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
# PURPOSE.
# See the Mulan PSL v1 for more details.
# Create: 2019-10-29
"""
The parser to parse the output of iostat.
"""
from __future__ import print_function
from . import base
class IostatParser(base.Parser):
"""The parser to parse the output of iostat"""
def __init__(self, raw_data_file, data_to_collect, skip_first=True, **kwargs):
"""Initialize a iostat parser.
@param raw_data_file: the path of raw data
@param data_to_collect: list of str which represents the metrics to parse
@param dev_list: list of devices of which metrics whille be collectted
@param skip_first: skip the first batch of data or not. Bacause the first
output of iostat is the statistics information since
boot, we can skip it.
@param alias: alias name of output fields (default: "iostat")
"""
base.Parser.__init__(self, raw_data_file, data_to_collect, **kwargs)
self._dev_list = kwargs.get("dev_list", None)
self._check_dev()
# the first output of iostat is the statistics information since boot, so we can skip it
if skip_first:
self.get_next_data()
def _check_data_to_collect(self):
"""Read the first batch output of iostat and check whether or not all
metrics in data_to_collect are in the output. If not, it will raise
ValueError.
"""
with open(self._raw_data_file, 'r') as raw_data_fd:
# skip the first line
raw_data_fd.readline()
for line in raw_data_fd:
line = line.strip()
if line:
datas = line.split()[1:]
break
diff_set = set(self._data_to_collect) - set(datas)
if diff_set:
raise ValueError("`{}`: Unknown data name `{}`".format(self._raw_data_file, ','.join(diff_set)))
def _check_dev(self):
"""Read the first batch output of iostat and check whether or not all
devices in dev_list are in the output. If not, it will raise ValueError.
"""
if not self._dev_list:
raise ValueError("You must assigned at least one device")
devs = set()
with open(self._raw_data_file, 'r') as raw_data_fd:
raw_data_fd.readline()
for line in raw_data_fd:
if line.strip():
break
for line in raw_data_fd:
if not line.strip():
break
devs.add(line.split()[0])
diff_set = set(self._dev_list) - devs
if diff_set:
raise ValueError("Can not find block device `{}`".format(','.join(diff_set)))
def _get_iter(self):
"""Get the iteration of the iostat parser.
@return: the iteration of the iostat parser
"""
# status: 0: ok, we have read data successfully
# 1: we need to read more data
status = 0
header = False
data = {}
attrs = []
with open(self._raw_data_file, 'r') as raw_data_fd:
raw_data_fd.readline()
for row_num, line in enumerate(raw_data_fd, 2):
line = line.strip()
if not line:
if status:
yield [data[dev][attr] for dev in self._dev_list for attr in self._data_to_collect]
data.clear()
status = 0
header = True
continue
status = 1
line_part = line.split()
if header:
attrs = line_part[1:]
header = False
else:
if len(attrs) != len(line_part) - 1:
print("WARNING: {}: Line {}: The number of columns may be wrong."
.format(self._raw_data_file, row_num))
return
dev = line_part[0]
data[dev] = dict(zip(attrs, [float(d) for d in line_part[1:]]))