Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Portstat]: Add -p option to support portstat timer #16

Merged
merged 4 commits into from
Mar 20, 2017
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
165 changes: 92 additions & 73 deletions scripts/portstat
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import os.path
import re
import subprocess
import sys
import time

from collections import namedtuple, OrderedDict
from tabulate import tabulate
Expand Down Expand Up @@ -204,6 +205,68 @@ def parse_bcmcmd_ps(bcmcmd_output):
bcmcmd_lines = bcmcmd_output.split("\n")


def get_cnstat():
"""
Get the cnstat from netstat.
"""
try:
bcmcmd_output = subprocess.Popen(([BCMCMD_PATH,
'show counters changed same nz']),
stdout=subprocess.PIPE,
shell=False).communicate()[0]
except EnvironmentError as e:
print e, e.errno
sys.exit(e.errno)

bcm_dict = parse_bcmcmd_output(bcmcmd_output)

try:
netstat_out = subprocess.Popen(([NETSTAT_PATH, '-i']),
stdout=subprocess.PIPE,
shell=False).communicate()[0]
except EnvironmentError as e:
print e, e.errno
sys.exit(e.errno)

netstat_lines = netstat_out.split("\n")

# Since netstat -i returns some stats as 32-bits, get full 64-bit
# stats from /prov/net/dev and display only the 64-bit stats.
try:
proc_out = subprocess.Popen(([CAT_PATH, '/proc/net/dev']),
stdout=subprocess.PIPE,
shell=False).communicate()[0]
except EnvironmentError as e:
print e, e.errno
sys.exit(e.errno)

proc = {}
for line in proc_out.split("\n"):
parsed = re.findall("\s*([^ ]+):(.*)", line)
if not parsed:
continue
iface, stats = parsed[0]
proc[iface] = stats.split()

# Build a dictionary of the stats
cnstat_dict = OrderedDict()

cnstat_dict['time'] = datetime.datetime.now()

# We skip the first 2 lines since they contain no interface information
for i in range(2, len(netstat_lines) - 1):
netstats = netstat_lines[i].split()
if ":" in netstats[0]:
continue # Skip aliased interfaces
if ("eth" in netstats[0] or "lo" in netstats[0] or "docker" in netstats[0] or
"Vlan" in netstats[0] or "PortChannel" in netstats[0]):
continue # Skip these types of interfaces
else:
cnstat_dict.update(cnstat_create_bcm_element(i, netstats, bcm_dict[netstats[0]]))


return cnstat_dict

def cnstat_diff_print(cnstat_new_dict, cnstat_old_dict, use_json):
table = []

