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(a)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(a)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:]))
>
>