예제 #1
0
 def save(self, *args, **kwargs):
     created = not self.pk
     super(Resource, self).save(*args, **kwargs)
     self.sync_periodic_task()
     # This only work on tests (multiprocessing used on real deployments)
     apps.get_app_config('resources').reload_relations()
     run('{ sleep 2 && touch %s/wsgi.py; } &' % get_project_dir(), async=True)
예제 #2
0
 def form_valid(self, form):
     settings = Setting.settings
     changes = {}
     for data in form.cleaned_data:
         setting = settings[data['name']]
         if not isinstance(data['value'], parser.NotSupported) and setting.editable:
             if setting.value != data['value']:
                 if setting.default == data['value']:
                     changes[setting.name] = parser.Remove()
                 else:
                     changes[setting.name] = parser.serialize(data['value'])
     if changes:
         # Display confirmation
         if not self.request.POST.get('confirmation'):
             settings_file = parser.get_settings_file()
             new_content = parser.apply(changes)
             diff = sys.run("cat <<EOF | diff %s -\n%s\nEOF" % (settings_file, new_content), error_codes=[1, 0]).stdout
             context = self.get_context_data(form=form)
             context['diff'] = diff
             return self.render_to_response(context)
         
         # Save changes
         parser.save(changes)
         sys.run('{ sleep 2 && touch %s/wsgi.py; } &' % paths.get_project_dir(), async=True)
         n = len(changes)
         context = {
             'message': ngettext(
                 _("One change successfully applied, orchestra is being restarted."),
                 _("%s changes successfully applied, orchestra is being restarted.") % n,
                 n),
         }
         return render_to_response(self.reload_template_name, context)
     else:
         messages.success(self.request, _("No changes have been detected."))
     return super(SettingView, self).form_valid(form)
예제 #3
0
 def handle(self, *args, **options):
     # Configure firmware generation
     context = {
         'db_name': options.get('db_name'),
         'db_user': options.get('db_user'),
         'db_password': options.get('db_password'),
         'db_host': options.get('db_host'),
         'db_port': options.get('db_port') }
     
     run('su postgres -c "psql -c \\"CREATE USER %(db_user)s PASSWORD \'%(db_password)s\';\\""' % context, valid_codes=(0,1))
     run('su postgres -c "psql -c \\"CREATE DATABASE %(db_name)s OWNER %(db_user)s;\\""' % context, valid_codes=(0,1))
     context.update({'settings': os.path.join(get_project_dir(), 'settings.py')})
     
     if run("grep 'DATABASES' %(settings)s" % context, valid_codes=(0,1)).exit_code == 0:
         # Update existing settings_file
         run("sed -i \"s/'ENGINE': '\w*',/'ENGINE': 'django.db.backends.postgresql_psycopg2',/\" %(settings)s" % context)
         run("sed -i \"s/'NAME': '.*',/'NAME': '%(db_name)s',/\" %(settings)s" % context)
         run("sed -i \"s/'USER': '******',/'USER': '******',/\" %(settings)s" % context)
         run("sed -i \"s/'PASSWORD': '******',/'PASSWORD': '******',/\" %(settings)s" % context)
         run("sed -i \"s/'HOST': '.*',/'HOST': '%(db_host)s',/\" %(settings)s" % context)
         run("sed -i \"s/'PORT': '.*',/'PORT': '%(db_port)s',/\" %(settings)s" % context)
     else:
         db_config = (
             "DATABASES = {\n"
             "    'default': {\n"
             "        'ENGINE': 'django.db.backends.postgresql_psycopg2',\n"
             "        'NAME': '%(db_name)s',\n"
             "        'USER': '******',\n"
             "        'PASSWORD': '******',\n"
             "        'HOST': '%(db_host)s',\n"
             "        'PORT': '%(db_port)s',\n"
             "        'ATOMIC_REQUESTS': True,\n"
             "    }\n"
             "}\n" % context)
         context.update({'db_config': db_config})
         run('echo "%(db_config)s" >> %(settings)s' % context)
