diff --git a/.gitignore b/.gitignore index 7e99e36..4f47d0b 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -*.pyc \ No newline at end of file +*.pyc +.ropeproject/ diff --git a/app/settings.py b/app/settings.py index b8f2dec..f966f70 100644 --- a/app/settings.py +++ b/app/settings.py @@ -103,7 +103,7 @@ 'main', 'servers', - 'groups', + 'groups', 'keymanager', 'hostnameforwarding', 'portforwarding', @@ -112,6 +112,7 @@ 'fabrun', 'wizard', 'samba', + 'logstash', ) LOGGING = { @@ -157,6 +158,10 @@ CREATE_VM_STORAGE = 'local' +FORCE_SECURE_FOR_USER = False + +NGNIX_DEFAULT_REDIRECT = '' + try: from settingsLocal import * except ImportError: diff --git a/app/settingsLocal.py.dist b/app/settingsLocal.py.dist index b7d0688..1a54b17 100644 --- a/app/settingsLocal.py.dist +++ b/app/settingsLocal.py.dist @@ -47,3 +47,10 @@ BROKER_URL = '' NGNIX_SSL_PEM = '' NGNIX_SSL_KEY = '' + +LOGSTASH_SERVER = '' + +MYSQL_USERNAME = '' +MYSQL_PASSWORD = '' + +NGNIX_DEFAULT_REDIRECT = '' diff --git a/app/urls.py b/app/urls.py index a0a2ad2..01dde56 100644 --- a/app/urls.py +++ b/app/urls.py @@ -18,6 +18,7 @@ url(r'^fabrun/', include('fabrun.urls')), url(r'^wizards/', include('wizard.urls')), url(r'^samba/', include('samba.urls')), + url(r'^logstash/', include('logstash.urls')), (r'^users/login/$', 'django.contrib.auth.views.login', {'template_name': 'login.html'}), (r'^users/logout/$', 'django.contrib.auth.views.logout', {'next_page': '/'}), diff --git a/backups/templates/backups/backups/show.html b/backups/templates/backups/backups/show.html index 39ab726..4132b61 100644 --- a/backups/templates/backups/backups/show.html +++ b/backups/templates/backups/backups/show.html @@ -66,10 +66,12 @@

{{object.name}}

- {% trans "Force execution" %} - {% trans "Back to the list" %} - {% trans "Edit" %} -
+ {% if user.is_staff %} + {% trans "Force execution" %} + {% trans "Back to the list" %} + {% trans "Edit" %} + {% endif %} + diff --git a/backups/views.py b/backups/views.py index 994c642..f74e0b7 100644 --- a/backups/views.py +++ b/backups/views.py @@ -35,12 +35,14 @@ def backups_list(request): @login_required -@staff_member_required def backups_show(request, pk): - """Show details of a hostname forwarder""" + """Show details of a backup""" object = get_object_or_404(Backup, pk=pk) + if not request.user.is_staff and (request.user not in object.server_from.users_owning_the_server.all() and request.user not in object.server_to.users_owning_the_server.all()): + raise Http404 + liste = object.backuprun_set.order_by('-start_date').all() return render_to_response('backups/backups/show.html', {'object': object, 'liste': liste}, context_instance=RequestContext(request)) diff --git a/fabrun/views.py b/fabrun/views.py index 7ce0512..cd21e0f 100644 --- a/fabrun/views.py +++ b/fabrun/views.py @@ -91,7 +91,7 @@ def show_run(request, pk): @staff_member_required def clean_up(request): - Task.objects.filter(creation_date__lt = timezone.now() - datetime.timedelta(days=1)).delete() + Task.objects.filter(creation_date__lt=timezone.now() - datetime.timedelta(days=1)).delete() messages.success(request, "Old fabric runs have been deleted") diff --git a/hostnameforwarding/views.py b/hostnameforwarding/views.py index 8fbad8f..c5d4a3e 100644 --- a/hostnameforwarding/views.py +++ b/hostnameforwarding/views.py @@ -138,7 +138,14 @@ def get_conf(request, pk): root /usr/share/nginx/www; index index.html index.htm; +""" + + if settings.NGNIX_DEFAULT_REDIRECT: + script += """ + return 301 """ + settings.NGNIX_DEFAULT_REDIRECT + """; +""" + script += """ server_name localhost; } diff --git a/keymanager/views.py b/keymanager/views.py index 9751aec..70f7349 100644 --- a/keymanager/views.py +++ b/keymanager/views.py @@ -63,4 +63,10 @@ def get_keys(request, server, user): if key.key not in ssh_keys: ssh_keys.append(key.key) + # Allow each user who 'own' the server + for user in server.users_owning_the_server.all(): + for key in user.sshkey_set.all(): + if key.key not in ssh_keys: + ssh_keys.append(key.key) + return render_to_response('keymanager/get_keys.html', {'ssh_keys': ssh_keys}, context_instance=RequestContext(request)) diff --git a/logstash/__init__.py b/logstash/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/logstash/forms.py b/logstash/forms.py new file mode 100644 index 0000000..497077a --- /dev/null +++ b/logstash/forms.py @@ -0,0 +1,16 @@ +from django.forms import ModelForm + +from logstash.models import File + +from servers.models import Server + + +class FileForm(ModelForm): + class Meta: + model = File + exclude = () + + def __init__(self, *args, **kwargs): + super(FileForm, self).__init__(*args, **kwargs) + + self.fields["server"].queryset = Server.objects.order_by('name').filter(logstash_shipper=True) diff --git a/logstash/migrations/0001_initial.py b/logstash/migrations/0001_initial.py new file mode 100644 index 0000000..09ce04c --- /dev/null +++ b/logstash/migrations/0001_initial.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'File' + db.create_table(u'logstash_file', ( + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('server', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['servers.Server'])), + ('file', self.gf('django.db.models.fields.CharField')(max_length=255)), + ('type', self.gf('django.db.models.fields.CharField')(max_length=255)), + ('tags', self.gf('django.db.models.fields.CharField')(max_length=512)), + )) + db.send_create_signal(u'logstash', ['File']) + + + def backwards(self, orm): + # Deleting model 'File' + db.delete_table(u'logstash_file') + + + models = { + u'logstash.file': { + 'Meta': {'object_name': 'File'}, + 'file': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'server': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['servers.Server']"}), + 'tags': ('django.db.models.fields.CharField', [], {'max_length': '512'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + }, + u'servers.server': { + 'Meta': {'object_name': 'Server'}, + 'external_hostname_for_vms_creation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'external_interface': ('django.db.models.fields.CharField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}), + 'external_ip': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}), + 'hostname_for_vms_creation': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'internal_ip': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}), + 'is_proxmox': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_vm': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'keymanger_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'logstash_shipper': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'ngnix_server': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ngnixed_server_set'", 'null': 'True', 'to': u"orm['servers.Server']"}), + 'proxmox_node_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'samba_base_folder': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'samba_management': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'ssh_connection_string_from_backup': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'ssh_connection_string_from_gestion': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'vm_host': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'server_set'", 'null': 'True', 'to': u"orm['servers.Server']"}) + } + } + + complete_apps = ['logstash'] \ No newline at end of file diff --git a/logstash/migrations/0002_auto__add_execution.py b/logstash/migrations/0002_auto__add_execution.py new file mode 100644 index 0000000..1d40fd3 --- /dev/null +++ b/logstash/migrations/0002_auto__add_execution.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'Execution' + db.create_table(u'logstash_execution', ( + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('output', self.gf('django.db.models.fields.TextField')()), + ('done', self.gf('django.db.models.fields.BooleanField')(default=False)), + ('sugested_result', self.gf('django.db.models.fields.TextField')()), + )) + db.send_create_signal(u'logstash', ['Execution']) + + + def backwards(self, orm): + # Deleting model 'Execution' + db.delete_table(u'logstash_execution') + + + models = { + u'logstash.execution': { + 'Meta': {'object_name': 'Execution'}, + 'done': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'output': ('django.db.models.fields.TextField', [], {}), + 'sugested_result': ('django.db.models.fields.TextField', [], {}) + }, + u'logstash.file': { + 'Meta': {'object_name': 'File'}, + 'file': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'server': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['servers.Server']"}), + 'tags': ('django.db.models.fields.CharField', [], {'max_length': '512'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + }, + u'servers.server': { + 'Meta': {'object_name': 'Server'}, + 'external_hostname_for_vms_creation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'external_interface': ('django.db.models.fields.CharField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}), + 'external_ip': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}), + 'hostname_for_vms_creation': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'internal_ip': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}), + 'is_proxmox': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_vm': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'keymanger_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'logstash_shipper': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'ngnix_server': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ngnixed_server_set'", 'null': 'True', 'to': u"orm['servers.Server']"}), + 'proxmox_node_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'samba_base_folder': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'samba_management': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'ssh_connection_string_from_backup': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'ssh_connection_string_from_gestion': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'vm_host': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'server_set'", 'null': 'True', 'to': u"orm['servers.Server']"}) + } + } + + complete_apps = ['logstash'] \ No newline at end of file diff --git a/logstash/migrations/__init__.py b/logstash/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/logstash/models.py b/logstash/models.py new file mode 100644 index 0000000..131d20c --- /dev/null +++ b/logstash/models.py @@ -0,0 +1,30 @@ +from django.db import models + + +from servers.models import Server + + +class File(models.Model): + """A file to monitor""" + + server = models.ForeignKey(Server) + + file = models.CharField(max_length=255) + + TYPE_CHOICES = (('apachelog', 'Apache log'), ('syslog', 'Syslog'), ('misc', 'Misc')) + type = models.CharField(max_length=255, choices=TYPE_CHOICES) + + tags = models.CharField(max_length=512) + + def sorted_tags(self): + """Return the list of tags, sorted""" + return ','.join(sorted(self.tags.split(','))) + + + + +class Execution(models.Model): + + output = models.TextField() + done = models.BooleanField(default=False) + sugested_result = models.TextField() diff --git a/logstash/tasks.py b/logstash/tasks.py new file mode 100644 index 0000000..ca4cb9b --- /dev/null +++ b/logstash/tasks.py @@ -0,0 +1,145 @@ +from celery import task + +from servers.models import Server +import os +from django.conf import settings +import subprocess +from logstash.models import Execution +import json +from proxmoxs.views import gimme_prox_cox + + +@task(ignore_result=True) +def do_autodetection(expk): + """Do automatic detection of logstash config""" + + ex = Execution.objects.get(pk=expk) + + ex.output += 'Sarting execution\n' + ex.save() + + def work_on_host(server, base_tags, base_path, no_vhost_check=True): + """Work on host""" + + things_to_checks = [] + + def check_file(file, type, file_tags): + + things_to_checks.append((base_path + file, type, file_tags + base_tags)) + + def do_query(query): + p = subprocess.Popen(['ssh'] + server.ssh_connection_string_from_gestion.split(' ') + ["cat - | sh"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, bufsize=1) + return p.communicate(query + '\nexit')[0].split() + + ex.output += '---Checking common files\n' + ex.save() + + # Syslog + check_file('/var/log/syslog', 'syslog', ['syslog']) + + # Auth.log + check_file('/var/log/auth.log', 'syslog', ['auth.log']) + + # Apache logs + ex.output += '---Checking apache logs\n' + ex.save() + + for folder in ['apache2', 'httpd']: + for prefix in ['', 'ssl_', 'others_vhost_']: + for end in ['.log', '_log']: + for postfix in ['', '_ssl']: + check_file('/var/log/' + folder + '/' + prefix + 'access' + postfix + end, 'apachelog', ['access.log']) + check_file('/var/log/' + folder + '/' + prefix + 'error' + postfix + end, 'apachelog', ['error.log']) + + # Ngnix log + ex.output += '---Checking ngnix logs\n' + ex.save() + + check_file('/var/log/nginx.log', 'nginxlog', ['ngnix.log']) + check_file('/var/log/error.log', 'nginxerrlog', ['ngnixerror.log']) + + # Mysql log + ex.output += '---Checking mysql logs\n' + ex.save() + + check_file('/var/log/mysql.log', 'mysqllog', ['mysql.log']) + check_file('/var/log/mysql.err', 'mysqlerrlog', ['mysqlerror.log']) + + if not no_vhost_check: + # Apache logs by vhost, + ex.output += '---Checking apache logs in vhost folders\n' + ex.save() + + base_list = do_query('for x in `ls /var/www/vhosts/*/logs/*/access_log`; do echo $x; done') + do_query('for x in `ls /var/www/vhosts/*/logs/access_log`; do echo $x; done') + + for elem in base_list: + bpath = '/'.join(elem.split('/')[:-1]) + '/' + tag = elem.split('/')[-2] + + check_file(bpath + 'access_log', 'apachelog', ['access.log', tag]) + check_file(bpath + 'access_ssl_log', 'apachelog', ['access.log', tag]) + check_file(bpath + 'error_log', 'apachelog', ['error.log', tag]) + + # Do check in one pass + entries_list = [] + + output = do_query('\n'.join(['ls ' + x[0] + ' 2>/dev/null' for x in things_to_checks])) + + for (path, type, tags) in things_to_checks: + + if path in output: + entries_list.append((path, type, ','.join(sorted(tags)), server.pk)) + ex.output += '---' + path + ': ' + type + '\n' + + ex.save() + + return entries_list + + retour = [] + + # Get all shipper + for s in Server.objects.filter(logstash_shipper=True).all(): + ex.output += 'Working on ' + s.name + '\n' + ex.save() + + bn = s.name.split('.')[-2] + + tags = [bn, s.name] + + if s.is_proxmox: + tags.append('proxmox') + + # Do normal checks + retour += work_on_host(s, tags, '', no_vhost_check=s.is_proxmox) # If not proxmox, check vhosts + + # If proxmox, check subvms + if s.is_proxmox: + + vm_ids = {} + + proxretour = gimme_prox_cox(s.ip_for_proxmox()).getNodeContainerIndex(s.proxmox_node_name) + + if 'data' in proxretour: + for elem in proxretour['data']: + vm_ids[elem['ip']] = elem['vmid'] + + for proxmox in s.server_set.all(): + tags = [bn, proxmox.name, 'vm'] + + if proxmox.internal_ip in vm_ids: + id = vm_ids[proxmox.internal_ip] + + ex.output += 'Working on ' + proxmox.name + ', proxmox vm\n' + ex.save() + + retour += work_on_host(s, tags, '/var/lib/vz/root/' + str(id)) + else: + + ex.output += 'Cannot work on ' + proxmox.name + ', no id\n' + ex.save() + + ex.sugested_result = json.dumps(retour) + + ex.output += 'Done !\n' + ex.done = True + ex.save() diff --git a/logstash/templates/logstash/autodetect/final.html b/logstash/templates/logstash/autodetect/final.html new file mode 100644 index 0000000..c004845 --- /dev/null +++ b/logstash/templates/logstash/autodetect/final.html @@ -0,0 +1,81 @@ +{% extends "base.html" %} +{% load i18n %} +{% load bootstrap_toolkit %} + + +{% block title %}Logstash{% endblock %} + +{% block content %} + +

