summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/bb/server/xmlrpcclient.py
blob: 4661a9e5a0ce4c97c7d004c17ca4d8e264628762 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#
# BitBake XMLRPC Client Interface
#
# Copyright (C) 2006 - 2007  Michael 'Mickey' Lauer
# Copyright (C) 2006 - 2008  Richard Purdie
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

import os
import sys

import socket
import http.client
import xmlrpc.client

import bb
from bb.ui import uievent

class BBTransport(xmlrpc.client.Transport):
    def __init__(self, timeout):
        self.timeout = timeout
        self.connection_token = None
        xmlrpc.client.Transport.__init__(self)

    # Modified from default to pass timeout to HTTPConnection
    def make_connection(self, host):
        #return an existing connection if possible.  This allows
        #HTTP/1.1 keep-alive.
        if self._connection and host == self._connection[0]:
            return self._connection[1]

        # create a HTTP connection object from a host descriptor
        chost, self._extra_headers, x509 = self.get_host_info(host)
        #store the host argument along with the connection object
        self._connection = host, http.client.HTTPConnection(chost, timeout=self.timeout)
        return self._connection[1]

    def set_connection_token(self, token):
        self.connection_token = token

    def send_content(self, h, body):
        if self.connection_token:
            h.putheader("Bitbake-token", self.connection_token)
        xmlrpc.client.Transport.send_content(self, h, body)

def _create_server(host, port, timeout = 60):
    t = BBTransport(timeout)
    s = xmlrpc.client.ServerProxy("http://%s:%d/" % (host, port), transport=t, allow_none=True, use_builtin_types=True)
    return s, t

def check_connection(remote, timeout):
    try:
        host, port = remote.split(":")
        port = int(port)
    except Exception as e:
        bb.warn("Failed to read remote definition (%s)" % str(e))
        raise e

    server, _transport = _create_server(host, port, timeout)
    try:
        ret, err =  server.runCommand(['getVariable', 'TOPDIR'])
        if err or not ret:
            return False
    except ConnectionError:
        return False
    return True

class BitBakeXMLRPCServerConnection(object):
    def __init__(self, host, port, clientinfo=("localhost", 0), observer_only = False, featureset = None):
        self.connection, self.transport = _create_server(host, port)
        self.clientinfo = clientinfo
        self.observer_only = observer_only
        if featureset:
            self.featureset = featureset
        else:
            self.featureset = []

        self.events = uievent.BBUIEventQueue(self.connection, self.clientinfo)

        _, error = self.connection.runCommand(["setFeatures", self.featureset])
        if error:
            # disconnect the client, we can't make the setFeature work
            self.connection.removeClient()
            # no need to log it here, the error shall be sent to the client
            raise BaseException(error)

    def connect(self, token = None):
        if token is None:
            if self.observer_only:
                token = "observer"
            else:
                token = self.connection.addClient()

        if token is None:
            return None

        self.transport.set_connection_token(token)
        return self

    def removeClient(self):
        if not self.observer_only:
            self.connection.removeClient()

    def terminate(self):
        # Don't wait for server indefinitely
        socket.setdefaulttimeout(2)
        try:
            self.events.system_quit()
        except:
            pass
        try:
            self.connection.removeClient()
        except:
            pass

def connectXMLRPC(remote, featureset, observer_only = False, token = None):
    # The format of "remote" must be "server:port"
    try:
        [host, port] = remote.split(":")
        port = int(port)
    except Exception as e:
        bb.warn("Failed to parse remote definition %s (%s)" % (remote, str(e)))
        raise e

    # We need our IP for the server connection. We get the IP
    # by trying to connect with the server
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.connect((host, port))
        ip = s.getsockname()[0]
        s.close()
    except Exception as e:
        bb.warn("Could not create socket for %s:%s (%s)" % (host, port, str(e)))
        raise e
    try:
        connection = BitBakeXMLRPCServerConnection(host, port, (ip, 0), observer_only, featureset)
        return connection.connect(token)
    except Exception as e:
        bb.warn("Could not connect to server at %s:%s (%s)" % (host, port, str(e)))
        raise e