Merge "Move net_check into dhcp_checker package"

This commit is contained in:
Jenkins 2014-03-04 11:11:14 +00:00 committed by Gerrit Code Review
commit 5a9b097a78
6 changed files with 892 additions and 0 deletions

View File

@ -0,0 +1,13 @@
# Copyright 2014 Mirantis, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may 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.

688
network_checker/net_check/api.py Executable file
View File

@ -0,0 +1,688 @@
#!/usr/bin/python
# Copyright 2014 Mirantis, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may 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.
#
# Generate and send Ethernet packets to specified interfaces.
# Collect data from interfaces.
# Analyse dumps for packets with special cookie in UDP payload.
#
import argparse
import functools
import json
import logging
import logging.handlers
import os
import re
import signal
import socket
import subprocess
import sys
import threading
import time
import traceback
from scapy import config as scapy_config
import pcap
scapy_config.logLevel = 40
scapy_config.use_pcap = True
import scapy.all as scapy
class ActorFabric(object):
@classmethod
def getInstance(cls, config):
if config.get('action') not in ('listen', 'generate'):
raise Exception(
'Wrong config, you need define '
'valid action instead of {0}'.format(config.get('action')))
if config['action'] in ('listen',):
return Listener(config)
elif config['action'] in ('generate',):
return Sender(config)
class ActorException(Exception):
def __init__(self, logger, message='', level='error'):
getattr(logger, level, logger.error)(message)
super(ActorException, self).__init__(message)
class Actor(object):
def __init__(self, config=None):
self.config = {
'src_mac': None,
'src': '1.0.0.0',
'dst': '1.0.0.0',
'sport': 31337,
'dport': 31337,
'cookie': "Nailgun:",
}
if config:
self.config.update(config)
self.logger.debug("Running with config: %s", json.dumps(self.config))
self._execute(["modprobe", "8021q"])
self.iface_down_after = {}
self.viface_remove_after = {}
def _define_logger(self, filename=None,
appname='netprobe', level=logging.DEBUG):
logger = logging.getLogger()
logger.setLevel(level)
syslog_formatter = logging.Formatter(
'{appname}: %(message)s'.format(appname=appname)
)
syslog_handler = logging.handlers.SysLogHandler('/dev/log')
syslog_handler.setFormatter(syslog_formatter)
logger.addHandler(syslog_handler)
# A syslog handler should be always. But a file handler is the option.
# If you don't want it you can keep 'filename' variable as None to skip
# this handler.
if filename:
file_formatter = logging.Formatter(
'%(asctime)s %(name)-12s %(levelname)-8s %(message)s'
)
file_handler = logging.FileHandler(filename)
file_handler.setFormatter(file_formatter)
logger.addHandler(file_handler)
return logger
def _execute(self, command, expected_exit_codes=(0,)):
self.logger.debug("Running command: %s" % " ".join(command))
env = os.environ
env["PATH"] = "/bin:/usr/bin:/sbin:/usr/sbin"
p = subprocess.Popen(command, shell=False,
env=env, stdout=subprocess.PIPE)
output, _ = p.communicate()
if p.returncode not in expected_exit_codes:
raise ActorException(
self.logger,
"Command exited with error: %s: %s" % (" ".join(command),
p.returncode)
)
return output.split('\n')
def _viface_by_iface_vid(self, iface, vid):
return (self._try_viface_create(iface, vid) or "%s.%d" % (iface, vid))
def _iface_name(self, iface, vid=None):
if vid:
return self._viface_by_iface_vid(iface, vid)
return iface
def _look_for_link(self, iface, vid=None):
viface = None
if vid:
viface = self._viface_by_iface_vid(iface, vid)
command = ['ip', 'link']
r = re.compile(ur"(\d+?):\s+((?P<viface>[^:@]+)@)?(?P<iface>[^:]+?):"
".+?(?P<state>UP|DOWN|UNKNOWN).*$")
for line in self._execute(command):
m = r.search(line)
if m:
md = m.groupdict()
if (iface == md.get('iface') and
viface == md.get('viface') and md.get('state')):
return (iface, viface, md.get('state'))
# If we are here we aren't able to say if iface with vid is up
raise ActorException(
self.logger,
"Cannot find interface %s with vid=%s" % (iface, vid)
)
def _try_iface_up(self, iface, vid=None):
if vid and not self._try_viface_create(iface, vid):
# if viface does not exist we raise exception
raise ActorException(
self.logger,
"Vlan %s on interface %s does not exist" % (str(vid), iface)
)
self.logger.debug("Checking if interface %s with vid %s is up",
iface, str(vid))
_, _, state = self._look_for_link(iface, vid)
return (state == 'UP')
def _iface_up(self, iface, vid=None):
"""Brings interface with vid up
"""
if vid and not self._try_viface_create(iface, vid):
# if viface does not exist we raise exception
raise ActorException(
self.logger,
"Vlan %s on interface %s does not exist" % (str(vid), iface)
)
set_iface = self._iface_name(iface, vid)
self.logger.debug("Brining interface %s with vid %s up",
set_iface, str(vid))
self._execute([
"ip",
"link", "set",
"dev", set_iface,
"up"])
def _ensure_iface_up(self, iface, vid=None):
"""Ensures interface is with vid up.
"""
if not self._try_iface_up(iface, vid):
# if iface is not up we try to bring it up
self._iface_up(iface, vid)
if self._try_iface_up(iface, vid):
# if iface was down and we have brought it up
# we should mark it to be brought down after probing
self.iface_down_after[self._iface_name(iface, vid)] = True
else:
# if viface is still down we raise exception
raise ActorException(
self.logger,
"Can not bring interface %s with vid %s up" % (iface,
str(vid))
)
def _ensure_iface_down(self, iface, vid=None):
set_iface = self._iface_name(iface, vid)
if self.iface_down_after.get(set_iface, False):
# if iface with vid have been marked to be brought down
# after probing we try to bring it down
self.logger.debug("Brining down interface %s with vid %s",
iface, str(vid))
self._execute([
"ip",
"link", "set",
"dev", set_iface,
"down"])
self.iface_down_after.pop(set_iface)
def _try_viface_create(self, iface, vid):
"""Tries to find vlan interface on iface with VLAN_ID=vid and return it
:returns: name of vlan interface if it exists or None
"""
self.logger.debug("Checking if vlan %s on interface %s exists",
str(vid), iface)
with open("/proc/net/vlan/config", "r") as f:
for line in f:
m = re.search(ur'(.+?)\s+\|\s+(.+?)\s+\|\s+(.+?)\s*$', line)
if m and m.group(2) == str(vid) and m.group(3) == iface:
return m.group(1)
def _viface_create(self, iface, vid):
"""Creates VLAN interface with VLAN_ID=vid on interface iface
:returns: None
"""
self.logger.debug("Creating vlan %s on interface %s", str(vid), iface)
self._execute([
"ip",
"link", "add",
"link", iface,
"name", self._viface_by_iface_vid(iface, vid),
"type", "vlan",
"id", str(vid)])
def _ensure_viface_create(self, iface, vid):
"""Ensures that vlan interface exists. If it does not already
exist, then we need it to be created. It also marks newly created
vlan interface to remove it after probing procedure.
"""
if not self._try_viface_create(iface, vid):
# if viface does not exist we try to create it
self._viface_create(iface, vid)
if self._try_viface_create(iface, vid):
# if viface had not existed and have been created
# we mark it to be removed after probing procedure
self.viface_remove_after[
self._viface_by_iface_vid(iface, vid)
] = True
else:
# if viface had not existed and still does not
# we raise exception
raise ActorException(
self.logger,
"Can not create vlan %d on interface %s" % (vid, iface)
)
def _ensure_viface_remove(self, iface, vid):
viface = self._viface_by_iface_vid(iface, vid)
if self.viface_remove_after.get(viface, False):
# if viface have been marked to be removed after probing
# we try to remove it
self.logger.debug("Removing vlan %s on interface %s",
str(vid), iface)
self._execute([
"ip",
"link", "del",
"dev", viface])
self.viface_remove_after.pop(viface)
def _parse_vlan_list(self, vlan_string):
self.logger.debug("Parsing vlan list: %s", vlan_string)
validate = lambda x: (x >= 0) and (x < 4095)
chunks = vlan_string.split(",")
vlan_list = []
for chunk in chunks:
delim = chunk.find("-")
try:
if delim > 0:
left = int(chunk[:delim])
right = int(chunk[delim + 1:])
if validate(left) and validate(right):
vlan_list.extend(xrange(left, right + 1))
else:
raise ValueError
else:
vlan = int(chunk)
if validate(vlan):
vlan_list.append(vlan)
else:
raise ValueError
except ValueError:
raise ActorException(self.logger, "Incorrect vlan: %s" % chunk)
self.logger.debug("Parsed vlans: %s", str(vlan_list))
return vlan_list
def _ensure_viface_create_and_up(self, iface, vid):
self._ensure_viface_create(iface, vid)
self._ensure_iface_up(iface, vid)
def _ensure_viface_down_and_remove(self, iface, vid):
self._ensure_iface_down(iface, vid)
self._ensure_viface_remove(iface, vid)
def _iface_vlan_iterator(self):
for iface, vlan_list in self.config['interfaces'].iteritems():
# Variables iface and vlan_list are getted from decoded JSON
# and json.dump convert all string data to Python unicode string.
# We use these variables in logging messages later.
# CentOS 6.4 uses Python 2.6 and logging module 0.5.0.5 which has
# a bug with converting unicode strings to message in
# SysLogHandler. So we need to convert all unicode to plain
# strings to avoid syslog message corruption.
for vlan in self._parse_vlan_list(str(vlan_list)):
yield (str(iface), vlan)
def _iface_iterator(self):
for iface in self.config['interfaces']:
yield iface
def _log_ifaces(self, prefix="Current interfaces"):
self.logger.debug("%s: ", prefix)
for line in self._execute(['ip', 'address']):
self.logger.debug(line.rstrip())
class Sender(Actor):
def __init__(self, config=None):
self.logger = self._define_logger('/root/netprobe_sender.log',
'netprobe_sender')
super(Sender, self).__init__(config)
self.logger.info("=== Starting Sender ===")
self._log_ifaces("Interfaces just before sending probing packages")
def run(self):
try:
self._run()
except Exception as e:
self.logger.error("An internal error occured: %s\n%s", str(e),
traceback.format_exc())
def _run(self):
for iface, vlan in self._iface_vlan_iterator():
self._ensure_iface_up(iface)
data = str(''.join((self.config['cookie'], iface, ' ',
self.config['uid'])))
self.logger.debug("Sending packets: iface=%s vlan=%s",
iface, str(vlan))
if vlan > 0:
self.logger.debug("Ensure up: %s, %s", iface, str(vlan))
self._ensure_viface_create_and_up(iface, vlan)
viface = self._viface_by_iface_vid(iface, vlan)
else:
viface = iface
p = scapy.Ether(src=self.config['src_mac'],
dst="ff:ff:ff:ff:ff:ff")
p = p / scapy.IP(src=self.config['src'], dst=self.config['dst'])
p = p / scapy.UDP(sport=self.config['sport'],
dport=self.config['dport']) / data
try:
for i in xrange(5):
self.logger.debug("Sending packet: iface=%s data=%s",
viface, data)
scapy.sendp(p, iface=viface)
except socket.error as e:
self.logger.error("Socket error: %s, %s", e, viface)
if vlan > 0:
self.logger.debug("Ensure down: %s, %s", iface, str(vlan))
self._ensure_viface_down_and_remove(iface, vlan)
self._log_ifaces("Interfaces just after sending probing packages")
for iface in self._iface_iterator():
self._ensure_iface_down(iface)
self._log_ifaces("Interfaces just after ensuring them down in sender")
self.logger.info("=== Sender Finished ===")
class Listener(Actor):
def __init__(self, config=None):
self.logger = self._define_logger('/root/netprobe_listener.log',
'netprobe_listener')
super(Listener, self).__init__(config)
self.logger.info("=== Starting Listener ===")
self._log_ifaces("Interfaces just before starting listerning "
"for probing packages")
self.pidfile = self.addpid('/var/run/net_probe')
self.neighbours = {}
def addpid(self, piddir):
pid = os.getpid()
if not os.path.exists(piddir):
os.mkdir(piddir)
pidfile = os.path.join(piddir, str(pid))
with open(pidfile, 'w') as fo:
fo.write('')
return pidfile
def run(self):
try:
self._run()
except Exception as e:
self.logger.error("An internal error occured: %s\n%s", str(e),
traceback.format_exc())
def _run(self):
sniffers = set()
def run_listener_thread(iface, vlan=False):
t = threading.Thread(
target=self.get_probe_frames,
args=(iface, vlan)
)
t.daemon = True
t.start()
return t
for iface, vlan in self._iface_vlan_iterator():
self._ensure_iface_up(iface)
if vlan > 0:
self.logger.debug("Ensure up: %s, %s", iface, str(vlan))
self._ensure_viface_create_and_up(iface, vlan)
self._viface_by_iface_vid(iface, vlan)
if iface not in sniffers:
run_listener_thread(iface)
run_listener_thread(iface, vlan=True)
sniffers.add(iface)
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((self.config.get('ready_address', 'locahost'),
self.config.get('ready_port', 31338)))
except socket.error as e:
self.logger.error("Socket error: %s", e)
else:
self.logger.debug("Listener threads have been launched. "
"Reporting READY.")
msg = "READY"
total_sent = 0
while total_sent < len(msg):
sent = s.send(msg[total_sent:])
if sent == 0:
raise ActorException(
self.logger,
"Socket broken. Cannot send %s status." % msg
)
total_sent += sent
s.shutdown(socket.SHUT_RDWR)
s.close()
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
self.logger.debug("Interruption signal catched")
except SystemExit:
self.logger.debug("TERM signal catched")
self._log_ifaces("Interfaces just before ensuring interfaces down")
for iface, vlan in self._iface_vlan_iterator():
if vlan > 0:
self.logger.debug("Ensure down: %s, %s", iface, str(vlan))
self._ensure_viface_down_and_remove(iface, vlan)
for iface in self._iface_iterator():
self._ensure_iface_down(iface)
self._log_ifaces(
"Interfaces just after ensuring them down in listener")
with open(self.config['dump_file'], 'w') as fo:
fo.write(json.dumps(self.neighbours))
os.unlink(self.pidfile)
self.logger.info("=== Listener Finished ===")
def fprn(self, p, iface):
if scapy.Dot1Q in p:
vlan = p[scapy.Dot1Q].vlan
else:
vlan = 0
self.logger.debug("Catched packet: vlan=%s len=%s payload=%s",
str(vlan), p[scapy.UDP].len, p[scapy.UDP].payload)
received_msg = str(p[scapy.UDP].payload)[:p[scapy.UDP].len]
decoded_msg = received_msg.decode()
riface, uid = decoded_msg[len(self.config["cookie"]):].split(' ', 1)
uid = uid.strip('\x00\n')
self.neighbours[iface].setdefault(vlan, {})
if riface not in self.neighbours[iface][vlan].setdefault(uid, []):
self.neighbours[iface][vlan][uid].append(riface)
def get_probe_frames(self, iface, vlan=False):
if iface not in self.neighbours:
self.neighbours[iface] = {}
"""
We do not use scapy filtering because it is slow. Instead we use
python binding to extreamely fast libpcap library to filter out
probing packages.
"""
pc = pcap.pcap(iface)
filter_string = 'udp and dst port {0}'.format(self.config['dport'])
if vlan:
filter_string = 'vlan and {0}'.format(filter_string)
pc.setfilter(filter_string)
def fltr(p):
try:
received_msg = str(p[scapy.UDP].payload)[:p[scapy.UDP].len]
decoded_msg = received_msg.decode()
return decoded_msg.startswith(self.config["cookie"])
except Exception as e:
self.logger.debug("Error while filtering packet: %s", str(e))
return False
pprn = functools.partial(self.fprn, iface=iface)
try:
while True:
ts, pkt = pc.next()
p = scapy.Ether(pkt)
if fltr(p):
pprn(p)
except (KeyboardInterrupt, SystemExit):
pass
# -------------- main ---------------
def define_parser():
config_examples = """
Config file examples:
Capture frames config file example is:
{"action": "listen", "interfaces": {"eth0": "1-4094"},
"dump_file": "/var/tmp/net-probe-dump-eth0"}
Simple frame generation config file example is:
{"action": "generate", "uid": "aaa-bb-cccccc",
"interfaces": { "eth0": "1-4094"}}
Full frame generation config file example is:
{ "action": "generate",
"uid": "aaa-bb-cccccc", "cookie": "Some cookie",
"src_mac": "11:22:33:44:55:66",
"src": "10.0.0.1", "dst": "10.255.255.255",
"sport": 4056, "dport": 4057,
"interfaces": {
"eth0": "10, 15, 20, 201-210, 301-310, 1000-2000",
"eth1": "1-4094"
}
}
"""
parser = argparse.ArgumentParser(epilog=config_examples)
parser.add_argument(
'-c', '--config', dest='config', action='store', type=str,
help='config file', default=None
)
return parser
def define_subparsers(parser):
subparsers = parser.add_subparsers(
dest="action", help='actions'
)
listen_parser = subparsers.add_parser(
'listen', help='listen for probe packets'
)
listen_parser.add_argument(
'-i', '--interface', dest='interface', action='store', type=str,
help='interface to listen on', required=True
)
listen_parser.add_argument(
'-v', '--vlans', dest='vlan_list', action='store', type=str,
help='vlan list to send tagged packets ("100,200-300")', required=True
)
listen_parser.add_argument(
'-k', '--cookie', dest='cookie', action='store', type=str,
help='cookie string to insert into probe packets payload',
default='Nailgun:'
)
listen_parser.add_argument(
'-o', '--file', dest='dump_file', action='store', type=str,
help='file to dump captured packets', default=None
)
listen_parser.add_argument(
'-a', '--address', dest='ready_address', action='store', type=str,
help='address to report listener ready state', default='localhost'
)
listen_parser.add_argument(
'-p', '--port', dest='ready_port', action='store', type=int,
help='port to report listener ready state', default=31338
)
generate_parser = subparsers.add_parser(
'generate', help='generate and send probe packets'
)
generate_parser.add_argument(
'-i', '--interface', dest='interface', action='store', type=str,
help='interface to send packets from', required=True
)
generate_parser.add_argument(
'-v', '--vlans', dest='vlan_list', action='store', type=str,
help='vlan list to send tagged packets ("100,200-300")', required=True
)
generate_parser.add_argument(
'-k', '--cookie', dest='cookie', action='store', type=str,
help='cookie string to insert into probe packets payload',
default='Nailgun:'
)
generate_parser.add_argument(
'-u', '--uid', dest='uid', action='store', type=str,
help='uid to insert into probe packets payload', default='1'
)
def term_handler(signum, sigframe):
sys.exit()
def main():
signal.signal(signal.SIGTERM, term_handler)
parser = define_parser()
params, other_params = parser.parse_known_args()
config = {}
if params.config:
# if config file is set then we discard all other
# command line parameters
try:
if params.config == '-':
fo = sys.stdin
else:
fo = open(params.config, 'r')
config = json.load(fo)
fo.close()
except IOError:
print("Can not read config file %s" % params.config)
exit(1)
except ValueError as e:
print("Can not parse config file: %s" % str(e))
exit(1)
else:
define_subparsers(parser)
params, other_params = parser.parse_known_args()
if params.action == 'listen':
config['action'] = 'listen'
config['interfaces'] = {}
config['interfaces'][params.interface] = params.vlan_list
config['cookie'] = params.cookie
config['ready_address'] = params.ready_address
config['ready_port'] = params.ready_port
if params.dump_file:
config['dump_file'] = params.dump_file
else:
config['dump_file'] = "/var/tmp/net-probe-dump-%s" %\
config['interface']
elif params.action == 'generate':
config['action'] = 'generate'
config['interfaces'] = {}
config['interfaces'][params.interface] = params.vlan_list
config['uid'] = params.uid
config['cookie'] = params.cookie
actor = ActorFabric.getInstance(config)
actor.run()
if __name__ == "__main__":
main()

