def handle(self, *args, **options):
     context = {
         'site_dir': get_site_dir(),
         'orchestra_beat': run('which orchestra-beat').stdout.decode('utf8'),
         'venv': os.environ.get('VIRTUAL_ENV', ''),
     }
     content = run('crontab -l || true').stdout.decode('utf8')
     if 'orchestra-beat' not in content:
         if context['venv']:
             content += "\n* * * * * . %(venv)s/bin/activate && %(orchestra_beat)s %(site_dir)s/manage.py; deactivate" % context
         else:
             content += "\n* * * * * %(orchestra_beat)s %(site_dir)s/manage.py" % context
         context['content'] = content
         run("cat << EOF | crontab\n%(content)s\nEOF" % context, display=True)
     
     # Configrue settings to use threaded task backend (default)
     changes = {}
     if Setting.settings['TASKS_BACKEND'].value == 'celery':
         changes['TASKS_BACKEND'] = settings_parser.Remove()
     if 'celeryd' in Setting.settings['ORCHESTRA_START_SERVICES'].value:
         changes['ORCHESTRA_START_SERVICES'] = settings_parser.Remove()
     if 'celeryd' in Setting.settings['ORCHESTRA_RESTART_SERVICES'].value:
         changes['ORCHESTRA_RESTART_SERVICES'] = settings_parser.Remove()
     if 'celeryd' in Setting.settings['ORCHESTRA_STOP_SERVICES'].value:
         changes['ORCHESTRA_STOP_SERVICES'] = settings_parser.Remove()
     if changes:
         settings_parser.apply(changes)
Example #2
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)
    def handle(self, *args, **options):
        context = {
            "site_dir": get_site_dir(),
            "orchestra_beat": run("which orchestra-beat").stdout.decode("utf8"),
            "venv": os.environ.get("VIRTUAL_ENV", ""),
        }
        content = run("crontab -l || true").stdout.decode("utf8")
        if "orchestra-beat" not in content:
            if context["venv"]:
                content += (
                    "\n* * * * * . %(venv)s/bin/activate && %(orchestra_beat)s %(site_dir)s/manage.py; deactivate"
                    % context
                )
            else:
                content += "\n* * * * * %(orchestra_beat)s %(site_dir)s/manage.py" % context
            context["content"] = content
            run("cat << EOF | crontab\n%(content)s\nEOF" % context, display=True)

        # Configrue settings to use threaded task backend (default)
        changes = {}
        if Setting.settings["TASKS_BACKEND"].value == "celery":
            changes["TASKS_BACKEND"] = settings_parser.Remove()
        if "celeryd" in Setting.settings["ORCHESTRA_START_SERVICES"].value:
            changes["ORCHESTRA_START_SERVICES"] = settings_parser.Remove()
        if "celeryd" in Setting.settings["ORCHESTRA_RESTART_SERVICES"].value:
            changes["ORCHESTRA_RESTART_SERVICES"] = settings_parser.Remove()
        if "celeryd" in Setting.settings["ORCHESTRA_STOP_SERVICES"].value:
            changes["ORCHESTRA_STOP_SERVICES"] = settings_parser.Remove()
        if changes:
            settings_parser.apply(changes)
Example #4
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)
Example #5
0
 def handle(self, *args, **options):
     context = {
         'project_name': paths.get_project_name(),
         'site_dir': paths.get_site_dir(),
     }
     banner = "%(project_name)s status" % context
     self.stdout.write(banner)
     self.stdout.write('-' * len(banner))
     self.stdout.write(' Orchestra version: ' + get_version())
     if djsettings.DEBUG:
         self.stdout.write(" debug enabled")
     else:
         self.stdout.write(" debug disabled")
     ps = run('ps aux').stdout.decode().replace('\n', ' ')
     for service in flatten(settings.ORCHESTRA_START_SERVICES):
         context['service'] = service
         if self.is_running(context, ps):
             self.stdout.write(" %(service)s online" % context)
         else:
             self.stdout.write(" %(service)s offline" % context)
         if service == 'nginx':
             try:
                 config_path = '/etc/nginx/sites-enabled/%(project_name)s.conf' % context
                 with open(config_path, 'r') as handler:
                     config = handler.read().replace('\n', ' ')
             except FileNotFoundError:
                 self.stdout.write("   * %s not found" % config_path)
             else:
                 regex = r'location\s+([^\s]+)\s+{.*uwsgi_pass unix:///var/run/uwsgi/app/%(project_name)s/socket;.*' % context
                 location = re.findall(regex, config)
                 if location:
                     ip = run(
                         "ip a | grep 'inet ' | awk {'print $2'} | grep -v '^127.0.' | head -n 1 | cut -d'/' -f1"
                     ).stdout.decode()
                     if not ip:
                         ip = '127.0.0.1'
                     location = 'http://%s%s' % (ip, location[0])
                     self.stdout.write("   * location %s" % location)
                 else:
                     self.stdout.write("   * location not found")
         elif service == 'postgresql':
             db_conn = connections['default']
             try:
                 c = db_conn.cursor()
             except OperationalError:
                 self.stdout.write("   * DB connection failed")
             else:
                 self.stdout.write("   * DB connection succeeded")
         elif service == 'uwsgi':
             uwsgi_config = '/etc/uwsgi/apps-enabled/%(project_name)s.ini' % context
             if os.path.isfile(uwsgi_config):
                 self.stdout.write("   * %s exists" % uwsgi_config)
             else:
                 self.stdout.write("   * %s does not exist" % uwsgi_config)
     cronbeat = 'crontab -l | grep "^.*/orchestra-beat %(site_dir)s/manage.py"' % context
     if run(cronbeat, valid_codes=(0, 1)).exit_code == 0:
         self.stdout.write(" cronbeat installed")
     else:
         self.stdout.write(" cronbeat not installed")
 def handle(self, *args, **options):
     context = {
         'project_name': paths.get_project_name(),
         'site_dir': paths.get_site_dir(),
     }
     banner = "%(project_name)s status" % context
     self.stdout.write(banner)
     self.stdout.write('-'*len(banner))
     self.stdout.write(' Orchestra version: ' + get_version())
     if djsettings.DEBUG:
         self.stdout.write(" debug enabled")
     else:
         self.stdout.write(" debug disabled")
     ps = run('ps aux').stdout.decode().replace('\n', ' ')
     for service in flatten(settings.ORCHESTRA_START_SERVICES):
         context['service'] = service
         if self.is_running(context, ps):
             self.stdout.write(" %(service)s online" % context)
         else:
             self.stdout.write(" %(service)s offline" % context)
         if service == 'nginx':
             try:
                 config_path = '/etc/nginx/sites-enabled/%(project_name)s.conf' % context
                 with open(config_path, 'r') as handler:
                     config = handler.read().replace('\n', ' ')
             except FileNotFoundError:
                 self.stdout.write("   * %s not found" % config_path)
             else:
                 regex = r'location\s+([^\s]+)\s+{.*uwsgi_pass unix:///var/run/uwsgi/app/%(project_name)s/socket;.*' % context
                 location = re.findall(regex, config)
                 if location:
                     ip = run("ip a | grep 'inet ' | awk {'print $2'} | grep -v '^127.0.' | head -n 1 | cut -d'/' -f1").stdout.decode()
                     if not ip:
                         ip = '127.0.0.1'
                     location = 'http://%s%s' % (ip, location[0])
                     self.stdout.write("   * location %s" % location)
                 else:
                     self.stdout.write("   * location not found")
         elif service == 'postgresql':
             db_conn = connections['default']
             try:
                 c = db_conn.cursor()
             except OperationalError:
                 self.stdout.write("   * DB connection failed")
             else:
                 self.stdout.write("   * DB connection succeeded")
         elif service == 'uwsgi':
             uwsgi_config = '/etc/uwsgi/apps-enabled/%(project_name)s.ini' % context
             if os.path.isfile(uwsgi_config):
                 self.stdout.write("   * %s exists" % uwsgi_config)
             else:
                 self.stdout.write("   * %s does not exist" % uwsgi_config)
     cronbeat = 'crontab -l | grep "^.*/orchestra-beat %(site_dir)s/manage.py"' % context
     if run(cronbeat, valid_codes=(0, 1)).exit_code == 0:
         self.stdout.write(" cronbeat installed")
     else:
         self.stdout.write(" cronbeat not installed")
 def handle(self, *args, **options):
     context = {
         'site_dir': get_site_dir(),
         'orchestra_beat': run('which orchestra-beat').stdout.decode('utf8'),
         'venv': os.environ.get('VIRTUAL_ENV', ''),
     }
     content = run('crontab -l || true').stdout.decode('utf8')
     if 'orchestra-beat' not in content:
         if context['venv']:
             content += "\n* * * * * . %(venv)s/bin/activate && %(orchestra_beat)s %(site_dir)s/manage.py; deactivate" % context
         else:
             content += "\n* * * * * %(orchestra_beat)s %(site_dir)s/manage.py" % context
         context['content'] = content
         run("cat << EOF | crontab\n%(content)s\nEOF" % context, display=True)