Automatic detection

+ + + +
+ +
+ +
+
+ {% csrf_token %} + + + + + + + + + + + + + + + {% for elem in toadd %} + + + + + + + + + {% endfor %} + + {% for elem in todel %} + + + + + + + + + {% endfor %} + + +
{% trans "Action" %}{% trans "Server" %}{% trans "File" %}{% trans "Type" %}{% trans "Tags" %}
+ + + + {{elem.3}}{{elem.0}}{{elem.1}}{{elem.2}}
+ + + + {{elem.server}}{{elem.file}}{{elem.type}}{{elem.sorted_tags}}
+ +
+ +
+
+ +
+
+
+ + +{% endblock %} + diff --git a/logstash/templates/logstash/autodetect/watch.html b/logstash/templates/logstash/autodetect/watch.html new file mode 100644 index 0000000..464bf11 --- /dev/null +++ b/logstash/templates/logstash/autodetect/watch.html @@ -0,0 +1,57 @@ +{% extends "base.html" %} +{% load i18n %} +{% load bootstrap_toolkit %} + + +{% block title %}Logstash{% endblock %} + +{% block content %} + +

Automatic detection

+ + + +
+ +
+ +
+
+ +
+ + +
+
+
+
+ + + + +{% endblock %} + diff --git a/logstash/templates/logstash/files/edit.html b/logstash/templates/logstash/files/edit.html new file mode 100644 index 0000000..66ba201 --- /dev/null +++ b/logstash/templates/logstash/files/edit.html @@ -0,0 +1,49 @@ +{% extends "base.html" %} +{% load i18n %} +{% load bootstrap_toolkit %} + + +{% block title %}Logstash{% endblock %} + +{% block content %} + +

