OpenStack Dashboard (Horizon)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

settings.py 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  1. # Copyright 2012 United States Government as represented by the
  2. # Administrator of the National Aeronautics and Space Administration.
  3. # All Rights Reserved.
  4. #
  5. # Copyright 2012 Nebula, Inc.
  6. #
  7. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  8. # not use this file except in compliance with the License. You may obtain
  9. # a copy of the License at
  10. #
  11. # http://www.apache.org/licenses/LICENSE-2.0
  12. #
  13. # Unless required by applicable law or agreed to in writing, software
  14. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  15. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  16. # License for the specific language governing permissions and limitations
  17. # under the License.
  18. import glob
  19. import logging
  20. import os
  21. import sys
  22. import warnings
  23. from django.utils.translation import pgettext_lazy
  24. from django.utils.translation import ugettext_lazy as _
  25. from openstack_dashboard import exceptions
  26. from openstack_dashboard import theme_settings
  27. from openstack_dashboard.utils import config
  28. from openstack_dashboard.utils import settings as settings_utils
  29. from horizon.utils.escape import monkeypatch_escape
  30. monkeypatch_escape()
  31. _LOG = logging.getLogger(__name__)
  32. warnings.formatwarning = lambda message, category, *args, **kwargs: \
  33. '%s: %s' % (category.__name__, message)
  34. ROOT_PATH = os.path.dirname(os.path.abspath(__file__))
  35. BIN_DIR = os.path.abspath(os.path.join(ROOT_PATH, '..', 'bin'))
  36. if ROOT_PATH not in sys.path:
  37. sys.path.append(ROOT_PATH)
  38. DEBUG = False
  39. SITE_BRANDING = 'OpenStack Dashboard'
  40. WEBROOT = '/'
  41. LOGIN_URL = None
  42. LOGOUT_URL = None
  43. LOGIN_REDIRECT_URL = None
  44. MEDIA_ROOT = None
  45. MEDIA_URL = None
  46. STATIC_ROOT = None
  47. STATIC_URL = None
  48. SELECTABLE_THEMES = None
  49. INTEGRATION_TESTS_SUPPORT = False
  50. NG_TEMPLATE_CACHE_AGE = 2592000
  51. ROOT_URLCONF = 'openstack_dashboard.urls'
  52. HORIZON_CONFIG = {
  53. 'user_home': 'openstack_dashboard.views.get_user_home',
  54. 'ajax_queue_limit': 10,
  55. 'auto_fade_alerts': {
  56. 'delay': 3000,
  57. 'fade_duration': 1500,
  58. 'types': ['alert-success', 'alert-info']
  59. },
  60. 'bug_url': None,
  61. 'help_url': "http://docs.openstack.org",
  62. 'exceptions': {'recoverable': exceptions.RECOVERABLE,
  63. 'not_found': exceptions.NOT_FOUND,
  64. 'unauthorized': exceptions.UNAUTHORIZED},
  65. 'modal_backdrop': 'static',
  66. 'angular_modules': [],
  67. 'js_files': [],
  68. 'js_spec_files': [],
  69. 'external_templates': [],
  70. 'plugins': [],
  71. 'integration_tests_support': INTEGRATION_TESTS_SUPPORT
  72. }
  73. # The OPENSTACK_IMAGE_BACKEND settings can be used to customize features
  74. # in the OpenStack Dashboard related to the Image service, such as the list
  75. # of supported image formats.
  76. OPENSTACK_IMAGE_BACKEND = {
  77. 'image_formats': [
  78. ('', _('Select format')),
  79. ('aki', _('AKI - Amazon Kernel Image')),
  80. ('ami', _('AMI - Amazon Machine Image')),
  81. ('ari', _('ARI - Amazon Ramdisk Image')),
  82. ('docker', _('Docker')),
  83. ('iso', _('ISO - Optical Disk Image')),
  84. ('ova', _('OVA - Open Virtual Appliance')),
  85. ('ploop', _('PLOOP - Virtuozzo/Parallels Loopback Disk')),
  86. ('qcow2', _('QCOW2 - QEMU Emulator')),
  87. ('raw', _('Raw')),
  88. ('vdi', _('VDI - Virtual Disk Image')),
  89. ('vhd', _('VHD - Virtual Hard Disk')),
  90. ('vhdx', _('VHDX - Large Virtual Hard Disk')),
  91. ('vmdk', _('VMDK - Virtual Machine Disk')),
  92. ]
  93. }
  94. MIDDLEWARE = (
  95. 'django.middleware.common.CommonMiddleware',
  96. 'django.middleware.csrf.CsrfViewMiddleware',
  97. 'django.contrib.sessions.middleware.SessionMiddleware',
  98. 'django.contrib.auth.middleware.AuthenticationMiddleware',
  99. 'horizon.middleware.OperationLogMiddleware',
  100. 'django.contrib.messages.middleware.MessageMiddleware',
  101. 'horizon.middleware.HorizonMiddleware',
  102. 'horizon.themes.ThemeMiddleware',
  103. 'django.middleware.locale.LocaleMiddleware',
  104. 'django.middleware.clickjacking.XFrameOptionsMiddleware',
  105. 'openstack_dashboard.contrib.developer.profiler.middleware.'
  106. 'ProfilerClientMiddleware',
  107. 'openstack_dashboard.contrib.developer.profiler.middleware.'
  108. 'ProfilerMiddleware',
  109. )
  110. CACHED_TEMPLATE_LOADERS = [
  111. 'django.template.loaders.filesystem.Loader',
  112. 'django.template.loaders.app_directories.Loader',
  113. 'horizon.loaders.TemplateLoader'
  114. ]
  115. ADD_TEMPLATE_LOADERS = []
  116. TEMPLATES = [
  117. {
  118. 'BACKEND': 'django.template.backends.django.DjangoTemplates',
  119. 'DIRS': [os.path.join(ROOT_PATH, 'templates')],
  120. 'OPTIONS': {
  121. 'context_processors': [
  122. 'django.template.context_processors.debug',
  123. 'django.template.context_processors.i18n',
  124. 'django.template.context_processors.request',
  125. 'django.template.context_processors.media',
  126. 'django.template.context_processors.static',
  127. 'django.contrib.messages.context_processors.messages',
  128. 'horizon.context_processors.horizon',
  129. 'openstack_dashboard.context_processors.openstack',
  130. ],
  131. 'loaders': [
  132. 'horizon.themes.ThemeTemplateLoader'
  133. ],
  134. },
  135. },
  136. ]
  137. STATICFILES_FINDERS = (
  138. 'django.contrib.staticfiles.finders.FileSystemFinder',
  139. 'horizon.contrib.staticfiles.finders.HorizonStaticFinder',
  140. 'compressor.finders.CompressorFinder',
  141. )
  142. COMPRESS_PRECOMPILERS = (
  143. ('text/scss', 'horizon.utils.scss_filter.HorizonScssFilter'),
  144. )
  145. COMPRESS_CSS_FILTERS = (
  146. 'compressor.filters.css_default.CssAbsoluteFilter',
  147. )
  148. COMPRESS_ENABLED = True
  149. COMPRESS_OUTPUT_DIR = 'dashboard'
  150. COMPRESS_CSS_HASHING_METHOD = 'hash'
  151. COMPRESS_PARSER = 'compressor.parser.HtmlParser'
  152. INSTALLED_APPS = [
  153. 'openstack_dashboard',
  154. 'django.contrib.contenttypes',
  155. 'django.contrib.auth',
  156. 'django.contrib.sessions',
  157. 'django.contrib.messages',
  158. 'django.contrib.staticfiles',
  159. 'django.contrib.humanize',
  160. 'django_pyscss',
  161. 'openstack_dashboard.django_pyscss_fix',
  162. 'compressor',
  163. 'horizon',
  164. 'openstack_auth',
  165. ]
  166. TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'
  167. AUTHENTICATION_BACKENDS = ('openstack_auth.backend.KeystoneBackend',)
  168. AUTHENTICATION_URLS = ['openstack_auth.urls']
  169. AUTH_USER_MODEL = 'openstack_auth.User'
  170. MESSAGE_STORAGE = 'django.contrib.messages.storage.fallback.FallbackStorage'
  171. SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'
  172. SESSION_COOKIE_HTTPONLY = True
  173. SESSION_EXPIRE_AT_BROWSER_CLOSE = True
  174. SESSION_COOKIE_SECURE = False
  175. # SESSION_TIMEOUT is a method to supersede the token timeout with a shorter
  176. # horizon session timeout (in seconds). So if your token expires in 60
  177. # minutes, a value of 1800 will log users out after 30 minutes
  178. SESSION_TIMEOUT = 3600
  179. # When using cookie-based sessions, log error when the session cookie exceeds
  180. # the following size (common browsers drop cookies above a certain size):
  181. SESSION_COOKIE_MAX_SIZE = 4093
  182. # when doing upgrades, it may be wise to stick to PickleSerializer
  183. # NOTE(berendt): Check during the K-cycle if this variable can be removed.
  184. # https://bugs.launchpad.net/horizon/+bug/1349463
  185. SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'
  186. CSRF_FAILURE_VIEW = 'openstack_dashboard.views.csrf_failure'
  187. LANGUAGES = (
  188. ('cs', 'Czech'),
  189. ('de', 'German'),
  190. ('en', 'English'),
  191. ('en-au', 'Australian English'),
  192. ('en-gb', 'British English'),
  193. ('eo', 'Esperanto'),
  194. ('es', 'Spanish'),
  195. ('fr', 'French'),
  196. ('id', 'Indonesian'),
  197. ('it', 'Italian'),
  198. ('ja', 'Japanese'),
  199. ('ko', 'Korean (Korea)'),
  200. ('pl', 'Polish'),
  201. ('pt-br', 'Portuguese (Brazil)'),
  202. ('ru', 'Russian'),
  203. ('tr', 'Turkish'),
  204. ('zh-cn', 'Simplified Chinese'),
  205. ('zh-tw', 'Chinese (Taiwan)'),
  206. )
  207. LANGUAGE_CODE = 'en'
  208. LANGUAGE_COOKIE_NAME = 'horizon_language'
  209. USE_I18N = True
  210. USE_L10N = True
  211. USE_TZ = True
  212. LOCALE_PATHS = [
  213. 'horizon/locale',
  214. 'openstack_dashboard/locale',
  215. ]
  216. # Set OPENSTACK_CLOUDS_YAML_NAME to provide a nicer name for this cloud for
  217. # the clouds.yaml file than "openstack".
  218. OPENSTACK_CLOUDS_YAML_NAME = 'openstack'
  219. # If this cloud has a vendor profile in os-client-config, put it's name here.
  220. OPENSTACK_CLOUDS_YAML_PROFILE = ''
  221. OPENSTACK_KEYSTONE_DEFAULT_ROLE = '_member_'
  222. DEFAULT_EXCEPTION_REPORTER_FILTER = 'horizon.exceptions.HorizonReporterFilter'
  223. POLICY_FILES_PATH = os.path.join(ROOT_PATH, "conf")
  224. # Map of local copy of service policy files
  225. POLICY_FILES = {
  226. 'identity': 'keystone_policy.json',
  227. 'compute': 'nova_policy.json',
  228. 'volume': 'cinder_policy.json',
  229. 'image': 'glance_policy.json',
  230. 'network': 'neutron_policy.json',
  231. }
  232. # Services for which horizon has extra policies are defined
  233. # in POLICY_DIRS by default.
  234. POLICY_DIRS = {
  235. 'compute': ['nova_policy.d'],
  236. 'volume': ['cinder_policy.d'],
  237. }
  238. SECRET_KEY = None
  239. LOCAL_PATH = None
  240. SECURITY_GROUP_RULES = {
  241. 'all_tcp': {
  242. 'name': _('All TCP'),
  243. 'ip_protocol': 'tcp',
  244. 'from_port': '1',
  245. 'to_port': '65535',
  246. },
  247. 'all_udp': {
  248. 'name': _('All UDP'),
  249. 'ip_protocol': 'udp',
  250. 'from_port': '1',
  251. 'to_port': '65535',
  252. },
  253. 'all_icmp': {
  254. 'name': _('All ICMP'),
  255. 'ip_protocol': 'icmp',
  256. 'from_port': '-1',
  257. 'to_port': '-1',
  258. },
  259. }
  260. ADD_INSTALLED_APPS = []
  261. USER_MENU_LINKS = [
  262. {'name': _('OpenStack RC File v2'),
  263. 'icon_classes': ['fa-download', ],
  264. 'url': 'horizon:project:api_access:openrcv2'
  265. },
  266. {'name': _('OpenStack RC File v3'),
  267. 'icon_classes': ['fa-download', ],
  268. 'url': 'horizon:project:api_access:openrc'
  269. }
  270. ]
  271. # Deprecated Theme Settings
  272. CUSTOM_THEME_PATH = None
  273. DEFAULT_THEME_PATH = None
  274. # 'key', 'label', 'path'
  275. AVAILABLE_THEMES = [
  276. (
  277. 'default',
  278. pgettext_lazy('Default style theme', 'Default'),
  279. 'themes/default'
  280. ), (
  281. 'material',
  282. pgettext_lazy("Google's Material Design style theme", "Material"),
  283. 'themes/material'
  284. ),
  285. ]
  286. # The default theme if no cookie is present
  287. DEFAULT_THEME = 'default'
  288. # Theme Static Directory
  289. THEME_COLLECTION_DIR = 'themes'
  290. # Theme Cookie Name
  291. THEME_COOKIE_NAME = 'theme'
  292. POLICY_CHECK_FUNCTION = 'openstack_auth.policy.check'
  293. CSRF_COOKIE_AGE = None
  294. COMPRESS_OFFLINE_CONTEXT = 'horizon.themes.offline_context'
  295. SHOW_KEYSTONE_V2_RC = True
  296. # Dictionary of currently available angular features
  297. ANGULAR_FEATURES = {
  298. 'images_panel': True,
  299. 'key_pairs_panel': True,
  300. 'flavors_panel': False,
  301. 'domains_panel': False,
  302. 'users_panel': False,
  303. 'groups_panel': False,
  304. 'roles_panel': True
  305. }
  306. # Notice all customizable configurations should be above this line
  307. XSTATIC_MODULES = settings_utils.BASE_XSTATIC_MODULES
  308. OPENSTACK_PROFILER = {
  309. 'enabled': False
  310. }
  311. if not LOCAL_PATH:
  312. LOCAL_PATH = os.path.join(ROOT_PATH, 'local')
  313. LOCAL_SETTINGS_DIR_PATH = os.path.join(LOCAL_PATH, "local_settings.d")
  314. _files = glob.glob(os.path.join(LOCAL_PATH, 'local_settings.conf'))
  315. _files.extend(
  316. sorted(glob.glob(os.path.join(LOCAL_SETTINGS_DIR_PATH, '*.conf'))))
  317. _config = config.load_config(_files, ROOT_PATH, LOCAL_PATH)
  318. # Apply the general configuration.
  319. config.apply_config(_config, globals())
  320. try:
  321. from local.local_settings import * # noqa: F403,H303
  322. except ImportError:
  323. _LOG.warning("No local_settings file found.")
  324. # configure templates
  325. if not TEMPLATES[0]['DIRS']:
  326. TEMPLATES[0]['DIRS'] = [os.path.join(ROOT_PATH, 'templates')]
  327. # configure template debugging
  328. TEMPLATES[0]['OPTIONS']['debug'] = DEBUG
  329. # Template loaders
  330. if DEBUG:
  331. TEMPLATES[0]['OPTIONS']['loaders'].extend(
  332. CACHED_TEMPLATE_LOADERS + ADD_TEMPLATE_LOADERS
  333. )
  334. else:
  335. TEMPLATES[0]['OPTIONS']['loaders'].extend(
  336. [('django.template.loaders.cached.Loader', CACHED_TEMPLATE_LOADERS)] +
  337. ADD_TEMPLATE_LOADERS
  338. )
  339. # allow to drop settings snippets into a local_settings_dir
  340. LOCAL_SETTINGS_DIR_PATH = os.path.join(ROOT_PATH, "local", "local_settings.d")
  341. if os.path.exists(LOCAL_SETTINGS_DIR_PATH):
  342. for (dirpath, dirnames, filenames) in os.walk(LOCAL_SETTINGS_DIR_PATH):
  343. for filename in sorted(filenames):
  344. if filename.endswith(".py"):
  345. try:
  346. with open(os.path.join(dirpath, filename)) as f:
  347. exec(f.read())
  348. except Exception as e:
  349. _LOG.exception(
  350. "Can not exec settings snippet %s", filename)
  351. # The purpose of OPENSTACK_IMAGE_FORMATS is to provide a simple object
  352. # that does not contain the lazy-loaded translations, so the list can
  353. # be sent as JSON to the client-side (Angular).
  354. OPENSTACK_IMAGE_FORMATS = [fmt for (fmt, name)
  355. in OPENSTACK_IMAGE_BACKEND['image_formats']]
  356. if not WEBROOT.endswith('/'):
  357. WEBROOT += '/'
  358. if LOGIN_URL is None:
  359. LOGIN_URL = WEBROOT + 'auth/login/'
  360. if LOGOUT_URL is None:
  361. LOGOUT_URL = WEBROOT + 'auth/logout/'
  362. if LOGIN_REDIRECT_URL is None:
  363. LOGIN_REDIRECT_URL = WEBROOT
  364. if MEDIA_ROOT is None:
  365. MEDIA_ROOT = os.path.abspath(os.path.join(ROOT_PATH, '..', 'media'))
  366. if MEDIA_URL is None:
  367. MEDIA_URL = WEBROOT + 'media/'
  368. if STATIC_ROOT is None:
  369. STATIC_ROOT = os.path.abspath(os.path.join(ROOT_PATH, '..', 'static'))
  370. if STATIC_URL is None:
  371. STATIC_URL = WEBROOT + 'static/'
  372. AVAILABLE_THEMES, SELECTABLE_THEMES, DEFAULT_THEME = (
  373. theme_settings.get_available_themes(
  374. AVAILABLE_THEMES,
  375. CUSTOM_THEME_PATH,
  376. DEFAULT_THEME_PATH,
  377. DEFAULT_THEME,
  378. SELECTABLE_THEMES
  379. )
  380. )
  381. if CUSTOM_THEME_PATH is not None:
  382. _LOG.warning("CUSTOM_THEME_PATH has been deprecated. Please convert "
  383. "your settings to make use of AVAILABLE_THEMES.")
  384. if DEFAULT_THEME_PATH is not None:
  385. _LOG.warning("DEFAULT_THEME_PATH has been deprecated. Please convert "
  386. "your settings to make use of AVAILABLE_THEMES.")
  387. # Discover all the directories that contain static files; at the same time
  388. # discover all the xstatic module entry points to embed in our HTML
  389. STATICFILES_DIRS = settings_utils.get_xstatic_dirs(
  390. XSTATIC_MODULES, HORIZON_CONFIG)
  391. STATICFILES_DIRS += theme_settings.get_theme_static_dirs(
  392. AVAILABLE_THEMES, THEME_COLLECTION_DIR, ROOT_PATH)
  393. # Ensure that we always have a SECRET_KEY set, even when no local_settings.py
  394. # file is present. See local_settings.py.example for full documentation on the
  395. # horizon.utils.secret_key module and its use.
  396. if not SECRET_KEY:
  397. if not LOCAL_PATH:
  398. LOCAL_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)),
  399. 'local')
  400. from horizon.utils import secret_key
  401. SECRET_KEY = secret_key.generate_or_read_from_file(os.path.join(LOCAL_PATH,
  402. '.secret_key_store'))
  403. # populate HORIZON_CONFIG with auto-discovered JavaScript sources, mock files,
  404. # specs files and external templates.
  405. settings_utils.find_static_files(HORIZON_CONFIG, AVAILABLE_THEMES,
  406. THEME_COLLECTION_DIR, ROOT_PATH)
  407. # Load the pluggable dashboard settings
  408. import openstack_dashboard.enabled
  409. import openstack_dashboard.local.enabled
  410. INSTALLED_APPS = list(INSTALLED_APPS) # Make sure it's mutable
  411. settings_utils.update_dashboards(
  412. [
  413. openstack_dashboard.enabled,
  414. openstack_dashboard.local.enabled,
  415. ],
  416. HORIZON_CONFIG,
  417. INSTALLED_APPS,
  418. )
  419. INSTALLED_APPS[0:0] = ADD_INSTALLED_APPS
  420. NG_TEMPLATE_CACHE_AGE = NG_TEMPLATE_CACHE_AGE if not DEBUG else 0
  421. # This base context objects gets added to the offline context generator
  422. # for each theme configured.
  423. HORIZON_COMPRESS_OFFLINE_CONTEXT_BASE = {
  424. 'WEBROOT': WEBROOT,
  425. 'STATIC_URL': STATIC_URL,
  426. 'HORIZON_CONFIG': HORIZON_CONFIG,
  427. 'NG_TEMPLATE_CACHE_AGE': NG_TEMPLATE_CACHE_AGE,
  428. }
  429. if DEBUG:
  430. logging.basicConfig(level=logging.DEBUG)
  431. # Here comes the Django settings deprecation section. Being at the very end
  432. # of settings.py allows it to catch the settings defined in local_settings.py
  433. # or inside one of local_settings.d/ snippets.
  434. if 'HORIZON_IMAGES_ALLOW_UPLOAD' in globals():
  435. message = 'The setting HORIZON_IMAGES_ALLOW_UPLOAD is deprecated in ' \
  436. 'Newton and will be removed in P release. Use the setting ' \
  437. 'HORIZON_IMAGES_UPLOAD_MODE instead.'
  438. if not HORIZON_IMAGES_ALLOW_UPLOAD:
  439. message += ' Keep in mind that HORIZON_IMAGES_ALLOW_UPLOAD set to ' \
  440. 'False overrides the value of HORIZON_IMAGES_UPLOAD_MODE.'
  441. _LOG.warning(message)