예제 #4
0
 def handle(self, *args, **options):
     user = options.get('user')
     if not user:
         raise CommandError("System user for running uwsgi must be provided.")
     
     cert_path, key_path = self.generate_certificate(**options)
     server_name = options.get('server_name')
     
     context = {
         'cert_path': cert_path,
         'key_path': key_path,
         'project_name': paths.get_project_name(),
         'project_dir': paths.get_project_dir(),
         'site_dir': paths.get_site_dir(),
         'static_root': settings.STATIC_ROOT,
         'static_url': (settings.STATIC_URL or '/static').rstrip('/'),
         'user': user,
         'group': options.get('group') or user,
         'home': expanduser("~%s" % options.get('user')),
         'processes': int(options.get('processes')),
         'server_name': 'server_name %s' % server_name if server_name else ''
     }
     
     nginx_conf = textwrap.dedent("""\
         server {
             listen 80;
             listen [::]:80 ipv6only=on;
             return 301 https://$host$request_uri;
         }
         
         server {
             listen 443 ssl;
             # listen [::]:443 ssl; # add SSL support to IPv6 address
             %(server_name)s
             ssl_certificate %(cert_path)s;
             ssl_certificate_key %(key_path)s;
             rewrite ^/$ /admin/;
             client_max_body_size 16m;
             location / {
                 uwsgi_pass unix:///var/run/uwsgi/app/%(project_name)s/socket;
                 include uwsgi_params;
             }
             location %(static_url)s {
                 alias %(static_root)s;
                 expires 30d;
             }
         }
         """
     ) % context
     
     uwsgi_conf = textwrap.dedent("""\
         [uwsgi]
         plugins        = python3
         chdir          = %(site_dir)s
         module         = %(project_name)s.wsgi
         master         = true
         workers        = %(processes)d
         chmod-socket   = 664
         stats          = /run/uwsgi/%%(deb-confnamespace)/%%(deb-confname)/statsocket
         uid            = %(user)s
         gid            = %(group)s
         env            = HOME=%(home)s
         touch-reload   = %(project_dir)s/wsgi.py
         vacuum         = true    # Remove socket stop
         enable-threads = true    # Initializes the GIL
         max-requests   = 500     # Mitigates memory leaks
         lazy-apps      = true    # Don't share database connections
         """
     ) % context
     
     nginx_file = '/etc/nginx/sites-available/%(project_name)s.conf' % context
     if server_name:
         context['server_name'] = server_name
         nginx_file = '/etc/nginx/sites-available/%(server_name)s.conf' % context
     nginx = {
         'file': nginx_file,
         'conf': nginx_conf
     }
     uwsgi = {
         'file': '/etc/uwsgi/apps-available/%(project_name)s.ini' % context,
         'conf': uwsgi_conf
     }
     
     interactive = options.get('interactive')
     for extra_context in (nginx, uwsgi):
         context.update(extra_context)
         diff = run("cat << 'EOF' | diff - %(file)s\n%(conf)s\nEOF" % context, valid_codes=(0,1,2))
         if diff.exit_code == 2:
             # File does not exist
             run("cat << 'EOF' > %(file)s\n%(conf)s\nEOF" % context, display=True)
         elif diff.exit_code == 1:
             # File is different, save the old one
             if interactive:
                 if not confirm("\n\nFile %(file)s be updated, do you like to overide "
                                "it? (yes/no): " % context):
                     return
             run(textwrap.dedent("""\
                 cp %(file)s %(file)s.save
                 cat << 'EOF' > %(file)s
                 %(conf)s
                 EOF""") % context, display=True
             )
             self.stdout.write("\033[1;31mA new version of %(file)s has been installed.\n "
                 "The old version has been placed at %(file)s.save\033[m" % context)
     
     if server_name:
         run('ln -s /etc/nginx/sites-available/%(server_name)s.conf /etc/nginx/sites-enabled/' % context,
             valid_codes=[0,1], display=True)
     else:
         run('rm -f /etc/nginx/sites-enabled/default')
         run('ln -s /etc/nginx/sites-available/%(project_name)s.conf /etc/nginx/sites-enabled/' % context,
             valid_codes=[0,1], display=True)
     run('ln -s /etc/uwsgi/apps-available/%(project_name)s.ini /etc/uwsgi/apps-enabled/' % context,
         valid_codes=[0,1], display=True)
     
     rotate = textwrap.dedent("""\
         /var/log/nginx/*.log {
             daily
             missingok
             rotate 30
             compress
             delaycompress
             notifempty
             create 640 root adm
             sharedscripts
             postrotate
                 [ ! -f /var/run/nginx.pid ] || kill -USR1 `cat /var/run/nginx.pid`
             endscript
         }"""
     )
     run("echo '%s' > /etc/logrotate.d/nginx" % rotate, display=True)
     
     # Allow nginx to write to uwsgi socket
     run('adduser www-data %(group)s' % context, display=True)
