Refactor dhcp code, flake8 and pep8 fixes
This commit is contained in:
parent
146d8412e2
commit
c8de4de836
|
@ -23,5 +23,4 @@ lock
|
||||||
.idea
|
.idea
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
Nailgun.egg-info
|
*.egg-info
|
||||||
Shotgun.egg-info
|
|
||||||
|
|
|
@ -1 +1,13 @@
|
||||||
__author__ = 'dshulyak'
|
# Copyright 2013 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.
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
#!/usr/bin/python
|
|
||||||
# Copyright 2013 Mirantis, Inc.
|
# Copyright 2013 Mirantis, Inc.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
@ -13,13 +12,11 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from scapy.all import *
|
|
||||||
import itertools
|
import itertools
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
import functools
|
|
||||||
import subprocess
|
from scapy import all as scapy
|
||||||
from dhcp_checker import utils
|
from dhcp_checker import utils
|
||||||
from dhcp_checker import vlans_utils
|
|
||||||
|
|
||||||
|
|
||||||
@utils.multiproc_map
|
@utils.multiproc_map
|
||||||
|
@ -31,24 +28,25 @@ def check_dhcp_on_eth(iface, timeout):
|
||||||
>>> check_dhcp_on_eth('eth1')
|
>>> check_dhcp_on_eth('eth1')
|
||||||
"""
|
"""
|
||||||
|
|
||||||
conf.iface = iface
|
scapy.conf.iface = iface
|
||||||
|
|
||||||
conf.checkIPaddr = False
|
scapy.conf.checkIPaddr = False
|
||||||
dhcp_options = [("message-type", "discover"),
|
dhcp_options = [("message-type", "discover"),
|
||||||
("param_req_list", utils.format_options([1, 2, 3, 4, 5, 6,
|
("param_req_list", utils.format_options(
|
||||||
11, 12, 13, 15, 16, 17, 18, 22, 23,
|
[1, 2, 3, 4, 5, 6,
|
||||||
28, 40, 41, 42, 43, 50, 51, 54, 58, 59, 60, 66, 67])),
|
11, 12, 13, 15, 16, 17, 18, 22, 23,
|
||||||
|
28, 40, 41, 42, 43, 50, 51, 54, 58, 59, 60, 66, 67])),
|
||||||
"end"]
|
"end"]
|
||||||
|
|
||||||
fam, hw = get_if_raw_hwaddr(iface)
|
fam, hw = scapy.get_if_raw_hwaddr(iface)
|
||||||
dhcp_discover = (
|
dhcp_discover = (
|
||||||
Ether(src=hw, dst="ff:ff:ff:ff:ff:ff") /
|
scapy.Ether(src=hw, dst="ff:ff:ff:ff:ff:ff") /
|
||||||
IP(src="0.0.0.0", dst="255.255.255.255") /
|
scapy.IP(src="0.0.0.0", dst="255.255.255.255") /
|
||||||
UDP(sport=68, dport=67) /
|
scapy.UDP(sport=68, dport=67) /
|
||||||
BOOTP(chaddr=hw) /
|
scapy.BOOTP(chaddr=hw) /
|
||||||
DHCP(options=dhcp_options))
|
scapy.DHCP(options=dhcp_options))
|
||||||
ans, unans = srp(dhcp_discover, multi=True,
|
ans, unans = scapy.srp(dhcp_discover, multi=True,
|
||||||
nofilter=1, timeout=timeout, verbose=0)
|
nofilter=1, timeout=timeout, verbose=0)
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
|
||||||
|
@ -64,8 +62,8 @@ def check_dhcp(ifaces, timeout=5, repeat=2):
|
||||||
if not ifaces_filtered:
|
if not ifaces_filtered:
|
||||||
raise EnvironmentError("No valid interfaces provided.")
|
raise EnvironmentError("No valid interfaces provided.")
|
||||||
pool = multiprocessing.Pool(len(ifaces_filtered)*repeat)
|
pool = multiprocessing.Pool(len(ifaces_filtered)*repeat)
|
||||||
return itertools.chain(*pool.map(check_dhcp_on_eth,
|
return itertools.chain(*pool.map(check_dhcp_on_eth, (
|
||||||
((iface, timeout) for iface in ifaces_filtered*repeat)))
|
(iface, timeout) for iface in ifaces_filtered*repeat)))
|
||||||
|
|
||||||
|
|
||||||
def check_dhcp_with_vlans(config, timeout=5, repeat=2):
|
def check_dhcp_with_vlans(config, timeout=5, repeat=2):
|
||||||
|
@ -74,7 +72,7 @@ def check_dhcp_with_vlans(config, timeout=5, repeat=2):
|
||||||
@ifaces - string : eth0, eth1
|
@ifaces - string : eth0, eth1
|
||||||
@vlans - iterable (100, 101, 102)
|
@vlans - iterable (100, 101, 102)
|
||||||
"""
|
"""
|
||||||
with vlans_utils.VlansActor(config) as vifaces:
|
with utils.VlansContext(config) as vifaces:
|
||||||
return check_dhcp(list(vifaces), timeout=timeout, repeat=repeat)
|
return check_dhcp(list(vifaces), timeout=timeout, repeat=repeat)
|
||||||
|
|
||||||
|
|
||||||
|
@ -85,22 +83,23 @@ def check_dhcp_request(iface, server, range_start, range_end, timeout=5):
|
||||||
>>> check_dhcp_request('eth1','10.10.0.5','10.10.0.10','10.10.0.15')
|
>>> check_dhcp_request('eth1','10.10.0.5','10.10.0.10','10.10.0.15')
|
||||||
"""
|
"""
|
||||||
|
|
||||||
conf.iface = iface
|
scapy.conf.iface = iface
|
||||||
|
|
||||||
conf.checkIPaddr = False
|
scapy.conf.checkIPaddr = False
|
||||||
|
|
||||||
fam, hw = get_if_raw_hwaddr(iface)
|
fam, hw = scapy.get_if_raw_hwaddr(iface)
|
||||||
|
|
||||||
ip_address = next(utils.pick_ip(range_start, range_end))
|
ip_address = next(utils.pick_ip(range_start, range_end))
|
||||||
|
|
||||||
# note lxc dhcp server does not respond to unicast
|
# note lxc dhcp server does not respond to unicast
|
||||||
dhcp_request = (Ether(src=hw, dst="ff:ff:ff:ff:ff:ff") /
|
dhcp_request = (scapy.Ether(src=hw, dst="ff:ff:ff:ff:ff:ff") /
|
||||||
IP(src="0.0.0.0", dst="255.255.255.255") /
|
scapy.IP(src="0.0.0.0", dst="255.255.255.255") /
|
||||||
UDP(sport=68, dport=67) /
|
scapy.UDP(sport=68, dport=67) /
|
||||||
BOOTP(chaddr=hw) /
|
scapy.BOOTP(chaddr=hw) /
|
||||||
DHCP(options=[("message-type", "request"),
|
scapy.DHCP(options=[("message-type", "request"),
|
||||||
("server_id", server),
|
("server_id", server),
|
||||||
("requested_addr", ip_address), "end"]))
|
("requested_addr", ip_address),
|
||||||
ans, unans = srp(dhcp_request, nofilter=1, multi=True,
|
"end"]))
|
||||||
timeout=timeout, verbose=0)
|
ans, unans = scapy.srp(dhcp_request, nofilter=1, multi=True,
|
||||||
|
timeout=timeout, verbose=0)
|
||||||
return ans
|
return ans
|
||||||
|
|
|
@ -12,11 +12,11 @@
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
import json
|
||||||
|
|
||||||
from cliff import lister
|
from cliff import lister
|
||||||
from cliff import command
|
from cliff import command
|
||||||
from dhcp_checker import api
|
from dhcp_checker import api
|
||||||
from itertools import chain
|
|
||||||
import json
|
|
||||||
|
|
||||||
|
|
||||||
class BaseCommand(command.Command):
|
class BaseCommand(command.Command):
|
||||||
|
@ -25,9 +25,9 @@ class BaseCommand(command.Command):
|
||||||
def get_parser(self, prog_name):
|
def get_parser(self, prog_name):
|
||||||
parser = super(BaseCommand, self).get_parser(prog_name)
|
parser = super(BaseCommand, self).get_parser(prog_name)
|
||||||
parser.add_argument('--timeout', default=5, type=int,
|
parser.add_argument('--timeout', default=5, type=int,
|
||||||
help="Provide timeout for each network request")
|
help="Provide timeout for each network request")
|
||||||
parser.add_argument('--repeat', default=2, type=int,
|
parser.add_argument('--repeat', default=2, type=int,
|
||||||
help="Provide number of repeats for request")
|
help="Provide number of repeats for request")
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
|
||||||
|
@ -44,8 +44,8 @@ class ListDhcpServers(lister.Lister, BaseCommand):
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
res = api.check_dhcp(parsed_args.ifaces,
|
res = api.check_dhcp(parsed_args.ifaces,
|
||||||
timeout=parsed_args.timeout,
|
timeout=parsed_args.timeout,
|
||||||
repeat=parsed_args.repeat)
|
repeat=parsed_args.repeat)
|
||||||
first = res.next()
|
first = res.next()
|
||||||
columns = first.keys()
|
columns = first.keys()
|
||||||
return columns, [first.values()] + [item.values() for item in res]
|
return columns, [first.values()] + [item.values() for item in res]
|
||||||
|
@ -86,13 +86,13 @@ class DhcpWithVlansCheck(lister.Lister, BaseCommand):
|
||||||
def get_parser(self, prog_name):
|
def get_parser(self, prog_name):
|
||||||
parser = super(DhcpWithVlansCheck, self).get_parser(prog_name)
|
parser = super(DhcpWithVlansCheck, self).get_parser(prog_name)
|
||||||
parser.add_argument('config',
|
parser.add_argument('config',
|
||||||
help='Ethernet interface name')
|
help='Ethernet interface name')
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
res = api.check_dhcp_with_vlans(json.loads(parsed_args.config),
|
res = api.check_dhcp_with_vlans(json.loads(parsed_args.config),
|
||||||
timeout=parsed_args.timeout,
|
timeout=parsed_args.timeout,
|
||||||
repeat=parsed_args.repeat)
|
repeat=parsed_args.repeat)
|
||||||
first = res.next()
|
first = res.next()
|
||||||
columns = first.keys()
|
columns = first.keys()
|
||||||
return columns, [first.values()] + [item.values() for item in res]
|
return columns, [first.values()] + [item.values() for item in res]
|
||||||
|
|
|
@ -11,10 +11,12 @@
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
from scapy.all import *
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import functools
|
import functools
|
||||||
import re
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from scapy import all as scapy
|
||||||
|
|
||||||
|
|
||||||
def command_util(*command):
|
def command_util(*command):
|
||||||
|
@ -39,7 +41,7 @@ def check_network_up(iface):
|
||||||
def check_iface_exist(iface):
|
def check_iface_exist(iface):
|
||||||
"""Check provided interface exists
|
"""Check provided interface exists
|
||||||
"""
|
"""
|
||||||
return not command_util("ip","link", "show", iface).stderr.read()
|
return not command_util("ip", "link", "show", iface).stderr.read()
|
||||||
|
|
||||||
|
|
||||||
def filtered_ifaces(ifaces):
|
def filtered_ifaces(ifaces):
|
||||||
|
@ -48,7 +50,8 @@ def filtered_ifaces(ifaces):
|
||||||
sys.stderr.write('Iface {0} does not exist.'.format(iface))
|
sys.stderr.write('Iface {0} does not exist.'.format(iface))
|
||||||
else:
|
else:
|
||||||
if not check_network_up(iface):
|
if not check_network_up(iface):
|
||||||
sys.stderr.write('Network for iface {0} is down.'.format(iface))
|
sys.stderr.write('Network for iface {0} is down.'.format(
|
||||||
|
iface))
|
||||||
else:
|
else:
|
||||||
yield iface
|
yield iface
|
||||||
|
|
||||||
|
@ -104,18 +107,18 @@ def single_format(func):
|
||||||
iface = args[0]
|
iface = args[0]
|
||||||
ans = func(*args, **kwargs)
|
ans = func(*args, **kwargs)
|
||||||
columns = ('iface', 'mac', 'server_ip', 'server_id', 'gateway',
|
columns = ('iface', 'mac', 'server_ip', 'server_id', 'gateway',
|
||||||
'dport', 'message', 'yiaddr')
|
'dport', 'message', 'yiaddr')
|
||||||
data = []
|
data = []
|
||||||
#scapy stores all sequence of requests
|
#scapy stores all sequence of requests
|
||||||
#so ans[0][1] would be response to first request
|
#so ans[0][1] would be response to first request
|
||||||
for response in ans:
|
for response in ans:
|
||||||
dhcp_options = dict(_dhcp_options(response[1][DHCP].options))
|
dhcp_options = dict(_dhcp_options(response[1][scapy.DHCP].options))
|
||||||
results = (
|
results = (
|
||||||
iface, response[1][Ether].src, response[1][IP].src,
|
iface, response[1][scapy.Ether].src, response[1][scapy.IP].src,
|
||||||
dhcp_options['server_id'], response[1][BOOTP].giaddr,
|
dhcp_options['server_id'], response[1][scapy.BOOTP].giaddr,
|
||||||
response[1][UDP].sport,
|
response[1][scapy.UDP].sport,
|
||||||
DHCPTypes[dhcp_options['message-type']],
|
scapy.DHCPTypes[dhcp_options['message-type']],
|
||||||
response[1][BOOTP].yiaddr)
|
response[1][scapy.BOOTP].yiaddr)
|
||||||
data.append(dict(zip(columns, results)))
|
data.append(dict(zip(columns, results)))
|
||||||
return data
|
return data
|
||||||
return formatter
|
return formatter
|
||||||
|
@ -138,3 +141,24 @@ def filter_duplicated_results(func):
|
||||||
resp = func(*args, **kwargs)
|
resp = func(*args, **kwargs)
|
||||||
return (dict(t) for t in set([tuple(d.items()) for d in resp]))
|
return (dict(t) for t in set([tuple(d.items()) for d in resp]))
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
class VlansContext(object):
|
||||||
|
"""Contains all logic to manage vlans
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, config):
|
||||||
|
"""
|
||||||
|
@config - list or tuple of (iface, vlan) pairs
|
||||||
|
"""
|
||||||
|
self.config = config
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
for iface, vlans in self.config.iteritems():
|
||||||
|
yield str(iface)
|
||||||
|
for vlan in vlans:
|
||||||
|
if vlan > 0:
|
||||||
|
yield '{0}.{1}'.format(iface, vlan)
|
||||||
|
|
||||||
|
def __exit__(self, type, value, trace):
|
||||||
|
pass
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
# Copyright 2013 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.
|
|
||||||
|
|
||||||
|
|
||||||
class VlansActor(object):
|
|
||||||
"""Contains all logic to manage vlans
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, config):
|
|
||||||
"""
|
|
||||||
@config - list or tuple of (iface, vlan) pairs
|
|
||||||
"""
|
|
||||||
self.config = config
|
|
||||||
|
|
||||||
def __enter__(self):
|
|
||||||
for iface, vlans in self.config.iteritems():
|
|
||||||
yield str(iface)
|
|
||||||
for vlan in vlans:
|
|
||||||
if vlan > 0:
|
|
||||||
yield '{0}.{1}'.format(iface, vlan)
|
|
||||||
|
|
||||||
def __exit__(self, type, value, trace):
|
|
||||||
pass
|
|
|
@ -13,13 +13,8 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import subprocess
|
|
||||||
import json
|
|
||||||
import unittest
|
import unittest
|
||||||
import time
|
|
||||||
|
|
||||||
from dhcp_checker import utils
|
|
||||||
from dhcp_checker import vlans_utils
|
|
||||||
from dhcp_checker import api
|
from dhcp_checker import api
|
||||||
|
|
||||||
|
|
||||||
|
@ -39,49 +34,3 @@ class TestDhcpServers(unittest.TestCase):
|
||||||
response = api.check_dhcp_on_eth('eth2', 5)
|
response = api.check_dhcp_on_eth('eth2', 5)
|
||||||
self.assertEqual(len(response), 1)
|
self.assertEqual(len(response), 1)
|
||||||
self.assertEqual(response[0]['server_ip'], '10.10.0.10')
|
self.assertEqual(response[0]['server_ip'], '10.10.0.10')
|
||||||
|
|
||||||
|
|
||||||
class VlanCreationTestCase(unittest.TestCase):
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def setUpClass(cls):
|
|
||||||
cls.vlan = vlans_utils.Vlan('eth0', '100')
|
|
||||||
|
|
||||||
def test_vlan_creation(self):
|
|
||||||
self.vlan.up()
|
|
||||||
time.sleep(5)
|
|
||||||
self.assertTrue(self.vlan.state)
|
|
||||||
|
|
||||||
def test_vlan_deletion(self):
|
|
||||||
self.assertTrue(self.vlan.state)
|
|
||||||
self.vlan.down()
|
|
||||||
time.sleep(5)
|
|
||||||
|
|
||||||
|
|
||||||
class VlanCreationWithExistingTestCase(unittest.TestCase):
|
|
||||||
|
|
||||||
def test_check_vlan_down_status(self):
|
|
||||||
self.vlan_down = vlans_utils.Vlan('eth0', '110')
|
|
||||||
self.vlan_down.create()
|
|
||||||
time.sleep(5)
|
|
||||||
self.assertEqual(self.vlan_down.state, 'DOWN')
|
|
||||||
self.vlan_down.down()
|
|
||||||
time.sleep(5)
|
|
||||||
|
|
||||||
def test_repeat_created_vlan(self):
|
|
||||||
self.vlan_up = vlans_utils.Vlan('eth0', '112')
|
|
||||||
self.vlan_up.up()
|
|
||||||
time.sleep(5)
|
|
||||||
self.assertEqual(self.vlan_up.state, 'UP')
|
|
||||||
self.vlan_up.down()
|
|
||||||
time.sleep(5)
|
|
||||||
|
|
||||||
|
|
||||||
class WithVlanDecoratorTestCase(unittest.TestCase):
|
|
||||||
|
|
||||||
|
|
||||||
def test_with_vlan_enter(self):
|
|
||||||
with vlans_utils.VlansContext('eth0', ('101','102','103'), delete=True) as vlan_list:
|
|
||||||
time.sleep(5)
|
|
||||||
for v in vlan_list:
|
|
||||||
self.assertEqual('UP', v.state)
|
|
||||||
|
|
Loading…
Reference in New Issue