View File

@ -0,0 +1,13 @@
# Copyright 2014 Mirantis, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may 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.

View File

@ -0,0 +1,137 @@
# Copyright 2014 Mirantis, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may 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.
import json
import multiprocessing
import os
import signal
import socket
import time
import unittest
import pcap
from scapy import all as scapy
from net_check import api
class TestNetCheckListener(unittest.TestCase):
def setUp(self):
directory_path = os.path.dirname(__file__)
self.scapy_data = scapy.rdpcap(os.path.join(directory_path,
'vlan.pcap'))
self.config = {
"src": "1.0.0.0", "ready_port": 31338,
"ready_address": "localhost", "dst": "1.0.0.0",
"interfaces": {"eth0": "0,100,100,101,102,103,104,105,106,107"},
"action": "listen",
"cookie": "Nailgun:", "dport": 31337, "sport": 31337,
"src_mac": None, "dump_file": "/var/tmp/net-probe-dump"
}
self.start_socket()
listener = api.Listener(self.config)
self.listener = multiprocessing.Process(target=listener.run)
self.listener.start()
def start_socket(self):
self.ready_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.ready_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.ready_socket.bind((self.config['ready_address'], 0))
self.config['ready_port'] = self.ready_socket.getsockname()[1]
self.ready_socket.listen(1)
self.ready_socket.settimeout(5)
def send_packets(self):
for p in self.scapy_data:
scapy.sendp(p, iface='eth0')
def test_listener(self):
connection, address = self.ready_socket.accept()
request = connection.recv(1024)
self.assertEqual('READY', request.decode())
connection.close()
self.ready_socket.shutdown(socket.SHUT_RDWR)
self.ready_socket.close()
self.send_packets()
os.kill(self.listener.pid, signal.SIGINT)
self.listener.join()
with open(self.config['dump_file'], 'r') as f:
data = json.loads(f.read())
self.assertEqual(data, {u'eth0': {
u'102': {u'1': [u'eth0'], u'2': [u'eth0']},
u'103': {u'1': [u'eth0'], u'2': [u'eth0']},
u'100': {u'1': [u'eth0'], u'2': [u'eth0']},
u'101': {u'1': [u'eth0'], u'2': [u'eth0']},
u'106': {u'1': [u'eth0'], u'2': [u'eth0']},
u'107': {u'1': [u'eth0'], u'2': [u'eth0']},
u'104': {u'1': [u'eth0'], u'2': [u'eth0']},
u'105': {u'1': [u'eth0'], u'2': [u'eth0']}}})
def tearDown(self):
self.ready_socket.close()
self.listener.terminate()
if os.path.exists(self.config['dump_file']):
os.unlink(self.config['dump_file'])
class TestNetCheckSender(unittest.TestCase):
def setUp(self):
directory_path = os.path.dirname(__file__)
self.scapy_data = scapy.rdpcap(os.path.join(directory_path,
'vlan.pcap'))
self.config = {
"src": "1.0.0.0", "ready_port": 31338,
"ready_address": "localhost", "dst": "1.0.0.0",
"interfaces": {"eth0": "0,100,101,102,106,107,108"},
"action": "listen",
"cookie": "Nailgun:", "dport": 31337, "sport": 31337,
"src_mac": None,
"dump_file": "/var/tmp/net-probe-dump",
"uid": "2"
}
def start_pcap_listener(self):
self.pcap_listener = pcap.pcap('eth0')
filter_string = 'udp and dst port {0}'.format(self.config['dport'])
self.pcap_listener.setfilter(filter_string)
def start_sender(self):
sender = api.Sender(self.config)
self.sender = multiprocessing.Process(target=sender.run)
self.sender.start()
@property
def received_vlans(self):
results = set()
for pkt in self.pcap_listener.readpkts():
ether = scapy.Ether(pkt[1])
if scapy.Dot1Q in ether:
vlan = str(ether[scapy.Dot1Q].vlan)
else:
vlan = '0'
results.update([vlan])
return results
def test_sender(self):
self.start_pcap_listener()
self.start_sender()
time.sleep(3)
self.sender.join()
expected_vlans = set(self.config['interfaces']['eth0'].split(','))
self.assertEqual(expected_vlans, self.received_vlans)

Binary file not shown.

41
network_checker/setup.py Normal file
View File

@ -0,0 +1,41 @@
# Copyright 2014 Mirantis, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may 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.
import setuptools
setuptools.setup(
name="nailgun-net-check",
version='0.1',
author="Mirantis Inc",
classifiers=[
"License :: OSI Approved :: Apache 2.0",
"Development Status :: 5 - Production/Stable",
"Environment :: Console",
"Operating System :: MacOS :: MacOS X",
"Operating System :: POSIX",
"Programming Language :: Python",
"Topic :: Software Development :: Testing"
],
install_requires=[
'argparse'
],
include_package_data=True,
packages=['net_check'],
entry_points={
'console_scripts': [
'net_probe.py = net_check.api:main'
],
},
)