예제 #5
0
def get_settings_file():
    return os.path.join(get_project_dir(), 'settings.py')
예제 #6
0
    def handle(self, *args, **options):
        context = {
            'site_dir': get_site_dir(),
            'username': options.get('username'),
            'bin_path': path.join(get_orchestra_dir(), 'bin'),
            'processes': options.get('processes'),
            'settings': path.join(get_project_dir(), 'settings.py')
        }

        celery_config = textwrap.dedent("""\
            # Name of nodes to start, here we have a single node
            CELERYD_NODES="w1"
            
            # Where to chdir at start.
            CELERYD_CHDIR="%(site_dir)s"
            
            # How to call "manage.py celeryd_multi"
            CELERYD_MULTI="python3 $CELERYD_CHDIR/manage.py celeryd_multi"
            
            # Extra arguments to celeryd
            CELERYD_OPTS="-P:w1 processes -c:w1 %(processes)s -Q:w1 celery"
            
            # Name of the celery config module.
            CELERY_CONFIG_MODULE="celeryconfig"
            
            # %%n will be replaced with the nodename.
            CELERYD_LOG_FILE="/var/log/celery/%%n.log"
            CELERYD_PID_FILE="/var/run/celery/%%n.pid"
            CELERY_CREATE_DIRS=1
            
            # Full path to the celeryd logfile.
            CELERYEV_LOG_FILE="/var/log/celery/celeryev.log"
            CELERYEV_PID_FILE="/var/run/celery/celeryev.pid"
            
            # Workers should run as an unprivileged user.
            CELERYD_USER="******"
            CELERYD_GROUP="$CELERYD_USER"
            
            # Persistent revokes
            CELERYD_STATE_DB="$CELERYD_CHDIR/persistent_revokes"
            
            # Celeryev
            CELERYEV="python3 $CELERYD_CHDIR/manage.py"
            CELERYEV_CAM="djcelery.snapshot.Camera"
            CELERYEV_USER="******"
            CELERYEV_GROUP="$CELERYD_USER"
            CELERYEV_OPTS="celerycam"
            
            # Celerybeat
            CELERYBEAT="python3 ${CELERYD_CHDIR}/manage.py celerybeat"
            CELERYBEAT_USER="******"
            CELERYBEAT_GROUP="$CELERYD_USER"
            CELERYBEAT_CHDIR="$CELERYD_CHDIR"
            CELERYBEAT_OPTS="--schedule=/var/run/celerybeat-schedule --scheduler=djcelery.schedulers.DatabaseScheduler"
            """ % context)

        run("echo '%s' > /etc/default/celeryd" % celery_config)

        # https://raw.github.com/celery/celery/master/extra/generic-init.d/
        for script in ['celeryevcam', 'celeryd', 'celerybeat']:
            context['script'] = script
            run('cp %(bin_path)s/%(script)s /etc/init.d/%(script)s' % context)
            run('chmod +x /etc/init.d/%(script)s' % context)
            run('update-rc.d %(script)s defaults' % context)

        rotate = textwrap.dedent("""\
            /var/log/celery/*.log {
                weekly
                missingok
                rotate 10
                compress
                delaycompress
                notifempty
                copytruncate
            }""")
        run("echo '%s' > /etc/logrotate.d/celeryd" % rotate)

        changes = {}
        if Setting.settings['TASKS_BACKEND'].value != 'celery':
            changes['TASKS_BACKEND'] = 'celery'
        if Setting.settings[
                'ORCHESTRA_START_SERVICES'].value == Setting.settings[
                    'ORCHESTRA_START_SERVICES'].default:
            changes['ORCHESTRA_START_SERVICES'] = (
                'postgresql',
                'celeryevcam',
                'celeryd',
                'celerybeat',
                ('uwsgi', 'nginx'),
            )
        if Setting.settings[
                'ORCHESTRA_RESTART_SERVICES'].value == Setting.settings[
                    'ORCHESTRA_RESTART_SERVICES'].default:
            changes['ORCHESTRA_RESTART_SERVICES'] = (
                'celeryd',
                'celerybeat',
                'uwsgi',
            )
        if Setting.settings[
                'ORCHESTRA_STOP_SERVICES'].value == Setting.settings[
                    'ORCHESTRA_STOP_SERVICES'].default:
            changes['ORCHESTRA_STOP_SERVICES'] = (('uwsgi', 'nginx'),
                                                  'celerybeat', 'celeryd',
                                                  'celeryevcam', 'postgresql')
        if changes:
            settings_parser.apply(changes)