def deprecate_periodic_tasks(names):
    from djcelery.models import PeriodicTask
    for name in names:
        PeriodicTask.objects.filter(name=name).delete()
    run('rabbitmqctl stop_app')
    run('rabbitmqctl reset')
    run('rabbitmqctl start_app')
    run('service celeryd restart')
Example #9
0
def deprecate_periodic_tasks(names):
    from djcelery.models import PeriodicTask
    for name in names:
        PeriodicTask.objects.filter(name=name).delete()
    run('rabbitmqctl stop_app')
    run('rabbitmqctl reset')
    run('rabbitmqctl start_app')
    run('service celeryd restart')
Example #10
0
def retrieve_state(servers):
    uptimes = []
    pings = []
    for server in servers:
        address = server.get_address()
        ping = run('ping -c 1 -w 1 %s' % address, async=True)
        pings.append(ping)
        uptime = sshrun(address, 'uptime', persist=True, async=True, options={'ConnectTimeout': 1})
        uptimes.append(uptime)
    
    state = {}
    for server, ping, uptime in zip(servers, pings, uptimes):
        ping = join(ping, silent=True)
        ping = ping.stdout.splitlines()[-1].decode()
        if ping.startswith('rtt'):
            ping = '%s ms' % ping.split('/')[4]
        else:
            ping = '<span style="color:red">Offline</span>'
        
        uptime = join(uptime, silent=True)
        uptime_stderr = uptime.stderr.decode()
        uptime = uptime.stdout.decode().split()
        if uptime:
            uptime = 'Up %s %s load %s %s %s' % (uptime[2], uptime[3], uptime[-3], uptime[-2], uptime[-1])
        else:
            uptime = '<span style="color:red">%s</span>' % uptime_stderr
        state[server.pk] = (ping, uptime)
    
    return state
Example #11
0
def validate_zone(zone):
    """ Ultimate zone file validation using named-checkzone """
    zone_name = zone.split()[0][:-1]
    zone_path = os.path.join(domains.settings.DOMAINS_ZONE_VALIDATION_TMP_DIR,
                             zone_name)
    checkzone = domains.settings.DOMAINS_CHECKZONE_BIN_PATH
    try:
        with open(zone_path, 'wb') as f:
            f.write(zone.encode('ascii'))
        # Don't use /dev/stdin becuase the 'argument list is too long' error
        check = run(' '.join([checkzone, zone_name, zone_path]),
                    valid_codes=(0, 1, 127),
                    display=False)
    finally:
        try:
            os.unlink(zone_path)
        except FileNotFoundError:
            pass
    if check.exit_code == 127:
        logger.error("Cannot validate domain zone: %s not installed." %
                     checkzone)
    elif check.exit_code == 1:
        errors = re.compile(r'zone.*: (.*)').findall(
            check.stdout.decode('utf8'))[:-1]
        raise ValidationError(', '.join(errors))
Example #12
0
def get_existing_pip_installation():
    """ returns current pip installation path """
    if run("pip freeze|grep django-orchestra", valid_codes=(0,1)).exit_code == 0:
        for lib_path in get_python_lib(), get_python_lib(prefix="/usr/local"):
            existing_path = os.path.abspath(os.path.join(lib_path, "orchestra"))
            if os.path.exists(existing_path):
                return existing_path
    return None
Example #13
0
def html_to_pdf(html):
    """ converts HTL to PDF using wkhtmltopdf """
    return run(
        'PATH=$PATH:/usr/local/bin/\n'
        'xvfb-run -a -s "-screen 0 640x4800x16" '
            'wkhtmltopdf -q --footer-center "Page [page] of [topage]" '
            '   --footer-font-size 9 --margin-bottom 20 --margin-top 20 - -',
        stdin=html.encode('utf-8')
    ).stdout
Example #14
0
def run_tuple(services, action, options, optional=False):
    if isinstance(services, str):
        services = [services]
    for service in services:
        if options.get(service):
            valid_codes = (0,1) if optional else (0,)
            e = run('service %s %s' % (service, action), valid_codes=valid_codes)
            if e.exit_code == 1:
                return False
    return True
Example #15
0
 def validate_www_update(self, server_addr, domain_name):
     context = {'domain_name': domain_name, 'server_addr': server_addr}
     dig_cname = 'dig @%(server_addr)s www.%(domain_name)s CNAME | grep "\sCNAME\s"'
     cname = run(dig_cname % context).stdout.split()
     # testdomain.org. 3600 IN MX 10 orchestra.lan.
     self.assertEqual('www.%(domain_name)s.' % context, cname[0])
     self.assertEqual('3600', cname[1])
     self.assertEqual('IN', cname[2])
     self.assertEqual('CNAME', cname[3])
     self.assertEqual('external.server.org.', cname[4])
Example #16
0
    def validate_add(self, server_addr, domain_name):
        context = {'domain_name': domain_name, 'server_addr': server_addr}
        dig_soa = 'dig @%(server_addr)s %(domain_name)s SOA|grep "\sSOA\s"'
        soa = run(dig_soa % context).stdout.split()
        # testdomain.org. 3600 IN SOA ns.example.com. hostmaster.example.com. 2014021100 86400 7200 2419200 3600
        self.assertEqual('%(domain_name)s.' % context, soa[0])
        self.assertEqual('3600', soa[1])
        self.assertEqual('IN', soa[2])
        self.assertEqual('SOA', soa[3])
        self.assertEqual('%s.' % settings.DOMAINS_DEFAULT_NAME_SERVER, soa[4])
        hostmaster = utils.format_hostmaster(
            settings.DOMAINS_DEFAULT_HOSTMASTER)
        self.assertEqual(hostmaster, soa[5])

        dig_ns = 'dig @%(server_addr)s %(domain_name)s NS|grep "\sNS\s"'
        name_servers = run(dig_ns % context).stdout
        # testdomain.org. 3600 IN NS ns1.orchestra.lan.
        ns_records = [
            'ns1.%s.' % self.domain_name,
            'ns2.%s.' % self.domain_name
        ]
        self.assertEqual(2, len(name_servers.splitlines()))
        for ns in name_servers.splitlines():
            ns = ns.split()
            # testdomain.org. 3600 IN NS ns1.orchestra.lan.
            self.assertEqual('%(domain_name)s.' % context, ns[0])
            self.assertEqual('3600', ns[1])
            self.assertEqual('IN', ns[2])
            self.assertEqual('NS', ns[3])
            self.assertIn(ns[4], ns_records)

        dig_mx = 'dig @%(server_addr)s %(domain_name)s MX|grep "\sMX\s"'
        mail_servers = run(dig_mx % context).stdout
        for mx in mail_servers.splitlines():
            mx = mx.split()
            # testdomain.org. 3600 IN NS ns1.orchestra.lan.
            self.assertEqual('%(domain_name)s.' % context, mx[0])
            self.assertEqual('3600', mx[1])
            self.assertEqual('IN', mx[2])
            self.assertEqual('MX', mx[3])
            self.assertIn(mx[4], ['10', '20'])
            self.assertIn(mx[5],
                          ['mail2.orchestra.lan.', 'mail.orchestra.lan.'])
