Coverage for /home/antoine/projects/xpra-git/dist/python3/lib64/python/xpra/server/mixins/networkstate_server.py : 70%
Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# -*- coding: utf-8 -*-
2# This file is part of Xpra.
3# Copyright (C) 2010-2020 Antoine Martin <antoine@xpra.org>
4# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
5# later version. See the file COPYING for details.
6#pylint: disable-msg=E1101
8import os
9from time import sleep
11from xpra.server.mixins.stub_server_mixin import StubServerMixin
12from xpra.scripts.config import parse_with_unit
13from xpra.simple_stats import std_unit
14from xpra.os_util import livefds, POSIX
15from xpra.util import envbool, envint, detect_leaks, typedict
16from xpra.log import Logger
18log = Logger("network")
19bandwidthlog = Logger("bandwidth")
21DETECT_MEMLEAKS = envint("XPRA_DETECT_MEMLEAKS", 0)
22DETECT_FDLEAKS = envbool("XPRA_DETECT_FDLEAKS", False)
24MIN_BANDWIDTH_LIMIT = envint("XPRA_MIN_BANDWIDTH_LIMIT", 1024*1024)
25MAX_BANDWIDTH_LIMIT = envint("XPRA_MAX_BANDWIDTH_LIMIT", 10*1024*1024*1024)
26CPUINFO = envbool("XPRA_CPUINFO", False)
28"""
29Mixin for adding client / network state monitoring functions:
30- ping and echo
31- bandwidth management
32- leak detection (file descriptors, memory)
33"""
34class NetworkStateServer(StubServerMixin):
36 def __init__(self):
37 self.pings = False
38 self.ping_timer = None
39 self.mem_bytes = 0
40 self.cpu_info = None
41 self.print_memleaks = None
43 def init(self, opts):
44 self.pings = opts.pings
45 self.bandwidth_limit = parse_with_unit("bandwidth-limit", opts.bandwidth_limit)
46 bandwidthlog("bandwidth-limit(%s)=%s", opts.bandwidth_limit, self.bandwidth_limit)
47 self.init_cpuinfo()
49 def setup(self):
50 self.init_leak_detection()
51 if self.pings>0:
52 self.ping_timer = self.timeout_add(1000*self.pings, self.send_ping)
54 def threaded_setup(self):
55 self.init_memcheck()
57 def cleanup(self):
58 pt = self.ping_timer
59 if pt:
60 self.ping_timer = None
61 self.source_remove(pt)
62 pm = self.print_memleaks
63 if pm:
64 pm()
66 def get_info(self, _source=None) -> dict:
67 info = {
68 "pings" : self.pings,
69 "bandwidth-limit" : self.bandwidth_limit or 0,
70 }
71 if POSIX:
72 info["load"] = tuple(int(x*1000) for x in os.getloadavg())
73 if self.mem_bytes:
74 info["total-memory"] = self.mem_bytes
75 if self.cpu_info:
76 info["cpuinfo"] = dict((k,v) for k,v in self.cpu_info.items() if k!="python_version")
77 return info
79 def get_server_features(self, _source) -> dict:
80 return {
81 "connection-data" : True, #added in v2.3
82 "network" : {
83 "bandwidth-limit-change" : True, #added in v2.2
84 "bandwidth-limit" : self.bandwidth_limit or 0,
85 }
86 }
89 def init_leak_detection(self):
90 if DETECT_MEMLEAKS:
91 self.print_memleaks = detect_leaks()
92 if bool(self.print_memleaks):
93 def leak_thread():
94 while not self._closing:
95 self.print_memleaks()
96 sleep(DETECT_MEMLEAKS)
97 from xpra.make_thread import start_thread
98 start_thread(leak_thread, "leak thread", daemon=True)
99 if DETECT_FDLEAKS:
100 self.fds = livefds()
101 def print_fds():
102 fds = livefds()
103 newfds = fds-self.fds
104 self.fds = fds
105 log.info("print_fds() new fds=%s (total=%s)", newfds, len(fds))
106 return True
107 self.timeout_add(10, print_fds)
109 def init_memcheck(self):
110 #verify we have enough memory:
111 if POSIX and self.mem_bytes==0:
112 try:
113 self.mem_bytes = os.sysconf('SC_PAGE_SIZE') * os.sysconf('SC_PHYS_PAGES') # e.g. 4015976448
114 LOW_MEM_LIMIT = 512*1024*1024
115 if self.mem_bytes<=LOW_MEM_LIMIT:
116 log.warn("Warning: only %iMB total system memory available", self.mem_bytes//(1024**2))
117 log.warn(" this may not be enough to run a server")
118 else:
119 log.info("%.1fGB of system memory", self.mem_bytes/(1024.0**3))
120 except Exception:
121 pass
123 def init_cpuinfo(self):
124 if not CPUINFO:
125 return
126 #this crashes if not run from the UI thread!
127 try:
128 from cpuinfo import get_cpu_info
129 except ImportError as e:
130 log("no cpuinfo: %s", e)
131 return
132 self.cpu_info = get_cpu_info()
133 if self.cpu_info:
134 c = typedict(self.cpu_info)
135 count = c.intget("count", 0)
136 brand = c.strget("brand")
137 if count>0 and brand:
138 log.info("%ix %s", count, brand)
141 def _process_connection_data(self, proto, packet):
142 ss = self.get_server_source(proto)
143 if ss:
144 ss.update_connection_data(packet[1])
146 def _process_bandwidth_limit(self, proto, packet):
147 log("_process_bandwidth_limit(%s, %s)", proto, packet)
148 ss = self.get_server_source(proto)
149 if not ss:
150 return
151 bandwidth_limit = packet[1]
152 if not isinstance(bandwidth_limit, int):
153 raise TypeError("bandwidth-limit must be an integer, not %s" % type(bandwidth_limit))
154 if (self.bandwidth_limit and bandwidth_limit>=self.bandwidth_limit) or bandwidth_limit<=0:
155 bandwidth_limit = self.bandwidth_limit or 0
156 if ss.bandwidth_limit==bandwidth_limit:
157 #unchanged
158 log("bandwidth limit unchanged: %s", std_unit(bandwidth_limit))
159 return
160 if bandwidth_limit<MIN_BANDWIDTH_LIMIT:
161 log.warn("Warning: bandwidth limit requested is too low (%s)", std_unit(bandwidth_limit))
162 bandwidth_limit = MIN_BANDWIDTH_LIMIT
163 if bandwidth_limit>=MAX_BANDWIDTH_LIMIT:
164 log("bandwidth limit over maximum, using no-limit instead")
165 bandwidth_limit = 0
166 ss.bandwidth_limit = bandwidth_limit
167 #we can't assume to have a full ClientConnection object:
168 client_id = getattr(ss, "counter", "")
169 if bandwidth_limit==0:
170 bandwidthlog.info("bandwidth-limit restrictions removed for client %s", client_id)
171 else:
172 bandwidthlog.info("bandwidth-limit changed to %sbps for client %s", std_unit(bandwidth_limit), client_id)
174 def send_ping(self) -> bool:
175 from xpra.server.source.networkstate_mixin import NetworkStateMixin
176 for ss in self._server_sources.values():
177 if isinstance(ss, NetworkStateMixin):
178 ss.ping()
179 return True
181 def _process_ping_echo(self, proto, packet):
182 ss = self.get_server_source(proto)
183 if ss:
184 ss.process_ping_echo(packet)
186 def _process_ping(self, proto, packet):
187 time_to_echo = packet[1]
188 sid = ""
189 if len(packet)>=4:
190 sid = packet[3]
191 ss = self.get_server_source(proto)
192 if ss:
193 ss.process_ping(time_to_echo, sid)
196 def init_packet_handlers(self):
197 self.add_packet_handlers({
198 "ping": self._process_ping,
199 "ping_echo": self._process_ping_echo,
200 "connection-data": self._process_connection_data,
201 "bandwidth-limit": self._process_bandwidth_limit,
202 }, False)