1#!/usr/bin/env python3
2#
3#   Copyright 2017 - Google, Inc.
4#
5#   Licensed under the Apache License, Version 2.0 (the "License");
6#   you may not use this file except in compliance with the License.
7#   You may obtain a copy of the License at
8#
9#       http://www.apache.org/licenses/LICENSE-2.0
10#
11#   Unless required by applicable law or agreed to in writing, software
12#   distributed under the License is distributed on an "AS IS" BASIS,
13#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14#   See the License for the specific language governing permissions and
15#   limitations under the License.
16
17import logging
18from acts.libs.proc import job
19
20GET_ALL_INTERFACE = 'ls /sys/class/net'
21GET_VIRTUAL_INTERFACE = 'ls /sys/devices/virtual/net'
22BRCTL_SHOW = 'brctl show'
23
24
25class ApInterfacesError(Exception):
26    """Error related to AP interfaces."""
27
28
29class ApInterfaces(object):
30    """Class to get network interface information for the device.
31
32    """
33    def __init__(self, ap):
34        """Initialize the ApInterface class.
35
36        Args:
37            ap: the ap object within ACTS
38        """
39        self.ssh = ap.ssh
40
41    def get_all_interface(self):
42        """Get all network interfaces on the device.
43
44        Returns:
45            interfaces_all: list of all the network interfaces on device
46        """
47        output = self.ssh.run(GET_ALL_INTERFACE)
48        interfaces_all = output.stdout.split('\n')
49
50        return interfaces_all
51
52    def get_virtual_interface(self):
53        """Get all virtual interfaces on the device.
54
55        Returns:
56            interfaces_virtual: list of all the virtual interfaces on device
57        """
58        output = self.ssh.run(GET_VIRTUAL_INTERFACE)
59        interfaces_virtual = output.stdout.split('\n')
60
61        return interfaces_virtual
62
63    def get_physical_interface(self):
64        """Get all the physical interfaces of the device.
65
66        Get all physical interfaces such as eth ports and wlan ports
67        Returns:
68            interfaces_phy: list of all the physical interfaces
69        """
70        interfaces_all = self.get_all_interface()
71        interfaces_virtual = self.get_virtual_interface()
72        interfaces_phy = list(set(interfaces_all) - set(interfaces_virtual))
73
74        return interfaces_phy
75
76    def get_bridge_interface(self):
77        """Get all the bridge interfaces of the device.
78
79        Returns:
80            interfaces_bridge: the list of bridge interfaces, return None if
81                bridge utility is not available on the device
82        """
83        interfaces_bridge = []
84        try:
85            output = self.ssh.run(BRCTL_SHOW)
86            lines = output.stdout.split('\n')
87            for line in lines:
88                interfaces_bridge.append(line.split('\t')[0])
89            interfaces_bridge.pop(0)
90            interfaces_bridge = [x for x in interfaces_bridge if x != '']
91            return interfaces_bridge
92        except job.Error:
93            logging.info('No brctl utility is available')
94            return None
95
96    def get_wlan_interface(self):
97        """Get all WLAN interfaces and specify 2.4 GHz and 5 GHz interfaces.
98
99        Returns:
100            interfaces_wlan: all wlan interfaces
101        Raises:
102            ApInterfacesError: Missing at least one WLAN interface
103        """
104        wlan_2g = None
105        wlan_5g = None
106        interfaces_phy = self.get_physical_interface()
107        for iface in interfaces_phy:
108            IW_LIST_FREQ = 'iwlist %s freq' % iface
109            output = self.ssh.run(IW_LIST_FREQ)
110            if 'Channel 06' in output.stdout and 'Channel 36' not in output.stdout:
111                wlan_2g = iface
112            elif 'Channel 36' in output.stdout and 'Channel 06' not in output.stdout:
113                wlan_5g = iface
114
115        interfaces_wlan = [wlan_2g, wlan_5g]
116
117        if None not in interfaces_wlan:
118            return interfaces_wlan
119
120        raise ApInterfacesError('Missing at least one WLAN interface')
121
122    def get_wan_interface(self):
123        """Get the WAN interface which has internet connectivity.
124
125        Returns:
126            wan: the only one WAN interface
127        Raises:
128            ApInterfacesError: no running WAN can be found
129        """
130        wan = None
131        interfaces_phy = self.get_physical_interface()
132        interfaces_wlan = self.get_wlan_interface()
133        interfaces_eth = list(set(interfaces_phy) - set(interfaces_wlan))
134        for iface in interfaces_eth:
135            network_status = self.check_ping(iface)
136            if network_status == 1:
137                wan = iface
138                break
139        if wan:
140            return wan
141
142        output = self.ssh.run('ifconfig')
143        interfaces_all = output.stdout.split('\n')
144        logging.info("IFCONFIG output = %s" % interfaces_all)
145
146        raise ApInterfacesError('No WAN interface available')
147
148    def get_lan_interface(self):
149        """Get the LAN interface connecting to local devices.
150
151        Returns:
152            lan: the only one running LAN interface of the devices
153            None, if nothing was found.
154        """
155        lan = None
156        interfaces_phy = self.get_physical_interface()
157        interfaces_wlan = self.get_wlan_interface()
158        interfaces_eth = list(set(interfaces_phy) - set(interfaces_wlan))
159        interface_wan = self.get_wan_interface()
160        interfaces_eth.remove(interface_wan)
161        for iface in interfaces_eth:
162            LAN_CHECK = 'ifconfig %s' % iface
163            output = self.ssh.run(LAN_CHECK)
164            if 'RUNNING' in output.stdout:
165                lan = iface
166                break
167        return lan
168
169    def check_ping(self, iface):
170        """Check the ping status on specific interface to determine the WAN.
171
172        Args:
173            iface: the specific interface to check
174        Returns:
175            network_status: the connectivity status of the interface
176        """
177        PING = 'ping -c 3 -I %s 8.8.8.8' % iface
178        try:
179            self.ssh.run(PING)
180            return 1
181        except job.Error:
182            return 0
183