tturney | 1bdf77d | 2015-12-28 17:46:13 -0800 | [diff] [blame] | 1 | #!/usr/bin/env python3.4 |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 2 | # |
tturney | 1bdf77d | 2015-12-28 17:46:13 -0800 | [diff] [blame] | 3 | # Copyright 2016 - Google, Inc. |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 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 | |
Christopher Wiley | ede81f0 | 2016-11-01 09:15:30 -0700 | [diff] [blame] | 17 | import collections |
Benny Peake | 85f112a | 2016-10-07 19:41:49 -0700 | [diff] [blame] | 18 | import ipaddress |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 19 | |
Qi Jiang | c3e8785 | 2017-10-04 22:45:52 -0700 | [diff] [blame^] | 20 | from acts.controllers.ap_lib import bridge_interface |
Benny Peake | 85f112a | 2016-10-07 19:41:49 -0700 | [diff] [blame] | 21 | from acts.controllers.ap_lib import dhcp_config |
| 22 | from acts.controllers.ap_lib import dhcp_server |
| 23 | from acts.controllers.ap_lib import hostapd |
| 24 | from acts.controllers.ap_lib import hostapd_config |
| 25 | from acts.controllers.utils_lib.commands import ip |
| 26 | from acts.controllers.utils_lib.commands import route |
Joe Brennan | 88279af | 2017-02-15 18:11:53 -0800 | [diff] [blame] | 27 | from acts.controllers.utils_lib.commands import shell |
Benny Peake | 85f112a | 2016-10-07 19:41:49 -0700 | [diff] [blame] | 28 | from acts.controllers.utils_lib.ssh import connection |
| 29 | from acts.controllers.utils_lib.ssh import settings |
| 30 | |
| 31 | ACTS_CONTROLLER_CONFIG_NAME = 'AccessPoint' |
Benny Peake | 0f5049b | 2016-10-28 15:57:01 -0700 | [diff] [blame] | 32 | ACTS_CONTROLLER_REFERENCE_NAME = 'access_points' |
Benny Peake | 85f112a | 2016-10-07 19:41:49 -0700 | [diff] [blame] | 33 | |
Ang Li | 51df045 | 2016-06-07 10:58:16 -0700 | [diff] [blame] | 34 | |
Ang Li | a11f2cf | 2016-05-05 11:54:40 -0700 | [diff] [blame] | 35 | def create(configs): |
Benny Peake | 85f112a | 2016-10-07 19:41:49 -0700 | [diff] [blame] | 36 | """Creates ap controllers from a json config. |
| 37 | |
| 38 | Creates an ap controller from either a list, or a single |
| 39 | element. The element can either be just the hostname or a dictionary |
| 40 | containing the hostname and username of the ap to connect to over ssh. |
| 41 | |
| 42 | Args: |
| 43 | The json configs that represent this controller. |
| 44 | |
| 45 | Returns: |
| 46 | A new AccessPoint. |
| 47 | """ |
Joe Brennan | 88279af | 2017-02-15 18:11:53 -0800 | [diff] [blame] | 48 | return [ |
| 49 | AccessPoint(settings.from_config(c['ssh_config'])) for c in configs |
| 50 | ] |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 51 | |
Ang Li | 51df045 | 2016-06-07 10:58:16 -0700 | [diff] [blame] | 52 | |
Benny Peake | 85f112a | 2016-10-07 19:41:49 -0700 | [diff] [blame] | 53 | def destroy(aps): |
| 54 | """Destroys a list of access points. |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 55 | |
Benny Peake | 85f112a | 2016-10-07 19:41:49 -0700 | [diff] [blame] | 56 | Args: |
| 57 | aps: The list of access points to destroy. |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 58 | """ |
Benny Peake | 85f112a | 2016-10-07 19:41:49 -0700 | [diff] [blame] | 59 | for ap in aps: |
| 60 | ap.close() |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 61 | |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 62 | |
Benny Peake | 85f112a | 2016-10-07 19:41:49 -0700 | [diff] [blame] | 63 | def get_info(aps): |
| 64 | """Get information on a list of access points. |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 65 | |
Benny Peake | 85f112a | 2016-10-07 19:41:49 -0700 | [diff] [blame] | 66 | Args: |
| 67 | aps: A list of AccessPoints. |
| 68 | |
| 69 | Returns: |
| 70 | A list of all aps hostname. |
| 71 | """ |
Christopher Wiley | 7dae1d9 | 2016-10-31 14:47:58 -0700 | [diff] [blame] | 72 | return [ap.ssh_settings.hostname for ap in aps] |
Benny Peake | 85f112a | 2016-10-07 19:41:49 -0700 | [diff] [blame] | 73 | |
| 74 | |
| 75 | class Error(Exception): |
| 76 | """Error raised when there is a problem with the access point.""" |
| 77 | |
| 78 | |
Joe Brennan | 88279af | 2017-02-15 18:11:53 -0800 | [diff] [blame] | 79 | _ApInstance = collections.namedtuple('_ApInstance', ['hostapd', 'subnet']) |
Christopher Wiley | ede81f0 | 2016-11-01 09:15:30 -0700 | [diff] [blame] | 80 | |
| 81 | # We use these today as part of a hardcoded mapping of interface name to |
| 82 | # capabilities. However, medium term we need to start inspecting |
| 83 | # interfaces to determine their capabilities. |
| 84 | _AP_2GHZ_INTERFACE = 'wlan0' |
| 85 | _AP_5GHZ_INTERFACE = 'wlan1' |
Joe Brennan | 88279af | 2017-02-15 18:11:53 -0800 | [diff] [blame] | 86 | # These ranges were split this way since each physical radio can have up |
| 87 | # to 8 SSIDs so for the 2GHz radio the DHCP range will be |
| 88 | # 192.168.1 - 8 and the 5Ghz radio will be 192.168.9 - 16 |
| 89 | _AP_2GHZ_SUBNET_STR = '192.168.1.0/24' |
| 90 | _AP_5GHZ_SUBNET_STR = '192.168.9.0/24' |
| 91 | _AP_2GHZ_SUBNET = dhcp_config.Subnet(ipaddress.ip_network(_AP_2GHZ_SUBNET_STR)) |
| 92 | _AP_5GHZ_SUBNET = dhcp_config.Subnet(ipaddress.ip_network(_AP_5GHZ_SUBNET_STR)) |
Qi Jiang | c3e8785 | 2017-10-04 22:45:52 -0700 | [diff] [blame^] | 93 | LAN_INTERFACE = 'eth1' |
| 94 | # The last digit of the ip for the bridge interface |
| 95 | BRIDGE_IP_LAST = '100' |
Christopher Wiley | ede81f0 | 2016-11-01 09:15:30 -0700 | [diff] [blame] | 96 | |
| 97 | |
Benny Peake | 85f112a | 2016-10-07 19:41:49 -0700 | [diff] [blame] | 98 | class AccessPoint(object): |
| 99 | """An access point controller. |
| 100 | |
| 101 | Attributes: |
Benny Peake | 85f112a | 2016-10-07 19:41:49 -0700 | [diff] [blame] | 102 | ssh: The ssh connection to this ap. |
| 103 | ssh_settings: The ssh settings being used by the ssh conneciton. |
| 104 | dhcp_settings: The dhcp server settings being used. |
| 105 | """ |
| 106 | |
Christopher Wiley | 7dae1d9 | 2016-10-31 14:47:58 -0700 | [diff] [blame] | 107 | def __init__(self, ssh_settings): |
Benny Peake | 85f112a | 2016-10-07 19:41:49 -0700 | [diff] [blame] | 108 | """ |
| 109 | Args: |
Christopher Wiley | 7dae1d9 | 2016-10-31 14:47:58 -0700 | [diff] [blame] | 110 | ssh_settings: acts.controllers.utils_lib.ssh.SshSettings instance. |
Benny Peake | 85f112a | 2016-10-07 19:41:49 -0700 | [diff] [blame] | 111 | """ |
Christopher Wiley | 7dae1d9 | 2016-10-31 14:47:58 -0700 | [diff] [blame] | 112 | self.ssh_settings = ssh_settings |
Benny Peake | 85f112a | 2016-10-07 19:41:49 -0700 | [diff] [blame] | 113 | self.ssh = connection.SshConnection(self.ssh_settings) |
| 114 | |
Christopher Wiley | ede81f0 | 2016-11-01 09:15:30 -0700 | [diff] [blame] | 115 | # Singleton utilities for running various commands. |
Benny Peake | 85f112a | 2016-10-07 19:41:49 -0700 | [diff] [blame] | 116 | self._ip_cmd = ip.LinuxIpCommand(self.ssh) |
| 117 | self._route_cmd = route.LinuxRouteCommand(self.ssh) |
| 118 | |
Christopher Wiley | ede81f0 | 2016-11-01 09:15:30 -0700 | [diff] [blame] | 119 | # A map from network interface name to _ApInstance objects representing |
| 120 | # the hostapd instance running against the interface. |
| 121 | self._aps = dict() |
Qi Jiang | c3e8785 | 2017-10-04 22:45:52 -0700 | [diff] [blame^] | 122 | self.bridge = bridge_interface.BridgeInterface(self.ssh) |
Christopher Wiley | ede81f0 | 2016-11-01 09:15:30 -0700 | [diff] [blame] | 123 | |
Joe Brennan | 88279af | 2017-02-15 18:11:53 -0800 | [diff] [blame] | 124 | def start_ap(self, hostapd_config, additional_parameters=None): |
Benny Peake | 85f112a | 2016-10-07 19:41:49 -0700 | [diff] [blame] | 125 | """Starts as an ap using a set of configurations. |
| 126 | |
| 127 | This will start an ap on this host. To start an ap the controller |
| 128 | selects a network interface to use based on the configs given. It then |
| 129 | will start up hostapd on that interface. Next a subnet is created for |
| 130 | the network interface and dhcp server is refreshed to give out ips |
| 131 | for that subnet for any device that connects through that interface. |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 132 | |
| 133 | Args: |
Benny Peake | 85f112a | 2016-10-07 19:41:49 -0700 | [diff] [blame] | 134 | hostapd_config: hostapd_config.HostapdConfig, The configurations |
| 135 | to use when starting up the ap. |
Joe Brennan | 88279af | 2017-02-15 18:11:53 -0800 | [diff] [blame] | 136 | additional_parameters: A dicitonary of parameters that can sent |
| 137 | directly into the hostapd config file. This |
| 138 | can be used for debugging and or adding one |
| 139 | off parameters into the config. |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 140 | |
| 141 | Returns: |
Benny Peake | 85f112a | 2016-10-07 19:41:49 -0700 | [diff] [blame] | 142 | An identifier for the ap being run. This identifier can be used |
| 143 | later by this controller to control the ap. |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 144 | |
| 145 | Raises: |
Benny Peake | 85f112a | 2016-10-07 19:41:49 -0700 | [diff] [blame] | 146 | Error: When the ap can't be brought up. |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 147 | """ |
Christopher Wiley | ede81f0 | 2016-11-01 09:15:30 -0700 | [diff] [blame] | 148 | # Right now, we hardcode that a frequency maps to a particular |
| 149 | # network interface. This is true of the hardware we're running |
| 150 | # against right now, but in general, we'll want to do some |
| 151 | # dynamic discovery of interface capabilities. See b/32582843 |
Benny Peake | 85f112a | 2016-10-07 19:41:49 -0700 | [diff] [blame] | 152 | if hostapd_config.frequency < 5000: |
Christopher Wiley | ede81f0 | 2016-11-01 09:15:30 -0700 | [diff] [blame] | 153 | interface = _AP_2GHZ_INTERFACE |
| 154 | subnet = _AP_2GHZ_SUBNET |
Benny Peake | 85f112a | 2016-10-07 19:41:49 -0700 | [diff] [blame] | 155 | else: |
Christopher Wiley | ede81f0 | 2016-11-01 09:15:30 -0700 | [diff] [blame] | 156 | interface = _AP_5GHZ_INTERFACE |
| 157 | subnet = _AP_5GHZ_SUBNET |
| 158 | |
Joe Brennan | 88279af | 2017-02-15 18:11:53 -0800 | [diff] [blame] | 159 | # In order to handle dhcp servers on any interface, the initiation of |
| 160 | # the dhcp server must be done after the wlan interfaces are figured |
| 161 | # out as opposed to being in __init__ |
| 162 | self._dhcp = dhcp_server.DhcpServer(self.ssh, interface=interface) |
| 163 | |
| 164 | # For multi bssid configurations the mac address |
| 165 | # of the wireless interface needs to have enough space to mask out |
| 166 | # up to 8 different mac addresses. The easiest way to do this |
| 167 | # is to set the last byte to 0. While technically this could |
| 168 | # cause a duplicate mac address it is unlikely and will allow for |
Bindu Mahadev | 2ef0c3e | 2017-07-13 15:39:55 -0700 | [diff] [blame] | 169 | # one radio to have up to 8 APs on the interface. |
Joe Brennan | 3d00b2a | 2017-04-13 14:27:05 -0700 | [diff] [blame] | 170 | interface_mac_orig = None |
Bindu Mahadev | 2ef0c3e | 2017-07-13 15:39:55 -0700 | [diff] [blame] | 171 | cmd = "ifconfig %s|grep ether|awk -F' ' '{print $2}'" % interface |
| 172 | interface_mac_orig = self.ssh.run(cmd) |
| 173 | hostapd_config.bssid = interface_mac_orig.stdout[:-1] + '0' |
Joe Brennan | 88279af | 2017-02-15 18:11:53 -0800 | [diff] [blame] | 174 | |
Christopher Wiley | ede81f0 | 2016-11-01 09:15:30 -0700 | [diff] [blame] | 175 | if interface in self._aps: |
| 176 | raise ValueError('No WiFi interface available for AP on ' |
| 177 | 'channel %d' % hostapd_config.channel) |
| 178 | |
| 179 | apd = hostapd.Hostapd(self.ssh, interface) |
Joe Brennan | 88279af | 2017-02-15 18:11:53 -0800 | [diff] [blame] | 180 | new_instance = _ApInstance(hostapd=apd, subnet=subnet) |
Christopher Wiley | ede81f0 | 2016-11-01 09:15:30 -0700 | [diff] [blame] | 181 | self._aps[interface] = new_instance |
| 182 | |
| 183 | # Turn off the DHCP server, we're going to change its settings. |
| 184 | self._dhcp.stop() |
| 185 | # Clear all routes to prevent old routes from interfering. |
| 186 | self._route_cmd.clear_routes(net_interface=interface) |
| 187 | |
Joe Brennan | 88279af | 2017-02-15 18:11:53 -0800 | [diff] [blame] | 188 | if hostapd_config.bss_lookup: |
| 189 | # The dhcp_bss dictionary is created to hold the key/value |
| 190 | # pair of the interface name and the ip scope that will be |
| 191 | # used for the particular interface. The a, b, c, d |
| 192 | # variables below are the octets for the ip address. The |
| 193 | # third octet is then incremented for each interface that |
| 194 | # is requested. This part is designed to bring up the |
| 195 | # hostapd interfaces and not the DHCP servers for each |
| 196 | # interface. |
| 197 | dhcp_bss = {} |
| 198 | counter = 1 |
| 199 | for bss in hostapd_config.bss_lookup: |
Bindu Mahadev | 2ef0c3e | 2017-07-13 15:39:55 -0700 | [diff] [blame] | 200 | if interface_mac_orig: |
| 201 | hostapd_config.bss_lookup[ |
| 202 | bss].bssid = interface_mac_orig.stdout[:-1] + str( |
| 203 | counter) |
Joe Brennan | 88279af | 2017-02-15 18:11:53 -0800 | [diff] [blame] | 204 | self._route_cmd.clear_routes(net_interface=str(bss)) |
| 205 | if interface is _AP_2GHZ_INTERFACE: |
| 206 | starting_ip_range = _AP_2GHZ_SUBNET_STR |
| 207 | else: |
| 208 | starting_ip_range = _AP_5GHZ_SUBNET_STR |
| 209 | a, b, c, d = starting_ip_range.split('.') |
| 210 | dhcp_bss[bss] = dhcp_config.Subnet( |
| 211 | ipaddress.ip_network('%s.%s.%s.%s' % (a, b, str( |
| 212 | int(c) + counter), d))) |
| 213 | counter = counter + 1 |
| 214 | |
| 215 | apd.start(hostapd_config, additional_parameters=additional_parameters) |
| 216 | |
| 217 | # The DHCP serer requires interfaces to have ips and routes before |
| 218 | # the server will come up. |
Christopher Wiley | ede81f0 | 2016-11-01 09:15:30 -0700 | [diff] [blame] | 219 | interface_ip = ipaddress.ip_interface( |
| 220 | '%s/%s' % (subnet.router, subnet.network.netmask)) |
| 221 | self._ip_cmd.set_ipv4_address(interface, interface_ip) |
Joe Brennan | 88279af | 2017-02-15 18:11:53 -0800 | [diff] [blame] | 222 | if hostapd_config.bss_lookup: |
| 223 | # This loop goes through each interface that was setup for |
| 224 | # hostapd and assigns the DHCP scopes that were defined but |
| 225 | # not used during the hostapd loop above. The k and v |
| 226 | # variables represent the interface name, k, and dhcp info, v. |
| 227 | for k, v in dhcp_bss.items(): |
| 228 | bss_interface_ip = ipaddress.ip_interface( |
markdr | 85a5e1a | 2017-07-17 14:11:34 -0700 | [diff] [blame] | 229 | '%s/%s' % (dhcp_bss[k].router, |
| 230 | dhcp_bss[k].network.netmask)) |
Joe Brennan | 88279af | 2017-02-15 18:11:53 -0800 | [diff] [blame] | 231 | self._ip_cmd.set_ipv4_address(str(k), bss_interface_ip) |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 232 | |
Christopher Wiley | ede81f0 | 2016-11-01 09:15:30 -0700 | [diff] [blame] | 233 | # Restart the DHCP server with our updated list of subnets. |
Joe Brennan | 88279af | 2017-02-15 18:11:53 -0800 | [diff] [blame] | 234 | configured_subnets = [x.subnet for x in self._aps.values()] |
| 235 | if hostapd_config.bss_lookup: |
| 236 | for k, v in dhcp_bss.items(): |
| 237 | configured_subnets.append(v) |
| 238 | |
| 239 | self._dhcp.start(config=dhcp_config.DhcpConfig(configured_subnets)) |
Benny Peake | 85f112a | 2016-10-07 19:41:49 -0700 | [diff] [blame] | 240 | |
Christopher Wiley | ede81f0 | 2016-11-01 09:15:30 -0700 | [diff] [blame] | 241 | return interface |
Benny Peake | 85f112a | 2016-10-07 19:41:49 -0700 | [diff] [blame] | 242 | |
Joe Brennan | 3d00b2a | 2017-04-13 14:27:05 -0700 | [diff] [blame] | 243 | def get_bssid_from_ssid(self, ssid): |
| 244 | """Gets the BSSID from a provided SSID |
| 245 | |
| 246 | Args: |
| 247 | ssid: An SSID string |
Bindu Mahadev | 9bc52e6 | 2017-06-28 17:01:12 -0700 | [diff] [blame] | 248 | Returns: The BSSID if on the AP or None if SSID could not be found. |
Joe Brennan | 3d00b2a | 2017-04-13 14:27:05 -0700 | [diff] [blame] | 249 | """ |
| 250 | |
Bindu Mahadev | 9bc52e6 | 2017-06-28 17:01:12 -0700 | [diff] [blame] | 251 | interfaces = [_AP_2GHZ_INTERFACE, _AP_5GHZ_INTERFACE, ssid] |
| 252 | # Get the interface name associated with the given ssid. |
| 253 | for interface in interfaces: |
markdr | 85a5e1a | 2017-07-17 14:11:34 -0700 | [diff] [blame] | 254 | cmd = "iw dev %s info|grep ssid|awk -F' ' '{print $2}'" % ( |
Bindu Mahadev | 9bc52e6 | 2017-06-28 17:01:12 -0700 | [diff] [blame] | 255 | str(interface)) |
| 256 | iw_output = self.ssh.run(cmd) |
| 257 | if 'command failed: No such device' in iw_output.stderr: |
| 258 | continue |
| 259 | else: |
| 260 | # If the configured ssid is equal to the given ssid, we found |
| 261 | # the right interface. |
| 262 | if iw_output.stdout == ssid: |
markdr | 85a5e1a | 2017-07-17 14:11:34 -0700 | [diff] [blame] | 263 | cmd = "iw dev %s info|grep addr|awk -F' ' '{print $2}'" % ( |
Bindu Mahadev | 9bc52e6 | 2017-06-28 17:01:12 -0700 | [diff] [blame] | 264 | str(interface)) |
| 265 | iw_output = self.ssh.run(cmd) |
| 266 | return iw_output.stdout |
| 267 | return None |
Joe Brennan | 3d00b2a | 2017-04-13 14:27:05 -0700 | [diff] [blame] | 268 | |
Benny Peake | 85f112a | 2016-10-07 19:41:49 -0700 | [diff] [blame] | 269 | def stop_ap(self, identifier): |
| 270 | """Stops a running ap on this controller. |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 271 | |
| 272 | Args: |
Benny Peake | 85f112a | 2016-10-07 19:41:49 -0700 | [diff] [blame] | 273 | identifier: The identify of the ap that should be taken down. |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 274 | """ |
Joe Brennan | 88279af | 2017-02-15 18:11:53 -0800 | [diff] [blame] | 275 | |
Qi Jiang | 293bc99 | 2017-09-14 17:15:20 -0700 | [diff] [blame] | 276 | if identifier not in list(self._aps.keys()): |
Benny Peake | 85f112a | 2016-10-07 19:41:49 -0700 | [diff] [blame] | 277 | raise ValueError('Invalid identifer %s given' % identifier) |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 278 | |
Joe Brennan | 88279af | 2017-02-15 18:11:53 -0800 | [diff] [blame] | 279 | instance = self._aps.get(identifier) |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 280 | |
Christopher Wiley | ede81f0 | 2016-11-01 09:15:30 -0700 | [diff] [blame] | 281 | instance.hostapd.stop() |
| 282 | self._dhcp.stop() |
Benny Peake | 85f112a | 2016-10-07 19:41:49 -0700 | [diff] [blame] | 283 | self._ip_cmd.clear_ipv4_addresses(identifier) |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 284 | |
Benny Peake | 85f112a | 2016-10-07 19:41:49 -0700 | [diff] [blame] | 285 | # DHCP server needs to refresh in order to tear down the subnet no |
| 286 | # longer being used. In the event that all interfaces are torn down |
| 287 | # then an exception gets thrown. We need to catch this exception and |
| 288 | # check that all interfaces should actually be down. |
Joe Brennan | 88279af | 2017-02-15 18:11:53 -0800 | [diff] [blame] | 289 | configured_subnets = [x.subnet for x in self._aps.values()] |
Qi Jiang | aa818ba | 2017-09-16 17:09:47 -0700 | [diff] [blame] | 290 | del self._aps[identifier] |
Christopher Wiley | ede81f0 | 2016-11-01 09:15:30 -0700 | [diff] [blame] | 291 | if configured_subnets: |
| 292 | self._dhcp.start(dhcp_config.DhcpConfig(configured_subnets)) |
Benny Peake | 85f112a | 2016-10-07 19:41:49 -0700 | [diff] [blame] | 293 | |
| 294 | def stop_all_aps(self): |
| 295 | """Stops all running aps on this device.""" |
Joe Brennan | 88279af | 2017-02-15 18:11:53 -0800 | [diff] [blame] | 296 | |
Benny Peake | 7336fa4 | 2017-08-17 19:30:35 -0700 | [diff] [blame] | 297 | for ap in list(self._aps.keys()): |
Joe Brennan | 88279af | 2017-02-15 18:11:53 -0800 | [diff] [blame] | 298 | try: |
| 299 | self.stop_ap(ap) |
| 300 | except dhcp_server.NoInterfaceError as e: |
| 301 | pass |
Benny Peake | 85f112a | 2016-10-07 19:41:49 -0700 | [diff] [blame] | 302 | |
| 303 | def close(self): |
| 304 | """Called to take down the entire access point. |
| 305 | |
| 306 | When called will stop all aps running on this host, shutdown the dhcp |
| 307 | server, and stop the ssh conneciton. |
Ang Li | 73697b3 | 2015-12-03 00:41:53 +0000 | [diff] [blame] | 308 | """ |
Joe Brennan | 88279af | 2017-02-15 18:11:53 -0800 | [diff] [blame] | 309 | |
| 310 | if self._aps: |
| 311 | self.stop_all_aps() |
markdr | 85a5e1a | 2017-07-17 14:11:34 -0700 | [diff] [blame] | 312 | self.ssh.close() |
Qi Jiang | c3e8785 | 2017-10-04 22:45:52 -0700 | [diff] [blame^] | 313 | |
| 314 | def generate_bridge_configs(self, channel, iface_lan=LAN_INTERFACE): |
| 315 | """Generate a list of configs for a bridge between LAN and WLAN. |
| 316 | |
| 317 | Args: |
| 318 | channel: the channel WLAN interface is brought up on |
| 319 | iface_lan: the LAN interface to bridge |
| 320 | Returns: |
| 321 | configs: tuple containing iface_wlan, iface_lan and bridge_ip |
| 322 | """ |
| 323 | |
| 324 | if channel < 15: |
| 325 | iface_wlan = _AP_2GHZ_INTERFACE |
| 326 | subnet_str = _AP_2GHZ_SUBNET_STR |
| 327 | else: |
| 328 | iface_wlan = _AP_5GHZ_INTERFACE |
| 329 | subnet_str = _AP_5GHZ_SUBNET_STR |
| 330 | |
| 331 | iface_lan = iface_lan |
| 332 | |
| 333 | a, b, c, d = subnet_str.strip('/24').split('.') |
| 334 | bridge_ip = "%s.%s.%s.%s" % (a, b, c, BRIDGE_IP_LAST) |
| 335 | |
| 336 | configs = (iface_wlan, iface_lan, bridge_ip) |
| 337 | |
| 338 | return configs |