From 13737fc25f7fc573595f8712f09b7c0441a0676d Mon Sep 17 00:00:00 2001 From: Brad Cowie Date: Mon, 9 Dec 2019 12:48:13 +1300 Subject: [PATCH] Revert "Refactor Chewie for Better SoC" --- .pylintrc | 4 - chewie/chewie.py | 302 +++++++++++++++++++++------------ chewie/managed_port.py | 133 --------------- chewie/radius_lifecycle.py | 4 +- chewie/utils.py | 5 - test/unit/test_chewie.py | 27 +-- test/unit/test_chewie_mocks.py | 53 +++--- test/unit/test_managed_port.py | 51 ------ 8 files changed, 225 insertions(+), 354 deletions(-) delete mode 100644 chewie/managed_port.py delete mode 100644 test/unit/test_managed_port.py diff --git a/.pylintrc b/.pylintrc index ff5e78fd..4df3b7ba 100644 --- a/.pylintrc +++ b/.pylintrc @@ -1,9 +1,5 @@ [MASTER] jobs=4 -[MESSAGES CONTROL] -disable=no-absolute-import - - [TYPECHECK] generated-members=socket.* diff --git a/chewie/chewie.py b/chewie/chewie.py index 04bcf76a..eb03b9bc 100644 --- a/chewie/chewie.py +++ b/chewie/chewie.py @@ -1,31 +1,48 @@ """ Entry point for 802.1X speaker. """ +import random from eventlet import sleep, GreenPool from eventlet.queue import Queue from chewie import timer_scheduler +from chewie.eap import Eap from chewie.nfv_sockets import EapSocket, MabSocket from chewie.ethernet_packet import EthernetPacket -from chewie.event import EventMessageReceived, EventPreemptiveEAPResponseMessageReceived +from chewie.event import EventMessageReceived, EventPortStatusChange, \ + EventPreemptiveEAPResponseMessageReceived from chewie.mac_address import MacAddress -from chewie.message_parser import MessageParser, MessagePacker +from chewie.message_parser import MessageParser, MessagePacker, IdentityMessage from chewie.radius_lifecycle import RadiusLifecycle from chewie.radius_socket import RadiusSocket from chewie.state_machines.eap_state_machine import FullEAPStateMachine from chewie.state_machines.mab_state_machine import MacAuthenticationBypassStateMachine -from chewie.utils import get_logger, MessageParseError -from chewie.managed_port import ManagedPort +from chewie.utils import get_logger, MessageParseError, EapQueueMessage +def unpack_byte_string(byte_string): + """unpacks a byte string""" + return "".join("%02x" % x for x in byte_string) + + +def get_random_id(): # pylint: disable=missing-docstring + return random.randint(0, 200) + +# TODO set unneeded public methods to private +# pylint: disable=too-many-instance-attributes +# pylint: disable=too-many-public-methods class Chewie: """Facilitates EAP supplicant and RADIUS server communication""" - _RADIUS_UDP_PORT = 1812 + RADIUS_UDP_PORT = 1812 PAE_GROUP_ADDRESS = MacAddress.from_string("01:80:C2:00:00:03") + DEFAULT_PORT_UP_IDENTITY_REQUEST_WAIT_PERIOD = 20 + DEFAULT_PREEMPTIVE_IDENTITY_REQUEST_INTERVAL = 60 + # pylint: disable=too-many-arguments def __init__(self, interface_name, logger=None, auth_handler=None, failure_handler=None, logoff_handler=None, radius_server_ip=None, radius_server_port=None, radius_server_secret=None, chewie_id=None): + self.interface_name = interface_name self.log_name = Chewie.__name__ if logger: @@ -38,7 +55,7 @@ def __init__(self, interface_name, logger=None, self.radius_server_ip = radius_server_ip self.radius_secret = radius_server_secret - self.radius_server_port = self._RADIUS_UDP_PORT + self.radius_server_port = self.RADIUS_UDP_PORT if radius_server_port: self.radius_server_port = radius_server_port self.radius_listen_ip = "0.0.0.0" @@ -49,30 +66,35 @@ def __init__(self, interface_name, logger=None, if chewie_id: self.chewie_id = chewie_id + self.state_machines = {} # port_id_str: { mac : state_machine} self.port_to_eapol_id = {} # port_id: last ID used in preemptive identity request. # TODO for port_to_eapol_id - may want to set ID to null (-1...) if sent from the # state machine. - self._managed_ports = {} + self.port_status = {} # port_id: status (true=up, false=down) + self.port_to_identity_job = {} # port_id: timerJob + self.eap_output_messages = Queue() self.radius_output_messages = Queue() self.radius_lifecycle = RadiusLifecycle(self.radius_secret, self.chewie_id, self.logger) self.timer_scheduler = timer_scheduler.TimerScheduler(self.logger) - self._eap_socket = None - self._mab_socket = None - self._radius_socket = None - + self.eap_socket = None + self.mab_socket = None self.pool = None + self.eventlets = None + self.radius_socket = None + self.interface_index = None + self.eventlets = [] def run(self): """setup chewie and start socket eventlet threads""" self.logger.info("Starting") - self._setup_eap_socket() - self._setup_mab_socket() - self._setup_radius_socket() - self._start_threads_and_wait() + self.setup_eap_socket() + self.setup_mab_socket() + self.setup_radius_socket() + self.start_threads_and_wait() def running(self): # pylint: disable=no-self-use """Used to nicely exit the event loops""" @@ -83,23 +105,23 @@ def shutdown(self): for eventlet in self.eventlets: eventlet.kill() - def _start_threads_and_wait(self): + def start_threads_and_wait(self): """Start the thread and wait until they complete (hopefully never)""" self.pool = GreenPool() - self.eventlets.append(self.pool.spawn(self._send_eap_messages)) - self.eventlets.append(self.pool.spawn(self._receive_eap_messages)) - self.eventlets.append(self.pool.spawn(self._receive_mab_messages)) + self.eventlets.append(self.pool.spawn(self.send_eap_messages)) + self.eventlets.append(self.pool.spawn(self.receive_eap_messages)) + self.eventlets.append(self.pool.spawn(self.receive_mab_messages)) - self.eventlets.append(self.pool.spawn(self._send_radius_messages)) - self.eventlets.append(self.pool.spawn(self._receive_radius_messages)) + self.eventlets.append(self.pool.spawn(self.send_radius_messages)) + self.eventlets.append(self.pool.spawn(self.receive_radius_messages)) self.eventlets.append(self.pool.spawn(self.timer_scheduler.run)) self.pool.waitall() - def _auth_success(self, src_mac, port_id, period, - *args, **kwargs): # pylint: disable=unused-variable + def auth_success(self, src_mac, port_id, period, + *args, **kwargs): # pylint: disable=unused-variable """authentication shim between faucet and chewie Args: src_mac (MacAddress): the mac of the successful supplicant @@ -110,10 +132,12 @@ def _auth_success(self, src_mac, port_id, period, if self.auth_handler: self.auth_handler(src_mac, port_id, *args, **kwargs) - managed_port = self._get_managed_port(port_id) - managed_port.start_port_session(period, src_mac) + self.port_to_identity_job[port_id] = self.timer_scheduler.call_later( + period, + self.reauth_port, src_mac, + port_id) - def _auth_failure(self, src_mac, port_id): + def auth_failure(self, src_mac, port_id): """failure shim between faucet and chewie Args: src_mac (MacAddress): the mac of the failed supplicant @@ -121,9 +145,8 @@ def _auth_failure(self, src_mac, port_id): the failure is on""" if self.failure_handler: self.failure_handler(src_mac, port_id) - # TODO Need to stop sessions on Failure - def _auth_logoff(self, src_mac, port_id): + def auth_logoff(self, src_mac, port_id): """logoff shim between faucet and chewie Args: src_mac (MacAddress): the mac of the logoff supplicant @@ -131,18 +154,6 @@ def _auth_logoff(self, src_mac, port_id): the logoff is on""" if self.logoff_handler: self.logoff_handler(src_mac, port_id) - # TODO Need to stop sessions on Logoff - - def _get_managed_port(self, port_id): - port_id = str(port_id) - if port_id in self._managed_ports: - return self._managed_ports[port_id] - - managed_port = ManagedPort(port_id, self.logger.name, self.timer_scheduler, - self.eap_output_messages, - self.radius_output_messages) - self._managed_ports[port_id] = managed_port - return managed_port def port_down(self, port_id): """ @@ -152,10 +163,16 @@ def port_down(self, port_id): """ # all chewie needs to do is change its internal state. # faucet will remove the acls by itself. - self.logger.info("port %s down", port_id) - managed_port = self._get_managed_port(port_id) - managed_port.status = False - managed_port.stop_identity_requests() + self.set_port_status(port_id, False) + + job = self.port_to_identity_job.get(port_id, None) + + if port_id in self.state_machines: + del self.state_machines[port_id] + + if job: + job.cancel() + self.port_to_eapol_id.pop(port_id, None) def port_up(self, port_id): """ @@ -164,36 +181,119 @@ def port_up(self, port_id): port_id (str): id of port. """ self.logger.info("port %s up", port_id) - managed_port = self._get_managed_port(port_id) - managed_port.status = True - managed_port.start_identity_requests() + self.set_port_status(port_id, True) + + self.port_to_identity_job[port_id] = self.timer_scheduler.call_later( + self.DEFAULT_PORT_UP_IDENTITY_REQUEST_WAIT_PERIOD, + self.send_preemptive_identity_request_if_no_active_on_port, + port_id) + + def send_preemptive_identity_request_if_no_active_on_port(self, port_id): + """ + If there is no active (in progress, or in state success(2)) supplicant send out the + preemptive identity request message. + Args: + port_id (str): + """ + self.logger.debug("thinking about executing timer preemptive on port %s", port_id) + # schedule next request. + self.port_to_identity_job[port_id] = self.timer_scheduler.call_later( + self.DEFAULT_PREEMPTIVE_IDENTITY_REQUEST_INTERVAL, + self.send_preemptive_identity_request_if_no_active_on_port, + port_id) + if not self.port_status.get(port_id, False): + self.logger.debug('cant send output on port %s is down', port_id) + return + + state_machines = self.state_machines.get(port_id, {}) + + # pylint: disable=invalid-name + for sm in state_machines.values(): + if sm.is_in_progress() or sm.is_success(): + self.logger.debug('port is active not sending on port %s', port_id) + break + else: + self.logger.debug("executing timer premptive on port %s", port_id) + self.send_preemptive_identity_request(port_id) + + def send_preemptive_identity_request(self, port_id): + """ + Message (EAP Identity Request) that notifies supplicant that port is using 802.1X + Args: + port_id (str): - def _setup_eap_socket(self): + """ + _id = get_random_id() + data = IdentityMessage(self.PAE_GROUP_ADDRESS, _id, Eap.REQUEST, "") + self.port_to_eapol_id[port_id] = _id + self.eap_output_messages.put_nowait( + EapQueueMessage(data, self.PAE_GROUP_ADDRESS, MacAddress.from_string(port_id))) + self.logger.info("sending premptive on port %s", port_id) + + def reauth_port(self, src_mac, port_id): + """ + Send an Identity Request to src_mac, on port_id. prompting + the supplicant to re authenticate. + Args: + src_mac (MacAddress): + port_id (str): + """ + state_machine = self.state_machines.get(port_id, {}).get(str(src_mac), None) + + if state_machine and state_machine.is_success(): + self.logger.info('reauthenticating src_mac: %s on port: %s', src_mac, port_id) + self.send_preemptive_identity_request(port_id) + elif state_machine is None: + self.logger.debug('not reauthing. state machine on port: %s, mac: %s is none', port_id, + src_mac) + else: + self.logger.debug("not reauthing, authentication is not in success(2) (state: %s)'", + state_machine.state) + + def set_port_status(self, port_id, status): + """ + Send status of a port at port_id + Args: + port_id (): + status (): + """ + port_id_str = str(port_id) + + self.port_status[port_id] = status + + if port_id_str not in self.state_machines: + self.state_machines[port_id_str] = {} + + for _, state_machine in self.state_machines[port_id_str].items(): + event = EventPortStatusChange(status) + state_machine.event(event) + + def setup_eap_socket(self): """Setup EAP socket""" log_prefix = "%s.EapSocket" % self.logger.name - self._eap_socket = EapSocket(self.interface_name, log_prefix) - self._eap_socket.setup() + self.eap_socket = EapSocket(self.interface_name, log_prefix) + self.eap_socket.setup() - def _setup_mab_socket(self): + def setup_mab_socket(self): """Setup Mab socket""" log_prefix = "%s.MabSocket" % self.logger.name - self._mab_socket = MabSocket(self.interface_name, log_prefix) - self._mab_socket.setup() + self.mab_socket = MabSocket(self.interface_name, log_prefix) + self.mab_socket.setup() - def _setup_radius_socket(self): + def setup_radius_socket(self): """Setup Radius socket""" log_prefix = "%s.RadiusSocket" % self.logger.name - self._radius_socket = RadiusSocket(self.radius_listen_ip, - self.radius_listen_port, - self.radius_server_ip, - self.radius_server_port, - log_prefix) - self._radius_socket.setup() + self.radius_socket = RadiusSocket(self.radius_listen_ip, + self.radius_listen_port, + self.radius_server_ip, + self.radius_server_port, + log_prefix) + self.radius_socket.setup() self.logger.info("Radius Listening on %s:%d", self.radius_listen_ip, self.radius_listen_port) - def _send_eap_messages(self): + def send_eap_messages(self): """Send EAP messages to Supplicant forever.""" while self.running(): sleep(0) @@ -202,11 +302,11 @@ def _send_eap_messages(self): eap_queue_message.message, str(eap_queue_message.port_mac), str(eap_queue_message.src_mac)) - self._eap_socket.send(MessagePacker.ethernet_pack(eap_queue_message.message, - eap_queue_message.port_mac, - eap_queue_message.src_mac)) + self.eap_socket.send(MessagePacker.ethernet_pack(eap_queue_message.message, + eap_queue_message.port_mac, + eap_queue_message.src_mac)) - def _send_eth_to_state_machine(self, packed_message): + def send_eth_to_state_machine(self, packed_message): """Send an ethernet frame to MAB State Machine""" ethernet_packet = EthernetPacket.parse(packed_message) port_id = ethernet_packet.dst_mac @@ -219,12 +319,12 @@ def _send_eth_to_state_machine(self, packed_message): state_machine.event(event) # NOTE: Should probably throttle packets in once one is received - def _receive_eap_messages(self): + def receive_eap_messages(self): """receive eap messages from supplicant forever.""" while self.running(): sleep(0) self.logger.info("waiting for eap.") - packed_message = self._eap_socket.receive() + packed_message = self.eap_socket.receive() self.logger.info("Received packed_message: %s", str(packed_message)) try: eap, dst_mac = MessageParser.ethernet_parse(packed_message) @@ -238,19 +338,19 @@ def _receive_eap_messages(self): continue self.logger.info("Received eap message: %s", str(eap)) - self._send_eap_to_state_machine(eap, dst_mac) + self.send_eap_to_state_machine(eap, dst_mac) - def _receive_mab_messages(self): + def receive_mab_messages(self): """Receive DHCP request for MAB.""" while self.running(): sleep(0) self.logger.info("waiting for MAB activity.") - packed_message = self._mab_socket.receive() + packed_message = self.mab_socket.receive() self.logger.info("Received DHCP packet for MAB. packed_message: %s", str(packed_message)) - self._send_eth_to_state_machine(packed_message) + self.send_eth_to_state_machine(packed_message) - def _send_eap_to_state_machine(self, eap, dst_mac): + def send_eap_to_state_machine(self, eap, dst_mac): """sends an eap message to the state machine""" self.logger.info("eap EAP(): %s", eap) message_id = getattr(eap, 'message_id', -1) @@ -267,21 +367,21 @@ def _send_eap_to_state_machine(self, eap, dst_mac): state_machine.event(event) - def _send_radius_messages(self): + def send_radius_messages(self): """send RADIUS messages to RADIUS Server forever.""" while self.running(): sleep(0) radius_output_bits = self.radius_output_messages.get() packed_message = self.radius_lifecycle.process_outbound(radius_output_bits) - self._radius_socket.send(packed_message) + self.radius_socket.send(packed_message) self.logger.info("sent radius message.") - def _receive_radius_messages(self): + def receive_radius_messages(self): """receive RADIUS messages from RADIUS server forever.""" while self.running(): sleep(0) self.logger.info("waiting for radius.") - packed_message = self._radius_socket.receive() + packed_message = self.radius_socket.receive() try: radius = MessageParser.radius_parse(packed_message, self.radius_secret, self.radius_lifecycle) @@ -294,15 +394,15 @@ def _receive_radius_messages(self): exception) continue self.logger.info("Received RADIUS message: %s", str(radius)) - self._send_radius_to_state_machine(radius) + self.send_radius_to_state_machine(radius) - def _send_radius_to_state_machine(self, radius): + def send_radius_to_state_machine(self, radius): """sends a radius message to the state machine""" event = self.radius_lifecycle.build_event_radius_message_received(radius) - state_machine = self._get_state_machine_from_radius_packet_id(radius.packet_id) + state_machine = self.get_state_machine_from_radius_packet_id(radius.packet_id) state_machine.event(event) - def _get_state_machine_from_radius_packet_id(self, packet_id): + def get_state_machine_from_radius_packet_id(self, packet_id): """Gets a FullEAPStateMachine from the RADIUS message packet_id Args: packet_id (int): id of the received RADIUS message @@ -312,7 +412,6 @@ def _get_state_machine_from_radius_packet_id(self, packet_id): return self.get_state_machine(**self.radius_lifecycle.packet_id_to_mac[packet_id]) # TODO change message_id functionality - # TODO Make Private def get_state_machine(self, src_mac, port_id, message_id=-1): """Gets or creates if it does not already exist an FullEAPStateMachine for the src_mac. Args: @@ -325,12 +424,13 @@ def get_state_machine(self, src_mac, port_id, message_id=-1): """ port_id_str = str(port_id) src_mac_str = str(src_mac) - - port_state_machines = self._get_managed_port(port_id).state_machines + port_state_machines = self.state_machines.get(port_id_str, None) + if port_state_machines is None: + self.state_machines[port_id_str] = {} self.logger.info("Port based state machines are as follows: %s", - port_state_machines) - state_machine = port_state_machines.get(src_mac_str, None) + self.state_machines[port_id_str]) + state_machine = self.state_machines[port_id_str].get(src_mac_str, None) if not state_machine and message_id == -2: # Do MAB @@ -339,10 +439,10 @@ def get_state_machine(self, src_mac, port_id, message_id=-1): state_machine = MacAuthenticationBypassStateMachine(self.radius_output_messages, src_mac, self.timer_scheduler, - self._auth_success, - self._auth_failure, + self.auth_success, + self.auth_failure, log_prefix) - port_state_machines[src_mac_str] = state_machine + self.state_machines[port_id_str][src_mac_str] = state_machine return state_machine if not state_machine: @@ -350,29 +450,11 @@ def get_state_machine(self, src_mac, port_id, message_id=-1): log_prefix = "%s.SM - port: %s, client: %s" % (self.logger.name, port_id_str, src_mac) state_machine = FullEAPStateMachine(self.eap_output_messages, self.radius_output_messages, src_mac, - self.timer_scheduler, self._auth_success, - self._auth_failure, self._auth_logoff, + self.timer_scheduler, self.auth_success, + self.auth_failure, self.auth_logoff, log_prefix) - port_state_machines[src_mac_str] = state_machine + self.state_machines[port_id_str][src_mac_str] = state_machine self.logger.debug("created new state machine for '%s' on port '%s'", src_mac_str, port_id_str) - return state_machine - - @property - def state_machines(self): - """state_machines property returns a list of all state machines managed by Chewie""" - state_machines = {} - for port in self._managed_ports.values(): - if port.state_machines: - state_machines[port.port_id] = port.state_machines - - return state_machines - @property - def clients(self): - """clients property returns a list of all clients managed by Chewie""" - clients = [] - for port in self._managed_ports.values(): - clients.extend(port.clients) - - return clients + return state_machine diff --git a/chewie/managed_port.py b/chewie/managed_port.py deleted file mode 100644 index f413aea3..00000000 --- a/chewie/managed_port.py +++ /dev/null @@ -1,133 +0,0 @@ -"""This module is used to represent a single 802.1x Port""" -from chewie.utils import get_logger, EapQueueMessage, get_random_id -from chewie.mac_address import MacAddress -from chewie.event import EventPortStatusChange -from chewie.message_parser import IdentityMessage -from chewie.eap import Eap - - -class ManagedPort: - """This class is used to represent a single 802.1x Port""" - DEFAULT_PORT_UP_IDENTITY_REQUEST_WAIT_PERIOD = 20 - DEFAULT_PREEMPTIVE_IDENTITY_REQUEST_INTERVAL = 60 - PAE_GROUP_ADDRESS = MacAddress.from_string("01:80:C2:00:00:03") - - def __init__(self, port_id, log_prefix, timer_scheduler, eap_output_messages, - radius_output_messages): - self.port_id = port_id - self.logger = get_logger(log_prefix) - self.supplicant_output_messages = eap_output_messages - self.radius_output_messages = radius_output_messages - - self.state_machines = {} # mac : state_machine - self.current_preemtive_eapol_id = None - self.port_status = False # port_id: status (true=up, false=down) - self.identity_job = None # timerJob - self.session_job = None # timerJob - self.timer_scheduler = timer_scheduler - - @property - def status(self): - """ - Returns the current status of the port. - True is up - False is down - """ - return self.port_status - - @status.setter - def status(self, value): - """ - Send status of a port at port_id - Args: - port_id (): - status (): - """ - self.port_status = value - - # Trigger Subscribers - for _, state_machine in self.state_machines.items(): - event = EventPortStatusChange(value) - state_machine.event(event) - - if not value: - self.state_machines.clear() - - @property - def clients(self): - """Returns a list of all managed clients that are attached to this port""" - return [(self.port_id, mac) for mac in self.state_machines.items()] - - def stop_identity_requests(self): - """Stop sending Preemptive Identitity Requests""" - if self.identity_job: - self.identity_job.cancel() - - self.current_preemtive_eapol_id = None - - def start_identity_requests(self): - """Start Sending Preemptive Identity Requests""" - self.identity_job = self.timer_scheduler.call_later( - self.DEFAULT_PORT_UP_IDENTITY_REQUEST_WAIT_PERIOD, - self.send_preemptive_identity_request) - - def send_preemptive_identity_request(self): - """ - If there is no active (in progress, or in state success(2)) supplicant send out the - preemptive identity request message. - """ - if not self.port_status: - self.logger.debug( - 'cant send output on port %s is down', self.port_id) - return - - self.logger.debug("Sending Identity Request on port %s", self.port_id) - # schedule next request. - self.identity_job = self.timer_scheduler.call_later( - self.DEFAULT_PREEMPTIVE_IDENTITY_REQUEST_INTERVAL, - self.send_preemptive_identity_request) - - self._send_identity_request() - - def _send_identity_request(self): - """ - Message (EAP Identity Request) that notifies supplicant that port is using 802.1X - Args: - port_id (str): - - """ - _id = get_random_id() - self.current_preemtive_eapol_id = _id - data = IdentityMessage(self.PAE_GROUP_ADDRESS, _id, Eap.REQUEST, "") - self.supplicant_output_messages.put_nowait( - EapQueueMessage(data, self.PAE_GROUP_ADDRESS, MacAddress.from_string(self.port_id))) - return _id - - def start_port_session(self, period, src_mac): - """Start a port session""" - self.session_job = self.timer_scheduler.call_later( - period, - self._reauth_port, src_mac) - - def _reauth_port(self, src_mac): - """ - Send an Identity Request to src_mac, on port_id. - prompting the supplicant to re authenticate. - Args: - src_mac (MacAddress): - port_id (str): - """ - state_machine = self.state_machines.get(str(src_mac), None) - - if state_machine and state_machine.is_success(): - self.logger.info( - 'reauthenticating src_mac: %s on port: %s', src_mac, self.port_id) - self.start_identity_requests() - - elif state_machine is None: - self.logger.debug('not reauthing. state machine on port: %s, mac: %s is none', - self.port_id, - src_mac) - else: - self.logger.debug("not reauthing, authentication is not in success(2) (state: %s)'", - state_machine.state) diff --git a/chewie/radius_lifecycle.py b/chewie/radius_lifecycle.py index f66ed052..092c9dba 100644 --- a/chewie/radius_lifecycle.py +++ b/chewie/radius_lifecycle.py @@ -33,7 +33,7 @@ def __init__(self, radius_secret, server_id, logger): self.packet_id_to_request_authenticator = {} def process_outbound(self, radius_output_bits): - """Placeholder method extracted from Chewie._send_radius_messages()""" + """Placeholder method extracted from Chewie.send_radius_messages()""" radius_payload = radius_output_bits.message src_mac = radius_output_bits.src_mac username = radius_output_bits.identity @@ -71,7 +71,7 @@ def build_event_radius_message_received(self, radius): return EventRadiusMessageReceived(radius, state, radius.attributes.to_dict()) def process_outbound_mab_request(self, radius_output_bits): - """Placeholder method extracted from Chewie._send_radius_messages()""" + """Placeholder method extracted from Chewie.send_radius_messages()""" src_mac = radius_output_bits.src_mac port_id = radius_output_bits.port_mac self.logger.info("Sending MAB to RADIUS: %s", src_mac) diff --git a/chewie/utils.py b/chewie/utils.py index 6f470d6e..1c3cdf8d 100644 --- a/chewie/utils.py +++ b/chewie/utils.py @@ -1,7 +1,6 @@ """Utility Functions""" import logging from collections import namedtuple # pytype: disable=pyi-error -import random def get_logger(logname): @@ -21,10 +20,6 @@ def wrapped(self, *args, **kwargs): return wrapped -def get_random_id(): # pylint: disable=missing-docstring - return random.randint(0, 200) - - class MessageParseError(Exception): """Error for when parsing cannot be successfully completed.""" pass diff --git a/test/unit/test_chewie.py b/test/unit/test_chewie.py index afe40a1d..0c3c48f0 100644 --- a/test/unit/test_chewie.py +++ b/test/unit/test_chewie.py @@ -14,7 +14,6 @@ from chewie.state_machines.mab_state_machine import MacAuthenticationBypassStateMachine from eventlet.queue import Queue -from chewie.managed_port import ManagedPort from helpers import FakeTimerScheduler FROM_SUPPLICANT = Queue() @@ -31,7 +30,7 @@ def patch_things(func): """decorator to mock patch socket operations and random number generators""" - @patch('chewie.managed_port.get_random_id', get_random_id_helper) + @patch('chewie.chewie.get_random_id', get_random_id_helper) @patch('chewie.chewie.EapSocket', FakeEapSocket) @patch('chewie.chewie.RadiusSocket', FakeRadiusSocket) @patch('chewie.chewie.MabSocket', FakeMabSocket) @@ -297,6 +296,7 @@ def test_get_state_machine(self): state_machine = self.chewie.get_state_machine('12:34:56:78:9a:bc', # pylint: disable=invalid-name '00:00:00:00:00:01') + self.assertEqual(len(self.chewie.state_machines), 1) self.assertIs(state_machine, self.chewie.get_state_machine('12:34:56:78:9a:bc', @@ -314,7 +314,6 @@ def test_get_state_machine(self): # port 2 has 1 mac self.assertEqual(len(self.chewie.state_machines['00:00:00:00:00:02']), 1) - # TODO Stop Test from touching internal get_state_machine_from_radius_packet def test_get_state_machine_by_packet_id(self): """Tests Chewie.get_state_machine_by_packet_id()""" self.chewie.radius_lifecycle.packet_id_to_mac[56] = {'src_mac': '12:34:56:78:9a:bc', @@ -323,10 +322,10 @@ def test_get_state_machine_by_packet_id(self): # pylint: disable=invalid-name '00:00:00:00:00:01') - self.assertIs(self.chewie._get_state_machine_from_radius_packet_id(56), + self.assertIs(self.chewie.get_state_machine_from_radius_packet_id(56), state_machine) with self.assertRaises(KeyError): - self.chewie._get_state_machine_from_radius_packet_id(20) + self.chewie.get_state_machine_from_radius_packet_id(20) @patch_things @setup_generators(sup_replies_success, radius_replies_success) @@ -344,6 +343,9 @@ def test_success_dot1x(self): '00:00:00:00:00:01').state, FullEAPStateMachine.SUCCESS2) + + + @patch_things @setup_generators(sup_replies_success, radius_replies_success) def test_chewie_identity_response_dot1x(self): @@ -401,7 +403,7 @@ def test_port_status_changes(self): # This will keep adding jobs forever. self.assertEqual(len(self.fake_scheduler.jobs), 1) self.assertEqual(self.fake_scheduler.jobs[0].function.__name__, - ManagedPort.send_preemptive_identity_request.__name__) + Chewie.send_preemptive_identity_request_if_no_active_on_port.__name__) @patch_things @@ -543,16 +545,3 @@ def test_mab_failure_auth(self): self.chewie.get_state_machine('02:42:ac:17:00:6f', '00:00:00:00:00:01').state, MacAuthenticationBypassStateMachine.AAA_FAILURE) - - @patch_things - @setup_generators(sup_replies_success, radius_replies_success) - def test_smoke_test_clients(self): - """Test success api""" - FROM_SUPPLICANT.put_nowait(bytes.fromhex("0000000000010242ac17006f888e01010000")) - - pool = eventlet.GreenPool() - pool.spawn(self.chewie.run) - - eventlet.sleep(1) - self.assertIsNotNone(self.chewie.clients) - self.assertEqual(len(self.chewie.clients), 1) diff --git a/test/unit/test_chewie_mocks.py b/test/unit/test_chewie_mocks.py index 8b6edc27..99744186 100644 --- a/test/unit/test_chewie_mocks.py +++ b/test/unit/test_chewie_mocks.py @@ -8,7 +8,6 @@ from chewie.event import EventMessageReceived from chewie.utils import EapQueueMessage -# pylint: disable=protected-access def return_if(expected, return_value): """allows us to do expect-this-return-that style mocking""" @@ -22,10 +21,8 @@ def inner_function(*args): return inner_function -FakeLogger = namedtuple('FakeLogger', ('name',) - ) # pylint: disable=invalid-name -FakeEapMessage = namedtuple( - 'FakeEapMessage', ('src_mac',)) # pylint: disable=invalid-name +FakeLogger = namedtuple('FakeLogger', ('name',)) # pylint: disable=invalid-name +FakeEapMessage = namedtuple('FakeEapMessage', ('src_mac',)) # pylint: disable=invalid-name class ChewieWithMocksTestCase(unittest.TestCase): @@ -44,16 +41,14 @@ def setUp(self): def test_eap_packet_in_goes_to_new_state_machine(self, state_machine, ethernet_parse): # pylint: disable=invalid-name """test EAP packet creates a new state machine and is sent on""" - self.chewie._eap_socket = Mock( - **{'receive.return_value': 'message from socket'}) + self.chewie.eap_socket = Mock(**{'receive.return_value': 'message from socket'}) ethernet_parse.side_effect = return_if( ('message from socket',), (FakeEapMessage('fake src mac'), 'fake dst mac') ) - self.chewie._receive_eap_messages() + self.chewie.receive_eap_messages() state_machine().event.assert_called_with( - EventMessageReceived(FakeEapMessage( - 'fake src mac'), 'fake dst mac') + EventMessageReceived(FakeEapMessage('fake src mac'), 'fake dst mac') ) @patch("chewie.chewie.Chewie.running", Mock(side_effect=[True, False])) @@ -62,35 +57,34 @@ def test_eap_packet_in_goes_to_new_state_machine(self, state_machine, def test_eap_output_packet_gets_packed_and_sent(self, ethernet_pack): # pylint: disable=invalid-name """test EAP packet creates a new state machine and is sent on""" - self.chewie._eap_socket = Mock() + self.chewie.eap_socket = Mock() ethernet_pack.return_value = "packed ethernet" self.chewie.eap_output_messages.put_nowait( EapQueueMessage("output eap message", "src mac", "port mac")) - self.chewie._send_eap_messages() - self.chewie._eap_socket.send.assert_called_with("packed ethernet") + self.chewie.send_eap_messages() + self.chewie.eap_socket.send.assert_called_with("packed ethernet") @patch("chewie.chewie.Chewie.running", Mock(side_effect=[True, False])) @patch("chewie.chewie.MessageParser.radius_parse") - @patch("chewie.chewie.Chewie._get_state_machine_from_radius_packet_id") + @patch("chewie.chewie.Chewie.get_state_machine_from_radius_packet_id") @patch("chewie.chewie.sleep", Mock()) def test_radius_packet_in_goes_to_state_machine(self, state_machine, radius_parse): # pylint: disable=invalid-name """test radius packet goes to a state machine""" # note that the state machine has to exist already - if not then we blow up fake_radius = namedtuple('Radius', ('packet_id',))('fake packet id') - self.chewie._radius_socket = Mock( - **{'receive.return_value': 'message from socket'}) + self.chewie.radius_socket = Mock(**{'receive.return_value': 'message from socket'}) self.chewie.radius_lifecycle = Mock(**{'build_event_radius_message_received.side_effect': - return_if( - (fake_radius,), - 'fake event' - )}) + return_if( + (fake_radius,), + 'fake event' + )}) radius_parse.side_effect = return_if( ('message from socket', 'SECRET', self.chewie.radius_lifecycle), fake_radius ) # not checking args as we can't mock the callback - self.chewie._receive_radius_messages() + self.chewie.receive_radius_messages() state_machine().event.assert_called_with( 'fake event' ) @@ -99,14 +93,13 @@ def test_radius_packet_in_goes_to_state_machine(self, state_machine, @patch("chewie.chewie.sleep", Mock()) def test_radius_output_packet_gets_packed_and_sent(self): # pylint: disable=invalid-name """test EAP packet creates a new state machine and is sent on""" - self.chewie._radius_socket = Mock() + self.chewie.radius_socket = Mock() - self.chewie.radius_output_messages.put_nowait( - 'fake radius output bits') + self.chewie.radius_output_messages.put_nowait('fake radius output bits') self.chewie.radius_lifecycle = Mock(**{'process_outbound.side_effect': - return_if( - ('fake radius output bits',), - 'packed radius' - )}) - self.chewie._send_radius_messages() - self.chewie._radius_socket.send.assert_called_with("packed radius") + return_if( + ('fake radius output bits',), + 'packed radius' + )}) + self.chewie.send_radius_messages() + self.chewie.radius_socket.send.assert_called_with("packed radius") diff --git a/test/unit/test_managed_port.py b/test/unit/test_managed_port.py deleted file mode 100644 index 78866460..00000000 --- a/test/unit/test_managed_port.py +++ /dev/null @@ -1,51 +0,0 @@ -import unittest -import logging -import tempfile -import sys - -from eventlet.queue import Queue - -from chewie.mac_address import MacAddress -from chewie.managed_port import ManagedPort -from helpers import FakeTimerScheduler - - -# pylint: disable=missing-docstring, -class ManagedPortTestCase(unittest.TestCase): - - def setUp(self): - self.logger = logging.getLogger() - self.logger.level = logging.DEBUG - self.log_file = tempfile.NamedTemporaryFile() - - self.logger.addHandler(logging.FileHandler(self.log_file.name)) - self.logger.addHandler(logging.StreamHandler(sys.stdout)) - - self.fake_scheduler = FakeTimerScheduler() - self.timer_scheduler = self.fake_scheduler - self.managed_port = None - self.eap_output_messages = Queue() # pylint: disable=global-statement - self.radius_output_messages = Queue() # pylint: disable=global-statement - - def test_successful_managed_port_smoke(self): - port_id = MacAddress.from_string('02:42:ac:17:00:6f') - self.managed_port = ManagedPort(port_id, self.logger.name, self.timer_scheduler, - self.eap_output_messages, - self.radius_output_messages) - self.assertIsNotNone(self.managed_port) - - def test_successful_managed_port_change_status(self): - self.test_successful_managed_port_smoke() - - current_status = self.managed_port.port_status - self.managed_port.port_status = not current_status - self.assertIsNot(self.managed_port.port_status, current_status, - "Managed Port unable to change status") - - # TODO Add tests: - # test_successful_managed_port_change_status_calls_state_machine - # test_successful_managed_port_start_identity_requests - # test_successful_managed_port_stop_identity_requests - -# Test chewie_send_preemptive_identity_requests when port is down -# test reauth_port