| 1 | from NSCP import Settings, Registry, Core, log, status, log_error, sleep
|
|---|
| 2 | from test_helper import BasicTest, TestResult, Callable, setup_singleton, install_testcases, init_testcases, shutdown_testcases
|
|---|
| 3 | import plugin_pb2
|
|---|
| 4 | from types import *
|
|---|
| 5 | import socket
|
|---|
| 6 | import uuid
|
|---|
| 7 | import unicodedata
|
|---|
| 8 | #import _thread
|
|---|
| 9 | #sync = _thread.allocate_lock()
|
|---|
| 10 |
|
|---|
| 11 | import threading
|
|---|
| 12 | sync = threading.RLock()
|
|---|
| 13 |
|
|---|
| 14 | core = Core.get()
|
|---|
| 15 |
|
|---|
| 16 | def isOpen(ip, port):
|
|---|
| 17 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|---|
| 18 | try:
|
|---|
| 19 | s.connect((ip, int(port)))
|
|---|
| 20 | s.shutdown(2)
|
|---|
| 21 | return True
|
|---|
| 22 | except:
|
|---|
| 23 | return False
|
|---|
| 24 |
|
|---|
| 25 | class NSCAMessage:
|
|---|
| 26 | uuid = None
|
|---|
| 27 | source = None
|
|---|
| 28 | command = None
|
|---|
| 29 | status = None
|
|---|
| 30 | message = None
|
|---|
| 31 | perfdata = None
|
|---|
| 32 | got_response = False
|
|---|
| 33 | got_simple_response = False
|
|---|
| 34 |
|
|---|
| 35 | def __init__(self, command):
|
|---|
| 36 | if type(command) == 'unicode':
|
|---|
| 37 | try:
|
|---|
| 38 | self.uuid = command.decode('ascii', 'replace')
|
|---|
| 39 | except UnicodeDecodeError:
|
|---|
| 40 | self.uuid = command
|
|---|
| 41 | else:
|
|---|
| 42 | self.uuid = command
|
|---|
| 43 | #self.uuid = unicodedata.normalize('NFKD', command).encode('ascii','ignore')
|
|---|
| 44 | self.command = command
|
|---|
| 45 | self.source = None
|
|---|
| 46 | self.status = None
|
|---|
| 47 | self.message = None
|
|---|
| 48 | self.perfdata = None
|
|---|
| 49 | self.got_response = False
|
|---|
| 50 | self.got_simple_response = False
|
|---|
| 51 |
|
|---|
| 52 |
|
|---|
| 53 | def copy_changed_attributes(self, other):
|
|---|
| 54 | if other.source:
|
|---|
| 55 | self.source = other.source
|
|---|
| 56 | if other.command:
|
|---|
| 57 | self.command = other.command
|
|---|
| 58 | if other.status:
|
|---|
| 59 | self.status = other.status
|
|---|
| 60 | if other.message:
|
|---|
| 61 | self.message = other.message
|
|---|
| 62 | if other.perfdata:
|
|---|
| 63 | self.perfdata = other.perfdata
|
|---|
| 64 | if other.got_simple_response:
|
|---|
| 65 | self.got_simple_response = True
|
|---|
| 66 |
|
|---|
| 67 | def __str__(self):
|
|---|
| 68 | return 'Message: %s (%s, %s, %s)'%(self.uuid, self.source, self.command, self.status)
|
|---|
| 69 |
|
|---|
| 70 | class NSCAServerTest(BasicTest):
|
|---|
| 71 | instance = None
|
|---|
| 72 | key = ''
|
|---|
| 73 | reg = None
|
|---|
| 74 | _responses = {}
|
|---|
| 75 |
|
|---|
| 76 | def has_response(self, id):
|
|---|
| 77 | with sync:
|
|---|
| 78 | return id in self._responses
|
|---|
| 79 |
|
|---|
| 80 | def get_response(self, id):
|
|---|
| 81 | with sync:
|
|---|
| 82 | if id in self._responses:
|
|---|
| 83 | return self._responses[id]
|
|---|
| 84 | msg = NSCAMessage(id)
|
|---|
| 85 | self._responses[id] = msg
|
|---|
| 86 | return msg
|
|---|
| 87 |
|
|---|
| 88 | def set_response(self, msg):
|
|---|
| 89 | with sync:
|
|---|
| 90 | if msg.uuid in self._responses:
|
|---|
| 91 | log('Updated: %s'%msg.uuid)
|
|---|
| 92 | self._responses[msg.uuid].copy_changed_attributes(msg)
|
|---|
| 93 | else:
|
|---|
| 94 | log('Added: %s'%msg.uuid)
|
|---|
| 95 | self._responses[msg.uuid] = msg
|
|---|
| 96 |
|
|---|
| 97 |
|
|---|
| 98 | def del_response(self, id):
|
|---|
| 99 | with sync:
|
|---|
| 100 | del self._responses[id]
|
|---|
| 101 |
|
|---|
| 102 |
|
|---|
| 103 | def desc(self):
|
|---|
| 104 | return 'Testcase for NSCA protocol'
|
|---|
| 105 |
|
|---|
| 106 | def title(self):
|
|---|
| 107 | return 'NSCA Server test'
|
|---|
| 108 |
|
|---|
| 109 | def setup(self, plugin_id, prefix):
|
|---|
| 110 | self.key = '_%stest_command'%prefix
|
|---|
| 111 | self.reg = Registry.get(plugin_id)
|
|---|
| 112 | self.reg.simple_subscription('nsca_test_inbox', NSCAServerTest.simple_inbox_handler)
|
|---|
| 113 | self.reg.subscription('nsca_test_inbox', NSCAServerTest.inbox_handler)
|
|---|
| 114 |
|
|---|
| 115 | def simple_inbox_handler(channel, source, command, code, message, perf):
|
|---|
| 116 | instance = NSCAServerTest.getInstance()
|
|---|
| 117 | return instance.simple_inbox_handler_wrapped(channel, source, command, code, message, perf)
|
|---|
| 118 | simple_inbox_handler = Callable(simple_inbox_handler)
|
|---|
| 119 |
|
|---|
| 120 | def inbox_handler(channel, request):
|
|---|
| 121 | instance = NSCAServerTest.getInstance()
|
|---|
| 122 | return instance.inbox_handler_wrapped(channel, request)
|
|---|
| 123 | inbox_handler = Callable(inbox_handler)
|
|---|
| 124 |
|
|---|
| 125 | def simple_inbox_handler_wrapped(self, channel, source, command, status, message, perf):
|
|---|
| 126 | log('Got simple message %s on %s'%(command, channel))
|
|---|
| 127 | msg = NSCAMessage(command)
|
|---|
| 128 | msg.source = source
|
|---|
| 129 | msg.status = status
|
|---|
| 130 | msg.message = message
|
|---|
| 131 | msg.perfdata = perf
|
|---|
| 132 | msg.got_simple_response = True
|
|---|
| 133 | self.set_response(msg)
|
|---|
| 134 | return True
|
|---|
| 135 |
|
|---|
| 136 | def inbox_handler_wrapped(self, channel, request):
|
|---|
| 137 | message = plugin_pb2.SubmitRequestMessage()
|
|---|
| 138 | message.ParseFromString(request)
|
|---|
| 139 | command = message.payload[0].command
|
|---|
| 140 | log('Got message %s on %s'%(command, channel))
|
|---|
| 141 |
|
|---|
| 142 | msg = NSCAMessage(command)
|
|---|
| 143 | msg.got_response = True
|
|---|
| 144 | self.set_response(msg)
|
|---|
| 145 | return None
|
|---|
| 146 |
|
|---|
| 147 | def teardown(self):
|
|---|
| 148 | None
|
|---|
| 149 |
|
|---|
| 150 | def wait_and_validate(self, uuid, result, msg, perf, tag):
|
|---|
| 151 | found = False
|
|---|
| 152 | for i in range(0,10):
|
|---|
| 153 | if self.has_response(uuid):
|
|---|
| 154 | rmsg = self.get_response(uuid)
|
|---|
| 155 | if not rmsg.got_simple_response or not rmsg.got_response:
|
|---|
| 156 | for j in range(0,3):
|
|---|
| 157 | rmsg = self.get_response(uuid)
|
|---|
| 158 | if rmsg.got_simple_response and rmsg.got_response:
|
|---|
| 159 | log('Got delayed response %s'%uuid)
|
|---|
| 160 | else:
|
|---|
| 161 | log('Waiting for delayed response %s (%d/10)'%(uuid, j+1))
|
|---|
| 162 |
|
|---|
| 163 | result.add_message(rmsg.got_response, 'Testing to recieve message using %s'%tag)
|
|---|
| 164 | result.add_message(rmsg.got_simple_response, 'Testing to recieve simple message using %s'%tag)
|
|---|
| 165 | result.assert_equals(rmsg.command, uuid, 'Verify that command is sent through using %s'%tag)
|
|---|
| 166 | result.assert_contains(rmsg.message, msg, 'Verify that message is sent through using %s'%tag)
|
|---|
| 167 |
|
|---|
| 168 | #result.assert_equals(rmsg.last_source, source, 'Verify that source is sent through')
|
|---|
| 169 | #result.assert_equals(rmsg.perfdata, perf, 'Verify that performance data is sent through using %s'%tag)
|
|---|
| 170 | self.del_response(uuid)
|
|---|
| 171 | return True
|
|---|
| 172 | else:
|
|---|
| 173 | log('Waiting for %s (%d/10)'%(uuid, i+1))
|
|---|
| 174 | sleep(1)
|
|---|
| 175 | result.add_message(False, 'Failed to recieve message %s using %s'%(uuid, tag))
|
|---|
| 176 | return False
|
|---|
| 177 |
|
|---|
| 178 |
|
|---|
| 179 | def submit_payload(self, encryption, source, status, msg, perf, tag):
|
|---|
| 180 | message = plugin_pb2.SubmitRequestMessage()
|
|---|
| 181 |
|
|---|
| 182 | message.header.version = plugin_pb2.Common.VERSION_1
|
|---|
| 183 | message.header.recipient_id = "test_rp"
|
|---|
| 184 | message.channel = 'nsca_test_outbox'
|
|---|
| 185 | host = message.header.hosts.add()
|
|---|
| 186 | host.address = "127.0.0.1:15667"
|
|---|
| 187 | host.id = "test_rp"
|
|---|
| 188 | enc = host.metadata.add()
|
|---|
| 189 | enc.key = "encryption"
|
|---|
| 190 | enc.value = encryption
|
|---|
| 191 | enc = host.metadata.add()
|
|---|
| 192 | enc.key = "password"
|
|---|
| 193 | enc.value = 'pwd-%s'%encryption
|
|---|
| 194 |
|
|---|
| 195 | uid = str(uuid.uuid4())
|
|---|
| 196 | payload = message.payload.add()
|
|---|
| 197 | payload.result = status
|
|---|
| 198 | payload.command = uid
|
|---|
| 199 | payload.message = '%s - %s'%(uid, msg)
|
|---|
| 200 | payload.source = source
|
|---|
| 201 | (result_code, err) = core.submit('nsca_test_outbox', message.SerializeToString())
|
|---|
| 202 |
|
|---|
| 203 | result = TestResult('Testing payload submission (via API): %s'%tag)
|
|---|
| 204 | result.add_message(len(err) == 0, 'Testing to send message using %s/sbp'%tag, err)
|
|---|
| 205 | self.wait_and_validate(uid, result, msg, perf, '%s/spb'%tag)
|
|---|
| 206 | return result
|
|---|
| 207 |
|
|---|
| 208 | def submit_via_exec(self, encryption, source, status, msg, perf, tag):
|
|---|
| 209 | uid = str(uuid.uuid4())
|
|---|
| 210 |
|
|---|
| 211 | args = [
|
|---|
| 212 | #'--exec', 'submit',
|
|---|
| 213 | '--alias', uid,
|
|---|
| 214 | '--result', '%d'%status,
|
|---|
| 215 | '--message', '%s - %s'%(uid, msg),
|
|---|
| 216 | '--encryption', encryption,
|
|---|
| 217 | '--password', 'pwd-%s'%encryption,
|
|---|
| 218 | '--host', '127.0.0.1:15667'
|
|---|
| 219 | ]
|
|---|
| 220 | (result_code, result_message) = core.simple_exec('any', 'nsca_submit', args)
|
|---|
| 221 | result = TestResult('Testing payload submission (via command line exec): %s'%tag)
|
|---|
| 222 |
|
|---|
| 223 | result.add_message(result_code == 0, 'Testing to send message using %s/exec:1'%tag)
|
|---|
| 224 | result.add_message(len(result_message) == 1, 'Testing to send message using %s/exec:2'%tag, len(result_message))
|
|---|
| 225 | result.add_message(len(result_message[0]) == 0, 'Testing to send message using %s/exec:3'%tag, result_message[0])
|
|---|
| 226 | self.wait_and_validate(uid, result, msg, perf, '%s/exec'%tag)
|
|---|
| 227 | return result
|
|---|
| 228 |
|
|---|
| 229 | def test_one_crypto_full(self, encryption, state, key):
|
|---|
| 230 | result = TestResult('Testing %s/%s'%(encryption, key))
|
|---|
| 231 | result.add(self.submit_payload(encryption, '%ssrc%s'%(key, key), state, '%smsg%s'%(key, key), '', '%s/%s'%(state, encryption)))
|
|---|
| 232 | result.add(self.submit_via_exec(encryption, '%ssrc%s'%(key, key), state, '%smsg%s'%(key, key), '', '%s/%s'%(state, encryption)))
|
|---|
| 233 | return result
|
|---|
| 234 |
|
|---|
| 235 | def test_one_crypto(self, crypto):
|
|---|
| 236 | conf = Settings.get()
|
|---|
| 237 | conf.set_string('/settings/NSCA/test_nsca_server', 'encryption', '%s'%crypto)
|
|---|
| 238 | conf.set_string('/settings/NSCA/test_nsca_server', 'password', 'pwd-%s'%crypto)
|
|---|
| 239 | core.reload('test_nsca_server')
|
|---|
| 240 | result = TestResult('Testing encryption algorith: %s'%crypto)
|
|---|
| 241 | result.add_message(isOpen('localhost', 15667), 'Checking that port is open')
|
|---|
| 242 | result.add(self.test_one_crypto_full(crypto, status.UNKNOWN, 'unknown'))
|
|---|
| 243 | result.add(self.test_one_crypto_full(crypto, status.OK, 'ok'))
|
|---|
| 244 | result.add(self.test_one_crypto_full(crypto, status.WARNING, 'warn'))
|
|---|
| 245 | result.add(self.test_one_crypto_full(crypto, status.CRITICAL, 'crit'))
|
|---|
| 246 | return result
|
|---|
| 247 |
|
|---|
| 248 | def run_test(self):
|
|---|
| 249 | result = TestResult()
|
|---|
| 250 | cryptos = ["none", "xor", "des", "3des", "cast128", "xtea", "blowfish", "twofish", "rc2", "aes", "aes256", "aes192", "aes128", "serpent", "gost", "3way"]
|
|---|
| 251 | for c in cryptos:
|
|---|
| 252 | result.add_message(True, 'Testing crypto: %s'%c)
|
|---|
| 253 | result.add(self.test_one_crypto(c))
|
|---|
| 254 |
|
|---|
| 255 | return result
|
|---|
| 256 |
|
|---|
| 257 | def install(self, arguments):
|
|---|
| 258 | conf = Settings.get()
|
|---|
| 259 | conf.set_string('/modules', 'test_nsca_server', 'NSCAServer')
|
|---|
| 260 | conf.set_string('/modules', 'test_nsca_client', 'NSCAClient')
|
|---|
| 261 | conf.set_string('/modules', 'pytest', 'PythonScript')
|
|---|
| 262 |
|
|---|
| 263 | conf.set_string('/settings/pytest/scripts', 'test_nsca', 'test_nsca.py')
|
|---|
| 264 |
|
|---|
| 265 | conf.set_string('/settings/NSCA/test_nsca_server', 'port', '15667')
|
|---|
| 266 | conf.set_string('/settings/NSCA/test_nsca_server', 'inbox', 'nsca_test_inbox')
|
|---|
| 267 | conf.set_string('/settings/NSCA/test_nsca_server', 'encryption', '1')
|
|---|
| 268 |
|
|---|
| 269 | conf.set_string('/settings/NSCA/test_nsca_client/targets', 'nsca_test_local', 'nsca://127.0.0.1:15667')
|
|---|
| 270 | conf.set_string('/settings/NSCA/test_nsca_client', 'channel', 'nsca_test_outbox')
|
|---|
| 271 |
|
|---|
| 272 | conf.save()
|
|---|
| 273 |
|
|---|
| 274 | def uninstall(self):
|
|---|
| 275 | None
|
|---|
| 276 |
|
|---|
| 277 | def help(self):
|
|---|
| 278 | None
|
|---|
| 279 |
|
|---|
| 280 | def init(self, plugin_id):
|
|---|
| 281 | None
|
|---|
| 282 |
|
|---|
| 283 | def shutdown(self):
|
|---|
| 284 | None
|
|---|
| 285 |
|
|---|
| 286 | def require_boot(self):
|
|---|
| 287 | return True
|
|---|
| 288 |
|
|---|
| 289 |
|
|---|
| 290 | setup_singleton(NSCAServerTest)
|
|---|
| 291 |
|
|---|
| 292 | all_tests = [NSCAServerTest]
|
|---|
| 293 |
|
|---|
| 294 | def __main__():
|
|---|
| 295 | install_testcases(all_tests)
|
|---|
| 296 |
|
|---|
| 297 | def init(plugin_id, plugin_alias, script_alias):
|
|---|
| 298 | init_testcases(plugin_id, plugin_alias, script_alias, all_tests)
|
|---|
| 299 |
|
|---|
| 300 | def shutdown():
|
|---|
| 301 | shutdown_testcases()
|
|---|