blob: 2757e0cc54428dd43822c13cb610ff5b79c15f9f [file] [log] [blame]
#!/usr/bin/python
# Copyright 2016 Google Inc. All Rights Reserved.
#
# Licensed 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.
"""Retrieve packet statistics from cpss, emit in JSON format."""
__author__ = 'poist@google.com (Gregory Poist)'
import itertools
import json
import os
import signal
import subprocess
import sys
import textwrap
import threading
import options
optspec = """
presterastats [options]
--
ports= prestera ports to collect [0/0,0/4,0/24,0/25]
timeout= seconds to wait for cpss response [5]
"""
class PresteraStats(object):
"""Class wrapping a cpss command to request stats."""
def __init__(self, ports, timeout):
self.ports = ports
self.timeout = timeout
def WriteToStderr(self, msg, is_json=False):
"""Write a message to stderr."""
if is_json:
# Make the json easier to parse from the logs.
json_data = json.loads(msg)
json_str = json.dumps(json_data, sort_keys=True, indent=2,
separators=(',', ': '))
# Logging pretty-printed json is like logging one huge line. Logos is
# configured to limit lines to 768 characters. Split the logged output at
# half of that to make sure logos doesn't clip our output.
sys.stderr.write('\n'.join(textwrap.wrap(json_str, width=384)))
sys.stderr.flush()
else:
sys.stderr.write(msg)
sys.stderr.flush()
def StartCpssSubprocess(self):
"""Start execution of the cpss_cmd sub-process."""
return subprocess.Popen(['cpss_cmd'],
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
preexec_fn=os.setsid)
def GetMibStats(self):
"""Extract statistics from cpss_cmd output."""
result = None
proc = self.StartCpssSubprocess()
if not proc:
self.WriteToStderr('Failed to start subprocess.')
return
kill_proc = lambda p: os.killpg(os.getpgid(p.pid), signal.SIGTERM)
timer = threading.Timer(self.timeout, kill_proc, [proc])
cpss_cmd_prefix = 'do show interfaces mac json-counters ethernet '
try:
timer.start()
cpssout, _ = proc.communicate(input=cpss_cmd_prefix + self.ports + '\n')
# itertools magic to take only the lines between JSONSTART and JSONEND.
it = itertools.dropwhile(lambda line: line.strip() != 'JSONSTART',
cpssout.splitlines())
it = itertools.islice(it, 1, None)
it = itertools.takewhile(lambda line: line.strip() != 'JSONEND', it)
# smack itertools iterable down to a string
result = ''.join(it)
finally:
timer.cancel()
if result:
return json.loads(result)
def main():
o = options.Options(optspec)
(opt, unused_flags, unused_extra) = o.parse(sys.argv[1:])
prestera = PresteraStats(opt.ports, opt.timeout)
results = prestera.GetMibStats()
if results:
print json.dumps(results, sort_keys=True,
indent=2, separators=(',', ': '))
sys.exit(0)
sys.exit(1)
if __name__ == '__main__':
main()