| #!/usr/bin/env python3 |
| # Copyright 2016 Red Hat, Inc. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); you may |
| # not use this file except in compliance with the License. You may obtain |
| # a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| # License for the specific language governing permissions and limitations |
| # under the License. |
| |
| import socket |
| import sys |
| import os |
| import json |
| |
| from openstackclient import shell as osc_shell |
| from io import StringIO |
| |
| server_address = "/tmp/openstack.sock" |
| |
| try: |
| os.unlink(server_address) |
| except OSError: |
| if os.path.exists(server_address): |
| raise |
| |
| sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) |
| print('starting up on %s' % server_address, file=sys.stderr) |
| sock.bind(server_address) |
| |
| # Listen for incoming connections |
| sock.listen(1) |
| |
| def send(sock, doc): |
| jdoc = json.dumps(doc) |
| sock.send(b'%d\n' % len(jdoc)) |
| sock.sendall(jdoc.encode('utf-8')) |
| |
| def recv(sock): |
| length_str = b'' |
| char = sock.recv(1) |
| while char != b'\n': |
| length_str += char |
| char = sock.recv(1) |
| |
| total = int(length_str) |
| |
| # use a memoryview to receive the data chunk by chunk efficiently |
| jdoc = memoryview(bytearray(total)) |
| next_offset = 0 |
| while total - next_offset > 0: |
| recv_size = sock.recv_into(jdoc[next_offset:], total - next_offset) |
| next_offset += recv_size |
| try: |
| doc = json.loads(jdoc.tobytes()) |
| except (TypeError, ValueError) as e: |
| raise Exception('Data received was not in JSON format') |
| return doc |
| |
| while True: |
| csock, client_address = sock.accept() |
| try: |
| doc = recv(csock) |
| |
| print("%s %s" % (doc["app"], doc["argv"]), file=sys.stderr) |
| oldenv = {} |
| for name in doc["env"].keys(): |
| oldenv[name] = os.environ.get(name, None) |
| os.environ[name] = doc["env"][name] |
| |
| try: |
| old_stdout = sys.stdout |
| old_stderr = sys.stderr |
| my_stdout = sys.stdout = StringIO() |
| my_stderr = sys.stderr = StringIO() |
| |
| class Exit(BaseException): |
| def __init__(self, status): |
| self.status = status |
| |
| def noexit(stat): |
| raise Exit(stat) |
| |
| sys.exit = noexit |
| |
| if doc["app"] == "openstack": |
| sh = osc_shell.OpenStackShell() |
| ret = sh.run(doc["argv"]) |
| else: |
| print("Unknown application %s" % doc["app"], file=sys.stderr) |
| ret = 1 |
| except Exit as e: |
| ret = e.status |
| finally: |
| sys.stdout = old_stdout |
| sys.stderr = old_stderr |
| |
| for name in oldenv.keys(): |
| if oldenv[name] is None: |
| del os.environ[name] |
| else: |
| os.environ[name] = oldenv[name] |
| |
| send(csock, { |
| "stdout": my_stdout.getvalue(), |
| "stderr": my_stderr.getvalue(), |
| "status": ret, |
| }) |
| |
| except BaseException as e: |
| print(e, file=sys.stderr) |
| finally: |
| csock.close() |