Passed
Pull Request — master (#4)
by
unknown
06:02
created

kytos_zabbix.print_stats()   C

Complexity

Conditions 9

Size

Total Lines 27
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 23
dl 0
loc 27
rs 6.6666
c 0
b 0
f 0
cc 9
nop 3
1
#!/usr/bin/python3
2
"""
3
   This code was created to integrate Kytos napps with Zabbix
4
5
   Created on: Dec/2020
6
   Author: Italo Valcy
7
8
   Changelog:
9
     - 2020/12/10 - Creating the script for monitoring items of Kytos similar to oess_zabbix.py
10
     - 2020/12/15 - adding option to export EVC statistics (bytes and packets)
11
     - 2022/11/30 - bug fixes and integration with kytos-ng/flow_stats rather than flow_manager
12
     - 2023/05/11 - integration with kytos-ng/kytos_stats rather than flow_stats
13
14
"""
15
16
import os
17
import sys
18
import json
19
import argparse
20
import requests
21
import time
22
23
url='http://192.168.0.1:8181/api'
24
25
CACHE_DIR='/var/cache/kytos_zabbix'
26
cache = {
27
 1: CACHE_DIR + '/nodes.json',
28
 2: CACHE_DIR + '/links.json',
29
 3: CACHE_DIR + '/evcs.json',
30
 4: CACHE_DIR + '/flows.json',
31
 5: CACHE_DIR + '/flows.json',
32
 6: CACHE_DIR + '/tables.json',
33
}
34
35
help_msg = "Kytos wrapper for Zabbix"
36
37
parser = argparse.ArgumentParser()
38
parser.add_argument("-l", "--url", dest="url", help="URL for your Kytos REST API", default=url)
39
parser.add_argument("-u", "--user", dest="username", help="Username to authenticate into Kytos API")
40
parser.add_argument("-p", "--pass", dest="password", help="Password to authenticate into Kytos API")
41
parser.add_argument("-f", "--authfile", dest="authfile", help="Authentication file containing username (first line) and password (second line) to authenticate into Kytos API")
42
parser.add_argument("-T", "--timeout", dest="timeout", type=int, help="You can tell Requests to stop waiting for a response after a given number of seconds", default=5)
43
parser.add_argument("-c", "--cache_policy", dest="cache_policy", default=60, help="Cache policy: never, always or X seconds (default to cache for 600 seconds)")
44
parser.add_argument("-o", "--monitoring_option", dest="option", type=int, default=1, choices=[1, 2, 3, 4, 5, 6], help="Monitoring option: 1 - for monitor nodes, 2 - for monitor links, 3 - for monitor evcs (status), 4 - evc statistics, 5 - OpenFlow flows stats, 6 - OpenFlow tables stats")
45
parser.add_argument("-t", "--target", dest="target", help="Item status (0-down/others, 1-disabled, 2-up/primary, 3-up/backup). Argument is the item id to be monitored (depending on the -o option).")
46
parser.add_argument("-z", "--zabbix_output", dest="count_output", type=int, choices=[1, 2], help="Zabbix LLD: (1) Count number of lines in each output or (2) list-only registers", default=None)
47
parser.add_argument("-s", "--stats", dest="stats", type=int, default=1, choices=[1, 2, 3, 4, 5], help="EVC statistics type: 1 - bytes/UNI_A, 2 - bytes/UNI_Z , 3 - packets/UNI_A, 4 - packets/UNI_Z. Table statistics type: 5 - active count, default - number of tables")
48
49
args = parser.parse_args()
50
51
if args.option == 4 and not args.target:
52
    sys.stderr.write('error: to print statistics it requires -t\n')
53
    parser.print_help()
54
    sys.exit(2)
55
56
if args.authfile:
57
    authdata = open(args.authfile).readlines()
58
    args.username = authdata[0].strip()
59
    args.password = authdata[1].strip()
60
61
args.url = os.environ.get("KYTOS_URL", args.url)
62
args.timeout = int(os.environ.get("KYTOS_TIMEOUT", args.timeout))
63
args.username = os.environ.get("KYTOS_USERNAME", args.username)
64
args.password = os.environ.get("KYTOS_PASSWORD", args.password)
65
66
def is_valid_cache(option):
67
    filename = cache[option]
68
    if not os.path.isfile(filename) or os.path.getsize(filename) == 0:
69
        return False
70
    if args.cache_policy == 'never':
71
        return False
72
    elif args.cache_policy == 'always':
73
        return True
74
    try:
75
        tmout = int(args.cache_policy)
76
    except:
77
        tmout = 600
78
    return os.stat(filename).st_mtime > time.time() - tmout
79
80
def get_data(option, url):
81
    if is_valid_cache(option):
82
        with open(cache[option], 'r') as file:
83
            return file.read()
84
85
    auth = None
86
    if args.username and args.password:
87
        auth = (args.username, args.password)  # assume HTTP Basic
88
89
    try:
90
        response = requests.get(url, auth=auth, timeout=args.timeout)
91
        assert response.status_code == 200, response.text
92
        data = response.text
93
    except Exception as e:
94
        print("ERROR: failed to get data from URL=%s: %s" % (url, e))
95
        sys.exit(2)
96
97
    if args.cache_policy != 'never':
98
        with open(cache[option], 'w') as file:
99
            file.write(data)
0 ignored issues
show
introduced by
The variable data does not seem to be defined for all execution paths.
Loading history...
100
    return data
101
102
def get_kytos_data(url, option):
103
    API_ENDPOINT = ""
104
    if option == 1:
105
        API_ENDPOINT="/kytos/topology/v3/switches"
106
    elif option == 2:
107
        API_ENDPOINT="/kytos/topology/v3/links"
108
    elif option == 3:
109
        API_ENDPOINT="/kytos/mef_eline/v2/evc"
110
    elif option in [4,5]:
111
        API_ENDPOINT="/amlight/kytos_stats/v1/flow/stats"
112
    elif option in [6]:
113
        API_ENDPOINT="/amlight/kytos_stats/v1/table/stats"
114
115
    data = get_data(option, url + API_ENDPOINT)
116
    try:
117
        data = json.loads(data)
118
        if option == 1:
119
            data = data["switches"]
120
        elif option == 2:
121
            data = data["links"]
122
    except Exception as e:
123
        print("ERROR: failed to get data from URL=%s: %s" % (url + API_ENDPOINT, e))
124
        sys.exit(2)
125
126
    return data
127
128
def convert_status(active, enabled):
129
    if active:
130
        return "2"
131
    elif not enabled:
132
        return "1"
133
    else:
134
        return "0"
135
136
def print_target_results(data, option, target):
137
    if target not in data:
138
        print("Unknown target=%s" % (target))
139
        return
140
141
    if option == 1:
142
        sw = data[target]
143
        print(convert_status(sw["active"], sw["enabled"]))
144
    elif option == 2:
145
        link = data[target]
146
        print(convert_status(link["active"], link["enabled"]))
147
    elif option == 3:
148
        evc = data[target]
149
        status = convert_status(evc["active"], evc["enabled"])
150
        if status != "2" or evc.get("dynamic_backup_path", False):
151
            print(status)
152
            return
153
        if evc["current_path"] == evc["primary_path"]:
154
            print("2")
155
        elif evc["current_path"] == evc["backup_path"]:
156
            print("3")
157
        else:
158
            print("0")
159
160
def print_flow_stats(data, target):    
161
    if target:
162
        print(len(data.get(target, [])))
163
    else:
164
        l = 0
165
        for s in data:
166
            l+= len(data[s])
167
        print(l)
168
169
def print_tables_stats(data, target, stats_type):
170
    if target:
171
        split = target.split('::')
172
        dpid = split[0]
173
        tables = data.get(dpid, [])
174
        if len(split) > 1:
175
            table_id = split[1]
176
            if table_id in tables:
177
                tables = {table_id: tables.get(table_id)} 
178
            else:
179
                tables = {} 
180
        if stats_type != 5:
181
            print(len(tables))
182
        else:             
183
            stats = {table_id:table.get('active_count') for table_id, table in tables.items()}
184
            print(stats)  
185
    else:
186
        l = 0
187
        for s in data:
188
            l+= len(data[s])
189
        print(l)
190
191
def print_stats(flows, target, stats_type):
192
    evcs = get_kytos_data(args.url, 3)
193
    if target not in evcs:
194
        print("Unknown target=%s" % (target))
195
        return
196
197
    uni_field = 'uni_a'
198
    if stats_type in [2,4]:
199
        uni_field = 'uni_z'
200
    stats_field = 'byte_count'
201
    if stats_type in [3, 4]:
202
        stats_field = 'packet_count'
203
204
    uni = evcs[target][uni_field]['interface_id'].split(':')
205
    sw = ':'.join(uni[:-1])
206
    iface = int(uni[-1])
207
    try:
208
        tag = evcs[target][uni_field]['tag']['value']
209
    except:
210
        tag = None
211
212
    result = 0
213
    for flow_id, flow in flows.get(sw, {}).items():
214
        if format(flow['cookie'], 'x')[2:] == target and flow['match']['in_port'] == iface and flow['match'].get('dl_vlan') == tag:
215
            result += flow[stats_field]
216
217
    print(result)
218
219
def list_items(data, option):
220
    result = {"data":[]}
221
    if option == 1:
222
        for sw in data:
223
            name = data[sw]["metadata"].get("name", None)
224
            if not name:
225
                name = data[sw]["name"]
226
            result["data"].append({"{#OFSWID}": sw, "{#OFSWNAME}": name})
227
    elif option == 2:
228
        for l in data:
229
            link = data[l]
230
            name = link.get("name", None)
231
            if not name:
232
                name = link["endpoint_a"]["name"] + "_" +  link["endpoint_b"]["name"]
233
            result["data"].append({"{#LINKID}": l, "{#LINKNAME}": name})
234
    elif option == 3:
235
        for evc in data:
236
            result["data"].append({"{#EVCID}": evc, "{#EVCNAME}": data[evc]["name"]})
237
    print(json.dumps(result))
238
239
data = get_kytos_data(args.url, args.option)
240
241
if args.option == 4:
242
    print_stats(data, args.target, args.stats)
243
elif args.option == 5:
244
    print_flow_stats(data, args.target)
245
elif args.option == 6:
246
    print_tables_stats(data, args.target, args.stats)
247
elif args.target:
248
    print_target_results(data, args.option, args.target)
249
elif args.count_output == 1:
250
    # Count amount of items
251
    print(len(data))
252
elif args.count_output == 2:
253
    # List in JSON format. Don't show status!
254
    list_items(data, args.option)
255
else:
256
    parser.print_help()
257