1*4882a593Smuzhiyun# 2*4882a593Smuzhiyun# BitBake XMLRPC Client Interface 3*4882a593Smuzhiyun# 4*4882a593Smuzhiyun# Copyright (C) 2006 - 2007 Michael 'Mickey' Lauer 5*4882a593Smuzhiyun# Copyright (C) 2006 - 2008 Richard Purdie 6*4882a593Smuzhiyun# 7*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0-only 8*4882a593Smuzhiyun# 9*4882a593Smuzhiyun 10*4882a593Smuzhiyunimport socket 11*4882a593Smuzhiyunimport http.client 12*4882a593Smuzhiyunimport xmlrpc.client 13*4882a593Smuzhiyun 14*4882a593Smuzhiyunimport bb 15*4882a593Smuzhiyunfrom bb.ui import uievent 16*4882a593Smuzhiyun 17*4882a593Smuzhiyunclass BBTransport(xmlrpc.client.Transport): 18*4882a593Smuzhiyun def __init__(self, timeout): 19*4882a593Smuzhiyun self.timeout = timeout 20*4882a593Smuzhiyun self.connection_token = None 21*4882a593Smuzhiyun xmlrpc.client.Transport.__init__(self) 22*4882a593Smuzhiyun 23*4882a593Smuzhiyun # Modified from default to pass timeout to HTTPConnection 24*4882a593Smuzhiyun def make_connection(self, host): 25*4882a593Smuzhiyun #return an existing connection if possible. This allows 26*4882a593Smuzhiyun #HTTP/1.1 keep-alive. 27*4882a593Smuzhiyun if self._connection and host == self._connection[0]: 28*4882a593Smuzhiyun return self._connection[1] 29*4882a593Smuzhiyun 30*4882a593Smuzhiyun # create a HTTP connection object from a host descriptor 31*4882a593Smuzhiyun chost, self._extra_headers, x509 = self.get_host_info(host) 32*4882a593Smuzhiyun #store the host argument along with the connection object 33*4882a593Smuzhiyun self._connection = host, http.client.HTTPConnection(chost, timeout=self.timeout) 34*4882a593Smuzhiyun return self._connection[1] 35*4882a593Smuzhiyun 36*4882a593Smuzhiyun def set_connection_token(self, token): 37*4882a593Smuzhiyun self.connection_token = token 38*4882a593Smuzhiyun 39*4882a593Smuzhiyun def send_content(self, h, body): 40*4882a593Smuzhiyun if self.connection_token: 41*4882a593Smuzhiyun h.putheader("Bitbake-token", self.connection_token) 42*4882a593Smuzhiyun xmlrpc.client.Transport.send_content(self, h, body) 43*4882a593Smuzhiyun 44*4882a593Smuzhiyundef _create_server(host, port, timeout = 60): 45*4882a593Smuzhiyun t = BBTransport(timeout) 46*4882a593Smuzhiyun s = xmlrpc.client.ServerProxy("http://%s:%d/" % (host, port), transport=t, allow_none=True, use_builtin_types=True) 47*4882a593Smuzhiyun return s, t 48*4882a593Smuzhiyun 49*4882a593Smuzhiyundef check_connection(remote, timeout): 50*4882a593Smuzhiyun try: 51*4882a593Smuzhiyun host, port = remote.split(":") 52*4882a593Smuzhiyun port = int(port) 53*4882a593Smuzhiyun except Exception as e: 54*4882a593Smuzhiyun bb.warn("Failed to read remote definition (%s)" % str(e)) 55*4882a593Smuzhiyun raise e 56*4882a593Smuzhiyun 57*4882a593Smuzhiyun server, _transport = _create_server(host, port, timeout) 58*4882a593Smuzhiyun try: 59*4882a593Smuzhiyun ret, err = server.runCommand(['getVariable', 'TOPDIR']) 60*4882a593Smuzhiyun if err or not ret: 61*4882a593Smuzhiyun return False 62*4882a593Smuzhiyun except ConnectionError: 63*4882a593Smuzhiyun return False 64*4882a593Smuzhiyun return True 65*4882a593Smuzhiyun 66*4882a593Smuzhiyunclass BitBakeXMLRPCServerConnection(object): 67*4882a593Smuzhiyun def __init__(self, host, port, clientinfo=("localhost", 0), observer_only = False, featureset = None): 68*4882a593Smuzhiyun self.connection, self.transport = _create_server(host, port) 69*4882a593Smuzhiyun self.clientinfo = clientinfo 70*4882a593Smuzhiyun self.observer_only = observer_only 71*4882a593Smuzhiyun if featureset: 72*4882a593Smuzhiyun self.featureset = featureset 73*4882a593Smuzhiyun else: 74*4882a593Smuzhiyun self.featureset = [] 75*4882a593Smuzhiyun 76*4882a593Smuzhiyun self.events = uievent.BBUIEventQueue(self.connection, self.clientinfo) 77*4882a593Smuzhiyun 78*4882a593Smuzhiyun _, error = self.connection.runCommand(["setFeatures", self.featureset]) 79*4882a593Smuzhiyun if error: 80*4882a593Smuzhiyun # disconnect the client, we can't make the setFeature work 81*4882a593Smuzhiyun self.connection.removeClient() 82*4882a593Smuzhiyun # no need to log it here, the error shall be sent to the client 83*4882a593Smuzhiyun raise BaseException(error) 84*4882a593Smuzhiyun 85*4882a593Smuzhiyun def connect(self, token = None): 86*4882a593Smuzhiyun if token is None: 87*4882a593Smuzhiyun if self.observer_only: 88*4882a593Smuzhiyun token = "observer" 89*4882a593Smuzhiyun else: 90*4882a593Smuzhiyun token = self.connection.addClient() 91*4882a593Smuzhiyun 92*4882a593Smuzhiyun if token is None: 93*4882a593Smuzhiyun return None 94*4882a593Smuzhiyun 95*4882a593Smuzhiyun self.transport.set_connection_token(token) 96*4882a593Smuzhiyun return self 97*4882a593Smuzhiyun 98*4882a593Smuzhiyun def removeClient(self): 99*4882a593Smuzhiyun if not self.observer_only: 100*4882a593Smuzhiyun self.connection.removeClient() 101*4882a593Smuzhiyun 102*4882a593Smuzhiyun def terminate(self): 103*4882a593Smuzhiyun # Don't wait for server indefinitely 104*4882a593Smuzhiyun socket.setdefaulttimeout(2) 105*4882a593Smuzhiyun try: 106*4882a593Smuzhiyun self.events.system_quit() 107*4882a593Smuzhiyun except: 108*4882a593Smuzhiyun pass 109*4882a593Smuzhiyun try: 110*4882a593Smuzhiyun self.connection.removeClient() 111*4882a593Smuzhiyun except: 112*4882a593Smuzhiyun pass 113*4882a593Smuzhiyun 114*4882a593Smuzhiyundef connectXMLRPC(remote, featureset, observer_only = False, token = None): 115*4882a593Smuzhiyun # The format of "remote" must be "server:port" 116*4882a593Smuzhiyun try: 117*4882a593Smuzhiyun [host, port] = remote.split(":") 118*4882a593Smuzhiyun port = int(port) 119*4882a593Smuzhiyun except Exception as e: 120*4882a593Smuzhiyun bb.warn("Failed to parse remote definition %s (%s)" % (remote, str(e))) 121*4882a593Smuzhiyun raise e 122*4882a593Smuzhiyun 123*4882a593Smuzhiyun # We need our IP for the server connection. We get the IP 124*4882a593Smuzhiyun # by trying to connect with the server 125*4882a593Smuzhiyun try: 126*4882a593Smuzhiyun s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 127*4882a593Smuzhiyun s.connect((host, port)) 128*4882a593Smuzhiyun ip = s.getsockname()[0] 129*4882a593Smuzhiyun s.close() 130*4882a593Smuzhiyun except Exception as e: 131*4882a593Smuzhiyun bb.warn("Could not create socket for %s:%s (%s)" % (host, port, str(e))) 132*4882a593Smuzhiyun raise e 133*4882a593Smuzhiyun try: 134*4882a593Smuzhiyun connection = BitBakeXMLRPCServerConnection(host, port, (ip, 0), observer_only, featureset) 135*4882a593Smuzhiyun return connection.connect(token) 136*4882a593Smuzhiyun except Exception as e: 137*4882a593Smuzhiyun bb.warn("Could not connect to server at %s:%s (%s)" % (host, port, str(e))) 138*4882a593Smuzhiyun raise e 139*4882a593Smuzhiyun 140*4882a593Smuzhiyun 141*4882a593Smuzhiyun 142