source: ReferenceDesigns/w3_802.11/python/wlan_exp/transport/util.py

Last change on this file was 6320, checked in by chunter, 5 years ago

1.8.0 release wlan-exp

File size: 14.1 KB
Line 
1# -*- coding: utf-8 -*-
2"""
3------------------------------------------------------------------------------
4Mango 802.11 Reference Design Experiments Framework - Transport Utilities
5------------------------------------------------------------------------------
6License:   Copyright 2019 Mango Communications, Inc. All rights reserved.
7           Use and distribution subject to terms in LICENSE.txt
8------------------------------------------------------------------------------
9
10This module provides transport utility commands.
11
12Functions (see below for more information):
13    init_nodes()                  -- Initialize nodes
14    identify_all_nodes()          -- Send the 'identify' command to all nodes
15    reset_network_inf_all_nodes() -- Reset the network interface for all nodes
16    get_serial_number()           -- Standard way to check / process serial numbers
17
18"""
19
20import sys
21import re
22
23from . import exception as ex
24
25__all__ = ['init_nodes', 'identify_all_nodes', 'reset_network_inf_all_nodes', 
26           'get_serial_number']
27
28
29# Fix to support Python 2.x and 3.x
30if sys.version[0]=="3": raw_input=input
31
32
33# -----------------------------------------------------------------------------
34# Node Utilities
35# -----------------------------------------------------------------------------
36
37def init_nodes(nodes_config, network_config=None, node_factory=None, 
38               network_reset=True, output=False):
39    """Initalize nodes.
40
41    Args:   
42        nodes_config   (NodesConfiguration):   Describes the node configuration
43        network_config (NetworkConfiguration): Describes the network configuration
44        node_factory   (NodeFactory):          Factory to create nodes of a given node type
45        network_reset  (bool):                 Issue a network reset to all the nodes
46        output         (bool):                 Print output about the nodes
47   
48    Returns:
49        nodes (list of WlanExpNodes):  List of initialized nodes.
50    """
51    nodes       = []
52    error_nodes = []
53
54    # Create a Network Configuration if there is none provided
55    if network_config is None:
56        from . import config
57        network_config = config.NetworkConfiguration()
58
59    host_id             = network_config.get_param('host_id')
60    jumbo_frame_support = network_config.get_param('jumbo_frame_support')
61   
62    # Process the config to create nodes
63    nodes_dict = nodes_config.get_nodes_dict()
64
65    # If node_factory is not defined, create a default NodeFactory
66    if node_factory is None:
67        from . import node
68        node_factory = node.WlanExpTransportNodeFactory(network_config)
69
70    if network_reset:
71        # Send a broadcast network reset command to make sure all nodes are
72        # in their default state.
73        reset_network_inf_all_nodes(network_config=network_config)
74
75    # Create the nodes in the dictionary
76    for node_dict in nodes_dict:
77        if (host_id == node_dict['node_id']):
78            msg = "Host id is set to {0} and must be unique.".format(host_id)
79            raise ex.ConfigError(msg)
80
81        # Set up the node_factory from the node dictionary
82        node_factory.setup(node_dict)
83
84        # Create the correct type of node; will return None and print a
85        #   warning if the node is not recognized
86        node = node_factory.create_node(network_config, network_reset)
87
88        if node is not None:
89            node.configure_node(jumbo_frame_support)
90            nodes.append(node)
91        else:
92            error_nodes.append(node_dict)
93
94    if (len(nodes) != len(nodes_dict)):
95        msg  = "\n\nERROR:  Was not able to initialize all nodes.  The following \n"
96        msg += "nodes were not able to be initialized:\n"
97        for node_dict in error_nodes:
98            msg += "    {0}\n".format(node_dict['serial_number'])
99        raise ex.ConfigError(msg)
100
101    if output:
102        print("-" * 50)
103        print("Initialized Nodes:")
104        print("-" * 50)
105        for node in nodes:
106            print(node)
107            print("-" * 30)
108        print("-" * 50)
109
110    return nodes
111
112# End def
113
114
115def identify_all_nodes(network_config):
116    """Issue a broadcast command to all nodes to blink their LEDs for 10 seconds.
117   
118    Args:
119        network_config (list of NetworkConfiguration):  One or more network
120            configurations
121    """
122    import time
123    from . import cmds
124    from . import transport_eth_ip_udp_py_broadcast as tp_broadcast
125
126    if type(network_config) is list:
127        my_network_config = network_config
128    else:
129        my_network_config = [network_config]
130
131    for network in my_network_config:
132
133        network_addr = network.get_param('network')
134        tx_buf_size  = network.get_param('tx_buffer_size')
135        rx_buf_size  = network.get_param('rx_buffer_size')
136   
137        msg  = "Identifying all nodes on network {0}.  ".format(network_addr)
138        msg += "Please check the LEDs."
139        print(msg)
140
141        transport = tp_broadcast.TransportEthIpUdpPyBroadcast(network_config=network)
142
143        transport.transport_open(tx_buf_size, rx_buf_size)
144
145        cmd = cmds.NodeIdentify(cmds.CMD_PARAM_NODE_IDENTIFY_ALL_NODES)
146        payload = cmd.serialize()
147        transport.send(payload=payload)
148       
149        transport.transport_close()
150
151# End def
152
153
154def reset_network_inf_all_nodes(network_config):
155    """Reset the wlan_exp network interface of all nodes.
156
157    This will issue a broadcast command to all nodes on each network to reset
158    the network interface informaiton (ie IP address, ports, etc.)
159   
160    Args:
161        network_config (list of NetworkConfiguration):  One or more network
162            configurations
163
164    """
165    from . import cmds
166    from . import transport_eth_ip_udp_py_broadcast as tp_broadcast
167
168    if type(network_config) is list:
169        my_network_config = network_config
170    else:
171        my_network_config = [network_config]
172
173    for network in my_network_config:
174
175        network_addr = network.get_param('network')
176        tx_buf_size  = network.get_param('tx_buffer_size')
177        rx_buf_size  = network.get_param('rx_buffer_size')
178   
179        msg  = "Resetting the network config for all nodes on network {0}.".format(network_addr)
180        print(msg)
181   
182        transport = tp_broadcast.TransportEthIpUdpPyBroadcast(network_config=network)
183
184        transport.transport_open(tx_buf_size, rx_buf_size)
185
186        cmd = cmds.NodeResetNetwork(cmds.CMD_PARAM_NODE_NETWORK_RESET_ALL_NODES)
187        payload = cmd.serialize()
188        transport.send(payload=payload)
189       
190        transport.transport_close()
191
192# End def
193
194
195# -----------------------------------------------------------------------------
196# Misc Utilities
197# -----------------------------------------------------------------------------
198def get_serial_number(serial_number):
199    """Get the numeric part of the provided serial number via the wlan_exp.platform subsystem
200    This is a temporary method that re-implements the API of the old util.get_serial_number()
201
202    Soon both methods will go away once the better wlan_exp platform structure is ready   
203    """
204
205    import wlan_exp.platform
206    import wlan_exp_platform_w3
207
208    return wlan_exp.platform.get_serial_number(serial_number)
209
210
211def mac_addr_to_str(mac_address):
212    """Convert an integer to a colon separated MAC address string."""
213    from . import transport_eth_ip_udp as tp
214    return tp.mac_addr_to_str(mac_address)
215
216def get_total_header_len():
217    """ Helper to compute the total number of header bytes that precede any
218    wlan_exp payload. This sum does not include any cmd/response arguments
219    """
220   
221    # This util method is hard-coded for a IPV4+UDP transport
222    # Future non-Ethernet/IP/UDP transports will require some
223    #  work to generalize conversion betwee transport MTU and
224    #  wlan_exp payload limits
225   
226    from wlan_exp.transport.message import TransportHeader as TrnsHdr
227    from wlan_exp.transport.message import CmdRespMessage as CmdRspMsg
228   
229    # Generic transport header, always the same size
230    th = TrnsHdr()
231   
232    # Include zero arguments in header bytes
233    crm = CmdRspMsg(num_args=0, args=[0])
234   
235    # Hard coded lengths for IPv4 and UDP headers
236    ipv4_hdr_len = 20
237    udp_hdr_len = 8
238   
239    # Command/Response packet headers:
240    #  [IPV4 UDP padding transport CmdRespHeader]
241    return ipv4_hdr_len + udp_hdr_len + 2 + th.sizeof() + crm.sizeof()
242
243# End def
244
245
246
247# -----------------------------------------------------------------------------
248# Internal Methods
249# -----------------------------------------------------------------------------
250
251def _get_all_host_ip_addrs():
252    """Get all host interface IP addresses."""
253    import socket
254
255    addrs = []
256
257    # Get all the host address information
258    addr_info = socket.getaddrinfo(socket.gethostname(), None)
259
260    # Add addresses that are IPv6 and support SOCK_DGRAM or everything (0)
261    #   to the list of host IP addresses
262    for info in addr_info:
263        if (info[0] == socket.AF_INET) and (info[1] in [0, socket.SOCK_DGRAM]):
264            addrs.append(info[4][0])
265
266    if (len(addrs) == 0):
267        msg  = "WARNING: Could not find any valid interface IP addresses for host {0}\n".format(socket.gethostname())
268        msg += "    Please check your network settings."
269        print(msg)
270
271    return addrs
272
273# End def
274
275
276def _get_host_ip_addr_for_network(network):
277    """Get the host IP address for the given network."""
278    import socket
279
280    # Get the broadcast address of the network
281    broadcast_addr = network.get_param('broadcast_address')
282   
283    try:
284        # Create a temporary UDP socket to get the hostname
285        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
286        s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
287        s.connect((broadcast_addr, 1))
288        socket_name = s.getsockname()[0]
289        s.close()
290    except (socket.gaierror, socket.error):
291        msg  = "WARNING: Could not get host IP for network {0}.".format(network.get_param('network'))
292        print(msg)
293        socket_name = ''
294
295    return socket_name
296
297# End def
298
299
300def _check_network_interface(network, quiet=False):
301    """Check that this network has a valid interface IP address."""
302    import socket
303
304    # Get the first three octets of the network address
305    network_ip_subnet = _get_ip_address_subnet(network)
306    test_ip_addr      = network_ip_subnet + ".255"
307    network_addr      = network_ip_subnet + ".0"
308
309    try:
310        # Create a temporary UDP socket to get the hostname
311        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
312        s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
313        s.connect((test_ip_addr, 1))
314        socket_name = s.getsockname()[0]
315        s.close()
316    except (socket.gaierror, socket.error):
317        msg  = "WARNING: Could not create temporary UDP socket.\n"
318        msg += "  {0} may not work as Network address.".format(network_addr)
319        print(msg)
320       
321
322    # Get the subnet of the socket
323    sock_ip_subnet = _get_ip_address_subnet(socket_name)
324
325    # Check that the socket_ip_subnet is equal to the host_ip_subnet
326    if ((network != network_addr) and not quiet):
327        msg  = "WARNING: Network address must be of the form 'X.Y.Z.0'.\n"
328        msg += "  Provided {0}.  Using {1} instead.".format(network, network_addr)
329        print(msg)
330   
331    # Check that the socket_ip_subnet is equal to the host_ip_subnet
332    if ((network_ip_subnet != sock_ip_subnet) and not quiet):
333        msg  = "WARNING: Interface IP address {0} and ".format(socket_name)
334        msg += "network {0} appear to be on different subnets.\n".format(network)
335        msg += "    Please check your network settings if this in not intentional."
336        print(msg)
337
338    return network_addr
339
340# End def
341
342
343def _get_ip_address_subnet(ip_address):
344    """Get the subnet X.Y.Z of ip_address X.Y.Z.W"""
345    #expr = re.compile('\.')
346    #tmp = [int(n) for n in expr.split(ip_address)]
347    tmp = [int(n) for n in ip_address.split('.')]
348    return "{0:d}.{1:d}.{2:d}".format(tmp[0], tmp[1], tmp[2])
349   
350# End def
351
352
353def _get_broadcast_address(ip_address):
354    """Get the broadcast address X.Y.Z.255 for ip_address X.Y.Z.W"""
355    ip_subnet      = _get_ip_address_subnet(ip_address)
356    broadcast_addr = ip_subnet + ".255"
357    return broadcast_addr
358   
359# End def
360   
361
362def _check_ip_address_format(ip_address):
363    """Check that the string has a valid IP address format."""
364    expr = re.compile(r'\d+\.\d+\.\d+\.\d+')
365
366    if not expr.match(ip_address) is None:
367        return True
368    else:
369        import warnings
370        msg  = "\n'{0}' is not a valid IP address format.\n" .format(ip_address) 
371        msg += "    Please use A.B.C.D where A, B, C and D are in [0, 254]"
372        warnings.warn(msg)
373        return False
374
375# End def
376
377
378def _check_host_id(host_id):
379    """Check that the host_id is valid."""
380    if (host_id >= 200) and (host_id <= 254):
381        return True
382    else:
383        import warnings
384        msg  = "\n'{0}' is not a valid Host ID.\n" .format(host_id) 
385        msg += "    Valid Host IDs are integers in the range of [200,254]"
386        warnings.warn(msg)
387        return False
388
389# End def
390
391
392def _get_os_socket_buffer_size(req_tx_buf_size, req_rx_buf_size):
393    """Get the size of the send and receive buffers from the OS given the
394    requested TX/RX buffer sizes.
395    """
396    import errno
397    import socket
398    from socket import error as socket_error
399   
400    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
401
402    try:
403        sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, req_tx_buf_size)
404        sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, req_rx_buf_size)
405    except socket_error as serr:
406        # Cannot set the buffer size on some hardware
407        if serr.errno != errno.ENOBUFS:
408            raise serr
409
410    sock_tx_buf_size = sock.getsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF)
411    sock_rx_buf_size = sock.getsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF)
412   
413    return (sock_tx_buf_size, sock_rx_buf_size)
414
415# End def
416   
417
418
419
Note: See TracBrowser for help on using the repository browser.