servers.py 10.3 KB
Newer Older
Will Daly committed
1 2 3
"""
Run and manage servers for local development.
"""
4
from __future__ import print_function
5

Will Daly committed
6
import argparse
7 8 9
import sys

from paver.easy import call_task, cmdopts, consume_args, needs, sh, task
10 11

from .assets import collect_assets
Will Daly committed
12
from .utils.cmd import django_cmd
13
from .utils.envs import Env
14
from .utils.process import run_multi_processes, run_process
15
from .utils.timer import timed
Will Daly committed
16 17

DEFAULT_PORT = {"lms": 8000, "studio": 8001}
18
DEFAULT_SETTINGS = Env.DEVSTACK_SETTINGS
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
OPTIMIZED_SETTINGS = "devstack_optimized"
OPTIMIZED_ASSETS_SETTINGS = "test_static_optimized"

ASSET_SETTINGS_HELP = (
    "Settings file used for updating assets. Defaults to the value of the settings variable if not provided."
)


def run_server(
        system, fast=False, settings=None, asset_settings=None, port=None, contracts=False
):
    """Start the server for LMS or Studio.

    Args:
        system (str): The system to be run (lms or studio).
        fast (bool): If true, then start the server immediately without updating assets (defaults to False).
        settings (str): The Django settings module to use; if not provided, use the default.
        asset_settings (str) The settings to use when generating assets. If not provided, assets are not generated.
        port (str): The port number to run the server on. If not provided, uses the default port for the system.
        contracts (bool) If true then PyContracts is enabled (defaults to False).
Will Daly committed
39 40
    """
    if system not in ['lms', 'studio']:
41
        print("System must be either lms or studio", file=sys.stderr)
Will Daly committed
42 43
        exit(1)

44 45 46
    if not settings:
        settings = DEFAULT_SETTINGS

47 48 49 50 51 52 53
    if not fast and asset_settings:
        args = [system, '--settings={}'.format(asset_settings), '--watch']
        # The default settings use DEBUG mode for running the server which means that
        # the optimized assets are ignored, so we skip collectstatic in that case
        # to save time.
        if settings == DEFAULT_SETTINGS:
            args.append('--skip-collect')
Will Daly committed
54 55 56 57 58
        call_task('pavelib.assets.update_assets', args=args)

    if port is None:
        port = DEFAULT_PORT[system]

59 60 61 62 63 64
    args = [settings, 'runserver', '--traceback', '--pythonpath=.', '0.0.0.0:{}'.format(port)]

    if contracts:
        args.append("--contracts")

    run_process(django_cmd(system, *args))
Will Daly committed
65 66 67 68 69 70


@task
@needs('pavelib.prereqs.install_prereqs')
@cmdopts([
    ("settings=", "s", "Django settings"),
71
    ("asset-settings=", "a", ASSET_SETTINGS_HELP),
Will Daly committed
72
    ("port=", "p", "Port"),
73
    ("fast", "f", "Skip updating assets"),
Will Daly committed
74 75 76 77 78
])
def lms(options):
    """
    Run the LMS server.
    """
79 80
    settings = getattr(options, 'settings', DEFAULT_SETTINGS)
    asset_settings = getattr(options, 'asset-settings', settings)
Will Daly committed
81 82
    port = getattr(options, 'port', None)
    fast = getattr(options, 'fast', False)
83 84 85 86 87 88 89
    run_server(
        'lms',
        fast=fast,
        settings=settings,
        asset_settings=asset_settings,
        port=port,
    )
Will Daly committed
90 91 92 93 94 95


@task
@needs('pavelib.prereqs.install_prereqs')
@cmdopts([
    ("settings=", "s", "Django settings"),
96
    ("asset-settings=", "a", ASSET_SETTINGS_HELP),
Will Daly committed
97
    ("port=", "p", "Port"),
98
    ("fast", "f", "Skip updating assets"),
Will Daly committed
99 100 101 102 103
])
def studio(options):
    """
    Run the Studio server.
    """
104 105
    settings = getattr(options, 'settings', DEFAULT_SETTINGS)
    asset_settings = getattr(options, 'asset-settings', settings)
Will Daly committed
106 107
    port = getattr(options, 'port', None)
    fast = getattr(options, 'fast', False)