def run_tuple(services, action, options, optional=False):
    if isinstance(services, str):
        services = [services]
    for service in services:
        if options.get(service):
            error_codes = [0,1] if optional else [0]
            e = run('service %s %s' % (service, action), error_codes=error_codes)
            if e.return_code == 1:
                return False
    return True
Example #18
0
 def validate_add(self, server_addr, domain_name):
     context = {
         'domain_name': domain_name,
         'server_addr': server_addr
     }
     dig_soa = 'dig @%(server_addr)s %(domain_name)s SOA|grep "\sSOA\s"'
     soa = run(dig_soa % context).stdout.split()
     # testdomain.org. 3600 IN SOA ns.example.com. hostmaster.example.com. 2014021100 86400 7200 2419200 3600
     self.assertEqual('%(domain_name)s.' % context, soa[0])
     self.assertEqual('3600', soa[1])
     self.assertEqual('IN', soa[2])
     self.assertEqual('SOA', soa[3])
     self.assertEqual('%s.' % settings.DOMAINS_DEFAULT_NAME_SERVER, soa[4])
     hostmaster = utils.format_hostmaster(settings.DOMAINS_DEFAULT_HOSTMASTER)
     self.assertEqual(hostmaster, soa[5])
     
     dig_ns = 'dig @%(server_addr)s %(domain_name)s NS|grep "\sNS\s"'
     name_servers = run(dig_ns % context).stdout
     # testdomain.org. 3600 IN NS ns1.orchestra.lan.
     ns_records = ['ns1.%s.' % self.domain_name, 'ns2.%s.' % self.domain_name]
     self.assertEqual(2, len(name_servers.splitlines()))
     for ns in name_servers.splitlines():
         ns = ns.split()
         # testdomain.org. 3600 IN NS ns1.orchestra.lan.
         self.assertEqual('%(domain_name)s.' % context, ns[0])
         self.assertEqual('3600', ns[1])
         self.assertEqual('IN', ns[2])
         self.assertEqual('NS', ns[3])
         self.assertIn(ns[4], ns_records)
     
     dig_mx = 'dig @%(server_addr)s %(domain_name)s MX|grep "\sMX\s"'
     mail_servers = run(dig_mx % context).stdout
     for mx in mail_servers.splitlines():
         mx = mx.split()
         # testdomain.org. 3600 IN NS ns1.orchestra.lan.
         self.assertEqual('%(domain_name)s.' % context, mx[0])
         self.assertEqual('3600', mx[1])
         self.assertEqual('IN', mx[2])
         self.assertEqual('MX', mx[3])
         self.assertIn(mx[4], ['10', '20'])
         self.assertIn(mx[5], ['mail2.orchestra.lan.', 'mail.orchestra.lan.'])
Example #19
0
def run_tuple(services, action, options, optional=False):
    if isinstance(services, str):
        services = [services]
    for service in services:
        if options.get(service):
            valid_codes = (0, 1) if optional else (0, )
            e = run('service %s %s' % (service, action),
                    valid_codes=valid_codes,
                    display=True)
            if e.exit_code == 1:
                return False
    return True
Example #20
0
 def validate_www_update(self, server_addr, domain_name):
     context = {
         'domain_name': domain_name,
         'server_addr': server_addr
     }
     dig_cname = 'dig @%(server_addr)s www.%(domain_name)s CNAME | grep "\sCNAME\s"'
     cname = run(dig_cname % context).stdout.split()
     # testdomain.org. 3600 IN MX 10 orchestra.lan.
     self.assertEqual('www.%(domain_name)s.' % context, cname[0])
     self.assertEqual('3600', cname[1])
     self.assertEqual('IN', cname[2])
     self.assertEqual('CNAME', cname[3])
     self.assertEqual('external.server.org.', cname[4])
Example #21
0
 def validate_delete(self, server_addr, domain_name):
     context = {'domain_name': domain_name, 'server_addr': server_addr}
     dig_soa = 'dig @%(server_addr)s %(domain_name)s|grep "\sSOA\s"'
     soa = run(dig_soa % context, valid_codes=(0, 1)).stdout
     if soa:
         soa = soa.split()
         self.assertEqual('IN', soa[2])
         self.assertEqual('SOA', soa[3])
         self.assertNotEqual('%s.' % settings.DOMAINS_DEFAULT_NAME_SERVER,
                             soa[4])
         hostmaster = utils.format_hostmaster(
             settings.DOMAINS_DEFAULT_HOSTMASTER)
         self.assertNotEqual(hostmaster, soa[5])
Example #22
0
 def validate_delete(self, server_addr, domain_name):
     context = {
         'domain_name': domain_name,
         'server_addr': server_addr
     }
     dig_soa = 'dig @%(server_addr)s %(domain_name)s|grep "\sSOA\s"'
     soa = run(dig_soa % context, valid_codes=(0, 1)).stdout
     if soa:
         soa = soa.split()
         self.assertEqual('IN', soa[2])
         self.assertEqual('SOA', soa[3])
         self.assertNotEqual('%s.' % settings.DOMAINS_DEFAULT_NAME_SERVER, soa[4])
         hostmaster = utils.format_hostmaster(settings.DOMAINS_DEFAULT_HOSTMASTER)
         self.assertNotEqual(hostmaster, soa[5])
Example #23
0
def validate_zone(zone):
    """ Ultimate zone file validation using named-checkzone """
    zone_name = zone.split()[0][:-1]
    zone_path = os.path.join(settings.DOMAINS_ZONE_VALIDATION_TMP_DIR, zone_name)
    checkzone = settings.DOMAINS_CHECKZONE_BIN_PATH
    try:
        with open(zone_path, 'wb') as f:
            f.write(zone.encode('ascii'))
        # Don't use /dev/stdin becuase the 'argument list is too long' error
        check = run(' '.join([checkzone, zone_name, zone_path]), error_codes=[0,1], display=False)
    finally:
        os.unlink(zone_path)
    if check.return_code == 1:
        errors = re.compile(r'zone.*: (.*)').findall(check.stdout)[:-1]
        raise ValidationError(', '.join(errors))
Example #24
0
def validate_sieve(value):
    sieve_name = '%s.sieve' % hashlib.md5(value.encode('utf8')).hexdigest()
    path = os.path.join(settings.MAILBOXES_SIEVETEST_PATH, sieve_name)
    with open(path, 'w') as f:
        f.write(value)
    context = {
        'orchestra_root': paths.get_orchestra_dir()
    }
    sievetest = settings.MAILBOXES_SIEVETEST_BIN_PATH % context
    test = run(' '.join([sievetest, path, '/dev/null']), silent=True)
    if test.return_code:
        errors = []
        for line in test.stderr.decode('utf8').splitlines():
            error = re.match(r'^.*(line\s+[0-9]+:.*)', line)
            if error:
                errors += error.groups()
        raise ValidationError(' '.join(errors))
Example #25
0
def validate_sieve(value):
    sieve_name = '%s.sieve' % hashlib.md5(value).hexdigest()
    path = os.path.join(settings.MAILBOXES_SIEVETEST_PATH, sieve_name)
    with open(path, 'wb') as f:
        f.write(value)
    context = {
        'orchestra_root': paths.get_orchestra_dir()
    }
    sievetest = settings.MAILBOXES_SIEVETEST_BIN_PATH % context
    try:
        test = run(' '.join([sievetest, path, '/dev/null']), display=False)
    except CommandError:
        errors = []
        for line in test.stderr.splitlines():
            error = re.match(r'^.*(line\s+[0-9]+:.*)', line)
            if error:
                errors += error.groups()
        raise ValidationError(' '.join(errors))
Example #26
0
def validate_sieve(value):
    sieve_name = '%s.sieve' % hashlib.md5(value.encode('utf8')).hexdigest()
    test_path = os.path.join(settings.MAILBOXES_SIEVETEST_PATH, sieve_name)
    with open(test_path, 'w') as f:
        f.write(value)
    context = {'orchestra_root': paths.get_orchestra_dir()}
    sievetest = settings.MAILBOXES_SIEVETEST_BIN_PATH % context
    try:
        test = run(' '.join([sievetest, test_path, '/dev/null']), silent=True)
    finally:
        os.unlink(test_path)
    if test.exit_code:
        errors = []
        for line in test.stderr.decode('utf8').splitlines():
            error = re.match(r'^.*(line\s+[0-9]+:.*)', line)
            if error:
                errors += error.groups()
        raise ValidationError(' '.join(errors))
