Skip to content

Commit feeac84

Browse files
[counters] Keep counters cache in a single directory (sonic-net#2232)
- What I did To fix sonic-net#9817. Cache all counters in the same place. Created a UserCache helper class to access the cache directory. - How I did it Implemented UserCache class. Changed all stats commands to use new class. Adopted fast-reboot script. - How to verify it Run on the switch and verify counters stats command and clear commands work correctly. After config reload or cold/fast reboot counters cache is removed. Signed-off-by: Stepan Blyschak <[email protected]>
1 parent cd7909e commit feeac84

20 files changed

+140
-263
lines changed

config/main.py

-5
Original file line numberDiff line numberDiff line change
@@ -1517,11 +1517,6 @@ def reload(db, filename, yes, load_sysinfo, no_service_restart, disable_arp_cach
15171517
if multi_asic.is_multi_asic() and file_format == 'config_db':
15181518
num_cfg_file += num_asic
15191519

1520-
# Remove cached PG drop counters data
1521-
dropstat_dir_prefix = '/tmp/dropstat'
1522-
command = "rm -rf {}-*".format(dropstat_dir_prefix)
1523-
clicommon.run_command(command, display_cmd=True)
1524-
15251520
# If the user give the filename[s], extract the file names.
15261521
if filename is not None:
15271522
cfg_files = filename.split(',')

config/plugins/pbh.py

+6-4
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@
66
CLI Auto-generation tool HLD - https://github.com/Azure/SONiC/pull/78
77
"""
88

9+
import os
910
import click
1011
import json
1112
import ipaddress
1213
import re
1314
import utilities_common.cli as clicommon
1415

15-
from show.plugins.pbh import deserialize_pbh_counters
16+
from show.plugins.pbh import deserialize_pbh_counters, PBH_COUNTERS_CACHE_FILENAME
1617

1718
GRE_KEY_RE = r"^(0x){1}[a-fA-F0-9]{1,8}/(0x){1}[a-fA-F0-9]{1,8}$"
1819

@@ -79,8 +80,6 @@
7980
PBH_UPDATE = "UPDATE"
8081
PBH_REMOVE = "REMOVE"
8182

82-
PBH_COUNTERS_LOCATION = "/tmp/.pbh_counters.txt"
83-
8483
#
8584
# DB interface --------------------------------------------------------------------------------------------------------
8685
#
@@ -467,11 +466,14 @@ def serialize_pbh_counters(obj):
467466
obj: counters dict.
468467
"""
469468

469+
cache = clicommon.UserCache('pbh')
470+
counters_cache_file = os.path.join(cache.get_directory(), PBH_COUNTERS_CACHE_FILENAME)
471+
470472
def remap_keys(obj):
471473
return [{'key': k, 'value': v} for k, v in obj.items()]
472474

473475
try:
474-
with open(PBH_COUNTERS_LOCATION, 'w') as f:
476+
with open(counters_cache_file, 'w') as f:
475477
json.dump(remap_keys(obj), f)
476478
except IOError as err:
477479
pass

scripts/aclshow

+9-8
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,13 @@ optional arguments:
2020
import argparse
2121
import json
2222
import os
23-
from swsscommon.swsscommon import SonicV2Connector, ConfigDBConnector
2423
import sys
2524

25+
from swsscommon.swsscommon import SonicV2Connector, ConfigDBConnector
26+
from utilities_common.cli import UserCache
27+
2628
from tabulate import tabulate
2729

28-
### temp file to save counter positions when doing clear counter action.
29-
### if we could have a SAI command to clear counters will be better, so no need to maintain
30-
### counters in temp loaction for clear conter action
31-
COUNTER_POSITION = '/tmp/.counters_acl.p'
3230
COUNTERS = "COUNTERS"
3331
ACL_COUNTER_RULE_MAP = "ACL_COUNTER_RULE_MAP"
3432

@@ -38,6 +36,9 @@ ACL_HEADER = ["RULE NAME", "TABLE NAME", "PRIO", "PACKETS COUNT", "BYTES COUNT"]
3836
COUNTER_PACKETS_ATTR = "SAI_ACL_COUNTER_ATTR_PACKETS"
3937
COUNTER_BYTES_ATTR = "SAI_ACL_COUNTER_ATTR_BYTES"
4038

39+
USER_CACHE = UserCache()
40+
COUNTERS_CACHE_DIR = USER_CACHE.get_directory()
41+
COUNTERS_CACHE = os.path.join(COUNTERS_CACHE_DIR, 'aclstat')
4142

4243
class AclStat(object):
4344
"""
@@ -78,9 +79,9 @@ class AclStat(object):
7879
res[e['key'][0], e['key'][1]] = e['value']
7980
return res
8081

81-
if os.path.isfile(COUNTER_POSITION):
82+
if os.path.isfile(COUNTERS_CACHE):
8283
try:
83-
with open(COUNTER_POSITION) as fp:
84+
with open(COUNTERS_CACHE) as fp:
8485
self.saved_acl_counters = remap_keys(json.load(fp))
8586
except Exception:
8687
pass
@@ -207,7 +208,7 @@ class AclStat(object):
207208
def remap_keys(dict):
208209
return [{'key': k, 'value': v} for k, v in dict.items()]
209210

210-
with open(COUNTER_POSITION, 'w') as fp:
211+
with open(COUNTERS_CACHE, 'w') as fp:
211212
json.dump(remap_keys(self.acl_counters), fp)
212213

213214
def main():

scripts/dropstat

+2-13
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ except KeyError:
3535
pass
3636

3737
from swsscommon.swsscommon import SonicV2Connector, ConfigDBConnector
38+
from utilities_common.cli import UserCache
3839

3940

4041
# COUNTERS_DB Tables
@@ -80,8 +81,7 @@ std_switch_description_header = ['DEVICE']
8081

8182

8283
def get_dropstat_dir():
83-
dropstat_dir_prefix = '/tmp/dropstat'
84-
return "{}-{}/".format(dropstat_dir_prefix, os.getuid())
84+
return UserCache().get_directory()
8585

8686

8787
class DropStat(object):
@@ -411,18 +411,7 @@ Examples:
411411
group = args.group
412412
counter_type = args.type
413413

414-
dropstat_dir = get_dropstat_dir()
415-
416-
# Create the directory to hold clear results
417-
if not os.path.exists(dropstat_dir):
418-
try:
419-
os.makedirs(dropstat_dir)
420-
except IOError as e:
421-
print(e)
422-
sys.exit(e.errno)
423-
424414
dcstat = DropStat()
425-
426415
if command == 'clear':
427416
dcstat.clear_drop_counts()
428417
elif command == 'show':

scripts/fast-reboot

+7-14
Original file line numberDiff line numberDiff line change
@@ -462,21 +462,14 @@ function unload_kernel()
462462
}
463463
464464
function save_counters_folder() {
465-
debug "Saving counters folder before warmboot..."
465+
if [[ "$REBOOT_TYPE" = "warm-reboot" ]]; then
466+
debug "Saving counters folder before warmboot..."
466467
467-
counters_folder="/host/counters"
468-
if [[ ! -d $counters_folder ]]; then
469-
mkdir $counters_folder
470-
fi
471-
if [[ "$REBOOT_TYPE" = "warm-reboot" || "$REBOOT_TYPE" = "fastfast-reboot" ]]; then
472-
modules=("portstat-0" "dropstat" "pfcstat-0" "queuestat-0" "intfstat-0")
473-
for module in ${modules[@]}
474-
do
475-
statfile="/tmp/$module"
476-
if [[ -d $statfile ]]; then
477-
cp -rf $statfile $counters_folder
478-
fi
479-
done
468+
counters_folder="/host/counters"
469+
if [[ ! -d $counters_folder ]]; then
470+
mkdir $counters_folder
471+
fi
472+
cp -rf /tmp/cache $counters_folder
480473
fi
481474
}
482475

scripts/flow_counters_stat

+5-3
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import utilities_common.multi_asic as multi_asic_util
2727
from flow_counter_util.route import build_route_pattern, extract_route_pattern, exit_if_route_flow_counter_not_support, DEFAULT_VRF, COUNTERS_ROUTE_TO_PATTERN_MAP
2828
from utilities_common import constants
2929
from utilities_common.netstat import format_number_with_comma, table_as_json, ns_diff, format_prate
30+
from utilities_common.cli import UserCache
3031

3132
# Flow counter meta data, new type of flow counters can extend this dictinary to reuse existing logic
3233
flow_counter_meta = {
@@ -57,9 +58,10 @@ class FlowCounterStats(object):
5758
meta_data = flow_counter_meta[args.type]
5859
self.name_map = meta_data['name_map']
5960
self.headers = meta_data['headers']
60-
self.data_file = os.path.join('/tmp/{}-stats-{}'.format(args.type, os.getuid()))
61-
if self.args.delete and os.path.exists(self.data_file):
62-
os.remove(self.data_file)
61+
self.cache = UserCache()
62+
self.data_file = os.path.join(self.cache.get_directory(), "flow-counter-stats")
63+
if self.args.delete:
64+
self.cache.remove()
6365
self.data = {}
6466

6567
def show(self):

scripts/intfstat

+12-43
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ from collections import namedtuple, OrderedDict
2828
from natsort import natsorted
2929
from tabulate import tabulate
3030
from utilities_common.netstat import ns_diff, table_as_json, STATUS_NA, format_brate, format_prate
31+
from utilities_common.cli import UserCache
3132
from swsscommon.swsscommon import SonicV2Connector
3233

3334
nstat_fields = (
@@ -274,63 +275,34 @@ def main():
274275
delete_saved_stats = args.delete
275276
delete_all_stats = args.delete_all
276277
use_json = args.json
277-
tag_name = args.tag if args.tag else ""
278-
uid = str(os.getuid())
278+
tag_name = args.tag
279279
wait_time_in_seconds = args.period
280280
interface_name = args.interface if args.interface else ""
281281

282-
# fancy filename with dashes: uid-tag / uid etc
283-
filename_components = [uid, tag_name]
282+
cnstat_file = "intfstat"
284283

285-
cnstat_file = "-".join(filter(None, filename_components))
284+
cache = UserCache(tag=tag_name)
286285

287-
cnstat_dir = "/tmp/intfstat-" + uid
286+
cache_general = UserCache()
287+
cnstat_dir = cache.get_directory()
288+
cnstat_general_dir = cache_general.get_directory()
289+
290+
cnstat_fqn_general_file = cnstat_general_dir + "/" + cnstat_file
288291
cnstat_fqn_file = cnstat_dir + "/" + cnstat_file
289292

290293
if delete_all_stats:
291-
# There is nothing to delete
292-
if not os.path.isdir(cnstat_dir):
293-
sys.exit(0)
294-
295-
for file in os.listdir(cnstat_dir):
296-
os.remove(cnstat_dir + "/" + file)
297-
298-
try:
299-
os.rmdir(cnstat_dir)
300-
sys.exit(0)
301-
except IOError as e:
302-
print(e.errno, e)
303-
sys.exit(e)
294+
cache.remove_all()
304295

305296
if delete_saved_stats:
306-
try:
307-
os.remove(cnstat_fqn_file)
308-
except IOError as e:
309-
if e.errno != ENOENT:
310-
print(e.errno, e)
311-
sys.exit(1)
312-
finally:
313-
if os.listdir(cnstat_dir) == []:
314-
os.rmdir(cnstat_dir)
315-
sys.exit(0)
297+
cache.remove()
316298

317299
intfstat = Intfstat()
318300
cnstat_dict, ratestat_dict = intfstat.get_cnstat(rif=interface_name)
319301

320-
# At this point, either we'll create a file or open an existing one.
321-
if not os.path.exists(cnstat_dir):
322-
try:
323-
os.makedirs(cnstat_dir)
324-
except IOError as e:
325-
print(e.errno, e)
326-
sys.exit(1)
327-
328302
if save_fresh_stats:
329303
try:
330304
# Add the information also to the general file - i.e. without the tag name
331-
if tag_name != '' and tag_name in cnstat_fqn_file.split('/')[-1]:
332-
gen_index = cnstat_fqn_file.rfind('/')
333-
cnstat_fqn_general_file = cnstat_fqn_file[:gen_index] + cnstat_fqn_file[gen_index:].split('-')[0]
305+
if tag_name is not None:
334306
if os.path.isfile(cnstat_fqn_general_file):
335307
try:
336308
general_data = pickle.load(open(cnstat_fqn_general_file, 'rb'))
@@ -354,9 +326,6 @@ def main():
354326
sys.exit(0)
355327

356328
if wait_time_in_seconds == 0:
357-
gen_index = cnstat_fqn_file.rfind('/')
358-
cnstat_fqn_general_file = cnstat_fqn_file[:gen_index] + cnstat_fqn_file[gen_index:].split('-')[0]
359-
360329
if os.path.isfile(cnstat_fqn_file) or (os.path.isfile(cnstat_fqn_general_file)):
361330
try:
362331
cnstat_cached_dict = {}

scripts/pfcstat

+10-23
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@ from natsort import natsorted
1818
from tabulate import tabulate
1919

2020
from sonic_py_common.multi_asic import get_external_ports
21-
from utilities_common.netstat import ns_diff, STATUS_NA, format_number_with_comma
22-
from utilities_common import multi_asic as multi_asic_util
23-
from utilities_common import constants
2421

2522
# mock the redis for unit test purposes #
2623
try:
@@ -37,6 +34,12 @@ try:
3734
except KeyError:
3835
pass
3936

37+
from utilities_common.netstat import ns_diff, STATUS_NA, format_number_with_comma
38+
from utilities_common import multi_asic as multi_asic_util
39+
from utilities_common import constants
40+
from utilities_common.cli import UserCache
41+
42+
4043
PStats = namedtuple("PStats", "pfc0, pfc1, pfc2, pfc3, pfc4, pfc5, pfc6, pfc7")
4144
header_Rx = ['Port Rx', 'PFC0', 'PFC1', 'PFC2', 'PFC3', 'PFC4', 'PFC5', 'PFC6', 'PFC7']
4245

@@ -224,10 +227,10 @@ Examples:
224227
save_fresh_stats = args.clear
225228
delete_all_stats = args.delete
226229

227-
uid = str(os.getuid())
228-
cnstat_file = uid
230+
cache = UserCache()
231+
cnstat_file = 'pfcstat'
229232

230-
cnstat_dir = os.path.join(os.sep, "tmp", "pfcstat-{}".format(uid))
233+
cnstat_dir = cache.get_directory()
231234
cnstat_fqn_file_rx = os.path.join(cnstat_dir, "{}rx".format(cnstat_file))
232235
cnstat_fqn_file_tx = os.path.join(cnstat_dir, "{}tx".format(cnstat_file))
233236

@@ -239,15 +242,7 @@ Examples:
239242
pfcstat = Pfcstat(args.namespace, args.show)
240243

241244
if delete_all_stats:
242-
for file in os.listdir(cnstat_dir):
243-
os.remove(os.path.join(cnstat_dir, file))
244-
245-
try:
246-
os.rmdir(cnstat_dir)
247-
sys.exit(0)
248-
except IOError as e:
249-
print(e.errno, e)
250-
sys.exit(e)
245+
cache.remove()
251246

252247
"""
253248
Get the counters of pfc rx counter
@@ -259,14 +254,6 @@ Examples:
259254
"""
260255
cnstat_dict_tx = deepcopy(pfcstat.get_cnstat(False))
261256

262-
# At this point, either we'll create a file or open an existing one.
263-
if not os.path.exists(cnstat_dir):
264-
try:
265-
os.makedirs(cnstat_dir)
266-
except IOError as e:
267-
print(e.errno, e)
268-
sys.exit(1)
269-
270257
if save_fresh_stats:
271258
try:
272259
pickle.dump(cnstat_dict_rx, open(cnstat_fqn_file_rx, 'wb'))

scripts/pg-drop

+2-2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ try:
2626
except KeyError:
2727
pass
2828

29+
from utilities_common.cli import UserCache
2930
from swsscommon.swsscommon import ConfigDBConnector, SonicV2Connector
3031

3132
STATUS_NA = 'N/A'
@@ -38,8 +39,7 @@ COUNTERS_PG_PORT_MAP = "COUNTERS_PG_PORT_MAP"
3839
COUNTERS_PG_INDEX_MAP = "COUNTERS_PG_INDEX_MAP"
3940

4041
def get_dropstat_dir():
41-
dropstat_dir_prefix = '/tmp/dropstat'
42-
return "{}-{}/".format(dropstat_dir_prefix, os.getuid())
42+
return UserCache().get_directory()
4343

4444
class PgDropStat(object):
4545

0 commit comments

Comments
 (0)