108 109 110 111 112 113 114
    run_server(
        'studio',
        fast=fast,
        settings=settings,
        asset_settings=asset_settings,
        port=port,
    )
Will Daly committed
115 116 117 118 119 120 121 122 123 124 125 126


@task
@needs('pavelib.prereqs.install_prereqs')
@consume_args
def devstack(args):
    """
    Start the devstack lms or studio server
    """
    parser = argparse.ArgumentParser(prog='paver devstack')
    parser.add_argument('system', type=str, nargs=1, help="lms or studio")
    parser.add_argument('--fast', action='store_true', default=False, help="Skip updating assets")
127 128 129
    parser.add_argument('--optimized', action='store_true', default=False, help="Run with optimized assets")
    parser.add_argument('--settings', type=str, default=DEFAULT_SETTINGS, help="Settings file")
    parser.add_argument('--asset-settings', type=str, default=None, help=ASSET_SETTINGS_HELP)
130 131 132 133 134 135
    parser.add_argument(
        '--no-contracts',
        action='store_true',
        default=False,
        help="Disable contracts. By default, they're enabled in devstack."
    )
Will Daly committed
136
    args = parser.parse_args(args)
137 138 139 140 141
    settings = args.settings
    asset_settings = args.asset_settings if args.asset_settings else settings
    if args.optimized:
        settings = OPTIMIZED_SETTINGS
        asset_settings = OPTIMIZED_ASSETS_SETTINGS
142
    sh(django_cmd('cms', settings, 'reindex_course', '--setup'))
143 144 145 146 147 148 149
    run_server(
        args.system[0],
        fast=args.fast,
        settings=settings,
        asset_settings=asset_settings,
        contracts=not args.no_contracts,
    )
Will Daly committed
150 151 152 153 154 155 156 157 158 159 160


@task
@needs('pavelib.prereqs.install_prereqs')
@cmdopts([
    ("settings=", "s", "Django settings"),
])
def celery(options):
    """
    Runs Celery workers.
    """
161
    settings = getattr(options, 'settings', 'devstack_with_worker')
162
    run_process(django_cmd('lms', settings, 'celery', 'worker', '--beat', '--loglevel=INFO', '--pythonpath=.'))
Will Daly committed
163 164 165 166 167


@task
@needs('pavelib.prereqs.install_prereqs')
@cmdopts([
168
    ("settings=", "s", "Django settings for both LMS and Studio"),
169 170
    ("asset-settings=", "a", "Django settings for updating assets for both LMS and Studio (defaults to settings)"),
    ("worker-settings=", "w", "Celery worker Django settings"),
171
    ("fast", "f", "Skip updating assets"),
172
    ("optimized", "o", "Run with optimized assets"),
173 174 175 176 177 178 179 180 181 182 183
    ("settings-lms=", "l", "Set LMS only, overriding the value from --settings (if provided)"),
    ("asset-settings-lms=", None, "Set LMS only, overriding the value from --asset-settings (if provided)"),
    ("settings-cms=", "c", "Set Studio only, overriding the value from --settings (if provided)"),
    ("asset-settings-cms=", None, "Set Studio only, overriding the value from --asset-settings (if provided)"),

    ("asset_settings=", None, "deprecated in favor of asset-settings"),
    ("asset_settings_cms=", None, "deprecated in favor of asset-settings-cms"),
    ("asset_settings_lms=", None, "deprecated in favor of asset-settings-lms"),
    ("settings_cms=", None, "deprecated in favor of settings-cms"),
    ("settings_lms=", None, "deprecated in favor of settings-lms"),
    ("worker_settings=", None, "deprecated in favor of worker-settings"),
Will Daly committed
184 185 186 187 188
])
def run_all_servers(options):
    """
    Runs Celery workers, Studio, and LMS.
    """
189
    settings = getattr(options, 'settings', DEFAULT_SETTINGS)
190
    asset_settings = getattr(options, 'asset_settings', settings)
191
    worker_settings = getattr(options, 'worker_settings', 'devstack_with_worker')
Will Daly committed
192
    fast = getattr(options, 'fast', False)
