Skip to content

Commit c2d9a37

Browse files
author
Yves-Gwenael Bourhis
committed
Created a make_web_conf command.
The current wsgi does not support virtualenvs (in some distributions, the dependencies may conflict with the distribution's python packages/dependencies and would require a virtualenv even in production). Also, making an apache web configuration is not always trivial especialy with ssl. This "make_web_conf" command creates a wsgi with automatic virtualenvironment detection (if there is a virtualenvironment), and creates an apache (normal or ssl) configuration TODO(ygbo):: - add nginx support to generate nginx configuration files. - add gunicorn support. - add uwsgi support. implements bp web-conf-generation-script Change-Id: I6397ba01df88b540bbdca4bf21ba90be6843022a
1 parent 022e719 commit c2d9a37

File tree

6 files changed

+295
-0
lines changed

6 files changed

+295
-0
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ horizon.egg-info
1515
openstack_dashboard/local/local_settings.py
1616
openstack_dashboard/local/.secret_key_store
1717
openstack_dashboard/test/.secret_key_store
18+
openstack_dashboard/wsgi/horizon.wsgi
1819
doc/build/
1920
doc/source/sourcecode
2021
/static/

openstack_dashboard/management/__init__.py

Whitespace-only changes.

openstack_dashboard/management/commands/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{% if SSL %}
2+
# Set "NameVirtualHost {{ NAMEDHOST }}:443" in your httpd.conf file if it's not already done.
3+
<VirtualHost {{ NAMEDHOST }}:443>
4+
SSLEngine on
5+
SSLCertificateFile {{ SSLCERT }}
6+
SSLCertificateKeyFile {{ SSLKEY }}
7+
{% if CACERT %} SSLCACertificateFile {{ CACERT }}{% endif %}
8+
{% else %}
9+
<VirtualHost {{ NAMEDHOST }}:80>
10+
{% endif %}
11+
ServerAdmin {{ ADMIN }}
12+
ServerName {{ VHOSTNAME }}
13+
14+
DocumentRoot {{ PROJECT_ROOT }}/
15+
16+
LogLevel warn
17+
ErrorLog {{ LOGDIR }}/{{ PROJECT_NAME }}-error.log
18+
CustomLog {{ LOGDIR }}/{{ PROJECT_NAME }}-access.log combined
19+
20+
WSGIScriptReloading On
21+
WSGIDaemonProcess {{ PROJECT_NAME }}_website
22+
WSGIProcessGroup {{ PROJECT_NAME }}_website
23+
WSGIApplicationGroup {{ PROJECT_NAME }}_website
24+
WSGIPassAuthorization On
25+
26+
WSGIScriptAlias / {{ WSGI_FILE }}
27+
28+
<Location "/">
29+
Order Allow,Deny
30+
Allow from all
31+
</Location>
32+
33+
Alias /static {{ STATIC_PATH }}
34+
<Location "/static">
35+
SetHandler None
36+
</Location>
37+
</Virtualhost>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!/usr/bin/env python
2+
import os
3+
import sys
4+
{% if ACTIVATE_THIS %}
5+
6+
activate_this = '{{ ACTIVATE_THIS }}'
7+
execfile(activate_this, dict(__file__=activate_this))
8+
{% endif %}
9+
sys.path.insert(0, '{{ PROJECT_ROOT }}')
10+
os.environ['DJANGO_SETTINGS_MODULE'] = '{{ DJANGO_SETTINGS_MODULE }}'
11+
12+
import django.core.handlers.wsgi
13+
application = django.core.handlers.wsgi.WSGIHandler()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
# -*- coding: utf-8 -*-
2+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
3+
# not use this file except in compliance with the License. You may obtain
4+
# a copy of the License at
5+
#
6+
# http://www.apache.org/licenses/LICENSE-2.0
7+
#
8+
# Unless required by applicable law or agreed to in writing, software
9+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
10+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
11+
# License for the specific language governing permissions and limitations
12+
# under the License.
13+
14+
from __future__ import print_function
15+
16+
from optparse import make_option # noqa
17+
import os
18+
import socket
19+
import sys
20+
import warnings
21+
22+
from django.conf import settings
23+
from django.core.management.base import BaseCommand # noqa
24+
from django.template import Context, Template # noqa
25+
26+
# Suppress DeprecationWarnings which clutter the output to the point of
27+
# rendering it unreadable.
28+
warnings.simplefilter('ignore')
29+
30+
cmd_name = __name__.split('.')[-1]
31+
32+
CURDIR = os.path.realpath(os.path.dirname(__file__))
33+
PROJECT_PATH = os.path.realpath(os.path.join(CURDIR, '../..'))
34+
STATIC_PATH = os.path.realpath(os.path.join(PROJECT_PATH, '../static'))
35+
36+
# Known apache log directory locations
37+
APACHE_LOG_DIRS = (
38+
'/var/log/httpd', # RHEL / Red Hat / CentOS / Fedora Linux
39+
'/var/log/apache2', # Debian / Ubuntu Linux
40+
)
41+
# Default log directory
42+
DEFAULT_LOG_DIR = '/var/log'
43+
44+
45+
def _getattr(obj, name, default):
46+
"""Like getattr but return `default` if None or False.
47+
48+
By default, getattr(obj, name, default) returns default only if
49+
attr does not exist, here, we return `default` even if attr evaluates to
50+
None or False.
51+
"""
52+
value = getattr(obj, name, default)
53+
if value:
54+
return value
55+
else:
56+
return default
57+
58+
59+
context = Context({
60+
'DJANGO_SETTINGS_MODULE': os.environ['DJANGO_SETTINGS_MODULE'],
61+
'HOSTNAME': socket.getfqdn(),
62+
'PROJECT_PATH': os.path.realpath(
63+
_getattr(settings, 'ROOT_PATH', PROJECT_PATH)),
64+
'STATIC_PATH': os.path.realpath(
65+
_getattr(settings, 'STATIC_ROOT', STATIC_PATH)),
66+
'SSLCERT': '/etc/pki/tls/certs/ca.crt',
67+
'SSLKEY': '/etc/pki/tls/private/ca.key',
68+
'CACERT': None,
69+
})
70+
71+
context['PROJECT_ROOT'] = os.path.dirname(context['PROJECT_PATH'])
72+
context['PROJECT_DIR_NAME'] = os.path.basename(
73+
context['PROJECT_PATH'].split(context['PROJECT_ROOT'])[1])
74+
context['PROJECT_NAME'] = context['PROJECT_DIR_NAME']
75+
76+
context['WSGI_FILE'] = os.path.join(
77+
context['PROJECT_PATH'], 'wsgi/horizon.wsgi')
78+
79+
VHOSTNAME = context['HOSTNAME'].split('.')
80+
VHOSTNAME[0] = context['PROJECT_NAME']
81+
context['VHOSTNAME'] = '.'.join(VHOSTNAME)
82+
83+
if len(VHOSTNAME) > 1:
84+
context['DOMAINNAME'] = '.'.join(VHOSTNAME[1:])
85+
else:
86+
context['DOMAINNAME'] = 'openstack.org'
87+
context['ADMIN'] = 'webmaster@%s' % context['DOMAINNAME']
88+
89+
context['ACTIVATE_THIS'] = None
90+
virtualenv = os.environ.get('VIRTUAL_ENV')
91+
if virtualenv:
92+
activate_this = os.path.join(
93+
virtualenv, 'bin/activate_this.py')
94+
if os.path.exists(activate_this):
95+
context['ACTIVATE_THIS'] = activate_this
96+
97+
98+
def find_apache_log_dir():
99+
for log_dir in APACHE_LOG_DIRS:
100+
if os.path.exists(log_dir) and os.path.isdir(log_dir):
101+
return log_dir
102+
return DEFAULT_LOG_DIR
103+
context['LOGDIR'] = find_apache_log_dir()
104+
105+
106+
class Command(BaseCommand):
107+
108+
args = ''
109+
help = """Create %(wsgi_file)s
110+
or the contents of an apache %(p_name)s.conf file (on stdout).
111+
The apache configuration is generated on stdout because the place of this
112+
file is distribution dependent.
113+
114+
examples::
115+
116+
manage.py %(cmd_name)s --wsgi # creates %(wsgi_file)s
117+
manage.py %(cmd_name)s --apache # creates an apache vhost conf file (on \
118+
stdout).
119+
manage.py %(cmd_name)s --apache --ssl --mail=%(admin)s \
120+
--project=%(p_name)s --hostname=%(hostname)s
121+
122+
To create an acpache configuration file, redirect the output towards the
123+
location you desire, e.g.::
124+
125+
manage.py %(cmd_name)s --apache > \
126+
/etc/httpd/conf.d/openstack_dashboard.conf
127+
128+
""" % {
129+
'cmd_name': cmd_name,
130+
'p_name': context['PROJECT_NAME'],
131+
'wsgi_file': context['WSGI_FILE'],
132+
'admin': context['ADMIN'],
133+
'hostname': context['VHOSTNAME'], }
134+
135+
option_list = BaseCommand.option_list + (
136+
# TODO(ygbo): Add an --nginx option.
137+
make_option("-a", "--apache",
138+
default=False, action="store_true", dest="apache",
139+
help="generate an apache vhost configuration"),
140+
make_option("--cacert",
141+
dest="cacert",
142+
help=("Use with the --apache and --ssl option to define "
143+
"the path to the SSLCACertificateFile"
144+
),
145+
metavar="CACERT"),
146+
make_option("-f", "--force",
147+
default=False, action="store_true", dest="force",
148+
help="force overwriting of an existing %s file" %
149+
context['WSGI_FILE']),
150+
make_option("-H", "--hostname",
151+
dest="hostname",
152+
help=("Use with the --apache option to define the server's"
153+
" hostname (default : %s)") % context['VHOSTNAME'],
154+
metavar="HOSTNAME"),
155+
make_option("--logdir",
156+
dest="logdir",
157+
help=("Use with the --apache option to define the path to "
158+
"the apache log directory(default : %s)"
159+
% context['LOGDIR']),
160+
metavar="CACERT"),
161+
make_option("-m", "--mail",
162+
dest="mail",
163+
help=("Use with the --apache option to define the web site"
164+
" administrator's email (default : %s)") %
165+
context['ADMIN'],
166+
metavar="MAIL"),
167+
make_option("-n", "--namedhost",
168+
default=False, action="store_true", dest="namedhost",
169+
help=("Use with the --apache option. The apache vhost "
170+
"configuration will work only when accessed with "
171+
"the proper hostname (see --hostname).")),
172+
make_option("-p", "--project",
173+
dest="project",
174+
help=("Use with the --apache option to define the project "
175+
"name (default : %s)") % context['PROJECT_NAME'],
176+
metavar="PROJECT"),
177+
make_option("-s", "--ssl",
178+
default=False, action="store_true", dest="ssl",
179+
help=("Use with the --apache option. The apache vhost "
180+
"configuration will use an SSL configuration")),
181+
make_option("--sslcert",
182+
dest="sslcert",
183+
help=("Use with the --apache and --ssl option to define "
184+
"the path to the SSLCertificateFile (default : %s)"
185+
) % context['SSLCERT'],
186+
metavar="SSLCERT"),
187+
make_option("--sslkey",
188+
dest="sslkey",
189+
help=("Use with the --apache and --ssl option to define "
190+
"the path to the SSLCertificateKeyFile "
191+
"(default : %s)") % context['SSLKEY'],
192+
metavar="SSLKEY"),
193+
make_option("-w", "--wsgi",
194+
default=False, action="store_true", dest="wsgi",
195+
help="generate the horizon.wsgi file"),
196+
)
197+
198+
def handle(self, *args, **options):
199+
force = options.get('force')
200+
context['SSL'] = options.get('ssl')
201+
202+
if options.get('mail'):
203+
context['ADMIN'] = options['mail']
204+
if options.get('cacert'):
205+
context['CACERT'] = options['cacert']
206+
if options.get('logdir'):
207+
context['LOGDIR'] = options['logdir'].rstrip('/')
208+
if options.get('project'):
209+
context['PROJECT_NAME'] = options['project']
210+
if options.get('hostname'):
211+
context['VHOSTNAME'] = options['hostname']
212+
if options.get('sslcert'):
213+
context['SSLCERT'] = options['sslcert']
214+
if options.get('sslkey'):
215+
context['SSLKEY'] = options['sslkey']
216+
217+
if options.get('namedhost'):
218+
context['NAMEDHOST'] = context['VHOSTNAME']
219+
else:
220+
context['NAMEDHOST'] = '*'
221+
222+
# Generate the WSGI.
223+
if options.get('wsgi'):
224+
with open(
225+
os.path.join(CURDIR, 'horizon.wsgi.template'), 'r'
226+
) as fp:
227+
wsgi_template = Template(fp.read())
228+
if not os.path.exists(context['WSGI_FILE']) or force:
229+
with open(context['WSGI_FILE'], 'w') as fp:
230+
fp.write(wsgi_template.render(context))
231+
print('Generated "%s"' % context['WSGI_FILE'])
232+
else:
233+
sys.exit('"%s" already exists, use --force to overwrite' %
234+
context['WSGI_FILE'])
235+
236+
# Generate the apache configuration.
237+
elif options.get('apache'):
238+
with open(
239+
os.path.join(CURDIR, 'apache_vhost.conf.template'), 'r'
240+
) as fp:
241+
wsgi_template = Template(fp.read())
242+
sys.stdout.write(wsgi_template.render(context))
243+
else:
244+
self.print_help('manage.py', cmd_name)

0 commit comments

Comments
 (0)