Skip to content

Commit 25f9e4f

Browse files
Handle error seen when interface counter is not available in COUNTERS_DB (#341)
**- What I did** Fix to handle error seen: ``` snmp-subagent [ax_interface] ERROR: SubtreeMIBEntry.__call__() caught an unexpected exception during _callable_.__call__()#012Traceback (most recent call last):#12 File "/usr/local/lib/python3.9/dist-packages/ax_interface/mib.py", line 194, in __call__#012 return self._callable_.__call__(sub_id, *self._callable_args)#12 File "/usr/local/lib/python3.9/dist-packages/sonic_ax_impl/mibs/vendor/cisco/ciscoPfcExtMIB.py", line 248, in indications_per_priority#012 counter_value += self._get_counter(mibs.get_index_from_str(lag_member), counter_name)#012TypeError: unsupported operand type(s) for += ``` Above error log occurs when the services restarted, swss/syncd due to some crash/reboot/config reload, this will also cause snmp service to restart. During this time, it can happen that all interface COUNTERS are not yet available in COUNTERS_DB for a short period. At this point, if a SNMP query is made to retrieve interface/PFC counters, then this error syslog will show up until the COUNTERS_DB data is populated with counters for all interfaces. MSFT ADO 26506804 **- How I did it** Avoid adding up counters if _get_counter returns None. **- How to verify it** Before FIX: send a continuous query to get pfc counters for any of the configured port-channel interface: watch -n 1 snmpwalk -v2c -c <comm> <ip> 1.3.6.1.4.1.9.9.813.1.2.1.3.1103 on the device, execute config reload and we should see the error log on the device: After fix, perform same snmpwalk as above, SNMP ERR log will not be seen.
1 parent 9e2c50a commit 25f9e4f

File tree

5 files changed

+179
-107
lines changed

5 files changed

+179
-107
lines changed

src/sonic_ax_impl/mibs/ietf/rfc2863.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,11 @@ def _get_counter(self, oid, table_name, mask):
250250
if oid in self.oid_lag_name_map:
251251
counter_value = 0
252252
for lag_member in self.lag_name_if_name_map[self.oid_lag_name_map[oid]]:
253-
counter_value += self._get_counter(mibs.get_index_from_str(lag_member), table_name, mask)
253+
member_counter = self._get_counter(mibs.get_index_from_str(lag_member), table_name, mask)
254+
if member_counter is not None:
255+
counter_value += member_counter
256+
else:
257+
return None
254258

255259
return counter_value & mask
256260

src/sonic_ax_impl/mibs/vendor/cisco/ciscoPfcExtMIB.py

+24-4
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,12 @@ def cpfc_if_requests(self, sub_id):
121121
if oid in self.oid_lag_name_map:
122122
counter_value = 0
123123
for lag_member in self.lag_name_if_name_map[self.oid_lag_name_map[oid]]:
124-
counter_value += self._get_counter(mibs.get_index_from_str(lag_member), counter_name)
124+
member_counter = self._get_counter(mibs.get_index_from_str(lag_member), counter_name)
125+
if member_counter is not None:
126+
counter_value += member_counter
127+
else:
128+
mibs.logger.warning("SyncD 'COUNTERS_DB' missing attribute '{}' for lag member '{}'.".format(counter_name, lag_member))
129+
return None
125130

126131
return counter_value
127132
else:
@@ -143,7 +148,12 @@ def cpfc_if_indications(self, sub_id):
143148
if oid in self.oid_lag_name_map:
144149
counter_value = 0
145150
for lag_member in self.lag_name_if_name_map[self.oid_lag_name_map[oid]]:
146-
counter_value += self._get_counter(mibs.get_index_from_str(lag_member), counter_name)
151+
member_counter = self._get_counter(mibs.get_index_from_str(lag_member), counter_name)
152+
if member_counter is not None:
153+
counter_value += member_counter
154+
else:
155+
mibs.logger.warning("SyncD 'COUNTERS_DB' missing attribute '{}' for lag member '{}'.".format(counter_name, lag_member))
156+
return None
147157

148158
return counter_value
149159
else:
@@ -215,7 +225,12 @@ def requests_per_priority(self, sub_id):
215225
if port_oid in self.oid_lag_name_map:
216226
counter_value = 0
217227
for lag_member in self.lag_name_if_name_map[self.oid_lag_name_map[port_oid]]:
218-
counter_value += self._get_counter(mibs.get_index_from_str(lag_member), counter_name)
228+
member_counter = self._get_counter(mibs.get_index_from_str(lag_member), counter_name)
229+
if member_counter is not None:
230+
counter_value += member_counter
231+
else:
232+
mibs.logger.warning("SyncD 'COUNTERS_DB' missing attribute '{}' for lag member '{}'.".format(counter_name, lag_member))
233+
return None
219234

220235
return counter_value
221236
else:
@@ -245,7 +260,12 @@ def indications_per_priority(self, sub_id):
245260
if port_oid in self.oid_lag_name_map:
246261
counter_value = 0
247262
for lag_member in self.lag_name_if_name_map[self.oid_lag_name_map[port_oid]]:
248-
counter_value += self._get_counter(mibs.get_index_from_str(lag_member), counter_name)
263+
member_counter = self._get_counter(mibs.get_index_from_str(lag_member), counter_name)
264+
if member_counter is not None:
265+
counter_value += member_counter
266+
else:
267+
mibs.logger.warning("SyncD 'COUNTERS_DB' missing attribute '{}' for lag member '{}'.".format(counter_name, lag_member))
268+
return None
249269

250270
return counter_value
251271
else:

tests/mock_tables/counters_db.json

-102
Original file line numberDiff line numberDiff line change
@@ -2794,108 +2794,6 @@
27942794
"SAI_PORT_STAT_PFC_7_RX_PKTS": "8",
27952795
"SAI_PORT_STAT_PFC_7_TX_PKTS": "8"
27962796
},
2797-
"COUNTERS:oid:0x1000000000003": {
2798-
"SAI_PORT_STAT_ETHER_STATS_TX_NO_ERRORS": "0",
2799-
"SAI_PORT_STAT_ETHER_STATS_OVERSIZE_PKTS": "0",
2800-
"SAI_PORT_STAT_IF_OUT_ERRORS": "0",
2801-
"SAI_PORT_STAT_ETHER_TX_OVERSIZE_PKTS": "0",
2802-
"SAI_PORT_STAT_ETHER_STATS_PKTS_1519_TO_2047_OCTETS": "0",
2803-
"SAI_PORT_STAT_IP_IN_RECEIVES": "0",
2804-
"SAI_PORT_STAT_ETHER_IN_PKTS_64_OCTETS": "0",
2805-
"SAI_PORT_STAT_IPV6_OUT_UCAST_PKTS": "0",
2806-
"SAI_PORT_STAT_ETHER_OUT_PKTS_4096_TO_9216_OCTETS": "0",
2807-
"SAI_PORT_STAT_IF_IN_ERRORS": "0",
2808-
"SAI_PORT_STAT_ETHER_STATS_PKTS": "0",
2809-
"SAI_PORT_STAT_ETHER_STATS_BROADCAST_PKTS": "0",
2810-
"SAI_PORT_STAT_IF_IN_DISCARDS": "0",
2811-
"SAI_PORT_STAT_IP_OUT_DISCARDS": "0",
2812-
"SAI_PORT_STAT_IF_IN_UNKNOWN_PROTOS": "0",
2813-
"SAI_PORT_STAT_IPV6_IN_DISCARDS": "0",
2814-
"SAI_PORT_STAT_IPV6_OUT_DISCARDS": "0",
2815-
"SAI_PORT_STAT_IPV6_IN_OCTETS": "0",
2816-
"SAI_PORT_STAT_ETHER_OUT_PKTS_65_TO_127_OCTETS": "0",
2817-
"SAI_PORT_STAT_IF_IN_BROADCAST_PKTS": "0",
2818-
"SAI_PORT_STAT_ETHER_IN_PKTS_1519_TO_2047_OCTETS": "0",
2819-
"SAI_PORT_STAT_IF_OUT_MULTICAST_PKTS": "0",
2820-
"SAI_PORT_STAT_ETHER_OUT_PKTS_512_TO_1023_OCTETS": "0",
2821-
"SAI_PORT_STAT_ETHER_STATS_PKTS_256_TO_511_OCTETS": "0",
2822-
"SAI_PORT_STAT_ETHER_STATS_PKTS_9217_TO_16383_OCTETS": "0",
2823-
"SAI_PORT_STAT_ETHER_IN_PKTS_512_TO_1023_OCTETS": "0",
2824-
"SAI_PORT_STAT_IPV6_IN_NON_UCAST_PKTS": "0",
2825-
"SAI_PORT_STAT_ETHER_IN_PKTS_4096_TO_9216_OCTETS": "0",
2826-
"SAI_PORT_STAT_IF_OUT_BROADCAST_PKTS": "0",
2827-
"SAI_PORT_STAT_IPV6_OUT_NON_UCAST_PKTS": "0",
2828-
"SAI_PORT_STAT_IF_IN_VLAN_DISCARDS": "0",
2829-
"SAI_PORT_STAT_ETHER_IN_PKTS_65_TO_127_OCTETS": "0",
2830-
"SAI_PORT_STAT_IP_IN_NON_UCAST_PKTS": "0",
2831-
"SAI_PORT_STAT_ETHER_STATS_FRAGMENTS": "0",
2832-
"SAI_PORT_STAT_IPV6_IN_UCAST_PKTS": "0",
2833-
"SAI_PORT_STAT_IPV6_IN_RECEIVES": "0",
2834-
"SAI_PORT_STAT_ETHER_STATS_PKTS_4096_TO_9216_OCTETS": "0",
2835-
"SAI_PORT_STAT_IF_OUT_DISCARDS": "0",
2836-
"SAI_PORT_STAT_ETHER_STATS_DROP_EVENTS": "0",
2837-
"SAI_PORT_STAT_IPV6_OUT_MCAST_PKTS": "0",
2838-
"SAI_PORT_STAT_ETHER_RX_OVERSIZE_PKTS": "0",
2839-
"SAI_PORT_STAT_IF_OUT_OCTETS": "0",
2840-
"SAI_PORT_STAT_IF_IN_NON_UCAST_PKTS": "0",
2841-
"SAI_PORT_STAT_ETHER_IN_PKTS_9217_TO_16383_OCTETS": "0",
2842-
"SAI_PORT_STAT_ETHER_OUT_PKTS_1024_TO_1518_OCTETS": "0",
2843-
"SAI_PORT_STAT_ETHER_STATS_PKTS_2048_TO_4095_OCTETS": "0",
2844-
"SAI_PORT_STAT_ETHER_STATS_PKTS_512_TO_1023_OCTETS": "0",
2845-
"SAI_PORT_STAT_ETHER_OUT_PKTS_1519_TO_2047_OCTETS": "0",
2846-
"SAI_PORT_STAT_ETHER_STATS_RX_NO_ERRORS": "0",
2847-
"SAI_PORT_STAT_ETHER_OUT_PKTS_64_OCTETS": "0",
2848-
"SAI_PORT_STAT_ETHER_STATS_COLLISIONS": "0",
2849-
"SAI_PORT_STAT_ETHER_IN_PKTS_1024_TO_1518_OCTETS": "0",
2850-
"SAI_PORT_STAT_ETHER_STATS_PKTS_1024_TO_1518_OCTETS": "0",
2851-
"SAI_PORT_STAT_ETHER_IN_PKTS_256_TO_511_OCTETS": "0",
2852-
"SAI_PORT_STAT_ETHER_OUT_PKTS_2048_TO_4095_OCTETS": "0",
2853-
"SAI_PORT_STAT_ETHER_STATS_OCTETS": "0",
2854-
"SAI_PORT_STAT_IF_OUT_UCAST_PKTS": "0",
2855-
"SAI_PORT_STAT_ETHER_STATS_UNDERSIZE_PKTS": "0",
2856-
"SAI_PORT_STAT_ETHER_OUT_PKTS_128_TO_255_OCTETS": "0",
2857-
"SAI_PORT_STAT_ETHER_STATS_PKTS_64_OCTETS": "0",
2858-
"SAI_PORT_STAT_IP_OUT_OCTETS": "0",
2859-
"SAI_PORT_STAT_IF_IN_UCAST_PKTS": "0",
2860-
"SAI_PORT_STAT_ETHER_OUT_PKTS_9217_TO_16383_OCTETS": "0",
2861-
"SAI_PORT_STAT_ETHER_IN_PKTS_2048_TO_4095_OCTETS": "0",
2862-
"SAI_PORT_STAT_IP_OUT_NON_UCAST_PKTS": "0",
2863-
"SAI_PORT_STAT_ETHER_STATS_JABBERS": "0",
2864-
"SAI_PORT_STAT_IF_IN_OCTETS": "0",
2865-
"SAI_PORT_STAT_IPV6_IN_MCAST_PKTS": "0",
2866-
"SAI_PORT_STAT_ETHER_STATS_PKTS_65_TO_127_OCTETS": "0",
2867-
"SAI_PORT_STAT_IF_OUT_QLEN": "0",
2868-
"SAI_PORT_STAT_ETHER_STATS_PKTS_128_TO_255_OCTETS": "0",
2869-
"SAI_PORT_STAT_IP_IN_DISCARDS": "0",
2870-
"SAI_PORT_STAT_IPV6_OUT_OCTETS": "0",
2871-
"SAI_PORT_STAT_IF_OUT_NON_UCAST_PKTS": "0",
2872-
"SAI_PORT_STAT_IP_IN_OCTETS": "0",
2873-
"SAI_PORT_STAT_ETHER_OUT_PKTS_256_TO_511_OCTETS": "0",
2874-
"SAI_PORT_STAT_ETHER_STATS_CRC_ALIGN_ERRORS": "0",
2875-
"SAI_PORT_STAT_IP_OUT_UCAST_PKTS": "0",
2876-
"SAI_PORT_STAT_IP_IN_UCAST_PKTS": "0",
2877-
"SAI_PORT_STAT_ETHER_STATS_MULTICAST_PKTS": "0",
2878-
"SAI_PORT_STAT_ETHER_IN_PKTS_128_TO_255_OCTETS": "0",
2879-
"SAI_PORT_STAT_IF_IN_MULTICAST_PKTS": "0",
2880-
"SAI_PORT_STAT_PAUSE_RX_PKTS": "0",
2881-
"SAI_PORT_STAT_PAUSE_TX_PKTS": "0",
2882-
"SAI_PORT_STAT_PFC_0_RX_PKTS": "1",
2883-
"SAI_PORT_STAT_PFC_0_TX_PKTS": "1",
2884-
"SAI_PORT_STAT_PFC_1_RX_PKTS": "2",
2885-
"SAI_PORT_STAT_PFC_1_TX_PKTS": "2",
2886-
"SAI_PORT_STAT_PFC_2_RX_PKTS": "3",
2887-
"SAI_PORT_STAT_PFC_2_TX_PKTS": "3",
2888-
"SAI_PORT_STAT_PFC_3_RX_PKTS": "4",
2889-
"SAI_PORT_STAT_PFC_3_TX_PKTS": "4",
2890-
"SAI_PORT_STAT_PFC_4_RX_PKTS": "5",
2891-
"SAI_PORT_STAT_PFC_4_TX_PKTS": "5",
2892-
"SAI_PORT_STAT_PFC_5_RX_PKTS": "6",
2893-
"SAI_PORT_STAT_PFC_5_TX_PKTS": "6",
2894-
"SAI_PORT_STAT_PFC_6_RX_PKTS": "7",
2895-
"SAI_PORT_STAT_PFC_6_TX_PKTS": "7",
2896-
"SAI_PORT_STAT_PFC_7_RX_PKTS": "8",
2897-
"SAI_PORT_STAT_PFC_7_TX_PKTS": "8"
2898-
},
28992797
"COUNTERS:oid:0x1000000000013": {
29002798
"SAI_PORT_STAT_ETHER_STATS_TX_NO_ERRORS": "0",
29012799
"SAI_PORT_STAT_ETHER_STATS_OVERSIZE_PKTS": "0",

tests/test_pfc.py

+126
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import os
22
import sys
33
import importlib
4+
import pytest
5+
6+
if sys.version_info.major == 3:
7+
from unittest import mock
8+
else:
9+
import mock
410

511
# noinspection PyUnresolvedReferences
612
import tests.mock_tables.dbconnector
@@ -155,6 +161,126 @@ def test_getPduIndicationForPriority(self):
155161
self.assertEqual(str(value0.name), str(oid))
156162
self.assertEqual(value0.data, 1)
157163

164+
def test_getPduPrioIndicationLagMemberCounterNotAvailable(self):
165+
oid = ObjectIdentifier(8, 0, 0, 0, (1, 3, 6, 1, 4, 1, 9, 9, 813, 1, 2, 1, 3, 1004, 0))
166+
get_pdu = GetPDU(
167+
header=PDUHeader(1, PduTypes.GET, 16, 0, 42, 0, 0, 0),
168+
oids=[oid]
169+
)
170+
171+
with mock.patch('ax_interface.logger.exception') as mock_logger:
172+
encoded = get_pdu.encode()
173+
response = get_pdu.make_response(self.lut_prio)
174+
mock_logger.assert_not_called()
175+
print(response)
176+
value0 = response.values[0]
177+
self.assertEqual(value0.data, None)
178+
179+
def test_getPduPrioIndicationLag(self):
180+
oid = ObjectIdentifier(8, 0, 0, 0, (1, 3, 6, 1, 4, 1, 9, 9, 813, 1, 2, 1, 3, 1003, 0))
181+
get_pdu = GetPDU(
182+
header=PDUHeader(1, PduTypes.GET, 16, 0, 42, 0, 0, 0),
183+
oids=[oid]
184+
)
185+
186+
with mock.patch('ax_interface.logger.exception') as mock_logger:
187+
encoded = get_pdu.encode()
188+
response = get_pdu.make_response(self.lut_prio)
189+
mock_logger.assert_not_called()
190+
print(response)
191+
value0 = response.values[0]
192+
self.assertEqual(value0.data, 1)
193+
194+
def test_getPduPrioRequestsLagMemberCounterNotAvailable(self):
195+
oid = ObjectIdentifier(8, 0, 0, 0, (1, 3, 6, 1, 4, 1, 9, 9, 813, 1, 2, 1, 2, 1004, 0))
196+
get_pdu = GetPDU(
197+
header=PDUHeader(1, PduTypes.GET, 16, 0, 42, 0, 0, 0),
198+
oids=[oid]
199+
)
200+
201+
with mock.patch('ax_interface.logger.exception') as mock_logger:
202+
encoded = get_pdu.encode()
203+
response = get_pdu.make_response(self.lut_prio)
204+
mock_logger.assert_not_called()
205+
print(response)
206+
value0 = response.values[0]
207+
self.assertEqual(value0.data, None)
208+
209+
def test_getPduPrioRequestsLag(self):
210+
oid = ObjectIdentifier(8, 0, 0, 0, (1, 3, 6, 1, 4, 1, 9, 9, 813, 1, 2, 1, 2, 1003, 1))
211+
get_pdu = GetPDU(
212+
header=PDUHeader(1, PduTypes.GET, 16, 0, 42, 0, 0, 0),
213+
oids=[oid]
214+
)
215+
216+
with mock.patch('ax_interface.logger.exception') as mock_logger:
217+
encoded = get_pdu.encode()
218+
response = get_pdu.make_response(self.lut_prio)
219+
mock_logger.assert_not_called()
220+
print(response)
221+
value0 = response.values[0]
222+
self.assertEqual(value0.data, 2)
223+
224+
def test_getPduifIndicationLagMemberCounterNotAvailable(self):
225+
oid = ObjectIdentifier(8, 0, 0, 0, (1, 3, 6, 1, 4, 1, 9, 9, 813, 1, 1, 1, 2, 1004))
226+
get_pdu = GetPDU(
227+
header=PDUHeader(1, PduTypes.GET, 16, 0, 42, 0, 0, 0),
228+
oids=[oid]
229+
)
230+
231+
with mock.patch('ax_interface.logger.exception') as mock_logger:
232+
encoded = get_pdu.encode()
233+
response = get_pdu.make_response(self.lut_port)
234+
mock_logger.assert_not_called()
235+
print(response)
236+
value0 = response.values[0]
237+
self.assertEqual(value0.data, None)
238+
239+
def test_getPduifIndicationLag(self):
240+
oid = ObjectIdentifier(8, 0, 0, 0, (1, 3, 6, 1, 4, 1, 9, 9, 813, 1, 1, 1, 2, 1003))
241+
get_pdu = GetPDU(
242+
header=PDUHeader(1, PduTypes.GET, 16, 0, 42, 0, 0, 0),
243+
oids=[oid]
244+
)
245+
246+
with mock.patch('ax_interface.logger.exception') as mock_logger:
247+
encoded = get_pdu.encode()
248+
response = get_pdu.make_response(self.lut_port)
249+
mock_logger.assert_not_called()
250+
print(response)
251+
value0 = response.values[0]
252+
self.assertEqual(value0.data, 4)
253+
254+
def test_getPduifRequestLagMemberCounterNotAvailable(self):
255+
oid = ObjectIdentifier(8, 0, 0, 0, (1, 3, 6, 1, 4, 1, 9, 9, 813, 1, 1, 1, 1, 1004))
256+
get_pdu = GetPDU(
257+
header=PDUHeader(1, PduTypes.GET, 16, 0, 42, 0, 0, 0),
258+
oids=[oid]
259+
)
260+
261+
with mock.patch('ax_interface.logger.exception') as mock_logger:
262+
encoded = get_pdu.encode()
263+
response = get_pdu.make_response(self.lut_port)
264+
mock_logger.assert_not_called()
265+
print(response)
266+
value0 = response.values[0]
267+
self.assertEqual(value0.data, None)
268+
269+
def test_getPduifRequestLag(self):
270+
oid = ObjectIdentifier(8, 0, 0, 0, (1, 3, 6, 1, 4, 1, 9, 9, 813, 1, 1, 1, 1, 1003))
271+
get_pdu = GetPDU(
272+
header=PDUHeader(1, PduTypes.GET, 16, 0, 42, 0, 0, 0),
273+
oids=[oid]
274+
)
275+
276+
with mock.patch('ax_interface.logger.exception') as mock_logger:
277+
encoded = get_pdu.encode()
278+
response = get_pdu.make_response(self.lut_port)
279+
mock_logger.assert_not_called()
280+
print(response)
281+
value0 = response.values[0]
282+
self.assertEqual(value0.data, 4)
283+
158284
def test_getNextPduindicationForPriority(self):
159285
oid = ObjectIdentifier(8, 0, 0, 0, (1, 3, 6, 1, 4, 1, 9, 9, 813, 1, 2, 1, 3, 1, 0))
160286
expected_oid = ObjectIdentifier(8, 0, 0, 0, (1, 3, 6, 1, 4, 1, 9, 9, 813, 1, 2, 1, 3, 1, 1))

tests/test_rfc2863.py

+24
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
sys.path.insert(0, os.path.join(modules_path, 'src'))
1313

1414
from sonic_ax_impl.mibs.ietf.rfc2863 import InterfaceMIBUpdater
15+
from sonic_ax_impl.mibs.ietf.rfc2863 import DbTables64
1516

1617
class TestInterfaceMIBUpdater(TestCase):
1718

@@ -66,3 +67,26 @@ def test_InterfaceMIBUpdater_get_high_speed(self):
6667
speed = updater.get_high_speed((1999,))
6768
print("999 speed: {}".format(speed))
6869
self.assertTrue(speed == 0)
70+
71+
72+
@mock.patch('sonic_ax_impl.mibs.Namespace.get_sync_d_from_all_namespace', mock_get_sync_d_from_all_namespace)
73+
@mock.patch('sonic_ax_impl.mibs.Namespace.dbs_get_all', mock_dbs_get_all)
74+
@mock.patch('sonic_ax_impl.mibs.lag_entry_table', mock_lag_entry_table)
75+
@mock.patch('sonic_ax_impl.mibs.init_mgmt_interface_tables', mock_init_mgmt_interface_tables)
76+
def test_InterfaceMIBUpdater_get_counters(self):
77+
updater = InterfaceMIBUpdater()
78+
79+
updater.reinit_data()
80+
updater.update_data()
81+
def mock_get_counter(oid, table_name, mask):
82+
if oid == 121:
83+
return None
84+
else:
85+
return updater._get_counter(oid, table_name, mask)
86+
87+
try:
88+
counter = updater.get_counter64((1103,), DbTables64(6))
89+
except TypeError:
90+
self.fail("Caught Type error")
91+
self.assertTrue(counter == None)
92+

0 commit comments

Comments
 (0)