blob: afd995cd2a492894eac2c41a78d99737459d5a51 [file] [log] [blame]
# Copyright 2016 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Prod host metrics"""
from __future__ import absolute_import
from __future__ import print_function
import collections
import json
import subprocess
from chromite.lib import cros_logging as logging
from chromite.lib import metrics
_METRIC_ROOT_PATH = 'prod_hosts/'
_ATEST_PROGRAM = '/usr/local/autotest/cli/atest'
logger = logging.getLogger(__name__)
class _ProdHostReporter(object):
"""Prod host metrics reporter.
ProdHostReporter takes source and sinks arguments. Sources have a
get_servers() method that returns prod host information as an iterable
of Server instances. Sinks have a write_servers() method for
reporting the server information.
"""
def __init__(self, source, sinks=()):
"""Initialize instance.
Args:
source: Source for getting prod host information.
sinks: Sinks for writing prod host information.
"""
self._source = source
self._sinks = sinks
def __call__(self):
"""Report prod hosts."""
servers = list(self._source.get_servers())
for sink in self._sinks:
sink.write_servers(servers)
class _AtestSource(object):
"""Source for prod host information, using atest."""
def __init__(self, atest_program):
"""Initialize instance.
Args:
atest_program: atest program as full path or name on the search path.
parser: Parser to use for atest output.
"""
self._atest_program = atest_program
def _query_atest_for_servers(self):
"""Run atest to get host information.
Returns:
atest output as a string (specifically, a bytestring)
"""
return subprocess.check_output(
[self._atest_program, 'server', 'list', '--json'])
def get_servers(self):
"""Get server information from this source.
Returns:
Iterable of Server instances.
"""
server_dicts = json.loads(self._query_atest_for_servers())
for server in server_dicts:
yield Server(
hostname=_get_hostname(server),
data_center=_get_data_center(server),
status=server['status'],
roles=tuple(server['roles']),
created=server['date_created'],
modified=server['date_modified'],
note=server['note'])
def _get_hostname(server):
"""Get server hostname from an atest dict.
>>> server = {'hostname': 'foo.example.com'} # from atest
>>> _get_hostname(server)
'foo'
"""
return server['hostname'].partition('.')[0]
def _get_data_center(server):
"""Get server data center from an atest dict.
>>> server = {'hostname': 'foo.mtv.example.com'} # from atest
>>> _get_data_center(server)
'mtv'
"""
try:
return server['hostname'].split('.')[1]
except IndexError:
raise ValueError('%r hostname is invalid' % server)
Server = collections.namedtuple(
'Server', 'hostname,data_center,status,roles,created,modified,note')
class _TsMonSink(object):
"""Sink using ts_mon to report Monarch metrics."""
def __init__(self, metric_root_path):
"""Initialize instance.
Args:
metric_root_path: Path for ts_mon metrics. Should end in a slash.
"""
if not metric_root_path.endswith('/'):
raise ValueError('metric_root_path should end with slash',
metric_root_path)
self._metric_root_path = metric_root_path
def write_servers(self, servers):
"""Write server information.
Args:
servers: Iterable of Server instances.
"""
for server in servers:
fields = {
'target_hostname': server.hostname,
'target_data_center': server.data_center,
}
self._presence_metric.set(True, fields)
self._roles_metric.set(self._format_roles(server.roles), fields)
@property
def _presence_metric(self):
return metrics.Boolean(self._metric_root_path + 'presence')
@property
def _roles_metric(self):
return metrics.String(self._metric_root_path + 'roles')
def _format_roles(self, roles):
return ','.join(sorted(roles))
class _LoggingSink(object):
"""Sink using Python's logging facilities."""
def write_servers(self, servers):
"""Write server information.
Args:
servers: Iterable of Server instances.
"""
for server in servers:
logger.debug('Server: %r', server)
collect_prod_hosts = _ProdHostReporter(
source=_AtestSource(_ATEST_PROGRAM),
sinks=(_TsMonSink(_METRIC_ROOT_PATH), _LoggingSink()))