{% trans "File management" %}

+ + + + +
+ +
+ +
+
+ +
+ {% csrf_token %} + + {{form|as_bootstrap }} + + + +
+ + +
+
+
+
+ + + +{% endblock %} + diff --git a/logstash/templates/logstash/files/list.html b/logstash/templates/logstash/files/list.html new file mode 100644 index 0000000..7697312 --- /dev/null +++ b/logstash/templates/logstash/files/list.html @@ -0,0 +1,63 @@ +{% extends "base.html" %} +{% load i18n %} +{% load bootstrap_toolkit %} + + +{% block title %}LogStash{% endblock %} + +{% block content %} + +

Monitored files

+ + + +
+ +
+ +
+
+ + + + + + + + + + + + + {% for elem in liste %} + + + + + + + + {% endfor %} + + +
{% trans "File" %}{% trans "Server" %}{% trans "Type" %}{% trans "Tags" %}
{{elem.file}}{{elem.server}}{{elem.type}}{{elem.sorted_tags}} + {% trans "Delete" %} + {% trans "Edit" %} +
+ + + + +
+
+
+
+ + +{% endblock %} + diff --git a/logstash/templates/logstash/files/show.html b/logstash/templates/logstash/files/show.html new file mode 100644 index 0000000..b2015fa --- /dev/null +++ b/logstash/templates/logstash/files/show.html @@ -0,0 +1,84 @@ +{% extends "base.html" %} +{% load i18n %} +{% load bootstrap_toolkit %} + + +{% block title %}Logstash{% endblock %} + +{% block content %} + + +

{{object.file}}

