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 | |||
13 | """ |
||
14 | |||
15 | import os |
||
16 | import sys |
||
17 | import json |
||
18 | import argparse |
||
19 | import requests |
||
20 | import time |
||
21 | |||
22 | url='http://192.168.0.1:8181/api' |
||
23 | |||
24 | CACHE_DIR='/var/cache/kytos_zabbix' |
||
25 | cache = { |
||
26 | 1: CACHE_DIR + '/nodes.json', |
||
27 | 2: CACHE_DIR + '/links.json', |
||
28 | 3: CACHE_DIR + '/evcs.json', |
||
29 | 4: CACHE_DIR + '/flows.json', |
||
30 | 5: CACHE_DIR + '/flows.json', |
||
31 | } |
||
32 | |||
33 | help_msg = "Kytos wrapper for Zabbix" |
||
34 | |||
35 | parser = argparse.ArgumentParser() |
||
36 | parser.add_argument("-l", "--url", dest="url", help="URL for your Kytos REST API", default=url) |
||
37 | parser.add_argument("-u", "--user", dest="username", help="Username to authenticate into Kytos API") |
||
38 | parser.add_argument("-p", "--pass", dest="password", help="Password to authenticate into Kytos API") |
||
39 | parser.add_argument("-f", "--authfile", dest="authfile", help="Authentication file containing username (first line) and password (second line) to authenticate into Kytos API") |
||
40 | 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) |
||
41 | 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)") |
||
42 | parser.add_argument("-o", "--monitoring_option", dest="option", type=int, default=1, choices=[1, 2, 3, 4, 5], help="Monitoring option: 1 - for monitor nodes, 2 - for monitor links, 3 - for monitor evcs (status), 4 - evc statistics, 5 - OpenFlow flows stats") |
||
43 | 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).") |
||
44 | 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) |
||
45 | parser.add_argument("-s", "--stats", dest="stats", type=int, default=1, choices=[1, 2, 3, 4], help="EVC statistics type: 1 - bytes/UNI_A, 2 - bytes/UNI_Z , 3 - packets/UNI_A, 4 - packets/UNI_Z") |
||
46 | |||
47 | args = parser.parse_args() |
||
48 | |||
49 | if args.option == 4 and not args.target: |
||
50 | sys.stderr.write('error: to print statistics it requires -t\n') |
||
51 | parser.print_help() |
||
52 | sys.exit(2) |
||
53 | |||
54 | if args.authfile: |
||
55 | authdata = open(args.authfile).readlines() |
||
56 | args.username = authdata[0].strip() |
||
57 | args.password = authdata[1].strip() |
||
58 | |||
59 | args.url = os.environ.get("KYTOS_URL", args.url) |
||
60 | args.timeout = os.environ.get("KYTOS_TIMEOUT", args.timeout) |
||
61 | args.username = os.environ.get("KYTOS_USERNAME", args.username) |
||
62 | args.password = os.environ.get("KYTOS_PASSWORD", args.password) |
||
63 | |||
64 | def is_valid_cache(option): |
||
65 | filename = cache[option] |
||
66 | if not os.path.isfile(filename) or os.path.getsize(filename) == 0: |
||
67 | return False |
||
68 | if args.cache_policy == 'never': |
||
69 | return False |
||
70 | elif args.cache_policy == 'always': |
||
71 | return True |
||
72 | try: |
||
73 | tmout = int(args.cache_policy) |
||
74 | except: |
||
75 | tmout = 600 |
||
76 | return os.stat(filename).st_mtime > time.time() - tmout |
||
77 | |||
78 | def get_data(option, url): |
||
79 | if is_valid_cache(option): |
||
80 | with open(cache[option], 'r') as file: |
||
81 | return file.read() |
||
82 | |||
83 | auth = None |
||
84 | if args.username and args.password: |
||
85 | auth = (args.username, args.password) # assume HTTP Basic |
||
86 | |||
87 | try: |
||
88 | response = requests.get(url, auth=auth, timeout=args.timeout) |
||
89 | assert response.status_code == 200, response.text |
||
90 | data = response.text |
||
91 | except Exception as e: |
||
92 | print("ERROR: failed to get data from URL=%s: %s" % (url, e)) |
||
93 | sys.exit(2) |
||
94 | |||
95 | if args.cache_policy != 'never': |
||
96 | with open(cache[option], 'w') as file: |
||
97 | file.write(data) |
||
0 ignored issues
–
show
introduced
by
![]() |
|||
98 | return data |
||
99 | |||
100 | def get_kytos_data(url, option): |
||
101 | API_ENDPOINT = "" |
||
102 | if option == 1: |
||
103 | API_ENDPOINT="/kytos/topology/v3/switches" |
||
104 | elif option == 2: |
||
105 | API_ENDPOINT="/kytos/topology/v3/links" |
||
106 | elif option == 3: |
||
107 | API_ENDPOINT="/kytos/mef_eline/v2/evc" |
||
108 | elif option in [4,5]: |
||
109 | API_ENDPOINT="/amlight/flow_stats/v1/flow/stats" |
||
110 | |||
111 | data = get_data(option, url + API_ENDPOINT) |
||
112 | try: |
||
113 | data = json.loads(data) |
||
114 | if option == 1: |
||
115 | data = data["switches"] |
||
116 | elif option == 2: |
||
117 | data = data["links"] |
||
118 | except Exception as e: |
||
119 | print("ERROR: failed to get data from URL=%s: %s" % (url + API_ENDPOINT, e)) |
||
120 | sys.exit(2) |
||
121 | |||
122 | return data |
||
123 | |||
124 | def convert_status(active, enabled): |
||
125 | if active: |
||
126 | return "2" |
||
127 | elif not enabled: |
||
128 | return "1" |
||
129 | else: |
||
130 | return "0" |
||
131 | |||
132 | def print_target_results(data, option, target): |
||
133 | if target not in data: |
||
134 | print("Unknown target=%s" % (target)) |
||
135 | return |
||
136 | |||
137 | if option == 1: |
||
138 | sw = data[target] |
||
139 | print(convert_status(sw["active"], sw["enabled"])) |
||
140 | elif option == 2: |
||
141 | link = data[target] |
||
142 | print(convert_status(link["active"], link["enabled"])) |
||
143 | elif option == 3: |
||
144 | evc = data[target] |
||
145 | status = convert_status(evc["active"], evc["enabled"]) |
||
146 | if status != "2" or evc.get("dynamic_backup_path", False): |
||
147 | print(status) |
||
148 | return |
||
149 | if evc["current_path"] == evc["primary_path"]: |
||
150 | print("2") |
||
151 | elif evc["current_path"] == evc["backup_path"]: |
||
152 | print("3") |
||
153 | else: |
||
154 | print("0") |
||
155 | |||
156 | def print_flow_stats(data, target): |
||
157 | if target: |
||
158 | print(len(data.get(target, []))) |
||
159 | else: |
||
160 | l = 0 |
||
161 | for s in data: |
||
162 | l+= len(data[s]) |
||
163 | print(l) |
||
164 | |||
165 | def print_stats(flows, target, stats_type): |
||
166 | evcs = get_kytos_data(args.url, 3) |
||
167 | if target not in evcs: |
||
168 | print("Unknown target=%s" % (target)) |
||
169 | return |
||
170 | |||
171 | uni_field = 'uni_a' |
||
172 | if stats_type in [2,4]: |
||
173 | uni_field = 'uni_z' |
||
174 | stats_field = 'byte_count' |
||
175 | if stats_type in [3, 4]: |
||
176 | stats_field = 'packet_count' |
||
177 | |||
178 | uni = evcs[target][uni_field]['interface_id'].split(':') |
||
179 | sw = ':'.join(uni[:-1]) |
||
180 | iface = int(uni[-1]) |
||
181 | try: |
||
182 | tag = evcs[target][uni_field]['tag']['value'] |
||
183 | except: |
||
184 | tag = None |
||
185 | |||
186 | result = 0 |
||
187 | for flow_id, flow in flows.get(sw, {}).items(): |
||
188 | if format(flow['cookie'], 'x')[2:] == target and flow['match']['in_port'] == iface and flow['match'].get('dl_vlan') == tag: |
||
189 | result += flow[stats_field] |
||
190 | |||
191 | print(result) |
||
192 | |||
193 | def list_items(data, option): |
||
194 | result = {"data":[]} |
||
195 | if option == 1: |
||
196 | for sw in data: |
||
197 | name = data[sw]["metadata"].get("name", None) |
||
198 | if not name: |
||
199 | name = data[sw]["name"] |
||
200 | result["data"].append({"{#OFSWID}": sw, "{#OFSWNAME}": name}) |
||
201 | elif option == 2: |
||
202 | for l in data: |
||
203 | link = data[l] |
||
204 | name = link.get("name", None) |
||
205 | if not name: |
||
206 | name = link["endpoint_a"]["name"] + "_" + link["endpoint_b"]["name"] |
||
207 | result["data"].append({"{#LINKID}": l, "{#LINKNAME}": name}) |
||
208 | elif option == 3: |
||
209 | for evc in data: |
||
210 | result["data"].append({"{#EVCID}": evc, "{#EVCNAME}": data[evc]["name"]}) |
||
211 | print(json.dumps(result)) |
||
212 | |||
213 | data = get_kytos_data(args.url, args.option) |
||
214 | |||
215 | if args.option == 4: |
||
216 | print_stats(data, args.target, args.stats) |
||
217 | elif args.option == 5: |
||
218 | print_flow_stats(data, args.target) |
||
219 | elif args.target: |
||
220 | print_target_results(data, args.option, args.target) |
||
221 | elif args.count_output == 1: |
||
222 | # Count amount of items |
||
223 | print(len(data)) |
||
224 | elif args.count_output == 2: |
||
225 | # List in JSON format. Don't show status! |
||
226 | list_items(data, args.option) |
||
227 | else: |
||
228 | parser.print_help() |
||
229 |