예제 #7
0
 def handle(self, *args, **options):
     interactive = options.get('interactive')
     context = {
         'site_dir': get_site_dir(),
         'settings_path': os.path.join(get_project_dir(), 'settings.py'),
         'project_name': get_project_name(),
         'log_dir': os.path.join(get_site_dir(), 'log'),
         'log_path': os.path.join(get_site_dir(), 'log', 'orchestra.log')
     }
     has_logging = not run(
         'grep "^LOGGING\s*=" %(settings_path)s' % context,
         valid_codes=(0, 1)).exit_code
     if has_logging:
         if not interactive:
             self.stderr.write(
                 "Project settings already defines LOGGING setting, doing nothing."
             )
             return
         msg = (
             "\n\nYour project settings file already defines a LOGGING setting.\n"
             "Do you want to override it? (yes/no): ")
         if not confirm(msg):
             return
         settings_parser.save({
             'LOGGING': settings_parser.Remove(),
         })
     setuplogrotate = textwrap.dedent("""\
         mkdir %(log_dir)s && chown --reference=%(site_dir)s %(log_dir)s
         touch %(log_path)s
         chown --reference=%(log_dir)s %(log_path)s
         echo '%(log_dir)s/*.log {
           copytruncate
           daily
           rotate 5
           compress
           delaycompress
           missingok
           notifempty
         }' > /etc/logrotate.d/orchestra.%(project_name)s
         cat << 'EOF' >> %(settings_path)s
         
         LOGGING = {
             'version': 1,
             'disable_existing_loggers': False,
             'formatters': {
                 'simple': {
                     'format': '%%(asctime)s %%(name)s %%(levelname)s %%(message)s'
                 },
             },
             'handlers': {
                 'file': {
                     'class': 'logging.FileHandler',
                     'filename': '%(log_path)s',
                     'formatter': 'simple'
                 },
             },
             'loggers': {
                 'orchestra': {
                     'handlers': ['file'],
                     'level': 'INFO',
                     'propagate': True,
                 },
                 'orm': {
                     'handlers': ['file'],
                     'level': 'INFO',
                     'propagate': True,
                 },
             },
         }
         EOF""") % context
     run(setuplogrotate, display=True)
예제 #8
0
def get_settings_file():
    return os.path.join(get_project_dir(), 'settings.py')
