| #!/usr/bin/env python |
| |
| ''' |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| ''' |
| |
| import logging |
| import operator |
| import os |
| import platform |
| import psutil |
| import re |
| import socket |
| import threading |
| import time |
| from collections import namedtuple |
| |
| logger = logging.getLogger() |
| cached_hostname = None |
| |
| def bytes2human(n): |
| bytes = float(n) |
| gigabytes = bytes / 1073741824 |
| return '%.2f' % gigabytes |
| pass |
| |
| class HostInfo(): |
| |
| def __init__(self, config): |
| self.__last_network_io_time = 0 |
| self.__last_network_data = {} |
| self.__last_network_lock = threading.Lock() |
| self.__last_disk_io_time = 0 |
| self.__last_disk_data = {} |
| self.__host_static_info = self.get_host_static_info() |
| self.__config = config |
| |
| def get_cpu_times(self): |
| """ |
| Return cpu stats at current time |
| """ |
| cpu_times = psutil.cpu_times_percent() |
| cpu_count = self.__host_static_info.get('cpu_num', 1) |
| # Since only boot time which is a part of static info is not sent with |
| # other payload sending it with cpu stats. |
| boot_time = self.__host_static_info.get('boottime') |
| |
| result = { |
| 'cpu_num': int(cpu_count), |
| 'cpu_user': cpu_times.user if hasattr(cpu_times, 'user') else 0, |
| 'cpu_system': cpu_times.system if hasattr(cpu_times, 'system') else 0, |
| 'cpu_idle': cpu_times.idle if hasattr(cpu_times, 'idle') else 0, |
| 'cpu_nice': cpu_times.nice if hasattr(cpu_times, 'nice') else 0, |
| 'cpu_wio': cpu_times.iowait if hasattr(cpu_times, 'iowait') else 0, |
| 'cpu_intr': cpu_times.irq if hasattr(cpu_times, 'irq') else 0, |
| 'cpu_sintr': cpu_times.softirq if hasattr(cpu_times, 'softirq') else 0, |
| 'cpu_steal': cpu_times.steal if hasattr(cpu_times, 'steal') else 0, |
| 'boottime': long(boot_time) if boot_time else 0 |
| } |
| if platform.system() != "Windows": |
| load_avg = os.getloadavg() |
| result.update({ |
| 'load_one' : load_avg[0] if len(load_avg) > 0 else '', |
| 'load_five' : load_avg[1] if len(load_avg) > 1 else '', |
| 'load_fifteen' : load_avg[2] if len(load_avg) > 2 else '' |
| }) |
| return result |
| |
| pass |
| |
| def get_process_info(self): |
| """ |
| Return processes statistics at current time |
| """ |
| |
| STATUS_RUNNING = "running" |
| |
| proc_stats = psutil.process_iter() |
| |
| proc_run = 0 |
| proc_total = 0 |
| for proc in proc_stats: |
| proc_total += 1 |
| try: |
| if STATUS_RUNNING == proc.status(): |
| proc_run += 1 |
| except (psutil.NoSuchProcess, psutil.AccessDenied) as e: |
| #NOP |
| pass |
| pass |
| |
| return { |
| 'proc_run': proc_run, |
| 'proc_total': proc_total |
| } |
| pass |
| |
| def get_mem_info(self): |
| """ |
| Return memory statistics at current time |
| """ |
| |
| mem_stats = psutil.virtual_memory() |
| swap_stats = psutil.swap_memory() |
| mem_total = self.__host_static_info.get('mem_total') |
| swap_total = self.__host_static_info.get('swap_total') |
| |
| bytes2kilobytes = lambda x: x / 1024 |
| |
| return { |
| 'mem_total': bytes2kilobytes(mem_total) if mem_total else 0, |
| 'mem_used': bytes2kilobytes(mem_stats.used - mem_stats.cached) if hasattr(mem_stats, 'used') and hasattr(mem_stats, 'cached') else 0, # Used memory w/o cached |
| 'mem_free': bytes2kilobytes(mem_stats.available) if hasattr(mem_stats, 'available') else 0, # the actual amount of available memory |
| 'mem_shared': bytes2kilobytes(mem_stats.shared) if hasattr(mem_stats, 'shared') else 0, |
| 'mem_buffered': bytes2kilobytes(mem_stats.buffers) if hasattr(mem_stats, 'buffers') else 0, |
| 'mem_cached': bytes2kilobytes(mem_stats.cached) if hasattr(mem_stats, 'cached') else 0, |
| 'swap_free': bytes2kilobytes(swap_stats.free) if hasattr(swap_stats, 'free') else 0, |
| 'swap_used': bytes2kilobytes(swap_stats.used) if hasattr(swap_stats, 'used') else 0, |
| 'swap_total': bytes2kilobytes(swap_total) if swap_total else 0, |
| 'swap_in': bytes2kilobytes(swap_stats.sin) if hasattr(swap_stats, 'sin') else 0, |
| 'swap_out': bytes2kilobytes(swap_stats.sout) if hasattr(swap_stats, 'sout') else 0, |
| # todo: cannot send string |
| #'part_max_used' : disk_usage.get("max_part_used")[0], |
| } |
| pass |
| |
| def get_network_info(self): |
| """ |
| Return network counters |
| """ |
| |
| with self.__last_network_lock: |
| current_time = time.time() |
| delta = current_time - self.__last_network_io_time |
| self.__last_network_io_time = current_time |
| |
| if delta <= 0: |
| delta = float("inf") |
| |
| net_stats = psutil.net_io_counters(True) |
| new_net_stats = {} |
| |
| skip_virtual_interfaces = self.get_virtual_network_interfaces() if self.__config.get_virtual_interfaces_skip() == 'True' else [] |
| skip_network_patterns = self.__config.get_network_interfaces_skip_pattern() |
| skip_network_patterns_list = skip_network_patterns.split(',') if skip_network_patterns and skip_network_patterns != 'None' else [] |
| for interface, values in net_stats.iteritems(): |
| if interface != 'lo' and not interface in skip_virtual_interfaces: |
| ignore_network = False |
| for p in skip_network_patterns_list: |
| if re.match(p, interface): |
| ignore_network = True |
| if not ignore_network: |
| new_net_stats = {'bytes_out': new_net_stats.get('bytes_out', 0) + values.bytes_sent, |
| 'bytes_in': new_net_stats.get('bytes_in', 0) + values.bytes_recv, |
| 'pkts_out': new_net_stats.get('pkts_out', 0) + values.packets_sent, |
| 'pkts_in': new_net_stats.get('pkts_in', 0) + values.packets_recv |
| } |
| |
| with self.__last_network_lock: |
| result = dict((k, (v - self.__last_network_data.get(k, 0)) / delta) for k, v in new_net_stats.iteritems()) |
| result = dict((k, 0 if v < 0 else v) for k, v in result.iteritems()) |
| self.__last_network_data = new_net_stats |
| |
| return result |
| pass |
| |
| # Faster version |
| def get_combined_disk_usage(self): |
| combined_disk_total = 0 |
| combined_disk_used = 0 |
| combined_disk_free = 0 |
| combined_disk_percent = 0 |
| max_percent_usage = ('', 0) |
| |
| partition_count = 0 |
| devices = set() |
| for part in psutil.disk_partitions(all=False): |
| if os.name == 'nt': |
| if 'cdrom' in part.opts or part.fstype == '': |
| # skip cd-rom drives with no disk in it; they may raise |
| # ENOENT, pop-up a Windows GUI error for a non-ready |
| # partition or just hang. |
| continue |
| pass |
| pass |
| try: |
| usage = psutil.disk_usage(part.mountpoint) |
| except Exception, e: |
| logger.debug('Failed to read disk_usage for a mountpoint : ' + str(e)) |
| continue |
| |
| if part.device in devices: # Skip devices already seen. |
| continue |
| devices.add(part.device) |
| combined_disk_total += usage.total if hasattr(usage, 'total') else 0 |
| combined_disk_used += usage.used if hasattr(usage, 'used') else 0 |
| combined_disk_free += usage.free if hasattr(usage, 'free') else 0 |
| if hasattr(usage, 'percent'): |
| combined_disk_percent += usage.percent |
| partition_count += 1 |
| |
| if hasattr(usage, 'percent') and max_percent_usage[1] < int(usage.percent): |
| max_percent_usage = (part.mountpoint, usage.percent) |
| pass |
| pass |
| |
| if partition_count > 0: |
| combined_disk_percent /= partition_count |
| |
| return { "disk_total" : bytes2human(combined_disk_total), |
| "disk_used" : bytes2human(combined_disk_used), |
| "disk_free" : bytes2human(combined_disk_free), |
| "disk_percent" : combined_disk_percent |
| # todo: cannot send string |
| #"max_part_used" : max_percent_usage } |
| } |
| pass |
| |
| def get_host_static_info(self): |
| |
| boot_time = psutil.boot_time() |
| cpu_count_logical = psutil.cpu_count() |
| swap_stats = psutil.swap_memory() |
| mem_info = psutil.virtual_memory() |
| |
| # No ability to store strings |
| return { |
| 'cpu_num' : cpu_count_logical, |
| 'swap_total' : swap_stats.total, |
| 'boottime' : boot_time, |
| # 'machine_type' : platform.processor(), |
| # 'os_name' : platform.system(), |
| # 'os_release' : platform.release(), |
| 'mem_total' : mem_info.total |
| } |
| |
| def get_combined_disk_io_counters(self): |
| # read_count: number of reads |
| # write_count: number of writes |
| # read_bytes: number of bytes read |
| # write_bytes: number of bytes written |
| # read_time: time spent reading from disk (in milliseconds) |
| # write_time: time spent writing to disk (in milliseconds) |
| |
| current_time = time.time() |
| delta = current_time - self.__last_disk_io_time |
| self.__last_disk_io_time = current_time |
| |
| if delta <= 0: |
| delta = float("inf") |
| |
| skip_disk_patterns = self.__config.get_disk_metrics_skip_pattern() |
| logger.debug('skip_disk_patterns: %s' % skip_disk_patterns) |
| if not skip_disk_patterns or skip_disk_patterns == 'None': |
| io_counters = psutil.disk_io_counters() |
| print io_counters |
| else: |
| sdiskio = namedtuple('sdiskio', ['read_count', 'write_count', |
| 'read_bytes', 'write_bytes', |
| 'read_time', 'write_time']) |
| skip_disk_pattern_list = skip_disk_patterns.split(',') |
| rawdict = psutil.disk_io_counters(True) |
| if not rawdict: |
| raise RuntimeError("Couldn't find any physical disk") |
| trimmed_dict = {} |
| for disk, fields in rawdict.items(): |
| ignore_disk = False |
| for p in skip_disk_pattern_list: |
| if re.match(p, disk): |
| ignore_disk = True |
| if not ignore_disk: |
| trimmed_dict[disk] = sdiskio(*fields) |
| io_counters = sdiskio(*[sum(x) for x in zip(*trimmed_dict.values())]) |
| |
| new_disk_stats = { |
| 'read_count' : io_counters.read_count if hasattr(io_counters, 'read_count') else 0, |
| 'write_count' : io_counters.write_count if hasattr(io_counters, 'write_count') else 0, |
| 'read_bytes' : io_counters.read_bytes if hasattr(io_counters, 'read_bytes') else 0, |
| 'write_bytes' : io_counters.write_bytes if hasattr(io_counters, 'write_bytes') else 0, |
| 'read_time' : io_counters.read_time if hasattr(io_counters, 'read_time') else 0, |
| 'write_time' : io_counters.write_time if hasattr(io_counters, 'write_time') else 0 |
| } |
| |
| if not self.__last_disk_data: |
| self.__last_disk_data = new_disk_stats |
| read_bps = (new_disk_stats['read_bytes'] - self.__last_disk_data['read_bytes']) / delta |
| write_bps = (new_disk_stats['write_bytes'] - self.__last_disk_data['write_bytes']) / delta |
| self.__last_disk_data = new_disk_stats |
| new_disk_stats['read_bps'] = read_bps |
| new_disk_stats['write_bps'] = write_bps |
| return new_disk_stats |
| |
| def get_disk_io_counters_per_disk(self): |
| # Return a normalized disk name with the counters per disk |
| disk_io_counters = psutil.disk_io_counters(True) |
| per_disk_io_counters = {} |
| |
| sortByKey = lambda x: sorted(x.items(), key=operator.itemgetter(0)) |
| |
| disk_counter = 0 |
| if disk_io_counters: |
| # Sort disks lexically, best chance for similar disk topologies getting |
| # aggregated correctly |
| disk_io_counters = sortByKey(disk_io_counters) |
| for item in disk_io_counters: |
| disk_counter += 1 |
| disk = item[0] |
| logger.debug('Adding disk counters for %s' % str(disk)) |
| sdiskio = item[1] |
| prefix = 'sdisk_{0}_'.format(disk) |
| counter_dict = { |
| prefix + 'read_count' : sdiskio.read_count if hasattr(sdiskio, 'read_count') else 0, |
| prefix + 'write_count' : sdiskio.write_count if hasattr(sdiskio, 'write_count') else 0, |
| prefix + 'read_bytes' : sdiskio.read_bytes if hasattr(sdiskio, 'read_bytes') else 0, |
| prefix + 'write_bytes' : sdiskio.write_bytes if hasattr(sdiskio, 'write_bytes') else 0, |
| prefix + 'read_time' : sdiskio.read_time if hasattr(sdiskio, 'read_time') else 0, |
| prefix + 'write_time' : sdiskio.write_time if hasattr(sdiskio, 'write_time') else 0 |
| } |
| # Optional platform specific attributes |
| if hasattr(sdiskio, 'busy_time'): |
| counter_dict[ prefix + 'busy_time' ] = sdiskio.busy_time |
| if hasattr(sdiskio, 'read_merged_count'): |
| counter_dict[ prefix + 'read_merged_count' ] = sdiskio.read_merged_count |
| if hasattr(sdiskio, 'write_merged_count'): |
| counter_dict[ prefix + 'write_merged_count' ] = sdiskio.write_merged_count |
| |
| per_disk_io_counters.update(counter_dict) |
| pass |
| pass |
| # Send total disk count as a metric |
| per_disk_io_counters[ 'disk_num' ] = disk_counter |
| |
| return per_disk_io_counters |
| |
| def get_hostname(self): |
| global cached_hostname |
| if cached_hostname is not None: |
| return cached_hostname |
| |
| try: |
| cached_hostname = self.__config.get_hostname_config() |
| if not cached_hostname: |
| cached_hostname = socket.getfqdn().lower() |
| except: |
| cached_hostname = socket.getfqdn().lower() |
| logger.info('Cached hostname: %s' % cached_hostname) |
| return cached_hostname |
| |
| def get_ip_address(self): |
| return socket.gethostbyname(socket.getfqdn()) |
| |
| def get_virtual_network_interfaces(self): |
| sys_net_path = "/sys/class/net" |
| net_devices = [] |
| if os.path.isdir(sys_net_path): |
| links = [f for f in os.listdir(sys_net_path) if os.path.islink(os.path.join(sys_net_path, f))] |
| for link in links: |
| if "devices/virtual" in os.readlink(os.path.join(sys_net_path, link)): |
| net_devices.append(link) |
| return net_devices |