-
Notifications
You must be signed in to change notification settings - Fork 3
/
stats_to_db.py
246 lines (213 loc) · 11.5 KB
/
stats_to_db.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright 2023 Consoli Solutions, LLC. All rights reserved.
#
# NOT BROADCOM SUPPORTED
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may also 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.
"""
:mod:`stats_to_db` - Example on how to capture port statistics and add them to your own database
**Description**
This module contains sample code to illustrate:
* How to capture basic port information
* How to capture GE port statistics
* Considerations for adding the statistics to a database
* Databases require unique keys.
* Many database applications have key naming convention rules.
Version Control::
+-----------+---------------+-----------------------------------------------------------------------------------+
| Version | Last Edit | Description |
+===========+===============+===================================================================================+
| 4.0.0 | 04 Aug 2023 | Re-Launch |
+-----------+---------------+-----------------------------------------------------------------------------------+
"""
__author__ = 'Jack Consoli'
__copyright__ = 'Copyright 2023 Consoli Solutions, LLC'
__date__ = '04 August 2023'
__license__ = 'Apache License, Version 2.0'
__email__ = '[email protected]'
__maintainer__ = 'Jack Consoli'
__status__ = 'Released'
__version__ = '4.0.0'
import argparse
import brcdapi.brcdapi_rest as brcdapi_rest
import brcdapi.fos_auth as brcdapi_auth
import brcdapi.log as brcdapi_log
_DOC_STRING = False # Should always be False. Prohibits any actual I/O. Only useful for building documentation
_DEBUG = False # When True, instead of getting input parameters from the command line (shell), use:
_DEBUG_ip = 'xxx.xxx.xxx.xxx'
_DEBUG_id = 'admin'
_DEBUG_pw = 'password'
_DEBUG_sec = 'self' # Use None or 'none' for HTTP. Use the certificate if HTTPS and not self signed
_DEBUG_fid = '128'
_DEBUG_verbose = False # When True, all content and responses are formatted and printed (pprint).
_DEBUG_log = '_logs'
_DEBUG_nl = False
def _db_add(key_0, key_1, key_2, val):
"""Stubbed out method to add key value pairs to your database. Derives a unique key from a hash of the 3 input keys
:param key_0: First key (switch WWN in xx:xx:xx:xx:xx:xx:xx:xx notation)
:type key_0: str
:param key_1: Second key (port number in s/p notation)
:type key_1: str
:param key_2: Third key (value type such as CRC)
:type key_2: str
:param val: Value associated with the keys
:type val: str, int, float
"""
# You might want to make sure you are adding a valid value to your database.
if not isinstance(val, (str, int, float)):
brcdapi_log.log('Invalid value type, ' + str(type(val)) + ', for database.', True)
return
# Verbose explanation of the next line of code:
# key_list = list() - create a list to store the keys in
# for key in (key_0, key_1, key_2):
# clean_key = key.replace(':', '_').replace('/', '_') - Replace ':' and '/' with '_'
# short_key = clean_key[11:] - removes the non-unique portion of WWN in the key
# key_list.append(short_key) - Add the key to key_list
# For the Python savvy, a compiled regex would be better than .replace() above but this is good enough for a simple
# example. Using a regex probably won't save enough time to make researching it worthwhile so if you don't
# understand this comment, just stick with using .replace().
key_list = [key.replace(':', '_').replace('/', '_')[11:] for key in (key_0, key_1, key_2)]
unique_key = '_'.join(key_list) # Concatenates all items in key_list seperated by a '_'
brcdapi_log.log('Adding key: ' + unique_key + ', Value: ' + str(val), True)
def parse_args():
"""Parses the module load command line
:return: ip, id, pw, sec, FID, verbose debug flag, log folder, log flag
:rtype: (str, str, str, str, str, bool, str, bool)
"""
global _DEBUG, _DEBUG_ip, _DEBUG_id, _DEBUG_pw, _DEBUG_sec, _DEBUG_fid, _DEBUG_verbose, _DEBUG_log, _DEBUG_nl
if _DEBUG:
return _DEBUG_ip, _DEBUG_id, _DEBUG_pw, _DEBUG_sec, _DEBUG_fid, _DEBUG_verbose, _DEBUG_log, _DEBUG_nl
buf = 'This is a programming example only. It illustrates how to capture port statistics and additional ' \
'information that is typical of a custom script to capture statistics and add them to a database.'
parser = argparse.ArgumentParser(description=buf)
parser.add_argument('-ip', help='(Required) IP address', required=True)
parser.add_argument('-id', help='(Required) User ID', required=True)
parser.add_argument('-pw', help='(Required) Password', required=True)
buf = '(Optional) Default is HTTP. Certificate or "self" for HTTPS mode.'
parser.add_argument('-s', help=buf, required=False,)
parser.add_argument('-fid', help='(Required) Virtual Fabric ID.', required=True)
buf = '(Optional) Enable debug logging. Prints the formatted data structures (pprint) to the log and console.'
parser.add_argument('-d', help=buf, action='store_true', required=False)
buf = '(Optional) Directory where log file is to be created. Default is to use the current directory. The ' \
'log file name will always be "Log_xxxx" where xxxx is a time and date stamp.'
parser.add_argument('-log', help=buf, required=False, )
buf = '(Optional) No parameters. When set, a log file is not created. The default is to create a log file.'
parser.add_argument('-nl', help=buf, action='store_true', required=False)
args = parser.parse_args()
return args.ip, args.id, args.pw, args.s, args.fid, args.d, args.log, args.nl
def pseudo_main():
"""Basically the main(). Did it this way to use with IDE
:return: Exit code
:rtype: int
"""
global _DEBUG
# Get the command line input
ml = ['WARNING!!! Debug is enabled'] if _DEBUG else list()
ip, user_id, pw, sec, fid_str, vd, log, nl = parse_args()
if vd:
brcdapi_rest.verbose_debug = True
if sec is None:
sec = 'none'
if not nl:
brcdapi_log.open_log(log)
ml.append('FID: ' + fid_str)
try:
fid = int(fid_str)
except ValueError:
brcdapi_log.log('Invalid FID, -f. FID must be an integer between 1-128')
brcdapi_log.log(ml, True)
# Login
session = brcdapi_rest.login(user_id, pw, ip, sec)
if brcdapi_auth.is_error(session):
brcdapi_log.log('Login failed:\n' + brcdapi_auth.formatted_error_msg(session), True)
return -1
ec = 0 # Error code. 0: No errors. -1: error encountered
port_info_d = dict() # Will use this to store basic port information
port_stats_d = dict() # Will use this to store port statistics in
# You may want to put better error checking in your code as well as use a more efficient code. A verbose coding
# style was used here for readability.
try:
# Get the switch WWN
brcdapi_log.log('Capturing chassis Data', True)
uri = 'running/brocade-fibrechannel-logical-switch/fibrechannel-logical-switch'
obj = brcdapi_rest.get_request(session, uri)
if brcdapi_auth.is_error(obj):
brcdapi_log.log(brcdapi_auth.formatted_error_msg(obj), True)
ec = -1
else:
# Find the switch with the matching FID
switch_wwn = None
for switch_obj in obj['fibrechannel-logical-switch']:
if switch_obj['fabric-id'] == fid:
switch_wwn = switch_obj['switch-wwn']
break
if switch_wwn is None:
brcdapi_log.log('Logical switch for FID ' + str(fid) + 'not found', True)
ec = -1
# Get some basic port information
if ec == 0: # Make sure we didn't encountered any errors above
# It's common to keep track of other port information, such as the user friendly name and FC address. Below
# captures this basic port information.
brcdapi_log.log('Capturing basic port information.', True)
uri = 'running/brocade-interface/fibrechannel'
port_info = brcdapi_rest.get_request(session, uri, fid)
if brcdapi_auth.is_error(port_info):
brcdapi_log.log(brcdapi_auth.formatted_error_msg(port_info), True)
ec = -1
else:
# To make it easier to match the port information with the port statistics, we're going to create a
# a dictionary using the port name (port number) as the key
for port_obj in port_info['fibrechannel']:
port_info_d.update({port_obj['name']: port_obj})
# Capture the port statistics
if ec == 0: # Make sure we didn't encountered any errors above
brcdapi_log.log('Capturing port statistics', True)
uri = 'running/brocade-interface/fibrechannel-statistics'
port_stats = brcdapi_rest.get_request(session, uri, fid)
if brcdapi_auth.is_error(port_stats):
brcdapi_log.log(brcdapi_auth.formatted_error_msg(port_stats), True)
ec = -1
else:
# We could just add each port to the database here but since it's common to capture additional
# information, such as determining the login alias(es), we'll add it to a dictionary as was done with
# the basic port information
for port_obj in port_stats['fibrechannel-statistics']:
port_stats_d.update({port_obj['name']: port_obj})
# Add all the ports to the database
if ec == 0: # Make sure we didn't encountered any errors above
brcdapi_log.log('Adding key value pairs to the database.', True)
for port_num, port_obj in port_info_d.items():
sub_key = 'fcid-hex' # Just using the FC address for this example
_db_add(switch_wwn, port_num, sub_key, port_obj[sub_key])
for k, v in port_stats_d[port_num].items():
_db_add(switch_wwn, port_num, k, v)
except: # Bare because I don't care what went wrong. I just want to logout
# The brcdapi_log.exception() method precedes the passed message parameter with a stack trace
brcdapi_log.exception('Unknown programming error occured while processing: ' + uri, True)
# Logout
obj = brcdapi_rest.logout(session)
if brcdapi_auth.is_error(obj):
brcdapi_log.log('Logout failed:\n' + brcdapi_auth.formatted_error_msg(obj), True)
return -1
return 0
###################################################################
#
# Main Entry Point
#
###################################################################
if _DOC_STRING:
print('_DOC_STRING set. No processing')
exit(0)
_ec = pseudo_main()
brcdapi_log.close_log('Processing Complete. Exit code: ' + str(_ec))
exit(_ec)