Example #27
0
def html_to_pdf(html, pagination=False):
    """ converts HTL to PDF using wkhtmltopdf """
    context = {
        'pagination': textwrap.dedent("""\
            --footer-center "Page [page] of [topage]" \\
            --footer-font-name sans \\
            --footer-font-size 7 \\
            --footer-spacing 7"""
        ) if pagination else '',
    }
    cmd = textwrap.dedent("""\
        PATH=$PATH:/usr/local/bin/
        xvfb-run -a -s "-screen 0 2480x3508x16" wkhtmltopdf -q \\
            --use-xserver \\
            %(pagination)s \\
            --margin-bottom 22 \\
            --margin-top 20 - - \
        """) % context
    return run(cmd, stdin=html.encode('utf-8')).stdout
Example #28
0
def html_to_pdf(html, pagination=False):
    """ converts HTL to PDF using wkhtmltopdf """
    context = {
        'pagination':
        textwrap.dedent("""\
            --footer-center "Page [page] of [topage]" \\
            --footer-font-name sans \\
            --footer-font-size 7 \\
            --footer-spacing 7""") if pagination else '',
    }
    cmd = textwrap.dedent("""\
        PATH=$PATH:/usr/local/bin/
        xvfb-run -a -s "-screen 0 2480x3508x16" wkhtmltopdf -q \\
            --use-xserver \\
            %(pagination)s \\
            --margin-bottom 22 \\
            --margin-top 20 - - \
        """) % context
    return run(cmd, stdin=html.encode('utf-8')).stdout
Example #29
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']:
                 # Ignore differences between lists and tuples
                 if (type(setting.value) != type(data['value']) and
                     isinstance(data['value'], list) and
                     tuple(data['value']) == setting.value):
                         continue
                 if setting.default == data['value']:
                     changes[setting.name] = parser.Remove()
                 else:
                     changes[setting.name] = data['value']
     if changes:
         # Display confirmation
         if not self.request.POST.get('confirmation'):
             settings_file = parser.get_settings_file()
             new_content = parser.apply(changes)
             cmd = "cat <<EOF | diff %s -\n%s\nEOF" % (settings_file, new_content)
             diff = sys.run(cmd, valid_codes=(1, 0)).stdout
             context = self.get_context_data(form=form)
             context['diff'] = diff
             if not diff:
                 messages.warning(self.request, _("Changes detected but no diff %s.") % changes)
             return self.render_to_response(context)
         n = len(changes)
         # Save changes
         parser.save(changes)
         sys.touch_wsgi()
         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)
Example #30
0
def validate_zone(zone):
    """ Ultimate zone file validation using named-checkzone """
    zone_name = zone.split()[0][:-1]
    zone_path = os.path.join(domains.settings.DOMAINS_ZONE_VALIDATION_TMP_DIR, zone_name)
    checkzone = domains.settings.DOMAINS_CHECKZONE_BIN_PATH
    try:
        with open(zone_path, 'wb') as f:
            f.write(zone.encode('ascii'))
        # Don't use /dev/stdin becuase the 'argument list is too long' error
        check = run(' '.join([checkzone, zone_name, zone_path]), valid_codes=(0,1,127), display=False)
    finally:
        try:
            os.unlink(zone_path)
        except FileNotFoundError:
            pass
    if check.exit_code == 127:
        logger.error("Cannot validate domain zone: %s not installed." % checkzone)
    elif check.exit_code == 1:
        errors = re.compile(r'zone.*: (.*)').findall(check.stdout.decode('utf8'))[:-1]
        raise ValidationError(', '.join(errors))
Example #31
0
def retrieve_state(servers):
    uptimes = []
    pings = []
    for server in servers:
        address = server.get_address()
        ping = run('ping -c 1 -w 1 %s' % address, run_async=True)
        pings.append(ping)
        uptime = sshrun(address,
                        'uptime',
                        persist=True,
                        run_async=True,
                        options={'ConnectTimeout': 1})
        uptimes.append(uptime)

    state = {}
    for server, ping, uptime in zip(servers, pings, uptimes):
        ping = join(ping, silent=True)

        try:
            ping = ping.stdout.splitlines()[-1].decode()
        except IndexError:
            ping = ''

        if ping.startswith('rtt'):
            ping = '%s ms' % ping.split('/')[4]
        else:
            ping = '<span style="color:red">Offline</span>'

        uptime = join(uptime, silent=True)
        uptime_stderr = uptime.stderr.decode()
        uptime = uptime.stdout.decode().split()
        if uptime:
            uptime = 'Up %s %s load %s %s %s' % (
                uptime[2], uptime[3], uptime[-3], uptime[-2], uptime[-1])
        else:
            uptime = '<span style="color:red">%s</span>' % uptime_stderr
        state[server.pk] = (ping, uptime)

    return state
Example #32
0
 def handle(self, *filenames, **options):
     flake = run('flake8 {%s,%s} | grep -v "W293\|E501"' %
                 (get_orchestra_dir(), get_site_dir()))
     self.stdout.write(flake.stdout.decode('utf8'))
Example #33
0
    def handle(self, *args, **options):
        version = options.get('version')
        upgrade_notes = []
        if version:
            version_re = re.compile(r'^\s*(\d+)\.(\d+)\.(\d+).*')
            minor_release = version_re.search(version)
            if minor_release is not None:
                major, major2, minor = version_re.search(version).groups()
            else:
                version_re = re.compile(r'^\s*(\d+)\.(\d+).*')
                major, major2 = version_re.search(version).groups()
                minor = 0
            # Represent version as two digits per number: 1.2.2 -> 10202
            version = int(
                str(major) + "%02d" % int(major2) + "%02d" % int(minor))

            # Pre version specific upgrade operations
            if version < '001':
                pass

        if not options.get('specifics_only'):
            # Common stuff
            orchestra_admin = os.path.join(os.path.dirname(__file__),
                                           '../../bin/')
            orchestra_admin = os.path.join(orchestra_admin, 'orchestra-admin')
            run('chmod +x %s' % orchestra_admin)
            run("%s install_requirements" % orchestra_admin)

            manage_path = os.path.join(get_site_dir(), 'manage.py')
            run("python %s collectstatic --noinput" % manage_path)
            run("python %s migrate --noinput accounts" % manage_path)
            run("python %s migrate --noinput" % manage_path)
            if options.get('restart'):
                run("python %s restartservices" % manage_path)

        if not version:
            self.stderr.write(
                '\n'
                'Next time you migth want to provide a --from argument in order '
                'to run version specific upgrade operations\n')
            return

        # Post version specific operations
        if version <= '001':
            pass

        if upgrade_notes and options.get('print_upgrade_notes'):
            self.stdout.write('\n\033[1m\n'
                              '    ===================\n'
                              '    ** UPGRADE NOTES **\n'
                              '    ===================\n\n' +
                              '\n'.join(upgrade_notes) + '\033[m\n')
