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, 'predown' : 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(iface["configStrings"])
175 if iface["preup"] is not False and not iface.has_key("configStrings"):
176 fp.write(iface["preup"])
177 if iface["predown"] is not False and not iface.has_key("configStrings"):
178 fp.write(iface["predown"])
181 self.writeNameserverConfig()
183 def writeNameserverConfig(self):
184 fp = file('/etc/resolv.conf', 'w')
185 for nameserver in self.nameservers:
186 fp.write("nameserver %d.%d.%d.%d\n" % tuple(nameserver))
189 def loadNetworkConfig(self,iface,callback = None):
191 # parse the interfaces-file
193 fp = file('/etc/network/interfaces', 'r')
194 interfaces = fp.readlines()
197 print "[Network.py] interfaces - opening failed"
202 split = i.strip().split(' ')
203 if (split[0] == "iface"):
206 if (len(split) == 4 and split[3] == "dhcp"):
207 ifaces[currif]["dhcp"] = True
209 ifaces[currif]["dhcp"] = False
210 if (currif == iface): #read information only for available interfaces
211 if (split[0] == "address"):
212 ifaces[currif]["address"] = map(int, split[1].split('.'))
213 if self.ifaces[currif].has_key("ip"):
214 if self.ifaces[currif]["ip"] != ifaces[currif]["address"] and ifaces[currif]["dhcp"] == False:
215 self.ifaces[currif]["ip"] = map(int, split[1].split('.'))
216 if (split[0] == "netmask"):
217 ifaces[currif]["netmask"] = map(int, split[1].split('.'))
218 if self.ifaces[currif].has_key("netmask"):
219 if self.ifaces[currif]["netmask"] != ifaces[currif]["netmask"] and ifaces[currif]["dhcp"] == False:
220 self.ifaces[currif]["netmask"] = map(int, split[1].split('.'))
221 if (split[0] == "gateway"):
222 ifaces[currif]["gateway"] = map(int, split[1].split('.'))
223 if self.ifaces[currif].has_key("gateway"):
224 if self.ifaces[currif]["gateway"] != ifaces[currif]["gateway"] and ifaces[currif]["dhcp"] == False:
225 self.ifaces[currif]["gateway"] = map(int, split[1].split('.'))
226 if (split[0] == "pre-up"):
227 if self.ifaces[currif].has_key("preup"):
228 self.ifaces[currif]["preup"] = i
229 if (split[0] in ("pre-down","post-down")):
230 if self.ifaces[currif].has_key("predown"):
231 self.ifaces[currif]["predown"] = i
233 for ifacename, iface in ifaces.items():
234 if self.ifaces.has_key(ifacename):
235 self.ifaces[ifacename]["dhcp"] = iface["dhcp"]
237 if len(self.Console.appContainers) == 0:
238 # save configured interfacelist
239 self.configuredNetworkAdapters = self.configuredInterfaces
241 self.loadNameserverConfig()
242 print "read configured interface:", ifaces
243 print "self.ifaces after loading:", self.ifaces
244 self.config_ready = True
246 if callback is not None:
249 def loadNameserverConfig(self):
250 ipRegexp = "[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}"
251 nameserverPattern = re_compile("nameserver +" + ipRegexp)
252 ipPattern = re_compile(ipRegexp)
256 fp = file('/etc/resolv.conf', 'r')
257 resolv = fp.readlines()
259 self.nameservers = []
261 print "[Network.py] resolv.conf - opening failed"
264 if self.regExpMatch(nameserverPattern, line) is not None:
265 ip = self.regExpMatch(ipPattern, line)
267 self.nameservers.append(self.convertIP(ip))
269 print "nameservers:", self.nameservers
271 def deactivateNetworkConfig(self, callback = None):
272 if self.onRemoteRootFS():
273 if callback is not None:
276 self.deactivateConsole = Console()
278 self.commands.append("/etc/init.d/avahi-daemon stop")
279 for iface in self.ifaces.keys():
280 cmd = "ip addr flush " + iface
281 self.commands.append(cmd)
282 self.commands.append("/etc/init.d/networking stop")
283 self.commands.append("killall -9 udhcpc")
284 self.commands.append("rm /var/run/udhcpc*")
285 self.deactivateConsole.eBatch(self.commands, self.deactivateNetworkFinished, callback, debug=True)
287 def deactivateNetworkFinished(self,extra_args):
288 callback = extra_args
289 if len(self.deactivateConsole.appContainers) == 0:
290 if callback is not None:
293 def activateNetworkConfig(self, callback = None):
294 if self.onRemoteRootFS():
295 if callback is not None:
298 self.activateConsole = Console()
300 self.commands.append("/etc/init.d/networking start")
301 self.commands.append("/etc/init.d/avahi-daemon start")
302 self.activateConsole.eBatch(self.commands, self.activateNetworkFinished, callback, debug=True)
304 def activateNetworkFinished(self,extra_args):
305 callback = extra_args
306 if len(self.activateConsole.appContainers) == 0:
307 if callback is not None:
310 def getConfiguredAdapters(self):
311 return self.configuredNetworkAdapters
313 def getNumberOfAdapters(self):
314 return len(self.ifaces)
316 def getFriendlyAdapterName(self, x):
317 if x in self.friendlyNames.keys():
318 return self.friendlyNames.get(x, x)
320 self.friendlyNames[x] = self.getFriendlyAdapterNaming(x)
321 return self.friendlyNames.get(x, x) # when we have no friendly name, use adapter name
323 def getFriendlyAdapterNaming(self, iface):
324 if iface.startswith('eth'):
325 if iface not in self.lan_interfaces and len(self.lan_interfaces) == 0:
326 self.lan_interfaces.append(iface)
327 return _("LAN connection")
328 elif iface not in self.lan_interfaces and len(self.lan_interfaces) >= 1:
329 self.lan_interfaces.append(iface)
330 return _("LAN connection") + " " + str(len(self.lan_interfaces))
332 if iface not in self.wlan_interfaces and len(self.wlan_interfaces) == 0:
333 self.wlan_interfaces.append(iface)
334 return _("WLAN connection")
335 elif iface not in self.wlan_interfaces and len(self.wlan_interfaces) >= 1:
336 self.wlan_interfaces.append(iface)
337 return _("WLAN connection") + " " + str(len(self.wlan_interfaces))
339 def getFriendlyAdapterDescription(self, iface):
341 return _("Internal LAN adapter.")
343 classdir = "/sys/class/net/" + iface + "/device/"
344 driverdir = "/sys/class/net/" + iface + "/device/driver/"
345 if os_path.exists(classdir):
346 files = listdir(classdir)
347 if 'driver' in files:
348 if os_path.realpath(driverdir).endswith('ath_pci'):
349 return _("Atheros")+ " " + str(os_path.basename(os_path.realpath(driverdir))) + " " + _("WLAN adapter.")
350 elif os_path.realpath(driverdir).endswith('zd1211b'):
351 return _("Zydas")+ " " + str(os_path.basename(os_path.realpath(driverdir))) + " " + _("WLAN adapter.")
352 elif os_path.realpath(driverdir).endswith('rt73'):
353 return _("Ralink")+ " " + str(os_path.basename(os_path.realpath(driverdir))) + " " + _("WLAN adapter.")
354 elif os_path.realpath(driverdir).endswith('rt73usb'):
355 return _("Ralink")+ " " + str(os_path.basename(os_path.realpath(driverdir))) + " " + _("WLAN adapter.")
357 return str(os_path.basename(os_path.realpath(driverdir))) + " " + _("WLAN adapter.")
359 return _("Unknown network adapter.")
361 def getAdapterName(self, iface):
364 def getAdapterList(self):
365 return self.ifaces.keys()
367 def getAdapterAttribute(self, iface, attribute):
368 if self.ifaces.has_key(iface):
369 if self.ifaces[iface].has_key(attribute):
370 return self.ifaces[iface][attribute]
373 def setAdapterAttribute(self, iface, attribute, value):
374 print "setting for adapter", iface, "attribute", attribute, " to value", value
375 if self.ifaces.has_key(iface):
376 self.ifaces[iface][attribute] = value
378 def removeAdapterAttribute(self, iface, attribute):
379 if self.ifaces.has_key(iface):
380 if self.ifaces[iface].has_key(attribute):
381 del self.ifaces[iface][attribute]
383 def getNameserverList(self):
384 if len(self.nameservers) == 0:
385 return [[0, 0, 0, 0], [0, 0, 0, 0]]
387 return self.nameservers
389 def clearNameservers(self):
390 self.nameservers = []
392 def addNameserver(self, nameserver):
393 if nameserver not in self.nameservers:
394 self.nameservers.append(nameserver)
396 def removeNameserver(self, nameserver):
397 if nameserver in self.nameservers:
398 self.nameservers.remove(nameserver)
400 def changeNameserver(self, oldnameserver, newnameserver):
401 if oldnameserver in self.nameservers:
402 for i in range(len(self.nameservers)):
403 if self.nameservers[i] == oldnameserver:
404 self.nameservers[i] = newnameserver
406 def resetNetworkConfig(self, mode='lan', callback = None):
407 if self.onRemoteRootFS():
408 if callback is not None:
411 self.resetNetworkConsole = Console()
413 self.commands.append("/etc/init.d/avahi-daemon stop")
414 for iface in self.ifaces.keys():
415 cmd = "ip addr flush " + iface
416 self.commands.append(cmd)
417 self.commands.append("/etc/init.d/networking stop")
418 self.commands.append("killall -9 udhcpc")
419 self.commands.append("rm /var/run/udhcpc*")
420 self.resetNetworkConsole.eBatch(self.commands, self.resetNetworkFinishedCB, [mode, callback], debug=True)
422 def resetNetworkFinishedCB(self, extra_args):
423 (mode, callback) = extra_args
424 if len(self.resetNetworkConsole.appContainers) == 0:
425 self.writeDefaultNetworkConfig(mode, callback)
427 def writeDefaultNetworkConfig(self,mode='lan', callback = None):
428 fp = file('/etc/network/interfaces', 'w')
429 fp.write("# automatically generated by enigma 2\n# do NOT change manually!\n\n")
430 fp.write("auto lo\n")
431 fp.write("iface lo inet loopback\n\n")
433 fp.write("auto wlan0\n")
434 fp.write("iface wlan0 inet dhcp\n")
435 if mode == 'wlan-mpci':
436 fp.write("auto ath0\n")
437 fp.write("iface ath0 inet dhcp\n")
439 fp.write("auto eth0\n")
440 fp.write("iface eth0 inet dhcp\n")
444 self.resetNetworkConsole = Console()
447 self.commands.append("ifconfig eth0 down")
448 self.commands.append("ifconfig ath0 down")
449 self.commands.append("ifconfig wlan0 up")
450 if mode == 'wlan-mpci':
451 self.commands.append("ifconfig eth0 down")
452 self.commands.append("ifconfig wlan0 down")
453 self.commands.append("ifconfig ath0 up")
455 self.commands.append("ifconfig eth0 up")
456 self.commands.append("ifconfig wlan0 down")
457 self.commands.append("ifconfig ath0 down")
458 self.commands.append("/etc/init.d/avahi-daemon start")
459 self.resetNetworkConsole.eBatch(self.commands, self.resetNetworkFinished, [mode,callback], debug=True)
461 def resetNetworkFinished(self,extra_args):
462 (mode, callback) = extra_args
463 if len(self.resetNetworkConsole.appContainers) == 0:
464 if callback is not None:
467 def checkNetworkState(self,statecallback):
468 # www.dream-multimedia-tv.de, www.heise.de, www.google.de
469 self.NetworkState = 0
470 cmd1 = "ping -c 1 82.149.226.170"
471 cmd2 = "ping -c 1 193.99.144.85"
472 cmd3 = "ping -c 1 209.85.135.103"
473 self.PingConsole = Console()
474 self.PingConsole.ePopen(cmd1, self.checkNetworkStateFinished,statecallback)
475 self.PingConsole.ePopen(cmd2, self.checkNetworkStateFinished,statecallback)
476 self.PingConsole.ePopen(cmd3, self.checkNetworkStateFinished,statecallback)
478 def checkNetworkStateFinished(self, result, retval,extra_args):
479 (statecallback) = extra_args
480 if self.PingConsole is not None:
482 self.PingConsole = None
483 statecallback(self.NetworkState)
485 self.NetworkState += 1
486 if len(self.PingConsole.appContainers) == 0:
487 statecallback(self.NetworkState)
489 def restartNetwork(self,callback = None):
490 if self.onRemoteRootFS():
491 if callback is not None:
494 self.restartConsole = Console()
495 self.config_ready = False
498 self.commands.append("/etc/init.d/avahi-daemon stop")
499 for iface in self.ifaces.keys():
500 cmd = "ip addr flush " + iface
501 self.commands.append(cmd)
502 self.commands.append("/etc/init.d/networking stop")
503 self.commands.append("killall -9 udhcpc")
504 self.commands.append("rm /var/run/udhcpc*")
505 self.commands.append("/etc/init.d/networking start")
506 self.commands.append("/etc/init.d/avahi-daemon start")
507 self.restartConsole.eBatch(self.commands, self.restartNetworkFinished, callback, debug=True)
509 def restartNetworkFinished(self,extra_args):
510 ( callback ) = extra_args
511 if callback is not None:
514 def getLinkState(self,iface,callback):
515 cmd = self.ethtool_bin + " " + iface
516 self.LinkConsole = Console()
517 self.LinkConsole.ePopen(cmd, self.getLinkStateFinished,callback)
519 def getLinkStateFinished(self, result, retval,extra_args):
520 (callback) = extra_args
522 if self.LinkConsole is not None:
523 if len(self.LinkConsole.appContainers) == 0:
526 def stopPingConsole(self):
527 if self.PingConsole is not None:
528 if len(self.PingConsole.appContainers):
529 for name in self.PingConsole.appContainers.keys():
530 self.PingConsole.kill(name)
532 def stopLinkStateConsole(self):
533 if self.LinkConsole is not None:
534 if len(self.LinkConsole.appContainers):
535 for name in self.LinkConsole.appContainers.keys():
536 self.LinkConsole.kill(name)
538 def stopDNSConsole(self):
539 if self.DnsConsole is not None:
540 if len(self.DnsConsole.appContainers):
541 for name in self.DnsConsole.appContainers.keys():
542 self.DnsConsole.kill(name)
544 def stopRestartConsole(self):
545 if self.restartConsole is not None:
546 if len(self.restartConsole.appContainers):
547 for name in self.restartConsole.appContainers.keys():
548 self.restartConsole.kill(name)
550 def stopGetInterfacesConsole(self):
551 if self.Console is not None:
552 if len(self.Console.appContainers):
553 for name in self.Console.appContainers.keys():
554 self.Console.kill(name)
556 def stopDeactivateInterfaceConsole(self):
557 if self.deactivateInterfaceConsole is not None:
558 if len(self.deactivateInterfaceConsole.appContainers):
559 for name in self.deactivateInterfaceConsole.appContainers.keys():
560 self.deactivateInterfaceConsole.kill(name)
562 def checkforInterface(self,iface):
563 if self.getAdapterAttribute(iface, 'up') is True:
566 ret=system("ifconfig " + iface + " up")
567 system("ifconfig " + iface + " down")
573 def checkDNSLookup(self,statecallback):
574 cmd1 = "nslookup www.dream-multimedia-tv.de"
575 cmd2 = "nslookup www.heise.de"
576 cmd3 = "nslookup www.google.de"
577 self.DnsConsole = Console()
578 self.DnsConsole.ePopen(cmd1, self.checkDNSLookupFinished,statecallback)
579 self.DnsConsole.ePopen(cmd2, self.checkDNSLookupFinished,statecallback)
580 self.DnsConsole.ePopen(cmd3, self.checkDNSLookupFinished,statecallback)
582 def checkDNSLookupFinished(self, result, retval,extra_args):
583 (statecallback) = extra_args
584 if self.DnsConsole is not None:
586 self.DnsConsole = None
587 statecallback(self.DnsState)
590 if len(self.DnsConsole.appContainers) == 0:
591 statecallback(self.DnsState)
593 def deactivateInterface(self,iface,callback = None):
594 if self.onRemoteRootFS():
595 if callback is not None:
598 self.deactivateInterfaceConsole = Console()
600 cmd1 = "ip addr flush " + iface
601 cmd2 = "ifconfig " + iface + " down"
602 self.commands.append(cmd1)
603 self.commands.append(cmd2)
604 self.deactivateInterfaceConsole.eBatch(self.commands, self.deactivateInterfaceFinished, callback, debug=True)
606 def deactivateInterfaceFinished(self,extra_args):
607 callback = extra_args
608 if self.deactivateInterfaceConsole:
609 if len(self.deactivateInterfaceConsole.appContainers) == 0:
610 if callback is not None:
613 def detectWlanModule(self, iface = None):
614 self.wlanmodule = None
615 classdir = "/sys/class/net/" + iface + "/device/"
616 driverdir = "/sys/class/net/" + iface + "/device/driver/"
617 if os_path.exists(classdir):
618 classfiles = listdir(classdir)
620 nl80211_found = False
624 if x.startswith('ieee80211:'):
627 if driver_found and nl80211_found:
628 #print about.getKernelVersionString()
629 self.wlanmodule = "nl80211"
631 if driver_found and not nl80211_found:
632 driverfiles = listdir(driverdir)
633 if os_path.realpath(driverdir).endswith('ath_pci'):
634 if len(driverfiles) >= 1:
635 self.wlanmodule = 'madwifi'
636 if os_path.realpath(driverdir).endswith('rt73'):
637 if len(driverfiles) == 2 or len(driverfiles) == 5:
638 self.wlanmodule = 'ralink'
639 if os_path.realpath(driverdir).endswith('zd1211b'):
640 if len(driverfiles) == 1 or len(driverfiles) == 5:
641 self.wlanmodule = 'zydas'
642 if self.wlanmodule is None:
643 self.wlanmodule = "wext"
644 print 'Using "%s" as wpa-supplicant driver' % (self.wlanmodule)
645 return self.wlanmodule
647 def calc_netmask(self,nmask):
648 from struct import pack, unpack
649 from socket import inet_ntoa, inet_aton
652 cidr_range = range(0, 32)
654 if cidr not in cidr_range:
655 print 'cidr invalid: %d' % cidr
658 nm = ((1L<<cidr)-1)<<(32-cidr)
659 netmask = str(inet_ntoa(pack('>L', nm)))
662 def msgPlugins(self):
663 if self.config_ready is not None:
664 for p in plugins.getPlugins(PluginDescriptor.WHERE_NETWORKCONFIG_READ):
665 p(reason=self.config_ready)