193 194 195 196 197 198 199 200 201 202
    optimized = getattr(options, 'optimized', False)

    if optimized:
        settings = OPTIMIZED_SETTINGS
        asset_settings = OPTIMIZED_ASSETS_SETTINGS

    settings_lms = getattr(options, 'settings_lms', settings)
    settings_cms = getattr(options, 'settings_cms', settings)
    asset_settings_lms = getattr(options, 'asset_settings_lms', asset_settings)
    asset_settings_cms = getattr(options, 'asset_settings_cms', asset_settings)
Will Daly committed
203 204

    if not fast:
205 206 207 208 209 210
        # First update assets for both LMS and Studio but don't collect static yet
        args = [
            'lms', 'studio',
            '--settings={}'.format(asset_settings),
            '--skip-collect'
        ]
211 212
        call_task('pavelib.assets.update_assets', args=args)

213 214 215 216 217 218 219
        # Now collect static for each system separately with the appropriate settings.
        # Note that the default settings use DEBUG mode for running the server which
        # means that the optimized assets are ignored, so we skip collectstatic in that
        # case to save time.
        if settings != DEFAULT_SETTINGS:
            collect_assets(['lms'], asset_settings_lms)
            collect_assets(['studio'], asset_settings_cms)
220

221
        # Install an asset watcher to regenerate files that change
222
        call_task('pavelib.assets.watch_assets', options={'background': True})
223 224 225 226 227 228 229

    # Start up LMS, CMS and Celery
    lms_port = DEFAULT_PORT['lms']
    cms_port = DEFAULT_PORT['studio']
    lms_runserver_args = ["0.0.0.0:{}".format(lms_port)]
    cms_runserver_args = ["0.0.0.0:{}".format(cms_port)]

Will Daly committed
230
    run_multi_processes([
231 232 233 234 235 236 237 238 239
        django_cmd(
            'lms', settings_lms, 'runserver', '--traceback', '--pythonpath=.', *lms_runserver_args
        ),
        django_cmd(
            'studio', settings_cms, 'runserver', '--traceback', '--pythonpath=.', *cms_runserver_args
        ),
        django_cmd(
            'lms', worker_settings, 'celery', 'worker', '--beat', '--loglevel=INFO', '--pythonpath=.'
        )
Will Daly committed
240 241 242 243 244 245 246
    ])


@task
@needs('pavelib.prereqs.install_prereqs')
@cmdopts([
    ("settings=", "s", "Django settings"),
247
    ("fake-initial", None, "Fake the initial migrations"),
Will Daly committed
248
])
249
@timed
250
def update_db(options):
Will Daly committed
251
    """
252
    Migrates the lms and cms across all databases
Will Daly committed
253
    """
254
    settings = getattr(options, 'settings', DEFAULT_SETTINGS)
255
    fake = "--fake-initial" if getattr(options, 'fake_initial', False) else ""
256
    for system in ('lms', 'cms'):
257 258 259 260 261
        # pylint: disable=line-too-long
        sh("NO_EDXAPP_SUDO=1 EDX_PLATFORM_SETTINGS_OVERRIDE={settings} /edx/bin/edxapp-migrate-{system} --traceback --pythonpath=. {fake}".format(
            settings=settings,
            system=system,
            fake=fake))
Will Daly committed
262 263 264 265 266


@task
@needs('pavelib.prereqs.install_prereqs')
@consume_args
267
@timed
Will Daly committed
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
def check_settings(args):
    """
    Checks settings files.
    """
    parser = argparse.ArgumentParser(prog='paver check_settings')
    parser.add_argument('system', type=str, nargs=1, help="lms or studio")
    parser.add_argument('settings', type=str, nargs=1, help='Django settings')
    args = parser.parse_args(args)

    system = args.system[0]
    settings = args.settings[0]

    try:
        import_cmd = "echo 'import {system}.envs.{settings}'".format(system=system, settings=settings)
        django_shell_cmd = django_cmd(system, settings, 'shell', '--plain', '--pythonpath=.')
        sh("{import_cmd} | {shell_cmd}".format(import_cmd=import_cmd, shell_cmd=django_shell_cmd))

285
    except:  # pylint: disable=bare-except
286
        print("Failed to import settings", file=sys.stderr)