Example #34
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'),
            'vmail_username': options.get('vmail_username'),
            'vmail_uid': options.get('vmail_uid'),
            'vmail_groupname': options.get('vmail_groupname'),
            'vmail_gid': options.get('vmail_gid'),
            'vmail_home': options.get('vmail_home'),
            'dovecot_dir': options.get('dovecot_dir'),
            'postfix_dir': options.get('postfix_dir'),
            'amavis_dir': options.get('amavis_dir'),
        }

        file_name = '%(postfix_dir)s/pgsql-email2email.cf' % context
        run("#Processing %s" % file_name)
        pgsql_email2email = """user = %(db_user)s
password = %(db_password)s
hosts = %(db_host)s
dbname = %(db_name)s

query = SELECT mails_mailbox.emailname || '@' || names_domain.name as email FROM mails_mailbox INNER JOIN names_domain ON (mails_mailbox.domain_id = names_domain.id) WHERE mails_mailbox.emailname = '%%u' AND names_domain.name = '%%d'
"""
        f = open(file_name, 'w')
        f.write(pgsql_email2email % context)
        f.close()
        run("chown root:postfix %s" % file_name)
        run("chmod 640 %s" % file_name)

        file_name = '%(postfix_dir)s/pgsql-virtual-alias-maps.cf' % context
        run("#Processing %s" % file_name)
        virtual_alias_maps = """user = %(db_user)s
password = %(db_password)s
hosts = %(db_host)s
dbname = %(db_name)s

query = SELECT mails_mailalias.destination FROM mails_mailalias INNER JOIN names_domain ON (mails_mailalias.domain_id = names_domain.id) WHERE mails_mailalias.emailname = '%%u' AND names_domain.name='%%d'
"""
        f = open(file_name, 'w')
        f.write(virtual_alias_maps % context)
        f.close()
        run("chown root:postfix %s" % file_name)
        run("chmod 640 %s" % file_name)

        file_name = '%(postfix_dir)s/pgsql-virtual-mailbox-domains.cf' % context
        run("#Processing %s" % file_name)
        virtual_mailbox_domains = """user = %(db_user)s
password = %(db_password)s
hosts = %(db_host)s
dbname = %(db_name)s

query = SELECT 1 FROM names_domain WHERE names_domain.name='%%s'
"""
        f = open(file_name, 'w')
        f.write(virtual_mailbox_domains % context)
        f.close()
        run("chown root:postfix %s" % file_name)
        run("chmod 640 %s" % file_name)

        file_name = '%(postfix_dir)s/pgsql-virtual-mailbox-maps.cf' % context
        run("#Processing %s" % file_name)
        virtual_mailbox_maps = """user = %(db_user)s
password = %(db_password)s
hosts = %(db_host)s
dbname = %(db_name)s

query = SELECT 1 FROM mails_mailbox INNER JOIN names_domain ON (mails_mailbox.domain_id = names_domain.id) WHERE mails_mailbox.emailname='%%u' AND names_domain.name='%%d'
"""
        f = open(file_name, 'w')
        f.write(virtual_mailbox_maps % context)
        f.close()
        run("chown root:postfix %s" % file_name)
        run("chmod 640 %s" % file_name)

        #Dovecot
        vmail_usename = run("id -u %(vmail_username)s" % context)
        vmail_groupname = run("id -g %(vmail_groupname)s" % context)
        if vmail_groupname != context["vmail_gid"]:
            run("groupadd -g %(vmail_gid)s %(vmail_groupname)s" % context)
            run("chown -R %(vmail_username)s:%(vmail_groupname)s %(vmail_home)s" % context)
        if vmail_usename != context["vmail_uid"]:
            run("useradd -g %(vmail_groupname)s -u %(vmail_uid)s %(vmail_username)s -d %(vmail_home)s -m" % context)
            run("chmod u+w %(vmail_home)s" % context)

        run("chown -R %(vmail_username)s:%(vmail_groupname)s %(vmail_home)s" % context)
        run("chmod u+w %(vmail_home)s" % context)

        file_name = "%(dovecot_dir)s/conf.d/10-auth.conf" % context
        run("""sed -i "s/auth_mechanisms = plain$/auth_mechanisms = plain login/g" %s """ % file_name)
        run("""sed -i "s/\#\!include auth-sql.conf.ext/\!include auth-sql.conf.ext/" %s """ % file_name)

        file_name = "%(dovecot_dir)s/conf.d/auth-sql.conf.ext" % context
        run("#Processing %s" % file_name)
        auth_sql_conf_ext = """passdb {
  driver = sql
  args = %(dovecot_dir)s/dovecot-sql.conf.ext
}

userdb {
  driver = static
  args = uid=%(vmail_username)s gid=%(vmail_groupname)s home=%(vmail_home)s/%%d/%%n/Maildir allow_all_users=yes
}
"""
        f = open(file_name, 'w')
        f.write(auth_sql_conf_ext % context)
        f.close()


        file_name = "%(dovecot_dir)s/conf.d/10-mail.conf" % context
        run("#Processing %s" % file_name)
        mail_conf = """mail_location = maildir:%(vmail_home)s/%%d/%%n/Maildir
namespace inbox {
    separator = .
    inbox = yes
}
        """
        f = open(file_name, 'w')
        f.write(mail_conf % context)
        f.close()


        file_name = "%(dovecot_dir)s/conf.d/10-master.conf" % context
        run("""sed -i "s/service auth {/service auth {\\n\\tunix_listener \/var\/spool\/postfix\/private\/auth {\\n\\t\\tmode = 0660\\n\\t\\tuser = postfix\\n\\t\\tgroup = postfix\\n\\t}\\n/g" %s """ % file_name)


        file_name = "%(dovecot_dir)s/conf.d/10-ssl.conf" % context

        run("#Processing %s" % file_name)
        ssl_conf = """ssl_cert = </etc/ssl/certs/mailserver.pem
ssl_key = </etc/ssl/private/mailserver.pem"""
        f = open(file_name, 'w')
        f.write(ssl_conf)
        f.close()

        file_name = "%(dovecot_dir)s/conf.d/15-lda.conf" % context
        run("#Processing %s" % file_name)
        lda_conf ="""protocol lda {
    postmaster_address = postmaster
    mail_plugins = $mail_plugins sieve
}
"""
        f = open(file_name, 'w')
        f.write(lda_conf)
        f.close()


        file_name = "%(dovecot_dir)s/dovecot-sql.conf.ext" % context
        run("#Processing %s" % file_name)
        dovecot_sql = """driver = pgsql
connect = host=%(db_host)s dbname=%(db_name)s user=%(db_user)s password=%(db_password)s
default_pass_scheme = SSHA
password_query = \
 SELECT mails_mailbox.emailname || '@' || names_domain.name as user, mails_mailbox.shadigest as password  \\
    FROM mails_mailbox \\
        INNER JOIN names_domain ON (mails_mailbox.domain_id = names_domain.id) \\
        INNER JOIN auth_user ON (mails_mailbox.user_id = auth_user.id) \\
    WHERE mails_mailbox.emailname = '%%n' AND \\
        names_domain.name = '%%d'
        """
        f = open(file_name, 'w')
        f.write(dovecot_sql % context)
        f.close()


        run("chgrp %(vmail_groupname)s %(dovecot_dir)s/dovecot.conf" % context)
        run("chmod g+r %(dovecot_dir)s/dovecot.conf" % context)

        run("chown root:root %(dovecot_dir)s/dovecot-sql.conf.ext" % context)
        run("chmod go= %(dovecot_dir)s/dovecot-sql.conf.ext" % context)

        file_name = "%(postfix_dir)s/master.cf" % context
        grep_dovecot = run("grep dovecot %s" % file_name, valid_codes=(0,1))
        if grep_dovecot == '':
            run("#Processing %s" % file_name)
            dovecot_master="""
dovecot   unix  -       n       n       -       -       pipe
  flags=DRhu user=%(vmail_username)s:%(vmail_groupname)s argv=/usr/lib/dovecot/dovecot-lda -f ${sender} -d ${recipient}

#Amavis:
amavis unix    -       -       n       -       5     smtp
     -o smtp_data_done_timeout=1200
     -o smtp_send_xforward_command=yes
     -o smtp_tls_note_starttls_offer=no

127.0.0.1:10025 inet n    -       n       -       -     smtpd
    -o content_filter=
    -o smtpd_delay_reject=no
    -o smtpd_client_restrictions=permit_mynetworks,reject
    -o smtpd_helo_restrictions=
    -o smtpd_sender_restrictions=
    -o smtpd_recipient_restrictions=permit_mynetworks,reject
    -o smtpd_data_restrictions=reject_unauth_pipelining
    -o smtpd_end_of_data_restrictions=
    -o smtpd_restriction_classes=
    -o mynetworks=127.0.0.0/8
    -o smtpd_error_sleep_time=0
    -o smtpd_soft_error_limit=1001
    -o smtpd_hard_error_limit=1000
    -o smtpd_client_connection_count_limit=0
    -o smtpd_client_connection_rate_limit=0
    -o receive_override_options=no_header_body_checks,no_unknown_recipient_checks,no_milters
    -o local_header_rewrite_clients=
    -o smtpd_milters=
    -o local_recipient_maps=
    -o relay_recipient_maps=
              """
            f = open(file_name, 'a')
            f.write(dovecot_master % context)
            f.close()


        #Postfix
        mailname = run("cat /etc/mailname", vallid_codes=[0,1])
        hostname = run("hostname", valid_codes=[0,1])
        if mailname != hostname:
            file_name = "/etc/mailname"
            run("#Processing %s" % file_name)
            f = open(file_name, 'w')
            f.write(hostname % context)
            f.close()


        # Set the base address for all virtual mailboxes
        run("postconf -e virtual_mailbox_base=%(vmail_home)s" % context)

        # A list of all virtual domains serviced by this instance of postfix.
        run("postconf -e virtual_mailbox_domains=pgsql:%(postfix_dir)s/pgsql-virtual-mailbox-domains.cf" % context)

        # Look up the mailbox location based on the email address received.
        run("postconf -e virtual_mailbox_maps=pgsql:%(postfix_dir)s/pgsql-virtual-mailbox-maps.cf" % context)

        # Any aliases that are supported by this system
        run("postconf -e virtual_alias_maps=pgsql:%(postfix_dir)s/pgsql-virtual-alias-maps.cf" % context)

        #Dovecot:
        run("postconf -e virtual_transport=dovecot")
        run("postconf -e dovecot_destination_recipient_limit=1")
        run("postconf -e smtpd_sasl_type=dovecot")
        run("postconf -e smtpd_sasl_path=private/auth")
        run("postconf -e smtpd_sasl_auth_enable=yes")
        if os.path.isfile("/etc/ssl/certs/mailserver.pem"):
            run("postconf -e smtpd_tls_security_level=may")
            run("postconf -e smtpd_tls_auth_only=yes")
            run("postconf -e smtpd_tls_cert_file=/etc/ssl/certs/mailserver.pem")
            run("postconf -e smtpd_tls_key_file=/etc/ssl/private/mailserver.pem")

        run("""postconf -e smtpd_recipient_restrictions=permit_mynetworks,permit_sasl_authenticated,reject_invalid_hostname,reject_non_fqdn_recipient,reject_unknown_sender_domain,reject_unknown_recipient_domain,reject_unauth_destination,permit""")
        run("""postconf -e soft_bounce=no""")
        run("""postconf -e content_filter=amavis:[127.0.0.1]:10024""")

        #Amavis:
        file_name = "%(amavis_dir)s/conf.d/15-content_filter_mode" % context
        run("""sed -i "s/#@bypass_virus_checks_maps/@bypass_virus_checks_maps/g" %s""" % file_name)
        run("""sed -i 's/#   \\\\%%bypass_virus_checks, \\\@bypass_virus_checks_acl, \\\$bypass_virus_checks_re/   \\\\%%bypass_virus_checks, \\\@bypass_virus_checks_acl, \\\$bypass_virus_checks_re/g' %s""" % file_name)
        run("""sed -i 's/#   \\\\%%bypass_virus_checks/   \\\\%%bypass_virus_checks/g' %s""" % (file_name,) )
        run("""sed -i "s/#@bypass_spam_checks_maps/@bypass_spam_checks_maps/g" %s""" % file_name)
        run("""sed -i 's/#   \\\\%%bypass_spam_checks, \\\@bypass_spam_checks_acl, \\\$bypass_spam_checks_re/   \\\\%%bypass_spam_checks, \\\@bypass_spam_checks_acl, \\\$bypass_spam_checks_re/g' %s""" % file_name)

        run("adduser clamav amavis")

        run("service clamav-freshclam restart")
        run("service clamav-daemon restart")
        run("service spamassassin restart")
        run("service amavis restart")
        run("service dovecot restart")
        run("service postfix restart")