예제 #9
0
 def handle(self, *args, **options):
     interactive = options.get("interactive")
     context = {
         "site_dir": get_site_dir(),
         "settings_path": os.path.join(get_project_dir(), "settings.py"),
         "project_name": get_project_name(),
         "log_dir": os.path.join(get_site_dir(), "log"),
         "log_path": os.path.join(get_site_dir(), "log", "orchestra.log"),
     }
     has_logging = not run('grep "^LOGGING\s*=" %(settings_path)s' % context, valid_codes=(0, 1)).exit_code
     if has_logging:
         if not interactive:
             self.stderr.write("Project settings already defines LOGGING setting, doing nothing.")
             return
         msg = (
             "\n\nYour project settings file already defines a LOGGING setting.\n"
             "Do you want to override it? (yes/no): "
         )
         if not confirm(msg):
             return
         settings_parser.save({"LOGGING": settings_parser.Remove()})
     setuplogrotate = (
         textwrap.dedent(
             """\
         mkdir %(log_dir)s && chown --reference=%(site_dir)s %(log_dir)s
         touch %(log_path)s
         chown --reference=%(log_dir)s %(log_path)s
         echo '%(log_dir)s/*.log {
           copytruncate
           daily
           rotate 5
           compress
           delaycompress
           missingok
           notifempty
         }' > /etc/logrotate.d/orchestra.%(project_name)s
         cat << 'EOF' >> %(settings_path)s
         
         LOGGING = {
             'version': 1,
             'disable_existing_loggers': False,
             'formatters': {
                 'simple': {
                     'format': '%%(asctime)s %%(name)s %%(levelname)s %%(message)s'
                 },
             },
             'handlers': {
                 'file': {
                     'class': 'logging.FileHandler',
                     'filename': '%(log_path)s',
                     'formatter': 'simple'
                 },
             },
             'loggers': {
                 'orchestra': {
                     'handlers': ['file'],
                     'level': 'INFO',
                     'propagate': True,
                 },
                 'orm': {
                     'handlers': ['file'],
                     'level': 'INFO',
                     'propagate': True,
                 },
             },
         }
         EOF"""
         )
         % context
     )
     run(setuplogrotate, display=True)
예제 #10
0
    def handle(self, *args, **options):
        interactive = options.get('interactive')
        db_password = options.get('db_password')
        context = {
            'db_name': options.get('db_name'),
            'db_user': options.get('db_user'),
            'db_password': db_password,
            'db_host': options.get('db_host'),
            'db_port': options.get('db_port'),
            'default_db_password': db_password or random_ascii(10),
        }

        create_user = "******"
        alter_user = "******"
        create_database = "CREATE DATABASE %(db_name)s OWNER %(db_user)s;"

        # Create or update user
        if self.run_postgres(create_user % context,
                             valid_codes=(0, 1)).exit_code == 1:
            if interactive and not db_password:
                msg = (
                    "Postgres user '%(db_user)s' already exists, "
                    "please provide a password [%(default_db_password)s]: " %
                    context)
                context['db_password'] = input(
                    msg) or context['default_db_password']
                self.run_postgres(alter_user % context)
                msg = "Updated Postgres user '%(db_user)s' password: '******'"
                self.stdout.write(msg % context)
            elif db_password:
                self.run_postgres(alter_user % context)
                msg = "Updated Postgres user '%(db_user)s' password: '******'"
                self.stdout.write(msg % context)
            else:
                raise CommandError(
                    "Postgres user '%(db_user)s' already exists and "
                    "--db_password has not been provided." % context)
        else:
            context['db_password'] = context['default_db_password']
            msg = "Created new Postgres user '%(db_user)s' with password '%(db_password)s'"
            self.stdout.write(msg % context)
        self.run_postgres(create_database % context, valid_codes=(0, 1))

        context.update(
            {'settings': os.path.join(get_project_dir(), 'settings.py')})

        if run("grep '^DATABASES\s*=\s*{' %(settings)s" % context,
               valid_codes=(0, 1)).exit_code == 0:
            # Update existing settings_file
            run(
                textwrap.dedent("""sed -i \\
                -e "s/'ENGINE':[^#]*/'ENGINE': 'django.db.backends.postgresql_psycopg2',  /" \\
                -e "s/'NAME':[^#]*/'NAME': '%(db_name)s',  /" \\
                -e "s/'USER':[^#]*/'USER': '******',  /" \\
                -e "s/'PASSWORD':[^#]*/'PASSWORD': '******',  /" \\
                -e "s/'HOST':[^#]*/'HOST': '%(db_host)s',  /" \\
                -e "s/'PORT':[^#]*/'PORT': '%(db_port)s',  /" %(settings)s\
                """) % context)
        else:
            db_config = textwrap.dedent("""\
                DATABASES = {
                    'default': {
                        'ENGINE': 'django.db.backends.postgresql_psycopg2',
                        'NAME': '%(db_name)s',
                        'USER': '******',
                        'PASSWORD': '******',
                        'HOST': '%(db_host)s',
                        'PORT': '%(db_port)s',
                        'ATOMIC_REQUESTS': True,
                    }
                }""") % context
            context.update({'db_config': db_config})
            run('echo "%(db_config)s" >> %(settings)s' % context)