Expand Down Expand Up @@ -261,13 +324,15 @@ Examples:
portstat -d -t test
portstat
portstat -r
portstat -p 20
""")
parser.add_argument('-c', '--clear', action='store_true', help='Copy & clear stats')
parser.add_argument('-d', '--delete', action='store_true', help='Delete saved stats, either the uid or the specified tag')
parser.add_argument('-D', '--delete-all', action='store_true', help='Delete all saved stats')
parser.add_argument('-j', '--json', action='store_true', help='Display in JSON format')
parser.add_argument('-r', '--raw', action='store_true', help='Raw stats (unmodified output of netstat)')
parser.add_argument('-t', '--tag', type=str, help='Save stats with name TAG', default=None)
parser.add_argument('-p', '--period', type=int, help='Display stats over a specified period (in seconds).', default=0)
args = parser.parse_args()

save_fresh_stats = args.clear
Expand All @@ -277,6 +342,7 @@ Examples:
raw_stats = args.raw
tag_name = args.tag
uid = str(os.getuid())
wait_time_in_seconds = args.period

if not os.geteuid() == 0:
raise RuntimeError("must be root to run")
Expand Down Expand Up @@ -311,45 +377,13 @@ Examples:
if os.listdir(cnstat_dir) == []:
os.rmdir(cnstat_dir)
sys.exit(0)

cnstat_dict = get_cnstat()

try:
bcmcmd_output = subprocess.Popen(([BCMCMD_PATH,
'show counters changed same nz']),
stdout=subprocess.PIPE,
shell=False).communicate()[0]
except EnvironmentError as e:
print e, e.errno
sys.exit(e.errno)

bcm_dict = parse_bcmcmd_output(bcmcmd_output)

try:
netstat_out = subprocess.Popen(([NETSTAT_PATH, '-i']),
stdout=subprocess.PIPE,
shell=False).communicate()[0]
except EnvironmentError as e:
print e, e.errno
sys.exit(e.errno)

netstat_lines = netstat_out.split("\n")

# Since netstat -i returns some stats as 32-bits, get full 64-bit
# stats from /prov/net/dev and display only the 64-bit stats.
try:
proc_out = subprocess.Popen(([CAT_PATH, '/proc/net/dev']),
stdout=subprocess.PIPE,
shell=False).communicate()[0]
except EnvironmentError as e:
print e, e.errno
sys.exit(e.errno)

proc = {}
for line in proc_out.split("\n"):
parsed = re.findall("\s*([^ ]+):(.*)", line)
if not parsed:
continue
iface, stats = parsed[0]
proc[iface] = stats.split()
# Now decide what information to display
if raw_stats:
cnstat_print(cnstat_dict, use_json)
sys.exit(0)

# At this point, either we'll create a file or open an existing one.
if not os.path.exists(cnstat_dir):
Expand All @@ -359,26 +393,6 @@ Examples:
print e.errno, e
sys.exit(1)

# Build a dictionary of the stats
cnstat_dict = OrderedDict()

cnstat_dict['time'] = datetime.datetime.now()

# We skip the first 2 lines since they contain no interface information
for i in range(2, len(netstat_lines) - 1):
netstats = netstat_lines[i].split()
if ":" in netstats[0]:
continue # Skip aliased interfaces
if ("eth" in netstats[0] or "lo" in netstats[0] or "docker" in netstats[0] or
"Vlan" in netstats[0] or "PortChannel" in netstats[0]):
continue # Skip these types of interfaces
else:
cnstat_dict.update(cnstat_create_bcm_element(i, netstats, bcm_dict[netstats[0]]))

# Now decide what information to display
if raw_stats:
cnstat_print(cnstat_dict, use_json)
sys.exit(0)

if save_fresh_stats:
try:
Expand All @@ -388,20 +402,25 @@ Examples:
else:
print "Cleared counters"
sys.exit(0)

cnstat_cached_dict = OrderedDict()

if os.path.isfile(cnstat_fqn_file):
try:
cnstat_cached_dict = pickle.load(open(cnstat_fqn_file, 'r'))
print "Last cached time was " + str(cnstat_cached_dict.get('time'))
cnstat_diff_print(cnstat_dict, cnstat_cached_dict, use_json)
except IOError as e:
print e.errno, e
else:
if tag_name:
print "\nFile '%s' does not exist" % cnstat_fqn_file
print "Did you run 'portstat -c -t %s' to record the counters via tag %s?\n" % (tag_name, tag_name)

if wait_time_in_seconds == 0:
cnstat_cached_dict = OrderedDict()
if os.path.isfile(cnstat_fqn_file):
try:
cnstat_cached_dict = pickle.load(open(cnstat_fqn_file, 'r'))
print "Last cached time was " + str(cnstat_cached_dict.get('time'))
cnstat_diff_print(cnstat_dict, cnstat_cached_dict, use_json)
except IOError as e:
print e.errno, e
else:
cnstat_print(cnstat_dict, use_json)
if tag_name:
print "\nFile '%s' does not exist" % cnstat_fqn_file
print "Did you run 'portstat -c -t %s' to record the counters via tag %s?\n" % (tag_name, tag_name)
else:
cnstat_print(cnstat_dict, use_json)
else:
#wait for the specified time and then gather the new stats and output the difference.
time.sleep(wait_time_in_seconds)
cnstat_new_dict = get_cnstat()
cnstat_diff_print(cnstat_new_dict, cnstat_dict, use_json)