Example #35
0
 def handle(self, *filenames, **options):
     flake = run('flake8 {%s,%s} | grep -v "W293\|E501"' % (get_orchestra_dir(), get_site_dir()))
     self.stdout.write(flake.stdout.decode('utf8'))
Example #36
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)
Example #37
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)
Example #38
0
 def run_postgres(self, cmd, *args, **kwargs):
     return run('su postgres -c "psql -c \\"%s\\""' % cmd, *args, **kwargs)
Example #39
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)
Example #40
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)
Example #41
0
 def run_postgres(self, cmd, *args, **kwargs):
     return run('su postgres -c "psql -c \\"%s\\""' % cmd, *args, **kwargs)
Example #42
0
 def handle(self, *args, **options):
     current_version = get_version()
     current_path = get_existing_pip_installation()
     
     if current_path is not None:
         desired_version = options.get('version')
         if args:
             desired_version = args[0]
         if current_version == desired_version:
             msg = "Not upgrading, you already have version %s installed"
             raise CommandError(msg % desired_version)
         # Create a backup of current installation
         base_path = os.path.abspath(os.path.join(current_path, '..'))
         char_set = string.ascii_uppercase + string.digits
         rand_name = ''.join(random.sample(char_set, 6))
         backup = os.path.join(base_path, 'orchestra.' + rand_name)
         run("mv %s %s" % (current_path, backup))
         
         # collect existing eggs previous to the installation
         eggs_regex = os.path.join(base_path, 'django_orchestra-*.egg-info')
         eggs = run('ls -d %s' % eggs_regex)
         eggs = eggs.stdout.splitlines()
         try:
             if desired_version:
                 r('pip install django-orchestra==%s' % desired_version)
             else:
                 # Did I mentioned how I hate PIP?
                 if run('pip --version|cut -d" " -f2').stdout == '1.0':
                     r('pip install django-orchestra --upgrade')
                 else:
                     # (F*****g pip)^2, it returns exit code 0 even when fails
                     # because requirement already up-to-date
                     r('pip install django-orchestra --upgrade --force')
         except CommandError:
             # Restore backup
             run('rm -rf %s' % current_path)
             run('mv %s %s' % (backup, current_path))
             raise CommandError("Problem runing pip upgrade, aborting...")
         else:
             # Some old versions of pip do not performe this cleaning ...
             # Remove all backups
             run('rm -fr %s' % os.path.join(base_path, 'orchestra\.*'))
             # Clean old egg files, yeah, cleaning PIP shit :P
             c_version = 'from orchestra import get_version; print get_version()'
             version = run('python -c "%s;"' % c_version).stdout
             for egg in eggs:
                 # Do not remove the actual egg file when upgrading twice the same version
                 if egg.split('/')[-1] != "django_orchestra-%s.egg-info" % version:
                     run('rm -fr %s' % egg)
     else:
         raise CommandError("You don't seem to have any previous PIP installation")
     
     # version specific upgrade operations
     if not options.get('pip_only'):
         call_command("postupgradeorchestra", version=current_version)
 def handle(self, *args, **options):
     version = options.get('version')
     upgrade_notes = []
     if version:
         version_re = re.compile(r'^\s*(\d+)\.(\d+)\.(\d+).*')
         minor_release = version_re.search(version)
         if minor_release is not None:
             major, major2, minor = version_re.search(version).groups()
         else:
             version_re = re.compile(r'^\s*(\d+)\.(\d+).*')
             major, major2 = version_re.search(version).groups()
             minor = 0
         # Represent version as two digits per number: 1.2.2 -> 10202
         version = int(str(major) + "%02d" % int(major2) + "%02d" % int(minor))
         
         # Pre version specific upgrade operations
         if version < '001':
             pass
     
     if not options.get('specifics_only'):
         # Common stuff
         orchestra_admin = os.path.join(os.path.dirname(__file__), '../../bin/')
         orchestra_admin = os.path.join(orchestra_admin, 'orchestra-admin')
         run('chmod +x %s' % orchestra_admin)
         run("%s install_requirements" % orchestra_admin)
         
         manage_path = os.path.join(get_site_dir(), 'manage.py')
         run("python %s collectstatic --noinput" % manage_path)
         run("python %s migrate --noinput accounts" % manage_path)
         run("python %s migrate --noinput" % manage_path)
         if options.get('restart'):
             run("python %s restartservices" % manage_path)
     
     if not version:
         self.stderr.write('\n'
             'Next time you migth want to provide a --from argument in order '
             'to run version specific upgrade operations\n')
         return
     
     # Post version specific operations
     if version <= '001':
         pass
     
     if upgrade_notes and options.get('print_upgrade_notes'):
         self.stdout.write('\n\033[1m\n'
             '    ===================\n'
             '    ** UPGRADE NOTES **\n'
             '    ===================\n\n' +
             '\n'.join(upgrade_notes) + '\033[m\n')