예제 #11
0
 def handle(self, *args, **options):
     interactive = options.get('interactive')
     db_password = options.get('db_password')
     context = {
         'db_name': options.get('db_name'),
         'db_user': options.get('db_user'),
         'db_password': db_password,
         'db_host': options.get('db_host'),
         'db_port': options.get('db_port'),
         'default_db_password': db_password or random_ascii(10),
     }
     
     create_user = "******"
     alter_user = "******"
     create_database = "CREATE DATABASE %(db_name)s OWNER %(db_user)s;"
     
     # Create or update user
     if self.run_postgres(create_user % context, valid_codes=(0,1)).exit_code == 1:
         if interactive and not db_password:
             msg = ("Postgres user '%(db_user)s' already exists, "
                    "please provide a password [%(default_db_password)s]: " % context)
             context['db_password'] = input(msg) or context['default_db_password']
             self.run_postgres(alter_user % context)
             msg = "Updated Postgres user '%(db_user)s' password: '******'"
             self.stdout.write(msg % context)
         elif db_password:
             self.run_postgres(alter_user % context)
             msg = "Updated Postgres user '%(db_user)s' password: '******'"
             self.stdout.write(msg % context)
         else:
             raise CommandError("Postgres user '%(db_user)s' already exists and "
                                "--db_pass has not been provided." % context)
     else:
         context['db_password'] = context['default_db_password']
         msg = "Created new Postgres user '%(db_user)s' with password '%(db_password)s'"
         self.stdout.write(msg % context)
     self.run_postgres(create_database % context, valid_codes=(0,1))
     
     context.update({
         'settings': os.path.join(get_project_dir(), 'settings.py')
     })
     
     if run("grep '^DATABASES\s*=\s*{' %(settings)s" % context, valid_codes=(0,1)).exit_code == 0:
         # Update existing settings_file
         run(textwrap.dedent("""sed -i \\
             -e "s/'ENGINE':[^#]*/'ENGINE': 'django.db.backends.postgresql_psycopg2',  /" \\
             -e "s/'NAME':[^#]*/'NAME': '%(db_name)s',  /" \\
             -e "s/'USER':[^#]*/'USER': '******',  /" \\
             -e "s/'PASSWORD':[^#]*/'PASSWORD': '******',  /" \\
             -e "s/'HOST':[^#]*/'HOST': '%(db_host)s',  /" \\
             -e "s/'PORT':[^#]*/'PORT': '%(db_port)s',  /" %(settings)s\
             """) % context
         )
     else:
         db_config = textwrap.dedent("""\
             DATABASES = {
                 'default': {
                     'ENGINE': 'django.db.backends.postgresql_psycopg2',
                     'NAME': '%(db_name)s',
                     'USER': '******',
                     'PASSWORD': '******',
                     'HOST': '%(db_host)s',
                     'PORT': '%(db_port)s',
                     'ATOMIC_REQUESTS': True,
                 }
             }""") % context
         context.update({
             'db_config': db_config
         })
         run('echo "%(db_config)s" >> %(settings)s' % context)
