-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlibrary.py
268 lines (254 loc) · 8.77 KB
/
library.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
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
#!/usr/bin/python3.5
################################################################################
# library.py - Library of functions for server_stats_collector.py
#
# Copyright 2016-2017 by David Brenner Jr <david.brenner.jr@gmail.com>
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program. If not, see <http://www.gnu.org/licenses/>.
################################################################################
# import required modules
try:
import sys
import platform
import subprocess
import os
import re
import time
import calendar
import json
import collections
import threading
import syslog
except ImportError:
print("FAILURE: Failed to import required modules")
sys.exit()
# globals
config_path = "server_stats_collector.conf"
update_interval = False
data_model = False
status_flag = False
# exec system command, return output with timestamp
def syscmd(command):
if command != 0 or command != "":
command2 = "%s" % command
try:
p = subprocess.Popen(command2, shell=True, executable='/bin/bash', stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True)
# ignore errors
output, _ = p.communicate()
timestamp = calendar.timegm(time.gmtime())
except ValueError or OSError:
p.kill()
else:
pass
return output, timestamp
# try checking operating system name and operating system version exists
# in list of supported operating systems, else throw OS error then
# display non-critical warning message.
def check_distribution():
try:
tested_os = {"Ubuntu":16, "Test1":1, "Test2":2}
os_name, os_version, _ = platform.linux_distribution()
if str(os_name) in tested_os.keys():
if int(round(float(os_version),0)) != tested_os[os_name]:
print("WARNING: Distribution not supported: %s %s" % (os_name, os_version))
raise OSError
else:
pass
else:
print("WARNING: Distribution not supported: %s %s" % (os_name, os_version))
raise OSError
except OSError:
os_list = ""
for key in tested_os:
os_list = os_list + key + " " + str(tested_os[key]) + ", "
print("Supported distributions: %s" % os_list[:-2])
# try querying the package management system for the status of the required
# package named "curl", else throw OS error then display failure message
# and exit agent.
def check_dependencies():
try:
package, _ = syscmd("dpkg-query -W --showformat='${Package} ${Status} ${Version}\n' rsyslog")
package = package.strip()
if "rsyslog install ok installed" not in package:
raise OSError
else:
pass
except OSError:
print("FAILURE: rsyslog not installed: %s" % package)
sys.exit()
# try checking the config file exists and it can be opened, else throw
# I/O error then display failure message and exit agent.
def check_config_file():
global config_path
try:
if os.path.exists(config_path):
try:
fd = open(config_path, "r")
fd.close()
except IOError:
print("FAILURE: Failed to read configuration file " % config_path)
sys.exit()
else:
raise IOError
except IOError:
print("FAILURE: Failed to open configuration file " % config_path)
sys.exit()
# check options in config file
def check_config_options():
global config_path
global update_interval
global data_model
# try opening config file for reading options, else throw I/O error then
# display failure message and exit agent.
try:
if os.path.exists(config_path):
configfile = open(config_path, "r")
configfile.seek(0)
else:
raise IOError
except IOError:
print("FAILURE: Failed to open configuration file " % config_path)
configfile.close()
sys.exit()
for line in configfile:
# try checking format of update-interval in config file, else throw value
# error then display failure message and exit agent.
if re.match("^update\-interval\:", line):
if update_interval is False:
try:
# get value of option
_, optval = line.split(':', 2)
optval = optval.strip()
# check update-interval pattern
if re.match("[1-9]{1,8}", optval):
# keep update-interval for agent
update_interval = int(optval)
else:
raise ValueError
except ValueError:
print("FAILURE: Invalid format of update-interval " % update_interval)
configfile.close()
sys.exit()
else:
pass
# try converting data-model in config file to dictionary, else throw value
# error then display failure message and exit agent.
elif re.match("^data\-model\:", line):
if data_model is False:
try:
# create temporary ordered dictionary
tmpdict = collections.OrderedDict()
# get next line
next = configfile.readline()
next = next.strip()
# check all lines after "data-model:" for pairs
for next in configfile:
if re.match("^#{1}", next):
pass
elif re.match("^\s?\n{1}", next):
pass
elif next is None:
pass
else:
# get the pair
optkey, optcmd = next.split('=', 2)
optkey = optkey.strip()
optcmd = optcmd.strip()
# add key and command to dictionary
tmpdict.update({str(optkey):[str(optcmd)]})
data_model = True
if data_model is True:
# keep data-model for agent
data_model = tmpdict
del tmpdict
elif data_model is False:
raise ValueError
else:
pass
except ValueError:
print("FAILURE: Invalid format of kvp in input model")
configfile.close()
sys.exit()
else:
pass
else:
pass
# try closing config file, else throw I/O error then display failure message
# and exit agent.
try:
if os.path.exists(config_path):
configfile.close()
else:
raise IOError
except IOError:
print("FAILURE: Failed to close configuration file " % config_path)
sys.exit()
# run required checks
check_distribution()
check_dependencies()
check_config_file()
check_config_options()
# remove code for checks
del check_distribution
del check_dependencies
del check_config_file
del check_config_options
# try checking service status of rsyslog, else throw OSError. if rsyslog is
# running, set flag to true. if rsyslog is not running, set flag to false.
def check_rsyslog_status():
global status_flag
try:
status, _ = syscmd("service rsyslog status")
if "Active: active (running)" in status:
status_flag = True
else:
raise OSError
except OSError:
print("WARNING: Rsyslog not running. Redirecting JSON to logs/server_stats_collector/timestamp")
status_flag = False
# check status of rsyslog service before logging data. if rsyslog is running,
# use logger. if rsyslog is not running, append data to new file at
# "logs/server_stats_collector/timestamp".
def log_data():
global update_interval
global data_model
global status_flag
check_rsyslog_status()
# use logger to send data to local rsyslog service
if status_flag is True:
syslog.syslog("SERVER_STATS_COLLECTOR.PY %s" % data_model)
# append data to new file
else:
timestamp = calendar.timegm(time.gmtime())
if os.path.exists("logs/server_stats_collector"):
_, _ = syscmd("echo %s >> logs/server_stats_collector/%s" % (data_model, timestamp))
else:
_, _ = syscmd("mkdir -p logs/server_stats_collector")
_, _ = syscmd("echo %s >> logs/server_stats_collector/%s" % (data_model, timestamp))
# get value and timestamp, append to key in dictionary.
def get_values():
global data_model
# create temporary dictionary
tmpdict = collections.OrderedDict()
# get values for keys in dictionary using syscmd()
for key,command in data_model.items():
value, timestamp = syscmd(command[0])
# add value and timestamp of key to temporary dictionary
keyname = "'%s'" % key
tmpdict.update({keyname: [value, timestamp]})
# replace data_model with tmpdict
data_model = tmpdict
del tmpdict
# convert dictionary to json
data_model = json.dumps(data_model, ensure_ascii=False)