check upnp redirects and external ip in a looping call
-add upnp setup success/fail analytics event
This commit is contained in:
parent
6d8ab97ca9
commit
213bdac55f
3 changed files with 91 additions and 23 deletions
|
@ -19,6 +19,7 @@ CLAIM_ACTION = 'Claim Action' # publish/create/update/abandon
|
||||||
NEW_CHANNEL = 'New Channel'
|
NEW_CHANNEL = 'New Channel'
|
||||||
CREDITS_SENT = 'Credits Sent'
|
CREDITS_SENT = 'Credits Sent'
|
||||||
NEW_DOWNLOAD_STAT = 'Download'
|
NEW_DOWNLOAD_STAT = 'Download'
|
||||||
|
UPNP_SETUP = "UPnP Setup"
|
||||||
|
|
||||||
BLOB_BYTES_UPLOADED = 'Blob Bytes Uploaded'
|
BLOB_BYTES_UPLOADED = 'Blob Bytes Uploaded'
|
||||||
|
|
||||||
|
@ -69,6 +70,14 @@ class Manager:
|
||||||
'timestamp': utils.isonow(),
|
'timestamp': utils.isonow(),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
def send_upnp_setup_success_fail(self, success, status):
|
||||||
|
self.analytics_api.track(
|
||||||
|
self._event(UPNP_SETUP, {
|
||||||
|
'success': success,
|
||||||
|
'status': status,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
def send_server_startup(self):
|
def send_server_startup(self):
|
||||||
self.analytics_api.track(self._event(SERVER_STARTUP))
|
self.analytics_api.track(self._event(SERVER_STARTUP))
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,10 @@ import math
|
||||||
import binascii
|
import binascii
|
||||||
from hashlib import sha256
|
from hashlib import sha256
|
||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
from twisted.internet import defer, threads, reactor, error
|
from twisted.internet import defer, threads, reactor, error, task
|
||||||
import lbryschema
|
import lbryschema
|
||||||
from aioupnp.upnp import UPnP
|
from aioupnp.upnp import UPnP
|
||||||
|
from aioupnp.fault import UPnPError
|
||||||
from lbrynet import conf
|
from lbrynet import conf
|
||||||
from lbrynet.core.utils import DeferredDict
|
from lbrynet.core.utils import DeferredDict
|
||||||
from lbrynet.core.PaymentRateManager import OnlyFreePaymentsManager
|
from lbrynet.core.PaymentRateManager import OnlyFreePaymentsManager
|
||||||
|
@ -684,6 +685,8 @@ class UPnPComponent(Component):
|
||||||
self.upnp = None
|
self.upnp = None
|
||||||
self.upnp_redirects = {}
|
self.upnp_redirects = {}
|
||||||
self.external_ip = None
|
self.external_ip = None
|
||||||
|
self._maintain_redirects_lc = task.LoopingCall(self._maintain_redirects)
|
||||||
|
self._maintain_redirects_lc.clock = self.component_manager.reactor
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def component(self):
|
def component(self):
|
||||||
|
@ -697,41 +700,91 @@ class UPnPComponent(Component):
|
||||||
})
|
})
|
||||||
self.upnp_redirects.update(upnp_redirects)
|
self.upnp_redirects.update(upnp_redirects)
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def _maintain_redirects(self):
|
||||||
|
# setup the gateway if necessary
|
||||||
|
if not self.upnp:
|
||||||
|
try:
|
||||||
|
self.upnp = yield from_future(UPnP.discover())
|
||||||
|
log.info("found upnp gateway: %s", self.upnp.gateway.manufacturer_string)
|
||||||
|
except Exception as err:
|
||||||
|
log.warning("upnp discovery failed: %s", err)
|
||||||
|
return
|
||||||
|
|
||||||
|
# update the external ip
|
||||||
|
try:
|
||||||
|
external_ip = yield from_future(self.upnp.get_external_ip())
|
||||||
|
if external_ip == "0.0.0.0":
|
||||||
|
log.warning("upnp doesn't know the external ip address (returned 0.0.0.0), using fallback")
|
||||||
|
external_ip = CS.get_external_ip()
|
||||||
|
if self.external_ip and self.external_ip != external_ip:
|
||||||
|
log.info("external ip changed from %s to %s", self.external_ip, external_ip)
|
||||||
|
elif not self.external_ip:
|
||||||
|
log.info("got external ip: %s", external_ip)
|
||||||
|
self.external_ip = external_ip
|
||||||
|
except (asyncio.TimeoutError, UPnPError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
if not self.upnp_redirects: # setup missing redirects
|
||||||
|
try:
|
||||||
|
upnp_redirects = yield DeferredDict({
|
||||||
|
"UDP": from_future(self.upnp.get_next_mapping(self._int_dht_node_port, "UDP", "LBRY DHT port")),
|
||||||
|
"TCP": from_future(self.upnp.get_next_mapping(self._int_peer_port, "TCP", "LBRY peer port"))
|
||||||
|
})
|
||||||
|
self.upnp_redirects.update(upnp_redirects)
|
||||||
|
except (asyncio.TimeoutError, UPnPError):
|
||||||
|
self.upnp = None
|
||||||
|
return self._maintain_redirects()
|
||||||
|
else: # check existing redirects are still active
|
||||||
|
found = set()
|
||||||
|
mappings = yield from_future(self.upnp.get_redirects())
|
||||||
|
for mapping in mappings:
|
||||||
|
proto = mapping['NewProtocol']
|
||||||
|
if proto in self.upnp_redirects and mapping['NewExternalPort'] == self.upnp_redirects[proto]:
|
||||||
|
if mapping['NewInternalClient'] == self.upnp.lan_address:
|
||||||
|
found.add(proto)
|
||||||
|
if 'UDP' not in found:
|
||||||
|
try:
|
||||||
|
udp_port = yield from_future(
|
||||||
|
self.upnp.get_next_mapping(self._int_dht_node_port, "UDP", "LBRY DHT port")
|
||||||
|
)
|
||||||
|
self.upnp_redirects['UDP'] = udp_port
|
||||||
|
log.info("refreshed upnp redirect for dht port: %i", udp_port)
|
||||||
|
except (asyncio.TimeoutError, UPnPError):
|
||||||
|
del self.upnp_redirects['UDP']
|
||||||
|
if 'TCP' not in found:
|
||||||
|
try:
|
||||||
|
tcp_port = yield from_future(
|
||||||
|
self.upnp.get_next_mapping(self._int_peer_port, "TCP", "LBRY peer port")
|
||||||
|
)
|
||||||
|
self.upnp_redirects['TCP'] = tcp_port
|
||||||
|
log.info("refreshed upnp redirect for peer port: %i", tcp_port)
|
||||||
|
except (asyncio.TimeoutError, UPnPError):
|
||||||
|
del self.upnp_redirects['TCP']
|
||||||
|
if 'TCP' in self.upnp_redirects and 'UDP' in self.upnp_redirects:
|
||||||
|
log.debug("upnp redirects are still active")
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def start(self):
|
def start(self):
|
||||||
if not self.use_upnp:
|
if not self.use_upnp:
|
||||||
self.external_ip = CS.get_external_ip()
|
self.external_ip = CS.get_external_ip()
|
||||||
return
|
return
|
||||||
try:
|
success = False
|
||||||
self.upnp = yield from_future(UPnP.discover())
|
yield self._maintain_redirects()
|
||||||
log.info("found upnp gateway")
|
|
||||||
found = True
|
|
||||||
except Exception as err:
|
|
||||||
log.warning("upnp discovery failed: %s", err)
|
|
||||||
found = False
|
|
||||||
if found:
|
|
||||||
try:
|
|
||||||
self.external_ip = yield from_future(self.upnp.get_external_ip())
|
|
||||||
if self.external_ip == "0.0.0.0":
|
|
||||||
log.warning("upnp doesn't know the external ip address (returned 0.0.0.0), using fallback")
|
|
||||||
self.external_ip = CS.get_external_ip()
|
|
||||||
else:
|
|
||||||
log.info("got external ip from upnp: %s", self.external_ip)
|
|
||||||
yield self._setup_redirects()
|
|
||||||
except Exception as err:
|
|
||||||
log.warning("error trying to set up upnp: %s", err)
|
|
||||||
self.external_ip = CS.get_external_ip()
|
|
||||||
else:
|
|
||||||
self.external_ip = CS.get_external_ip()
|
|
||||||
if self.upnp:
|
if self.upnp:
|
||||||
if not self.upnp_redirects:
|
if not self.upnp_redirects:
|
||||||
log.error("failed to setup upnp, debugging infomation: %s", self.upnp.zipped_debugging_info)
|
log.error("failed to setup upnp, debugging infomation: %s", self.upnp.zipped_debugging_info)
|
||||||
else:
|
else:
|
||||||
log.info("set up upnp port redirects for gateway: %s", self.upnp.gateway.manufacturer_string)
|
success = True
|
||||||
|
log.debug("set up upnp port redirects for gateway: %s", self.upnp.gateway.manufacturer_string)
|
||||||
else:
|
else:
|
||||||
log.error("failed to setup upnp")
|
log.error("failed to setup upnp")
|
||||||
|
self.component_manager.analytics_manager.send_upnp_setup_success_fail(success, self.get_status())
|
||||||
|
self._maintain_redirects_lc.start(360, now=False)
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
|
if self._maintain_redirects_lc.running:
|
||||||
|
self._maintain_redirects_lc.stop()
|
||||||
return defer.DeferredList(
|
return defer.DeferredList(
|
||||||
[from_future(self.upnp.delete_port_mapping(port, protocol))
|
[from_future(self.upnp.delete_port_mapping(port, protocol))
|
||||||
for protocol, port in self.upnp_redirects.items()]
|
for protocol, port in self.upnp_redirects.items()]
|
||||||
|
@ -741,6 +794,9 @@ class UPnPComponent(Component):
|
||||||
return {
|
return {
|
||||||
'redirects': self.upnp_redirects,
|
'redirects': self.upnp_redirects,
|
||||||
'gateway': '' if not self.upnp else self.upnp.gateway.manufacturer_string,
|
'gateway': '' if not self.upnp else self.upnp.gateway.manufacturer_string,
|
||||||
|
'dht_redirect_set': 'UDP' in self.upnp_redirects,
|
||||||
|
'peer_redirect_set': 'TCP' in self.upnp_redirects,
|
||||||
|
'external_ip': self.external_ip
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -777,6 +777,9 @@ class Daemon(AuthJSONRPCServer):
|
||||||
<TCP | UDP>: (int) external_port,
|
<TCP | UDP>: (int) external_port,
|
||||||
},
|
},
|
||||||
'gateway': (str) manufacturer and model,
|
'gateway': (str) manufacturer and model,
|
||||||
|
'dht_redirect_set': (bool),
|
||||||
|
'peer_redirect_set': (bool),
|
||||||
|
'external_ip': (str) external ip address,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in a new issue