Example #44
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)
Example #45
0
 def generate_certificate(self, **options):
     override = options.get('cert_override')
     interactive = options.get('interactive')
     
     cert = options.get('cert')
     key = options.get('cert_key')
     if bool(cert) != bool(key):
         raise CommandError("--cert and --cert-key go in tandem")
     
     cert_path = options.get('cert_path')
     key_path = options.get('cert_key_path')
     
     run('mkdir -p %s' % os.path.dirname(cert_path))
     exists = os.path.isfile(cert_path)
     
     if not override and exists:
         self.stdout.write('Your cert and keys are already in place.')
         self.stdout.write('Use --override in order to regenerate them.')
         return cert_path, key_path
     
     common_name = options.get('cert_common_name') or options.get('server_name') or 'orchestra.lan'
     country = options.get('cert_country')
     state = options.get('cert_state')
     locality = options.get('cert_locality')
     org_name = options.get('cert_org_name')
     org_unit = options.get('cert_org_unit')
     email = options.get('cert_email')
     if interactive:
         msg = ('-----\n'
             'You are about to be asked to enter information that\n'
             'will be incorporated\n'
             'into your certificate request.\n'
             'What you are about to enter is what is called a\n'
             'Distinguished Name or a DN.\n'
             'There are quite a few fields but you can leave some blank\n'
             '-----\n')
         self.stdout.write(msg)
         
         msg = 'Country Name (2 letter code) [%s]: ' % country
         country = input(msg) or country
         
         msg = 'State or Province Name (full name) [%s]: ' % state
         state = input(msg) or state
         
         msg = 'Locality Name (eg, city) [%s]: ' % locality
         locality = input(msg) or locality
         
         msg = 'Organization Name (eg, company) [%s]: ' % org_name
         org_name = input(msg) or org_name
         
         msg = 'Organizational Unit Name (eg, section) [%s]: ' % org_unit
         org_unit = input(msg) or org_unit
         
         msg = 'Email Address [%s]: ' % email
         email = input(msg) or email
         
     self.stdout.write('Common Name: %s' % common_name)
     subject = {
         'C': country,
         'S': state,
         'L': locality,
         'O': org_name,
         'OU': org_unit,
         'Email': email,
         'CN': common_name,
     }
     context = {
         'subject': ''.join(('/%s=%s' % (k,v) for k,v in subject.items())),
         'key_path': key_path,
         'cert_path': cert_path,
         'cert_root': os.path.dirname(cert_path),
     }
     self.stdout.write('writing new cert to \'%s\'' % cert_path)
     self.stdout.write('writing new cert key to \'%s\'' % key_path)
     run(textwrap.dedent("""\
         openssl req -x509 -nodes -days 365 -newkey rsa:4096 -keyout %(key_path)s -out %(cert_path)s -subj "%(subject)s"
         chown --reference=%(cert_root)s %(cert_path)s %(key_path)s\
         """) % context, display=True
     )
     return cert_path, key_path