+ + + + +
+ +
+
+ +
+
+ +
+
+ {% trans "Details" %} + +
+ +
{{object.file}}
+
+ +
+ +
{{object.server}}
+
+ +
+ +
{{object.get_type_display}}
+
+ +
+ +
{{object.sorted_tags}}
+
+ +
+
+ + + +
+
+
+
+ + +
+ + + + + +{% endblock %} diff --git a/logstash/tests.py b/logstash/tests.py new file mode 100644 index 0000000..501deb7 --- /dev/null +++ b/logstash/tests.py @@ -0,0 +1,16 @@ +""" +This file demonstrates writing tests using the unittest module. These will pass +when you run "manage.py test". + +Replace this with more appropriate tests for your application. +""" + +from django.test import TestCase + + +class SimpleTest(TestCase): + def test_basic_addition(self): + """ + Tests that 1 + 1 always equals 2. + """ + self.assertEqual(1 + 1, 2) diff --git a/logstash/urls.py b/logstash/urls.py new file mode 100644 index 0000000..f2d4206 --- /dev/null +++ b/logstash/urls.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- + +from django.conf.urls import patterns, url + + +urlpatterns = patterns( + 'logstash.views', + + url(r'^$', 'file_list'), + url(r'^(?P[0-9]*)/show/$', 'file_show'), + url(r'^(?P[0-9]*)/edit/$', 'file_edit'), + url(r'^(?P[0-9]*)/delete/$', 'file_delete'), + + url(r'^(?P.*)/shipper.conf$', 'generate_config'), + + url(r'^auto$', 'start_autodetect'), + url(r'^auto/(?P[0-9]*)$', 'watch_autodetect'), + url(r'^auto/w/(?P[0-9]*)$', 'watch_get_status'), + url(r'^auto/confirm/(?P[0-9]*)$', 'watch_final'), + +) diff --git a/logstash/views.py b/logstash/views.py new file mode 100644 index 0000000..b53985b --- /dev/null +++ b/logstash/views.py @@ -0,0 +1,256 @@ +# -*- coding: utf-8 -*- + +from django.shortcuts import get_object_or_404, render_to_response, redirect +from django.template import RequestContext +from django.core.context_processors import csrf +from django.views.decorators.csrf import csrf_exempt +from django.http import Http404, HttpResponse, HttpResponseForbidden, HttpResponseNotFound +from django.utils.encoding import smart_str +from django.conf import settings +from django.contrib.admin.views.decorators import staff_member_required +from django.contrib.auth.decorators import login_required +from django.http import HttpResponseRedirect +from django.db import connections +from django.core.paginator import InvalidPage, EmptyPage, Paginator +from django.core.cache import cache +from django.core.urlresolvers import reverse +from django.contrib import messages + +from servers.models import Server +from logstash.models import File, Execution +from logstash.forms import FileForm +from logstash.tasks import do_autodetection + +import json + + +@login_required +@staff_member_required +def file_list(request): + """Show the home page with the list of monitored files""" + + liste = File.objects.order_by('file').all() + + return render_to_response('logstash/files/list.html', {'liste': liste}, context_instance=RequestContext(request)) + + +@login_required +@staff_member_required +def file_show(request, pk): + """Show details of an file""" + + object = get_object_or_404(File, pk=pk) + + return render_to_response('logstash/files/show.html', {'object': object}, context_instance=RequestContext(request)) + + +@login_required +@staff_member_required +def file_edit(request, pk): + """Edit an file""" + + try: + object = File.objects.get(pk=pk) + except: + object = File() + + if request.method == 'POST': # If the form has been submitted... + form = FileForm(request.POST, instance=object) + + if form.is_valid(): # If the form is valid + object = form.save() + + messages.success(request, 'The file has been saved.') + + return redirect(reverse('logstash.views.file_list')) + else: + form = FileForm(instance=object) + + tags = [] + + for f in File.objects.all(): + for t in f.tags.split(','): + if t not in tags: + tags.append(t) + + return render_to_response('logstash/files/edit.html', {'form': form, 'tags': tags}, context_instance=RequestContext(request)) + + +@login_required +@staff_member_required +def file_delete(request, pk): + """Delete an file""" + + object = get_object_or_404(File, pk=pk) + + object.delete() + + messages.success(request, 'File has been deleted.') + + return redirect(reverse('logstash.views.file_list', args=())) + + +def generate_config(request, name): + """Generate logstash shipper config""" + + server = get_object_or_404(Server, logstash_shipper=True, name=name) + + config = """input { + + """ + + for file in server.file_set.all(): + config += """file { + type => \"""" + file.type + """\" + path => [ \"""" + file.file + """\" ] + tags => [ """ + ','.join(['"' + x + '"' for x in file.tags.split(',')]) + """ ] + sincedb_path => "/var/cache/logstash/.sincedb" + } +""" + + config += """ +} + +filter { + if [type] == "apachelog" { + grok { + match => { "message" => "%{IPORHOST:clientip} %{USER:ident} %{USER:auth} \[%{HTTPDATE:timestamp}\] \\"(?:%{WORD:verb} %{URIPATHPARAM:request}(?: HTTP/%{NUMBER:httpversion})?|-)\\" %{NUMBER:response} (?:%{NUMBER:bytes}|-)" } + } + + date { + match => { "timestamp" => "dd/MMM/yyyy:HH:mm:ss Z" } + } + } + + if [type] == "syslog" { + + grok { + pattern => [ "%{SYSLOGTIMESTAMP:syslog_timestamp} %{SYSLOGHOST:syslog_hostname} %{DATA:syslog_program}(?:\\[%{POSINT:syslog_pid}\\])?: %{GREEDYDATA:syslog_message}" ] + add_field => [ "received_at", "%{@timestamp}" ] + } + + date { + match => [ "syslog_timestamp", "MMM d HH:mm:ss", "MMM dd HH:mm:ss" ] + } + if !("_grokparsefailure" in [tags]) { + mutate { + replace => [ "@source_host", "%{syslog_hostname}" ] + replace => [ "message", "%{syslog_message}" ] + } + } + + mutate { + remove => [ "syslog_hostname", "syslog_message", "syslog_timestamp" ] + } + } + + if [type] == "nginxlog" { + grok { + match => { "message" => "%{IPORHOST:clientip} %{GREEDYDATA:ident} %{GREEDYDATA:auth} \\[%{HTTPDATE:timestamp}\\] \\"%{WORD:verb} %{URIPATHPARAM:request} HTTP/%{NUMBER:httpversion}\\" %{NUMBER:response} (?:%{NUMBER:bytes}|-) (?:\\"(?:%{URI:referrer}|-)\\"|%{QS:referrer}) %{QS:agent} %{QS:xforwardedfor} %{IPORHOST:host} %{BASE10NUM:request_duration}" } + } + + date { + match => { "timestamp" => "dd/MMM/yyyy:HH:mm:ss Z" } + } + } + + if [type] == "nginxerrlog" { + grok { + match => { "message" => "%{DATESTAMP:timestamp} %{GREEDYDATA:message}, client: %{IP:clientip}, server: %{IPORHOST:host}, request: \\"%{WORD:verb} %{URIPATHPARAM:request} HTTP/%{NUMBER:httpversion}\\", upstream: \\"%{URI:upstream}\\", host: \\"%{IPORHOST:host}\\", referrer: \\"%{URI:referrer}\\"" } + } + + date { + match => { "timestamp" => "yyyy/mm/dd HH:mm:ss" } + } + } + +} + +output { + redis { host => \"""" + settings.LOGSTASH_SERVER + """\" data_type => "list" key => "logstash" } +} +""" + return HttpResponse(config) + + +@login_required +@staff_member_required +def start_autodetect(request): + """Start automatic detection""" + + e = Execution() + e.save() + + do_autodetection.delay(e.pk) + + return redirect(reverse('logstash.views.watch_autodetect', args=(e.pk,))) + + +@login_required +@staff_member_required +def watch_autodetect(request, key): + """Watch automatic detection""" + + ex = get_object_or_404(Execution, pk=key) + + return render_to_response('logstash/autodetect/watch.html', {'ex': ex}, context_instance=RequestContext(request)) + + +@login_required +@staff_member_required +def watch_get_status(request, key): + """Return current status about automatic detection""" + + ex = get_object_or_404(Execution, pk=key) + + sta = 'd' if ex.done else 'r' + + return HttpResponse(sta + ex.output) + + +@login_required +@staff_member_required +def watch_final(request, key): + """Final step for automatic detection: Display results and save""" + + ex = get_object_or_404(Execution, pk=key) + + if request.method == 'POST': + + for elem in request.POST.getlist('todel[]'): + File.objects.get(pk=elem).delete() + + for elem in request.POST.getlist('toadd[]'): + file, type, tags, server_pk = json.loads(elem) + server = Server.objects.get(pk=server_pk) + + File(file=file, type=type, tags=tags, server=server).save() + + messages.success(request, 'The file list has been updated !') + return redirect(reverse('logstash.views.file_list')) + + toadd = [] + todel = [] + + data = json.loads(ex.sugested_result) + + for elem in data: + toadd.append(json.dumps(elem)) + + for file in File.objects.all(): + json_repr = json.dumps((file.file, file.type, file.sorted_tags(), file.server.pk)) + + if json_repr in toadd: + toadd.remove(json_repr) + else: + todel.append(file) + + toadd_with_data = [] + + for elem in toadd: + file, type, tags, server_pk = json.loads(elem) + server = Server.objects.get(pk=server_pk) + + toadd_with_data.append((file, type, tags, server, elem)) + + return render_to_response('logstash/autodetect/final.html', {'ex': ex, 'toadd': toadd_with_data, 'todel': todel}, context_instance=RequestContext(request)) diff --git a/main/templates/main/users/_servers.html b/main/templates/main/users/_servers.html new file mode 100644 index 0000000..57ca19c --- /dev/null +++ b/main/templates/main/users/_servers.html @@ -0,0 +1,46 @@ +{% load i18n %} + +
+ + +
+ + + {% for server in object.server_set.all %} + + + + + + {% endfor %} +
+ {{server}} + + {% if user.is_staff %} + {% trans "Remove access" %} + {% endif %} +
+ + {% if user.is_staff %} + + + {% trans "Add" %} + + {% endif %} + + +
+
+ + diff --git a/main/templates/main/users/show.html b/main/templates/main/users/show.html index 57ada57..f01b40f 100644 --- a/main/templates/main/users/show.html +++ b/main/templates/main/users/show.html @@ -86,6 +86,10 @@

