[Kimchi-devel] [PATCH 1/4] Github #329: kimchid script changes

Daniel H Barboza danielhb at linux.vnet.ibm.com
Mon Apr 7 21:31:12 UTC 2014


On 04/07/2014 05:56 PM, Paulo Ricardo Paz Vital wrote:
>
> On Mon, 2014-04-07 at 17:27 -0300, Ramon Medeiros wrote:
>> On 04/07/2014 04:55 PM, Daniel Barboza wrote:
>>> From: Daniel Henrique Barboza <danielhb at linux.vnet.ibm.com>
>>>
>>> The kimchid script was revamped to launch 3 processes:
>>>
>>> - a cherrypy process that will act as frontend, running as
>>> non-root, providing the html templates;
>>> - another cherrypy process running as backend, with root privileges;
>>> - a reverse proxy (nginx) that will forward all requests made to
>>> the kimchi port to the frontend/backend.
>>>
>>> The same options that the previous kimchid scripts provided are
>>> available in this new version.
>>>
>>> Signed-off-by: Daniel Henrique Barboza <danielhb at linux.vnet.ibm.com>
>>> ---
>>>    src/kimchid.in | 202 +++++++++++++++++++++++++++++++++++++++++++++++++--------
>>>    1 file changed, 175 insertions(+), 27 deletions(-)
>>>
>>> diff --git a/src/kimchid.in b/src/kimchid.in
>>> index 8b63b57..9e24797 100644
>>> --- a/src/kimchid.in
>>> +++ b/src/kimchid.in
>>> @@ -2,7 +2,7 @@
>>>    #
>>>    # Project Kimchi
>>>    #
>>> -# Copyright IBM, Corp. 2013
>>> +# Copyright IBM, Corp. 2014
>>>    #
>>>    # This library is free software; you can redistribute it and/or
>>>    # modify it under the terms of the GNU Lesser General Public
>>> @@ -16,18 +16,27 @@
>>>    #
>>>    # You should have received a copy of the GNU Lesser General Public
>>>    # License along with this library; if not, write to the Free Software
>>> -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
>>> +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
>>> +# 02110-1301  USA
>>>
>>>    import logging
>>>    import os
>>> +import pwd
>>> +import re
>>> +import signal
>>> +import subprocess
>>>    import sys
>>> -sys.path.insert(1, '@pythondir@')
>>> +import time
>>> +sys.path.insert(1, '/usr/lib/python2.7/site-packages')
>> not sure if all supported distros came with python2.7. But, would be
>> nice to check if the path exists:
>>
>> if os.path.exists('/usr/lib/python2.7/site-packages'):  ...
> Exactly! On RHEL6.X for example the default version of python is 2.6
> You can check this using sys.path:
>
> $ python
> Python 2.7.5 (default, Nov 12 2013, 16:18:42)
> [GCC 4.8.2 20131017 (Red Hat 4.8.2-1)] on linux2
> Type "help", "copyright", "credits" or "license" for more information.

I'll restore this line to:

" sys.path.insert(1, '@pythondir@')"


in the v2 of the patch. Not sure why this line was changed ... probably 
because I've used
the compiled script as skeleton instead of kimchid.in, the pre-compiled 
file.