Example #46
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)
    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'),
            'vmail_username': options.get('vmail_username'),
            'vmail_uid': options.get('vmail_uid'),
            'vmail_groupname': options.get('vmail_groupname'),
            'vmail_gid': options.get('vmail_gid'),
            'vmail_home': options.get('vmail_home'),
            'dovecot_dir': options.get('dovecot_dir'),
            'postfix_dir': options.get('postfix_dir'),
            'amavis_dir': options.get('amavis_dir'),
        }

        file_name = '%(postfix_dir)s/pgsql-email2email.cf' % context
        run("#Processing %s" % file_name)
        pgsql_email2email = """user = %(db_user)s
password = %(db_password)s
hosts = %(db_host)s
dbname = %(db_name)s

query = SELECT mails_mailbox.emailname || '@' || names_domain.name as email FROM mails_mailbox INNER JOIN names_domain ON (mails_mailbox.domain_id = names_domain.id) WHERE mails_mailbox.emailname = '%%u' AND names_domain.name = '%%d'
"""
        f = open(file_name, 'w')
        f.write(pgsql_email2email % context)
        f.close()
        run("chown root:postfix %s" % file_name)
        run("chmod 640 %s" % file_name)

        file_name = '%(postfix_dir)s/pgsql-virtual-alias-maps.cf' % context
        run("#Processing %s" % file_name)
        virtual_alias_maps = """user = %(db_user)s
password = %(db_password)s
hosts = %(db_host)s
dbname = %(db_name)s

query = SELECT mails_mailalias.destination FROM mails_mailalias INNER JOIN names_domain ON (mails_mailalias.domain_id = names_domain.id) WHERE mails_mailalias.emailname = '%%u' AND names_domain.name='%%d'
"""
        f = open(file_name, 'w')
        f.write(virtual_alias_maps % context)
        f.close()
        run("chown root:postfix %s" % file_name)
        run("chmod 640 %s" % file_name)

        file_name = '%(postfix_dir)s/pgsql-virtual-mailbox-domains.cf' % context
        run("#Processing %s" % file_name)
        virtual_mailbox_domains = """user = %(db_user)s
password = %(db_password)s
hosts = %(db_host)s
dbname = %(db_name)s

query = SELECT 1 FROM names_domain WHERE names_domain.name='%%s'
"""
        f = open(file_name, 'w')
        f.write(virtual_mailbox_domains % context)
        f.close()
        run("chown root:postfix %s" % file_name)
        run("chmod 640 %s" % file_name)

        file_name = '%(postfix_dir)s/pgsql-virtual-mailbox-maps.cf' % context
        run("#Processing %s" % file_name)
        virtual_mailbox_maps = """user = %(db_user)s
password = %(db_password)s
hosts = %(db_host)s
dbname = %(db_name)s

query = SELECT 1 FROM mails_mailbox INNER JOIN names_domain ON (mails_mailbox.domain_id = names_domain.id) WHERE mails_mailbox.emailname='%%u' AND names_domain.name='%%d'
"""
        f = open(file_name, 'w')
        f.write(virtual_mailbox_maps % context)
        f.close()
        run("chown root:postfix %s" % file_name)
        run("chmod 640 %s" % file_name)

        #Dovecot
        vmail_usename = run("id -u %(vmail_username)s" % context)
        vmail_groupname = run("id -g %(vmail_groupname)s" % context)
        if vmail_groupname != context["vmail_gid"]:
            run("groupadd -g %(vmail_gid)s %(vmail_groupname)s" % context)
            run("chown -R %(vmail_username)s:%(vmail_groupname)s %(vmail_home)s"
                % context)
        if vmail_usename != context["vmail_uid"]:
            run("useradd -g %(vmail_groupname)s -u %(vmail_uid)s %(vmail_username)s -d %(vmail_home)s -m"
                % context)
            run("chmod u+w %(vmail_home)s" % context)

        run("chown -R %(vmail_username)s:%(vmail_groupname)s %(vmail_home)s" %
            context)
        run("chmod u+w %(vmail_home)s" % context)

        file_name = "%(dovecot_dir)s/conf.d/10-auth.conf" % context
        run("""sed -i "s/auth_mechanisms = plain$/auth_mechanisms = plain login/g" %s """
            % file_name)
        run("""sed -i "s/\#\!include auth-sql.conf.ext/\!include auth-sql.conf.ext/" %s """
            % file_name)

        file_name = "%(dovecot_dir)s/conf.d/auth-sql.conf.ext" % context
        run("#Processing %s" % file_name)
        auth_sql_conf_ext = """passdb {
  driver = sql
  args = %(dovecot_dir)s/dovecot-sql.conf.ext
}

userdb {
  driver = static
  args = uid=%(vmail_username)s gid=%(vmail_groupname)s home=%(vmail_home)s/%%d/%%n/Maildir allow_all_users=yes
}
"""
        f = open(file_name, 'w')
        f.write(auth_sql_conf_ext % context)
        f.close()

        file_name = "%(dovecot_dir)s/conf.d/10-mail.conf" % context
        run("#Processing %s" % file_name)
        mail_conf = """mail_location = maildir:%(vmail_home)s/%%d/%%n/Maildir
namespace inbox {
    separator = .
    inbox = yes
}
        """
        f = open(file_name, 'w')
        f.write(mail_conf % context)
        f.close()

        file_name = "%(dovecot_dir)s/conf.d/10-master.conf" % context
        run("""sed -i "s/service auth {/service auth {\\n\\tunix_listener \/var\/spool\/postfix\/private\/auth {\\n\\t\\tmode = 0660\\n\\t\\tuser = postfix\\n\\t\\tgroup = postfix\\n\\t}\\n/g" %s """
            % file_name)

        file_name = "%(dovecot_dir)s/conf.d/10-ssl.conf" % context

        run("#Processing %s" % file_name)
        ssl_conf = """ssl_cert = </etc/ssl/certs/mailserver.pem
ssl_key = </etc/ssl/private/mailserver.pem"""
        f = open(file_name, 'w')
        f.write(ssl_conf)
        f.close()

        file_name = "%(dovecot_dir)s/conf.d/15-lda.conf" % context
        run("#Processing %s" % file_name)
        lda_conf = """protocol lda {
    postmaster_address = postmaster
    mail_plugins = $mail_plugins sieve
}
"""
        f = open(file_name, 'w')
        f.write(lda_conf)
        f.close()

        file_name = "%(dovecot_dir)s/dovecot-sql.conf.ext" % context
        run("#Processing %s" % file_name)
        dovecot_sql = """driver = pgsql
connect = host=%(db_host)s dbname=%(db_name)s user=%(db_user)s password=%(db_password)s
default_pass_scheme = SSHA
password_query = \
 SELECT mails_mailbox.emailname || '@' || names_domain.name as user, mails_mailbox.shadigest as password  \\
    FROM mails_mailbox \\
        INNER JOIN names_domain ON (mails_mailbox.domain_id = names_domain.id) \\
        INNER JOIN auth_user ON (mails_mailbox.user_id = auth_user.id) \\
    WHERE mails_mailbox.emailname = '%%n' AND \\
        names_domain.name = '%%d'
        """
        f = open(file_name, 'w')
        f.write(dovecot_sql % context)
        f.close()

        run("chgrp %(vmail_groupname)s %(dovecot_dir)s/dovecot.conf" % context)
        run("chmod g+r %(dovecot_dir)s/dovecot.conf" % context)

        run("chown root:root %(dovecot_dir)s/dovecot-sql.conf.ext" % context)
        run("chmod go= %(dovecot_dir)s/dovecot-sql.conf.ext" % context)

        file_name = "%(postfix_dir)s/master.cf" % context
        grep_dovecot = run("grep dovecot %s" % file_name, valid_codes=(0, 1))
        if grep_dovecot == '':
            run("#Processing %s" % file_name)
            dovecot_master = """
dovecot   unix  -       n       n       -       -       pipe
  flags=DRhu user=%(vmail_username)s:%(vmail_groupname)s argv=/usr/lib/dovecot/dovecot-lda -f ${sender} -d ${recipient}

#Amavis:
amavis unix    -       -       n       -       5     smtp
     -o smtp_data_done_timeout=1200
     -o smtp_send_xforward_command=yes
     -o smtp_tls_note_starttls_offer=no

127.0.0.1:10025 inet n    -       n       -       -     smtpd
    -o content_filter=
    -o smtpd_delay_reject=no
    -o smtpd_client_restrictions=permit_mynetworks,reject
    -o smtpd_helo_restrictions=
    -o smtpd_sender_restrictions=
    -o smtpd_recipient_restrictions=permit_mynetworks,reject
    -o smtpd_data_restrictions=reject_unauth_pipelining
    -o smtpd_end_of_data_restrictions=
    -o smtpd_restriction_classes=
    -o mynetworks=127.0.0.0/8
    -o smtpd_error_sleep_time=0
    -o smtpd_soft_error_limit=1001
    -o smtpd_hard_error_limit=1000
    -o smtpd_client_connection_count_limit=0
    -o smtpd_client_connection_rate_limit=0
    -o receive_override_options=no_header_body_checks,no_unknown_recipient_checks,no_milters
    -o local_header_rewrite_clients=
    -o smtpd_milters=
    -o local_recipient_maps=
    -o relay_recipient_maps=
              """
            f = open(file_name, 'a')
            f.write(dovecot_master % context)
            f.close()

        #Postfix
        mailname = run("cat /etc/mailname", vallid_codes=[0, 1])
        hostname = run("hostname", valid_codes=[0, 1])
        if mailname != hostname:
            file_name = "/etc/mailname"
            run("#Processing %s" % file_name)
            f = open(file_name, 'w')
            f.write(hostname % context)
            f.close()

        # Set the base address for all virtual mailboxes
        run("postconf -e virtual_mailbox_base=%(vmail_home)s" % context)

        # A list of all virtual domains serviced by this instance of postfix.
        run("postconf -e virtual_mailbox_domains=pgsql:%(postfix_dir)s/pgsql-virtual-mailbox-domains.cf"
            % context)

        # Look up the mailbox location based on the email address received.
        run("postconf -e virtual_mailbox_maps=pgsql:%(postfix_dir)s/pgsql-virtual-mailbox-maps.cf"
            % context)

        # Any aliases that are supported by this system
        run("postconf -e virtual_alias_maps=pgsql:%(postfix_dir)s/pgsql-virtual-alias-maps.cf"
            % context)

        #Dovecot:
        run("postconf -e virtual_transport=dovecot")
        run("postconf -e dovecot_destination_recipient_limit=1")
        run("postconf -e smtpd_sasl_type=dovecot")
        run("postconf -e smtpd_sasl_path=private/auth")
        run("postconf -e smtpd_sasl_auth_enable=yes")
        if os.path.isfile("/etc/ssl/certs/mailserver.pem"):
            run("postconf -e smtpd_tls_security_level=may")
            run("postconf -e smtpd_tls_auth_only=yes")
            run("postconf -e smtpd_tls_cert_file=/etc/ssl/certs/mailserver.pem"
                )
            run("postconf -e smtpd_tls_key_file=/etc/ssl/private/mailserver.pem"
                )

        run("""postconf -e smtpd_recipient_restrictions=permit_mynetworks,permit_sasl_authenticated,reject_invalid_hostname,reject_non_fqdn_recipient,reject_unknown_sender_domain,reject_unknown_recipient_domain,reject_unauth_destination,permit"""
            )
        run("""postconf -e soft_bounce=no""")
        run("""postconf -e content_filter=amavis:[127.0.0.1]:10024""")

        #Amavis:
        file_name = "%(amavis_dir)s/conf.d/15-content_filter_mode" % context
        run("""sed -i "s/#@bypass_virus_checks_maps/@bypass_virus_checks_maps/g" %s"""
            % file_name)
        run("""sed -i 's/#   \\\\%%bypass_virus_checks, \\\@bypass_virus_checks_acl, \\\$bypass_virus_checks_re/   \\\\%%bypass_virus_checks, \\\@bypass_virus_checks_acl, \\\$bypass_virus_checks_re/g' %s"""
            % file_name)
        run("""sed -i 's/#   \\\\%%bypass_virus_checks/   \\\\%%bypass_virus_checks/g' %s"""
            % (file_name, ))
        run("""sed -i "s/#@bypass_spam_checks_maps/@bypass_spam_checks_maps/g" %s"""
            % file_name)
        run("""sed -i 's/#   \\\\%%bypass_spam_checks, \\\@bypass_spam_checks_acl, \\\$bypass_spam_checks_re/   \\\\%%bypass_spam_checks, \\\@bypass_spam_checks_acl, \\\$bypass_spam_checks_re/g' %s"""
            % file_name)

        run("adduser clamav amavis")

        run("service clamav-freshclam restart")
        run("service clamav-daemon restart")
        run("service spamassassin restart")
        run("service amavis restart")
        run("service dovecot restart")
        run("service postfix restart")
Example #48
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)