{{user.username}}

{% include "main/users/_groups.html" %} +
+ {% include "main/users/_servers.html" %} +
+ {% endblock %} diff --git a/main/urls.py b/main/urls.py index 782e553..791de7a 100644 --- a/main/urls.py +++ b/main/urls.py @@ -6,7 +6,8 @@ urlpatterns = patterns( 'main.views', - url(r'^$', 'home'), + url(r'^$', 'home_redirect'), + url(r'^home/$', 'home'), url(r'^about/me$', 'me'), url(r'^about/me/edit$', 'me_edit'), @@ -30,5 +31,8 @@ url(r'^users/groups/add/(?P[0-9]*)$', 'users_groups_add'), url(r'^users/groups/delete/(?P[0-9]*)/(?P[0-9]*)$', 'users_groups_delete'), + url(r'^users/servers/add/(?P[0-9]*)$', 'users_server_add'), + url(r'^users/servers/delete/(?P[0-9]*)/(?P[0-9]*)$', 'users_server_delete'), + url(r'^git_hook/(?P.*)/$', 'git_hook'), ) diff --git a/main/views.py b/main/views.py index 3013fa0..36c118c 100644 --- a/main/views.py +++ b/main/views.py @@ -24,12 +24,28 @@ from groups.models import Group from main.tasks import update_git_repo from backups.models import Backup +from servers.models import Server + + +def home_redirect(request): + """Redirect the user to the home page, forcing HTTPs if needed. As the real home force authentification, this try to force https for the login too""" + + if not request.is_secure() and settings.FORCE_SECURE_FOR_USER: + return HttpResponseRedirect('https://' + request.get_host() + request.path) + + r = redirect('main.views.home') + if settings.FORCE_SECURE_FOR_USER: + r['Strict-Transport-Security'] = 'max-age=31536000' + return r @login_required def home(request): """Show the welcome page""" + if not request.is_secure() and settings.FORCE_SECURE_FOR_USER: + return HttpResponseRedirect('https://' + request.get_host() + request.path) + backups = Backup.objects.order_by('name').all() return render_to_response('main/home.html', {'backups': backups}, context_instance=RequestContext(request)) @@ -60,8 +76,9 @@ def users_show(request, pk): object = get_object_or_404(User, pk=pk) groups = Group.objects.exclude(users=object).order_by('name').all() + servers = Server.objects.exclude(users_owning_the_server=object).order_by('name').all() - return render_to_response('main/users/show.html', {'object': object, 'groups': groups}, context_instance=RequestContext(request)) + return render_to_response('main/users/show.html', {'object': object, 'groups': groups, 'servers': servers}, context_instance=RequestContext(request)) @login_required @@ -312,3 +329,35 @@ def git_hook(request, id): update_git_repo.delay(id) return HttpResponse('Happy') + + +@login_required +@staff_member_required +def users_server_add(request, pk): + """Add a user to a server access""" + + user = get_object_or_404(User, pk=pk) + + server = get_object_or_404(Server, pk=request.GET.get('serverPk')) + + server.users_owning_the_server.add(user) + + messages.success(request, 'The user has been added to the server.') + + return redirect(reverse('main.views.users_show', args=(pk, ))) + + +@login_required +@staff_member_required +def users_server_delete(request, pk, serverPk): + """Delete an user from a server access""" + + user = get_object_or_404(User, pk=pk) + + server = get_object_or_404(Server, pk=serverPk) + + server.users_owning_the_server.remove(user) + + messages.success(request, 'The user has been removed from the server.') + + return redirect(reverse('main.views.users_show', args=(pk, ))) diff --git a/servers/migrations/0014_auto__add_field_server_logstash_shipper.py b/servers/migrations/0014_auto__add_field_server_logstash_shipper.py new file mode 100644 index 0000000..37aaeb9 --- /dev/null +++ b/servers/migrations/0014_auto__add_field_server_logstash_shipper.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding field 'Server.logstash_shipper' + db.add_column(u'servers_server', 'logstash_shipper', + self.gf('django.db.models.fields.BooleanField')(default=False), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'Server.logstash_shipper' + db.delete_column(u'servers_server', 'logstash_shipper') + + + models = { + u'servers.server': { + 'Meta': {'object_name': 'Server'}, + 'external_hostname_for_vms_creation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'external_interface': ('django.db.models.fields.CharField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}), + 'external_ip': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}), + 'hostname_for_vms_creation': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'internal_ip': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}), + 'is_proxmox': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_vm': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'keymanger_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'logstash_shipper': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'ngnix_server': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ngnixed_server_set'", 'null': 'True', 'to': u"orm['servers.Server']"}), + 'proxmox_node_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'samba_base_folder': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'samba_management': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'ssh_connection_string_from_backup': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'ssh_connection_string_from_gestion': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'vm_host': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'server_set'", 'null': 'True', 'to': u"orm['servers.Server']"}) + }, + u'servers.serveruser': { + 'Meta': {'object_name': 'ServerUser'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'server': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['servers.Server']"}) + }, + u'servers.sshkey': { + 'Meta': {'object_name': 'SshKey'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.TextField', [], {}), + 'server': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['servers.Server']"}), + 'user': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + } + } + + complete_apps = ['servers'] \ No newline at end of file diff --git a/servers/migrations/0015_auto.py b/servers/migrations/0015_auto.py new file mode 100644 index 0000000..acbb8b3 --- /dev/null +++ b/servers/migrations/0015_auto.py @@ -0,0 +1,100 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding M2M table for field users_owning_the_server on 'Server' + m2m_table_name = db.shorten_name(u'servers_server_users_owning_the_server') + db.create_table(m2m_table_name, ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('server', models.ForeignKey(orm[u'servers.server'], null=False)), + ('user', models.ForeignKey(orm[u'auth.user'], null=False)) + )) + db.create_unique(m2m_table_name, ['server_id', 'user_id']) + + + def backwards(self, orm): + # Removing M2M table for field users_owning_the_server on 'Server' + db.delete_table(db.shorten_name(u'servers_server_users_owning_the_server')) + + + models = { + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + u'servers.server': { + 'Meta': {'object_name': 'Server'}, + 'external_hostname_for_vms_creation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'external_interface': ('django.db.models.fields.CharField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}), + 'external_ip': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}), + 'hostname_for_vms_creation': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'internal_ip': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}), + 'is_proxmox': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_vm': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'keymanger_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'logstash_shipper': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'ngnix_server': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ngnixed_server_set'", 'null': 'True', 'to': u"orm['servers.Server']"}), + 'proxmox_node_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'samba_base_folder': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'samba_management': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'ssh_connection_string_from_backup': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'ssh_connection_string_from_gestion': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'users_owning_the_server': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}), + 'vm_host': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'server_set'", 'null': 'True', 'to': u"orm['servers.Server']"}) + }, + u'servers.serveruser': { + 'Meta': {'object_name': 'ServerUser'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'server': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['servers.Server']"}) + }, + u'servers.sshkey': { + 'Meta': {'object_name': 'SshKey'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.TextField', [], {}), + 'server': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['servers.Server']"}), + 'user': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + } + } + + complete_apps = ['servers'] \ No newline at end of file diff --git a/servers/migrations/0016_auto__add_field_server_notes.py b/servers/migrations/0016_auto__add_field_server_notes.py new file mode 100644 index 0000000..79ee8db --- /dev/null +++ b/servers/migrations/0016_auto__add_field_server_notes.py @@ -0,0 +1,97 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding field 'Server.notes' + db.add_column(u'servers_server', 'notes', + self.gf('django.db.models.fields.TextField')(null=True, blank=True), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'Server.notes' + db.delete_column(u'servers_server', 'notes') + + + models = { + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + u'servers.server': { + 'Meta': {'object_name': 'Server'}, + 'external_hostname_for_vms_creation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'external_interface': ('django.db.models.fields.CharField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}), + 'external_ip': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}), + 'hostname_for_vms_creation': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'internal_ip': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}), + 'is_proxmox': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_vm': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'keymanger_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'logstash_shipper': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'ngnix_server': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ngnixed_server_set'", 'null': 'True', 'to': u"orm['servers.Server']"}), + 'notes': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'proxmox_node_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'samba_base_folder': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'samba_management': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'ssh_connection_string_from_backup': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'ssh_connection_string_from_gestion': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'users_owning_the_server': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}), + 'vm_host': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'server_set'", 'null': 'True', 'to': u"orm['servers.Server']"}) + }, + u'servers.serveruser': { + 'Meta': {'object_name': 'ServerUser'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'server': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['servers.Server']"}) + }, + u'servers.sshkey': { + 'Meta': {'object_name': 'SshKey'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.TextField', [], {}), + 'server': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['servers.Server']"}), + 'user': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + } + } + + complete_apps = ['servers'] \ No newline at end of file diff --git a/servers/migrations/0017_auto__add_field_server_mysql_server.py b/servers/migrations/0017_auto__add_field_server_mysql_server.py new file mode 100644 index 0000000..167e495 --- /dev/null +++ b/servers/migrations/0017_auto__add_field_server_mysql_server.py @@ -0,0 +1,98 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding field 'Server.mysql_server' + db.add_column(u'servers_server', 'mysql_server', + self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='mysqled_server_set', null=True, to=orm['servers.Server']), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'Server.mysql_server' + db.delete_column(u'servers_server', 'mysql_server_id') + + + models = { + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + u'servers.server': { + 'Meta': {'object_name': 'Server'}, + 'external_hostname_for_vms_creation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'external_interface': ('django.db.models.fields.CharField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}), + 'external_ip': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}), + 'hostname_for_vms_creation': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'internal_ip': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}), + 'is_proxmox': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_vm': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'keymanger_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'logstash_shipper': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'mysql_server': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'mysqled_server_set'", 'null': 'True', 'to': u"orm['servers.Server']"}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'ngnix_server': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ngnixed_server_set'", 'null': 'True', 'to': u"orm['servers.Server']"}), + 'notes': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'proxmox_node_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'samba_base_folder': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'samba_management': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'ssh_connection_string_from_backup': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'ssh_connection_string_from_gestion': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'users_owning_the_server': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}), + 'vm_host': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'server_set'", 'null': 'True', 'to': u"orm['servers.Server']"}) + }, + u'servers.serveruser': { + 'Meta': {'object_name': 'ServerUser'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'server': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['servers.Server']"}) + }, + u'servers.sshkey': { + 'Meta': {'object_name': 'SshKey'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.TextField', [], {}), + 'server': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['servers.Server']"}), + 'user': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + } + } + + complete_apps = ['servers'] \ No newline at end of file diff --git a/servers/models.py b/servers/models.py index 88442fd..1fd47fa 100644 --- a/servers/models.py +++ b/servers/models.py @@ -4,6 +4,8 @@ import hashlib +from main.models import User + class Server(models.Model): name = models.CharField(max_length=255) @@ -14,6 +16,7 @@ class Server(models.Model): internal_ip = models.IPAddressField(blank=True, null=True) vm_host = models.ForeignKey('Server', blank=True, null=True, related_name='server_set') ngnix_server = models.ForeignKey('Server', blank=True, null=True, related_name='ngnixed_server_set') + mysql_server = models.ForeignKey('Server', blank=True, null=True, related_name='mysqled_server_set') ssh_connection_string_from_gestion = models.CharField(max_length=255, blank=True, null=True) ssh_connection_string_from_backup = models.CharField(max_length=255, blank=True, null=True) @@ -29,6 +32,12 @@ class Server(models.Model): samba_management = models.BooleanField(default=False) samba_base_folder = models.CharField(max_length=255, blank=True, null=True, default='') + logstash_shipper = models.BooleanField(default=False) + + users_owning_the_server = models.ManyToManyField(User, blank=True, null=True, help_text='Keys of users will be allowed and he will be able do display the server page details') + + notes = models.TextField(blank=True, null=True) + def all_ports(self): """Return all ports (forwarded from and to)""" @@ -92,7 +101,6 @@ def get_port(self): # Hack, but easy way :D return self.get_host_for_fabric().split(':')[-1] - def random_proxmox_password(self): """Return a unique but hard to guess password for new VMs""" b = '' diff --git a/servers/templates/servers/servers/_groups_allowed.html b/servers/templates/servers/servers/_groups_allowed.html index 476c69e..b902cf8 100644 --- a/servers/templates/servers/servers/_groups_allowed.html +++ b/servers/templates/servers/servers/_groups_allowed.html @@ -11,7 +11,9 @@ {% for group in object.groupwithaccess_set.all %} - {{group}} + {% if user.is_staff %}{% endif %} + {{group}} + {% if user.is_staff %}{% endif %} @@ -30,7 +32,9 @@ {% for group in user_.group_set.all %} - {{group}} + {% if user.is_staff %}{% endif %} + {{group}} + {% if user.is_staff %}{% endif %} @@ -75,4 +79,4 @@ $("#group_select2").select2({placeholder: "{% trans 'Select a group to add' %}"}); $("#user_select").select2({placeholder: "{% trans 'Select an user' %}"}); - \ No newline at end of file + diff --git a/servers/templates/servers/servers/_groups_membership.html b/servers/templates/servers/servers/_groups_membership.html index 1ef2696..b813345 100644 --- a/servers/templates/servers/servers/_groups_membership.html +++ b/servers/templates/servers/servers/_groups_membership.html @@ -11,7 +11,9 @@ {% for group in object.group_set.all %} - {{group}} + {% if user.is_staff %}{% endif %} + {{group}} + {% if user.is_staff %}{% endif %} @@ -30,7 +32,9 @@ {% for group in key.group_set.all %} - {{group}} + {% if user.is_staff %}{% endif %} + {{group}} + {% if user.is_staff %}{% endif %} @@ -77,4 +81,4 @@ $("#key_select").select2({placeholder: "{% trans 'Select a key' %}"}); $("#group_select").select2({placeholder: "{% trans 'Select a group to add' %}"}); - \ No newline at end of file + diff --git a/servers/templates/servers/servers/_runs.html b/servers/templates/servers/servers/_runs.html index b531948..c6cb22d 100644 --- a/servers/templates/servers/servers/_runs.html +++ b/servers/templates/servers/servers/_runs.html @@ -12,7 +12,9 @@ {% for run in object.last_runs %} - {{run.command}} + {% if user.is_staff %}{% endif %} + {{run.command}} + {% if user.is_staff %}{% endif %} {{run.creation_date|timesince}} {% trans "ago" %} diff --git a/servers/templates/servers/servers/edit.html b/servers/templates/servers/servers/edit.html index 10267ab..b821eb7 100644 --- a/servers/templates/servers/servers/edit.html +++ b/servers/templates/servers/servers/edit.html @@ -12,35 +12,33 @@