예제 #12
0
 def handle(self, *args, **options):
     context = {
         'site_dir': get_site_dir(),
         'username': options.get('username'),
         'bin_path': path.join(get_orchestra_dir(), 'bin'),
         'processes': options.get('processes'),
         'settings': path.join(get_project_dir(), 'settings.py')
     }
     
     celery_config = textwrap.dedent("""\
         # Name of nodes to start, here we have a single node
         CELERYD_NODES="w1"
         
         # Where to chdir at start.
         CELERYD_CHDIR="%(site_dir)s"
         
         # How to call "manage.py celeryd_multi"
         CELERYD_MULTI="python3 $CELERYD_CHDIR/manage.py celeryd_multi"
         
         # Extra arguments to celeryd
         CELERYD_OPTS="-P:w1 processes -c:w1 %(processes)s -Q:w1 celery"
         
         # Name of the celery config module.
         CELERY_CONFIG_MODULE="celeryconfig"
         
         # %%n will be replaced with the nodename.
         CELERYD_LOG_FILE="/var/log/celery/%%n.log"
         CELERYD_PID_FILE="/var/run/celery/%%n.pid"
         CELERY_CREATE_DIRS=1
         
         # Full path to the celeryd logfile.
         CELERYEV_LOG_FILE="/var/log/celery/celeryev.log"
         CELERYEV_PID_FILE="/var/run/celery/celeryev.pid"
         
         # Workers should run as an unprivileged user.
         CELERYD_USER="******"
         CELERYD_GROUP="$CELERYD_USER"
         
         # Persistent revokes
         CELERYD_STATE_DB="$CELERYD_CHDIR/persistent_revokes"
         
         # Celeryev
         CELERYEV="python3 $CELERYD_CHDIR/manage.py"
         CELERYEV_CAM="djcelery.snapshot.Camera"
         CELERYEV_USER="******"
         CELERYEV_GROUP="$CELERYD_USER"
         CELERYEV_OPTS="celerycam"
         
         # Celerybeat
         CELERYBEAT="python3 ${CELERYD_CHDIR}/manage.py celerybeat"
         CELERYBEAT_USER="******"
         CELERYBEAT_GROUP="$CELERYD_USER"
         CELERYBEAT_CHDIR="$CELERYD_CHDIR"
         CELERYBEAT_OPTS="--schedule=/var/run/celerybeat-schedule --scheduler=djcelery.schedulers.DatabaseScheduler"
         """ % context
     )
     
     run("echo '%s' > /etc/default/celeryd" % celery_config)
     
     # https://raw.github.com/celery/celery/master/extra/generic-init.d/
     for script in ['celeryevcam', 'celeryd', 'celerybeat']:
         context['script'] = script
         run('cp %(bin_path)s/%(script)s /etc/init.d/%(script)s' % context)
         run('chmod +x /etc/init.d/%(script)s' % context)
         run('update-rc.d %(script)s defaults' % context)
     
     rotate = textwrap.dedent("""\
         /var/log/celery/*.log {
             weekly
             missingok
             rotate 10
             compress
             delaycompress
             notifempty
             copytruncate
         }"""
     )
     run("echo '%s' > /etc/logrotate.d/celeryd" % rotate)
     
     changes = {}
     if Setting.settings['TASKS_BACKEND'].value != 'celery':
         changes['TASKS_BACKEND'] = 'celery'
     if Setting.settings['ORCHESTRA_START_SERVICES'].value == Setting.settings['ORCHESTRA_START_SERVICES'].default:
         changes['ORCHESTRA_START_SERVICES'] = (
                 'postgresql',
                 'celeryevcam',
                 'celeryd',
                 'celerybeat',
                 ('uwsgi', 'nginx'),
             )
     if Setting.settings['ORCHESTRA_RESTART_SERVICES'].value == Setting.settings['ORCHESTRA_RESTART_SERVICES'].default:
         changes['ORCHESTRA_RESTART_SERVICES'] = (
                 'celeryd',
                 'celerybeat',
                 'uwsgi',
             )
     if Setting.settings['ORCHESTRA_STOP_SERVICES'].value == Setting.settings['ORCHESTRA_STOP_SERVICES'].default:
         changes['ORCHESTRA_STOP_SERVICES'] = (
                 ('uwsgi', 'nginx'),
                 'celerybeat',
                 'celeryd',
                 'celeryevcam',
                 'postgresql'
             )
     if changes:
         settings_parser.apply(changes)