1 from os import system, popen, path as os_path, listdir
2 from re import compile as re_compile, search as re_search
4 from enigma import eConsoleAppContainer
5 from Components.Console import Console
6 from Components.PluginComponent import plugins
7 from Components.About import about
8 from Plugins.Plugin import PluginDescriptor
13 self.configuredInterfaces = []
14 self.configuredNetworkAdapters = []
18 self.ethtool_bin = "ethtool"
19 self.container = eConsoleAppContainer()
20 self.Console = Console()
21 self.LinkConsole = Console()
22 self.restartConsole = Console()
23 self.deactivateConsole = Console()
24 self.deactivateInterfaceConsole = Console()
25 self.activateConsole = Console()
26 self.resetNetworkConsole = Console()
27 self.DnsConsole = Console()
28 self.PingConsole = Console()
29 self.config_ready = None
30 self.friendlyNames = {}
31 self.lan_interfaces = []
32 self.wlan_interfaces = []
35 def onRemoteRootFS(self):
36 fp = file('/proc/mounts', 'r')
37 mounts = fp.readlines()
40 parts = line.strip().split(' ')
41 if parts[1] == '/' and (parts[2] == 'nfs' or parts[2] == 'smbfs'):
45 def getInterfaces(self, callback = None):
46 devicesPattern = re_compile('[a-z]+[0-9]+')
47 self.configuredInterfaces = []
48 fp = file('/proc/net/dev', 'r')
49 result = fp.readlines()
53 device = devicesPattern.search(line).group()
54 if device in ('wifi0', 'wmaster0'):
56 self.getDataForInterface(device, callback)
57 except AttributeError:
59 #print "self.ifaces:", self.ifaces
60 #self.writeNetworkConfig()
66 def regExpMatch(self, pattern, string):
70 return pattern.search(string).group()
71 except AttributeError:
74 # helper function to convert ips from a sring to a list of ints
75 def convertIP(self, ip):
82 def getDataForInterface(self, iface,callback):
83 #get ip out of ip addr, as avahi sometimes overrides it in ifconfig.
85 self.Console = Console()
87 self.Console.ePopen(cmd, self.IPaddrFinished, [iface,callback])
89 def IPaddrFinished(self, result, retval, extra_args):
90 (iface, callback ) = extra_args
91 data = { 'up': False, 'dhcp': False, 'preup' : False, 'postdown' : False }
92 globalIPpattern = re_compile("scope global")
93 ipRegexp = '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'
94 netRegexp = '[0-9]{1,2}'
95 macRegexp = '[0-9]{2}\:[0-9]{2}\:[0-9]{2}\:[a-z0-9]{2}\:[a-z0-9]{2}\:[a-z0-9]{2}'
96 ipLinePattern = re_compile('inet ' + ipRegexp + '/')
97 ipPattern = re_compile(ipRegexp)
98 netmaskLinePattern = re_compile('/' + netRegexp)
99 netmaskPattern = re_compile(netRegexp)
100 bcastLinePattern = re_compile(' brd ' + ipRegexp)
101 upPattern = re_compile('UP')
102 macPattern = re_compile('[0-9]{2}\:[0-9]{2}\:[0-9]{2}\:[a-z0-9]{2}\:[a-z0-9]{2}\:[a-z0-9]{2}')
103 macLinePattern = re_compile('link/ether ' + macRegexp)
105 for line in result.splitlines():
106 split = line.strip().split(' ',2)
107 if (split[1][:-1] == iface):
108 up = self.regExpMatch(upPattern, split[2])
109 mac = self.regExpMatch(macPattern, self.regExpMatch(macLinePattern, split[2]))
112 if iface is not 'lo':
113 self.configuredInterfaces.append(iface)
116 if (split[1] == iface):
117 if re_search(globalIPpattern, split[2]):
118 ip = self.regExpMatch(ipPattern, self.regExpMatch(ipLinePattern, split[2]))
119 netmask = self.calc_netmask(self.regExpMatch(netmaskPattern, self.regExpMatch(netmaskLinePattern, split[2])))
120 bcast = self.regExpMatch(ipPattern, self.regExpMatch(bcastLinePattern, split[2]))
122 data['ip'] = self.convertIP(ip)
123 if netmask is not None:
124 data['netmask'] = self.convertIP(netmask)
125 if bcast is not None:
126 data['bcast'] = self.convertIP(bcast)
128 if not data.has_key('ip'):
130 data['ip'] = [0, 0, 0, 0]
131 data['netmask'] = [0, 0, 0, 0]
132 data['gateway'] = [0, 0, 0, 0]
134 cmd = "route -n | grep " + iface
135 self.Console.ePopen(cmd,self.routeFinished, [iface, data, callback])
137 def routeFinished(self, result, retval, extra_args):
138 (iface, data, callback) = extra_args
139 ipRegexp = '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'
140 ipPattern = re_compile(ipRegexp)
141 ipLinePattern = re_compile(ipRegexp)
143 for line in result.splitlines():
145 if line[0:7] == "0.0.0.0":
146 gateway = self.regExpMatch(ipPattern, line[16:31])
147 if gateway is not None:
148 data['gateway'] = self.convertIP(gateway)
150 self.ifaces[iface] = data
151 self.loadNetworkConfig(iface,callback)
153 def writeNetworkConfig(self):
154 self.configuredInterfaces = []
155 fp = file('/etc/network/interfaces', 'w')
156 fp.write("# automatically generated by enigma 2\n# do NOT change manually!\n\n")
157 fp.write("auto lo\n")
158 fp.write("iface lo inet loopback\n\n")
159 for ifacename, iface in self.ifaces.items():
160 if iface['up'] == True:
161 fp.write("auto " + ifacename + "\n")
162 self.configuredInterfaces.append(ifacename)
163 if iface['dhcp'] == True:
164 fp.write("iface "+ ifacename +" inet dhcp\n")
165 if iface['dhcp'] == False:
166 fp.write("iface "+ ifacename +" inet static\n")
167 if iface.has_key('ip'):
168 print tuple(iface['ip'])
169 fp.write(" address %d.%d.%d.%d\n" % tuple(iface['ip']))
170 fp.write(" netmask %d.%d.%d.%d\n" % tuple(iface['netmask']))
171 if iface.has_key('gateway'):
172 fp.write(" gateway %d.%d.%d.%d\n" % tuple(iface['gateway']))
173 if iface.has_key("configStrings"):
174 fp.write("\n" + iface["configStrings"] + "\n")
175 if iface["preup"] is not False and not iface.has_key("configStrings"):
176 fp.write(iface["preup"])
177 fp.write(iface["postdown"])
180 self.writeNameserverConfig()
182 def writeNameserverConfig(self):
183 fp = file('/etc/resolv.conf', 'w')
184 for nameserver in self.nameservers:
185 fp.write("nameserver %d.%d.%d.%d\n" % tuple(nameserver))
188 def loadNetworkConfig(self,iface,callback = None):
190 # parse the interfaces-file
192 fp = file('/etc/network/interfaces', 'r')
193 interfaces = fp.readlines()
196 print "[Network.py] interfaces - opening failed"
201 split = i.strip().split(' ')
202 if (split[0] == "iface"):
205 if (len(split) == 4 and split[3] == "dhcp"):
206 ifaces[currif]["dhcp"] = True
208 ifaces[currif]["dhcp"] = False
209 if (currif == iface): #read information only for available interfaces
210 if (split[0] == "address"):
211 ifaces[currif]["address"] = map(int, split[1].split('.'))
212 if self.ifaces[currif].has_key("ip"):
213 if self.ifaces[currif]["ip"] != ifaces[currif]["address"] and ifaces[currif]["dhcp"] == False:
214 self.ifaces[currif]["ip"] = map(int, split[1].split('.'))
215 if (split[0] == "netmask"):
216 ifaces[currif]["netmask"] = map(int, split[1].split('.'))
217 if self.ifaces[currif].has_key("netmask"):
218 if self.ifaces[currif]["netmask"] != ifaces[currif]["netmask"] and ifaces[currif]["dhcp"] == False:
219 self.ifaces[currif]["netmask"] = map(int, split[1].split('.'))
220 if (split[0] == "gateway"):
221 ifaces[currif]["gateway"] = map(int, split[1].split('.'))
222 if self.ifaces[currif].has_key("gateway"):
223 if self.ifaces[currif]["gateway"] != ifaces[currif]["gateway"] and ifaces[currif]["dhcp"] == False:
224 self.ifaces[currif]["gateway"] = map(int, split[1].split('.'))
225 if (split[0] == "pre-up"):
226 if self.ifaces[currif].has_key("preup"):
227 self.ifaces[currif]["preup"] = i
228 if (split[0] == "post-down"):
229 if self.ifaces[currif].has_key("postdown"):
230 self.ifaces[currif]["postdown"] = i
232 for ifacename, iface in ifaces.items():
233 if self.ifaces.has_key(ifacename):
234 self.ifaces[ifacename]["dhcp"] = iface["dhcp"]
236 if len(self.Console.appContainers) == 0:
237 # save configured interfacelist
238 self.configuredNetworkAdapters = self.configuredInterfaces
240 self.loadNameserverConfig()
241 print "read configured interface:", ifaces
242 print "self.ifaces after loading:", self.ifaces
243 self.config_ready = True
245 if callback is not None:
248 def loadNameserverConfig(self):
249 ipRegexp = "[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}"
250 nameserverPattern = re_compile("nameserver +" + ipRegexp)
251 ipPattern = re_compile(ipRegexp)
255 fp = file('/etc/resolv.conf', 'r')
256 resolv = fp.readlines()
258 self.nameservers = []
260 print "[Network.py] resolv.conf - opening failed"
263 if self.regExpMatch(nameserverPattern, line) is not None:
264 ip = self.regExpMatch(ipPattern, line)
266 self.nameservers.append(self.convertIP(ip))
268 print "nameservers:", self.nameservers
270 def deactivateNetworkConfig(self, callback = None):
271 if self.onRemoteRootFS():
272 if callback is not None:
275 self.deactivateConsole = Console()
277 self.commands.append("/etc/init.d/avahi-daemon stop")
278 for iface in self.ifaces.keys():
279 cmd = "ip addr flush " + iface
280 self.commands.append(cmd)
281 self.commands.append("/etc/init.d/networking stop")
282 self.commands.append("killall -9 udhcpc")
283 self.commands.append("rm /var/run/udhcpc*")
284 self.deactivateConsole.eBatch(self.commands, self.deactivateNetworkFinished, callback, debug=True)
286 def deactivateNetworkFinished(self,extra_args):
287 callback = extra_args
288 if len(self.deactivateConsole.appContainers) == 0:
289 if callback is not None:
292 def activateNetworkConfig(self, callback = None):
293 if self.onRemoteRootFS():
294 if callback is not None:
297 self.activateConsole = Console()
299 self.commands.append("/etc/init.d/networking start")
300 self.commands.append("/etc/init.d/avahi-daemon start")
301 self.activateConsole.eBatch(self.commands, self.activateNetworkFinished, callback, debug=True)
303 def activateNetworkFinished(self,extra_args):
304 callback = extra_args
305 if len(self.activateConsole.appContainers) == 0:
306 if callback is not None:
309 def getConfiguredAdapters(self):
310 return self.configuredNetworkAdapters
312 def getNumberOfAdapters(self):
313 return len(self.ifaces)
315 def getFriendlyAdapterName(self, x):
316 if x in self.friendlyNames.keys():
317 return self.friendlyNames.get(x, x)
319 self.friendlyNames[x] = self.getFriendlyAdapterNaming(x)
320 return self.friendlyNames.get(x, x) # when we have no friendly name, use adapter name
322 def getFriendlyAdapterNaming(self, iface):
323 if iface.startswith('eth'):
324 if iface not in self.lan_interfaces and len(self.lan_interfaces) == 0:
325 self.lan_interfaces.append(iface)
326 return _("LAN connection")
327 elif iface not in self.lan_interfaces and len(self.lan_interfaces) >= 1:
328 self.lan_interfaces.append(iface)
329 return _("LAN connection") + " " + str(len(self.lan_interfaces))
331 if iface not in self.wlan_interfaces and len(self.wlan_interfaces) == 0:
332 self.wlan_interfaces.append(iface)
333 return _("WLAN connection")
334 elif iface not in self.wlan_interfaces and len(self.wlan_interfaces) >= 1:
335 self.wlan_interfaces.append(iface)
336 return _("WLAN connection") + " " + str(len(self.wlan_interfaces))
338 def getFriendlyAdapterDescription(self, iface):
340 return _("Internal LAN adapter.")
342 classdir = "/sys/class/net/" + iface + "/device/"
343 driverdir = "/sys/class/net/" + iface + "/device/driver/"
344 if os_path.exists(classdir):
345 files = listdir(classdir)
346 if 'driver' in files:
347 if os_path.realpath(driverdir).endswith('ath_pci'):
348 return _("Atheros")+ " " + str(os_path.basename(os_path.realpath(driverdir))) + " " + _("WLAN adapter.")
349 elif os_path.realpath(driverdir).endswith('zd1211b'):
350 return _("Zydas")+ " " + str(os_path.basename(os_path.realpath(driverdir))) + " " + _("WLAN adapter.")
351 elif os_path.realpath(driverdir).endswith('rt73'):
352 return _("Ralink")+ " " + str(os_path.basename(os_path.realpath(driverdir))) + " " + _("WLAN adapter.")
353 elif os_path.realpath(driverdir).endswith('rt73usb'):
354 return _("Ralink")+ " " + str(os_path.basename(os_path.realpath(driverdir))) + " " + _("WLAN adapter.")
356 return str(os_path.basename(os_path.realpath(driverdir))) + " " + _("WLAN adapter.")
358 return _("Unknown network adapter.")
360 def getAdapterName(self, iface):
363 def getAdapterList(self):
364 return self.ifaces.keys()
366 def getAdapterAttribute(self, iface, attribute):
367 if self.ifaces.has_key(iface):
368 if self.ifaces[iface].has_key(attribute):
369 return self.ifaces[iface][attribute]
372 def setAdapterAttribute(self, iface, attribute, value):
373 print "setting for adapter", iface, "attribute", attribute, " to value", value
374 if self.ifaces.has_key(iface):
375 self.ifaces[iface][attribute] = value
377 def removeAdapterAttribute(self, iface, attribute):
378 if self.ifaces.has_key(iface):
379 if self.ifaces[iface].has_key(attribute):
380 del self.ifaces[iface][attribute]
382 def getNameserverList(self):
383 if len(self.nameservers) == 0:
384 return [[0, 0, 0, 0], [0, 0, 0, 0]]
386 return self.nameservers
388 def clearNameservers(self):
389 self.nameservers = []
391 def addNameserver(self, nameserver):
392 if nameserver not in self.nameservers:
393 self.nameservers.append(nameserver)
395 def removeNameserver(self, nameserver):
396 if nameserver in self.nameservers:
397 self.nameservers.remove(nameserver)
399 def changeNameserver(self, oldnameserver, newnameserver):
400 if oldnameserver in self.nameservers:
401 for i in range(len(self.nameservers)):
402 if self.nameservers[i] == oldnameserver:
403 self.nameservers[i] = newnameserver
405 def resetNetworkConfig(self, mode='lan', callback = None):
406 if self.onRemoteRootFS():
407 if callback is not None:
410 self.resetNetworkConsole = Console()
412 self.commands.append("/etc/init.d/avahi-daemon stop")
413 for iface in self.ifaces.keys():
414 cmd = "ip addr flush " + iface
415 self.commands.append(cmd)
416 self.commands.append("/etc/init.d/networking stop")
417 self.commands.append("killall -9 udhcpc")
418 self.commands.append("rm /var/run/udhcpc*")
419 self.resetNetworkConsole.eBatch(self.commands, self.resetNetworkFinishedCB, [mode, callback], debug=True)
421 def resetNetworkFinishedCB(self, extra_args):
422 (mode, callback) = extra_args
423 if len(self.resetNetworkConsole.appContainers) == 0:
424 self.writeDefaultNetworkConfig(mode, callback)
426 def writeDefaultNetworkConfig(self,mode='lan', callback = None):
427 fp = file('/etc/network/interfaces', 'w')
428 fp.write("# automatically generated by enigma 2\n# do NOT change manually!\n\n")
429 fp.write("auto lo\n")
430 fp.write("iface lo inet loopback\n\n")
432 fp.write("auto wlan0\n")
433 fp.write("iface wlan0 inet dhcp\n")
434 if mode == 'wlan-mpci':
435 fp.write("auto ath0\n")
436 fp.write("iface ath0 inet dhcp\n")
438 fp.write("auto eth0\n")
439 fp.write("iface eth0 inet dhcp\n")
443 self.resetNetworkConsole = Console()
446 self.commands.append("ifconfig eth0 down")
447 self.commands.append("ifconfig ath0 down")
448 self.commands.append("ifconfig wlan0 up")
449 if mode == 'wlan-mpci':
450 self.commands.append("ifconfig eth0 down")
451 self.commands.append("ifconfig wlan0 down")
452 self.commands.append("ifconfig ath0 up")
454 self.commands.append("ifconfig eth0 up")
455 self.commands.append("ifconfig wlan0 down")
456 self.commands.append("ifconfig ath0 down")
457 self.commands.append("/etc/init.d/avahi-daemon start")
458 self.resetNetworkConsole.eBatch(self.commands, self.resetNetworkFinished, [mode,callback], debug=True)
460 def resetNetworkFinished(self,extra_args):
461 (mode, callback) = extra_args
462 if len(self.resetNetworkConsole.appContainers) == 0:
463 if callback is not None:
466 def checkNetworkState(self,statecallback):
467 # www.dream-multimedia-tv.de, www.heise.de, www.google.de
468 self.NetworkState = 0
469 cmd1 = "ping -c 1 82.149.226.170"
470 cmd2 = "ping -c 1 193.99.144.85"
471 cmd3 = "ping -c 1 209.85.135.103"
472 self.PingConsole = Console()
473 self.PingConsole.ePopen(cmd1, self.checkNetworkStateFinished,statecallback)
474 self.PingConsole.ePopen(cmd2, self.checkNetworkStateFinished,statecallback)
475 self.PingConsole.ePopen(cmd3, self.checkNetworkStateFinished,statecallback)
477 def checkNetworkStateFinished(self, result, retval,extra_args):
478 (statecallback) = extra_args
479 if self.PingConsole is not None:
481 self.PingConsole = None
482 statecallback(self.NetworkState)
484 self.NetworkState += 1
485 if len(self.PingConsole.appContainers) == 0:
486 statecallback(self.NetworkState)
488 def restartNetwork(self,callback = None):
489 if self.onRemoteRootFS():
490 if callback is not None:
493 self.restartConsole = Console()
494 self.config_ready = False
497 self.commands.append("/etc/init.d/avahi-daemon stop")
498 for iface in self.ifaces.keys():
499 cmd = "ip addr flush " + iface
500 self.commands.append(cmd)
501 self.commands.append("/etc/init.d/networking stop")
502 self.commands.append("killall -9 udhcpc")
503 self.commands.append("rm /var/run/udhcpc*")
504 self.commands.append("/etc/init.d/networking start")
505 self.commands.append("/etc/init.d/avahi-daemon start")
506 self.restartConsole.eBatch(self.commands, self.restartNetworkFinished, callback, debug=True)
508 def restartNetworkFinished(self,extra_args):
509 ( callback ) = extra_args
510 if callback is not None:
513 def getLinkState(self,iface,callback):
514 cmd = self.ethtool_bin + " " + iface
515 self.LinkConsole = Console()
516 self.LinkConsole.ePopen(cmd, self.getLinkStateFinished,callback)
518 def getLinkStateFinished(self, result, retval,extra_args):
519 (callback) = extra_args
521 if self.LinkConsole is not None:
522 if len(self.LinkConsole.appContainers) == 0:
525 def stopPingConsole(self):
526 if self.PingConsole is not None:
527 if len(self.PingConsole.appContainers):
528 for name in self.PingConsole.appContainers.keys():
529 self.PingConsole.kill(name)
531 def stopLinkStateConsole(self):
532 if self.LinkConsole is not None:
533 if len(self.LinkConsole.appContainers):
534 for name in self.LinkConsole.appContainers.keys():
535 self.LinkConsole.kill(name)
537 def stopDNSConsole(self):
538 if self.DnsConsole is not None:
539 if len(self.DnsConsole.appContainers):
540 for name in self.DnsConsole.appContainers.keys():
541 self.DnsConsole.kill(name)
543 def stopRestartConsole(self):
544 if self.restartConsole is not None:
545 if len(self.restartConsole.appContainers):
546 for name in self.restartConsole.appContainers.keys():
547 self.restartConsole.kill(name)
549 def stopGetInterfacesConsole(self):
550 if self.Console is not None:
551 if len(self.Console.appContainers):
552 for name in self.Console.appContainers.keys():
553 self.Console.kill(name)
555 def stopDeactivateInterfaceConsole(self):
556 if self.deactivateInterfaceConsole is not None:
557 if len(self.deactivateInterfaceConsole.appContainers):
558 for name in self.deactivateInterfaceConsole.appContainers.keys():
559 self.deactivateInterfaceConsole.kill(name)
561 def checkforInterface(self,iface):
562 if self.getAdapterAttribute(iface, 'up') is True:
565 ret=system("ifconfig " + iface + " up")
566 system("ifconfig " + iface + " down")
572 def checkDNSLookup(self,statecallback):
573 cmd1 = "nslookup www.dream-multimedia-tv.de"
574 cmd2 = "nslookup www.heise.de"
575 cmd3 = "nslookup www.google.de"
576 self.DnsConsole = Console()
577 self.DnsConsole.ePopen(cmd1, self.checkDNSLookupFinished,statecallback)
578 self.DnsConsole.ePopen(cmd2, self.checkDNSLookupFinished,statecallback)
579 self.DnsConsole.ePopen(cmd3, self.checkDNSLookupFinished,statecallback)
581 def checkDNSLookupFinished(self, result, retval,extra_args):
582 (statecallback) = extra_args
583 if self.DnsConsole is not None:
585 self.DnsConsole = None
586 statecallback(self.DnsState)
589 if len(self.DnsConsole.appContainers) == 0:
590 statecallback(self.DnsState)
592 def deactivateInterface(self,iface,callback = None):
593 if self.onRemoteRootFS():
594 if callback is not None:
597 self.deactivateInterfaceConsole = Console()
599 cmd1 = "ip addr flush " + iface
600 cmd2 = "ifconfig " + iface + " down"
601 self.commands.append(cmd1)
602 self.commands.append(cmd2)
603 self.deactivateInterfaceConsole.eBatch(self.commands, self.deactivateInterfaceFinished, callback, debug=True)
605 def deactivateInterfaceFinished(self,extra_args):
606 callback = extra_args
607 if self.deactivateInterfaceConsole:
608 if len(self.deactivateInterfaceConsole.appContainers) == 0:
609 if callback is not None:
612 def detectWlanModule(self, iface = None):
613 self.wlanmodule = None
614 classdir = "/sys/class/net/" + iface + "/device/"
615 driverdir = "/sys/class/net/" + iface + "/device/driver/"
616 if os_path.exists(classdir):
617 classfiles = listdir(classdir)
619 nl80211_found = False
623 if x.startswith('ieee80211:'):
626 if driver_found and nl80211_found:
627 #print about.getKernelVersionString()
628 self.wlanmodule = "nl80211"
630 if driver_found and not nl80211_found:
631 driverfiles = listdir(driverdir)
632 if os_path.realpath(driverdir).endswith('ath_pci'):
633 if len(driverfiles) >= 1:
634 self.wlanmodule = 'madwifi'
635 if os_path.realpath(driverdir).endswith('rt73'):
636 if len(driverfiles) == 2 or len(driverfiles) == 5:
637 self.wlanmodule = 'ralink'
638 if os_path.realpath(driverdir).endswith('zd1211b'):
639 if len(driverfiles) == 1 or len(driverfiles) == 5:
640 self.wlanmodule = 'zydas'
641 if self.wlanmodule is None:
642 self.wlanmodule = "wext"
643 print 'Using "%s" as wpa-supplicant driver' % (self.wlanmodule)
644 return self.wlanmodule
646 def calc_netmask(self,nmask):
647 from struct import pack, unpack
648 from socket import inet_ntoa, inet_aton
651 cidr_range = range(0, 32)
653 if cidr not in cidr_range:
654 print 'cidr invalid: %d' % cidr
657 nm = ((1L<<cidr)-1)<<(32-cidr)
658 netmask = str(inet_ntoa(pack('>L', nm)))
661 def msgPlugins(self):
662 if self.config_ready is not None:
663 for p in plugins.getPlugins(PluginDescriptor.WHERE_NETWORKCONFIG_READ):
664 p(reason=self.config_ready)