{% trans "Servers management" %}

-
- -
- -
-
+
+ +
+ +
+
- {% csrf_token %} + {% csrf_token %} - {{form|as_bootstrap }} + {{form|as_bootstrap }} -
- -
- +
+ +
+ +
-
- + - -
@@ -55,10 +53,13 @@

{% trans "Servers management" %}

$('#id_vm_host').parent().parent().show(); $('#id_ngnix_server').parent().parent().hide(); $('#id_ngnix_server').val(''); + $('#id_mysql_server').parent().parent().hide(); + $('#id_mysql_server').val(''); } else { $('#id_internal_ip').parent().parent().hide(); $('#id_vm_host').parent().parent().hide(); $('#id_ngnix_server').parent().parent().show(); + $('#id_mysql_server').parent().parent().show(); $('#id_internal_ip').val(''); $('#id_vm_host').val(''); } @@ -84,9 +85,8 @@

{% trans "Servers management" %}

$("#users").select2({tags:[{% for u in all_users %}"{{u}}",{% endfor %}],tokenSeparators: [",", " "]}); - $('#id_vm_host,#id_ngnix_server').css('width', '220px').select2(); - + $('#id_vm_host,#id_ngnix_server,#id_mysql_server,#id_users_owning_the_server').css('width', '220px').select2(); -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/servers/templates/servers/servers/list.html b/servers/templates/servers/servers/list.html index 5df69fa..1664e64 100644 --- a/servers/templates/servers/servers/list.html +++ b/servers/templates/servers/servers/list.html @@ -28,7 +28,7 @@