>>>> import sys
>>>> sys.path
> ['', '/usr/lib64/python27.zip', '/usr/lib64/python2.7',
> '/usr/lib64/python2.7/plat-linux2', '/usr/lib64/python2.7/lib-tk',
> '/usr/lib64/python2.7/lib-old', '/usr/lib64/python2.7/lib-dynload',
> '/usr/lib64/python2.7/site-packages',
> '/usr/lib64/python2.7/site-packages/gtk-2.0',
> '/usr/lib/python2.7/site-packages',
> '/usr/lib/python2.7/site-packages/setuptools-0.6c11-py2.7.egg-info']
>
>
>
>>> +
>>> +from string import Template
>>>
>>>    import kimchi.server
>>>    import kimchi.config
>>>
>>>    from kimchi.config import config, paths
>>> -from optparse import OptionParser
>>> +from kimchi.utils import parse_command_line_options
>>> +
>>>
>>>    if not paths.installed:
>>>        sys.path.append(paths.prefix)
>>> @@ -35,30 +44,169 @@ if not paths.installed:
>>>    ACCESS_LOG = "kimchi-access.log"
>>>    ERROR_LOG = "kimchi-error.log"
>>>
>>> +
>>> +def create_proxy_config(p_port, f_port, b_port,
>>> +                        p_ssl_port, f_ssl_port, b_ssl_port):
>>> +    """Create nginx configuration file based on current ports config
>>> +
>>> +    To allow flexibility in which port kimchi runs, we need the same
>>> +    flexibility with the nginx proxy. This method creates the config
>>> +    file dynamically by using 'nginx.conf.in' as a template, creating
>>> +    the file 'nginx_kimchi.config' which will be used to launch the
>>> +    proxy.
>>> +
>>> +    Arguments:
>>> +    p_port - proxy port
>>> +    f_port - frontend port
>>> +    b_port - backend port
>>> +    p_ssl_port - proxy SSL port
>>> +    f_ssl_port - frontend SSL port
>>> +    b_ssl_port - backend SSL port
>>> +    """
>>> +
>>> +    # get SSL paths to be used by the proxy
>>> +    config_dir = paths.conf_dir
>>> +    cert = '%s/kimchi-cert.pem' % config_dir
>>> +    key = '%s/kimchi-key.pem' % config_dir
>>> +
>>> +    with open(os.path.join(paths.conf_dir, "nginx.conf.in")) as myfile:
>>> +        data = myfile.read()
>>> +
>>> +    data = Template(data)
>>> +    data = data.safe_substitute(proxy_port=p_port,
>>> +                                frontend_port=f_port,
>>> +                                backend_port=b_port,
>>> +                                proxy_ssl_port=p_ssl_port,
>>> +                                frontend_ssl_port=f_ssl_port,
>>> +                                backend_ssl_port=b_ssl_port,
>>> +                                cert_pem=cert, cert_key=key)
>>> +
>>> +    config_file = open(os.getcwd() + "/nginx_kimchi.conf", "w")
>>> +    config_file.write(data)
>>> +    config_file.close()
>>> +
>>> +
>>> +def start_proxy():
>>> +    """Start nginx reverse proxy."""
>>> +    config_file = os.getcwd() + "/nginx_kimchi.conf"
>>> +    cmd = ['nginx', '-c', config_file]
>>> +    subprocess.call(cmd)
>>> +
>>> +
>>> +def terminate_proxy():
>>> +    """Stop nginx process."""
>>> +    term_proxy_cmd = ['nginx', '-s', 'stop']
>>> +    subprocess.call(term_proxy_cmd)
>>> +
>>> +
>>>    def main(options):
>>> -    host = config.get("server", "host")
>>> -    port = config.get("server", "port")
>>> -    ssl_port = config.get("server", "ssl_port")
>>> -    runningEnv = config.get('server', 'environment')
>>> -    logDir = config.get("logging", "log_dir")
>>> -    logLevel = config.get("logging", "log_level")
>>> -
>>> -    parser = OptionParser()
>>> -    parser.add_option('--host', type="string", default=host, help="Hostname to listen on")
>>> -    parser.add_option('--port', type="int", default=port, help="Port to listen on")
>>> -    parser.add_option('--ssl-port', type="int", default=ssl_port, help="Enable a SSL server on the given port")
>>> -    parser.add_option('--log-level', default=logLevel, help="Logging level")
>>> -    parser.add_option('--access-log', default=os.path.join(logDir,ACCESS_LOG), help="Access log file")
>>> -    parser.add_option('--error-log', default=os.path.join(logDir,ERROR_LOG), help="Error log file")
>>> -    parser.add_option('--environment', default=runningEnv, help="Running environment of kimchi server")
>>> -    parser.add_option('--test', action='store_true', help="Run server in mock model")
>>> -    (options, args) = parser.parse_args()
>>> -
>>> -    # Add non-option arguments
>>> -    setattr(options, 'ssl_cert', config.get('server', 'ssl_cert'))
>>> -    setattr(options, 'ssl_key', config.get('server', 'ssl_key'))
>>> -
>>> -    kimchi.server.main(options)
>>> +    # Script must run as root or with sudo.
>>> +    if not os.geteuid() == 0:
>>> +        sys.exit("\nMust be root to run this script. Exiting ...\n")
>>> +
>>> +    # The following method is used when the process receives a
>>> +    # SIGINT signal (CTRL+C).
>>> +    def terminate_kimchi(signal, frame):
>>> +        terminate_proxy()
>>> +        sys.exit(0)
>>> +    signal.signal(signal.SIGINT, terminate_kimchi)
>>> +
>>> +    # User that will execute the frontend will own this
>>> +    # process temporarily. First we need to detect if the script
>>> +    # was run with SUDO or from the root directly. If
>>> +    # run with SUDO, the user that fired the execution will
>>> +    # take over (this user is registered at the 'SUDO_USER' env
>>> +    # variable). Otherwise the user kimchi will take over.
>>> +    #
>>> +    # The frontend will **NOT** be run as root under any
>>> +    # circunstance using this script.
>>> +    if os.getenv("SUDO_USER") is not None:
>>> +        temp_user = os.getenv("SUDO_USER")
>>> +    else:
>>> +        temp_user = 'kimchi'
>>> +
>>> +    # Drop execution privileges temporarily
>>> +    os.seteuid(pwd.getpwnam(temp_user).pw_uid)
>>> +
>>> +    # Validate command line options before forwarding them
>>> +    # to the cherrypy processes.
>>> +    parse_command_line_options()
>>> +
>>> +    # Copy the options (sys.argv[:1]) into 2 other lists. That
>>> +    # way we can edit the copies with modified parameters to
>>> +    # use in the cherrypy processes.
>>> +    argv_frontend = list(options)
>>> +    argv_backend = list(options)
>>> +
>>> +    ssl_port_match = re.compile("^--ssl-port.*")
>>> +    port_match = re.compile("^--port.*")
>>> +
>>> +    port_index = None
>>> +    ssl_port_index = None
>>> +
>>> +    # Default ports
>>> +    proxy_port = 8000
>>> +    proxy_ssl_port = 8001
>>> +
>>> +    # Search for the '--port=' and '--ssl-port=' arguments to verify
>>> +    # if the user wants to run in a port other than the default.
>>> +    for i, value in enumerate(options):
>>> +        if re.search(port_match, value):
>>> +            proxy_port = int(value.partition("=")[2])
>>> +            port_index = i
>>> +        elif re.search(ssl_port_match, value):
>>> +            proxy_ssl_port = int(value.partition("=")[2])
>>> +            ssl_port_index = i
>>> +
>>> +    # The max value between port and proxy_port
>>> +    # will be used to calculate the frontend and
>>> +    # backend ports.
>>> +    next_port = max(proxy_port, proxy_ssl_port) + 1
>>> +    frontend_port = next_port
>>> +    next_port += 1
>>> +    frontend_ssl_port = next_port
>>> +    next_port += 1
>>> +    backend_port = next_port
>>> +    next_port += 1
>>> +    backend_ssl_port = next_port
>>> +
>>> +    if port_index is not None:
>>> +        # edit args to be used by the front/backend
>>> +        argv_frontend[port_index] = "--port="+str(frontend_port)
>>> +        argv_backend[port_index] = "--port="+str(backend_port)
>>> +    else:
>>> +        # add port info to the front/backend options
>>> +        argv_frontend += ["--port="+str(frontend_port)]
>>> +        argv_backend += ["--port="+str(backend_port)]
>>> +
>>> +    if ssl_port_index is not None:
>>> +        # edit args to be used by the front/backend
>>> +        argv_frontend[ssl_port_index] = "--ssl-port="+str(frontend_ssl_port)
>>> +        argv_backend[ssl_port_index] = "--ssl-port="+str(backend_ssl_port)
>>> +    else:
>>> +        # add ssl-port info to the front/backend options
>>> +        argv_frontend += ["--ssl-port="+str(frontend_ssl_port)]
>>> +        argv_backend += ["--ssl-port="+str(backend_ssl_port)]
>>> +
>>> +    # launch frontend with edited options
>>> +    command = os.getcwd() + '/src/kimchid_server'
>>> +    cmd = [command] + argv_frontend
>>> +    frontend = subprocess.Popen(cmd)
>>> +
>>> +    # Retrieve root access before launching the backend.
>>> +    os.seteuid(0)
>>> +
>>> +    # Launch the cherrypy backend process.
>>> +    cmd = [command] + argv_backend + ['--backend'] + ['--log_screen']
>>> +    backend = subprocess.Popen(cmd, stderr=subprocess.STDOUT)
>>> +
>>> +    # Launch reverse proxy: create config file and start.
>>> +    create_proxy_config(proxy_port, frontend_port, backend_port,
>>> +                        proxy_ssl_port, frontend_ssl_port, backend_ssl_port)
>>> +    start_proxy()
>>> +
>>> +    # suspend execution until interruption (SIGINT, SIGKILL)
>>> +    signal.pause()
>>>
>>>    if __name__ == '__main__':
>>>        sys.exit(main(sys.argv[1:]))
>>
>>




More information about the Kimchi-devel mailing list