{% trans "Servers management" %}

{% trans "Proxmox ?" %} {% trans "Vm Host" %} {% trans "IP" %} - + {% if user.is_staff %}{% endif %} @@ -39,10 +39,12 @@

{% trans "Servers management" %}

{{elem.is_proxmox|yesno}} {{elem.vm_host|default:""}} {{elem.external_ip|default:""}} {{elem.internal_ip|default:""}} - - {% trans "Delete" %} - {% trans "Edit" %} - + {% if user.is_staff %} + + {% trans "Delete" %} + {% trans "Edit" %} + + {% endif %} {% endfor %} @@ -59,4 +61,4 @@

{% trans "Servers management" %}

-{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/servers/templates/servers/servers/show.html b/servers/templates/servers/servers/show.html index 1e3f584..b96e42b 100644 --- a/servers/templates/servers/servers/show.html +++ b/servers/templates/servers/servers/show.html @@ -30,6 +30,13 @@

{{server.name}}

{{object.name}}
+ {% if object.notes %} +
+ +
{{object.notes|linebreaks}}
+
+ {% endif %} + {% if object.keymanger_name %}
@@ -42,8 +49,6 @@

{{server.name}}

{{object.is_vm|yesno}}
- - {% if object.vm_host %}
@@ -85,6 +90,13 @@

{{server.name}}

{% endif %} + {% if object.mysql_server %} +
+ +
{{object.mysql_server}}
+
+ {% endif %} + {% if object.ssh_connection_string_from_gestion %}
@@ -134,6 +146,11 @@

{{server.name}}

{% endif %} +
+ +
{{object.logstash_shipper|yesno}}
+
+ {% if object.serveruser_set.all %}
@@ -147,15 +164,26 @@

{{server.name}}

{% endif %} + {% if object.users_owning_the_server.all %} +
+ +
+
    + {% for u in object.users_owning_the_server.all %} +
  • {{u}}
  • + {% endfor %} +
+
+
+ {% endif %} + - - diff --git a/servers/views.py b/servers/views.py index 4766e78..8549f9b 100644 --- a/servers/views.py +++ b/servers/views.py @@ -23,22 +23,26 @@ @login_required -@staff_member_required def servers_list(request): """Show the list of servers""" - liste = Server.objects.order_by('-is_vm', 'vm_host__name', 'name').all() + if request.user.is_staff: + liste = Server.objects.order_by('-is_vm', 'vm_host__name', 'name').all() + else: + liste = Server.objects.order_by('-is_vm', 'vm_host__name', 'name').filter(users_owning_the_server=request.user).all() return render_to_response('servers/servers/list.html', {'liste': liste}, context_instance=RequestContext(request)) @login_required -@staff_member_required def servers_show(request, pk): """Show details of an Server""" object = get_object_or_404(Server, pk=pk) + if not request.user.is_staff and not request.user in object.users_owning_the_server.all(): + raise Http404 + groups = Group.objects.order_by('name').all() return render_to_response('servers/servers/show.html', {'object': object, 'groups': groups}, context_instance=RequestContext(request)) @@ -330,4 +334,4 @@ def servers_map(request): proxmox_servers = Server.objects.order_by('name').filter(is_proxmox=True).all() outside_servers = Server.objects.order_by('name').filter(is_proxmox=False, is_vm=False).all() - return render_to_response('servers/map.html', {'proxmox_servers': proxmox_servers, 'outside_servers': outside_servers }, context_instance=RequestContext(request)) + return render_to_response('servers/map.html', {'proxmox_servers': proxmox_servers, 'outside_servers': outside_servers}, context_instance=RequestContext(request)) diff --git a/templates/base.html b/templates/base.html index 84199bc..16e6d86 100644 --- a/templates/base.html +++ b/templates/base.html @@ -81,10 +81,12 @@ {% trans "Users" %} + {% endif %} + {% if user.is_staff %} @@ -128,6 +130,10 @@ + + {% endif %} diff --git a/wizard/views.py b/wizard/views.py index 3cbec5e..5d14354 100644 --- a/wizard/views.py +++ b/wizard/views.py @@ -143,7 +143,7 @@ def do_tasks(request, uid): status_id = request.session['wiz_' + uid + '_status_id'] - return render_to_response('wizard/task.html', {'wiz': wiz, 'tasks': tasks, 'nb_tasks': wiz.get_nb_task(), 'status_id': status_id}, context_instance=RequestContext(request)) + return render_to_response('wizard/task.html', {'wiz': wiz, 'tasks': tasks, 'nb_tasks': wiz.get_nb_task(), 'status_id': status_id}, context_instance=RequestContext(request)) @login_required diff --git a/wizard/wizards/create_mysql_table.py b/wizard/wizards/create_mysql_table.py new file mode 100644 index 0000000..29835aa --- /dev/null +++ b/wizard/wizards/create_mysql_table.py @@ -0,0 +1,117 @@ + +from _wizard import _Wizard + +from django import forms + +from django.conf import settings + +from django.utils import timezone + +from servers.models import Server +import os +import uuid +import re + + +class Step1Form(forms.Form): + name = forms.CharField(help_text='The name of the database and the user. This will be prefixed with the VM name') + password = forms.CharField(widget=forms.PasswordInput(), required=False, help_text='Leave blank to generate a random password') + save_in_notes = forms.BooleanField(help_text='Check this to save the password in the server\'s note') + server = forms.ModelChoiceField(queryset=Server.objects.exclude(ssh_connection_string_from_gestion=None).order_by('name')) + + +class CreateMysqlTable(_Wizard): + """Simple wizard to create a mysql table""" + + _name = 'CreateMysqlTable' + _description = 'Simple wizard to create a mysql table and a linked user' + + _nb_step = 1 + _nb_task = 5 + + _steps_names = ['Informations needed'] + _tasks_names = ['Create database', 'Create mysql user', 'Grand rights on the server', 'Flush privileges', 'Save data'] + + def display_step_1(self, request): + + if request.method == 'POST': + form = Step1Form(request.POST) + else: + form = Step1Form() + + return ('', form, "$('#id_server').css('width', '220px').select2();") + + def save_step_1(self, form): + server = Server.objects.get(pk=form.cleaned_data['server'].pk) + name = re.sub('[\W_]+', '', server.name) + '_' + form.cleaned_data['name'] + server_pk = form.cleaned_data['server'].pk + password = form.cleaned_data['password'] + save_in_notes = form.cleaned_data['save_in_notes'] + + if not password: + password = str(uuid.uuid4()) + + return {'name': name, 'server_pk': server_pk, 'password': password, 'save_in_notes': save_in_notes} + + def do_task_1(self): + """Create the database""" + + server = Server.objects.get(pk=self.step_data[0]['server_pk']) + + # Find the mysql server + try: + server_mysql = server.vm_host.mysql_server + except: + return (False, None) + + os.system('ssh ' + server_mysql.ssh_connection_string_from_gestion + ' "echo \'CREATE DATABASE %s DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;\' | mysql --user=\'%s\' --password=\'%s\'"' % (self.step_data[0]['name'], settings.MYSQL_USERNAME, settings.MYSQL_PASSWORD)) + + return (True, None) + + def do_task_2(self): + """Create the mysql user""" + + server = Server.objects.get(pk=self.step_data[0]['server_pk']) + server_mysql = server.vm_host.mysql_server + + os.system('ssh ' + server_mysql.ssh_connection_string_from_gestion + ' "echo \'CREATE USER %s@%s IDENTIFIED BY \\"%s\\";\' | mysql --user=\'%s\' --password=\'%s\'"' % (self.step_data[0]['name'], server.internal_ip, self.step_data[0]['password'], settings.MYSQL_USERNAME, settings.MYSQL_PASSWORD)) + + return (True, None) + + def do_task_3(self): + """Grand rights on the server""" + + server = Server.objects.get(pk=self.step_data[0]['server_pk']) + server_mysql = server.vm_host.mysql_server + + os.system('ssh ' + server_mysql.ssh_connection_string_from_gestion + ' "echo \'GRANT ALL ON %s.* TO %s@%s;\' | mysql --user=\'%s\' --password=\'%s\'"' % (self.step_data[0]['name'], self.step_data[0]['name'], server.internal_ip, settings.MYSQL_USERNAME, settings.MYSQL_PASSWORD)) + + return (True, None) + + def do_task_4(self): + """Flush privileges""" + + server = Server.objects.get(pk=self.step_data[0]['server_pk']) + server_mysql = server.vm_host.mysql_server + + os.system('ssh ' + server_mysql.ssh_connection_string_from_gestion + ' "echo \'FLUSH PRIVILEGES;\' | mysql --user=\'%s\' --password=\'%s\'"' % (settings.MYSQL_USERNAME, settings.MYSQL_PASSWORD)) + + return (True, None) + + def do_task_5(self): + """Save data""" + + if self.step_data[0]['save_in_notes']: + server = Server.objects.get(pk=self.step_data[0]['server_pk']) + + if not server.notes: + server.notes = "" + + server.notes += """ + ==MysqlDB Created %s== + Mysql database: %s + Mysql username: %s + Mysql password: %s""" % (str(timezone.now()), self.step_data[0]['name'], self.step_data[0]['name'], self.step_data[0]['password']) + server.save() + + return (True, None) diff --git a/wizard/wizards/create_user.py b/wizard/wizards/create_user.py index fd7f11c..8d246b7 100644 --- a/wizard/wizards/create_user.py +++ b/wizard/wizards/create_user.py @@ -20,7 +20,7 @@ class Step1Form(forms.Form): class CreateUser(_Wizard): - """Simple wizard to create a VM""" + """Simple wizard to create a user on a VM""" _name = 'CreateUser' _description = 'Simple wizard to create a new user on a server' diff --git a/wizard/wizards/create_vm.py b/wizard/wizards/create_vm.py index 21d6668..a135568 100644 --- a/wizard/wizards/create_vm.py +++ b/wizard/wizards/create_vm.py @@ -18,6 +18,7 @@ from fabrun.models import Task from fabrun.tasks import run_task from backups.tasks import run_backup +from logstash.models import File class Step1Form(forms.Form): @@ -43,7 +44,6 @@ def get_full_name(self): return self.cleaned_data['name'] + '.' + self.cleaned_data['proxmox'].name - class Step2Form(forms.Form): add_to_groups = forms.BooleanField(initial=True, required=False) add_groups = forms.BooleanField(initial=True, required=False) @@ -53,6 +53,7 @@ class Step2Form(forms.Form): setup_hostforwarding = forms.BooleanField(initial=True, required=False) setup_backups = forms.BooleanField(initial=True, required=False) do_first_backup = forms.BooleanField(initial=True, required=False) + create_logstash_entries = forms.BooleanField(initial=True, required=False) name = forms.CharField() keymanager_name = forms.CharField() @@ -166,10 +167,10 @@ class CreateVm(_Wizard): _description = 'Simple wizard to create a VM' _nb_step = 2 - _nb_task = 9 + _nb_task = 10 _steps_names = ['VM Name and Host', 'Advanced parameters'] - _tasks_names = ['Create server', 'Setup VM groups', 'Create VM', 'Start VM', 'Open ports', 'Forward domain', 'Execute setup script', 'Create backup task', 'Execute backup task'] + _tasks_names = ['Create server', 'Setup VM groups', 'Create VM', 'Start VM', 'Open ports', 'Forward domain', 'Execute setup script', 'Create backup task', 'Execute backup task', 'Create logstash entries'] def display_step_1(self, request): @@ -371,3 +372,15 @@ def do_task_9(self): run_backup(id) return (True, None) + + def do_task_10(self): + """Create logstash entries""" + if self.step_data[1]['create_logstash_entries']: + + srv = Server.objects.get(pk=self.task_data[0]['server_pk']) + + if srv.vm_host.logstash_shipper: + File(server=srv.vm_host, file='/var/lib/vz/root/' + str(self.task_data[2]['vm_id']) + '/var/log/auth.log', type='syslog', tags=srv.vm_host.name.split('.')[-2] + ',vm,auth.log,' + srv.name).save() + File(server=srv.vm_host, file='/var/lib/vz/root/' + str(self.task_data[2]['vm_id']) + '/var/log/syslog', type='syslog', tags=srv.vm_host.name.split('.')[-2] + ',vm,syslog,' + srv.name).save() + + return (True, None)